sa2kit 3.2.1 → 3.2.2

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.
@@ -5,6 +5,8 @@ require('../../../chunk-NSBPE2FW.js');
5
5
  var betterAuth = require('better-auth');
6
6
  var drizzleAdapter = require('@better-auth/drizzle-adapter');
7
7
  var plugins = require('better-auth/plugins');
8
+ var drizzleOrm = require('drizzle-orm');
9
+ var crypto = require('better-auth/crypto');
8
10
  var nextJs = require('better-auth/next-js');
9
11
 
10
12
  // src/common/auth/server/plugins/dev-otp.ts
@@ -18,6 +20,70 @@ function createDevOtpLogger(enabled) {
18
20
  }
19
21
  var defaultPhoneValidator = (phoneNumber2) => /^1\d{10}$/.test(phoneNumber2);
20
22
  var defaultTempEmailFromPhone = (phoneNumber2) => `${phoneNumber2.replace(/\D/g, "")}@phone.sa2kit.local`;
23
+ function createId() {
24
+ return globalThis.crypto?.randomUUID?.() ?? `acc_${Date.now()}_${Math.random().toString(36).slice(2)}`;
25
+ }
26
+ async function upsertCredentialPassword(db, userId, plainPassword) {
27
+ const database = db;
28
+ const now = /* @__PURE__ */ new Date();
29
+ const passwordHash = await crypto.hashPassword(plainPassword);
30
+ const existing = await database.select({ id: chunkLGHUCQIU_js.account.id }).from(chunkLGHUCQIU_js.account).where(drizzleOrm.and(drizzleOrm.eq(chunkLGHUCQIU_js.account.userId, userId), drizzleOrm.eq(chunkLGHUCQIU_js.account.providerId, "credential"))).limit(1);
31
+ if (existing[0]) {
32
+ await database.update(chunkLGHUCQIU_js.account).set({ password: passwordHash, updatedAt: now }).where(drizzleOrm.eq(chunkLGHUCQIU_js.account.id, existing[0].id));
33
+ return;
34
+ }
35
+ await database.insert(chunkLGHUCQIU_js.account).values({
36
+ id: createId(),
37
+ accountId: userId,
38
+ providerId: "credential",
39
+ userId,
40
+ password: passwordHash,
41
+ createdAt: now,
42
+ updatedAt: now
43
+ });
44
+ }
45
+
46
+ // src/common/auth/server/phone-signup-intent.ts
47
+ var pendingByPhone = /* @__PURE__ */ new Map();
48
+ var TTL_MS = 5 * 60 * 1e3;
49
+ function normalizePhone(phoneNumber2) {
50
+ return phoneNumber2.replace(/\D/g, "");
51
+ }
52
+ function stashPhoneSignupPassword(phoneNumber2, password) {
53
+ pendingByPhone.set(normalizePhone(phoneNumber2), {
54
+ password,
55
+ expiresAt: Date.now() + TTL_MS
56
+ });
57
+ }
58
+ function consumePhoneSignupPassword(phoneNumber2) {
59
+ const key = normalizePhone(phoneNumber2);
60
+ const entry = pendingByPhone.get(key);
61
+ pendingByPhone.delete(key);
62
+ if (!entry) return void 0;
63
+ if (entry.expiresAt < Date.now()) return void 0;
64
+ return entry.password;
65
+ }
66
+ async function handlePhoneSignupIntentRequest(request) {
67
+ if (request.method !== "POST") {
68
+ return new Response("Method Not Allowed", { status: 405 });
69
+ }
70
+ let body;
71
+ try {
72
+ body = await request.json();
73
+ } catch {
74
+ return Response.json({ error: "invalid_json" }, { status: 400 });
75
+ }
76
+ const phoneNumber2 = String(body.phoneNumber ?? "").trim();
77
+ const password = String(body.password ?? "");
78
+ if (!defaultPhoneValidator(phoneNumber2)) {
79
+ return Response.json({ error: "invalid_phone" }, { status: 400 });
80
+ }
81
+ if (password.length < 6) {
82
+ return Response.json({ error: "invalid_password" }, { status: 400 });
83
+ }
84
+ stashPhoneSignupPassword(phoneNumber2, password);
85
+ return Response.json({ ok: true });
86
+ }
21
87
 
22
88
  // src/common/auth/server/create-auth.ts
