@mesob/auth-hono 0.4.7 → 0.5.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.
package/dist/index.js CHANGED
@@ -11,23 +11,6 @@ import { deepmerge } from "deepmerge-ts";
11
11
  import { drizzle } from "drizzle-orm/node-postgres";
12
12
  import { Pool } from "pg";
13
13
 
14
- // src/db/relations.ts
15
- var relations_exports = {};
16
- __export(relations_exports, {
17
- accountChangesInIamRelations: () => accountChangesInIamRelations,
18
- accountsInIamRelations: () => accountsInIamRelations,
19
- domainsInIamRelations: () => domainsInIamRelations,
20
- permissionsInIamRelations: () => permissionsInIamRelations,
21
- rolePermissionsInIamRelations: () => rolePermissionsInIamRelations,
22
- rolesInIamRelations: () => rolesInIamRelations,
23
- sessionsInIamRelations: () => sessionsInIamRelations,
24
- tenantsInIamRelations: () => tenantsInIamRelations,
25
- userRolesInIamRelations: () => userRolesInIamRelations,
26
- usersInIamRelations: () => usersInIamRelations,
27
- verificationsInIamRelations: () => verificationsInIamRelations
28
- });
29
- import { relations } from "drizzle-orm/relations";
30
-
31
14
  // src/db/schema.ts
32
15
  var schema_exports = {};
