chore: creating shared/api structure

This commit is contained in:
John Yeo
2025-09-30 13:52:43 +01:00
parent 9eef072f91
commit 8778057e6f
16 changed files with 420 additions and 35 deletions

View File

@@ -1,34 +1,32 @@
import {
AllowanceType,
AppEnv,
type AppEnv,
EntInterval,
FullCusEntWithProduct,
FullEntitlement,
Organization,
ResetCusEnt,
type FullCusEntWithProduct,
type FullEntitlement,
type Organization,
type ResetCusEnt,
} from "@autumn/shared";
import { CusEntService } from "@/internal/customers/cusProducts/cusEnts/CusEntitlementService.js";
import { getEntOptions } from "@/internal/products/prices/priceUtils.js";
import { getNextResetAt } from "@/utils/timeUtils.js";
import { UTCDate } from "@date-fns/utc";
import chalk from "chalk";
import { format, getDate, getMonth, setDate } from "date-fns";
import { Decimal } from "decimal.js";
import { createStripeCli } from "@/external/stripe/utils.js";
import { deleteCusCache } from "@/internal/customers/cusCache/updateCachedCus.js";
import { CusProductService } from "@/internal/customers/cusProducts/CusProductService.js";
import { CusEntService } from "@/internal/customers/cusProducts/cusEnts/CusEntitlementService.js";
import {
getRelatedCusPrice,
getResetBalance,
} from "@/internal/customers/cusProducts/cusEnts/cusEntUtils.js";
import { getResetBalancesUpdate } from "@/internal/customers/cusProducts/cusEnts/groupByUtils.js";
import { getRolloverUpdates } from "@/internal/customers/cusProducts/cusEnts/cusRollovers/rolloverUtils.js";
import { CusProductService } from "@/internal/customers/cusProducts/CusProductService.js";
import { createStripeCli } from "@/external/stripe/utils.js";
import { UTCDate } from "@date-fns/utc";
import { type DrizzleCli } from "../db/initDrizzle.js";
import { RolloverService } from "../internal/customers/cusProducts/cusEnts/cusRollovers/RolloverService.js";
import { deleteCusCache } from "@/internal/customers/cusCache/updateCachedCus.js";
import { getResetBalancesUpdate } from "@/internal/customers/cusProducts/cusEnts/groupByUtils.js";
import { CusPriceService } from "@/internal/customers/cusProducts/cusPrices/CusPriceService.js";
import { OrgService } from "@/internal/orgs/OrgService.js";
import { Decimal } from "decimal.js";
import { getEntOptions } from "@/internal/products/prices/priceUtils.js";
import { getNextResetAt } from "@/utils/timeUtils.js";
import type { DrizzleCli } from "../db/initDrizzle.js";
import { RolloverService } from "../internal/customers/cusProducts/cusEnts/cusRollovers/RolloverService.js";
const checkSubAnchor = async ({
db,
@@ -101,9 +99,9 @@ const handleShortDurationCusEnt = async ({
cusEnt: ResetCusEnt;
cacheEnabledOrgs: any[];
}) => {
let ent = cusEnt.entitlement as FullEntitlement;
const ent = cusEnt.entitlement as FullEntitlement;
let resetCusEnt = {
const resetCusEnt = {
...cusEnt,
next_reset_at: getNextResetAt({
curReset: new UTCDate(cusEnt.next_reset_at!),
@@ -118,9 +116,9 @@ const handleShortDurationCusEnt = async ({
.toNumber(),
}),
};
let newCusEnt = resetCusEnt;
const newCusEnt = resetCusEnt;
let rolloverUpdate = getRolloverUpdates({
const rolloverUpdate = getRolloverUpdates({
cusEnt,
nextResetAt: cusEnt.next_reset_at! as number,
});
@@ -137,7 +135,7 @@ const handleShortDurationCusEnt = async ({
`Reseting short cus ent (${cusEnt.feature_id}) [${ent.interval}], customer: ${cusEnt.customer_id}, org: ${cusEnt.customer.org_id}`,
);
let org = await OrgService.get({
const org = await OrgService.get({
db,
orgId: cusEnt.customer.org_id,
});
@@ -163,7 +161,7 @@ export const resetCustomerEntitlement = async ({
cacheEnabledOrgs: any[];
}) => {
try {
let ent = cusEnt.entitlement as FullEntitlement;
const ent = cusEnt.entitlement as FullEntitlement;
if (
ent.allowance_type == AllowanceType.Fixed &&
@@ -194,7 +192,7 @@ export const resetCustomerEntitlement = async ({
);
// Handle if entitlement changed to unlimited...
let entitlement = cusEnt.entitlement;
const entitlement = cusEnt.entitlement;
if (entitlement.allowance_type === AllowanceType.Unlimited) {
await CusEntService.update({
db,
@@ -249,12 +247,12 @@ export const resetCustomerEntitlement = async ({
intervalCount: cusEnt.entitlement.interval_count,
});
let rolloverUpdate = getRolloverUpdates({
const rolloverUpdate = getRolloverUpdates({
cusEnt,
nextResetAt: cusEnt.next_reset_at! as number,
});
let resetBalanceUpdate = getResetBalancesUpdate({
const resetBalanceUpdate = getResetBalancesUpdate({
cusEnt,
allowance: resetBalance || undefined,
});
@@ -304,7 +302,7 @@ export const resetCustomerEntitlement = async ({
// (org) => org.id === cusEnt.customer.org_id
// );
let org = await OrgService.get({
const org = await OrgService.get({
db,
orgId: cusEnt.customer.org_id,
});

View File

@@ -0,0 +1,80 @@
import { AppEnv } from "@models/genModels/genEnums.js";
import { z } from "zod/v4";
// import { AppEnv } from "../genModels/genEnums.js";
// import { RewardResponseSchema } from "../rewardModels/rewardModels/rewardResponseModels.js";
// import { CusProductResponseSchema } from "./cusResModels/cusProductResponse.js";
// import { CusReferralResponseSchema } from "./cusResModels/cusReferralsResponse.js";
// import { UpcomingInvoiceResponseSchema } from "./cusResModels/upcomingInvoiceResponse.js";
// import { EntityResponseSchema } from "./entityModels/entityResModels.js";
// import { InvoiceResponseSchema } from "./invoiceModels/invoiceResponseModels.js";
// export const TrialUsedResponseSchema = z.object({
// product_id: z.string().meta({
// description: "The ID of the product for which the trial was used",
// example: "prod_123",
// }),
// customer_id: z.string().meta({
// description: "The ID of the customer who used the trial",
// example: "cus_123",
// }),
// fingerprint: z.string().nullish().meta({
// description: "Device/browser fingerprint when the trial was used",
// example: "fp_1234567890abcdef",
// }),
// });
export const APICustomerSchema = z.object({
id: z.string().nullable().meta({
description: "Your internal ID for the customer",
example: "cus_123",
}),
name: z.string().nullable().meta({
description: "The customer's full name",
example: "John Doe",
}),
email: z.string().nullable().meta({
description: "The customer's email address",
example: "john.doe@example.com",
}),
fingerprint: z.string().nullable().meta({
description: "Unique device/browser fingerprint for the customer",
example: "fp_1234567890abcdef",
}),
stripe_id: z.string().nullable().meta({
description: "The customer's Stripe customer ID",
example: "cus_1234567890abcdef",
}),
created_at: z.number().meta({
description:
"The date and time the customer was created in milliseconds since epoch",
example: 1717000000,
}),
env: z.enum(AppEnv),
// products: z.array(CusProductResponseSchema),
// features: z.any(),
// invoices: z.array(InvoiceResponseSchema).optional(),
// trials_used: z.array(TrialUsedResponseSchema).optional(),
// rewards: RewardResponseSchema.nullish(),
// metadata: z.record(z.any(), z.any()).default({}),
// entities: z.array(EntityResponseSchema).optional(),
// referrals: z.array(CusReferralResponseSchema).optional(),
// payment_method: z.any().nullish(),
// upcoming_invoice: UpcomingInvoiceResponseSchema.nullish(),
});
// export const IntAPICustomer = CusResponseV2Schema.extend({
// autumn_id: z.string().nullish().meta({
// description: "Internal Autumn system ID for the customer",
// example: "autumn_cus_1234567890",
// }),
// });
export type APICustomer = z.infer<typeof APICustomerSchema>;
// export type CusProductResponse = z.infer<typeof CusProductResponseSchema>;

View File

@@ -0,0 +1,44 @@
// import { ProductResV2Schema } from "@api/products/prodResV2/prodResV2.js";
// import { z } from "zod/v4";
// // import { CusProductStatus } from "../../../models/cusProductModels/cusProductEnums.js";
// // import { ProductItemResponseSchema } from "../../../models/productV2Models/productItemModels/prodItemResponseModels.js";
// const PartialProduct = ProductResV2Schema.pick({
// id: true,
// name: true,
// is_default: true,
// is_add_on: true,
// version: true,
// group: true,
// items: true,
// });
// export const CusProductResSchema = z.object({
// ...PartialProduct.shape,
// group: z.string().nullable(),
// status: z.enum(["active", "past_due", "expired"]),
// canceled_at: z.number().nullable(),
// started_at: z.number(),
// current_period_start: z.number().nullish(),
// current_period_end: z.number().nullish(),
// entity_id: z.string().nullish(),
// quantity: z.number().optional(),
// });
// // Product related fields
// // id: z.string(),
// // name: z.string().nullable(),
// // is_default: z.boolean(),
// // is_add_on: z.boolean(),
// // version: z.number().nullish(),
// // items: z.array(ProductItemResponseSchema).nullish(),
// // product: {
// // },
// // entity_id: z.string().nullish(),
// // stripe_subscription_ids: z.array(z.string()).nullish(),

View File

@@ -0,0 +1,81 @@
// import { z } from "zod/v4";
// import { EntInterval } from "../../productModels/entModels/entEnums.js";
// import { ProductItemFeatureType } from "../../productV2Models/productItemModels/productItemModels.js";
// export const CusRolloverSchema = z.object({
// balance: z.number(),
// expires_at: z.number(),
// });
// export const CusEntResponseSchema = z.object({
// feature_id: z.string(),
// interval: z.nativeEnum(EntInterval).nullish(),
// interval_count: z.number().nullish(),
// unlimited: z.boolean().nullish(),
// balance: z.number().nullish(), //
// usage: z.number().nullish(),
// included_usage: z.number().nullish(),
// next_reset_at: z.number().nullish(),
// overage_allowed: z.boolean().nullish(),
// usage_limit: z.number().nullish(),
// rollovers: z.array(CusRolloverSchema).nullish(),
// });
// export const CoreCusFeatureResponseSchema = z.object({
// interval: z.nativeEnum(EntInterval).or(z.literal("multiple")).nullish(),
// interval_count: z.number().nullish(),
// unlimited: z.boolean().nullish(),
// balance: z.number().nullish(),
// usage: z.number().nullish(),
// included_usage: z.number().nullish(),
// next_reset_at: z.number().nullish(),
// overage_allowed: z.boolean().nullish(),
// breakdown: z
// .array(
// z.object({
// interval: z.nativeEnum(EntInterval),
// interval_count: z.number().nullish(),
// balance: z.number().nullish(),
// usage: z.number().nullish(),
// included_usage: z.number().nullish(),
// next_reset_at: z.number().nullish(),
// }),
// )
// .nullish(),
// credit_schema: z
// .array(
// z.object({
// feature_id: z.string(),
// credit_amount: z.number(),
// }),
// )
// .nullish(),
// usage_limit: z.number().nullish(),
// rollovers: z.array(CusRolloverSchema).nullish(),
// });
// export const CusEntResponseV2Schema = z
// .object({
// id: z.string(),
// type: z.nativeEnum(ProductItemFeatureType),
// name: z.string().nullish(),
// })
// .extend(CoreCusFeatureResponseSchema.shape);
// export const CheckResponseSchema = z
// .object({
// allowed: z.boolean(),
// customer_id: z.string(),
// feature_id: z.string(),
// entity_id: z.string().nullish(),
// required_balance: z.number(),
// code: z.string(),
// })
// .extend(CoreCusFeatureResponseSchema.shape);
// export type CusEntResponse = z.infer<typeof CusEntResponseSchema>;
// export type CusEntResponseV2 = z.infer<typeof CusEntResponseV2Schema>;
// export type CheckResponse = z.infer<typeof CheckResponseSchema>;
// export type CusRollover = z.infer<typeof CusRolloverSchema>;

View File

@@ -0,0 +1,14 @@
import { z } from "zod/v4";
export const CusReferralResponseSchema = z.object({
program_id: z.string(),
customer: z.object({
id: z.string(),
name: z.string().nullish(),
email: z.string().nullish(),
}),
reward_applied: z.boolean(),
created_at: z.number(),
});
export type CusReferralResponse = z.infer<typeof CusReferralResponseSchema>;

View File

@@ -0,0 +1,20 @@
// import { z } from "zod/v4";
// import { DiscountResponseSchema } from "../../rewardModels/rewardModels/rewardResponseModels.js";
// export const UpcomingInvoiceResponseSchema = z.object({
// lines: z.array(
// z.object({
// product_id: z.string().nullish(),
// description: z.string(),
// amount: z.number(),
// }),
// ),
// discounts: z.array(DiscountResponseSchema),
// subtotal: z.number(),
// total: z.number(),
// currency: z.string(),
// });
// export type UpcomingInvoiceResponse = z.infer<
// typeof UpcomingInvoiceResponseSchema
// >;

View File

@@ -0,0 +1,33 @@
import { z } from "zod/v4";
export enum FeatureResType {
Boolean = "boolean",
SingleUsage = "single_use",
ContinuousUse = "continuous_use",
CreditSystem = "credit_system",
}
export const APIFeatureSchema = z.object({
id: z.string(),
name: z.string().nullish(),
type: z.enum(FeatureResType),
display: z
.object({
singular: z.string(),
plural: z.string(),
})
.nullish(),
credit_schema: z
.array(
z.object({
metered_feature_id: z.string(),
credit_cost: z.number(),
}),
)
.nullish(),
archived: z.boolean().nullish(),
});
export type APIFeature = z.infer<typeof APIFeatureSchema>;

3
shared/api/models.ts Normal file
View File

@@ -0,0 +1,3 @@
export * from "./products/apiFreeTrial.js";
export * from "./products/apiProduct.js";
export * from "./products/apiProductItem.js";

View File

@@ -0,0 +1,12 @@
import { FreeTrialDuration } from "@models/productModels/freeTrialModels/freeTrialEnums.js";
import { z } from "zod/v4";
export const APIFreeTrial = z.object({
duration: z.enum(FreeTrialDuration),
length: z.number(),
unique_fingerprint: z.boolean(),
card_required: z.boolean(),
// For Cus Product
trial_available: z.boolean().nullish().default(true),
});

View File

@@ -0,0 +1,41 @@
import { AttachScenario } from "@models/checkModels/checkPreviewModels.js";
import { AppEnv } from "@models/genModels/genEnums.js";
import { ProductItemResponseSchema } from "@models/productV2Models/productItemModels/prodItemResponseModels.js";
import { z } from "zod/v4";
import { APIFreeTrial } from "./apiFreeTrial.js";
// import { AttachScenario } from "../checkModels/checkPreviewModels.js";
// import { AppEnv } from "../genModels/genEnums.js";
// import { FreeTrialResponseSchema } from "../productModels/freeTrialModels/freeTrialModels.js";
// import { ProductItemResponseSchema } from "./productItemModels/prodItemResponseModels.js";
export const APIProductPropertiesSchema = z.object({
is_free: z.boolean(),
is_one_off: z.boolean(),
interval_group: z.string().nullish(),
has_trial: z.boolean().nullish(),
updateable: z.boolean().nullish(),
});
export const APIProductSchema = z.object({
id: z.string(),
name: z.string(),
group: z.string().nullable(),
env: z.enum(AppEnv),
is_add_on: z.boolean(),
is_default: z.boolean(),
version: z.number(),
created_at: z.number(),
items: z.array(ProductItemResponseSchema),
free_trial: APIFreeTrial.nullable(),
base_variant_id: z.string().nullable(),
scenario: z.enum(AttachScenario).optional(),
properties: APIProductPropertiesSchema.optional(),
archived: z.boolean().optional(),
});
export type APIProduct = z.infer<typeof APIProductSchema>;
export type APIProductProperties = z.infer<typeof APIProductPropertiesSchema>;
// export type ProductResponse = z.infer<typeof ProductResponseSchema>;

View File

@@ -0,0 +1,43 @@
import { APIFeatureSchema } from "@api/features/apiFeature.js";
import { Infinite } from "@models/productModels/productEnums.js";
import {
PriceTierSchema,
ProductItemFeatureType,
ProductItemInterval,
ProductItemType,
UsageModel,
} from "@models/productV2Models/productItemModels/productItemModels.js";
import { z } from "zod/v4";
export const APIProductItemSchema = z.object({
// Feature stuff
type: z.enum(ProductItemType).nullish(),
feature_id: z.string().nullish(),
feature_type: z.enum(ProductItemFeatureType).nullish(),
// Feature response
feature: APIFeatureSchema.nullish(),
included_usage: z.number().or(z.literal(Infinite)).nullish(),
interval: z.enum(ProductItemInterval).nullish(),
interval_count: z.number().nullish(),
// Price config
price: z.number().nullish(),
tiers: z.array(PriceTierSchema).nullish(),
usage_model: z.enum(UsageModel).nullish(),
billing_units: z.number().nullish(), // amount per billing unit (eg. $9 / 250 units)
reset_usage_when_enabled: z.boolean().nullish(),
quantity: z.number().nullish(),
next_cycle_quantity: z.number().nullish(),
entity_feature_id: z.string().nullish(),
display: z
.object({
primary_text: z.string(),
secondary_text: z.string().nullish(),
})
.nullish(),
});
export type APIProductItem = z.infer<typeof APIProductItemSchema>;

View File

@@ -2,15 +2,20 @@
import * as schemas from "./db/schema.js";
export { schemas };
// API MODELS
export * from "./api/models.js";
// Auth Models
export * from "./db/auth-schema.js";
export * from "./enums/APIVersion.js";
export * from "./enums/AttachErrCode.js";
export * from "./enums/ErrCode.js";
export * from "./enums/LoggerAction.js";
// ENUMS
export * from "./enums/SuccessCode.js";
export * from "./enums/WebhookEventType.js";
// ANALYTICS MODELS
export * from "./models/analyticsModels/actionEnums.js";
export * from "./models/analyticsModels/actionTable.js";
@@ -18,21 +23,24 @@ export * from "./models/attachModels/attachBody.js";
export * from "./models/attachModels/attachEnums/AttachBranch.js";
export * from "./models/attachModels/attachEnums/AttachConfig.js";
export * from "./models/attachModels/attachEnums/AttachFunction.js";
// Attach Models
export * from "./models/attachModels/attachPreviewModels.js";
export * from "./models/attachModels/checkoutModels.js";
export * from "./models/authModels/membership.js";
export * from "./models/chatResultModels/chatResultFeature.js";
export * from "./models/chatResultModels/chatResultFeature.js";
// 4. Chat Result Models
export * from "./models/chatResultModels/chatResultTable.js";
export * from "./models/checkModels/checkPreviewModels.js";
export * from "./models/cusModels/cusExpand.js";
// 8. Customer Models
export * from "./models/cusModels/cusModels.js";
export * from "./models/cusModels/cusResModels/cusFeatureResponse.js";
export * from "./models/cusModels/cusResModels/cusProductResponse.js";
export * from "./models/cusModels/cusResModels/cusReferralsResponse.js";
// Cus response
export * from "./models/cusModels/cusResponseModels.js";
export * from "./models/cusModels/cusTable.js";
@@ -54,11 +62,13 @@ export * from "./models/cusProductModels/cusEntModels/rolloverModels/rolloverTab
export * from "./models/cusProductModels/cusPriceModels/cusPriceModels.js";
export * from "./models/cusProductModels/cusPriceModels/cusPriceTable.js";
export * from "./models/cusProductModels/cusProductEnums.js";
// 7. Cus Product Models
export * from "./models/cusProductModels/cusProductModels.js";
export * from "./models/cusProductModels/cusProductTable.js";
export * from "./models/devModels/apiKeyModels.js";
export * from "./models/devModels/apiKeyTable.js";
// 5. Others: events, apiKeys
export * from "./models/eventModels/eventModels.js";
export * from "./models/eventModels/eventTable.js";
@@ -66,9 +76,11 @@ export * from "./models/featureModels/featureConfig/creditConfig.js";
export * from "./models/featureModels/featureConfig/meteredConfig.js";
export * from "./models/featureModels/featureEnums.js";
export * from "./models/featureModels/featureModels.js";
export * from "./models/featureModels/featureResModels.js";
// export * from "./models/featureModels/featureResModels.js";
// 2. Feature Models
export * from "./models/featureModels/featureTable.js";
// Gen Models
export * from "./models/genModels/genEnums.js";
export * from "./models/migrationModels/migrationErrorTable.js";
@@ -76,6 +88,7 @@ export * from "./models/migrationModels/migrationJobTable.js";
export * from "./models/migrationModels/migrationModels.js";
export * from "./models/orgModels/frontendOrg.js";
export * from "./models/orgModels/orgConfig.js";
// 1. Org Models
export * from "./models/orgModels/orgTable.js";
export * from "./models/otherModels/metadataModels.js";

View File

@@ -1,6 +1,6 @@
import { UsageModel } from "../productV2Models/productItemModels/productItemModels.js";
import { Infinite } from "../productModels/productEnums.js";
import { ProductResponse } from "../productV2Models/productResponseModels.js";
import type { Infinite } from "../productModels/productEnums.js";
import type { UsageModel } from "../productV2Models/productItemModels/productItemModels.js";
import type { ProductResponse } from "../productV2Models/productResponseModels.js";
export enum AttachScenario {
Scheduled = "scheduled",

View File

@@ -1,5 +1,4 @@
import { z } from "zod/v4";
import { AppEnv } from "../genModels/genEnums.js";
import { RewardResponseSchema } from "../rewardModels/rewardModels/rewardResponseModels.js";
import { CusProductResponseSchema } from "./cusResModels/cusProductResponse.js";

View File

@@ -10,7 +10,11 @@
"moduleResolution": "NodeNext",
"target": "ES2020",
"outDir": "./dist",
"rootDir": "."
"rootDir": ".",
"paths": {
"@api/*": ["./api/*"],
"@models/*": ["./models/*"]
}
},
"include": ["./**/*"],
"types": ["node"],