@mesob/auth-hono 0.4.6 → 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-zShda6U3.d.ts → index-D8OE85f8.d.ts} +2 -58
- package/dist/{index-CKOeabpa.d.ts → index-DwIwuvVj.d.ts} +11 -2
- package/dist/index.d.ts +7 -14
- package/dist/index.js +768 -690
- package/dist/index.js.map +1 -1
- package/dist/lib/cleanup.d.ts +2 -2
- package/dist/lib/cookie.d.ts +3 -3
- package/dist/lib/has-role-permission.d.ts +3 -3
- package/dist/lib/iam-seed.d.ts +4 -76
- package/dist/lib/normalize-auth-response.d.ts +3 -3
- package/dist/lib/normalize-user.d.ts +3 -3
- package/dist/lib/openapi-config.d.ts +3 -3
- package/dist/lib/permission-catalog.d.ts +2 -2
- package/dist/lib/phone-validation.d.ts +3 -3
- package/dist/lib/session.d.ts +3 -3
- package/dist/lib/tenant.d.ts +3 -3
- package/package.json +4 -4
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
|
|
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 });
|
|
@@ -612,6 +486,7 @@ var fetchUserWithRoles = async ({
|
|
|
612
486
|
emailVerified: usersInIam.emailVerified,
|
|
613
487
|
phoneVerified: usersInIam.phoneVerified,
|
|
614
488
|
lastSignInAt: usersInIam.lastSignInAt,
|
|
489
|
+
bannedUntil: usersInIam.bannedUntil,
|
|
615
490
|
...getUserAuthSelect(tenantId)
|
|
616
491
|
}).from(usersInIam).where(and4(eq4(usersInIam.id, userId), eq4(usersInIam.tenantId, tenantId))).limit(1);
|
|
617
492
|
return userResult || null;
|
|
@@ -862,6 +737,7 @@ var userSchema = z.object({
|
|
|
862
737
|
emailVerified: z.boolean(),
|
|
863
738
|
phoneVerified: z.boolean(),
|
|
864
739
|
lastSignInAt: z.string().datetime().nullable(),
|
|
740
|
+
bannedUntil: z.string().datetime().nullable().optional(),
|
|
865
741
|
createdAt: z.string().datetime().nullable().optional(),
|
|
866
742
|
userType: z.array(z.string()).optional(),
|
|
867
743
|
roles: z.array(z.string()).nullable().optional(),
|
|
@@ -993,6 +869,8 @@ var checkAccountResponseSchema = z.object({
|
|
|
993
869
|
verified: z.boolean(),
|
|
994
870
|
hasPassword: z.boolean(),
|
|
995
871
|
requiresPasswordSetup: z.boolean(),
|
|
872
|
+
needsVerification: z.boolean().optional(),
|
|
873
|
+
verificationId: z.string().uuid().optional(),
|
|
996
874
|
account: authAccountSchema.nullable()
|
|
997
875
|
});
|
|
998
876
|
var updateProfileSchema = z.object({
|
|
@@ -1019,7 +897,7 @@ var pendingAccountChangeResponseSchema = z.object({
|
|
|
1019
897
|
});
|
|
1020
898
|
|
|
1021
899
|
// src/routes/auth/handler/check-account.ts
|
|
1022
|
-
import { and as
|
|
900
|
+
import { and as and6, eq as eq6, sql as sql4 } from "drizzle-orm";
|
|
1023
901
|
|
|
1024
902
|
// src/lib/tenant.ts
|
|
1025
903
|
import { HTTPException as HTTPException2 } from "hono/http-exception";
|
|
@@ -1041,79 +919,9 @@ var ensureTenantId = (config, tenantId) => {
|
|
|
1041
919
|
return config.tenant.tenantId;
|
|
1042
920
|
};
|
|
1043
921
|
|
|
1044
|
-
// src/routes/auth/
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
const config = c.get("config");
|
|
1048
|
-
const database = c.get("database");
|
|
1049
|
-
const tenantId = c.get("tenantId");
|
|
1050
|
-
const resolvedTenantId = ensureTenantId(config, tenantId);
|
|
1051
|
-
const { username } = body;
|
|
1052
|
-
const isEmail = username.includes("@");
|
|
1053
|
-
const userTypeFilter = sql4`${usersInIam.userType} @> ARRAY[${config.userType}]::text[]`;
|
|
1054
|
-
const whereClause = isEmail ? and5(
|
|
1055
|
-
eq5(usersInIam.tenantId, resolvedTenantId),
|
|
1056
|
-
userTypeFilter,
|
|
1057
|
-
sql4`lower(${usersInIam.email}) = lower(${username})`
|
|
1058
|
-
) : and5(
|
|
1059
|
-
eq5(usersInIam.tenantId, resolvedTenantId),
|
|
1060
|
-
userTypeFilter,
|
|
1061
|
-
eq5(usersInIam.phone, username)
|
|
1062
|
-
);
|
|
1063
|
-
const [result] = await database.select({
|
|
1064
|
-
fullName: usersInIam.fullName,
|
|
1065
|
-
email: usersInIam.email,
|
|
1066
|
-
phone: usersInIam.phone,
|
|
1067
|
-
verified: isEmail ? usersInIam.emailVerified : usersInIam.phoneVerified,
|
|
1068
|
-
hasPassword: sql4`exists(
|
|
1069
|
-
select 1
|
|
1070
|
-
from ${accountsInIam}
|
|
1071
|
-
where ${eq5(accountsInIam.tenantId, resolvedTenantId)}
|
|
1072
|
-
and ${eq5(accountsInIam.userId, usersInIam.id)}
|
|
1073
|
-
and ${eq5(accountsInIam.provider, "credentials")}
|
|
1074
|
-
and ${sql4`${accountsInIam.password} is not null`}
|
|
1075
|
-
)`
|
|
1076
|
-
}).from(usersInIam).where(whereClause).limit(1);
|
|
1077
|
-
const verified = result?.verified ?? false;
|
|
1078
|
-
const hasPassword = result?.hasPassword ?? false;
|
|
1079
|
-
return c.json(
|
|
1080
|
-
{
|
|
1081
|
-
exists: !!result,
|
|
1082
|
-
verified,
|
|
1083
|
-
hasPassword,
|
|
1084
|
-
requiresPasswordSetup: !!result && verified && !hasPassword,
|
|
1085
|
-
account: result ? {
|
|
1086
|
-
fullName: result.fullName,
|
|
1087
|
-
email: result.email,
|
|
1088
|
-
phone: result.phone,
|
|
1089
|
-
verified,
|
|
1090
|
-
hasPassword,
|
|
1091
|
-
requiresPasswordSetup: verified && !hasPassword
|
|
1092
|
-
} : null
|
|
1093
|
-
},
|
|
1094
|
-
200
|
|
1095
|
-
);
|
|
1096
|
-
};
|
|
1097
|
-
|
|
1098
|
-
// src/routes/auth/handler/sign-in.ts
|
|
1099
|
-
import { logger as logger2 } from "@mesob/common";
|
|
1100
|
-
import { and as and9, eq as eq9 } from "drizzle-orm";
|
|
1101
|
-
|
|
1102
|
-
// src/errors.ts
|
|
1103
|
-
var AUTH_ERRORS = {
|
|
1104
|
-
USER_NOT_FOUND: "USER_NOT_FOUND",
|
|
1105
|
-
INVALID_PASSWORD: "INVALID_PASSWORD",
|
|
1106
|
-
USER_EXISTS: "USER_EXISTS",
|
|
1107
|
-
VERIFICATION_EXPIRED: "VERIFICATION_EXPIRED",
|
|
1108
|
-
VERIFICATION_MISMATCH: "VERIFICATION_MISMATCH",
|
|
1109
|
-
VERIFICATION_NOT_FOUND: "VERIFICATION_NOT_FOUND",
|
|
1110
|
-
TOO_MANY_ATTEMPTS: "TOO_MANY_ATTEMPTS",
|
|
1111
|
-
REQUIRES_VERIFICATION: "REQUIRES_VERIFICATION",
|
|
1112
|
-
UNAUTHORIZED: "UNAUTHORIZED",
|
|
1113
|
-
ACCESS_DENIED: "ACCESS_DENIED",
|
|
1114
|
-
HAS_NO_PASSWORD: "HAS_NO_PASSWORD",
|
|
1115
|
-
PASSWORD_ALREADY_SET: "PASSWORD_ALREADY_SET"
|
|
1116
|
-
};
|
|
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";
|
|
1117
925
|
|
|
1118
926
|
// src/lib/normalize-auth-response.ts
|
|
1119
927
|
var normalizeAuthUser = (user) => ({
|
|
@@ -1207,278 +1015,125 @@ var getRefreshedExpiresAt = ({
|
|
|
1207
1015
|
return addDuration(duration);
|
|
1208
1016
|
};
|
|
1209
1017
|
|
|
1210
|
-
// src/routes/auth/helper/
|
|
1211
|
-
|
|
1212
|
-
var createSessionRecord = async ({
|
|
1018
|
+
// src/routes/auth/helper/verification.ts
|
|
1019
|
+
var createVerification = async ({
|
|
1213
1020
|
tx,
|
|
1214
1021
|
tenantId,
|
|
1215
1022
|
userId,
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
action,
|
|
1220
|
-
rememberMe,
|
|
1221
|
-
expiresIn
|
|
1023
|
+
type,
|
|
1024
|
+
to,
|
|
1025
|
+
config
|
|
1222
1026
|
}) => {
|
|
1223
|
-
const
|
|
1224
|
-
const
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
meta.rememberMe = rememberMe;
|
|
1233
|
-
}
|
|
1234
|
-
const [session] = await tx.insert(sessionsInIam).values({
|
|
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({
|
|
1235
1036
|
tenantId,
|
|
1236
1037
|
userId,
|
|
1237
|
-
|
|
1038
|
+
type,
|
|
1039
|
+
code: hashedCode,
|
|
1238
1040
|
expiresAt,
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
}
|
|
1243
|
-
id: sessionsInIam.id,
|
|
1244
|
-
expiresAt: sessionsInIam.expiresAt,
|
|
1245
|
-
createdAt: sessionsInIam.createdAt,
|
|
1246
|
-
updatedAt: sessionsInIam.updatedAt,
|
|
1247
|
-
userAgent: sessionsInIam.userAgent,
|
|
1248
|
-
ip: sessionsInIam.ip
|
|
1249
|
-
});
|
|
1250
|
-
return { session, sessionToken, expiresAt };
|
|
1041
|
+
to,
|
|
1042
|
+
attempt: 0
|
|
1043
|
+
}).returning();
|
|
1044
|
+
return { verificationId: verification.id, code, hash: hashedCode };
|
|
1251
1045
|
};
|
|
1252
|
-
var
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1046
|
+
var sendVerification = async ({
|
|
1047
|
+
channel,
|
|
1048
|
+
to,
|
|
1049
|
+
code,
|
|
1256
1050
|
config,
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
rememberMe = true
|
|
1051
|
+
hash,
|
|
1052
|
+
type
|
|
1260
1053
|
}) => {
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
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
|
+
}
|
|
1272
1069
|
};
|
|
1273
|
-
var
|
|
1070
|
+
var checkVerificationResend = async ({
|
|
1274
1071
|
database,
|
|
1275
|
-
userId,
|
|
1276
1072
|
tenantId,
|
|
1277
|
-
|
|
1073
|
+
userId,
|
|
1074
|
+
type,
|
|
1075
|
+
resendInterval
|
|
1278
1076
|
}) => {
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
eq6(sessionsInIam.tenantId, tenantId),
|
|
1282
|
-
eq6(sessionsInIam.userId, userId),
|
|
1283
|
-
gt2(sessionsInIam.expiresAt, (/* @__PURE__ */ new Date()).toISOString())
|
|
1284
|
-
)
|
|
1285
|
-
);
|
|
1286
|
-
if (count <= maxSessions) {
|
|
1287
|
-
return;
|
|
1077
|
+
if (!resendInterval) {
|
|
1078
|
+
return { blocked: false };
|
|
1288
1079
|
}
|
|
1289
|
-
const
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
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)
|
|
1295
1088
|
)
|
|
1296
|
-
).orderBy(
|
|
1297
|
-
if (!
|
|
1298
|
-
return;
|
|
1089
|
+
).orderBy(desc(verificationsInIam.createdAt)).limit(1);
|
|
1090
|
+
if (!verification) {
|
|
1091
|
+
return { blocked: false };
|
|
1299
1092
|
}
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
inArray(
|
|
1305
|
-
sessionsInIam.id,
|
|
1306
|
-
idsToDelete.map((s) => s.id)
|
|
1307
|
-
)
|
|
1308
|
-
)
|
|
1309
|
-
);
|
|
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 };
|
|
1310
1097
|
};
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
import { and as and7, eq as eq7, gt as gt3, sql as sql6 } from "drizzle-orm";
|
|
1314
|
-
var checkExistingUserStatus = async ({
|
|
1315
|
-
tx,
|
|
1316
|
-
identifier,
|
|
1098
|
+
var ensureVerificationForCheckAccount = async ({
|
|
1099
|
+
database,
|
|
1317
1100
|
tenantId,
|
|
1318
|
-
|
|
1101
|
+
userId,
|
|
1102
|
+
type,
|
|
1103
|
+
to,
|
|
1104
|
+
config
|
|
1319
1105
|
}) => {
|
|
1320
|
-
const
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
return { action: "error", code: AUTH_ERRORS.USER_EXISTS };
|
|
1331
|
-
}
|
|
1332
|
-
const [pendingVerification] = await tx.select().from(verificationsInIam).where(
|
|
1333
|
-
and7(
|
|
1334
|
-
eq7(verificationsInIam.userId, existingUser.id),
|
|
1335
|
-
eq7(verificationsInIam.tenantId, tenantId),
|
|
1336
|
-
gt3(verificationsInIam.expiresAt, (/* @__PURE__ */ new Date()).toISOString())
|
|
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)
|
|
1337
1116
|
)
|
|
1338
|
-
).limit(1);
|
|
1339
|
-
if (
|
|
1340
|
-
return {
|
|
1341
|
-
action: "pending",
|
|
1342
|
-
verificationId: pendingVerification.id,
|
|
1343
|
-
user: existingUser
|
|
1344
|
-
};
|
|
1117
|
+
).orderBy(desc(verificationsInIam.createdAt)).limit(1);
|
|
1118
|
+
if (existing) {
|
|
1119
|
+
return { verificationId: existing.id };
|
|
1345
1120
|
}
|
|
1346
|
-
await
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
userId,
|
|
1352
|
-
tenantId
|
|
1353
|
-
}) => {
|
|
1354
|
-
await tx.delete(verificationsInIam).where(
|
|
1355
|
-
and7(
|
|
1356
|
-
eq7(verificationsInIam.userId, userId),
|
|
1357
|
-
eq7(verificationsInIam.tenantId, tenantId)
|
|
1121
|
+
await database.delete(verificationsInIam).where(
|
|
1122
|
+
and5(
|
|
1123
|
+
eq5(verificationsInIam.tenantId, tenantId),
|
|
1124
|
+
eq5(verificationsInIam.userId, userId),
|
|
1125
|
+
eq5(verificationsInIam.type, type)
|
|
1358
1126
|
)
|
|
1359
1127
|
);
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
eq7(accountsInIam.tenantId, tenantId)
|
|
1364
|
-
)
|
|
1365
|
-
);
|
|
1366
|
-
await tx.delete(usersInIam).where(and7(eq7(usersInIam.id, userId), eq7(usersInIam.tenantId, tenantId)));
|
|
1367
|
-
};
|
|
1368
|
-
var createUserWithAccount = async ({
|
|
1369
|
-
tx,
|
|
1370
|
-
tenantId,
|
|
1371
|
-
email,
|
|
1372
|
-
phone,
|
|
1373
|
-
password,
|
|
1374
|
-
fullName,
|
|
1375
|
-
handle,
|
|
1376
|
-
config
|
|
1377
|
-
}) => {
|
|
1378
|
-
const [user] = await tx.insert(usersInIam).values({
|
|
1379
|
-
tenantId,
|
|
1380
|
-
fullName,
|
|
1381
|
-
handle,
|
|
1382
|
-
email: email || null,
|
|
1383
|
-
phone: phone || null,
|
|
1384
|
-
emailVerified: email ? !config.email.required : false,
|
|
1385
|
-
phoneVerified: phone ? !config.phone.required : false,
|
|
1386
|
-
userType: [config.userType]
|
|
1387
|
-
}).returning();
|
|
1388
|
-
const passwordHash = await hashPassword(password);
|
|
1389
|
-
await tx.insert(accountsInIam).values({
|
|
1390
|
-
tenantId,
|
|
1391
|
-
userId: user.id,
|
|
1392
|
-
provider: "credentials",
|
|
1393
|
-
providerAccountId: email || phone || user.id,
|
|
1394
|
-
password: passwordHash
|
|
1395
|
-
});
|
|
1396
|
-
return user;
|
|
1397
|
-
};
|
|
1398
|
-
var fetchUserForLogin = async ({
|
|
1399
|
-
database,
|
|
1400
|
-
identifier,
|
|
1401
|
-
tenantId,
|
|
1402
|
-
isEmail,
|
|
1403
|
-
userType
|
|
1404
|
-
}) => {
|
|
1405
|
-
const userTypeFilter = sql6`${usersInIam.userType} @> ARRAY[${userType}]::text[]`;
|
|
1406
|
-
const whereClause = isEmail ? and7(
|
|
1407
|
-
eq7(usersInIam.tenantId, tenantId),
|
|
1408
|
-
userTypeFilter,
|
|
1409
|
-
sql6`lower(${usersInIam.email}) = lower(${identifier})`
|
|
1410
|
-
) : and7(
|
|
1411
|
-
eq7(usersInIam.tenantId, tenantId),
|
|
1412
|
-
userTypeFilter,
|
|
1413
|
-
eq7(usersInIam.phone, identifier)
|
|
1414
|
-
);
|
|
1415
|
-
const [row] = await database.select({
|
|
1416
|
-
id: usersInIam.id,
|
|
1417
|
-
tenantId: usersInIam.tenantId,
|
|
1418
|
-
fullName: usersInIam.fullName,
|
|
1419
|
-
email: usersInIam.email,
|
|
1420
|
-
phone: usersInIam.phone,
|
|
1421
|
-
handle: usersInIam.handle,
|
|
1422
|
-
image: usersInIam.image,
|
|
1423
|
-
emailVerified: usersInIam.emailVerified,
|
|
1424
|
-
phoneVerified: usersInIam.phoneVerified,
|
|
1425
|
-
lastSignInAt: usersInIam.lastSignInAt,
|
|
1426
|
-
bannedUntil: usersInIam.bannedUntil,
|
|
1427
|
-
loginAttempt: usersInIam.loginAttempt,
|
|
1428
|
-
hasPassword: sql6`exists(
|
|
1429
|
-
select 1
|
|
1430
|
-
from ${accountsInIam}
|
|
1431
|
-
where ${eq7(accountsInIam.tenantId, tenantId)}
|
|
1432
|
-
and ${eq7(accountsInIam.userId, usersInIam.id)}
|
|
1433
|
-
and ${eq7(accountsInIam.provider, "credentials")}
|
|
1434
|
-
and ${sql6`${accountsInIam.password} is not null`}
|
|
1435
|
-
)`
|
|
1436
|
-
}).from(usersInIam).where(whereClause).limit(1);
|
|
1437
|
-
return row || null;
|
|
1438
|
-
};
|
|
1439
|
-
var fetchUserByIdWithRoles = async ({
|
|
1440
|
-
database,
|
|
1441
|
-
userId,
|
|
1442
|
-
tenantId
|
|
1443
|
-
}) => {
|
|
1444
|
-
const [result] = await database.select({
|
|
1445
|
-
id: usersInIam.id,
|
|
1446
|
-
tenantId: usersInIam.tenantId,
|
|
1447
|
-
fullName: usersInIam.fullName,
|
|
1448
|
-
email: usersInIam.email,
|
|
1449
|
-
phone: usersInIam.phone,
|
|
1450
|
-
handle: usersInIam.handle,
|
|
1451
|
-
image: usersInIam.image,
|
|
1452
|
-
emailVerified: usersInIam.emailVerified,
|
|
1453
|
-
phoneVerified: usersInIam.phoneVerified,
|
|
1454
|
-
lastSignInAt: usersInIam.lastSignInAt,
|
|
1455
|
-
bannedUntil: usersInIam.bannedUntil,
|
|
1456
|
-
loginAttempt: usersInIam.loginAttempt,
|
|
1457
|
-
...getUserAuthSelect(tenantId)
|
|
1458
|
-
}).from(usersInIam).where(and7(eq7(usersInIam.id, userId), eq7(usersInIam.tenantId, tenantId))).limit(1);
|
|
1459
|
-
return result || null;
|
|
1460
|
-
};
|
|
1461
|
-
|
|
1462
|
-
// src/routes/auth/helper/verification.ts
|
|
1463
|
-
import { dayjs as dayjs2 } from "@mesob/common";
|
|
1464
|
-
import { and as and8, desc, eq as eq8 } from "drizzle-orm";
|
|
1465
|
-
var createVerification = async ({
|
|
1466
|
-
tx,
|
|
1467
|
-
tenantId,
|
|
1468
|
-
userId,
|
|
1469
|
-
type,
|
|
1470
|
-
to,
|
|
1471
|
-
config
|
|
1472
|
-
}) => {
|
|
1473
|
-
const isPhone = type === "phone-otp-sign-up";
|
|
1474
|
-
const code = generateOtpCode(
|
|
1475
|
-
isPhone ? config.phone.otpLength : config.email.otpLength
|
|
1128
|
+
const isPhone = type === "phone-otp";
|
|
1129
|
+
const code = generateOtpCode(
|
|
1130
|
+
isPhone ? config.phone.otpLength : config.email.otpLength
|
|
1476
1131
|
);
|
|
1477
1132
|
const hashedCode = await hashToken(code, config.secret);
|
|
1478
1133
|
const expiresAt = addDuration(
|
|
1479
1134
|
isPhone ? config.phone.expiresIn : config.email.expiresIn
|
|
1480
1135
|
);
|
|
1481
|
-
const [verification] = await
|
|
1136
|
+
const [verification] = await database.insert(verificationsInIam).values({
|
|
1482
1137
|
tenantId,
|
|
1483
1138
|
userId,
|
|
1484
1139
|
type,
|
|
@@ -1487,59 +1142,22 @@ var createVerification = async ({
|
|
|
1487
1142
|
to,
|
|
1488
1143
|
attempt: 0
|
|
1489
1144
|
}).returning();
|
|
1490
|
-
|
|
1491
|
-
};
|
|
1492
|
-
var sendVerification = async ({
|
|
1493
|
-
channel,
|
|
1494
|
-
to,
|
|
1495
|
-
code,
|
|
1496
|
-
config,
|
|
1497
|
-
hash,
|
|
1498
|
-
type
|
|
1499
|
-
}) => {
|
|
1500
|
-
if (channel === "phone" && config.phone.sendVerificationOTP) {
|
|
1145
|
+
if (isPhone && config.phone.sendVerificationOTP) {
|
|
1501
1146
|
await config.phone.sendVerificationOTP({
|
|
1502
1147
|
phone: to,
|
|
1503
1148
|
code,
|
|
1504
|
-
hash,
|
|
1149
|
+
hash: hashedCode,
|
|
1505
1150
|
type
|
|
1506
1151
|
});
|
|
1507
|
-
} else if (config.email.sendVerificationOTP) {
|
|
1152
|
+
} else if (!isPhone && config.email.sendVerificationOTP) {
|
|
1508
1153
|
await config.email.sendVerificationOTP({
|
|
1509
1154
|
email: to,
|
|
1510
1155
|
code,
|
|
1511
|
-
hash,
|
|
1156
|
+
hash: hashedCode,
|
|
1512
1157
|
type
|
|
1513
1158
|
});
|
|
1514
1159
|
}
|
|
1515
|
-
};
|
|
1516
|
-
var checkVerificationResend = async ({
|
|
1517
|
-
database,
|
|
1518
|
-
tenantId,
|
|
1519
|
-
userId,
|
|
1520
|
-
type,
|
|
1521
|
-
resendInterval
|
|
1522
|
-
}) => {
|
|
1523
|
-
if (!resendInterval) {
|
|
1524
|
-
return { blocked: false };
|
|
1525
|
-
}
|
|
1526
|
-
const [verification] = await database.select({
|
|
1527
|
-
id: verificationsInIam.id,
|
|
1528
|
-
createdAt: verificationsInIam.createdAt
|
|
1529
|
-
}).from(verificationsInIam).where(
|
|
1530
|
-
and8(
|
|
1531
|
-
eq8(verificationsInIam.tenantId, tenantId),
|
|
1532
|
-
eq8(verificationsInIam.userId, userId),
|
|
1533
|
-
eq8(verificationsInIam.type, type)
|
|
1534
|
-
)
|
|
1535
|
-
).orderBy(desc(verificationsInIam.createdAt)).limit(1);
|
|
1536
|
-
if (!verification) {
|
|
1537
|
-
return { blocked: false };
|
|
1538
|
-
}
|
|
1539
|
-
const cooldownSeconds = parseDuration(resendInterval);
|
|
1540
|
-
const createdAt = dayjs2(verification.createdAt);
|
|
1541
|
-
const blocked = dayjs2().diff(createdAt, "second") < cooldownSeconds;
|
|
1542
|
-
return { blocked, verificationId: verification.id };
|
|
1160
|
+
return { verificationId: verification.id };
|
|
1543
1161
|
};
|
|
1544
1162
|
var handleEmailVerification = async ({
|
|
1545
1163
|
c,
|
|
@@ -1552,10 +1170,10 @@ var handleEmailVerification = async ({
|
|
|
1552
1170
|
return c.json({ error: "User email not found" }, 401);
|
|
1553
1171
|
}
|
|
1554
1172
|
await database.delete(verificationsInIam).where(
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1173
|
+
and5(
|
|
1174
|
+
eq5(verificationsInIam.tenantId, tenantId),
|
|
1175
|
+
eq5(verificationsInIam.userId, user.id),
|
|
1176
|
+
eq5(verificationsInIam.type, "email-verification")
|
|
1559
1177
|
)
|
|
1560
1178
|
);
|
|
1561
1179
|
const code = generateOtpCode(config.email.otpLength);
|
|
@@ -1588,72 +1206,418 @@ var handleEmailVerification = async ({
|
|
|
1588
1206
|
type: "email-verification"
|
|
1589
1207
|
});
|
|
1590
1208
|
}
|
|
1591
|
-
return c.json(
|
|
1592
|
-
{
|
|
1593
|
-
user: normalizeAuthUser(user),
|
|
1594
|
-
session: null,
|
|
1595
|
-
verificationId: verification.id,
|
|
1596
|
-
requiresVerification: true
|
|
1597
|
-
},
|
|
1598
|
-
200
|
|
1599
|
-
);
|
|
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"
|
|
1369
|
+
};
|
|
1370
|
+
|
|
1371
|
+
// src/routes/auth/helper/session.ts
|
|
1372
|
+
import { and as and7, asc, eq as eq7, gt as gt3, inArray, sql as sql5 } from "drizzle-orm";
|
|
1373
|
+
var createSessionRecord = async ({
|
|
1374
|
+
tx,
|
|
1375
|
+
tenantId,
|
|
1376
|
+
userId,
|
|
1377
|
+
config,
|
|
1378
|
+
userAgent,
|
|
1379
|
+
ip,
|
|
1380
|
+
action,
|
|
1381
|
+
rememberMe,
|
|
1382
|
+
expiresIn
|
|
1383
|
+
}) => {
|
|
1384
|
+
const sessionToken = generateToken();
|
|
1385
|
+
const hashedToken = await hashToken(sessionToken, config.secret);
|
|
1386
|
+
const sessionDuration = expiresIn || getSessionDuration({
|
|
1387
|
+
sessionConfig: config.session,
|
|
1388
|
+
rememberMe: rememberMe ?? true
|
|
1389
|
+
});
|
|
1390
|
+
const expiresAt = addDuration(sessionDuration);
|
|
1391
|
+
const meta = { action };
|
|
1392
|
+
if (rememberMe !== void 0) {
|
|
1393
|
+
meta.rememberMe = rememberMe;
|
|
1394
|
+
}
|
|
1395
|
+
const [session] = await tx.insert(sessionsInIam).values({
|
|
1396
|
+
tenantId,
|
|
1397
|
+
userId,
|
|
1398
|
+
token: hashedToken,
|
|
1399
|
+
expiresAt,
|
|
1400
|
+
userAgent,
|
|
1401
|
+
ip,
|
|
1402
|
+
meta
|
|
1403
|
+
}).returning({
|
|
1404
|
+
id: sessionsInIam.id,
|
|
1405
|
+
expiresAt: sessionsInIam.expiresAt,
|
|
1406
|
+
createdAt: sessionsInIam.createdAt,
|
|
1407
|
+
updatedAt: sessionsInIam.updatedAt,
|
|
1408
|
+
userAgent: sessionsInIam.userAgent,
|
|
1409
|
+
ip: sessionsInIam.ip
|
|
1410
|
+
});
|
|
1411
|
+
return { session, sessionToken, expiresAt };
|
|
1412
|
+
};
|
|
1413
|
+
var createSession = async ({
|
|
1414
|
+
tx,
|
|
1415
|
+
tenantId,
|
|
1416
|
+
userId,
|
|
1417
|
+
config,
|
|
1418
|
+
userAgent,
|
|
1419
|
+
ip,
|
|
1420
|
+
rememberMe = true
|
|
1421
|
+
}) => {
|
|
1422
|
+
const { session, sessionToken, expiresAt } = await createSessionRecord({
|
|
1423
|
+
tx,
|
|
1424
|
+
tenantId,
|
|
1425
|
+
userId,
|
|
1426
|
+
config,
|
|
1427
|
+
userAgent,
|
|
1428
|
+
ip,
|
|
1429
|
+
rememberMe,
|
|
1430
|
+
action: "sign-up"
|
|
1431
|
+
});
|
|
1432
|
+
return { sessionId: session.id, sessionToken, expiresAt };
|
|
1433
|
+
};
|
|
1434
|
+
var cleanupOldSessions = async ({
|
|
1435
|
+
database,
|
|
1436
|
+
userId,
|
|
1437
|
+
tenantId,
|
|
1438
|
+
maxSessions
|
|
1439
|
+
}) => {
|
|
1440
|
+
const [{ count }] = await database.select({ count: sql5`count(*)` }).from(sessionsInIam).where(
|
|
1441
|
+
and7(
|
|
1442
|
+
eq7(sessionsInIam.tenantId, tenantId),
|
|
1443
|
+
eq7(sessionsInIam.userId, userId),
|
|
1444
|
+
gt3(sessionsInIam.expiresAt, (/* @__PURE__ */ new Date()).toISOString())
|
|
1445
|
+
)
|
|
1446
|
+
);
|
|
1447
|
+
if (count <= maxSessions) {
|
|
1448
|
+
return;
|
|
1449
|
+
}
|
|
1450
|
+
const toDeleteCount = count - maxSessions;
|
|
1451
|
+
const idsToDelete = await database.select({ id: sessionsInIam.id }).from(sessionsInIam).where(
|
|
1452
|
+
and7(
|
|
1453
|
+
eq7(sessionsInIam.tenantId, tenantId),
|
|
1454
|
+
eq7(sessionsInIam.userId, userId),
|
|
1455
|
+
gt3(sessionsInIam.expiresAt, (/* @__PURE__ */ new Date()).toISOString())
|
|
1456
|
+
)
|
|
1457
|
+
).orderBy(asc(sessionsInIam.createdAt)).limit(toDeleteCount);
|
|
1458
|
+
if (!idsToDelete.length) {
|
|
1459
|
+
return;
|
|
1460
|
+
}
|
|
1461
|
+
await database.delete(sessionsInIam).where(
|
|
1462
|
+
and7(
|
|
1463
|
+
eq7(sessionsInIam.tenantId, tenantId),
|
|
1464
|
+
eq7(sessionsInIam.userId, userId),
|
|
1465
|
+
inArray(
|
|
1466
|
+
sessionsInIam.id,
|
|
1467
|
+
idsToDelete.map((s) => s.id)
|
|
1468
|
+
)
|
|
1469
|
+
)
|
|
1470
|
+
);
|
|
1471
|
+
};
|
|
1472
|
+
|
|
1473
|
+
// src/routes/auth/helper/user.ts
|
|
1474
|
+
import { and as and8, eq as eq8, gt as gt4, sql as sql6 } from "drizzle-orm";
|
|
1475
|
+
var checkExistingUserStatus = async ({
|
|
1476
|
+
tx,
|
|
1477
|
+
identifier,
|
|
1478
|
+
tenantId,
|
|
1479
|
+
isEmail
|
|
1480
|
+
}) => {
|
|
1481
|
+
const whereClause = isEmail ? and8(
|
|
1482
|
+
eq8(usersInIam.tenantId, tenantId),
|
|
1483
|
+
sql6`lower(${usersInIam.email}) = lower(${identifier})`
|
|
1484
|
+
) : and8(eq8(usersInIam.tenantId, tenantId), eq8(usersInIam.phone, identifier));
|
|
1485
|
+
const [existingUser] = await tx.select().from(usersInIam).where(whereClause).limit(1);
|
|
1486
|
+
if (!existingUser) {
|
|
1487
|
+
return { action: "proceed" };
|
|
1488
|
+
}
|
|
1489
|
+
const isVerified = isEmail ? existingUser.emailVerified : existingUser.phoneVerified;
|
|
1490
|
+
if (isVerified) {
|
|
1491
|
+
return { action: "error", code: AUTH_ERRORS.USER_EXISTS };
|
|
1492
|
+
}
|
|
1493
|
+
const [pendingVerification] = await tx.select().from(verificationsInIam).where(
|
|
1494
|
+
and8(
|
|
1495
|
+
eq8(verificationsInIam.userId, existingUser.id),
|
|
1496
|
+
eq8(verificationsInIam.tenantId, tenantId),
|
|
1497
|
+
gt4(verificationsInIam.expiresAt, (/* @__PURE__ */ new Date()).toISOString())
|
|
1498
|
+
)
|
|
1499
|
+
).limit(1);
|
|
1500
|
+
if (pendingVerification) {
|
|
1501
|
+
return {
|
|
1502
|
+
action: "pending",
|
|
1503
|
+
verificationId: pendingVerification.id,
|
|
1504
|
+
user: existingUser
|
|
1505
|
+
};
|
|
1506
|
+
}
|
|
1507
|
+
await deleteUnverifiedUser({ tx, userId: existingUser.id, tenantId });
|
|
1508
|
+
return { action: "proceed" };
|
|
1600
1509
|
};
|
|
1601
|
-
var
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
config,
|
|
1605
|
-
database,
|
|
1510
|
+
var deleteUnverifiedUser = async ({
|
|
1511
|
+
tx,
|
|
1512
|
+
userId,
|
|
1606
1513
|
tenantId
|
|
1607
1514
|
}) => {
|
|
1608
|
-
|
|
1609
|
-
return c.json({ error: "User phone not found" }, 401);
|
|
1610
|
-
}
|
|
1611
|
-
await database.delete(verificationsInIam).where(
|
|
1515
|
+
await tx.delete(verificationsInIam).where(
|
|
1612
1516
|
and8(
|
|
1613
|
-
eq8(verificationsInIam.
|
|
1614
|
-
eq8(verificationsInIam.
|
|
1615
|
-
eq8(verificationsInIam.type, "phone-otp")
|
|
1517
|
+
eq8(verificationsInIam.userId, userId),
|
|
1518
|
+
eq8(verificationsInIam.tenantId, tenantId)
|
|
1616
1519
|
)
|
|
1617
1520
|
);
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1521
|
+
await tx.delete(accountsInIam).where(
|
|
1522
|
+
and8(
|
|
1523
|
+
eq8(accountsInIam.userId, userId),
|
|
1524
|
+
eq8(accountsInIam.tenantId, tenantId)
|
|
1525
|
+
)
|
|
1526
|
+
);
|
|
1527
|
+
await tx.delete(usersInIam).where(and8(eq8(usersInIam.id, userId), eq8(usersInIam.tenantId, tenantId)));
|
|
1528
|
+
};
|
|
1529
|
+
var createUserWithAccount = async ({
|
|
1530
|
+
tx,
|
|
1531
|
+
tenantId,
|
|
1532
|
+
email,
|
|
1533
|
+
phone,
|
|
1534
|
+
password,
|
|
1535
|
+
fullName,
|
|
1536
|
+
handle,
|
|
1537
|
+
config
|
|
1538
|
+
}) => {
|
|
1539
|
+
const [user] = await tx.insert(usersInIam).values({
|
|
1540
|
+
tenantId,
|
|
1541
|
+
fullName,
|
|
1542
|
+
handle,
|
|
1543
|
+
email: email || null,
|
|
1544
|
+
phone: phone || null,
|
|
1545
|
+
emailVerified: email ? !config.email.required : false,
|
|
1546
|
+
phoneVerified: phone ? !config.phone.required : false,
|
|
1547
|
+
userType: [config.userType]
|
|
1548
|
+
}).returning();
|
|
1549
|
+
const passwordHash = await hashPassword(password);
|
|
1550
|
+
await tx.insert(accountsInIam).values({
|
|
1622
1551
|
tenantId,
|
|
1623
1552
|
userId: user.id,
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
to: user.phone,
|
|
1628
|
-
attempt: 0
|
|
1629
|
-
}).returning({
|
|
1630
|
-
id: verificationsInIam.id,
|
|
1631
|
-
tenantId: verificationsInIam.tenantId,
|
|
1632
|
-
userId: verificationsInIam.userId,
|
|
1633
|
-
type: verificationsInIam.type,
|
|
1634
|
-
code: verificationsInIam.code,
|
|
1635
|
-
to: verificationsInIam.to,
|
|
1636
|
-
expiresAt: verificationsInIam.expiresAt,
|
|
1637
|
-
createdAt: verificationsInIam.createdAt,
|
|
1638
|
-
attempt: verificationsInIam.attempt
|
|
1553
|
+
provider: "credentials",
|
|
1554
|
+
providerAccountId: email || phone || user.id,
|
|
1555
|
+
password: passwordHash
|
|
1639
1556
|
});
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1557
|
+
return user;
|
|
1558
|
+
};
|
|
1559
|
+
var fetchUserForLogin = async ({
|
|
1560
|
+
database,
|
|
1561
|
+
identifier,
|
|
1562
|
+
tenantId,
|
|
1563
|
+
isEmail,
|
|
1564
|
+
userType
|
|
1565
|
+
}) => {
|
|
1566
|
+
const userTypeFilter = sql6`${usersInIam.userType} @> ARRAY[${userType}]::text[]`;
|
|
1567
|
+
const whereClause = isEmail ? and8(
|
|
1568
|
+
eq8(usersInIam.tenantId, tenantId),
|
|
1569
|
+
userTypeFilter,
|
|
1570
|
+
sql6`lower(${usersInIam.email}) = lower(${identifier})`
|
|
1571
|
+
) : and8(
|
|
1572
|
+
eq8(usersInIam.tenantId, tenantId),
|
|
1573
|
+
userTypeFilter,
|
|
1574
|
+
eq8(usersInIam.phone, identifier)
|
|
1656
1575
|
);
|
|
1576
|
+
const [row] = await database.select({
|
|
1577
|
+
id: usersInIam.id,
|
|
1578
|
+
tenantId: usersInIam.tenantId,
|
|
1579
|
+
fullName: usersInIam.fullName,
|
|
1580
|
+
email: usersInIam.email,
|
|
1581
|
+
phone: usersInIam.phone,
|
|
1582
|
+
handle: usersInIam.handle,
|
|
1583
|
+
image: usersInIam.image,
|
|
1584
|
+
emailVerified: usersInIam.emailVerified,
|
|
1585
|
+
phoneVerified: usersInIam.phoneVerified,
|
|
1586
|
+
lastSignInAt: usersInIam.lastSignInAt,
|
|
1587
|
+
bannedUntil: usersInIam.bannedUntil,
|
|
1588
|
+
loginAttempt: usersInIam.loginAttempt,
|
|
1589
|
+
hasPassword: sql6`exists(
|
|
1590
|
+
select 1
|
|
1591
|
+
from ${accountsInIam}
|
|
1592
|
+
where ${eq8(accountsInIam.tenantId, tenantId)}
|
|
1593
|
+
and ${eq8(accountsInIam.userId, usersInIam.id)}
|
|
1594
|
+
and ${eq8(accountsInIam.provider, "credentials")}
|
|
1595
|
+
and ${sql6`${accountsInIam.password} is not null`}
|
|
1596
|
+
)`
|
|
1597
|
+
}).from(usersInIam).where(whereClause).limit(1);
|
|
1598
|
+
return row || null;
|
|
1599
|
+
};
|
|
1600
|
+
var fetchUserByIdWithRoles = async ({
|
|
1601
|
+
database,
|
|
1602
|
+
userId,
|
|
1603
|
+
tenantId
|
|
1604
|
+
}) => {
|
|
1605
|
+
const [result] = await database.select({
|
|
1606
|
+
id: usersInIam.id,
|
|
1607
|
+
tenantId: usersInIam.tenantId,
|
|
1608
|
+
fullName: usersInIam.fullName,
|
|
1609
|
+
email: usersInIam.email,
|
|
1610
|
+
phone: usersInIam.phone,
|
|
1611
|
+
handle: usersInIam.handle,
|
|
1612
|
+
image: usersInIam.image,
|
|
1613
|
+
emailVerified: usersInIam.emailVerified,
|
|
1614
|
+
phoneVerified: usersInIam.phoneVerified,
|
|
1615
|
+
lastSignInAt: usersInIam.lastSignInAt,
|
|
1616
|
+
bannedUntil: usersInIam.bannedUntil,
|
|
1617
|
+
loginAttempt: usersInIam.loginAttempt,
|
|
1618
|
+
...getUserAuthSelect(tenantId)
|
|
1619
|
+
}).from(usersInIam).where(and8(eq8(usersInIam.id, userId), eq8(usersInIam.tenantId, tenantId))).limit(1);
|
|
1620
|
+
return result || null;
|
|
1657
1621
|
};
|
|
1658
1622
|
|
|
1659
1623
|
// src/routes/auth/handler/sign-in.ts
|
|
@@ -1819,7 +1783,7 @@ var signInHandler = (
|
|
|
1819
1783
|
);
|
|
1820
1784
|
|
|
1821
1785
|
// src/routes/auth/handler/sign-out.ts
|
|
1822
|
-
import { and as and10, eq as eq10, gt as
|
|
1786
|
+
import { and as and10, eq as eq10, gt as gt5 } from "drizzle-orm";
|
|
1823
1787
|
import { getCookie } from "hono/cookie";
|
|
1824
1788
|
var signOutHandler = async (c) => {
|
|
1825
1789
|
const config = c.get("config");
|
|
@@ -1843,7 +1807,7 @@ var signOutHandler = async (c) => {
|
|
|
1843
1807
|
}).from(sessionsInIam).where(
|
|
1844
1808
|
and10(
|
|
1845
1809
|
eq10(sessionsInIam.token, hashedToken),
|
|
1846
|
-
|
|
1810
|
+
gt5(sessionsInIam.expiresAt, (/* @__PURE__ */ new Date()).toISOString())
|
|
1847
1811
|
)
|
|
1848
1812
|
).limit(1);
|
|
1849
1813
|
if (session) {
|
|
@@ -2171,6 +2135,24 @@ var auth_route_default = authRoutes;
|
|
|
2171
2135
|
// src/routes/domains/domains.route.ts
|
|
2172
2136
|
import { createRoute as createRoute2, OpenAPIHono as OpenAPIHono2 } from "@hono/zod-openapi";
|
|
2173
2137
|
|
|
2138
|
+
// src/lib/has-role-permission.ts
|
|
2139
|
+
import { grant } from "@mesob/common";
|
|
2140
|
+
import { HTTPException as HTTPException3 } from "hono/http-exception";
|
|
2141
|
+
var toArray = (v) => {
|
|
2142
|
+
return Array.isArray(v) ? v : [v];
|
|
2143
|
+
};
|
|
2144
|
+
var hasPermission = (c, permission) => {
|
|
2145
|
+
const user = c.get("user");
|
|
2146
|
+
const perms = user?.permissions;
|
|
2147
|
+
const check2 = toArray(permission);
|
|
2148
|
+
return grant(check2, perms);
|
|
2149
|
+
};
|
|
2150
|
+
var hasPermissionThrow = (c, permission) => {
|
|
2151
|
+
if (!hasPermission(c, permission)) {
|
|
2152
|
+
throw new HTTPException3(401, { message: "Unauthorized" });
|
|
2153
|
+
}
|
|
2154
|
+
};
|
|
2155
|
+
|
|
2174
2156
|
// src/routes/domains/domains.schema.ts
|
|
2175
2157
|
import { z as z2 } from "zod";
|
|
2176
2158
|
var listDomainsQuerySchema = z2.object({
|
|
@@ -2230,10 +2212,11 @@ var createDomainHandler = async (c) => {
|
|
|
2230
2212
|
const database = c.get("database");
|
|
2231
2213
|
const tenantId = c.get("tenantId");
|
|
2232
2214
|
const resolvedTenantId = ensureTenantId(config, tenantId);
|
|
2215
|
+
const status = (body.status || "pending").toUpperCase();
|
|
2233
2216
|
const [domain] = await database.insert(domainsInIam).values({
|
|
2234
2217
|
tenantId: resolvedTenantId,
|
|
2235
2218
|
domain: body.domain,
|
|
2236
|
-
status
|
|
2219
|
+
status,
|
|
2237
2220
|
meta: body.meta || null,
|
|
2238
2221
|
isPrimary: body.isPrimary
|
|
2239
2222
|
}).returning();
|
|
@@ -2278,7 +2261,7 @@ var listDomainsHandler = async (c) => {
|
|
|
2278
2261
|
const offset = (page - 1) * limit;
|
|
2279
2262
|
const conditions = [eq13(domainsInIam.tenantId, tenantId)];
|
|
2280
2263
|
if (query.status) {
|
|
2281
|
-
conditions.push(eq13(domainsInIam.status, query.status));
|
|
2264
|
+
conditions.push(eq13(domainsInIam.status, query.status.toUpperCase()));
|
|
2282
2265
|
}
|
|
2283
2266
|
const [domains, totalResult] = await Promise.all([
|
|
2284
2267
|
database.select().from(domainsInIam).where(and13(...conditions)).limit(limit).offset(offset),
|
|
@@ -2304,7 +2287,7 @@ var updateDomainHandler = async (c) => {
|
|
|
2304
2287
|
updateData.domain = body.domain;
|
|
2305
2288
|
}
|
|
2306
2289
|
if (body.status !== void 0) {
|
|
2307
|
-
updateData.status = body.status;
|
|
2290
|
+
updateData.status = body.status.toUpperCase();
|
|
2308
2291
|
}
|
|
2309
2292
|
if (body.meta !== void 0) {
|
|
2310
2293
|
updateData.meta = body.meta;
|
|
@@ -2502,7 +2485,12 @@ var verifyDomainRoute = createRoute2({
|
|
|
2502
2485
|
}
|
|
2503
2486
|
}
|
|
2504
2487
|
});
|
|
2505
|
-
var
|
|
2488
|
+
var IAM_ALL = "iam:all:all";
|
|
2489
|
+
var domainRoutesBase = new OpenAPIHono2().use("*", (c, next) => {
|
|
2490
|
+
hasPermissionThrow(c, IAM_ALL);
|
|
2491
|
+
return next();
|
|
2492
|
+
});
|
|
2493
|
+
var domainRoutes = domainRoutesBase.openapi(listDomainsRoute, listDomainsHandler).openapi(getDomainRoute, getDomainHandler).openapi(createDomainRoute, createDomainHandler).openapi(updateDomainRoute, updateDomainHandler).openapi(deleteDomainRoute, deleteDomainHandler).openapi(verifyDomainRoute, verifyDomainHandler);
|
|
2506
2494
|
var domains_route_default = domainRoutes;
|
|
2507
2495
|
|
|
2508
2496
|
// src/routes/email/email.route.ts
|
|
@@ -2806,7 +2794,7 @@ var email_route_default = emailRoutes;
|
|
|
2806
2794
|
import { createRoute as createRoute4, OpenAPIHono as OpenAPIHono4 } from "@hono/zod-openapi";
|
|
2807
2795
|
|
|
2808
2796
|
// src/routes/password/handler/change.ts
|
|
2809
|
-
import { and as and18, eq as eq18, gt as
|
|
2797
|
+
import { and as and18, eq as eq18, gt as gt6, ne } from "drizzle-orm";
|
|
2810
2798
|
import { getCookie as getCookie2 } from "hono/cookie";
|
|
2811
2799
|
var changePasswordHandler = async (c) => {
|
|
2812
2800
|
const body = c.req.valid("json");
|
|
@@ -2852,7 +2840,7 @@ var changePasswordHandler = async (c) => {
|
|
|
2852
2840
|
and18(
|
|
2853
2841
|
eq18(sessionsInIam.tenantId, resolvedTenantId),
|
|
2854
2842
|
eq18(sessionsInIam.userId, userId),
|
|
2855
|
-
|
|
2843
|
+
gt6(sessionsInIam.expiresAt, (/* @__PURE__ */ new Date()).toISOString()),
|
|
2856
2844
|
ne(sessionsInIam.token, hashedToken)
|
|
2857
2845
|
)
|
|
2858
2846
|
);
|
|
@@ -3049,7 +3037,7 @@ var forgotPasswordHandler = async (c) => {
|
|
|
3049
3037
|
};
|
|
3050
3038
|
|
|
3051
3039
|
// src/routes/password/handler/reset.ts
|
|
3052
|
-
import { and as and20, eq as eq20, gt as
|
|
3040
|
+
import { and as and20, eq as eq20, gt as gt7 } from "drizzle-orm";
|
|
3053
3041
|
|
|
3054
3042
|
// src/routes/password/helper/session.ts
|
|
3055
3043
|
var createPasswordResetSession = async ({
|
|
@@ -3129,7 +3117,7 @@ var resetPasswordHandler = async (c) => {
|
|
|
3129
3117
|
and20(
|
|
3130
3118
|
eq20(sessionsInIam.tenantId, resolvedTenantId),
|
|
3131
3119
|
eq20(sessionsInIam.userId, verification.userId),
|
|
3132
|
-
|
|
3120
|
+
gt7(sessionsInIam.expiresAt, (/* @__PURE__ */ new Date()).toISOString())
|
|
3133
3121
|
)
|
|
3134
3122
|
);
|
|
3135
3123
|
await database.delete(verificationsInIam).where(eq20(verificationsInIam.id, verificationId));
|
|
@@ -3549,7 +3537,7 @@ import {
|
|
|
3549
3537
|
notInArray,
|
|
3550
3538
|
sql as sql12
|
|
3551
3539
|
} from "drizzle-orm";
|
|
3552
|
-
import { HTTPException as
|
|
3540
|
+
import { HTTPException as HTTPException4 } from "hono/http-exception";
|
|
3553
3541
|
function buildPermissionDescription(code) {
|
|
3554
3542
|
return {
|
|
3555
3543
|
en: toTitleCase(code.replaceAll(":", " ").replaceAll("_", " "))
|
|
@@ -3606,7 +3594,7 @@ async function assertPermissionsExist({
|
|
|
3606
3594
|
(id) => !existingIds.has(id)
|
|
3607
3595
|
);
|
|
3608
3596
|
if (missingPermissionIds.length) {
|
|
3609
|
-
throw new
|
|
3597
|
+
throw new HTTPException4(400, {
|
|
3610
3598
|
message: `Unknown permissions: ${missingPermissionIds.join(", ")}`
|
|
3611
3599
|
});
|
|
3612
3600
|
}
|
|
@@ -3856,7 +3844,12 @@ var seedPermissionsRoute = createRoute5({
|
|
|
3856
3844
|
}
|
|
3857
3845
|
}
|
|
3858
3846
|
});
|
|
3859
|
-
var
|
|
3847
|
+
var IAM_ALL2 = "iam:all:all";
|
|
3848
|
+
var permissionRoutesBase = new OpenAPIHono5().use("*", (c, next) => {
|
|
3849
|
+
hasPermissionThrow(c, IAM_ALL2);
|
|
3850
|
+
return next();
|
|
3851
|
+
});
|
|
3852
|
+
var permissionRoutes = permissionRoutesBase.openapi(listPermissionsRoute, listPermissionsHandler).openapi(seedPermissionsRoute, seedPermissionsHandler).openapi(getPermissionRoute, getPermissionHandler);
|
|
3860
3853
|
var permissions_route_default = permissionRoutes;
|
|
3861
3854
|
|
|
3862
3855
|
// src/routes/phone/phone.route.ts
|
|
@@ -4190,7 +4183,7 @@ import { createRoute as createRoute7, OpenAPIHono as OpenAPIHono7 } from "@hono/
|
|
|
4190
4183
|
import { z as z4 } from "zod";
|
|
4191
4184
|
|
|
4192
4185
|
// src/routes/profile/handler/account-change-pending.ts
|
|
4193
|
-
import { and as and26, eq as eq27, gt as
|
|
4186
|
+
import { and as and26, eq as eq27, gt as gt8, sql as sql13 } from "drizzle-orm";
|
|
4194
4187
|
var accountChangePendingHandler = async (c) => {
|
|
4195
4188
|
const config = c.get("config");
|
|
4196
4189
|
const database = c.get("database");
|
|
@@ -4228,7 +4221,7 @@ var accountChangePendingHandler = async (c) => {
|
|
|
4228
4221
|
eq27(verificationsInIam.userId, userId),
|
|
4229
4222
|
eq27(verificationsInIam.type, "email-verification"),
|
|
4230
4223
|
eq27(verificationsInIam.to, accountChange.newEmail),
|
|
4231
|
-
|
|
4224
|
+
gt8(verificationsInIam.expiresAt, sql13`CURRENT_TIMESTAMP`)
|
|
4232
4225
|
)
|
|
4233
4226
|
).limit(1);
|
|
4234
4227
|
verification = v || null;
|
|
@@ -4243,7 +4236,7 @@ var accountChangePendingHandler = async (c) => {
|
|
|
4243
4236
|
eq27(verificationsInIam.userId, userId),
|
|
4244
4237
|
eq27(verificationsInIam.type, "phone-otp-change-phone"),
|
|
4245
4238
|
eq27(verificationsInIam.to, accountChange.newPhone),
|
|
4246
|
-
|
|
4239
|
+
gt8(verificationsInIam.expiresAt, sql13`CURRENT_TIMESTAMP`)
|
|
4247
4240
|
)
|
|
4248
4241
|
).limit(1);
|
|
4249
4242
|
verification = v || null;
|
|
@@ -4890,7 +4883,15 @@ var revokeRolePermissionRoute = createRoute8({
|
|
|
4890
4883
|
}
|
|
4891
4884
|
}
|
|
4892
4885
|
});
|
|
4893
|
-
var
|
|
4886
|
+
var IAM_ALL3 = "iam:all:all";
|
|
4887
|
+
var rolePermissionRoutesBase = new OpenAPIHono8().use(
|
|
4888
|
+
"*",
|
|
4889
|
+
(c, next) => {
|
|
4890
|
+
hasPermissionThrow(c, IAM_ALL3);
|
|
4891
|
+
return next();
|
|
4892
|
+
}
|
|
4893
|
+
);
|
|
4894
|
+
var rolePermissionRoutes = rolePermissionRoutesBase.openapi(listRolePermissionsRoute, listRolePermissionsHandler).openapi(assignRolePermissionRoute, assignRolePermissionHandler).openapi(revokeRolePermissionRoute, revokeRolePermissionHandler);
|
|
4894
4895
|
var role_permissions_route_default = rolePermissionRoutes;
|
|
4895
4896
|
|
|
4896
4897
|
// src/routes/roles/roles.route.ts
|
|
@@ -6050,7 +6051,12 @@ var seedRolesRoute = createRoute9({
|
|
|
6050
6051
|
}
|
|
6051
6052
|
}
|
|
6052
6053
|
});
|
|
6053
|
-
var
|
|
6054
|
+
var IAM_ALL4 = "iam:all:all";
|
|
6055
|
+
var roleRoutesBase = new OpenAPIHono9().use("*", (c, next) => {
|
|
6056
|
+
hasPermissionThrow(c, IAM_ALL4);
|
|
6057
|
+
return next();
|
|
6058
|
+
});
|
|
6059
|
+
var roleRoutes = roleRoutesBase.openapi(listRolesRoute, listRolesHandler).openapi(seedRolesRoute, seedRolesHandler).openapi(getRoleRoute, getRoleHandler).openapi(createRoleRoute, createRoleHandler).openapi(updateRoleRoute, updateRoleHandler).openapi(listRolePermissionsRoute2, listRolePermissionsHandler2).openapi(assignRolePermissionsRoute, assignRolePermissionsHandler).openapi(revokeRolePermissionRoute2, revokeRolePermissionHandler2).openapi(listRoleUsersRoute, listRoleUsersHandler).openapi(assignRoleUsersRoute, assignRoleUsersHandler).openapi(revokeRoleUserRoute, revokeRoleUserHandler).openapi(deleteRoleRoute, deleteRoleHandler);
|
|
6054
6060
|
var roles_route_default = roleRoutes;
|
|
6055
6061
|
|
|
6056
6062
|
// src/routes/sessions/sessions.route.ts
|
|
@@ -6253,7 +6259,12 @@ var revokeAllSessionsRoute = createRoute10({
|
|
|
6253
6259
|
}
|
|
6254
6260
|
}
|
|
6255
6261
|
});
|
|
6256
|
-
var
|
|
6262
|
+
var IAM_ALL5 = "iam:all:all";
|
|
6263
|
+
var sessionRoutesBase = new OpenAPIHono10().use("*", (c, next) => {
|
|
6264
|
+
hasPermissionThrow(c, IAM_ALL5);
|
|
6265
|
+
return next();
|
|
6266
|
+
});
|
|
6267
|
+
var sessionRoutes = sessionRoutesBase.openapi(listSessionsRoute, listSessionsHandler).openapi(getSessionRoute, getSessionHandler).openapi(revokeSessionRoute, revokeSessionHandler).openapi(revokeAllSessionsRoute, revokeAllSessionsHandler);
|
|
6257
6268
|
var sessions_route_default = sessionRoutes;
|
|
6258
6269
|
|
|
6259
6270
|
// src/routes/system/system.route.ts
|
|
@@ -6277,6 +6288,7 @@ var tenantHandler = (c) => {
|
|
|
6277
6288
|
};
|
|
6278
6289
|
|
|
6279
6290
|
// src/routes/system/system.route.ts
|
|
6291
|
+
var IAM_ALL6 = "iam:all:all";
|
|
6280
6292
|
var tenantRoute = createRoute11({
|
|
6281
6293
|
method: "get",
|
|
6282
6294
|
path: "/init",
|
|
@@ -6311,10 +6323,11 @@ var tenantRoute = createRoute11({
|
|
|
6311
6323
|
}
|
|
6312
6324
|
}
|
|
6313
6325
|
});
|
|
6314
|
-
var
|
|
6315
|
-
|
|
6316
|
-
|
|
6317
|
-
);
|
|
6326
|
+
var tenantRoutesBase = new OpenAPIHono11().use("*", (c, next) => {
|
|
6327
|
+
hasPermissionThrow(c, IAM_ALL6);
|
|
6328
|
+
return next();
|
|
6329
|
+
});
|
|
6330
|
+
var tenantRoutes = tenantRoutesBase.openapi(tenantRoute, tenantHandler);
|
|
6318
6331
|
var system_route_default = tenantRoutes;
|
|
6319
6332
|
|
|
6320
6333
|
// src/routes/tenants/tenants.route.ts
|
|
@@ -6322,42 +6335,7 @@ import { createRoute as createRoute12, OpenAPIHono as OpenAPIHono12 } from "@hon
|
|
|
6322
6335
|
|
|
6323
6336
|
// src/routes/tenants/handler/create-tenant.ts
|
|
6324
6337
|
import { eq as eq48 } from "drizzle-orm";
|
|
6325
|
-
|
|
6326
|
-
// src/lib/has-role-permission.ts
|
|
6327
|
-
import { grant } from "@mesob/common";
|
|
6328
|
-
import { HTTPException as HTTPException4 } from "hono/http-exception";
|
|
6329
|
-
var toArray = (v) => {
|
|
6330
|
-
return Array.isArray(v) ? v : [v];
|
|
6331
|
-
};
|
|
6332
|
-
var hasRole = (c, role) => {
|
|
6333
|
-
const user = c.get("user");
|
|
6334
|
-
const codes = user?.roleCodes;
|
|
6335
|
-
if (!codes?.length) {
|
|
6336
|
-
return false;
|
|
6337
|
-
}
|
|
6338
|
-
const check2 = toArray(role);
|
|
6339
|
-
return check2.some((r) => codes.includes(r));
|
|
6340
|
-
};
|
|
6341
|
-
var hasRoleThrow = (c, role) => {
|
|
6342
|
-
if (!hasRole(c, role)) {
|
|
6343
|
-
throw new HTTPException4(401, { message: "Unauthorized" });
|
|
6344
|
-
}
|
|
6345
|
-
};
|
|
6346
|
-
var hasPermission = (c, permission) => {
|
|
6347
|
-
const user = c.get("user");
|
|
6348
|
-
const perms = user?.permissions;
|
|
6349
|
-
const check2 = toArray(permission);
|
|
6350
|
-
return grant(check2, perms);
|
|
6351
|
-
};
|
|
6352
|
-
var hasPermissionThrow = (c, permission) => {
|
|
6353
|
-
if (!hasPermission(c, permission)) {
|
|
6354
|
-
throw new HTTPException4(401, { message: "Unauthorized" });
|
|
6355
|
-
}
|
|
6356
|
-
};
|
|
6357
|
-
|
|
6358
|
-
// src/routes/tenants/handler/create-tenant.ts
|
|
6359
6338
|
var createTenantHandler = async (c) => {
|
|
6360
|
-
hasRoleThrow(c, ["owner", "tenant-admin"]);
|
|
6361
6339
|
const body = c.req.valid("json");
|
|
6362
6340
|
const database = c.get("database");
|
|
6363
6341
|
const [existing] = await database.select().from(tenantsInIam).where(eq48(tenantsInIam.id, body.id)).limit(1);
|
|
@@ -6385,7 +6363,6 @@ var createTenantHandler = async (c) => {
|
|
|
6385
6363
|
// src/routes/tenants/handler/delete-tenant.ts
|
|
6386
6364
|
import { eq as eq49 } from "drizzle-orm";
|
|
6387
6365
|
var deleteTenantHandler = async (c) => {
|
|
6388
|
-
hasRoleThrow(c, ["owner", "tenant-admin"]);
|
|
6389
6366
|
const { id } = c.req.valid("param");
|
|
6390
6367
|
const database = c.get("database");
|
|
6391
6368
|
const [existing] = await database.select().from(tenantsInIam).where(eq49(tenantsInIam.id, id)).limit(1);
|
|
@@ -6399,7 +6376,6 @@ var deleteTenantHandler = async (c) => {
|
|
|
6399
6376
|
// src/routes/tenants/handler/get-tenant.ts
|
|
6400
6377
|
import { eq as eq50 } from "drizzle-orm";
|
|
6401
6378
|
var getTenantHandler = async (c) => {
|
|
6402
|
-
hasRoleThrow(c, ["owner", "tenant-admin"]);
|
|
6403
6379
|
const { id } = c.req.valid("param");
|
|
6404
6380
|
const database = c.get("database");
|
|
6405
6381
|
const [tenant] = await database.select().from(tenantsInIam).where(eq50(tenantsInIam.id, id)).limit(1);
|
|
@@ -6417,7 +6393,6 @@ var sortColumnMap3 = {
|
|
|
6417
6393
|
name: sql23`${tenantsInIam.name}::text`
|
|
6418
6394
|
};
|
|
6419
6395
|
var listTenantsHandler = async (c) => {
|
|
6420
|
-
hasRoleThrow(c, ["owner", "tenant-admin"]);
|
|
6421
6396
|
const query = c.req.valid("query");
|
|
6422
6397
|
const database = c.get("database");
|
|
6423
6398
|
const page = query.page || 1;
|
|
@@ -6456,7 +6431,6 @@ var listTenantsHandler = async (c) => {
|
|
|
6456
6431
|
// src/routes/tenants/handler/update-tenant.ts
|
|
6457
6432
|
import { eq as eq52, sql as sql24 } from "drizzle-orm";
|
|
6458
6433
|
var updateTenantHandler = async (c) => {
|
|
6459
|
-
hasRoleThrow(c, ["owner", "tenant-admin"]);
|
|
6460
6434
|
const { id } = c.req.valid("param");
|
|
6461
6435
|
const body = c.req.valid("json");
|
|
6462
6436
|
const database = c.get("database");
|
|
@@ -6730,7 +6704,12 @@ var deleteTenantRoute = createRoute12({
|
|
|
6730
6704
|
}
|
|
6731
6705
|
}
|
|
6732
6706
|
});
|
|
6733
|
-
var
|
|
6707
|
+
var IAM_ALL7 = "iam:all:all";
|
|
6708
|
+
var tenantRoutesBase2 = new OpenAPIHono12().use("*", (c, next) => {
|
|
6709
|
+
hasPermissionThrow(c, IAM_ALL7);
|
|
6710
|
+
return next();
|
|
6711
|
+
});
|
|
6712
|
+
var tenantRoutes2 = tenantRoutesBase2.openapi(listTenantsRoute, listTenantsHandler).openapi(getTenantRoute, getTenantHandler).openapi(createTenantRoute, createTenantHandler).openapi(updateTenantRoute, updateTenantHandler).openapi(deleteTenantRoute, deleteTenantHandler);
|
|
6734
6713
|
var tenants_route_default = tenantRoutes2;
|
|
6735
6714
|
|
|
6736
6715
|
// src/routes/user-roles/user-roles.route.ts
|
|
@@ -6905,7 +6884,12 @@ var revokeUserRoleRoute = createRoute13({
|
|
|
6905
6884
|
}
|
|
6906
6885
|
}
|
|
6907
6886
|
});
|
|
6908
|
-
var
|
|
6887
|
+
var IAM_ALL8 = "iam:all:all";
|
|
6888
|
+
var userRoleRoutesBase = new OpenAPIHono13().use("*", (c, next) => {
|
|
6889
|
+
hasPermissionThrow(c, IAM_ALL8);
|
|
6890
|
+
return next();
|
|
6891
|
+
});
|
|
6892
|
+
var userRoleRoutes = userRoleRoutesBase.openapi(listUserRolesRoute, listUserRolesHandler).openapi(assignUserRoleRoute, assignUserRoleHandler).openapi(revokeUserRoleRoute, revokeUserRoleHandler);
|
|
6909
6893
|
var user_roles_route_default = userRoleRoutes;
|
|
6910
6894
|
|
|
6911
6895
|
// src/routes/users/users.route.ts
|
|
@@ -6922,27 +6906,24 @@ var banUserHandler = async (c) => {
|
|
|
6922
6906
|
if (!existing) {
|
|
6923
6907
|
return c.json({ error: "User not found" }, 404);
|
|
6924
6908
|
}
|
|
6925
|
-
|
|
6926
|
-
bannedUntil: body.bannedUntil
|
|
6909
|
+
await database.update(usersInIam).set({
|
|
6910
|
+
bannedUntil: body.bannedUntil ?? null,
|
|
6927
6911
|
updatedAt: sql25`CURRENT_TIMESTAMP`
|
|
6928
|
-
}).where(and50(eq55(usersInIam.id, id), eq55(usersInIam.tenantId, tenantId)))
|
|
6929
|
-
|
|
6930
|
-
|
|
6931
|
-
|
|
6932
|
-
|
|
6933
|
-
phone: usersInIam.phone,
|
|
6934
|
-
handle: usersInIam.handle,
|
|
6935
|
-
image: usersInIam.image,
|
|
6936
|
-
emailVerified: usersInIam.emailVerified,
|
|
6937
|
-
phoneVerified: usersInIam.phoneVerified,
|
|
6938
|
-
lastSignInAt: usersInIam.lastSignInAt
|
|
6912
|
+
}).where(and50(eq55(usersInIam.id, id), eq55(usersInIam.tenantId, tenantId)));
|
|
6913
|
+
const userWithRoles = await fetchUserWithRoles({
|
|
6914
|
+
database,
|
|
6915
|
+
userId: id,
|
|
6916
|
+
tenantId
|
|
6939
6917
|
});
|
|
6940
|
-
if (!
|
|
6918
|
+
if (!userWithRoles) {
|
|
6941
6919
|
return c.json({ error: "User not found" }, 404);
|
|
6942
6920
|
}
|
|
6943
|
-
return c.json({ user: normalizeUser(
|
|
6921
|
+
return c.json({ user: normalizeUser(userWithRoles) }, 200);
|
|
6944
6922
|
};
|
|
6945
6923
|
|
|
6924
|
+
// src/routes/users/helper/invite.ts
|
|
6925
|
+
import { and as and52, eq as eq57 } from "drizzle-orm";
|
|
6926
|
+
|
|
6946
6927
|
// src/routes/users/helper/user.ts
|
|
6947
6928
|
import { and as and51, eq as eq56, sql as sql26 } from "drizzle-orm";
|
|
6948
6929
|
var checkUserExists = async ({
|
|
@@ -7012,7 +6993,89 @@ var inviteUser = (
|
|
|
7012
6993
|
isEmail
|
|
7013
6994
|
});
|
|
7014
6995
|
if (existing) {
|
|
7015
|
-
|
|
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
|
+
};
|
|
7016
7079
|
}
|
|
7017
7080
|
const userHandle = payload.handle || generateHandle();
|
|
7018
7081
|
const existingHandle = await checkHandleExists({
|
|
@@ -7197,16 +7260,16 @@ var createUserHandler = async (c) => {
|
|
|
7197
7260
|
};
|
|
7198
7261
|
|
|
7199
7262
|
// src/routes/users/handler/delete-user.ts
|
|
7200
|
-
import { and as
|
|
7263
|
+
import { and as and53, eq as eq58 } from "drizzle-orm";
|
|
7201
7264
|
var deleteUserHandler = async (c) => {
|
|
7202
7265
|
const { id } = c.req.valid("param");
|
|
7203
7266
|
const database = c.get("database");
|
|
7204
7267
|
const tenantId = c.get("tenantId");
|
|
7205
|
-
const [existing] = await database.select().from(usersInIam).where(
|
|
7268
|
+
const [existing] = await database.select().from(usersInIam).where(and53(eq58(usersInIam.id, id), eq58(usersInIam.tenantId, tenantId))).limit(1);
|
|
7206
7269
|
if (!existing) {
|
|
7207
7270
|
return c.json({ error: "User not found" }, 404);
|
|
7208
7271
|
}
|
|
7209
|
-
await database.delete(usersInIam).where(
|
|
7272
|
+
await database.delete(usersInIam).where(and53(eq58(usersInIam.id, id), eq58(usersInIam.tenantId, tenantId)));
|
|
7210
7273
|
return c.json({ message: "User deleted" }, 200);
|
|
7211
7274
|
};
|
|
7212
7275
|
|
|
@@ -7250,7 +7313,7 @@ var inviteUserHandler = async (c) => {
|
|
|
7250
7313
|
};
|
|
7251
7314
|
|
|
7252
7315
|
// src/routes/users/handler/list-users.ts
|
|
7253
|
-
import { and as
|
|
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";
|
|
7254
7317
|
var userSelect = {
|
|
7255
7318
|
id: usersInIam.id,
|
|
7256
7319
|
tenantId: usersInIam.tenantId,
|
|
@@ -7266,9 +7329,6 @@ var userSelect = {
|
|
|
7266
7329
|
userType: usersInIam.userType,
|
|
7267
7330
|
roleCount: sql27`(select count(*)::int from ${userRolesInIam} where ${userRolesInIam.userId} = ${usersInIam.id} and ${userRolesInIam.tenantId} = ${usersInIam.tenantId})`.as(
|
|
7268
7331
|
"roleCount"
|
|
7269
|
-
),
|
|
7270
|
-
activeSessionCount: sql27`(select count(*)::int from ${sessionsInIam} where ${sessionsInIam.userId} = ${usersInIam.id} and ${sessionsInIam.tenantId} = ${usersInIam.tenantId} and ${sessionsInIam.expiresAt} > now())`.as(
|
|
7271
|
-
"activeSessionCount"
|
|
7272
7332
|
)
|
|
7273
7333
|
};
|
|
7274
7334
|
var sortColumnMap4 = {
|
|
@@ -7301,26 +7361,27 @@ var listUsersHandler = async (c) => {
|
|
|
7301
7361
|
const query = c.req.valid("query");
|
|
7302
7362
|
const database = c.get("database");
|
|
7303
7363
|
const tenantId = c.get("tenantId");
|
|
7304
|
-
const
|
|
7364
|
+
const _config = c.get("config");
|
|
7305
7365
|
const page = query.page || 1;
|
|
7306
7366
|
const limit = query.limit || 20;
|
|
7307
7367
|
const offset = (page - 1) * limit;
|
|
7308
|
-
const userTypeFilter =
|
|
7309
|
-
const conditions = [
|
|
7368
|
+
const userTypeFilter = query.userType && query.userType !== "all" ? query.userType : null;
|
|
7369
|
+
const conditions = [eq59(usersInIam.tenantId, tenantId)];
|
|
7310
7370
|
if (userTypeFilter) {
|
|
7311
7371
|
conditions.push(
|
|
7312
7372
|
sql27`${usersInIam.userType} @> ARRAY[${userTypeFilter}]::text[]`
|
|
7313
7373
|
);
|
|
7314
7374
|
}
|
|
7315
7375
|
if (query.search?.trim()) {
|
|
7316
|
-
const term = `%${query.search.trim().replace(/[%_\\]/g, (
|
|
7317
|
-
|
|
7318
|
-
|
|
7319
|
-
|
|
7320
|
-
|
|
7321
|
-
ilike4(usersInIam.phone, term)
|
|
7322
|
-
)
|
|
7376
|
+
const term = `%${query.search.trim().replace(/[%_\\]/g, (ch) => `\\${ch}`)}%`;
|
|
7377
|
+
const searchCond = or4(
|
|
7378
|
+
ilike4(usersInIam.fullName, term),
|
|
7379
|
+
ilike4(usersInIam.email, term),
|
|
7380
|
+
ilike4(usersInIam.phone, term)
|
|
7323
7381
|
);
|
|
7382
|
+
if (searchCond) {
|
|
7383
|
+
conditions.push(searchCond);
|
|
7384
|
+
}
|
|
7324
7385
|
}
|
|
7325
7386
|
if (query.email) {
|
|
7326
7387
|
conditions.push(ilike4(usersInIam.email, `%${query.email}%`));
|
|
@@ -7332,38 +7393,52 @@ var listUsersHandler = async (c) => {
|
|
|
7332
7393
|
conditions.push(ilike4(usersInIam.handle, `%${query.handle}%`));
|
|
7333
7394
|
}
|
|
7334
7395
|
if (query.filter === "verified") {
|
|
7335
|
-
|
|
7336
|
-
|
|
7337
|
-
|
|
7338
|
-
eq58(usersInIam.phoneVerified, true)
|
|
7339
|
-
)
|
|
7396
|
+
const verifiedCond = or4(
|
|
7397
|
+
eq59(usersInIam.emailVerified, true),
|
|
7398
|
+
eq59(usersInIam.phoneVerified, true)
|
|
7340
7399
|
);
|
|
7400
|
+
if (verifiedCond) {
|
|
7401
|
+
conditions.push(verifiedCond);
|
|
7402
|
+
}
|
|
7341
7403
|
} else if (query.filter === "unverified") {
|
|
7342
|
-
conditions.push(
|
|
7343
|
-
conditions.push(
|
|
7404
|
+
conditions.push(eq59(usersInIam.emailVerified, false));
|
|
7405
|
+
conditions.push(eq59(usersInIam.phoneVerified, false));
|
|
7344
7406
|
}
|
|
7345
7407
|
const orderDir = query.order === "asc" ? asc5 : desc5;
|
|
7346
7408
|
const sortCol = query.sort && sortColumnMap4[query.sort] ? sortColumnMap4[query.sort] : usersInIam.fullName;
|
|
7347
|
-
const whereClause =
|
|
7409
|
+
const whereClause = and54(...conditions);
|
|
7348
7410
|
const [users, totalResult] = await Promise.all([
|
|
7349
7411
|
database.select(userSelect).from(usersInIam).where(whereClause).orderBy(orderDir(sortCol)).limit(limit).offset(offset),
|
|
7350
7412
|
database.select({ count: sql27`count(*)` }).from(usersInIam).where(whereClause)
|
|
7351
7413
|
]);
|
|
7352
7414
|
const total = Number(totalResult[0]?.count || 0);
|
|
7353
7415
|
const userIds = users.map((u) => u.id);
|
|
7416
|
+
const sessionCountRows = userIds.length > 0 ? await database.select({
|
|
7417
|
+
userId: sessionsInIam.userId,
|
|
7418
|
+
count: sql27`count(*)::int`.as("count")
|
|
7419
|
+
}).from(sessionsInIam).where(
|
|
7420
|
+
and54(
|
|
7421
|
+
eq59(sessionsInIam.tenantId, tenantId),
|
|
7422
|
+
inArray6(sessionsInIam.userId, userIds),
|
|
7423
|
+
gt9(sessionsInIam.expiresAt, (/* @__PURE__ */ new Date()).toISOString())
|
|
7424
|
+
)
|
|
7425
|
+
).groupBy(sessionsInIam.userId) : [];
|
|
7426
|
+
const sessionCountByUser = new Map(
|
|
7427
|
+
sessionCountRows.map((r) => [r.userId, Number(r.count) ?? 0])
|
|
7428
|
+
);
|
|
7354
7429
|
const roleRows = userIds.length > 0 ? await database.select({
|
|
7355
7430
|
userId: userRolesInIam.userId,
|
|
7356
7431
|
code: rolesInIam.code,
|
|
7357
7432
|
name: rolesInIam.name
|
|
7358
7433
|
}).from(userRolesInIam).innerJoin(
|
|
7359
7434
|
rolesInIam,
|
|
7360
|
-
|
|
7361
|
-
|
|
7362
|
-
|
|
7435
|
+
and54(
|
|
7436
|
+
eq59(rolesInIam.tenantId, userRolesInIam.tenantId),
|
|
7437
|
+
eq59(rolesInIam.id, userRolesInIam.roleId)
|
|
7363
7438
|
)
|
|
7364
7439
|
).where(
|
|
7365
|
-
|
|
7366
|
-
|
|
7440
|
+
and54(
|
|
7441
|
+
eq59(userRolesInIam.tenantId, tenantId),
|
|
7367
7442
|
inArray6(userRolesInIam.userId, userIds)
|
|
7368
7443
|
)
|
|
7369
7444
|
) : [];
|
|
@@ -7382,7 +7457,7 @@ var listUsersHandler = async (c) => {
|
|
|
7382
7457
|
...u,
|
|
7383
7458
|
roles: null,
|
|
7384
7459
|
userRoles: userRolesMap.get(u.id) ?? [],
|
|
7385
|
-
activeSessionCount:
|
|
7460
|
+
activeSessionCount: sessionCountByUser.get(u.id) ?? 0
|
|
7386
7461
|
})),
|
|
7387
7462
|
total,
|
|
7388
7463
|
page,
|
|
@@ -7393,13 +7468,13 @@ var listUsersHandler = async (c) => {
|
|
|
7393
7468
|
};
|
|
7394
7469
|
|
|
7395
7470
|
// src/routes/users/handler/search-users.ts
|
|
7396
|
-
import { and as
|
|
7471
|
+
import { and as and55, eq as eq60, ilike as ilike5, or as or5 } from "drizzle-orm";
|
|
7397
7472
|
var searchUsersHandler = async (c) => {
|
|
7398
7473
|
const query = c.req.valid("query");
|
|
7399
7474
|
const database = c.get("database");
|
|
7400
7475
|
const tenantId = c.get("tenantId");
|
|
7401
7476
|
const limit = query.limit || 20;
|
|
7402
|
-
const conditions = [
|
|
7477
|
+
const conditions = [eq60(usersInIam.tenantId, tenantId)];
|
|
7403
7478
|
if (query.search && query.search.trim().length > 0) {
|
|
7404
7479
|
const searchCondition = or5(
|
|
7405
7480
|
ilike5(usersInIam.fullName, `%${query.search}%`),
|
|
@@ -7417,25 +7492,25 @@ var searchUsersHandler = async (c) => {
|
|
|
7417
7492
|
phone: usersInIam.phone,
|
|
7418
7493
|
handle: usersInIam.handle,
|
|
7419
7494
|
image: usersInIam.image
|
|
7420
|
-
}).from(usersInIam).where(
|
|
7495
|
+
}).from(usersInIam).where(and55(...conditions)).limit(limit);
|
|
7421
7496
|
return c.json({ users }, 200);
|
|
7422
7497
|
};
|
|
7423
7498
|
|
|
7424
7499
|
// src/routes/users/handler/update-user.ts
|
|
7425
|
-
import { and as
|
|
7500
|
+
import { and as and56, eq as eq61, inArray as inArray7, sql as sql28 } from "drizzle-orm";
|
|
7426
7501
|
var updateUserHandler = async (c) => {
|
|
7427
7502
|
const { id } = c.req.valid("param");
|
|
7428
7503
|
const body = c.req.valid("json");
|
|
7429
7504
|
const database = c.get("database");
|
|
7430
7505
|
const tenantId = c.get("tenantId");
|
|
7431
|
-
const [existing] = await database.select().from(usersInIam).where(
|
|
7506
|
+
const [existing] = await database.select().from(usersInIam).where(and56(eq61(usersInIam.id, id), eq61(usersInIam.tenantId, tenantId))).limit(1);
|
|
7432
7507
|
if (!existing) {
|
|
7433
7508
|
return c.json({ error: "User not found" }, 404);
|
|
7434
7509
|
}
|
|
7435
7510
|
if (body.handle && body.handle !== existing.handle) {
|
|
7436
7511
|
const [handleExists] = await database.select().from(usersInIam).where(
|
|
7437
|
-
|
|
7438
|
-
|
|
7512
|
+
and56(
|
|
7513
|
+
eq61(usersInIam.tenantId, tenantId),
|
|
7439
7514
|
sql28`lower(${usersInIam.handle}) = lower(${body.handle})`
|
|
7440
7515
|
)
|
|
7441
7516
|
).limit(1);
|
|
@@ -7468,7 +7543,7 @@ var updateUserHandler = async (c) => {
|
|
|
7468
7543
|
const [updated] = await database.update(usersInIam).set({
|
|
7469
7544
|
...updateData,
|
|
7470
7545
|
updatedAt: sql28`CURRENT_TIMESTAMP`
|
|
7471
|
-
}).where(
|
|
7546
|
+
}).where(and56(eq61(usersInIam.id, id), eq61(usersInIam.tenantId, tenantId))).returning({
|
|
7472
7547
|
id: usersInIam.id,
|
|
7473
7548
|
tenantId: usersInIam.tenantId,
|
|
7474
7549
|
fullName: usersInIam.fullName,
|
|
@@ -7487,8 +7562,8 @@ var updateUserHandler = async (c) => {
|
|
|
7487
7562
|
const roleIds = body.roleIds;
|
|
7488
7563
|
if (roleIds.length > 0) {
|
|
7489
7564
|
const validRoles = await database.select({ id: rolesInIam.id, code: rolesInIam.code }).from(rolesInIam).where(
|
|
7490
|
-
|
|
7491
|
-
|
|
7565
|
+
and56(
|
|
7566
|
+
eq61(rolesInIam.tenantId, tenantId),
|
|
7492
7567
|
inArray7(rolesInIam.id, roleIds)
|
|
7493
7568
|
)
|
|
7494
7569
|
);
|
|
@@ -7497,9 +7572,9 @@ var updateUserHandler = async (c) => {
|
|
|
7497
7572
|
);
|
|
7498
7573
|
const toInsert = roleIds.filter((rid) => assignableIds.has(rid));
|
|
7499
7574
|
await database.delete(userRolesInIam).where(
|
|
7500
|
-
|
|
7501
|
-
|
|
7502
|
-
|
|
7575
|
+
and56(
|
|
7576
|
+
eq61(userRolesInIam.tenantId, tenantId),
|
|
7577
|
+
eq61(userRolesInIam.userId, id)
|
|
7503
7578
|
)
|
|
7504
7579
|
);
|
|
7505
7580
|
if (toInsert.length > 0) {
|
|
@@ -7513,9 +7588,9 @@ var updateUserHandler = async (c) => {
|
|
|
7513
7588
|
}
|
|
7514
7589
|
} else {
|
|
7515
7590
|
await database.delete(userRolesInIam).where(
|
|
7516
|
-
|
|
7517
|
-
|
|
7518
|
-
|
|
7591
|
+
and56(
|
|
7592
|
+
eq61(userRolesInIam.tenantId, tenantId),
|
|
7593
|
+
eq61(userRolesInIam.userId, id)
|
|
7519
7594
|
)
|
|
7520
7595
|
);
|
|
7521
7596
|
}
|
|
@@ -7917,38 +7992,43 @@ var bulkInviteUsersRoute = createRoute14({
|
|
|
7917
7992
|
}
|
|
7918
7993
|
}
|
|
7919
7994
|
});
|
|
7920
|
-
var
|
|
7995
|
+
var IAM_ALL9 = "iam:all:all";
|
|
7996
|
+
var userRoutesBase = new OpenAPIHono14().use("*", (c, next) => {
|
|
7997
|
+
hasPermissionThrow(c, IAM_ALL9);
|
|
7998
|
+
return next();
|
|
7999
|
+
});
|
|
8000
|
+
var userRoutes = userRoutesBase.openapi(listUsersRoute, listUsersHandler).openapi(getUserRoute, getUserHandler).openapi(createUserRoute, createUserHandler).openapi(updateUserRoute, updateUserHandler).openapi(deleteUserRoute, deleteUserHandler).openapi(banUserRoute, banUserHandler).openapi(searchUsersRoute, searchUsersHandler).openapi(inviteUserRoute, inviteUserHandler).openapi(bulkInviteUsersRoute, bulkInviteUsersHandler);
|
|
7921
8001
|
var users_route_default = userRoutes;
|
|
7922
8002
|
|
|
7923
8003
|
// src/routes/verifications/verifications.route.ts
|
|
7924
8004
|
import { createRoute as createRoute15, OpenAPIHono as OpenAPIHono15 } from "@hono/zod-openapi";
|
|
7925
8005
|
|
|
7926
8006
|
// src/routes/verifications/handler/invalidate-verification.ts
|
|
7927
|
-
import { and as
|
|
8007
|
+
import { and as and57, eq as eq62 } from "drizzle-orm";
|
|
7928
8008
|
var invalidateVerificationHandler = async (c) => {
|
|
7929
8009
|
const { id } = c.req.valid("param");
|
|
7930
8010
|
const database = c.get("database");
|
|
7931
8011
|
const tenantId = c.get("tenantId");
|
|
7932
8012
|
const [existing] = await database.select().from(verificationsInIam).where(
|
|
7933
|
-
|
|
7934
|
-
|
|
7935
|
-
|
|
8013
|
+
and57(
|
|
8014
|
+
eq62(verificationsInIam.id, id),
|
|
8015
|
+
eq62(verificationsInIam.tenantId, tenantId)
|
|
7936
8016
|
)
|
|
7937
8017
|
).limit(1);
|
|
7938
8018
|
if (!existing) {
|
|
7939
8019
|
return c.json({ error: "Verification not found" }, 404);
|
|
7940
8020
|
}
|
|
7941
8021
|
await database.delete(verificationsInIam).where(
|
|
7942
|
-
|
|
7943
|
-
|
|
7944
|
-
|
|
8022
|
+
and57(
|
|
8023
|
+
eq62(verificationsInIam.id, id),
|
|
8024
|
+
eq62(verificationsInIam.tenantId, tenantId)
|
|
7945
8025
|
)
|
|
7946
8026
|
);
|
|
7947
8027
|
return c.json({ message: "Verification invalidated" }, 200);
|
|
7948
8028
|
};
|
|
7949
8029
|
|
|
7950
8030
|
// src/routes/verifications/handler/list-verifications.ts
|
|
7951
|
-
import { and as
|
|
8031
|
+
import { and as and58, eq as eq63, sql as sql29 } from "drizzle-orm";
|
|
7952
8032
|
var listVerificationsHandler = async (c) => {
|
|
7953
8033
|
const query = c.req.valid("query");
|
|
7954
8034
|
const database = c.get("database");
|
|
@@ -7956,12 +8036,12 @@ var listVerificationsHandler = async (c) => {
|
|
|
7956
8036
|
const page = query.page || 1;
|
|
7957
8037
|
const limit = query.limit || 20;
|
|
7958
8038
|
const offset = (page - 1) * limit;
|
|
7959
|
-
const conditions = [
|
|
8039
|
+
const conditions = [eq63(verificationsInIam.tenantId, tenantId)];
|
|
7960
8040
|
if (query.userId) {
|
|
7961
|
-
conditions.push(
|
|
8041
|
+
conditions.push(eq63(verificationsInIam.userId, query.userId));
|
|
7962
8042
|
}
|
|
7963
8043
|
if (query.type) {
|
|
7964
|
-
conditions.push(
|
|
8044
|
+
conditions.push(eq63(verificationsInIam.type, query.type));
|
|
7965
8045
|
}
|
|
7966
8046
|
if (query.status) {
|
|
7967
8047
|
if (query.status === "active") {
|
|
@@ -7975,8 +8055,8 @@ var listVerificationsHandler = async (c) => {
|
|
|
7975
8055
|
}
|
|
7976
8056
|
}
|
|
7977
8057
|
const [verifications, totalResult] = await Promise.all([
|
|
7978
|
-
database.select().from(verificationsInIam).where(
|
|
7979
|
-
database.select({ count: sql29`count(*)` }).from(verificationsInIam).where(
|
|
8058
|
+
database.select().from(verificationsInIam).where(and58(...conditions)).limit(limit).offset(offset),
|
|
8059
|
+
database.select({ count: sql29`count(*)` }).from(verificationsInIam).where(and58(...conditions))
|
|
7980
8060
|
]);
|
|
7981
8061
|
const total = Number(totalResult[0]?.count || 0);
|
|
7982
8062
|
return c.json({ verifications, total, page, limit }, 200);
|
|
@@ -8558,8 +8638,6 @@ export {
|
|
|
8558
8638
|
createSessionMiddleware,
|
|
8559
8639
|
createTenantMiddleware,
|
|
8560
8640
|
hasPermission,
|
|
8561
|
-
hasPermissionThrow
|
|
8562
|
-
hasRole,
|
|
8563
|
-
hasRoleThrow
|
|
8641
|
+
hasPermissionThrow
|
|
8564
8642
|
};
|
|
8565
8643
|
//# sourceMappingURL=index.js.map
|