@windrun-huaiin/backend-core 29.0.3 → 31.0.0

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 (51) hide show
  1. package/README.md +95 -0
  2. package/dist/app/api/user/anonymous/init/route.d.ts +1 -1
  3. package/dist/app/api/user/anonymous/init/route.d.ts.map +1 -1
  4. package/dist/app/api/user/anonymous/init/route.js +18 -19
  5. package/dist/app/api/user/anonymous/init/route.mjs +18 -19
  6. package/dist/app/api/webhook/clerk/user/route.js +16 -16
  7. package/dist/app/api/webhook/clerk/user/route.mjs +16 -16
  8. package/dist/auth/auth-utils.d.ts +8 -23
  9. package/dist/auth/auth-utils.d.ts.map +1 -1
  10. package/dist/auth/auth-utils.js +8 -20
  11. package/dist/auth/auth-utils.mjs +8 -20
  12. package/dist/lib/money-price-config.d.ts +28 -28
  13. package/dist/lib/money-price-config.js +31 -31
  14. package/dist/lib/money-price-config.mjs +31 -31
  15. package/dist/lib/stripe-config.js +3 -3
  16. package/dist/lib/stripe-config.mjs +3 -3
  17. package/dist/prisma/prisma-transaction-util.js +1 -1
  18. package/dist/prisma/prisma-transaction-util.mjs +1 -1
  19. package/dist/prisma/prisma.d.ts.map +1 -1
  20. package/dist/prisma/prisma.js +18 -19
  21. package/dist/prisma/prisma.mjs +18 -19
  22. package/dist/services/aggregate/billing.aggregate.service.js +6 -6
  23. package/dist/services/aggregate/billing.aggregate.service.mjs +6 -6
  24. package/dist/services/aggregate/user.aggregate.service.d.ts +9 -9
  25. package/dist/services/aggregate/user.aggregate.service.js +16 -16
  26. package/dist/services/aggregate/user.aggregate.service.mjs +16 -16
  27. package/dist/services/database/constants.js +34 -34
  28. package/dist/services/database/constants.mjs +34 -34
  29. package/dist/services/database/credit.service.js +2 -2
  30. package/dist/services/database/credit.service.mjs +2 -2
  31. package/dist/services/database/transaction.service.js +1 -1
  32. package/dist/services/database/transaction.service.mjs +1 -1
  33. package/dist/services/database/user.service.js +2 -2
  34. package/dist/services/database/user.service.mjs +2 -2
  35. package/dist/services/stripe/webhook-handler.js +5 -5
  36. package/dist/services/stripe/webhook-handler.mjs +5 -5
  37. package/package.json +13 -6
  38. package/src/app/api/user/anonymous/init/route.ts +21 -22
  39. package/src/app/api/webhook/clerk/user/route.ts +17 -17
  40. package/src/auth/auth-utils.ts +8 -23
  41. package/src/lib/money-price-config.ts +31 -32
  42. package/src/lib/stripe-config.ts +3 -3
  43. package/src/prisma/prisma-transaction-util.ts +1 -1
  44. package/src/prisma/prisma.ts +18 -19
  45. package/src/services/aggregate/billing.aggregate.service.ts +7 -7
  46. package/src/services/aggregate/user.aggregate.service.ts +16 -16
  47. package/src/services/database/constants.ts +34 -34
  48. package/src/services/database/credit.service.ts +2 -2
  49. package/src/services/database/transaction.service.ts +1 -1
  50. package/src/services/database/user.service.ts +2 -2
  51. package/src/services/stripe/webhook-handler.ts +5 -5