23
89
  function createSa2kitAuth(config) {
@@ -65,7 +131,13 @@ function createSa2kitAuth(config) {
65
131
  async sendOTP({ phoneNumber: phone, code }) {
66
132
  devLog?.("sms", phone, code);
67
133
  if (config.sms?.sendOTP) {
68
- void config.sms.sendOTP(phone, code);
134
+ await config.sms.sendOTP(phone, code);
135
+ }
136
+ },
137
+ async callbackOnVerification({ phoneNumber: phone, user: user2 }) {
138
+ const pendingPassword = consumePhoneSignupPassword(phone);
139
+ if (pendingPassword && user2?.id) {
140
+ await upsertCredentialPassword(config.db, String(user2.id), pendingPassword);
69
141
  }
70
142
  },
71
143
  signUpOnVerification: {
@@ -77,6 +149,530 @@ function createSa2kitAuth(config) {
77
149
  });
78
150
  return auth;
79
151
  }
152
+
153
+ // src/common/auth/server/env/auth-env-catalog.ts
154
+ var AUTH_FEATURES = [
155
+ {
156
+ id: "core",
157
+ name: "\u6838\u5FC3\u8BA4\u8BC1",
158
+ description: "Better Auth \u670D\u52A1\u7AEF\u57FA\u7840\u80FD\u529B\uFF08\u4F1A\u8BDD\u3001Cookie\u3001API \u8DEF\u7531\uFF09",
159
+ envKeys: ["BETTER_AUTH_SECRET", "BETTER_AUTH_URL"]
160
+ },
161
+ {
162
+ id: "client",
163
+ name: "Web \u5BA2\u6237\u7AEF",
164
+ description: "\u6D4F\u89C8\u5668\u7AEF AuthProvider / \u767B\u5F55\u5F39\u7A97\u8BF7\u6C42\u6B63\u786E origin",
165
+ envKeys: ["NEXT_PUBLIC_APP_URL"]
166
+ },
167
+ {
168
+ id: "trusted_origins",
169
+ name: "\u8DE8\u57DF\u4FE1\u4EFB\u6E90",
170
+ description: "\u591A\u57DF\u540D / \u9884\u89C8\u73AF\u5883 CORS \u4E0E Cookie",
171
+ envKeys: ["BETTER_AUTH_TRUSTED_ORIGINS"]
172
+ },
173
+ {
174
+ id: "sms_phone_otp",
175
+ name: "\u624B\u673A\u77ED\u4FE1 OTP",
176
+ description: "\u624B\u673A\u53F7\u6CE8\u518C / \u767B\u5F55 / \u627E\u56DE\u5BC6\u7801\u7684\u77ED\u4FE1\u9A8C\u8BC1\u7801",
177
+ envKeys: [
178
+ "SA2KIT_SMS_PROVIDER",
179
+ "ALIYUN_SMS_ACCESS_KEY_ID",
180
+ "ALIYUN_SMS_ACCESS_KEY_SECRET",
181
+ "ALIYUN_SMS_SIGN_NAME",
182
+ "ALIYUN_SMS_TEMPLATE_CODE",
183
+ "ALIYUN_SMS_COUNTRY_CODE",
184
+ "ALIYUN_SMS_CODE_VALID_MINUTES",
185
+ "ALIYUN_SMS_ENDPOINT"
186
+ ]
187
+ },
188
+ {
189
+ id: "email_otp",
190
+ name: "\u90AE\u7BB1 OTP",
191
+ description: "\u90AE\u7BB1\u9A8C\u8BC1\u7801\uFF08\u9700\u5728 createSa2kitAuth \u4F20\u5165 email.sendVerificationOTP\uFF09",
192
+ envKeys: ["SA2KIT_EMAIL_PROVIDER"]
193
+ },
194
+ {
195
+ id: "database",
196
+ name: "\u6570\u636E\u5E93",
197
+ description: "Drizzle + PostgreSQL \u6301\u4E45\u5316 user/session\uFF08\u7531\u5BBF\u4E3B\u9879\u76EE\u914D\u7F6E\uFF09",
198
+ envKeys: ["DATABASE_URL"]
199
+ }
200
+ ];
201
+ var AUTH_ENV_CATALOG = [
202
+ {
203
+ key: "BETTER_AUTH_SECRET",
204
+ featureId: "core",
205
+ required: true,
206
+ placement: ["env_file", "github_secret", "runtime_env"],
207
+ description: "Better Auth \u7B7E\u540D\u5BC6\u94A5\uFF0C\u81F3\u5C11 32 \u5B57\u7B26\u3002\u53EF\u517C\u5BB9\u8BFB\u53D6 NEXTAUTH_SECRET\u3002",
208
+ example: "openssl rand -base64 32",
209
+ secret: true
210
+ },
211
+ {
212
+ key: "BETTER_AUTH_URL",
213
+ featureId: "core",
214
+ required: true,
215
+ placement: ["env_file", "github_secret", "runtime_env"],
216
+ description: "\u5BF9\u5916\u53EF\u8BBF\u95EE\u7684\u7AD9\u70B9 URL\uFF08\u542B\u534F\u8BAE\u4E0E\u7AEF\u53E3\uFF09\u3002\u53EF\u56DE\u9000 NEXT_PUBLIC_APP_URL / NEXTAUTH_URL\u3002",
217
+ example: "https://example.com"
218
+ },
219
+ {
220
+ key: "NEXT_PUBLIC_APP_URL",
221
+ featureId: "client",
222
+ required: true,
223
+ placement: ["env_file", "github_secret", "runtime_env"],
224
+ description: "\u6D4F\u89C8\u5668\u7AEF auth client \u7684 baseURL\u3002",
225
+ example: "https://example.com"
226
+ },
227
+ {
228
+ key: "BETTER_AUTH_TRUSTED_ORIGINS",
229
+ featureId: "trusted_origins",
230
+ required: false,
231
+ placement: ["env_file", "github_secret", "runtime_env"],
232
+ description: "\u9017\u53F7\u5206\u9694\u7684\u53EF\u4FE1 origin \u5217\u8868\uFF0C\u7528\u4E8E\u9884\u89C8\u57DF / \u591A\u57DF\u540D\u90E8\u7F72\u3002",
233
+ example: "https://example.com,https://www.example.com"
234
+ },
235
+ {
236
+ key: "SA2KIT_SMS_PROVIDER",
237
+ featureId: "sms_phone_otp",
238
+ required: false,
239
+ placement: ["env_file", "github_secret", "runtime_env"],
240
+ description: "\u77ED\u4FE1 provider\uFF1Aconsole\uFF08\u5F00\u53D1\u65E5\u5FD7\uFF09| aliyun-pnvs\uFF08\u751F\u4EA7\uFF09| none\u3002\u5F00\u53D1\u9ED8\u8BA4 console\uFF0C\u751F\u4EA7\u672A\u914D\u7F6E\u5219\u65E0\u6CD5\u53D1\u77ED\u4FE1\u3002",
241
+ example: "aliyun-pnvs"
242
+ },
243
+ {
244
+ key: "ALIYUN_SMS_ACCESS_KEY_ID",
245
+ featureId: "sms_phone_otp",
246
+ required: false,
247
+ placement: ["env_file", "github_secret", "runtime_env"],
248
+ description: "\u963F\u91CC\u4E91 RAM AccessKey ID\uFF08\u77ED\u4FE1\u8BA4\u8BC1 SendSmsVerifyCode\uFF09\u3002",
249
+ secret: true
250
+ },
251
+ {
252
+ key: "ALIYUN_SMS_ACCESS_KEY_SECRET",
253
+ featureId: "sms_phone_otp",
254
+ required: false,
255
+ placement: ["env_file", "github_secret", "runtime_env"],
256
+ description: "\u963F\u91CC\u4E91 RAM AccessKey Secret\u3002",
257
+ secret: true
258
+ },
259
+ {
260
+ key: "ALIYUN_SMS_SIGN_NAME",
261
+ featureId: "sms_phone_otp",
262
+ required: false,
263
+ placement: ["env_file", "github_secret", "runtime_env"],
264
+ description: "\u77ED\u4FE1\u8BA4\u8BC1\u63A7\u5236\u53F0\u8D60\u9001/\u7533\u8BF7\u7684\u7B7E\u540D\u540D\u79F0\u3002",
265
+ example: "\u901F\u901A\u4E92\u8054\u9A8C\u8BC1\u7801"
266
+ },
267
+ {
268
+ key: "ALIYUN_SMS_TEMPLATE_CODE",
269
+ featureId: "sms_phone_otp",
270
+ required: false,
271
+ placement: ["env_file", "github_secret", "runtime_env"],
272
+ description: "\u77ED\u4FE1\u8BA4\u8BC1\u63A7\u5236\u53F0\u8D60\u9001/\u7533\u8BF7\u7684\u6A21\u677F CODE\u3002",
273
+ example: "100001"
274
+ },
275
+ {
276
+ key: "ALIYUN_SMS_COUNTRY_CODE",
277
+ featureId: "sms_phone_otp",
278
+ required: false,
279
+ placement: ["env_file", "runtime_env"],
280
+ description: "\u56FD\u5BB6\u7801\uFF0C\u9ED8\u8BA4 86\uFF08\u4E2D\u56FD\u5927\u9646\uFF09\u3002",
281
+ example: "86"
282
+ },
283
+ {
284
+ key: "ALIYUN_SMS_CODE_VALID_MINUTES",
285
+ featureId: "sms_phone_otp",
286
+ required: false,
287
+ placement: ["env_file", "runtime_env"],
288
+ description: "\u77ED\u4FE1\u6A21\u677F\u4E2D\u5C55\u793A\u7684\u6709\u6548\u5206\u949F\u6570\uFF0C\u9ED8\u8BA4 5\uFF08\u4E0E Better Auth OTP \u6709\u6548\u671F\u72EC\u7ACB\uFF09\u3002",
289
+ example: "5"
290
+ },
291
+ {
292
+ key: "ALIYUN_SMS_ENDPOINT",
293
+ featureId: "sms_phone_otp",
294
+ required: false,
295
+ placement: ["env_file", "runtime_env"],
296
+ description: "\u53EF\u9009\uFF0C\u9ED8\u8BA4 https://dypnsapi.aliyuncs.com"
297
+ },
298
+ {
299
+ key: "SA2KIT_EMAIL_PROVIDER",
300
+ featureId: "email_otp",
301
+ required: false,
302
+ placement: ["env_file", "github_secret", "runtime_env"],
303
+ description: "\u90AE\u7BB1 OTP \u7531\u5BBF\u4E3B\u5B9E\u73B0 config.email.sendVerificationOTP\uFF1B\u6B64\u53D8\u91CF\u4EC5\u7528\u4E8E env \u68C0\u67E5\u63D0\u793A\u3002",
304
+ example: "resend | smtp | console"
305
+ },
306
+ {
307
+ key: "DATABASE_URL",
308
+ featureId: "database",
309
+ required: true,
310
+ placement: ["env_file", "github_secret", "runtime_env", "database"],
311
+ description: "PostgreSQL \u8FDE\u63A5\u4E32\uFF08\u5BBF\u4E3B\u9879\u76EE Drizzle \u4F7F\u7528\uFF0C\u975E sa2kit \u76F4\u63A5\u8BFB\u53D6\uFF09\u3002",
312
+ secret: true
313
+ },
314
+ {
315
+ key: "SA2KIT_AUTH_LOG_OTP",
316
+ featureId: "sms_phone_otp",
317
+ required: false,
318
+ placement: ["env_file", "runtime_env"],
319
+ description: "\u8BBE\u4E3A 1 \u65F6\u5728\u975E\u751F\u4EA7\u73AF\u5883\u5C06 OTP \u6253\u5370\u5230\u670D\u52A1\u7AEF\u65E5\u5FD7\uFF1B\u751F\u4EA7\u73AF\u5883\u5FFD\u7565\u3002\u8BBE\u4E3A 0 \u53EF\u5173\u95ED\u5F00\u53D1\u65E5\u5FD7\u3002",
320
+ example: "1"
321
+ }
322
+ ];
323
+ var AUTH_ENV_ALIASES = {
324
+ BETTER_AUTH_SECRET: ["NEXTAUTH_SECRET"],
325
+ BETTER_AUTH_URL: ["NEXT_PUBLIC_APP_URL", "NEXTAUTH_URL"]
326
+ };
327
+
328
+ // src/common/auth/server/sms/providers/aliyun-pnvs.ts
329
+ async function loadPopCore() {
330
+ try {
331
+ const mod = await import('@alicloud/pop-core');
332
+ return mod.default ?? mod;
333
+ } catch {
334
+ throw new Error(
335
+ "\u542F\u7528 SA2KIT_SMS_PROVIDER=aliyun-pnvs \u9700\u8981\u5B89\u88C5 @alicloud/pop-core\uFF1Apnpm add @alicloud/pop-core"
336
+ );
337
+ }
338
+ }
339
+ function createAliyunPnvsSmsProvider(config) {
340
+ return {
341
+ async sendOTP(phoneNumber2, code) {
342
+ const Core = await loadPopCore();
343
+ const client = new Core({
344
+ accessKeyId: config.accessKeyId,
345
+ accessKeySecret: config.accessKeySecret,
346
+ endpoint: config.endpoint ?? "https://dypnsapi.aliyuncs.com",
347
+ apiVersion: "2017-05-25"
348
+ });
349
+ const minutes = String(config.codeValidMinutes ?? 5);
350
+ const result = await client.request(
351
+ "SendSmsVerifyCode",
352
+ {
353
+ PhoneNumber: phoneNumber2,
354
+ CountryCode: config.countryCode ?? "86",
355
+ SignName: config.signName,
356
+ TemplateCode: config.templateCode,
357
+ TemplateParam: JSON.stringify({ code, min: minutes })
358
+ },
359
+ { method: "POST" }
360
+ );
361
+ if (result.Code !== "OK" && result.Success !== true) {
362
+ throw new Error(
363
+ `\u963F\u91CC\u4E91\u77ED\u4FE1\u8BA4\u8BC1\u53D1\u9001\u5931\u8D25: ${result.Message ?? result.Code ?? "unknown error"}`
364
+ );
365
+ }
366
+ }
367
+ };
368
+ }
369
+
370
+ // src/common/auth/server/sms/providers/console.ts
371
+ function createConsoleSmsProvider() {
372
+ return {
373
+ async sendOTP(phoneNumber2, code) {
374
+ console.info(`[sa2kit/auth][sms][console] ${phoneNumber2} => ${code}`);
375
+ }
376
+ };
377
+ }
378
+
379
+ // src/common/auth/server/sms/create-sms-provider-from-env.ts
380
+ function readEnv(key) {
381
+ const value = process.env[key];
382
+ return value && value.trim().length > 0 ? value.trim() : void 0;
383
+ }
384
+ function resolveSmsProviderId(explicit) {
385
+ if (explicit) return explicit;
386
+ const fromEnv = readEnv("SA2KIT_SMS_PROVIDER");
387
+ if (fromEnv) return fromEnv;
388
+ if (process.env.NODE_ENV !== "production") return "console";
389
+ return void 0;
390
+ }
391
+ function createSmsProviderFromEnv(options) {
392
+ const providerId = resolveSmsProviderId(options?.providerId);
393
+ if (!providerId || providerId === "none") return void 0;
394
+ if (providerId === "console") {
395
+ return createConsoleSmsProvider();
396
+ }
397
+ if (providerId === "aliyun-pnvs") {
398
+ const accessKeyId = readEnv("ALIYUN_SMS_ACCESS_KEY_ID");
399
+ const accessKeySecret = readEnv("ALIYUN_SMS_ACCESS_KEY_SECRET");
400
+ const signName = readEnv("ALIYUN_SMS_SIGN_NAME");
401
+ const templateCode = readEnv("ALIYUN_SMS_TEMPLATE_CODE");
402
+ if (!accessKeyId || !accessKeySecret || !signName || !templateCode) {
403
+ throw new Error(
404
+ "SA2KIT_SMS_PROVIDER=aliyun-pnvs \u9700\u8981 ALIYUN_SMS_ACCESS_KEY_ID\u3001ALIYUN_SMS_ACCESS_KEY_SECRET\u3001ALIYUN_SMS_SIGN_NAME\u3001ALIYUN_SMS_TEMPLATE_CODE"
405
+ );
406
+ }
407
+ const codeValidMinutes = Number.parseInt(readEnv("ALIYUN_SMS_CODE_VALID_MINUTES") ?? "5", 10);
408
+ return createAliyunPnvsSmsProvider({
409
+ accessKeyId,
410
+ accessKeySecret,
411
+ signName,
412
+ templateCode,
413
+ countryCode: readEnv("ALIYUN_SMS_COUNTRY_CODE") ?? "86",
414
+ codeValidMinutes: Number.isFinite(codeValidMinutes) ? codeValidMinutes : 5,
415
+ endpoint: readEnv("ALIYUN_SMS_ENDPOINT")
416
+ });
417
+ }
418
+ throw new Error(`\u672A\u77E5\u7684 SA2KIT_SMS_PROVIDER: ${providerId}`);
419
+ }
420
+
421
+ // src/common/auth/server/env/resolve-auth-env.ts
422
+ function readEnv2(key) {
423
+ const value = process.env[key];
424
+ return value && value.trim().length > 0 ? value.trim() : void 0;
425
+ }
426
+ function readEnvWithAliases(key) {
427
+ const direct = readEnv2(key);
428
+ if (direct) return direct;
429
+ for (const alias of AUTH_ENV_ALIASES[key] ?? []) {
430
+ const value = readEnv2(alias);
431
+ if (value) return value;
432
+ }
433
+ return void 0;
434
+ }
435
+ function parseTrustedOrigins(baseURL) {
436
+ const fromEnv = readEnv2("BETTER_AUTH_TRUSTED_ORIGINS");
437
+ const defaults = [baseURL, "http://localhost:3000", "http://127.0.0.1:3000"];
438
+ const extra = fromEnv ? fromEnv.split(",").map((item) => item.trim()).filter(Boolean) : [];
439
+ return [...defaults, ...extra].filter((origin, index, list) => list.indexOf(origin) === index);
440
+ }
441
+ function resolveAuthEnv(input) {
442
+ const baseURL = input.baseURL ?? readEnvWithAliases("BETTER_AUTH_URL") ?? readEnv2("NEXT_PUBLIC_APP_URL") ?? "http://localhost:3000";
443
+ const secret = input.secret ?? readEnvWithAliases("BETTER_AUTH_SECRET") ?? (process.env.NODE_ENV !== "production" ? "dev-better-auth-secret-min-32-chars!!" : void 0);
444
+ if (!secret || secret.length < 32) {
445
+ throw new Error("BETTER_AUTH_SECRET \u81F3\u5C11 32 \u5B57\u7B26\uFF08\u6216\u5F00\u53D1\u73AF\u5883\u4F7F\u7528\u9ED8\u8BA4 dev secret\uFF09");
446
+ }
447
+ const smsProvider = createSmsProviderFromEnv();
448
+ const sms = input.sms ?? (smsProvider ? { sendOTP: smsProvider.sendOTP.bind(smsProvider) } : void 0);
449
+ const config = {
450
+ db: input.db,
451
+ baseURL,
452
+ secret,
453
+ trustedOrigins: input.trustedOrigins ?? parseTrustedOrigins(baseURL),
454
+ basePath: input.basePath,
455
+ sms,
456
+ email: input.email,
457
+ phoneNumberValidator: input.phoneNumberValidator,
458
+ logOtpInDev: input.logOtpInDev ?? (process.env.SA2KIT_AUTH_LOG_OTP === "1" || process.env.NODE_ENV !== "production")
459
+ };
460
+ const envSnapshot = {
461
+ BETTER_AUTH_SECRET: readEnvWithAliases("BETTER_AUTH_SECRET") ? "[set]" : void 0,
462
+ BETTER_AUTH_URL: readEnvWithAliases("BETTER_AUTH_URL"),
463
+ NEXT_PUBLIC_APP_URL: readEnv2("NEXT_PUBLIC_APP_URL"),
464
+ BETTER_AUTH_TRUSTED_ORIGINS: readEnv2("BETTER_AUTH_TRUSTED_ORIGINS"),
465
+ SA2KIT_SMS_PROVIDER: String(resolveSmsProviderId() ?? ""),
466
+ ALIYUN_SMS_ACCESS_KEY_ID: readEnv2("ALIYUN_SMS_ACCESS_KEY_ID") ? "[set]" : void 0,
467
+ ALIYUN_SMS_ACCESS_KEY_SECRET: readEnv2("ALIYUN_SMS_ACCESS_KEY_SECRET") ? "[set]" : void 0,
468
+ ALIYUN_SMS_SIGN_NAME: readEnv2("ALIYUN_SMS_SIGN_NAME"),
469
+ ALIYUN_SMS_TEMPLATE_CODE: readEnv2("ALIYUN_SMS_TEMPLATE_CODE"),
470
+ SA2KIT_EMAIL_PROVIDER: readEnv2("SA2KIT_EMAIL_PROVIDER"),
471
+ DATABASE_URL: readEnv2("DATABASE_URL") ? "[set]" : void 0,
472
+ NODE_ENV: process.env.NODE_ENV
473
+ };
474
+ return { config, envSnapshot };
475
+ }
476
+
477
+ // src/common/auth/server/env/check-auth-env.ts
478
+ function isSet(snapshot, key) {
479
+ const value = snapshot[key];
480
+ return value !== void 0 && value !== "[unset]" && value.length > 0;
481
+ }
482
+ function featureById(id) {
483
+ return AUTH_FEATURES.find((f) => f.id === id);
484
+ }
485
+ function varsForFeature(featureId) {
486
+ return AUTH_ENV_CATALOG.filter((item) => item.featureId === featureId);
487
+ }
488
+ function checkAuthEnv(envSnapshot) {
489
+ const issues = [];
490
+ const enabledFeatures = [];
491
+ const disabledFeatures = [];
492
+ const checkRequiredFeature = (featureId, productionOnly = false) => {
493
+ const feature = featureById(featureId);
494
+ const requiredVars = varsForFeature(featureId).filter((item) => item.required);
495
+ const missing = requiredVars.filter((item) => !isSet(envSnapshot, item.key)).map((item) => item.key);
496
+ if (missing.length === 0) {
497
+ enabledFeatures.push(featureId);
498
+ return;
499
+ }
500
+ if (productionOnly && envSnapshot.NODE_ENV !== "production") {
501
+ disabledFeatures.push(featureId);
502
+ issues.push({
503
+ level: "info",
504
+ featureId,
505
+ featureName: feature.name,
506
+ message: `\u5F00\u53D1\u73AF\u5883\u53EF\u6682\u7F3A\uFF1A${missing.join(", ")}`,
507
+ missingKeys: missing
508
+ });
509
+ return;
510
+ }
511
+ disabledFeatures.push(featureId);
512
+ issues.push({
513
+ level: "error",
514
+ featureId,
515
+ featureName: feature.name,
516
+ message: `\u7F3A\u5C11\u5FC5\u9700\u73AF\u5883\u53D8\u91CF\uFF1A${missing.join(", ")}`,
517
+ missingKeys: missing
518
+ });
519
+ };
520
+ checkRequiredFeature("core");
521
+ checkRequiredFeature("client", true);
522
+ checkRequiredFeature("database", true);
523
+ const smsFeature = featureById("sms_phone_otp");
524
+ const smsProvider = envSnapshot.SA2KIT_SMS_PROVIDER;
525
+ if (smsProvider === "aliyun-pnvs") {
526
+ const aliyunKeys = [
527
+ "ALIYUN_SMS_ACCESS_KEY_ID",
528
+ "ALIYUN_SMS_ACCESS_KEY_SECRET",
529
+ "ALIYUN_SMS_SIGN_NAME",
530
+ "ALIYUN_SMS_TEMPLATE_CODE"
531
+ ];
532
+ const missing = aliyunKeys.filter((key) => !isSet(envSnapshot, key));
533
+ if (missing.length === 0) {
534
+ enabledFeatures.push("sms_phone_otp");
535
+ } else {
536
+ disabledFeatures.push("sms_phone_otp");
537
+ issues.push({
538
+ level: "error",
539
+ featureId: "sms_phone_otp",
540
+ featureName: smsFeature.name,
541
+ message: `SA2KIT_SMS_PROVIDER=aliyun-pnvs \u4F46\u7F3A\u5C11\uFF1A${missing.join(", ")}`,
542
+ missingKeys: missing,
543
+ hints: ["\u5728 GitHub Secrets \u6216 .env.production \u4E2D\u914D\u7F6E ALIYUN_SMS_*"]
544
+ });
545
+ }
546
+ } else if (smsProvider === "console") {
547
+ enabledFeatures.push("sms_phone_otp");
548
+ if (envSnapshot.NODE_ENV === "production") {
549
+ issues.push({
550
+ level: "warning",
551
+ featureId: "sms_phone_otp",
552
+ featureName: smsFeature.name,
553
+ message: "\u751F\u4EA7\u73AF\u5883\u4ECD\u5728\u4F7F\u7528 SA2KIT_SMS_PROVIDER=console\uFF0C\u9A8C\u8BC1\u7801\u4EC5\u8F93\u51FA\u5230\u65E5\u5FD7\uFF0C\u7528\u6237\u6536\u4E0D\u5230\u77ED\u4FE1\u3002",
554
+ hints: ["\u751F\u4EA7\u8BF7\u6539\u4E3A aliyun-pnvs \u5E76\u914D\u7F6E ALIYUN_SMS_*"]
555
+ });
556
+ }
557
+ } else {
558
+ disabledFeatures.push("sms_phone_otp");
559
+ issues.push({
560
+ level: envSnapshot.NODE_ENV === "production" ? "warning" : "info",
561
+ featureId: "sms_phone_otp",
562
+ featureName: smsFeature.name,
563
+ message: envSnapshot.NODE_ENV === "production" ? "\u672A\u914D\u7F6E\u77ED\u4FE1 provider\uFF0C\u624B\u673A\u53F7 OTP \u65E0\u6CD5\u9001\u8FBE\uFF08\u7528\u6237\u6536\u4E0D\u5230\u9A8C\u8BC1\u7801\uFF09\u3002" : "\u672A\u914D\u7F6E\u77ED\u4FE1 provider\uFF1B\u5F00\u53D1\u73AF\u5883\u53EF\u8BBE\u7F6E SA2KIT_SMS_PROVIDER=console \u5E76\u5728\u670D\u52A1\u7AEF\u65E5\u5FD7\u67E5\u770B OTP\u3002",
564
+ hints: [
565
+ "\u5F00\u53D1\uFF1ASA2KIT_SMS_PROVIDER=console",
566
+ "\u751F\u4EA7\uFF1ASA2KIT_SMS_PROVIDER=aliyun-pnvs + ALIYUN_SMS_*"
567
+ ]
568
+ });
569
+ }
570
+ if (isSet(envSnapshot, "SA2KIT_EMAIL_PROVIDER")) {
571
+ enabledFeatures.push("email_otp");
572
+ } else {
573
+ disabledFeatures.push("email_otp");
574
+ issues.push({
575
+ level: "info",
576
+ featureId: "email_otp",
577
+ featureName: featureById("email_otp").name,
578
+ message: "\u672A\u58F0\u660E SA2KIT_EMAIL_PROVIDER\uFF1B\u82E5\u9700\u90AE\u7BB1\u9A8C\u8BC1\u7801\u8BF7\u5728 createSa2kitAuth \u914D\u7F6E email.sendVerificationOTP\u3002"
579
+ });
580
+ }
581
+ if (isSet(envSnapshot, "BETTER_AUTH_TRUSTED_ORIGINS")) {
582
+ enabledFeatures.push("trusted_origins");
583
+ } else {
584
+ disabledFeatures.push("trusted_origins");
585
+ issues.push({
586
+ level: "info",
587
+ featureId: "trusted_origins",
588
+ featureName: featureById("trusted_origins").name,
589
+ message: "\u672A\u8BBE\u7F6E BETTER_AUTH_TRUSTED_ORIGINS\uFF0C\u4EC5\u4F7F\u7528 baseURL + localhost \u9ED8\u8BA4\u503C\u3002"
590
+ });
591
+ }
592
+ const ok = !issues.some((issue) => issue.level === "error");
593
+ return { ok, issues, enabledFeatures, disabledFeatures };
594
+ }
595
+ var loggedOnce = false;
596
+ function logAuthEnvReport(report, options) {
597
+ if (loggedOnce && !options?.force) return;
598
+ loggedOnce = true;
599
+ const lines = ["[sa2kit/auth] \u73AF\u5883\u914D\u7F6E\u68C0\u67E5"];
600
+ for (const issue of report.issues) {
601
+ const prefix = issue.level === "error" ? "\u2717" : issue.level === "warning" ? "\u26A0" : "\u25CB";
602
+ lines.push(`${prefix} ${issue.featureName}: ${issue.message}`);
603
+ if (issue.hints?.length) {
604
+ for (const hint of issue.hints) {
605
+ lines.push(` \u2192 ${hint}`);
606
+ }
607
+ }
608
+ }
609
+ if (report.issues.length === 0) {
610
+ lines.push("\u2713 \u6240\u6709\u5DF2\u542F\u7528\u80FD\u529B\u7684\u73AF\u5883\u53D8\u91CF\u5747\u5DF2\u5C31\u7EEA");
611
+ }
612
+ lines.push(` \u5DF2\u542F\u7528: ${report.enabledFeatures.join(", ") || "\u65E0"}`);
613
+ console.info(lines.join("\n"));
614
+ }
615
+ function formatAuthEnvSetupMarkdown(report) {
616
+ const lines = [
617
+ "# sa2kit Auth \u73AF\u5883\u53D8\u91CF",
618
+ "",
619
+ "\u5B8C\u6574\u8BF4\u660E\u89C1 sa2kit \u6587\u6863\uFF1A`docs/auth-env.md`",
620
+ "",
621
+ "## \u529F\u80FD \u2194 \u73AF\u5883\u53D8\u91CF",
622
+ ""
623
+ ];
624
+ for (const feature of AUTH_FEATURES) {
625
+ lines.push(`### ${feature.name} (\`${feature.id}\`)`);
626
+ lines.push("");
627
+ lines.push(feature.description);
628
+ lines.push("");
629
+ const vars = varsForFeature(feature.id);
630
+ lines.push("| \u53D8\u91CF | \u5FC5\u9700 | \u5B58\u653E\u4F4D\u7F6E | \u8BF4\u660E |");
631
+ lines.push("|------|------|----------|------|");
632
+ for (const item of vars) {
633
+ lines.push(
634
+ `| \`${item.key}\` | ${item.required ? "\u662F" : "\u5426"} | ${item.placement.join(", ")} | ${item.description} |`
635
+ );
636
+ }
637
+ lines.push("");
638
+ }
639
+ if (report) {
640
+ lines.push("## \u5F53\u524D\u68C0\u67E5\u7ED3\u679C");
641
+ lines.push("");
642
+ for (const issue of report.issues) {
643
+ lines.push(`- **${issue.featureName}** (${issue.level}): ${issue.message}`);
644
+ }
645
+ }
646
+ return lines.join("\n");
647
+ }
648
+ function checkAuthEnvFromProcessEnv(extraSnapshot) {
649
+ const snapshot = {
650
+ BETTER_AUTH_SECRET: process.env.BETTER_AUTH_SECRET || process.env.NEXTAUTH_SECRET ? "[set]" : void 0,
651
+ BETTER_AUTH_URL: process.env.BETTER_AUTH_URL ?? process.env.NEXT_PUBLIC_APP_URL,
652
+ NEXT_PUBLIC_APP_URL: process.env.NEXT_PUBLIC_APP_URL,
653
+ BETTER_AUTH_TRUSTED_ORIGINS: process.env.BETTER_AUTH_TRUSTED_ORIGINS,
654
+ SA2KIT_SMS_PROVIDER: process.env.SA2KIT_SMS_PROVIDER ?? (process.env.NODE_ENV !== "production" ? "console" : void 0),
655
+ ALIYUN_SMS_ACCESS_KEY_ID: process.env.ALIYUN_SMS_ACCESS_KEY_ID ? "[set]" : void 0,
656
+ ALIYUN_SMS_ACCESS_KEY_SECRET: process.env.ALIYUN_SMS_ACCESS_KEY_SECRET ? "[set]" : void 0,
657
+ ALIYUN_SMS_SIGN_NAME: process.env.ALIYUN_SMS_SIGN_NAME,
658
+ ALIYUN_SMS_TEMPLATE_CODE: process.env.ALIYUN_SMS_TEMPLATE_CODE,
659
+ SA2KIT_EMAIL_PROVIDER: process.env.SA2KIT_EMAIL_PROVIDER,
660
+ DATABASE_URL: process.env.DATABASE_URL ? "[set]" : void 0,
661
+ NODE_ENV: process.env.NODE_ENV,
662
+ ...extraSnapshot
663
+ };
664
+ return checkAuthEnv(snapshot);
665
+ }
666
+
667
+ // src/common/auth/server/create-auth-from-env.ts
668
+ function createSa2kitAuthFromEnv(input, options) {
669
+ const resolved = resolveAuthEnv(input);
670
+ const report = checkAuthEnv(resolved.envSnapshot);
671
+ if (options?.logEnvReport !== false) {
672
+ logAuthEnvReport(report);
673
+ }
674
+ return createSa2kitAuth(resolved.config);
675
+ }
80
676
  function mountNextAuthHandler(auth) {
81
677
  return nextJs.toNextJsHandler(auth);
82
678
  }
@@ -164,14 +760,31 @@ Object.defineProperty(exports, "verifications", {
164
760
  enumerable: true,
165
761
  get: function () { return chunkLGHUCQIU_js.verifications; }
166
762
  });
763
+ exports.AUTH_ENV_ALIASES = AUTH_ENV_ALIASES;
764
+ exports.AUTH_ENV_CATALOG = AUTH_ENV_CATALOG;
765
+ exports.AUTH_FEATURES = AUTH_FEATURES;
766
+ exports.checkAuthEnv = checkAuthEnv;
767
+ exports.checkAuthEnvFromProcessEnv = checkAuthEnvFromProcessEnv;
768
+ exports.consumePhoneSignupPassword = consumePhoneSignupPassword;
769
+ exports.createAliyunPnvsSmsProvider = createAliyunPnvsSmsProvider;
167
770
  exports.createAuthRouteHandlers = createAuthRouteHandlers;
771
+ exports.createConsoleSmsProvider = createConsoleSmsProvider;
168
772
  exports.createSa2kitAuth = createSa2kitAuth;
773
+ exports.createSa2kitAuthFromEnv = createSa2kitAuthFromEnv;
169
774
  exports.createSessionValidator = createSessionValidator;
775
+ exports.createSmsProviderFromEnv = createSmsProviderFromEnv;
170
776
  exports.defaultPhoneValidator = defaultPhoneValidator;
171
777
  exports.defaultTempEmailFromPhone = defaultTempEmailFromPhone;
778
+ exports.formatAuthEnvSetupMarkdown = formatAuthEnvSetupMarkdown;
172
779
  exports.getSessionUser = getSessionUser;
173
780
  exports.getSessionUserNumeric = getSessionUserNumeric;
781
+ exports.handlePhoneSignupIntentRequest = handlePhoneSignupIntentRequest;
782
+ exports.logAuthEnvReport = logAuthEnvReport;
174
783
  exports.mountAuthHandler = mountAuthHandler;
175
784
  exports.mountNextAuthHandler = mountNextAuthHandler;
785
+ exports.resolveAuthEnv = resolveAuthEnv;
786
+ exports.resolveSmsProviderId = resolveSmsProviderId;
787
+ exports.stashPhoneSignupPassword = stashPhoneSignupPassword;
788
+ exports.upsertCredentialPassword = upsertCredentialPassword;
176
789
  //# sourceMappingURL=index.js.map
177
790
  //# sourceMappingURL=index.js.map