@windrun-huaiin/backend-core 10.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (198) hide show
  1. package/LICENSE +21 -0
  2. package/dist/app/api/stripe/checkout/route.d.ts +19 -0
  3. package/dist/app/api/stripe/checkout/route.d.ts.map +1 -0
  4. package/dist/app/api/stripe/checkout/route.js +120 -0
  5. package/dist/app/api/stripe/checkout/route.mjs +118 -0
  6. package/dist/app/api/stripe/customer-portal/route.d.ts +11 -0
  7. package/dist/app/api/stripe/customer-portal/route.d.ts.map +1 -0
  8. package/dist/app/api/stripe/customer-portal/route.js +73 -0
  9. package/dist/app/api/stripe/customer-portal/route.mjs +71 -0
  10. package/dist/app/api/user/anonymous/init/route.d.ts +7 -0
  11. package/dist/app/api/user/anonymous/init/route.d.ts.map +1 -0
  12. package/dist/app/api/user/anonymous/init/route.js +210 -0
  13. package/dist/app/api/user/anonymous/init/route.mjs +208 -0
  14. package/dist/app/api/webhook/clerk/user/route.d.ts +7 -0
  15. package/dist/app/api/webhook/clerk/user/route.d.ts.map +1 -0
  16. package/dist/app/api/webhook/clerk/user/route.js +202 -0
  17. package/dist/app/api/webhook/clerk/user/route.mjs +200 -0
  18. package/dist/app/api/webhook/stripe/route.d.ts +8 -0
  19. package/dist/app/api/webhook/stripe/route.d.ts.map +1 -0
  20. package/dist/app/api/webhook/stripe/route.js +70 -0
  21. package/dist/app/api/webhook/stripe/route.mjs +67 -0
  22. package/dist/index.d.ts +7 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +83 -0
  25. package/dist/index.mjs +18 -0
  26. package/dist/lib/auth-utils.d.ts +46 -0
  27. package/dist/lib/auth-utils.d.ts.map +1 -0
  28. package/dist/lib/auth-utils.js +107 -0
  29. package/dist/lib/auth-utils.mjs +102 -0
  30. package/dist/lib/credit-init.d.ts +8 -0
  31. package/dist/lib/credit-init.d.ts.map +1 -0
  32. package/dist/lib/credit-init.js +16 -0
  33. package/dist/lib/credit-init.mjs +10 -0
  34. package/dist/lib/index.d.ts +5 -0
  35. package/dist/lib/index.d.ts.map +1 -0
  36. package/dist/lib/index.js +31 -0
  37. package/dist/lib/index.mjs +4 -0
  38. package/dist/lib/money-price-config.d.ts +51 -0
  39. package/dist/lib/money-price-config.d.ts.map +1 -0
  40. package/dist/lib/money-price-config.js +156 -0
  41. package/dist/lib/money-price-config.mjs +151 -0
  42. package/dist/lib/stripe-config.d.ts +31 -0
  43. package/dist/lib/stripe-config.d.ts.map +1 -0
  44. package/dist/lib/stripe-config.js +278 -0
  45. package/dist/lib/stripe-config.mjs +268 -0
  46. package/dist/node_modules/.pnpm/@rollup_plugin-typescript@12.1.4_rollup@4.46.2_tslib@2.8.1_typescript@5.9.3/node_modules/tslib/tslib.es6.js +48 -0
  47. package/dist/node_modules/.pnpm/@rollup_plugin-typescript@12.1.4_rollup@4.46.2_tslib@2.8.1_typescript@5.9.3/node_modules/tslib/tslib.es6.mjs +45 -0
  48. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/classic/errors.js +54 -0
  49. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/classic/errors.mjs +51 -0
  50. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/classic/iso.js +44 -0
  51. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/classic/iso.mjs +35 -0
  52. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/classic/parse.js +31 -0
  53. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/classic/parse.mjs +18 -0
  54. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/classic/schemas.js +587 -0
  55. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/classic/schemas.mjs +527 -0
  56. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/core/api.js +447 -0
  57. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/core/api.mjs +399 -0
  58. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/core/checks.js +245 -0
  59. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/core/checks.mjs +232 -0
  60. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/core/core.js +68 -0
  61. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/core/core.mjs +62 -0
  62. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/core/doc.js +39 -0
  63. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/core/doc.mjs +37 -0
  64. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/core/errors.js +80 -0
  65. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/core/errors.mjs +75 -0
  66. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/core/parse.js +101 -0
  67. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/core/parse.mjs +86 -0
  68. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/core/regexes.js +102 -0
  69. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/core/regexes.mjs +76 -0
  70. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/core/registries.js +56 -0
  71. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/core/registries.mjs +52 -0
  72. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/core/schemas.js +1205 -0
  73. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/core/schemas.mjs +1157 -0
  74. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/core/util.js +407 -0
  75. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/core/util.mjs +374 -0
  76. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/core/versions.js +9 -0
  77. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/core/versions.mjs +7 -0
  78. package/dist/prisma/client.d.ts +2 -0
  79. package/dist/prisma/client.d.ts.map +1 -0
  80. package/dist/prisma/client.js +12 -0
  81. package/dist/prisma/client.mjs +1 -0
  82. package/dist/prisma/index.d.ts +4 -0
  83. package/dist/prisma/index.d.ts.map +1 -0
  84. package/dist/prisma/index.js +10 -0
  85. package/dist/prisma/index.mjs +2 -0
  86. package/dist/prisma/prisma-transaction-util.d.ts +3 -0
  87. package/dist/prisma/prisma-transaction-util.d.ts.map +1 -0
  88. package/dist/prisma/prisma-transaction-util.js +29 -0
  89. package/dist/prisma/prisma-transaction-util.mjs +27 -0
  90. package/dist/prisma/prisma.d.ts +4 -0
  91. package/dist/prisma/prisma.d.ts.map +1 -0
  92. package/dist/prisma/prisma.js +109 -0
  93. package/dist/prisma/prisma.mjs +106 -0
  94. package/dist/services/aggregate/billing.aggregate.service.d.ts +83 -0
  95. package/dist/services/aggregate/billing.aggregate.service.d.ts.map +1 -0
  96. package/dist/services/aggregate/billing.aggregate.service.js +308 -0
  97. package/dist/services/aggregate/billing.aggregate.service.mjs +306 -0
  98. package/dist/services/aggregate/index.d.ts +3 -0
  99. package/dist/services/aggregate/index.d.ts.map +1 -0
  100. package/dist/services/aggregate/index.js +9 -0
  101. package/dist/services/aggregate/index.mjs +2 -0
  102. package/dist/services/aggregate/user.aggregate.service.d.ts +34 -0
  103. package/dist/services/aggregate/user.aggregate.service.d.ts.map +1 -0
  104. package/dist/services/aggregate/user.aggregate.service.js +136 -0
  105. package/dist/services/aggregate/user.aggregate.service.mjs +133 -0
  106. package/dist/services/context/index.d.ts +2 -0
  107. package/dist/services/context/index.d.ts.map +1 -0
  108. package/dist/services/context/index.js +13 -0
  109. package/dist/services/context/index.mjs +1 -0
  110. package/dist/services/context/user-context-service.d.ts +30 -0
  111. package/dist/services/context/user-context-service.d.ts.map +1 -0
  112. package/dist/services/context/user-context-service.js +170 -0
  113. package/dist/services/context/user-context-service.mjs +162 -0
  114. package/dist/services/database/apilog.service.d.ts +39 -0
  115. package/dist/services/database/apilog.service.d.ts.map +1 -0
  116. package/dist/services/database/apilog.service.js +174 -0
  117. package/dist/services/database/apilog.service.mjs +170 -0
  118. package/dist/services/database/constants.d.ts +73 -0
  119. package/dist/services/database/constants.d.ts.map +1 -0
  120. package/dist/services/database/constants.js +135 -0
  121. package/dist/services/database/constants.mjs +117 -0
  122. package/dist/services/database/credit.service.d.ts +107 -0
  123. package/dist/services/database/credit.service.d.ts.map +1 -0
  124. package/dist/services/database/credit.service.js +515 -0
  125. package/dist/services/database/credit.service.mjs +512 -0
  126. package/dist/services/database/creditAuditLog.service.d.ts +73 -0
  127. package/dist/services/database/creditAuditLog.service.d.ts.map +1 -0
  128. package/dist/services/database/creditAuditLog.service.js +305 -0
  129. package/dist/services/database/creditAuditLog.service.mjs +302 -0
  130. package/dist/services/database/index.d.ts +10 -0
  131. package/dist/services/database/index.d.ts.map +1 -0
  132. package/dist/services/database/index.js +38 -0
  133. package/dist/services/database/index.mjs +8 -0
  134. package/dist/services/database/prisma-model-type.d.ts +3 -0
  135. package/dist/services/database/prisma-model-type.d.ts.map +1 -0
  136. package/dist/services/database/subscription.service.d.ts +48 -0
  137. package/dist/services/database/subscription.service.d.ts.map +1 -0
  138. package/dist/services/database/subscription.service.js +267 -0
  139. package/dist/services/database/subscription.service.mjs +264 -0
  140. package/dist/services/database/transaction.service.d.ts +92 -0
  141. package/dist/services/database/transaction.service.d.ts.map +1 -0
  142. package/dist/services/database/transaction.service.js +326 -0
  143. package/dist/services/database/transaction.service.mjs +323 -0
  144. package/dist/services/database/user.service.d.ts +45 -0
  145. package/dist/services/database/user.service.d.ts.map +1 -0
  146. package/dist/services/database/user.service.js +180 -0
  147. package/dist/services/database/user.service.mjs +177 -0
  148. package/dist/services/database/userBackup.service.d.ts +45 -0
  149. package/dist/services/database/userBackup.service.d.ts.map +1 -0
  150. package/dist/services/database/userBackup.service.js +249 -0
  151. package/dist/services/database/userBackup.service.mjs +246 -0
  152. package/dist/services/stripe/index.d.ts +2 -0
  153. package/dist/services/stripe/index.d.ts.map +1 -0
  154. package/dist/services/stripe/index.js +7 -0
  155. package/dist/services/stripe/index.mjs +1 -0
  156. package/dist/services/stripe/webhook-handler.d.ts +6 -0
  157. package/dist/services/stripe/webhook-handler.d.ts.map +1 -0
  158. package/dist/services/stripe/webhook-handler.js +537 -0
  159. package/dist/services/stripe/webhook-handler.mjs +535 -0
  160. package/migrations/create.sql +176 -0
  161. package/migrations/db.init.sql +13 -0
  162. package/migrations/init-schema.sql +19 -0
  163. package/migrations/purge.sql +27 -0
  164. package/migrations/test-check.sql +167 -0
  165. package/package.json +123 -0
  166. package/prisma/schema.prisma +191 -0
  167. package/src/app/api/stripe/checkout/route.ts +145 -0
  168. package/src/app/api/stripe/customer-portal/route.ts +83 -0
  169. package/src/app/api/user/anonymous/init/route.ts +284 -0
  170. package/src/app/api/webhook/clerk/user/route.ts +249 -0
  171. package/src/app/api/webhook/stripe/route.ts +93 -0
  172. package/src/index.ts +6 -0
  173. package/src/lib/auth-utils.ts +101 -0
  174. package/src/lib/credit-init.ts +9 -0
  175. package/src/lib/index.ts +4 -0
  176. package/src/lib/money-price-config.ts +168 -0
  177. package/src/lib/stripe-config.ts +333 -0
  178. package/src/prisma/client.ts +2 -0
  179. package/src/prisma/index.ts +3 -0
  180. package/src/prisma/prisma-transaction-util.ts +24 -0
  181. package/src/prisma/prisma.ts +122 -0
  182. package/src/services/aggregate/billing.aggregate.service.ts +498 -0
  183. package/src/services/aggregate/index.ts +2 -0
  184. package/src/services/aggregate/user.aggregate.service.ts +168 -0
  185. package/src/services/context/index.ts +1 -0
  186. package/src/services/context/user-context-service.ts +200 -0
  187. package/src/services/database/apilog.service.ts +185 -0
  188. package/src/services/database/constants.ts +148 -0
  189. package/src/services/database/credit.service.ts +747 -0
  190. package/src/services/database/creditAuditLog.service.ts +402 -0
  191. package/src/services/database/index.ts +41 -0
  192. package/src/services/database/prisma-model-type.ts +13 -0
  193. package/src/services/database/subscription.service.ts +319 -0
  194. package/src/services/database/transaction.service.ts +447 -0
  195. package/src/services/database/user.service.ts +218 -0
  196. package/src/services/database/userBackup.service.ts +290 -0
  197. package/src/services/stripe/index.ts +1 -0
  198. package/src/services/stripe/webhook-handler.ts +648 -0
