@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-Cb7JZobZ.d.ts → index-D8OE85f8.d.ts} +1 -57
- package/dist/{index-DssTTD4U.d.ts → index-DwIwuvVj.d.ts} +1 -1
- package/dist/index.d.ts +5 -5
- package/dist/index.js +544 -497
- 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 +3 -75
- 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 });
|
|
@@ -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
|
|
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/
|
|
1047
|
-
|
|
1048
|
-
|
|
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
|
|
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
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
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
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
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
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
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
|
|
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 ?
|
|
1323
|
-
|
|
1481
|
+
const whereClause = isEmail ? and8(
|
|
1482
|
+
eq8(usersInIam.tenantId, tenantId),
|
|
1324
1483
|
sql6`lower(${usersInIam.email}) = lower(${identifier})`
|
|
1325
|
-
) :
|
|
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
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
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
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1516
|
+
and8(
|
|
1517
|
+
eq8(verificationsInIam.userId, userId),
|
|
1518
|
+
eq8(verificationsInIam.tenantId, tenantId)
|
|
1360
1519
|
)
|
|
1361
1520
|
);
|
|
1362
1521
|
await tx.delete(accountsInIam).where(
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1522
|
+
and8(
|
|
1523
|
+
eq8(accountsInIam.userId, userId),
|
|
1524
|
+
eq8(accountsInIam.tenantId, tenantId)
|
|
1366
1525
|
)
|
|
1367
1526
|
);
|
|
1368
|
-
await tx.delete(usersInIam).where(
|
|
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 ?
|
|
1409
|
-
|
|
1567
|
+
const whereClause = isEmail ? and8(
|
|
1568
|
+
eq8(usersInIam.tenantId, tenantId),
|
|
1410
1569
|
userTypeFilter,
|
|
1411
1570
|
sql6`lower(${usersInIam.email}) = lower(${identifier})`
|
|
1412
|
-
) :
|
|
1413
|
-
|
|
1571
|
+
) : and8(
|
|
1572
|
+
eq8(usersInIam.tenantId, tenantId),
|
|
1414
1573
|
userTypeFilter,
|
|
1415
|
-
|
|
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 ${
|
|
1434
|
-
and ${
|
|
1435
|
-
and ${
|
|
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(
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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(
|
|
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(
|
|
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
|
|
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 = [
|
|
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
|
-
|
|
7351
|
-
|
|
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(
|
|
7358
|
-
conditions.push(
|
|
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 =
|
|
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
|
-
|
|
7374
|
-
|
|
7420
|
+
and54(
|
|
7421
|
+
eq59(sessionsInIam.tenantId, tenantId),
|
|
7375
7422
|
inArray6(sessionsInIam.userId, userIds),
|
|
7376
|
-
|
|
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
|
-
|
|
7389
|
-
|
|
7390
|
-
|
|
7435
|
+
and54(
|
|
7436
|
+
eq59(rolesInIam.tenantId, userRolesInIam.tenantId),
|
|
7437
|
+
eq59(rolesInIam.id, userRolesInIam.roleId)
|
|
7391
7438
|
)
|
|
7392
7439
|
).where(
|
|
7393
|
-
|
|
7394
|
-
|
|
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
|
|
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 = [
|
|
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(
|
|
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
|
|
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(
|
|
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
|
-
|
|
7466
|
-
|
|
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(
|
|
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
|
-
|
|
7519
|
-
|
|
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
|
-
|
|
7529
|
-
|
|
7530
|
-
|
|
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
|
-
|
|
7545
|
-
|
|
7546
|
-
|
|
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
|
|
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
|
-
|
|
7967
|
-
|
|
7968
|
-
|
|
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
|
-
|
|
7976
|
-
|
|
7977
|
-
|
|
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
|
|
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 = [
|
|
8039
|
+
const conditions = [eq63(verificationsInIam.tenantId, tenantId)];
|
|
7993
8040
|
if (query.userId) {
|
|
7994
|
-
conditions.push(
|
|
8041
|
+
conditions.push(eq63(verificationsInIam.userId, query.userId));
|
|
7995
8042
|
}
|
|
7996
8043
|
if (query.type) {
|
|
7997
|
-
conditions.push(
|
|
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(
|
|
8012
|
-
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))
|
|
8013
8060
|
]);
|
|
8014
8061
|
const total = Number(totalResult[0]?.count || 0);
|
|
8015
8062
|
return c.json({ verifications, total, page, limit }, 200);
|