@@ -16,7 +16,7 @@ class TransactionService {
16
16
  orderId: data.orderId,
17
17
  orderStatus: data.orderStatus || constants.OrderStatus.CREATED,
18
18
  paymentStatus: data.paymentStatus || constants.PaymentStatus.UN_PAID,
19
- orderExpiredAt: data.orderExpiredAt || new Date(Date.now() + 30 * 60 * 1000), // 默认30分钟过期
19
+ orderExpiredAt: data.orderExpiredAt || new Date(Date.now() + 30 * 60 * 1000), // Default expiration: 30 minutes
20
20
  paySupplier: data.paySupplier,
21
21
  payTransactionId: data.payTransactionId,
22
22
  paySubscriptionId: data.paySubscriptionId,
@@ -14,7 +14,7 @@ class TransactionService {
14
14
  orderId: data.orderId,
15
15
  orderStatus: data.orderStatus || OrderStatus.CREATED,
16
16
  paymentStatus: data.paymentStatus || PaymentStatus.UN_PAID,
17
- orderExpiredAt: data.orderExpiredAt || new Date(Date.now() + 30 * 60 * 1000), // 默认30分钟过期
17
+ orderExpiredAt: data.orderExpiredAt || new Date(Date.now() + 30 * 60 * 1000), // Default expiration: 30 minutes
18
18
  paySupplier: data.paySupplier,
19
19
  payTransactionId: data.payTransactionId,
20
20
  paySubscriptionId: data.paySubscriptionId,
@@ -63,7 +63,7 @@ class UserService {
63
63
  findByClerkUserId(clerkUserId, tx) {
64
64
  return tslib.__awaiter(this, void 0, void 0, function* () {
65
65
  const client = prisma.checkAndFallbackWithNonTCClient(tx);
66
- // DB的部分索引与这里的状态查询相对应,因而可以使用findUnique
66
+ // Partial DB indexes match this status filter, so findUnique is valid here.
67
67
  return yield client.user.findUnique({
68
68
  where: {
69
69
  clerkUserId,
@@ -137,7 +137,7 @@ class UserService {
137
137
  return { users, total };
138
138
  });
139
139
  }
140
- // 批量创建匿名用户
140
+ // Create anonymous users in bulk
141
141
  createBatchAnonymousUsers(fingerprintIds, tx) {
142
142
  return tslib.__awaiter(this, void 0, void 0, function* () {
143
143
  const client = prisma.checkAndFallbackWithNonTCClient(tx);
@@ -61,7 +61,7 @@ class UserService {
61
61
  findByClerkUserId(clerkUserId, tx) {
62
62
  return __awaiter(this, void 0, void 0, function* () {
63
63
  const client = checkAndFallbackWithNonTCClient(tx);
64
- // DB的部分索引与这里的状态查询相对应,因而可以使用findUnique
64
+ // Partial DB indexes match this status filter, so findUnique is valid here.
65
65
  return yield client.user.findUnique({
66
66
  where: {
67
67
  clerkUserId,
@@ -135,7 +135,7 @@ class UserService {
135
135
  return { users, total };
136
136
  });
137
137
  }
138
- // 批量创建匿名用户
138
+ // Create anonymous users in bulk
139
139
  createBatchAnonymousUsers(fingerprintIds, tx) {
140
140
  return __awaiter(this, void 0, void 0, function* () {
141
141
  const client = checkAndFallbackWithNonTCClient(tx);
@@ -236,7 +236,7 @@ function handleInvoicePaid(invoice) {
236
236
  periodEnd: utils.viewLocalTime(subPeriodEnd),
237
237
  });
238
238
  if (isInitialPayment) {
239
- // 首次订阅校验
239
+ // check
240
240
  const nonActiveSubscription = yield subscription_service.subscriptionService.getNonActiveSubscription(userId);
241
241
  if (!nonActiveSubscription) {
242
242
  throw new Error(`Subscription status is ACTIVE for user ${userId}, forbidden to re-active!`);
@@ -263,7 +263,7 @@ function handleInvoicePaid(invoice) {
263
263
  return;
264
264
  }
265
265
  if (isRenewal) {
266
- // 续订时,一定要查到订阅记录
266
+ // must query it's subscription db record
267
267
  const subscription = yield subscription_service.subscriptionService.findByPaySubscriptionId(subscriptionId);
268
268
  if (!subscription) {
269
269
  throw new Error(`Subscription not found for renewal: ${subscriptionId}`);
@@ -274,8 +274,8 @@ function handleInvoicePaid(invoice) {
274
274
  throw new Error(`Renewal invoice ${invoice.id} already processed as ${existingOrder.orderId}, skipping.`);
275
275
  }
276
276
  // Get credits from current price configuration (handles plan upgrades/downgrades)
277
- // 优先从配置中取,取不到就以上个周期的为准作为Fallback,后续有问题再人工补偿,优先保证能用
278
- // 只要配置正确,这里就不会出错!
277
+ // Prefer values from configuration; fall back to the previous cycle value if unavailable. Manual remediation will be applied later if issues occur, prioritizing functional availability
278
+ // No error occurs here as long as the configuration is correct!
279
279
  const creditsForRenewal = subscription.priceId
280
280
  ? moneyPriceConfig.getCreditsFromPriceId(subscription.priceId)
281
281
  : subscription.creditsAllocated;
@@ -386,7 +386,7 @@ function handleInvoicePaymentFailed(invoice) {
386
386
  }
387
387
  const subscriptionId = parentDetails.subscription;
388
388
  const subscriptionMetadata = parentDetails.metadata || {};
389
- // 支付ID
389
+ // PaymentIntentId
390
390
  const paymentIntentId = yield stripeConfig.fetchPaymentId(invoice.id);
391
391
  console.log('Invoice payment failed event key-info:', {
392
392
  invoiceId: invoice.id,
@@ -234,7 +234,7 @@ function handleInvoicePaid(invoice) {
234
234
  periodEnd: viewLocalTime(subPeriodEnd),
235
235
  });
236
236
  if (isInitialPayment) {
237
- // 首次订阅校验
237
+ // check
238
238
  const nonActiveSubscription = yield subscriptionService.getNonActiveSubscription(userId);
239
239
  if (!nonActiveSubscription) {
240
240
  throw new Error(`Subscription status is ACTIVE for user ${userId}, forbidden to re-active!`);
@@ -261,7 +261,7 @@ function handleInvoicePaid(invoice) {
261
261
  return;
262
262
  }
263
263
  if (isRenewal) {
264
- // 续订时,一定要查到订阅记录
264
+ // must query it's subscription db record
265
265
  const subscription = yield subscriptionService.findByPaySubscriptionId(subscriptionId);
266
266
  if (!subscription) {
267
267
  throw new Error(`Subscription not found for renewal: ${subscriptionId}`);
@@ -272,8 +272,8 @@ function handleInvoicePaid(invoice) {
272
272
  throw new Error(`Renewal invoice ${invoice.id} already processed as ${existingOrder.orderId}, skipping.`);
273
273
  }
274
274
  // Get credits from current price configuration (handles plan upgrades/downgrades)
275
- // 优先从配置中取,取不到就以上个周期的为准作为Fallback,后续有问题再人工补偿,优先保证能用
276
- // 只要配置正确,这里就不会出错!
275
+ // Prefer values from configuration; fall back to the previous cycle value if unavailable. Manual remediation will be applied later if issues occur, prioritizing functional availability
276
+ // No error occurs here as long as the configuration is correct!
277
277
  const creditsForRenewal = subscription.priceId
278
278
  ? getCreditsFromPriceId(subscription.priceId)
279
279
  : subscription.creditsAllocated;
@@ -384,7 +384,7 @@ function handleInvoicePaymentFailed(invoice) {
384
384
  }
385
385
  const subscriptionId = parentDetails.subscription;
386
386
  const subscriptionMetadata = parentDetails.metadata || {};
387
- // 支付ID
387
+ // PaymentIntentId
388
388
  const paymentIntentId = yield fetchPaymentId(invoice.id);
389
389
  console.log('Invoice payment failed event key-info:', {
390
390
  invoiceId: invoice.id,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@windrun-huaiin/backend-core",
3
- "version": "29.0.3",
3
+ "version": "31.0.0",
4
4
  "description": "Shared backend primitives: Prisma schema/client, database services, routing helpers",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -139,7 +139,7 @@
139
139
  "LICENSE"
140
140
  ],
141
141
  "dependencies": {
142
- "@clerk/nextjs": "^7.0.5",
142
+ "@clerk/nextjs": "^7.3.3",
143
143
  "@upstash/redis": "^1.34.0",
144
144
  "@upstash/qstash": "^2.7.0",
145
145
  "@upstash/lock": "^0.2.1",
@@ -148,9 +148,9 @@
148
148
  "svix": "^1.86.0",
149
149
  "tslib": "^2.8.1",
150
150
  "zod": "^4.3.6",
151
- "@windrun-huaiin/contracts": "^29.0.0",
152
- "@windrun-huaiin/lib": "^29.0.0",
153
- "@windrun-huaiin/third-ui": "^29.0.3"
151
+ "@windrun-huaiin/lib": "^31.0.0",
152
+ "@windrun-huaiin/third-ui": "^31.0.0",
153
+ "@windrun-huaiin/contracts": "^31.0.0"
154
154
  },
155
155
  "devDependencies": {
156
156
  "@rollup/plugin-alias": "^5.1.1",
@@ -163,7 +163,7 @@
163
163
  },
164
164
  "peerDependencies": {
165
165
  "@windrun-huaiin/contracts": ">=22.0.0",
166
- "@clerk/nextjs": "^7.0.5",
166
+ "@clerk/nextjs": "^7.3.3",
167
167
  "@prisma/client": "^7.8.0",
168
168
  "next": "16.1.6",
169
169
  "stripe": "22.0.2",
@@ -172,6 +172,13 @@
172
172
  "publishConfig": {
173
173
  "access": "public"
174
174
  },
175
+ "author": "windrun-huaiin",
176
+ "homepage": "https://d8ger.com",
177
+ "repository": {
178
+ "type": "git",
179
+ "url": "git+https://github.com/caofanCPU/next-ai-build.git",
180
+ "directory": "packages/backend-core"
181
+ },
175
182
  "scripts": {
176
183
  "build": "rm -rf dist && rollup -c rollup.config.mjs",
177
184
  "build:prod": "rm -rf dist && rollup -c rollup.config.mjs",
@@ -23,9 +23,9 @@ import { finalizeUserContext } from '@core/context/user-context-finalizer';
23
23
  import type { CoreJsonObject } from '@core/db/prisma-model-type';
24
24
 
25
25
 
26
- // ==================== 类型定义 ====================
26
+ // ==================== Type Definitions ====================
27
27
 
28
- /** 成功响应数据 */
28
+ /** Successful response payload */
29
29
  interface XUserResponse {
30
30
  success: true;
31
31
  xUser: XUser;
@@ -36,14 +36,14 @@ interface XUserResponse {
36
36
  hasAnonymousUser?: boolean;
37
37
  }
38
38
 
39
- /** 错误响应数据 */
39
+ /** Error response payload */
40
40
  interface ErrorResponse {
41
41
  error: string;
42
42
  }
43
43
 
44
- // ==================== 工具函数 ====================
44
+ // ==================== Utilities ====================
45
45
 
46
- /** 创建成功响应对象 */
46
+ /** Create a successful response payload */
47
47
  function createSuccessResponse(params: {
48
48
  entities: UserContextEntities;
49
49
  isNewUser: boolean;
@@ -64,7 +64,7 @@ function createSuccessResponse(params: {
64
64
  return finalizeUserContext(response);
65
65
  }
66
66
 
67
- /** 创建错误响应 */
67
+ /** Create an error response */
68
68
  function createErrorResponse(message: string, status = 400): NextResponse {
69
69
  const errorResponse: ErrorResponse = { error: message };
70
70
  return NextResponse.json(errorResponse, { status });
@@ -552,7 +552,7 @@ function finalizeAttribution(sourceRef: SourceRefData) {
552
552
  sourceRef.sourceType = 'direct';
553
553
  }
554
554
 
555
- // 提取用户首次访问来源
555
+ // Extract the user's first-touch attribution source.
556
556
  function extractSourceRef(request: NextRequest): SourceRefData | null {
557
557
  const headerRef = request.headers.get('referer') || request.headers.get('referrer');
558
558
  const customRef = request.headers.get('x-source-ref');
@@ -603,7 +603,7 @@ function extractSourceRef(request: NextRequest): SourceRefData | null {
603
603
 
604
604
 
605
605
  /**
606
- * 根据fingerprint_id查询用户并返回响应数据
606
+ * Query the user by Clerk user ID and return response data.
607
607
  */
608
608
  async function getUserByClerkId(clerkUserId: string): Promise<XUserResponse | null> {
609
609
  const entities = await fetchUserContextByClerkUserId(clerkUserId);
@@ -618,7 +618,7 @@ async function getUserByClerkId(clerkUserId: string): Promise<XUserResponse | nu
618
618
  }
619
619
 
620
620
  /**
621
- * 根据fingerprint_id查询用户并返回响应数据
621
+ * Query the user by fingerprint ID and return response data.
622
622
  */
623
623
  async function getUserByFingerprintId(fingerprintId: string): Promise<XUserResponse | null> {
624
624
  const result = await fetchLatestUserContextByFingerprintId(fingerprintId);
@@ -639,12 +639,12 @@ async function getUserByFingerprintId(fingerprintId: string): Promise<XUserRespo
639
639
  }
640
640
 
641
641
  /**
642
- * 通用的fingerprint处理逻辑
642
+ * Shared fingerprint request handling logic.
643
643
  */
644
644
  async function handleFingerprintRequest(request: NextRequest, options: { createIfNotExists?: boolean; } = {}) {
645
- // 从请求中提取fingerprint ID
645
+ // Extract the fingerprint ID from the request.
646
646
  const fingerprintId = extractFingerprintFromNextRequest(request);
647
- // 验证fingerprint ID
647
+ // Validate the fingerprint ID.
648
648
  if (!fingerprintId) {
649
649
  return createErrorResponse('Invalid or missing fingerprint ID');
650
650
  }
@@ -653,27 +653,26 @@ async function handleFingerprintRequest(request: NextRequest, options: { createI
653
653
  const authIdentity = await getOptionalServerAuthIdentity();
654
654
  const clerkUserId = authIdentity?.providerUserId ?? null;
655
655
  try {
656
- // 优先根据 Clerk ID 查询(如果已登录)
656
+ // Prefer Clerk user ID lookup when the user is authenticated.
657
657
  let existingUserResult: XUserResponse | null = null;
658
658
  if (clerkUserId) {
659
- // 已登录一律按照clerkUserId去查
659
+ // Authenticated users are always resolved by clerkUserId.
660
660
  existingUserResult = await getUserByClerkId(clerkUserId);
661
661
  if (existingUserResult && existingUserResult.xUser.fingerprintId !== fingerprintId) {
662
- // 说明当前用户的指纹ID发生了改变,为什么呢?因为它使用同一账号去注册Clerk,Clerk判定是同一用户!
663
- // 这个时候一定以登录用户clerkUserId为准
664
- // 但是考虑到同一指纹ID本身可以绑定多个账号,所以这里什么都不做
665
- // 就是以当前登录用户去查他自己的数据就行!
662
+ // The authenticated user's fingerprint changed. Clerk still identifies the account as the same user.
663
+ // Trust clerkUserId as the source of truth and keep resolving the user's own data by login identity.
664
+ // A single fingerprint can be associated with multiple accounts, so no mutation is needed here.
666
665
  console.warn(`Current login user used diff fp_ids: ${clerkUserId}, db_fp_id=${existingUserResult.xUser.fingerprintId}, req_fp_id=${fingerprintId}`);
667
666
  }
668
667
  } else {
669
- // 其次才是检查是否已存在该fingerprint的用户
668
+ // For anonymous requests, fall back to fingerprint lookup.
670
669
  existingUserResult = await getUserByFingerprintId(fingerprintId);
671
670
  }
672
671
  if (existingUserResult) {
673
672
  return NextResponse.json(existingUserResult);
674
673
  }
675
674
 
676
- // 如果不存在用户且不允许创建,返回404
675
+ // If the user does not exist and creation is disabled, return 404.
677
676
  if (!options.createIfNotExists) {
678
677
  return createErrorResponse('User not found', 404);
679
678
  }
@@ -689,7 +688,7 @@ async function handleFingerprintRequest(request: NextRequest, options: { createI
689
688
  console.log(`Created new anonymous user ${anonymousInitResult.user.userId} with fingerprint ${fingerprintId}`);
690
689
  }
691
690
 
692
- // 返回创建结果
691
+ // Return the created or existing context.
693
692
  const response = createSuccessResponse({
694
693
  entities: {
695
694
  user: anonymousInitResult.user,
@@ -711,7 +710,7 @@ async function handleFingerprintRequest(request: NextRequest, options: { createI
711
710
  }
712
711
 
713
712
  /**
714
- * 匿名用户初始化API
713
+ * Anonymous user initialization API.
715
714
  * POST /api/user/anonymous/init
716
715
  */
717
716
  export async function POST(request: NextRequest) {
@@ -11,7 +11,7 @@ import { headers } from 'next/headers';
11
11
  import { NextRequest, NextResponse } from 'next/server';
12
12
  import { Webhook } from 'svix';
13
13
 
14
- // 定义Clerk Webhook事件类型
14
+ // Clerk webhook event type definition
15
15
  interface ClerkWebhookEvent {
16
16
  data: {
17
17
  id: string;
@@ -41,12 +41,12 @@ interface ClerkWebhookEvent {
41
41
  }
42
42
  export async function POST(request: NextRequest) {
43
43
  try {
44
- // 获取原始请求体
44
+ // Read the raw request body.
45
45
  const rawBody = await request.text();
46
46
 
47
47
  let event: ClerkWebhookEvent;
48
48
 
49
- // 开发环境跳过签名校验
49
+ // Skip signature verification in development.
50
50
  if (process.env.NODE_ENV === 'development') {
51
51
  console.log('Development mode: skipping webhook signature verification');
52
52
  try {
@@ -59,13 +59,13 @@ export async function POST(request: NextRequest) {
59
59
  );
60
60
  }
61
61
  } else {
62
- // 生产环境进行签名校验
62
+ // Verify the signature in production.
63
63
  const headerPayload = await headers();
64
64
  const svix_id = headerPayload.get('svix-id');
65
65
  const svix_timestamp = headerPayload.get('svix-timestamp');
66
66
  const svix_signature = headerPayload.get('svix-signature');
67
67
 
68
- // 如果缺少必要的header,返回错误
68
+ // Reject requests missing required headers.
69
69
  if (!svix_id || !svix_timestamp || !svix_signature) {
70
70
  return NextResponse.json(
71
71
  { error: 'Missing webhook headers' },
@@ -73,7 +73,7 @@ export async function POST(request: NextRequest) {
73
73
  );
74
74
  }
75
75
 
76
- // 获取webhook signing secret
76
+ // Load the webhook signing secret.
77
77
  const webhookSecret = process.env.CLERK_WEBHOOK_SECRET;
78
78
  if (!webhookSecret) {
79
79
  console.error('CLERK_WEBHOOK_SECRET is not configured');
@@ -83,7 +83,7 @@ export async function POST(request: NextRequest) {
83
83
  );
84
84
  }
85
85
 
86
- // 验证webhook签名
86
+ // Verify the webhook signature.
87
87
  try {
88
88
  const wh = new Webhook(webhookSecret);
89
89
  event = wh.verify(rawBody, {
@@ -110,7 +110,7 @@ export async function POST(request: NextRequest) {
110
110
  let processingResult = { success: true, message: 'Event processed successfully' };
111
111
 
112
112
  try {
113
- // 处理不同的事件类型
113
+ // Dispatch by event type.
114
114
  const { type } = event;
115
115
 
116
116
  switch (type) {
@@ -155,7 +155,7 @@ export async function POST(request: NextRequest) {
155
155
  }
156
156
 
157
157
  /**
158
- * 处理用户创建事件
158
+ * Handle the user.created event.
159
159
  */
160
160
  async function handleUserCreated(event: ClerkWebhookEvent) {
161
161
  const { data } = event;
@@ -174,7 +174,7 @@ async function handleUserCreated(event: ClerkWebhookEvent) {
174
174
  userName
175
175
  });
176
176
 
177
- // 检查必要参数
177
+ // Validate required fields.
178
178
  if (!fingerprintId) {
179
179
  console.error('Missing fingerprintId in webhook data, process flow error');
180
180
  return;
@@ -186,17 +186,17 @@ async function handleUserCreated(event: ClerkWebhookEvent) {
186
186
  }
187
187
 
188
188
  try {
189
- // 按fingerprintId查询该设备的所有未注销过的用户记录,注销过的记录相当于是死数据
189
+ // Find all non-deleted users for this device fingerprint.
190
190
  const existingUsers = await userService.findListByFingerprintId(fingerprintId);
191
191
  if (!existingUsers || existingUsers.length === 0) {
192
192
  console.error('Invalid fingerprintId in webhook data, process flow error');
193
193
  return;
194
194
  }
195
195
 
196
- // 查找email相同的记录
196
+ // Find an existing user with the same email.
197
197
  const sameEmailUser = existingUsers.find(user => user.email === email);
198
198
  if (sameEmailUser) {
199
- // 同一账号,检查是否需要更新clerkUserId
199
+ // Same account; update clerkUserId if needed.
200
200
  if (sameEmailUser.clerkUserId !== clerkUserId) {
201
201
  await userService.updateUser(sameEmailUser.userId, { clerkUserId, userName: userName, status: UserStatus.REGISTERED });
202
202
  console.log(`Updated clerkUserId for user ${sameEmailUser.userId}`);
@@ -206,16 +206,16 @@ async function handleUserCreated(event: ClerkWebhookEvent) {
206
206
  return;
207
207
  }
208
208
 
209
- // 查找匿名用户(email为空且clerkUserId为空)
209
+ // Find an anonymous user with no email or clerkUserId.
210
210
  const anonymousUser = existingUsers.find(user => !user.email && !user.clerkUserId && user.status === UserStatus.ANONYMOUS );
211
211
  if (anonymousUser) {
212
- // 匿名用户升级
212
+ // Upgrade the anonymous user.
213
213
  await userAggregateService.upgradeToRegistered(anonymousUser.userId, email, clerkUserId, userName);
214
214
  console.log(`Successfully upgraded anonymous user ${anonymousUser.userId} to registered user`);
215
215
  return;
216
216
  }
217
217
 
218
- // 同设备新账号,创建新用户
218
+ // New account on the same device; create a new user.
219
219
  await userAggregateService.createNewRegisteredUser(clerkUserId, email, fingerprintId, userName);
220
220
  console.log(`Created new user for device ${fingerprintId} with email ${email}`);
221
221
 
@@ -226,7 +226,7 @@ async function handleUserCreated(event: ClerkWebhookEvent) {
226
226
  }
227
227
 
228
228
  /**
229
- * 处理用户删除事件
229
+ * Handle the user.deleted event.
230
230
  */
231
231
  async function handleUserDeleted(event: ClerkWebhookEvent) {
232
232
  const { data } = event;
@@ -4,9 +4,6 @@ import { userService } from '../services/database/index';
4
4
  import { User } from '../services/database/prisma-model-type';
5
5
  import { AUTH_ERRORS, AUTH_HEADERS, type AuthProvider, type ProviderIdentity } from './auth-shared';
6
6
 
7
- /**
8
- * 认证结果类型
9
- */
10
7
  export interface AuthResult {
11
8
  userId: string;
12
9
  user: User;
@@ -15,7 +12,7 @@ export interface AuthResult {
15
12
  }
16
13
 
17
14
  /**
18
- * 从中间件设置的 Clerk ID 获取完整用户信息
15
+ * Fetch User's info from header field by Middleware
19
16
  */
20
17
  export async function getAuthenticatedUser(req: NextRequest): Promise<AuthResult> {
21
18
  try {
@@ -43,7 +40,7 @@ export async function getAuthenticatedUser(req: NextRequest): Promise<AuthResult
43
40
  }
44
41
 
45
42
  /**
46
- * 要求用户必须已认证,返回用户ID
43
+ * Require Auth, success back user's id
47
44
  */
48
45
  export async function requireAuth(req: NextRequest): Promise<string> {
49
46
  const auth = await getAuthenticatedUser(req);
@@ -51,15 +48,15 @@ export async function requireAuth(req: NextRequest): Promise<string> {
51
48
  }
52
49
 
53
50
  /**
54
- * 要求用户必须已认证,返回完整用户信息
51
+ * Require Auth, success back user's info
55
52
  */
56
53
  export async function requireAuthWithUser(req: NextRequest): Promise<AuthResult> {
57
54
  return await getAuthenticatedUser(req);
58
55
  }
59
56
 
60
57
  /**
61
- * 服务端场景下获取当前已认证身份(如果存在)
62
- * 适用于只依赖登录态、不需要查询业务用户的逻辑
58
+ * Only use in server side
59
+ * Server Component / Server Action, just need user's login status
63
60
  */
64
61
  export async function getOptionalServerAuthIdentity(): Promise<ProviderIdentity | null> {
65
62
  try {
@@ -79,8 +76,8 @@ export async function getOptionalServerAuthIdentity(): Promise<ProviderIdentity
79
76
  }
80
77
 
81
78
  /**
82
- * 服务端场景下获取当前已认证用户(如果存在)
83
- * 适用于 Server Component / Server Action 中基于登录态控制展示的逻辑
79
+ * Only use in server side
80
+ * Server Component / Server Action, need user's login status and user's data, will check db
84
81
  */
85
82
  export async function getOptionalServerAuthUser(): Promise<AuthResult | null> {
86
83
  try {
@@ -107,7 +104,7 @@ export async function getOptionalServerAuthUser(): Promise<AuthResult | null> {
107
104
  }
108
105
 
109
106
  /**
110
- * API Route版本的认证工具函数
107
+ * API Route Auth Util
111
108
  */
112
109
  export class ApiAuthUtils {
113
110
  private req: NextRequest;
@@ -116,23 +113,14 @@ export class ApiAuthUtils {
116
113
  this.req = req;
117
114
  }
118
115
 
119
- /**
120
- * 要求用户必须已认证,返回用户ID
121
- */
122
116
  async requireAuth(): Promise<string> {
123
117
  return await requireAuth(this.req);
124
118
  }
125
119
 
126
- /**
127
- * 要求用户必须已认证,返回完整用户信息
128
- */
129
120
  async requireAuthWithUser(): Promise<AuthResult> {
130
121
  return await requireAuthWithUser(this.req);
131
122
  }
132
123
 
133
- /**
134
- * 获取用户ID(如果已认证)
135
- */
136
124
  async getUserId(): Promise<string | null> {
137
125
  try {
138
126
  const auth = await getAuthenticatedUser(this.req);
@@ -142,9 +130,6 @@ export class ApiAuthUtils {
142
130
  }
143
131
  }
144
132
 
145
- /**
146
- * 获取完整用户信息(如果已认证)
147
- */
148
133
  async getUser(): Promise<AuthResult | null> {
149
134
  try {
150
135
  return await getAuthenticatedUser(this.req);
@@ -12,7 +12,7 @@ export const moneyPriceConfig: MoneyPriceConfig = {
12
12
  stripe: {
13
13
  provider: 'stripe',
14
14
  enabled: true,
15
- // 订阅模式产品
15
+ // Subscription products
16
16
  subscriptionProducts: {
17
17
  F1: {
18
18
  key: 'F1',
@@ -70,7 +70,7 @@ export const moneyPriceConfig: MoneyPriceConfig = {
70
70
  }
71
71
  }
72
72
  },
73
- // 积分包产品
73
+ // Credit pack products
74
74
  creditPackProducts: {
75
75
  F1: {
76
76
  key: 'F1',
@@ -106,56 +106,56 @@ export const moneyPriceConfig: MoneyPriceConfig = {
106
106
  }
107
107
  };
108
108
 
109
- // ============ 应用层wrapper - 隐藏moneyPriceConfig细节 ============
109
+ // ============ Application-level wrappers that hide moneyPriceConfig details ============
110
110
 
111
111
  /**
112
- * 获取当前激活的支付供应商配置
112
+ * Get the currently active payment provider configuration.
113
113
  *
114
- * 🔒 安全设计:
115
- * - wrapper函数隐藏moneyPriceConfig
116
- * - util层负责从config中提取激活的provider配置
117
- * - 外部只能通过这个wrapper访问,看不到config对象
114
+ * Security design:
115
+ * - Wrapper functions keep moneyPriceConfig private.
116
+ * - Utility functions extract the active provider configuration from the config.
117
+ * - External callers can access only this wrapper, not the full config object.
118
118
  *
119
- * @returns 当前激活的支付供应商配置
119
+ * @returns The currently active payment provider configuration.
120
120
  */
121
121
  export function getActiveProviderConfig(): PaymentProviderConfig {
122
122
  return getActiveProviderConfigUtil(moneyPriceConfig);
123
123
  }
124
124
 
125
125
  /**
126
- * 根据 priceId 获取对应的积分数量
126
+ * Get the credit amount for a price ID.
127
127
  *
128
- * 🔒 安全设计:
129
- * - wrapper函数隐藏moneyPriceConfig
130
- * - util层负责解析config并提取结果
131
- * - 外部只能通过这个wrapper访问,看不到config对象
128
+ * Security design:
129
+ * - Wrapper functions keep moneyPriceConfig private.
130
+ * - Utility functions parse the config and extract the result.
131
+ * - External callers can access only this wrapper, not the full config object.
132
132
  *
133
- * @param priceId - 查询的价格ID
134
- * @param _provider - 保留参数(向后兼容),暂未使用
135
- * @returns 对应的积分数量,或null
133
+ * @param priceId - Price ID to query.
134
+ * @param _provider - Reserved for backward compatibility; currently unused.
135
+ * @returns The matching credit amount, or null.
136
136
  */
137
137
  export function getCreditsFromPriceId(priceId?: string, _provider?: string): number | null {
138
138
  return getCreditsFromPriceIdUtil(priceId, moneyPriceConfig);
139
139
  }
140
140
 
141
141
  /**
142
- * 根据查询参数获取价格配置
142
+ * Get price configuration by query parameters.
143
143
  *
144
- * 支持三种查询方式:
145
- * 1. priceId 查询:getPriceConfig(priceId='price_xxx')
146
- * 2. plan billingType 查询:getPriceConfig(undefined, 'P2', 'monthly')
147
- * 3. plan 查询:getPriceConfig(undefined, 'P2')
144
+ * Supported query modes:
145
+ * 1. By priceId: getPriceConfig(priceId='price_xxx')
146
+ * 2. By plan and billingType: getPriceConfig(undefined, 'P2', 'monthly')
147
+ * 3. By plan: getPriceConfig(undefined, 'P2')
148
148
  *
149
- * 🔒 安全设计:
150
- * - wrapper函数隐藏moneyPriceConfig
151
- * - util层负责解析config并提取匹配的结果
152
- * - 外部只能通过这个wrapper访问,看不到config对象
149
+ * Security design:
150
+ * - Wrapper functions keep moneyPriceConfig private.
151
+ * - Utility functions parse the config and extract the matching result.
152
+ * - External callers can access only this wrapper, not the full config object.
153
153
  *
154
- * @param priceId - 查询的价格ID(可选)
155
- * @param plan - 查询的套餐名称如'P2''U3'(可选)
156
- * @param billingType - 查询的计费类型如'monthly''yearly'(可选)
157
- * @param _provider - 保留参数(向后兼容),暂未使用
158
- * @returns 匹配的价格配置,包含计算好的元数据(priceNamedescriptioninterval
154
+ * @param priceId - Optional price ID to query.
155
+ * @param plan - Optional plan name, such as 'P2' or 'U3'.
156
+ * @param billingType - Optional billing type, such as 'monthly' or 'yearly'.
157
+ * @param _provider - Reserved for backward compatibility; currently unused.
158
+ * @returns The matching price config with derived metadata: priceName, description, and interval.
159
159
  */
160
160
  export function getPriceConfig(
161
161
  priceId?: string,
@@ -165,4 +165,3 @@ export function getPriceConfig(
165
165
  ): (EnhancePricePlan & { priceName: string; description: string; interval?: string }) | null {
166
166
  return getPriceConfigUtil(priceId, plan, billingType, moneyPriceConfig);
167
167
  }
168
-