@@ -0,0 +1,200 @@
1
+ import { creditService, subscriptionService, userService } from '../database/index';
2
+ import type { Credit, Subscription, User } from '../database/prisma-model-type';
3
+ import { viewLocalTime } from '@windrun-huaiin/lib/utils';
4
+ import type { XCredit, XSubscription, XUser } from '@windrun-huaiin/third-ui/fingerprint';
5
+ import type { InitUserContext } from '@windrun-huaiin/third-ui/main/server';
6
+
7
+ export interface UserContextEntities {
8
+ user: User;
9
+ credit: Credit | null;
10
+ subscription: Subscription | null;
11
+ }
12
+
13
+ export interface FingerprintUserContext extends UserContextEntities {
14
+ totalUsersOnDevice: number;
15
+ hasAnonymousUser: boolean;
16
+ }
17
+
18
+ export function mapUserToXUser(user: User): XUser {
19
+ return {
20
+ userId: user.userId,
21
+ userName: user.userName || '',
22
+ fingerprintId: user.fingerprintId || '',
23
+ clerkUserId: user.clerkUserId || '',
24
+ stripeCusId: user.stripeCusId || '',
25
+ email: user.email || '',
26
+ status: user.status,
27
+ createdAt: viewLocalTime(user.createdAt),
28
+ };
29
+ }
30
+
31
+ export function mapCreditToXCredit(credit: Credit): XCredit {
32
+ return {
33
+ balanceFree: credit.balanceFree,
34
+ totalFreeLimit: credit.totalFreeLimit,
35
+ freeStart: viewLocalTime(credit.freeStart),
36
+ freeEnd: viewLocalTime(credit.freeEnd),
37
+ balancePaid: credit.balancePaid,
38
+ totalPaidLimit: credit.totalPaidLimit,
39
+ paidStart: viewLocalTime(credit.paidStart),
40
+ paidEnd: viewLocalTime(credit.paidEnd),
41
+ balanceOneTimePaid: credit.balanceOneTimePaid,
42
+ totalOneTimePaidLimit: credit.totalOneTimePaidLimit,
43
+ oneTimePaidStart: viewLocalTime(credit.oneTimePaidStart),
44
+ oneTimePaidEnd: viewLocalTime(credit.oneTimePaidEnd),
45
+ totalBalance: credit.balanceFree + credit.balancePaid + credit.balanceOneTimePaid,
46
+ };
47
+ }
48
+
49
+ export function mapSubscriptionToXSubscription(
50
+ subscription: Subscription | null,
51
+ ): XSubscription | null {
52
+ if (!subscription) {
53
+ return null;
54
+ }
55
+
56
+ return {
57
+ id: subscription.id,
58
+ userId: subscription.userId || '',
59
+ paySubscriptionId: subscription.paySubscriptionId || '',
60
+ orderId: subscription.orderId || '',
61
+ priceId: subscription.priceId || '',
62
+ priceName: subscription.priceName || '',
63
+ status: subscription.status || '',
64
+ creditsAllocated: subscription.creditsAllocated,
65
+ subPeriodStart: viewLocalTime(subscription.subPeriodStart),
66
+ subPeriodEnd: viewLocalTime(subscription.subPeriodEnd),
67
+ };
68
+ }
69
+
70
+ export function buildInitUserContextFromEntities(params: {
71
+ user: User;
72
+ credit: Credit | null;
73
+ subscription: Subscription | null;
74
+ isClerkAuthenticated?: boolean;
75
+ }): InitUserContext {
76
+ return {
77
+ fingerprintId: params.user.fingerprintId || null,
78
+ xUser: mapUserToXUser(params.user),
79
+ xCredit: params.credit ? mapCreditToXCredit(params.credit) : null,
80
+ xSubscription: mapSubscriptionToXSubscription(params.subscription),
81
+ isClerkAuthenticated: params.isClerkAuthenticated ?? true,
82
+ };
83
+ }
84
+
85
+ export async function fetchUserContextByClerkUserId(
86
+ clerkUserId: string,
87
+ ): Promise<UserContextEntities | null> {
88
+ const user = await userService.findByClerkUserId(clerkUserId);
89
+
90
+ if (!user) {
91
+ return null;
92
+ }
93
+
94
+ const [credit, subscription] = await Promise.all([
95
+ creditService.getCredit(user.userId),
96
+ subscriptionService.getActiveSubscription(user.userId),
97
+ ]);
98
+
99
+ return { user, credit, subscription };
100
+ }
101
+
102
+ export async function fetchLatestUserContextByFingerprintId(
103
+ fingerprintId: string,
104
+ ): Promise<FingerprintUserContext | null> {
105
+ const existingUsers = await userService.findListByFingerprintId(fingerprintId);
106
+ if (existingUsers.length === 0) {
107
+ return null;
108
+ }
109
+
110
+ const latestAnonymousUser = existingUsers[0];
111
+ const [credit, subscription] = await Promise.all([
112
+ creditService.getCredit(latestAnonymousUser.userId),
113
+ subscriptionService.getActiveSubscription(latestAnonymousUser.userId),
114
+ ]);
115
+
116
+ return {
117
+ user: latestAnonymousUser,
118
+ credit,
119
+ subscription,
120
+ totalUsersOnDevice: existingUsers.length,
121
+ hasAnonymousUser: true,
122
+ };
123
+ }
124
+
125
+ type MockableContext = {
126
+ xUser: XUser | null;
127
+ xSubscription: XSubscription | null;
128
+ };
129
+
130
+ export function applyUserMockContext<T extends MockableContext>(context: T): T {
131
+ const mockEnabled = process.env.MONEY_PRICE_MOCK_USER_ENABLED === 'true';
132
+ const mockType = Number(process.env.MONEY_PRICE_MOCK_USER_TYPE ?? NaN);
133
+
134
+ if (
135
+ !context.xUser ||
136
+ !mockEnabled ||
137
+ !Number.isInteger(mockType) ||
138
+ mockType < 0 ||
139
+ mockType > 4
140
+ ) {
141
+ return context;
142
+ }
143
+
144
+ const ensureSubscription = () => {
145
+ if (!context.xSubscription) {
146
+ const now = new Date();
147
+ context.xSubscription = {
148
+ id: BigInt(99999),
149
+ userId: context.xUser!.userId,
150
+ paySubscriptionId: 'MOCK-PAY-SUB-ID',
151
+ orderId: '',
152
+ priceId: '',
153
+ priceName: 'MOCK-TEST',
154
+ status: 'active',
155
+ creditsAllocated: 0,
156
+ subPeriodStart: viewLocalTime(now),
157
+ subPeriodEnd: viewLocalTime(now),
158
+ };
159
+ }
160
+
161
+ return context.xSubscription!;
162
+ };
163
+
164
+ switch (mockType) {
165
+ case 0: {
166
+ const subscription = ensureSubscription();
167
+ subscription.status = '';
168
+ subscription.priceId = '';
169
+ break;
170
+ }
171
+ case 1: {
172
+ const subscription = ensureSubscription();
173
+ subscription.priceId =
174
+ process.env.STRIPE_PRO_MONTHLY_PRICE_ID || subscription.priceId;
175
+ break;
176
+ }
177
+ case 2: {
178
+ const subscription = ensureSubscription();
179
+ subscription.priceId =
180
+ process.env.STRIPE_ULTRA_MONTHLY_PRICE_ID || subscription.priceId;
181
+ break;
182
+ }
183
+ case 3: {
184
+ const subscription = ensureSubscription();
185
+ subscription.priceId =
186
+ process.env.STRIPE_PRO_YEARLY_PRICE_ID || subscription.priceId;
187
+ break;
188
+ }
189
+ case 4: {
190
+ const subscription = ensureSubscription();
191
+ subscription.priceId =
192
+ process.env.STRIPE_ULTRA_YEARLY_PRICE_ID || subscription.priceId;
193
+ break;
194
+ }
195
+ default:
196
+ break;
197
+ }
198
+
199
+ return context;
200
+ }
@@ -0,0 +1,185 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+
3
+ import type { Prisma } from './prisma-model-type';
4
+ import type { Apilog } from './prisma-model-type';
5
+ import { checkAndFallbackWithNonTCClient } from '../../prisma/index';
6
+
7
+ export type ApiType = 'from_clerk_in' | 'to_clerk_out' | 'from_stripe_in' | 'to_stripe_out';
8
+
9
+ export interface CreateApiLogData {
10
+ methodName: string;
11
+ request?: any;
12
+ summary?: any;
13
+ apiType: ApiType;
14
+ }
15
+
16
+ export class ApilogService {
17
+
18
+ // Create API log record with request
19
+ async createApilog(data: CreateApiLogData, tx?: Prisma.TransactionClient): Promise<string> {
20
+ const client = checkAndFallbackWithNonTCClient(tx);
21
+ const log = await client.apilog.create({
22
+ data: {
23
+ methodName: data.methodName,
24
+ request: data.request ? JSON.stringify(data.request) : null,
25
+ summary: data.summary ? JSON.stringify(data.summary) : null,
26
+ apiType: data.apiType,
27
+ },
28
+ });
29
+ return log.id.toString();
30
+ }
31
+
32
+ // Update API log record with response
33
+ async updateApilogResponse(logId: string, response: any, tx?: Prisma.TransactionClient): Promise<void> {
34
+ const client = checkAndFallbackWithNonTCClient(tx);
35
+ await client.apilog.update({
36
+ where: { id: BigInt(logId) },
37
+ data: {
38
+ response: response ? JSON.stringify(response) : null,
39
+ },
40
+ });
41
+ }
42
+
43
+ // Get API log by ID
44
+ async getApilog(logId: string, tx?: Prisma.TransactionClient): Promise<Apilog | null> {
45
+ const client = checkAndFallbackWithNonTCClient(tx);
46
+ return await client.apilog.findUnique({
47
+ where: { id: BigInt(logId) },
48
+ });
49
+ }
50
+
51
+ // Get API logs with filters
52
+ async getApilogList(params: {
53
+ apiType?: ApiType;
54
+ methodName?: string;
55
+ limit?: number;
56
+ offset?: number;
57
+ }, tx?: Prisma.TransactionClient): Promise<Apilog[]> {
58
+ const { apiType, methodName, limit = 50, offset = 0 } = params;
59
+
60
+ const client = checkAndFallbackWithNonTCClient(tx);
61
+
62
+ return await client.apilog.findMany({
63
+ where: {
64
+ ...(apiType && { apiType }),
65
+ ...(methodName && { methodName: { contains: methodName } }),
66
+ },
67
+ orderBy: { createdAt: 'desc' },
68
+ take: limit,
69
+ skip: offset,
70
+ });
71
+ }
72
+
73
+ // Delete old API logs (cleanup)
74
+ async deleteOldLogList(daysOld: number = 30, tx?: Prisma.TransactionClient): Promise<number> {
75
+ const cutoffDate = new Date();
76
+ cutoffDate.setDate(cutoffDate.getDate() - daysOld);
77
+
78
+ const client = checkAndFallbackWithNonTCClient(tx);
79
+ const result = await client.apilog.deleteMany({
80
+ where: {
81
+ createdAt: {
82
+ lt: cutoffDate,
83
+ },
84
+ },
85
+ });
86
+
87
+ return result.count;
88
+ }
89
+
90
+ // Get API log statistics
91
+ async getApilogStats(tx?: Prisma.TransactionClient): Promise<{
92
+ totalLogs: number;
93
+ clerkIncoming: number;
94
+ clerkOutgoing: number;
95
+ stripeIncoming: number;
96
+ stripeOutgoing: number;
97
+ }> {
98
+ const client = checkAndFallbackWithNonTCClient(tx);
99
+ const [total, clerkIn, clerkOut, stripeIn, stripeOut] = await Promise.all([
100
+ client.apilog.count(),
101
+ client.apilog.count({ where: { apiType: 'from_clerk_in' } }),
102
+ client.apilog.count({ where: { apiType: 'to_clerk_out' } }),
103
+ client.apilog.count({ where: { apiType: 'from_stripe_in' } }),
104
+ client.apilog.count({ where: { apiType: 'to_stripe_out' } }),
105
+ ]);
106
+
107
+ return {
108
+ totalLogs: total,
109
+ clerkIncoming: clerkIn,
110
+ clerkOutgoing: clerkOut,
111
+ stripeIncoming: stripeIn,
112
+ stripeOutgoing: stripeOut,
113
+ };
114
+ }
115
+ }
116
+
117
+ // API Logger Helper Class for convenience
118
+ export class Apilogger {
119
+ static async createLogAsync(data: CreateApiLogData): Promise<string | null> {
120
+ try {
121
+ return await apilogService.createApilog(data);
122
+ } catch (error) {
123
+ console.error('Failed to create API log:', error);
124
+ return null;
125
+ }
126
+ }
127
+
128
+ static async updateResponseAsync(logId: string, response: any): Promise<void> {
129
+ try {
130
+ setImmediate(async () => {
131
+ try {
132
+ await apilogService.updateApilogResponse(logId, response);
133
+ } catch (error) {
134
+ console.error('Failed to update API log response:', error);
135
+ }
136
+ });
137
+ } catch (error) {
138
+ console.error('Failed to queue API log update:', error);
139
+ }
140
+ }
141
+
142
+ static async logClerkIncoming(methodName: string, summary?: any, originalRequest?: any): Promise<string | null> {
143
+ return await this.createLogAsync({
144
+ methodName,
145
+ request: originalRequest,
146
+ summary,
147
+ apiType: 'from_clerk_in',
148
+ });
149
+ }
150
+
151
+ static async logClerkOutgoing(methodName: string, request?: any, summary?: any): Promise<string | null> {
152
+ return await this.createLogAsync({
153
+ methodName,
154
+ request,
155
+ summary,
156
+ apiType: 'to_clerk_out',
157
+ });
158
+ }
159
+
160
+ static async logStripeIncoming(methodName: string, summary?: any, originalRequest?: any): Promise<string | null> {
161
+ return await this.createLogAsync({
162
+ methodName,
163
+ request: originalRequest,
164
+ summary,
165
+ apiType: 'from_stripe_in',
166
+ });
167
+ }
168
+
169
+ static async logStripeOutgoing(methodName: string, request?: any, summary?: any): Promise<string | null> {
170
+ return await this.createLogAsync({
171
+ methodName,
172
+ request,
173
+ summary,
174
+ apiType: 'to_stripe_out',
175
+ });
176
+ }
177
+
178
+ static updateResponse(logId: string | null, response: any): void {
179
+ if (logId) {
180
+ this.updateResponseAsync(logId, response);
181
+ }
182
+ }
183
+ }
184
+
185
+ export const apilogService = new ApilogService();
@@ -0,0 +1,148 @@
1
+ // Database Field Enums
2
+ // Keep in sync with DB CHECK constraints
3
+
4
+ export const UserStatus = {
5
+ // 匿名用户
6
+ ANONYMOUS: 'anonymous',
7
+ // 注册用户
8
+ REGISTERED: 'registered',
9
+ // 管理员介入管控
10
+ FROZEN: 'frozen',
11
+ // 用户注销,数据软删除,用户数据将不可复用
12
+ DELETED: 'deleted',
13
+ } as const;
14
+
15
+ export const SubscriptionStatus = {
16
+ // 初始状态或注销后状态
17
+ INCOMPLETE: 'incomplete',
18
+ // 订阅试用期
19
+ TRIALING: 'trialing',
20
+ // 有效订阅激活
21
+ ACTIVE: 'active',
22
+ // 订阅过期
23
+ PAST_DUE: 'past_due',
24
+ // 取消订阅
25
+ CANCELED: 'canceled',
26
+ } as const;
27
+
28
+ export const OrderStatus = {
29
+ // 初始状态
30
+ CREATED: 'created',
31
+ // 中间状态,待支付,可能是支付失败事件触发
32
+ PENDING_UNPAID: 'pending_unpaid',
33
+ // 中间状态(也可是最终状态),支付成功,后续可变为「退款或取消」
34
+ SUCCESS: 'success',
35
+ // 中间状态(也可是最终状态),CheckOut失败或者是支付失败,后续可变为「退款或取消」
36
+ FAILED: 'failed',
37
+ // 最终状态,已退款
38
+ REFUNDED: 'refunded',
39
+ // 最终状态,已取消
40
+ CANCELED: 'canceled',
41
+ } as const;
42
+
43
+ export const TransactionType = {
44
+ // 订阅订单
45
+ SUBSCRIPTION: 'subscription',
46
+ // 即付订单
47
+ ONE_TIME: 'one_time',
48
+ } as const;
49
+
50
+ export const CreditType = {
51
+ // 订阅积分
52
+ PAID: 'paid',
53
+ // 即付积分
54
+ ONE_TIME_PAID: 'one_time_paid',
55
+ // 免费积分
56
+ FREE: 'free',
57
+ } as const;
58
+
59
+ export const OperationType = {
60
+ // 系统奖励积分
61
+ SYS_GIFT: 'system_gift',
62
+ // 用户消费积分
63
+ CONSUME: 'consume',
64
+ // 用户充值积分
65
+ RECHARGE: 'recharge',
66
+ // 管理员介入冻结积分
67
+ FREEZE: 'freeze',
68
+ // 管理员介入解冻积分
69
+ UNFREEZE: 'unfreeze',
70
+ // 管理员介入赠送积分
71
+ ADJUST_INCREASE: 'adjust_increase',
72
+ // 管理员介入抹去积分
73
+ ADJUST_DECREASE: 'adjust_decrease',
74
+ // 清理积分,事件触发或者是积分过期触发
75
+ PURGE: 'purge',
76
+ } as const;
77
+
78
+ // 支付厂商类型
79
+ export const PaySupplier = {
80
+ STRIPE: 'Stripe',
81
+ APPLE: 'Apple',
82
+ PAYPAL: 'Paypal',
83
+ } as const;
84
+
85
+ export const BillingReason = {
86
+ // 首次订阅
87
+ SUBSCRIPTION_CREATE: 'subscription_create',
88
+ // 续订
89
+ SUBSCRIPTION_CYCLE: 'subscription_cycle',
90
+ } as const;
91
+
92
+ export const PaymentStatus = {
93
+ // 已支付
94
+ PAID: 'paid',
95
+ // 待支付
96
+ UN_PAID: 'un_paid',
97
+ // 无需支付
98
+ NO_PAYMENT_REQUIRED: 'no_payment_required',
99
+ } as const;
100
+
101
+
102
+ // Type Definitions
103
+ export type UserStatus = typeof UserStatus[keyof typeof UserStatus];
104
+ export type SubscriptionStatus = typeof SubscriptionStatus[keyof typeof SubscriptionStatus];
105
+ export type OrderStatus = typeof OrderStatus[keyof typeof OrderStatus];
106
+ export type TransactionType = typeof TransactionType[keyof typeof TransactionType];
107
+ export type CreditType = typeof CreditType[keyof typeof CreditType];
108
+ export type OperationType = typeof OperationType[keyof typeof OperationType];
109
+ export type PaySupplier = typeof PaySupplier[keyof typeof PaySupplier];
110
+ export type BillingReason = typeof BillingReason[keyof typeof BillingReason];
111
+ export type PaymentStatus = typeof PaymentStatus[keyof typeof PaymentStatus];
112
+
113
+ // Validation Functions
114
+ export const isValidUserStatus = (status: string): status is UserStatus => {
115
+ return Object.values(UserStatus).includes(status as UserStatus);
116
+ };
117
+
118
+ export const isValidSubscriptionStatus = (status: string): status is SubscriptionStatus => {
119
+ return Object.values(SubscriptionStatus).includes(status as SubscriptionStatus);
120
+ };
121
+
122
+ export const isValidOrderStatus = (status: string): status is OrderStatus => {
123
+ return Object.values(OrderStatus).includes(status as OrderStatus);
124
+ };
125
+
126
+ export const isValidTransactionType = (type: string): type is TransactionType => {
127
+ return Object.values(TransactionType).includes(type as TransactionType);
128
+ };
129
+
130
+ export const isValidCreditType = (type: string): type is CreditType => {
131
+ return Object.values(CreditType).includes(type as CreditType);
132
+ };
133
+
134
+ export const isValidOperationType = (type: string): type is OperationType => {
135
+ return Object.values(OperationType).includes(type as OperationType);
136
+ };
137
+
138
+ export const isValidPaySupplier = (supplier: string): supplier is PaySupplier => {
139
+ return Object.values(PaySupplier).includes(supplier as PaySupplier);
140
+ };
141
+
142
+ export const isValidBillingReason = (reason: string): reason is BillingReason => {
143
+ return Object.values(BillingReason).includes(reason as BillingReason);
144
+ };
145
+
146
+ export const isValidPaymentStatus = (status: string): status is PaymentStatus => {
147
+ return Object.values(PaymentStatus).includes(status as PaymentStatus);
148
+ };