33
16
  __export(schema_exports, {
@@ -333,117 +316,8 @@ var domainsInIam = iam.table("domains", {
333
316
  check("domains_status_check", sql`status = ANY (ARRAY['PENDING'::text, 'ACTIVE'::text, 'DISABLED'::text, 'DELETED'::text])`)
334
317
  ]);
335
318
 
336
- // src/db/relations.ts
337
- var verificationsInIamRelations = relations(verificationsInIam, ({ one }) => ({
338
- tenantsInIam: one(tenantsInIam, {
339
- fields: [verificationsInIam.tenantId],
340
- references: [tenantsInIam.id]
341
- }),
342
- usersInIam: one(usersInIam, {
343
- fields: [verificationsInIam.userId],
344
- references: [usersInIam.id]
345
- })
346
- }));
347
- var tenantsInIamRelations = relations(tenantsInIam, ({ many }) => ({
348
- verificationsInIam: many(verificationsInIam),
349
- sessionsInIam: many(sessionsInIam),
350
- accountChangesInIam: many(accountChangesInIam),
351
- rolePermissionsInIam: many(rolePermissionsInIam),
352
- accountsInIam: many(accountsInIam),
353
- usersInIam: many(usersInIam),
354
- rolesInIam: many(rolesInIam),
355
- userRolesInIam: many(userRolesInIam),
356
- domainsInIam: many(domainsInIam)
357
- }));
358
- var usersInIamRelations = relations(usersInIam, ({ one, many }) => ({
359
- verificationsInIam: many(verificationsInIam),
360
- sessionsInIam: many(sessionsInIam),
361
- accountChangesInIam: many(accountChangesInIam),
362
- accountsInIam: many(accountsInIam),
363
- tenantsInIam: one(tenantsInIam, {
364
- fields: [usersInIam.tenantId],
365
- references: [tenantsInIam.id]
366
- }),
367
- userRolesInIam: many(userRolesInIam)
368
- }));
369
- var sessionsInIamRelations = relations(sessionsInIam, ({ one }) => ({
370
- tenantsInIam: one(tenantsInIam, {
371
- fields: [sessionsInIam.tenantId],
372
- references: [tenantsInIam.id]
373
- }),
374
- usersInIam: one(usersInIam, {
375
- fields: [sessionsInIam.userId],
376
- references: [usersInIam.id]
377
- })
378
- }));
379
- var accountChangesInIamRelations = relations(accountChangesInIam, ({ one }) => ({
380
- tenantsInIam: one(tenantsInIam, {
381
- fields: [accountChangesInIam.tenantId],
382
- references: [tenantsInIam.id]
383
- }),
384
- usersInIam: one(usersInIam, {
385
- fields: [accountChangesInIam.userId],
386
- references: [usersInIam.id]
387
- })
388
- }));
389
- var rolePermissionsInIamRelations = relations(rolePermissionsInIam, ({ one }) => ({
390
- tenantsInIam: one(tenantsInIam, {
391
- fields: [rolePermissionsInIam.tenantId],
392
- references: [tenantsInIam.id]
393
- }),
394
- permissionsInIam: one(permissionsInIam, {
395
- fields: [rolePermissionsInIam.permissionId],
396
- references: [permissionsInIam.id]
397
- }),
398
- rolesInIam: one(rolesInIam, {
399
- fields: [rolePermissionsInIam.tenantId],
400
- references: [rolesInIam.tenantId]
401
- })
402
- }));
403
- var permissionsInIamRelations = relations(permissionsInIam, ({ many }) => ({
404
- rolePermissionsInIam: many(rolePermissionsInIam)
405
- }));
406
- var rolesInIamRelations = relations(rolesInIam, ({ one, many }) => ({
407
- rolePermissionsInIam: many(rolePermissionsInIam),
408
- tenantsInIam: one(tenantsInIam, {
409
- fields: [rolesInIam.tenantId],
410
- references: [tenantsInIam.id]
411
- }),
412
- userRolesInIam: many(userRolesInIam)
413
- }));
414
- var accountsInIamRelations = relations(accountsInIam, ({ one }) => ({
415
- tenantsInIam: one(tenantsInIam, {
416
- fields: [accountsInIam.tenantId],
417
- references: [tenantsInIam.id]
418
- }),
419
- usersInIam: one(usersInIam, {
420
- fields: [accountsInIam.userId],
421
- references: [usersInIam.id]
422
- })
423
- }));
424
- var userRolesInIamRelations = relations(userRolesInIam, ({ one }) => ({
425
- tenantsInIam: one(tenantsInIam, {
426
- fields: [userRolesInIam.tenantId],
427
- references: [tenantsInIam.id]
428
- }),
429
- usersInIam: one(usersInIam, {
430
- fields: [userRolesInIam.userId],
431
- references: [usersInIam.id]
432
- }),
433
- rolesInIam: one(rolesInIam, {
434
- fields: [userRolesInIam.tenantId],
435
- references: [rolesInIam.tenantId]
436
- })
437
- }));
438
- var domainsInIamRelations = relations(domainsInIam, ({ one }) => ({
439
- tenantsInIam: one(tenantsInIam, {
440
- fields: [domainsInIam.tenantId],
441
- references: [tenantsInIam.id]
442
- })
443
- }));
444
-
445
319
  // src/db/index.ts
446
- var schemaConfig = { schema: { ...schema_exports, ...relations_exports } };
320
+ var schemaConfig = { schema: { ...schema_exports } };
447
321
  var createDatabase = (connectionString) => {
448
322
  const pool = new Pool({ connectionString });
449
323
  return drizzle({ client: pool, ...schemaConfig });
@@ -995,6 +869,8 @@ var checkAccountResponseSchema = z.object({
995
869
  verified: z.boolean(),
996
870
  hasPassword: z.boolean(),
997
871
  requiresPasswordSetup: z.boolean(),
872
+ needsVerification: z.boolean().optional(),
873
+ verificationId: z.string().uuid().optional(),
998
874
  account: authAccountSchema.nullable()
999
875
  });
1000
876
  var updateProfileSchema = z.object({
@@ -1021,7 +897,7 @@ var pendingAccountChangeResponseSchema = z.object({
1021
897
  });
1022
898
 
1023
899
  // src/routes/auth/handler/check-account.ts
1024
- import { and as and5, eq as eq5, sql as sql4 } from "drizzle-orm";
900
+ import { and as and6, eq as eq6, sql as sql4 } from "drizzle-orm";
1025
901
 
1026
902
  // src/lib/tenant.ts
1027
903
  import { HTTPException as HTTPException2 } from "hono/http-exception";
@@ -1043,79 +919,9 @@ var ensureTenantId = (config, tenantId) => {
1043
919
  return config.tenant.tenantId;
1044
920
  };
1045
921
 
1046
- // src/routes/auth/handler/check-account.ts
1047
- var checkAccountHandler = async (c) => {
1048
- const body = c.req.valid("json");
1049
- const config = c.get("config");
1050
- const database = c.get("database");
1051
- const tenantId = c.get("tenantId");
1052
- const resolvedTenantId = ensureTenantId(config, tenantId);
1053
- const { username } = body;
1054
- const isEmail = username.includes("@");
1055
- const userTypeFilter = sql4`${usersInIam.userType} @> ARRAY[${config.userType}]::text[]`;
1056
- const whereClause = isEmail ? and5(
1057
- eq5(usersInIam.tenantId, resolvedTenantId),
1058
- userTypeFilter,
1059
- sql4`lower(${usersInIam.email}) = lower(${username})`
1060
- ) : and5(
1061
- eq5(usersInIam.tenantId, resolvedTenantId),
1062
- userTypeFilter,
1063
- eq5(usersInIam.phone, username)
1064
- );
1065
- const [result] = await database.select({
1066
- fullName: usersInIam.fullName,
1067
- email: usersInIam.email,
1068
- phone: usersInIam.phone,
1069
- verified: isEmail ? usersInIam.emailVerified : usersInIam.phoneVerified,
1070
- hasPassword: sql4`exists(
1071
- select 1
1072
- from ${accountsInIam}
1073
- where ${eq5(accountsInIam.tenantId, resolvedTenantId)}
1074
- and ${eq5(accountsInIam.userId, usersInIam.id)}
1075
- and ${eq5(accountsInIam.provider, "credentials")}
1076
- and ${sql4`${accountsInIam.password} is not null`}
1077
- )`
1078
- }).from(usersInIam).where(whereClause).limit(1);
1079
- const verified = result?.verified ?? false;
1080
- const hasPassword = result?.hasPassword ?? false;
1081
- return c.json(
1082
- {
1083
- exists: !!result,
1084
- verified,
1085
- hasPassword,
1086
- requiresPasswordSetup: !!result && verified && !hasPassword,
1087
- account: result ? {
1088
- fullName: result.fullName,
1089
- email: result.email,
1090
- phone: result.phone,
1091
- verified,
1092
- hasPassword,
1093
- requiresPasswordSetup: verified && !hasPassword
1094
- } : null
1095
- },
1096
- 200
1097
- );
1098
- };
1099
-
1100
- // src/routes/auth/handler/sign-in.ts
1101
- import { logger as logger2 } from "@mesob/common";
1102
- import { and as and9, eq as eq9 } from "drizzle-orm";
1103
-
1104
- // src/errors.ts
1105
- var AUTH_ERRORS = {
1106
- USER_NOT_FOUND: "USER_NOT_FOUND",
1107
- INVALID_PASSWORD: "INVALID_PASSWORD",
1108
- USER_EXISTS: "USER_EXISTS",
1109
- VERIFICATION_EXPIRED: "VERIFICATION_EXPIRED",
1110
- VERIFICATION_MISMATCH: "VERIFICATION_MISMATCH",
1111
- VERIFICATION_NOT_FOUND: "VERIFICATION_NOT_FOUND",
1112
- TOO_MANY_ATTEMPTS: "TOO_MANY_ATTEMPTS",
1113
- REQUIRES_VERIFICATION: "REQUIRES_VERIFICATION",
1114
- UNAUTHORIZED: "UNAUTHORIZED",
1115
- ACCESS_DENIED: "ACCESS_DENIED",
1116
- HAS_NO_PASSWORD: "HAS_NO_PASSWORD",
1117
- PASSWORD_ALREADY_SET: "PASSWORD_ALREADY_SET"
1118
- };
922
+ // src/routes/auth/helper/verification.ts
923
+ import { dayjs as dayjs2 } from "@mesob/common";
924
+ import { and as and5, desc, eq as eq5, gt as gt2 } from "drizzle-orm";
1119
925
 
1120
926
  // src/lib/normalize-auth-response.ts
1121
927
  var normalizeAuthUser = (user) => ({
@@ -1201,16 +1007,369 @@ var shouldRefreshSession = ({
1201
1007
  const remainingSeconds = expires.diff(now, "second");
1202
1008
  return remainingSeconds > 0 && remainingSeconds <= updateAgeSeconds;
1203
1009
  };
1204
- var getRefreshedExpiresAt = ({
1205
- sessionConfig,
1206
- rememberMe
1207
- }) => {
1208
- const duration = getSessionDuration({ sessionConfig, rememberMe });
1209
- return addDuration(duration);
1010
+ var getRefreshedExpiresAt = ({
1011
+ sessionConfig,
1012
+ rememberMe
1013
+ }) => {
1014
+ const duration = getSessionDuration({ sessionConfig, rememberMe });
1015
+ return addDuration(duration);
1016
+ };
1017
+
1018
+ // src/routes/auth/helper/verification.ts
1019
+ var createVerification = async ({
1020
+ tx,
1021
+ tenantId,
1022
+ userId,
1023
+ type,
1024
+ to,
1025
+ config
1026
+ }) => {
1027
+ const isPhone = type === "phone-otp-sign-up";
1028
+ const code = generateOtpCode(
1029
+ isPhone ? config.phone.otpLength : config.email.otpLength
1030
+ );
1031
+ const hashedCode = await hashToken(code, config.secret);
1032
+ const expiresAt = addDuration(
1033
+ isPhone ? config.phone.expiresIn : config.email.expiresIn
1034
+ );
1035
+ const [verification] = await tx.insert(verificationsInIam).values({
1036
+ tenantId,
1037
+ userId,
1038
+ type,
1039
+ code: hashedCode,
1040
+ expiresAt,
1041
+ to,
1042
+ attempt: 0
1043
+ }).returning();
1044
+ return { verificationId: verification.id, code, hash: hashedCode };
1045
+ };
1046
+ var sendVerification = async ({
1047
+ channel,
1048
+ to,
1049
+ code,
1050
+ config,
1051
+ hash,
1052
+ type
1053
+ }) => {
1054
+ if (channel === "phone" && config.phone.sendVerificationOTP) {
1055
+ await config.phone.sendVerificationOTP({
1056
+ phone: to,
1057
+ code,
1058
+ hash,
1059
+ type
1060
+ });
1061
+ } else if (config.email.sendVerificationOTP) {
1062
+ await config.email.sendVerificationOTP({
1063
+ email: to,
1064
+ code,
1065
+ hash,
1066
+ type
1067
+ });
1068
+ }
1069
+ };
1070
+ var checkVerificationResend = async ({
1071
+ database,
1072
+ tenantId,
1073
+ userId,
1074
+ type,
1075
+ resendInterval
1076
+ }) => {
1077
+ if (!resendInterval) {
1078
+ return { blocked: false };
1079
+ }
1080
+ const [verification] = await database.select({
1081
+ id: verificationsInIam.id,
1082
+ createdAt: verificationsInIam.createdAt
1083
+ }).from(verificationsInIam).where(
1084
+ and5(
1085
+ eq5(verificationsInIam.tenantId, tenantId),
1086
+ eq5(verificationsInIam.userId, userId),
1087
+ eq5(verificationsInIam.type, type)
1088
+ )
1089
+ ).orderBy(desc(verificationsInIam.createdAt)).limit(1);
1090
+ if (!verification) {
1091
+ return { blocked: false };
1092
+ }
1093
+ const cooldownSeconds = parseDuration(resendInterval);
1094
+ const createdAt = dayjs2(verification.createdAt);
1095
+ const blocked = dayjs2().diff(createdAt, "second") < cooldownSeconds;
1096
+ return { blocked, verificationId: verification.id };
1097
+ };
1098
+ var ensureVerificationForCheckAccount = async ({
1099
+ database,
1100
+ tenantId,
1101
+ userId,
1102
+ type,
1103
+ to,
1104
+ config
1105
+ }) => {
1106
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1107
+ const [existing] = await database.select({
1108
+ id: verificationsInIam.id,
1109
+ expiresAt: verificationsInIam.expiresAt
1110
+ }).from(verificationsInIam).where(
1111
+ and5(
1112
+ eq5(verificationsInIam.tenantId, tenantId),
1113
+ eq5(verificationsInIam.userId, userId),
1114
+ eq5(verificationsInIam.type, type),
1115
+ gt2(verificationsInIam.expiresAt, now)
1116
+ )
1117
+ ).orderBy(desc(verificationsInIam.createdAt)).limit(1);
1118
+ if (existing) {
1119
+ return { verificationId: existing.id };
1120
+ }
1121
+ await database.delete(verificationsInIam).where(
1122
+ and5(
1123
+ eq5(verificationsInIam.tenantId, tenantId),
1124
+ eq5(verificationsInIam.userId, userId),
1125
+ eq5(verificationsInIam.type, type)
1126
+ )
1127
+ );
1128
+ const isPhone = type === "phone-otp";
1129
+ const code = generateOtpCode(
1130
+ isPhone ? config.phone.otpLength : config.email.otpLength
1131
+ );
1132
+ const hashedCode = await hashToken(code, config.secret);
1133
+ const expiresAt = addDuration(
1134
+ isPhone ? config.phone.expiresIn : config.email.expiresIn
1135
+ );
1136
+ const [verification] = await database.insert(verificationsInIam).values({
1137
+ tenantId,
1138
+ userId,
1139
+ type,
1140
+ code: hashedCode,
1141
+ expiresAt,
1142
+ to,
1143
+ attempt: 0
1144
+ }).returning();
1145
+ if (isPhone && config.phone.sendVerificationOTP) {
1146
+ await config.phone.sendVerificationOTP({
1147
+ phone: to,
1148
+ code,
1149
+ hash: hashedCode,
1150
+ type
1151
+ });
1152
+ } else if (!isPhone && config.email.sendVerificationOTP) {
1153
+ await config.email.sendVerificationOTP({
1154
+ email: to,
1155
+ code,
1156
+ hash: hashedCode,
1157
+ type
1158
+ });
1159
+ }
1160
+ return { verificationId: verification.id };
1161
+ };
1162
+ var handleEmailVerification = async ({
1163
+ c,
1164
+ user,
1165
+ config,
1166
+ database,
1167
+ tenantId
1168
+ }) => {
1169
+ if (!user.email) {
1170
+ return c.json({ error: "User email not found" }, 401);
1171
+ }
1172
+ await database.delete(verificationsInIam).where(
1173
+ and5(
1174
+ eq5(verificationsInIam.tenantId, tenantId),
1175
+ eq5(verificationsInIam.userId, user.id),
1176
+ eq5(verificationsInIam.type, "email-verification")
1177
+ )
1178
+ );
1179
+ const code = generateOtpCode(config.email.otpLength);
1180
+ const hashedCode = await hashToken(code, config.secret);
1181
+ const expiresAt = addDuration(config.email.expiresIn);
1182
+ const [verification] = await database.insert(verificationsInIam).values({
1183
+ tenantId,
1184
+ userId: user.id,
1185
+ type: "email-verification",
1186
+ code: hashedCode,
1187
+ expiresAt,
1188
+ to: user.email,
1189
+ attempt: 0
1190
+ }).returning({
1191
+ id: verificationsInIam.id,
1192
+ tenantId: verificationsInIam.tenantId,
1193
+ userId: verificationsInIam.userId,
1194
+ type: verificationsInIam.type,
1195
+ code: verificationsInIam.code,
1196
+ to: verificationsInIam.to,
1197
+ expiresAt: verificationsInIam.expiresAt,
1198
+ createdAt: verificationsInIam.createdAt,
1199
+ attempt: verificationsInIam.attempt
1200
+ });
1201
+ if (config.email.sendVerificationOTP) {
1202
+ await config.email.sendVerificationOTP({
1203
+ email: user.email,
1204
+ code,
1205
+ hash: hashedCode,
1206
+ type: "email-verification"
1207
+ });
1208
+ }
1209
+ return c.json(
1210
+ {
1211
+ user: normalizeAuthUser(user),
1212
+ session: null,
1213
+ verificationId: verification.id,
1214
+ requiresVerification: true
1215
+ },
1216
+ 200
1217
+ );
1218
+ };
1219
+ var handlePhoneVerification = async ({
1220
+ c,
1221
+ user,
1222
+ config,
1223
+ database,
1224
+ tenantId
1225
+ }) => {
1226
+ if (!user.phone) {
1227
+ return c.json({ error: "User phone not found" }, 401);
1228
+ }
1229
+ await database.delete(verificationsInIam).where(
1230
+ and5(
1231
+ eq5(verificationsInIam.tenantId, tenantId),
1232
+ eq5(verificationsInIam.userId, user.id),
1233
+ eq5(verificationsInIam.type, "phone-otp")
1234
+ )
1235
+ );
1236
+ const code = generateOtpCode(config.phone.otpLength);
1237
+ const hashedCode = await hashToken(code, config.secret);
1238
+ const expiresAt = addDuration(config.phone.expiresIn);
1239
+ const [verification] = await database.insert(verificationsInIam).values({
1240
+ tenantId,
1241
+ userId: user.id,
1242
+ type: "phone-otp",
1243
+ code: hashedCode,
1244
+ expiresAt,
1245
+ to: user.phone,
1246
+ attempt: 0
1247
+ }).returning({
1248
+ id: verificationsInIam.id,
1249
+ tenantId: verificationsInIam.tenantId,
1250
+ userId: verificationsInIam.userId,
1251
+ type: verificationsInIam.type,
1252
+ code: verificationsInIam.code,
1253
+ to: verificationsInIam.to,
1254
+ expiresAt: verificationsInIam.expiresAt,
1255
+ createdAt: verificationsInIam.createdAt,
1256
+ attempt: verificationsInIam.attempt
1257
+ });
1258
+ if (config.phone.sendVerificationOTP) {
1259
+ await config.phone.sendVerificationOTP({
1260
+ phone: user.phone,
1261
+ code,
1262
+ hash: hashedCode,
1263
+ type: "phone-otp"
1264
+ });
1265
+ }
1266
+ return c.json(
1267
+ {
1268
+ user: normalizeAuthUser(user),
1269
+ session: null,
1270
+ verificationId: verification.id,
1271
+ requiresVerification: true
1272
+ },
1273
+ 200
1274
+ );
1275
+ };
1276
+
1277
+ // src/routes/auth/handler/check-account.ts
1278
+ var checkAccountHandler = async (c) => {
1279
+ const body = c.req.valid("json");
1280
+ const config = c.get("config");
1281
+ const database = c.get("database");
1282
+ const tenantId = c.get("tenantId");
1283
+ const resolvedTenantId = ensureTenantId(config, tenantId);
1284
+ const { username } = body;
1285
+ const isEmail = username.includes("@");
1286
+ const userTypeFilter = sql4`${usersInIam.userType} @> ARRAY[${config.userType}]::text[]`;
1287
+ const whereClause = isEmail ? and6(
1288
+ eq6(usersInIam.tenantId, resolvedTenantId),
1289
+ userTypeFilter,
1290
+ sql4`lower(${usersInIam.email}) = lower(${username})`
1291
+ ) : and6(
1292
+ eq6(usersInIam.tenantId, resolvedTenantId),
1293
+ userTypeFilter,
1294
+ eq6(usersInIam.phone, username)
1295
+ );
1296
+ const [result] = await database.select({
1297
+ id: usersInIam.id,
1298
+ fullName: usersInIam.fullName,
1299
+ email: usersInIam.email,
1300
+ phone: usersInIam.phone,
1301
+ verified: isEmail ? usersInIam.emailVerified : usersInIam.phoneVerified,
1302
+ hasPassword: sql4`exists(
1303
+ select 1
1304
+ from ${accountsInIam}
1305
+ where ${eq6(accountsInIam.tenantId, resolvedTenantId)}
1306
+ and ${eq6(accountsInIam.userId, usersInIam.id)}
1307
+ and ${eq6(accountsInIam.provider, "credentials")}
1308
+ and ${sql4`${accountsInIam.password} is not null`}
1309
+ )`
1310
+ }).from(usersInIam).where(whereClause).limit(1);
1311
+ const verified = result?.verified ?? false;
1312
+ const hasPassword = result?.hasPassword ?? false;
1313
+ let needsVerification = false;
1314
+ let verificationId;
1315
+ if (result && !verified) {
1316
+ const type = isEmail ? "email-verification" : "phone-otp";
1317
+ const to = isEmail ? result.email ?? username : result.phone ?? username;
1318
+ if (to) {
1319
+ const { verificationId: vid } = await ensureVerificationForCheckAccount({
1320
+ database,
1321
+ tenantId: resolvedTenantId,
1322
+ userId: result.id,
1323
+ type,
1324
+ to,
1325
+ config
1326
+ });
1327
+ needsVerification = true;
1328
+ verificationId = vid;
1329
+ }
1330
+ }
1331
+ return c.json(
1332
+ {
1333
+ exists: !!result,
1334
+ verified,
1335
+ hasPassword,
1336
+ requiresPasswordSetup: !!result && verified && !hasPassword,
1337
+ ...needsVerification && verificationId ? { needsVerification: true, verificationId } : {},
1338
+ account: result ? {
1339
+ fullName: result.fullName,
1340
+ email: result.email,
1341
+ phone: result.phone,
1342
+ verified,
1343
+ hasPassword,
1344
+ requiresPasswordSetup: verified && !hasPassword
1345
+ } : null
1346
+ },
1347
+ 200
1348
+ );
1349
+ };
1350
+
1351
+ // src/routes/auth/handler/sign-in.ts
1352
+ import { logger as logger2 } from "@mesob/common";
1353
+ import { and as and9, eq as eq9 } from "drizzle-orm";
1354
+
1355
+ // src/errors.ts
1356
+ var AUTH_ERRORS = {
1357
+ USER_NOT_FOUND: "USER_NOT_FOUND",
1358
+ INVALID_PASSWORD: "INVALID_PASSWORD",
1359
+ USER_EXISTS: "USER_EXISTS",
1360
+ VERIFICATION_EXPIRED: "VERIFICATION_EXPIRED",
1361
+ VERIFICATION_MISMATCH: "VERIFICATION_MISMATCH",
1362
+ VERIFICATION_NOT_FOUND: "VERIFICATION_NOT_FOUND",
1363
+ TOO_MANY_ATTEMPTS: "TOO_MANY_ATTEMPTS",
1364
+ REQUIRES_VERIFICATION: "REQUIRES_VERIFICATION",
1365
+ UNAUTHORIZED: "UNAUTHORIZED",
1366
+ ACCESS_DENIED: "ACCESS_DENIED",
1367
+ HAS_NO_PASSWORD: "HAS_NO_PASSWORD",
1368
+ PASSWORD_ALREADY_SET: "PASSWORD_ALREADY_SET"
1210
1369
  };
1211
1370
 
1212
1371
  // src/routes/auth/helper/session.ts
1213
- import { and as and6, asc, eq as eq6, gt as gt2, inArray, sql as sql5 } from "drizzle-orm";
1372
+ import { and as and7, asc, eq as eq7, gt as gt3, inArray, sql as sql5 } from "drizzle-orm";
1214
1373
  var createSessionRecord = async ({
1215
1374
  tx,
1216
1375
  tenantId,
@@ -1279,10 +1438,10 @@ var cleanupOldSessions = async ({
1279
1438
  maxSessions
1280
1439
  }) => {
1281
1440
  const [{ count }] = await database.select({ count: sql5`count(*)` }).from(sessionsInIam).where(
1282
- and6(
1283
- eq6(sessionsInIam.tenantId, tenantId),
1284
- eq6(sessionsInIam.userId, userId),
1285
- gt2(sessionsInIam.expiresAt, (/* @__PURE__ */ new Date()).toISOString())
1441
+ and7(
1442
+ eq7(sessionsInIam.tenantId, tenantId),
1443
+ eq7(sessionsInIam.userId, userId),
1444
+ gt3(sessionsInIam.expiresAt, (/* @__PURE__ */ new Date()).toISOString())
1286
1445
  )
1287
1446
  );
1288
1447
  if (count <= maxSessions) {
@@ -1290,19 +1449,19 @@ var cleanupOldSessions = async ({
1290
1449
  }
1291
1450
  const toDeleteCount = count - maxSessions;
1292
1451
  const idsToDelete = await database.select({ id: sessionsInIam.id }).from(sessionsInIam).where(
1293
- and6(
1294
- eq6(sessionsInIam.tenantId, tenantId),
1295
- eq6(sessionsInIam.userId, userId),
1296
- gt2(sessionsInIam.expiresAt, (/* @__PURE__ */ new Date()).toISOString())
1452
+ and7(
1453
+ eq7(sessionsInIam.tenantId, tenantId),
1454
+ eq7(sessionsInIam.userId, userId),
1455
+ gt3(sessionsInIam.expiresAt, (/* @__PURE__ */ new Date()).toISOString())
1297
1456
  )
1298
1457
  ).orderBy(asc(sessionsInIam.createdAt)).limit(toDeleteCount);
1299
1458
  if (!idsToDelete.length) {
1300
1459
  return;
1301
1460
  }
1302
1461
  await database.delete(sessionsInIam).where(
1303
- and6(
1304
- eq6(sessionsInIam.tenantId, tenantId),
1305
- eq6(sessionsInIam.userId, userId),
1462
+ and7(
1463
+ eq7(sessionsInIam.tenantId, tenantId),
1464
+ eq7(sessionsInIam.userId, userId),
1306
1465
  inArray(
1307
1466
  sessionsInIam.id,
1308
1467
  idsToDelete.map((s) => s.id)
@@ -1312,17 +1471,17 @@ var cleanupOldSessions = async ({
1312
1471
  };
1313
1472
 
1314
1473
  // src/routes/auth/helper/user.ts
1315
- import { and as and7, eq as eq7, gt as gt3, sql as sql6 } from "drizzle-orm";
1474
+ import { and as and8, eq as eq8, gt as gt4, sql as sql6 } from "drizzle-orm";
1316
1475
  var checkExistingUserStatus = async ({
1317
1476
  tx,
1318
1477
  identifier,
1319
1478
  tenantId,
1320
1479
  isEmail
1321
1480
  }) => {
1322
- const whereClause = isEmail ? and7(
1323
- eq7(usersInIam.tenantId, tenantId),
1481
+ const whereClause = isEmail ? and8(
1482
+ eq8(usersInIam.tenantId, tenantId),
1324
1483
  sql6`lower(${usersInIam.email}) = lower(${identifier})`
1325
- ) : and7(eq7(usersInIam.tenantId, tenantId), eq7(usersInIam.phone, identifier));
1484
+ ) : and8(eq8(usersInIam.tenantId, tenantId), eq8(usersInIam.phone, identifier));
1326
1485
  const [existingUser] = await tx.select().from(usersInIam).where(whereClause).limit(1);
1327
1486
  if (!existingUser) {
1328
1487
  return { action: "proceed" };
@@ -1332,10 +1491,10 @@ var checkExistingUserStatus = async ({
1332
1491
  return { action: "error", code: AUTH_ERRORS.USER_EXISTS };
1333
1492
  }
1334
1493
  const [pendingVerification] = await tx.select().from(verificationsInIam).where(
1335
- and7(
1336
- eq7(verificationsInIam.userId, existingUser.id),
1337
- eq7(verificationsInIam.tenantId, tenantId),
1338
- gt3(verificationsInIam.expiresAt, (/* @__PURE__ */ new Date()).toISOString())
1494
+ and8(
1495
+ eq8(verificationsInIam.userId, existingUser.id),
1496
+ eq8(verificationsInIam.tenantId, tenantId),
1497
+ gt4(verificationsInIam.expiresAt, (/* @__PURE__ */ new Date()).toISOString())
1339
1498
  )
1340
1499
  ).limit(1);
1341
1500
  if (pendingVerification) {
@@ -1354,18 +1513,18 @@ var deleteUnverifiedUser = async ({
1354
1513
  tenantId
1355
1514
  }) => {
1356
1515
  await tx.delete(verificationsInIam).where(
1357
- and7(
1358
- eq7(verificationsInIam.userId, userId),
1359
- eq7(verificationsInIam.tenantId, tenantId)
1516
+ and8(
1517
+ eq8(verificationsInIam.userId, userId),
1518
+ eq8(verificationsInIam.tenantId, tenantId)
1360
1519
  )
1361
1520
  );
1362
1521
  await tx.delete(accountsInIam).where(
1363
- and7(
1364
- eq7(accountsInIam.userId, userId),
1365
- eq7(accountsInIam.tenantId, tenantId)
1522
+ and8(
1523
+ eq8(accountsInIam.userId, userId),
1524
+ eq8(accountsInIam.tenantId, tenantId)
1366
1525
  )
1367
1526
  );
1368
- await tx.delete(usersInIam).where(and7(eq7(usersInIam.id, userId), eq7(usersInIam.tenantId, tenantId)));
1527
+ await tx.delete(usersInIam).where(and8(eq8(usersInIam.id, userId), eq8(usersInIam.tenantId, tenantId)));
1369
1528
  };
1370
1529
  var createUserWithAccount = async ({
1371
1530
  tx,
@@ -1405,14 +1564,14 @@ var fetchUserForLogin = async ({
1405
1564
  userType
1406
1565
  }) => {
1407
1566
  const userTypeFilter = sql6`${usersInIam.userType} @> ARRAY[${userType}]::text[]`;
1408
- const whereClause = isEmail ? and7(
1409
- eq7(usersInIam.tenantId, tenantId),
1567
+ const whereClause = isEmail ? and8(
1568
+ eq8(usersInIam.tenantId, tenantId),
1410
1569
  userTypeFilter,
1411
1570
  sql6`lower(${usersInIam.email}) = lower(${identifier})`
1412
- ) : and7(
1413
- eq7(usersInIam.tenantId, tenantId),
1571
+ ) : and8(
1572
+ eq8(usersInIam.tenantId, tenantId),
1414
1573
  userTypeFilter,
1415
- eq7(usersInIam.phone, identifier)
1574
+ eq8(usersInIam.phone, identifier)
1416
1575
  );
1417
1576
  const [row] = await database.select({
1418
1577
  id: usersInIam.id,
@@ -1430,9 +1589,9 @@ var fetchUserForLogin = async ({
1430
1589
  hasPassword: sql6`exists(
1431
1590
  select 1
1432
1591
  from ${accountsInIam}
1433
- where ${eq7(accountsInIam.tenantId, tenantId)}
1434
- and ${eq7(accountsInIam.userId, usersInIam.id)}
1435
- and ${eq7(accountsInIam.provider, "credentials")}
1592
+ where ${eq8(accountsInIam.tenantId, tenantId)}
1593
+ and ${eq8(accountsInIam.userId, usersInIam.id)}
1594
+ and ${eq8(accountsInIam.provider, "credentials")}
1436
1595
  and ${sql6`${accountsInIam.password} is not null`}
1437
1596
  )`
1438
1597
  }).from(usersInIam).where(whereClause).limit(1);
@@ -1457,207 +1616,10 @@ var fetchUserByIdWithRoles = async ({
1457
1616
  bannedUntil: usersInIam.bannedUntil,
1458
1617
  loginAttempt: usersInIam.loginAttempt,
1459
1618
  ...getUserAuthSelect(tenantId)
1460
- }).from(usersInIam).where(and7(eq7(usersInIam.id, userId), eq7(usersInIam.tenantId, tenantId))).limit(1);
1619
+ }).from(usersInIam).where(and8(eq8(usersInIam.id, userId), eq8(usersInIam.tenantId, tenantId))).limit(1);
1461
1620
  return result || null;
1462
1621
  };
1463
1622
 
1464
- // src/routes/auth/helper/verification.ts
1465
- import { dayjs as dayjs2 } from "@mesob/common";
1466
- import { and as and8, desc, eq as eq8 } from "drizzle-orm";
1467
- var createVerification = async ({
1468
- tx,
1469
- tenantId,
1470
- userId,
1471
- type,
1472
- to,
1473
- config
1474
- }) => {
1475
- const isPhone = type === "phone-otp-sign-up";
1476
- const code = generateOtpCode(
1477
- isPhone ? config.phone.otpLength : config.email.otpLength
1478
- );
1479
- const hashedCode = await hashToken(code, config.secret);
1480
- const expiresAt = addDuration(
1481
- isPhone ? config.phone.expiresIn : config.email.expiresIn
1482
- );
1483
- const [verification] = await tx.insert(verificationsInIam).values({
1484
- tenantId,
1485
- userId,
1486
- type,
1487
- code: hashedCode,
1488
- expiresAt,
1489
- to,
1490
- attempt: 0
1491
- }).returning();
1492
- return { verificationId: verification.id, code, hash: hashedCode };
1493
- };
1494
- var sendVerification = async ({
1495
- channel,
1496
- to,
1497
- code,
1498
- config,
1499
- hash,
1500
- type
1501
- }) => {
1502
- if (channel === "phone" && config.phone.sendVerificationOTP) {
1503
- await config.phone.sendVerificationOTP({
1504
- phone: to,
1505
- code,
1506
- hash,
1507
- type
1508
- });
1509
- } else if (config.email.sendVerificationOTP) {
1510
- await config.email.sendVerificationOTP({
1511
- email: to,
1512
- code,
1513
- hash,
1514
- type
1515
- });
1516
- }
1517
- };
1518
- var checkVerificationResend = async ({
1519
- database,
1520
- tenantId,
1521
- userId,
1522
- type,
1523
- resendInterval
1524
- }) => {
1525
- if (!resendInterval) {
1526
- return { blocked: false };
1527
- }
1528
- const [verification] = await database.select({
1529
- id: verificationsInIam.id,
1530
- createdAt: verificationsInIam.createdAt
1531
- }).from(verificationsInIam).where(
1532
- and8(
1533
- eq8(verificationsInIam.tenantId, tenantId),
1534
- eq8(verificationsInIam.userId, userId),
1535
- eq8(verificationsInIam.type, type)
1536
- )
1537
- ).orderBy(desc(verificationsInIam.createdAt)).limit(1);
1538
- if (!verification) {
1539
- return { blocked: false };
1540
- }
1541
- const cooldownSeconds = parseDuration(resendInterval);
1542
- const createdAt = dayjs2(verification.createdAt);
1543
- const blocked = dayjs2().diff(createdAt, "second") < cooldownSeconds;
1544
- return { blocked, verificationId: verification.id };
1545
- };
1546
- var handleEmailVerification = async ({
1547
- c,
1548
- user,
1549
- config,
1550
- database,
1551
- tenantId
1552
- }) => {
1553
- if (!user.email) {
1554
- return c.json({ error: "User email not found" }, 401);
1555
- }
1556
- await database.delete(verificationsInIam).where(
1557
- and8(
1558
- eq8(verificationsInIam.tenantId, tenantId),
1559
- eq8(verificationsInIam.userId, user.id),
1560
- eq8(verificationsInIam.type, "email-verification")
1561
- )
1562
- );
1563
- const code = generateOtpCode(config.email.otpLength);
1564
- const hashedCode = await hashToken(code, config.secret);
1565
- const expiresAt = addDuration(config.email.expiresIn);
1566
- const [verification] = await database.insert(verificationsInIam).values({
1567
- tenantId,
1568
- userId: user.id,
1569
- type: "email-verification",
1570
- code: hashedCode,
1571
- expiresAt,
1572
- to: user.email,
1573
- attempt: 0
1574
- }).returning({
1575
- id: verificationsInIam.id,
1576
- tenantId: verificationsInIam.tenantId,
1577
- userId: verificationsInIam.userId,
1578
- type: verificationsInIam.type,
1579
- code: verificationsInIam.code,
1580
- to: verificationsInIam.to,
1581
- expiresAt: verificationsInIam.expiresAt,
1582
- createdAt: verificationsInIam.createdAt,
1583
- attempt: verificationsInIam.attempt
1584
- });
1585
- if (config.email.sendVerificationOTP) {
1586
- await config.email.sendVerificationOTP({
1587
- email: user.email,
1588
- code,
1589
- hash: hashedCode,
1590
- type: "email-verification"
1591
- });
1592
- }
1593
- return c.json(
1594
- {
1595
- user: normalizeAuthUser(user),
1596
- session: null,
1597
- verificationId: verification.id,
1598
- requiresVerification: true
1599
- },
1600
- 200
1601
- );
1602
- };
1603
- var handlePhoneVerification = async ({
1604
- c,
1605
- user,
1606
- config,
1607
- database,
1608
- tenantId
1609
- }) => {
1610
- if (!user.phone) {
1611
- return c.json({ error: "User phone not found" }, 401);
1612
- }
1613
- await database.delete(verificationsInIam).where(
1614
- and8(
1615
- eq8(verificationsInIam.tenantId, tenantId),
1616
- eq8(verificationsInIam.userId, user.id),
1617
- eq8(verificationsInIam.type, "phone-otp")
1618
- )
1619
- );
1620
- const code = generateOtpCode(config.phone.otpLength);
1621
- const hashedCode = await hashToken(code, config.secret);
1622
- const expiresAt = addDuration(config.phone.expiresIn);
1623
- const [verification] = await database.insert(verificationsInIam).values({
1624
- tenantId,
1625
- userId: user.id,
1626
- type: "phone-otp",
1627
- code: hashedCode,
1628
- expiresAt,
1629
- to: user.phone,
1630
- attempt: 0
1631
- }).returning({
1632
- id: verificationsInIam.id,
1633
- tenantId: verificationsInIam.tenantId,
1634
- userId: verificationsInIam.userId,
1635
- type: verificationsInIam.type,
1636
- code: verificationsInIam.code,
1637
- to: verificationsInIam.to,
1638
- expiresAt: verificationsInIam.expiresAt,
1639
- createdAt: verificationsInIam.createdAt,
1640
- attempt: verificationsInIam.attempt
1641
- });
1642
- if (config.phone.sendVerificationOTP) {
1643
- await config.phone.sendVerificationOTP({
1644
- phone: user.phone,
1645
- code,
1646
- hash: hashedCode,
1647
- type: "phone-otp"
1648
- });
1649
- }
1650
- return c.json(
1651
- {
1652
- user: normalizeAuthUser(user),
1653
- session: null,
1654
- verificationId: verification.id,
1655
- requiresVerification: true
1656
- },
1657
- 200
1658
- );
1659
- };
1660
-
1661
1623
  // src/routes/auth/handler/sign-in.ts
1662
1624
  var signInHandler = (
1663
1625
  // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: auth flow combines lockout, verification, and session creation
@@ -1821,7 +1783,7 @@ var signInHandler = (
1821
1783
  );
1822
1784
 
1823
1785
  // src/routes/auth/handler/sign-out.ts
1824
- import { and as and10, eq as eq10, gt as gt4 } from "drizzle-orm";
1786
+ import { and as and10, eq as eq10, gt as gt5 } from "drizzle-orm";
1825
1787
  import { getCookie } from "hono/cookie";
1826
1788
  var signOutHandler = async (c) => {
1827
1789
  const config = c.get("config");
@@ -1845,7 +1807,7 @@ var signOutHandler = async (c) => {
1845
1807
  }).from(sessionsInIam).where(
1846
1808
  and10(
1847
1809
  eq10(sessionsInIam.token, hashedToken),
1848
- gt4(sessionsInIam.expiresAt, (/* @__PURE__ */ new Date()).toISOString())
1810
+ gt5(sessionsInIam.expiresAt, (/* @__PURE__ */ new Date()).toISOString())
1849
1811
  )
1850
1812
  ).limit(1);
1851
1813
  if (session) {
@@ -2832,7 +2794,7 @@ var email_route_default = emailRoutes;
2832
2794
  import { createRoute as createRoute4, OpenAPIHono as OpenAPIHono4 } from "@hono/zod-openapi";
2833
2795
 
2834
2796
  // src/routes/password/handler/change.ts
2835
- import { and as and18, eq as eq18, gt as gt5, ne } from "drizzle-orm";
2797
+ import { and as and18, eq as eq18, gt as gt6, ne } from "drizzle-orm";
2836
2798
  import { getCookie as getCookie2 } from "hono/cookie";
2837
2799
  var changePasswordHandler = async (c) => {
2838
2800
  const body = c.req.valid("json");
@@ -2878,7 +2840,7 @@ var changePasswordHandler = async (c) => {
2878
2840
  and18(
2879
2841
  eq18(sessionsInIam.tenantId, resolvedTenantId),
2880
2842
  eq18(sessionsInIam.userId, userId),
2881
- gt5(sessionsInIam.expiresAt, (/* @__PURE__ */ new Date()).toISOString()),
2843
+ gt6(sessionsInIam.expiresAt, (/* @__PURE__ */ new Date()).toISOString()),
2882
2844
  ne(sessionsInIam.token, hashedToken)
2883
2845
  )
2884
2846
  );
@@ -3075,7 +3037,7 @@ var forgotPasswordHandler = async (c) => {
3075
3037
  };
3076
3038
 
3077
3039
  // src/routes/password/handler/reset.ts
3078
- import { and as and20, eq as eq20, gt as gt6 } from "drizzle-orm";
3040
+ import { and as and20, eq as eq20, gt as gt7 } from "drizzle-orm";
3079
3041
 
3080
3042
  // src/routes/password/helper/session.ts
3081
3043
  var createPasswordResetSession = async ({
@@ -3155,7 +3117,7 @@ var resetPasswordHandler = async (c) => {
3155
3117
  and20(
3156
3118
  eq20(sessionsInIam.tenantId, resolvedTenantId),
3157
3119
  eq20(sessionsInIam.userId, verification.userId),
3158
- gt6(sessionsInIam.expiresAt, (/* @__PURE__ */ new Date()).toISOString())
3120
+ gt7(sessionsInIam.expiresAt, (/* @__PURE__ */ new Date()).toISOString())
3159
3121
  )
3160
3122
  );
3161
3123
  await database.delete(verificationsInIam).where(eq20(verificationsInIam.id, verificationId));
@@ -4221,7 +4183,7 @@ import { createRoute as createRoute7, OpenAPIHono as OpenAPIHono7 } from "@hono/
4221
4183
  import { z as z4 } from "zod";
4222
4184
 
4223
4185
  // src/routes/profile/handler/account-change-pending.ts
4224
- import { and as and26, eq as eq27, gt as gt7, sql as sql13 } from "drizzle-orm";
4186
+ import { and as and26, eq as eq27, gt as gt8, sql as sql13 } from "drizzle-orm";
4225
4187
  var accountChangePendingHandler = async (c) => {
4226
4188
  const config = c.get("config");
4227
4189
  const database = c.get("database");
@@ -4259,7 +4221,7 @@ var accountChangePendingHandler = async (c) => {
4259
4221
  eq27(verificationsInIam.userId, userId),
4260
4222
  eq27(verificationsInIam.type, "email-verification"),
4261
4223
  eq27(verificationsInIam.to, accountChange.newEmail),
4262
- gt7(verificationsInIam.expiresAt, sql13`CURRENT_TIMESTAMP`)
4224
+ gt8(verificationsInIam.expiresAt, sql13`CURRENT_TIMESTAMP`)
4263
4225
  )
4264
4226
  ).limit(1);
4265
4227
  verification = v || null;
@@ -4274,7 +4236,7 @@ var accountChangePendingHandler = async (c) => {
4274
4236
  eq27(verificationsInIam.userId, userId),
4275
4237
  eq27(verificationsInIam.type, "phone-otp-change-phone"),
4276
4238
  eq27(verificationsInIam.to, accountChange.newPhone),
4277
- gt7(verificationsInIam.expiresAt, sql13`CURRENT_TIMESTAMP`)
4239
+ gt8(verificationsInIam.expiresAt, sql13`CURRENT_TIMESTAMP`)
4278
4240
  )
4279
4241
  ).limit(1);
4280
4242
  verification = v || null;
@@ -6959,6 +6921,9 @@ var banUserHandler = async (c) => {
6959
6921
  return c.json({ user: normalizeUser(userWithRoles) }, 200);
6960
6922
  };
6961
6923
 
6924
+ // src/routes/users/helper/invite.ts
6925
+ import { and as and52, eq as eq57 } from "drizzle-orm";
6926
+
6962
6927
  // src/routes/users/helper/user.ts
6963
6928
  import { and as and51, eq as eq56, sql as sql26 } from "drizzle-orm";
6964
6929
  var checkUserExists = async ({
@@ -7028,7 +6993,89 @@ var inviteUser = (
7028
6993
  isEmail
7029
6994
  });
7030
6995
  if (existing) {
7031
- throw new Error("User already exists");
6996
+ const identifierVerified = isEmail ? existing.emailVerified : existing.phoneVerified;
6997
+ const hasInviteType = Array.isArray(existing.userType) && existing.userType.includes(config.userType);
6998
+ if (identifierVerified && hasInviteType) {
6999
+ throw new Error("User already exists");
7000
+ }
7001
+ const updates = {};
7002
+ if (!identifierVerified) {
7003
+ if (payload.emailVerified !== void 0 && payload.email) {
7004
+ updates.emailVerified = Boolean(payload.emailVerified);
7005
+ }
7006
+ if (payload.phoneVerified !== void 0 && payload.phone) {
7007
+ updates.phoneVerified = Boolean(payload.phoneVerified);
7008
+ }
7009
+ }
7010
+ if (!hasInviteType) {
7011
+ updates.userType = [config.userType];
7012
+ }
7013
+ let user2;
7014
+ if (Object.keys(updates).length > 0) {
7015
+ const [updated] = await database.update(usersInIam).set(updates).where(eq57(usersInIam.id, existing.id)).returning();
7016
+ if (!updated) {
7017
+ throw new Error("User update failed");
7018
+ }
7019
+ user2 = updated;
7020
+ } else {
7021
+ user2 = existing;
7022
+ }
7023
+ const [credAccount] = await database.select().from(accountsInIam).where(
7024
+ and52(
7025
+ eq57(accountsInIam.tenantId, tenantId),
7026
+ eq57(accountsInIam.userId, user2.id),
7027
+ eq57(accountsInIam.provider, "credentials")
7028
+ )
7029
+ ).limit(1);
7030
+ const hasPassword2 = Boolean(credAccount?.password);
7031
+ const resolvedInviteUrl2 = resolveInviteUrl({
7032
+ inviteUrl: payload.inviteUrl,
7033
+ identifier,
7034
+ hasPassword: hasPassword2
7035
+ });
7036
+ const delivery2 = {};
7037
+ if (payload.sendVia?.includes("email")) {
7038
+ if (payload.email && config.email.sendInvitation) {
7039
+ try {
7040
+ await config.email.sendInvitation({
7041
+ email: payload.email,
7042
+ fullName: user2.fullName,
7043
+ identifier,
7044
+ inviteUrl: resolvedInviteUrl2,
7045
+ tenantId
7046
+ });
7047
+ delivery2.email = "sent";
7048
+ } catch {
7049
+ delivery2.email = "failed";
7050
+ }
7051
+ } else {
7052
+ delivery2.email = "skipped";
7053
+ }
7054
+ }
7055
+ if (payload.sendVia?.includes("sms")) {
7056
+ if (payload.phone && config.phone.sendInvitation) {
7057
+ try {
7058
+ await config.phone.sendInvitation({
7059
+ phone: payload.phone,
7060
+ fullName: user2.fullName,
7061
+ identifier,
7062
+ inviteUrl: resolvedInviteUrl2,
7063
+ tenantId
7064
+ });
7065
+ delivery2.sms = "sent";
7066
+ } catch {
7067
+ delivery2.sms = "failed";
7068
+ }
7069
+ } else {
7070
+ delivery2.sms = "skipped";
7071
+ }
7072
+ }
7073
+ return {
7074
+ user: normalizeUser(user2),
7075
+ delivery: delivery2,
7076
+ inviteUrl: resolvedInviteUrl2,
7077
+ hasPassword: hasPassword2
7078
+ };
7032
7079
  }
7033
7080
  const userHandle = payload.handle || generateHandle();
7034
7081
  const existingHandle = await checkHandleExists({
@@ -7213,16 +7260,16 @@ var createUserHandler = async (c) => {
7213
7260
  };
7214
7261
 
7215
7262
  // src/routes/users/handler/delete-user.ts
7216
- import { and as and52, eq as eq57 } from "drizzle-orm";
7263
+ import { and as and53, eq as eq58 } from "drizzle-orm";
7217
7264
  var deleteUserHandler = async (c) => {
7218
7265
  const { id } = c.req.valid("param");
7219
7266
  const database = c.get("database");
7220
7267
  const tenantId = c.get("tenantId");
7221
- const [existing] = await database.select().from(usersInIam).where(and52(eq57(usersInIam.id, id), eq57(usersInIam.tenantId, tenantId))).limit(1);
7268
+ const [existing] = await database.select().from(usersInIam).where(and53(eq58(usersInIam.id, id), eq58(usersInIam.tenantId, tenantId))).limit(1);
7222
7269
  if (!existing) {
7223
7270
  return c.json({ error: "User not found" }, 404);
7224
7271
  }
7225
- await database.delete(usersInIam).where(and52(eq57(usersInIam.id, id), eq57(usersInIam.tenantId, tenantId)));
7272
+ await database.delete(usersInIam).where(and53(eq58(usersInIam.id, id), eq58(usersInIam.tenantId, tenantId)));
7226
7273
  return c.json({ message: "User deleted" }, 200);
7227
7274
  };
7228
7275
 
@@ -7266,7 +7313,7 @@ var inviteUserHandler = async (c) => {
7266
7313
  };
7267
7314
 
7268
7315
  // src/routes/users/handler/list-users.ts
7269
- import { and as and53, asc as asc5, desc as desc5, eq as eq58, gt as gt8, ilike as ilike4, inArray as inArray6, or as or4, sql as sql27 } from "drizzle-orm";
7316
+ import { and as and54, asc as asc5, desc as desc5, eq as eq59, gt as gt9, ilike as ilike4, inArray as inArray6, or as or4, sql as sql27 } from "drizzle-orm";
7270
7317
  var userSelect = {
7271
7318
  id: usersInIam.id,
7272
7319
  tenantId: usersInIam.tenantId,
@@ -7319,7 +7366,7 @@ var listUsersHandler = async (c) => {
7319
7366
  const limit = query.limit || 20;
7320
7367
  const offset = (page - 1) * limit;
7321
7368
  const userTypeFilter = query.userType && query.userType !== "all" ? query.userType : null;
7322
- const conditions = [eq58(usersInIam.tenantId, tenantId)];
7369
+ const conditions = [eq59(usersInIam.tenantId, tenantId)];
7323
7370
  if (userTypeFilter) {
7324
7371
  conditions.push(
7325
7372
  sql27`${usersInIam.userType} @> ARRAY[${userTypeFilter}]::text[]`
@@ -7347,19 +7394,19 @@ var listUsersHandler = async (c) => {
7347
7394
  }
7348
7395
  if (query.filter === "verified") {
7349
7396
  const verifiedCond = or4(
7350
- eq58(usersInIam.emailVerified, true),
7351
- eq58(usersInIam.phoneVerified, true)
7397
+ eq59(usersInIam.emailVerified, true),
7398
+ eq59(usersInIam.phoneVerified, true)
7352
7399
  );
7353
7400
  if (verifiedCond) {
7354
7401
  conditions.push(verifiedCond);
7355
7402
  }
7356
7403
  } else if (query.filter === "unverified") {
7357
- conditions.push(eq58(usersInIam.emailVerified, false));
7358
- conditions.push(eq58(usersInIam.phoneVerified, false));
7404
+ conditions.push(eq59(usersInIam.emailVerified, false));
7405
+ conditions.push(eq59(usersInIam.phoneVerified, false));
7359
7406
  }
7360
7407
  const orderDir = query.order === "asc" ? asc5 : desc5;
7361
7408
  const sortCol = query.sort && sortColumnMap4[query.sort] ? sortColumnMap4[query.sort] : usersInIam.fullName;
7362
- const whereClause = and53(...conditions);
7409
+ const whereClause = and54(...conditions);
7363
7410
  const [users, totalResult] = await Promise.all([
7364
7411
  database.select(userSelect).from(usersInIam).where(whereClause).orderBy(orderDir(sortCol)).limit(limit).offset(offset),
7365
7412
  database.select({ count: sql27`count(*)` }).from(usersInIam).where(whereClause)
@@ -7370,10 +7417,10 @@ var listUsersHandler = async (c) => {
7370
7417
  userId: sessionsInIam.userId,
7371
7418
  count: sql27`count(*)::int`.as("count")
7372
7419
  }).from(sessionsInIam).where(
7373
- and53(
7374
- eq58(sessionsInIam.tenantId, tenantId),
7420
+ and54(
7421
+ eq59(sessionsInIam.tenantId, tenantId),
7375
7422
  inArray6(sessionsInIam.userId, userIds),
7376
- gt8(sessionsInIam.expiresAt, (/* @__PURE__ */ new Date()).toISOString())
7423
+ gt9(sessionsInIam.expiresAt, (/* @__PURE__ */ new Date()).toISOString())
7377
7424
  )
7378
7425
  ).groupBy(sessionsInIam.userId) : [];
7379
7426
  const sessionCountByUser = new Map(
@@ -7385,13 +7432,13 @@ var listUsersHandler = async (c) => {
7385
7432
  name: rolesInIam.name
7386
7433
  }).from(userRolesInIam).innerJoin(
7387
7434
  rolesInIam,
7388
- and53(
7389
- eq58(rolesInIam.tenantId, userRolesInIam.tenantId),
7390
- eq58(rolesInIam.id, userRolesInIam.roleId)
7435
+ and54(
7436
+ eq59(rolesInIam.tenantId, userRolesInIam.tenantId),
7437
+ eq59(rolesInIam.id, userRolesInIam.roleId)
7391
7438
  )
7392
7439
  ).where(
7393
- and53(
7394
- eq58(userRolesInIam.tenantId, tenantId),
7440
+ and54(
7441
+ eq59(userRolesInIam.tenantId, tenantId),
7395
7442
  inArray6(userRolesInIam.userId, userIds)
7396
7443
  )
7397
7444
  ) : [];
@@ -7421,13 +7468,13 @@ var listUsersHandler = async (c) => {
7421
7468
  };
7422
7469
 
7423
7470
  // src/routes/users/handler/search-users.ts
7424
- import { and as and54, eq as eq59, ilike as ilike5, or as or5 } from "drizzle-orm";
7471
+ import { and as and55, eq as eq60, ilike as ilike5, or as or5 } from "drizzle-orm";
7425
7472
  var searchUsersHandler = async (c) => {
7426
7473
  const query = c.req.valid("query");
7427
7474
  const database = c.get("database");
7428
7475
  const tenantId = c.get("tenantId");
7429
7476
  const limit = query.limit || 20;
7430
- const conditions = [eq59(usersInIam.tenantId, tenantId)];
7477
+ const conditions = [eq60(usersInIam.tenantId, tenantId)];
7431
7478
  if (query.search && query.search.trim().length > 0) {
7432
7479
  const searchCondition = or5(
7433
7480
  ilike5(usersInIam.fullName, `%${query.search}%`),
@@ -7445,25 +7492,25 @@ var searchUsersHandler = async (c) => {
7445
7492
  phone: usersInIam.phone,
7446
7493
  handle: usersInIam.handle,
7447
7494
  image: usersInIam.image
7448
- }).from(usersInIam).where(and54(...conditions)).limit(limit);
7495
+ }).from(usersInIam).where(and55(...conditions)).limit(limit);
7449
7496
  return c.json({ users }, 200);
7450
7497
  };
7451
7498
 
7452
7499
  // src/routes/users/handler/update-user.ts
7453
- import { and as and55, eq as eq60, inArray as inArray7, sql as sql28 } from "drizzle-orm";
7500
+ import { and as and56, eq as eq61, inArray as inArray7, sql as sql28 } from "drizzle-orm";
7454
7501
  var updateUserHandler = async (c) => {
7455
7502
  const { id } = c.req.valid("param");
7456
7503
  const body = c.req.valid("json");
7457
7504
  const database = c.get("database");
7458
7505
  const tenantId = c.get("tenantId");
7459
- const [existing] = await database.select().from(usersInIam).where(and55(eq60(usersInIam.id, id), eq60(usersInIam.tenantId, tenantId))).limit(1);
7506
+ const [existing] = await database.select().from(usersInIam).where(and56(eq61(usersInIam.id, id), eq61(usersInIam.tenantId, tenantId))).limit(1);
7460
7507
  if (!existing) {
7461
7508
  return c.json({ error: "User not found" }, 404);
7462
7509
  }
7463
7510
  if (body.handle && body.handle !== existing.handle) {
7464
7511
  const [handleExists] = await database.select().from(usersInIam).where(
7465
- and55(
7466
- eq60(usersInIam.tenantId, tenantId),
7512
+ and56(
7513
+ eq61(usersInIam.tenantId, tenantId),
7467
7514
  sql28`lower(${usersInIam.handle}) = lower(${body.handle})`
7468
7515
  )
7469
7516
  ).limit(1);
@@ -7496,7 +7543,7 @@ var updateUserHandler = async (c) => {
7496
7543
  const [updated] = await database.update(usersInIam).set({
7497
7544
  ...updateData,
7498
7545
  updatedAt: sql28`CURRENT_TIMESTAMP`
7499
- }).where(and55(eq60(usersInIam.id, id), eq60(usersInIam.tenantId, tenantId))).returning({
7546
+ }).where(and56(eq61(usersInIam.id, id), eq61(usersInIam.tenantId, tenantId))).returning({
7500
7547
  id: usersInIam.id,
7501
7548
  tenantId: usersInIam.tenantId,
7502
7549
  fullName: usersInIam.fullName,
@@ -7515,8 +7562,8 @@ var updateUserHandler = async (c) => {
7515
7562
  const roleIds = body.roleIds;
7516
7563
  if (roleIds.length > 0) {
7517
7564
  const validRoles = await database.select({ id: rolesInIam.id, code: rolesInIam.code }).from(rolesInIam).where(
7518
- and55(
7519
- eq60(rolesInIam.tenantId, tenantId),
7565
+ and56(
7566
+ eq61(rolesInIam.tenantId, tenantId),
7520
7567
  inArray7(rolesInIam.id, roleIds)
7521
7568
  )
7522
7569
  );
@@ -7525,9 +7572,9 @@ var updateUserHandler = async (c) => {
7525
7572
  );
7526
7573
  const toInsert = roleIds.filter((rid) => assignableIds.has(rid));
7527
7574
  await database.delete(userRolesInIam).where(
7528
- and55(
7529
- eq60(userRolesInIam.tenantId, tenantId),
7530
- eq60(userRolesInIam.userId, id)
7575
+ and56(
7576
+ eq61(userRolesInIam.tenantId, tenantId),
7577
+ eq61(userRolesInIam.userId, id)
7531
7578
  )
7532
7579
  );
7533
7580
  if (toInsert.length > 0) {
@@ -7541,9 +7588,9 @@ var updateUserHandler = async (c) => {
7541
7588
  }
7542
7589
  } else {
7543
7590
  await database.delete(userRolesInIam).where(
7544
- and55(
7545
- eq60(userRolesInIam.tenantId, tenantId),
7546
- eq60(userRolesInIam.userId, id)
7591
+ and56(
7592
+ eq61(userRolesInIam.tenantId, tenantId),
7593
+ eq61(userRolesInIam.userId, id)
7547
7594
  )
7548
7595
  );
7549
7596
  }
@@ -7957,31 +8004,31 @@ var users_route_default = userRoutes;
7957
8004
  import { createRoute as createRoute15, OpenAPIHono as OpenAPIHono15 } from "@hono/zod-openapi";
7958
8005
 
7959
8006
  // src/routes/verifications/handler/invalidate-verification.ts
7960
- import { and as and56, eq as eq61 } from "drizzle-orm";
8007
+ import { and as and57, eq as eq62 } from "drizzle-orm";
7961
8008
  var invalidateVerificationHandler = async (c) => {
7962
8009
  const { id } = c.req.valid("param");
7963
8010
  const database = c.get("database");
7964
8011
  const tenantId = c.get("tenantId");
7965
8012
  const [existing] = await database.select().from(verificationsInIam).where(
7966
- and56(
7967
- eq61(verificationsInIam.id, id),
7968
- eq61(verificationsInIam.tenantId, tenantId)
8013
+ and57(
8014
+ eq62(verificationsInIam.id, id),
8015
+ eq62(verificationsInIam.tenantId, tenantId)
7969
8016
  )
7970
8017
  ).limit(1);
7971
8018
  if (!existing) {
7972
8019
  return c.json({ error: "Verification not found" }, 404);
7973
8020
  }
7974
8021
  await database.delete(verificationsInIam).where(
7975
- and56(
7976
- eq61(verificationsInIam.id, id),
7977
- eq61(verificationsInIam.tenantId, tenantId)
8022
+ and57(
8023
+ eq62(verificationsInIam.id, id),
8024
+ eq62(verificationsInIam.tenantId, tenantId)
7978
8025
  )
7979
8026
  );
7980
8027
  return c.json({ message: "Verification invalidated" }, 200);
7981
8028
  };
7982
8029
 
7983
8030
  // src/routes/verifications/handler/list-verifications.ts
7984
- import { and as and57, eq as eq62, sql as sql29 } from "drizzle-orm";
8031
+ import { and as and58, eq as eq63, sql as sql29 } from "drizzle-orm";
7985
8032
  var listVerificationsHandler = async (c) => {
7986
8033
  const query = c.req.valid("query");
7987
8034
  const database = c.get("database");
@@ -7989,12 +8036,12 @@ var listVerificationsHandler = async (c) => {
7989
8036
  const page = query.page || 1;
7990
8037
  const limit = query.limit || 20;
7991
8038
  const offset = (page - 1) * limit;
7992
- const conditions = [eq62(verificationsInIam.tenantId, tenantId)];
8039
+ const conditions = [eq63(verificationsInIam.tenantId, tenantId)];
7993
8040
  if (query.userId) {
7994
- conditions.push(eq62(verificationsInIam.userId, query.userId));
8041
+ conditions.push(eq63(verificationsInIam.userId, query.userId));
7995
8042
  }
7996
8043
  if (query.type) {
7997
- conditions.push(eq62(verificationsInIam.type, query.type));
8044
+ conditions.push(eq63(verificationsInIam.type, query.type));
7998
8045
  }
7999
8046
  if (query.status) {
8000
8047
  if (query.status === "active") {
@@ -8008,8 +8055,8 @@ var listVerificationsHandler = async (c) => {
8008
8055
  }
8009
8056
  }
8010
8057
  const [verifications, totalResult] = await Promise.all([
8011
- database.select().from(verificationsInIam).where(and57(...conditions)).limit(limit).offset(offset),
8012
- database.select({ count: sql29`count(*)` }).from(verificationsInIam).where(and57(...conditions))
8058
+ database.select().from(verificationsInIam).where(and58(...conditions)).limit(limit).offset(offset),
8059
+ database.select({ count: sql29`count(*)` }).from(verificationsInIam).where(and58(...conditions))
8013
8060
  ]);
8014
8061
  const total = Number(totalResult[0]?.count || 0);
8015
8062
  return c.json({ verifications, total, page, limit }, 200);