@mesob/auth-hono 0.3.5 → 0.4.1
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-BvdbhtRX.d.ts → index-CKOeabpa.d.ts} +23 -2
- package/dist/{index-Bh3bDnP5.d.ts → index-zShda6U3.d.ts} +188 -137
- package/dist/index.d.ts +5 -4
- package/dist/index.js +2503 -839
- package/dist/index.js.map +1 -1
- package/dist/lib/cleanup.d.ts +1 -1
- package/dist/lib/cleanup.js +36 -34
- package/dist/lib/cleanup.js.map +1 -1
- package/dist/lib/cookie.d.ts +3 -2
- package/dist/lib/has-role-permission.d.ts +3 -2
- package/dist/lib/has-role-permission.js +2 -4
- package/dist/lib/has-role-permission.js.map +1 -1
- package/dist/lib/iam-seed.d.ts +2178 -0
- package/dist/lib/iam-seed.js +470 -0
- package/dist/lib/iam-seed.js.map +1 -0
- package/dist/lib/normalize-auth-response.d.ts +26 -0
- package/dist/lib/normalize-auth-response.js +20 -0
- package/dist/lib/normalize-auth-response.js.map +1 -0
- package/dist/lib/normalize-user.d.ts +3 -2
- package/dist/lib/openapi-config.d.ts +3 -2
- package/dist/lib/permission-catalog.d.ts +27 -0
- package/dist/lib/permission-catalog.js +364 -0
- package/dist/lib/permission-catalog.js.map +1 -0
- package/dist/lib/phone-validation.d.ts +3 -2
- package/dist/lib/session.d.ts +3 -2
- package/dist/lib/tenant.d.ts +3 -2
- package/dist/lib/user-auth-select.d.ts +9 -0
- package/dist/lib/user-auth-select.js +352 -0
- package/dist/lib/user-auth-select.js.map +1 -0
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -161,11 +161,10 @@ var tenantsInIam = iam.table("tenants", {
|
|
|
161
161
|
var rolePermissionsInIam = iam.table("role_permissions", {
|
|
162
162
|
id: uuid().default(sql`uuid_generate_v7()`).primaryKey().notNull(),
|
|
163
163
|
tenantId: varchar("tenant_id", { length: 30 }).notNull(),
|
|
164
|
-
|
|
165
|
-
|
|
164
|
+
permissionId: text("permission_id").notNull(),
|
|
165
|
+
roleId: uuid("role_id").notNull()
|
|
166
166
|
}, (table) => [
|
|
167
167
|
index("idx_role_permissions_permission_id").using("btree", table.tenantId.asc().nullsLast().op("text_ops"), table.permissionId.asc().nullsLast().op("text_ops")),
|
|
168
|
-
index("idx_role_permissions_tenant_role").using("btree", table.tenantId.asc().nullsLast().op("text_ops"), table.roleId.asc().nullsLast().op("text_ops")),
|
|
169
168
|
foreignKey({
|
|
170
169
|
columns: [table.tenantId],
|
|
171
170
|
foreignColumns: [tenantsInIam.id],
|
|
@@ -177,11 +176,11 @@ var rolePermissionsInIam = iam.table("role_permissions", {
|
|
|
177
176
|
name: "role_permissions_permission_id_fkey"
|
|
178
177
|
}).onUpdate("cascade").onDelete("cascade"),
|
|
179
178
|
foreignKey({
|
|
180
|
-
columns: [table.roleId],
|
|
181
|
-
foreignColumns: [rolesInIam.id],
|
|
182
|
-
name: "
|
|
183
|
-
}).
|
|
184
|
-
unique("
|
|
179
|
+
columns: [table.tenantId, table.roleId],
|
|
180
|
+
foreignColumns: [rolesInIam.tenantId, rolesInIam.id],
|
|
181
|
+
name: "role_permissions_tenant_role_fkey"
|
|
182
|
+
}).onDelete("cascade"),
|
|
183
|
+
unique("role_permissions_tenant_role_permission_unique").on(table.tenantId, table.permissionId, table.roleId),
|
|
185
184
|
pgPolicy("tenant_isolation", { as: "permissive", for: "all", to: ["public"], using: sql`((tenant_id)::text = (iam.current_tenant_id())::text)`, withCheck: sql`((tenant_id)::text = (iam.current_tenant_id())::text)` })
|
|
186
185
|
]);
|
|
187
186
|
var permissionsInIam = iam.table("permissions", {
|
|
@@ -225,23 +224,6 @@ var accountsInIam = iam.table("accounts", {
|
|
|
225
224
|
unique("accounts_tenant_provider_account_unique").on(table.tenantId, table.provider, table.providerAccountId),
|
|
226
225
|
pgPolicy("tenant_isolation", { as: "permissive", for: "all", to: ["public"], using: sql`((tenant_id)::text = (iam.current_tenant_id())::text)`, withCheck: sql`((tenant_id)::text = (iam.current_tenant_id())::text)` })
|
|
227
226
|
]);
|
|
228
|
-
var rolesInIam = iam.table("roles", {
|
|
229
|
-
id: text().primaryKey().notNull(),
|
|
230
|
-
tenantId: varchar("tenant_id", { length: 30 }).notNull(),
|
|
231
|
-
createdAt: timestamp("created_at", { withTimezone: true, mode: "string" }).default(sql`CURRENT_TIMESTAMP`).notNull(),
|
|
232
|
-
updatedAt: timestamp("updated_at", { withTimezone: true, mode: "string" }).default(sql`CURRENT_TIMESTAMP`).notNull(),
|
|
233
|
-
name: jsonb().notNull(),
|
|
234
|
-
description: jsonb().notNull(),
|
|
235
|
-
code: text().notNull()
|
|
236
|
-
}, (table) => [
|
|
237
|
-
foreignKey({
|
|
238
|
-
columns: [table.tenantId],
|
|
239
|
-
foreignColumns: [tenantsInIam.id],
|
|
240
|
-
name: "roles_tenant_id_fkey"
|
|
241
|
-
}).onUpdate("cascade").onDelete("cascade"),
|
|
242
|
-
unique("roles_tenant_code_unique").on(table.tenantId, table.code),
|
|
243
|
-
pgPolicy("tenant_isolation", { as: "permissive", for: "all", to: ["public"], using: sql`((tenant_id)::text = (iam.current_tenant_id())::text)`, withCheck: sql`((tenant_id)::text = (iam.current_tenant_id())::text)` })
|
|
244
|
-
]);
|
|
245
227
|
var usersInIam = iam.table("users", {
|
|
246
228
|
id: uuid().default(sql`uuid_generate_v7()`).primaryKey().notNull(),
|
|
247
229
|
tenantId: varchar("tenant_id", { length: 30 }).notNull(),
|
|
@@ -257,7 +239,7 @@ var usersInIam = iam.table("users", {
|
|
|
257
239
|
bannedUntil: timestamp("banned_until", { withTimezone: true, mode: "string" }),
|
|
258
240
|
lastSignInAt: timestamp("last_sign_in_at", { withTimezone: true, mode: "string" }),
|
|
259
241
|
loginAttempt: smallint("login_attempt").default(0).notNull(),
|
|
260
|
-
userType: text("user_type").array().default(["
|
|
242
|
+
userType: text("user_type").array().default(["RAY"]).notNull()
|
|
261
243
|
}, (table) => [
|
|
262
244
|
index("idx_users_auth_lookup").using("btree", table.tenantId.asc().nullsLast().op("bool_ops"), table.email.asc().nullsLast().op("bool_ops"), table.id.asc().nullsLast().op("timestamptz_ops"), table.emailVerified.asc().nullsLast().op("timestamptz_ops"), table.bannedUntil.asc().nullsLast().op("uuid_ops")).where(sql`(email IS NOT NULL)`),
|
|
263
245
|
index("idx_users_email_lookup").using("btree", table.tenantId.asc().nullsLast().op("text_ops"), table.email.asc().nullsLast().op("text_ops")).where(sql`(email IS NOT NULL)`),
|
|
@@ -281,14 +263,34 @@ var usersInIam = iam.table("users", {
|
|
|
281
263
|
check("users_contact_required_check", sql`(email IS NOT NULL) OR (phone IS NOT NULL)`),
|
|
282
264
|
check("users_user_type_check", sql`user_type <@ ARRAY['candidate'::text, 'employee'::text, 'admin'::text]`)
|
|
283
265
|
]);
|
|
266
|
+
var rolesInIam = iam.table("roles", {
|
|
267
|
+
tenantId: varchar("tenant_id", { length: 30 }).notNull(),
|
|
268
|
+
createdAt: timestamp("created_at", { withTimezone: true, mode: "string" }).default(sql`CURRENT_TIMESTAMP`).notNull(),
|
|
269
|
+
updatedAt: timestamp("updated_at", { withTimezone: true, mode: "string" }).default(sql`CURRENT_TIMESTAMP`).notNull(),
|
|
270
|
+
name: jsonb().notNull(),
|
|
271
|
+
description: jsonb().notNull(),
|
|
272
|
+
code: text().notNull(),
|
|
273
|
+
id: uuid().default(sql`uuid_generate_v7()`).primaryKey().notNull(),
|
|
274
|
+
isSystem: boolean("is_system").default(false).notNull(),
|
|
275
|
+
isEditable: boolean("is_editable").default(true).notNull(),
|
|
276
|
+
isDeletable: boolean("is_deletable").default(true).notNull()
|
|
277
|
+
}, (table) => [
|
|
278
|
+
foreignKey({
|
|
279
|
+
columns: [table.tenantId],
|
|
280
|
+
foreignColumns: [tenantsInIam.id],
|
|
281
|
+
name: "roles_tenant_id_fkey"
|
|
282
|
+
}).onUpdate("cascade").onDelete("cascade"),
|
|
283
|
+
unique("roles_tenant_code_unique").on(table.tenantId, table.code),
|
|
284
|
+
unique("roles_tenant_id_unique").on(table.tenantId, table.id),
|
|
285
|
+
pgPolicy("tenant_isolation", { as: "permissive", for: "all", to: ["public"], using: sql`((tenant_id)::text = (iam.current_tenant_id())::text)`, withCheck: sql`((tenant_id)::text = (iam.current_tenant_id())::text)` })
|
|
286
|
+
]);
|
|
284
287
|
var userRolesInIam = iam.table("user_roles", {
|
|
285
288
|
id: uuid().default(sql`uuid_generate_v7()`).primaryKey().notNull(),
|
|
286
289
|
tenantId: varchar("tenant_id", { length: 30 }).notNull(),
|
|
287
290
|
userId: uuid("user_id").notNull(),
|
|
288
|
-
roleId:
|
|
291
|
+
roleId: uuid("role_id").notNull()
|
|
289
292
|
}, (table) => [
|
|
290
|
-
index("
|
|
291
|
-
index("idx_user_roles_tenant_user").using("btree", table.tenantId.asc().nullsLast().op("text_ops"), table.userId.asc().nullsLast().op("text_ops")),
|
|
293
|
+
index("idx_user_roles_tenant_user").using("btree", table.tenantId.asc().nullsLast().op("text_ops"), table.userId.asc().nullsLast().op("uuid_ops")),
|
|
292
294
|
foreignKey({
|
|
293
295
|
columns: [table.tenantId],
|
|
294
296
|
foreignColumns: [tenantsInIam.id],
|
|
@@ -300,11 +302,11 @@ var userRolesInIam = iam.table("user_roles", {
|
|
|
300
302
|
name: "user_roles_user_id_fkey"
|
|
301
303
|
}).onUpdate("cascade").onDelete("cascade"),
|
|
302
304
|
foreignKey({
|
|
303
|
-
columns: [table.roleId],
|
|
304
|
-
foreignColumns: [rolesInIam.id],
|
|
305
|
-
name: "
|
|
306
|
-
}).
|
|
307
|
-
unique("
|
|
305
|
+
columns: [table.tenantId, table.roleId],
|
|
306
|
+
foreignColumns: [rolesInIam.tenantId, rolesInIam.id],
|
|
307
|
+
name: "user_roles_tenant_role_fkey"
|
|
308
|
+
}).onDelete("cascade"),
|
|
309
|
+
unique("user_roles_tenant_user_role_unique").on(table.tenantId, table.userId, table.roleId),
|
|
308
310
|
pgPolicy("tenant_isolation", { as: "permissive", for: "all", to: ["public"], using: sql`((tenant_id)::text = (iam.current_tenant_id())::text)`, withCheck: sql`((tenant_id)::text = (iam.current_tenant_id())::text)` })
|
|
309
311
|
]);
|
|
310
312
|
var domainsInIam = iam.table("domains", {
|
|
@@ -348,8 +350,8 @@ var tenantsInIamRelations = relations(tenantsInIam, ({ many }) => ({
|
|
|
348
350
|
accountChangesInIam: many(accountChangesInIam),
|
|
349
351
|
rolePermissionsInIam: many(rolePermissionsInIam),
|
|
350
352
|
accountsInIam: many(accountsInIam),
|
|
351
|
-
rolesInIam: many(rolesInIam),
|
|
352
353
|
usersInIam: many(usersInIam),
|
|
354
|
+
rolesInIam: many(rolesInIam),
|
|
353
355
|
userRolesInIam: many(userRolesInIam),
|
|
354
356
|
domainsInIam: many(domainsInIam)
|
|
355
357
|
}));
|
|
@@ -394,8 +396,8 @@ var rolePermissionsInIamRelations = relations(rolePermissionsInIam, ({ one }) =>
|
|
|
394
396
|
references: [permissionsInIam.id]
|
|
395
397
|
}),
|
|
396
398
|
rolesInIam: one(rolesInIam, {
|
|
397
|
-
fields: [rolePermissionsInIam.
|
|
398
|
-
references: [rolesInIam.
|
|
399
|
+
fields: [rolePermissionsInIam.tenantId],
|
|
400
|
+
references: [rolesInIam.tenantId]
|
|
399
401
|
})
|
|
400
402
|
}));
|
|
401
403
|
var permissionsInIamRelations = relations(permissionsInIam, ({ many }) => ({
|
|
@@ -429,8 +431,8 @@ var userRolesInIamRelations = relations(userRolesInIam, ({ one }) => ({
|
|
|
429
431
|
references: [usersInIam.id]
|
|
430
432
|
}),
|
|
431
433
|
rolesInIam: one(rolesInIam, {
|
|
432
|
-
fields: [userRolesInIam.
|
|
433
|
-
references: [rolesInIam.
|
|
434
|
+
fields: [userRolesInIam.tenantId],
|
|
435
|
+
references: [rolesInIam.tenantId]
|
|
434
436
|
})
|
|
435
437
|
}));
|
|
436
438
|
var domainsInIamRelations = relations(domainsInIam, ({ one }) => ({
|
|
@@ -534,7 +536,66 @@ var findTenantById = async (database, tenantId) => {
|
|
|
534
536
|
};
|
|
535
537
|
|
|
536
538
|
// src/db/orm/user.ts
|
|
539
|
+
import { and as and4, eq as eq4 } from "drizzle-orm";
|
|
540
|
+
|
|
541
|
+
// src/lib/user-auth-select.ts
|
|
537
542
|
import { and as and3, eq as eq3, sql as sql3 } from "drizzle-orm";
|
|
543
|
+
function getUserAuthSelect(tenantId) {
|
|
544
|
+
return {
|
|
545
|
+
roles: sql3`COALESCE(
|
|
546
|
+
(
|
|
547
|
+
select array_to_json(array_agg(distinct "iam"."roles"."id"))
|
|
548
|
+
from ${userRolesInIam}
|
|
549
|
+
inner join ${rolesInIam}
|
|
550
|
+
on ${and3(
|
|
551
|
+
eq3(rolesInIam.tenantId, userRolesInIam.tenantId),
|
|
552
|
+
eq3(rolesInIam.id, userRolesInIam.roleId)
|
|
553
|
+
)}
|
|
554
|
+
where ${and3(
|
|
555
|
+
eq3(userRolesInIam.tenantId, tenantId),
|
|
556
|
+
eq3(userRolesInIam.userId, usersInIam.id)
|
|
557
|
+
)}
|
|
558
|
+
),
|
|
559
|
+
'[]'::json
|
|
560
|
+
)`,
|
|
561
|
+
roleCodes: sql3`COALESCE(
|
|
562
|
+
(
|
|
563
|
+
select array_to_json(array_agg(distinct ${rolesInIam.code}))
|
|
564
|
+
from ${userRolesInIam}
|
|
565
|
+
inner join ${rolesInIam}
|
|
566
|
+
on ${and3(
|
|
567
|
+
eq3(rolesInIam.tenantId, userRolesInIam.tenantId),
|
|
568
|
+
eq3(rolesInIam.id, userRolesInIam.roleId)
|
|
569
|
+
)}
|
|
570
|
+
where ${and3(
|
|
571
|
+
eq3(userRolesInIam.tenantId, tenantId),
|
|
572
|
+
eq3(userRolesInIam.userId, usersInIam.id)
|
|
573
|
+
)}
|
|
574
|
+
),
|
|
575
|
+
'[]'::json
|
|
576
|
+
)`,
|
|
577
|
+
permissions: sql3`COALESCE(
|
|
578
|
+
(
|
|
579
|
+
select array_to_json(array_agg(distinct "iam"."permissions"."id"))
|
|
580
|
+
from ${userRolesInIam}
|
|
581
|
+
inner join ${rolePermissionsInIam}
|
|
582
|
+
on ${and3(
|
|
583
|
+
eq3(rolePermissionsInIam.tenantId, userRolesInIam.tenantId),
|
|
584
|
+
eq3(rolePermissionsInIam.roleId, userRolesInIam.roleId)
|
|
585
|
+
)}
|
|
586
|
+
inner join ${permissionsInIam}
|
|
587
|
+
on ${eq3(permissionsInIam.id, rolePermissionsInIam.permissionId)}
|
|
588
|
+
where ${and3(
|
|
589
|
+
eq3(userRolesInIam.tenantId, tenantId),
|
|
590
|
+
eq3(userRolesInIam.userId, usersInIam.id)
|
|
591
|
+
)}
|
|
592
|
+
),
|
|
593
|
+
'[]'::json
|
|
594
|
+
)`
|
|
595
|
+
};
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
// src/db/orm/user.ts
|
|
538
599
|
var fetchUserWithRoles = async ({
|
|
539
600
|
database,
|
|
540
601
|
userId,
|
|
@@ -551,40 +612,8 @@ var fetchUserWithRoles = async ({
|
|
|
551
612
|
emailVerified: usersInIam.emailVerified,
|
|
552
613
|
phoneVerified: usersInIam.phoneVerified,
|
|
553
614
|
lastSignInAt: usersInIam.lastSignInAt,
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
'[]'::json
|
|
557
|
-
)`,
|
|
558
|
-
roleCodes: sql3`COALESCE(
|
|
559
|
-
array_to_json(array_agg(DISTINCT ${rolesInIam.code}) FILTER (WHERE ${rolesInIam.code} IS NOT NULL)),
|
|
560
|
-
'[]'::json
|
|
561
|
-
)`,
|
|
562
|
-
permissions: sql3`COALESCE(
|
|
563
|
-
array_to_json(array_agg(DISTINCT ${permissionsInIam.id}) FILTER (WHERE ${permissionsInIam.id} IS NOT NULL)),
|
|
564
|
-
'[]'::json
|
|
565
|
-
)`
|
|
566
|
-
}).from(usersInIam).leftJoin(
|
|
567
|
-
userRolesInIam,
|
|
568
|
-
and3(
|
|
569
|
-
eq3(userRolesInIam.userId, usersInIam.id),
|
|
570
|
-
eq3(userRolesInIam.tenantId, tenantId)
|
|
571
|
-
)
|
|
572
|
-
).leftJoin(
|
|
573
|
-
rolesInIam,
|
|
574
|
-
and3(
|
|
575
|
-
eq3(userRolesInIam.roleId, rolesInIam.id),
|
|
576
|
-
eq3(rolesInIam.tenantId, tenantId)
|
|
577
|
-
)
|
|
578
|
-
).leftJoin(
|
|
579
|
-
rolePermissionsInIam,
|
|
580
|
-
and3(
|
|
581
|
-
eq3(rolePermissionsInIam.roleId, rolesInIam.id),
|
|
582
|
-
eq3(rolePermissionsInIam.tenantId, tenantId)
|
|
583
|
-
)
|
|
584
|
-
).leftJoin(
|
|
585
|
-
permissionsInIam,
|
|
586
|
-
eq3(rolePermissionsInIam.permissionId, permissionsInIam.id)
|
|
587
|
-
).where(and3(eq3(usersInIam.id, userId), eq3(usersInIam.tenantId, tenantId))).groupBy(usersInIam.id).limit(1);
|
|
615
|
+
...getUserAuthSelect(tenantId)
|
|
616
|
+
}).from(usersInIam).where(and4(eq4(usersInIam.id, userId), eq4(usersInIam.tenantId, tenantId))).limit(1);
|
|
588
617
|
return userResult || null;
|
|
589
618
|
};
|
|
590
619
|
|
|
@@ -821,6 +850,7 @@ import { z } from "zod";
|
|
|
821
850
|
var emailField = z.string().trim().email("Invalid email address").max(255, "Email too long");
|
|
822
851
|
var phoneField = z.string().trim().min(6, "Phone too short").max(30, "Phone too long").regex(/^[+()\d\s-]+$/, "Invalid phone number format");
|
|
823
852
|
var passwordField = z.string().min(8, "Password must be at least 8 characters").max(128, "Password too long");
|
|
853
|
+
var identifierField = z.string().trim().min(1, "Identifier is required");
|
|
824
854
|
var userSchema = z.object({
|
|
825
855
|
id: z.string().uuid(),
|
|
826
856
|
tenantId: z.string(),
|
|
@@ -843,12 +873,34 @@ var sessionSchema = z.object({
|
|
|
843
873
|
userAgent: z.string().nullable().optional(),
|
|
844
874
|
ip: z.string().nullable().optional()
|
|
845
875
|
});
|
|
876
|
+
var authUserSchema = z.object({
|
|
877
|
+
id: z.string().uuid(),
|
|
878
|
+
tenantId: z.string(),
|
|
879
|
+
fullName: z.string(),
|
|
880
|
+
email: z.string().email().nullable(),
|
|
881
|
+
phone: z.string().nullable(),
|
|
882
|
+
image: z.string().nullable(),
|
|
883
|
+
emailVerified: z.boolean(),
|
|
884
|
+
phoneVerified: z.boolean()
|
|
885
|
+
});
|
|
886
|
+
var authSessionSchema = z.object({
|
|
887
|
+
id: z.string().uuid(),
|
|
888
|
+
expiresAt: z.string().datetime()
|
|
889
|
+
});
|
|
846
890
|
var authSuccessSchema = z.object({
|
|
847
|
-
user:
|
|
848
|
-
session:
|
|
891
|
+
user: authUserSchema,
|
|
892
|
+
session: authSessionSchema.nullable(),
|
|
849
893
|
sessionToken: z.string().optional(),
|
|
850
894
|
sessionExpiresAt: z.string().datetime().optional()
|
|
851
895
|
});
|
|
896
|
+
var authAccountSchema = z.object({
|
|
897
|
+
fullName: z.string(),
|
|
898
|
+
email: z.string().email().nullable(),
|
|
899
|
+
phone: z.string().nullable(),
|
|
900
|
+
verified: z.boolean(),
|
|
901
|
+
hasPassword: z.boolean(),
|
|
902
|
+
requiresPasswordSetup: z.boolean()
|
|
903
|
+
});
|
|
852
904
|
var messageSchema = z.object({
|
|
853
905
|
message: z.string()
|
|
854
906
|
});
|
|
@@ -870,7 +922,13 @@ var signUpSchema = z.object({
|
|
|
870
922
|
path: ["email"]
|
|
871
923
|
});
|
|
872
924
|
var signInSchema = z.object({
|
|
873
|
-
identifier:
|
|
925
|
+
identifier: identifierField,
|
|
926
|
+
password: passwordField,
|
|
927
|
+
/** Keep me signed in for longer. Default: true */
|
|
928
|
+
rememberMe: z.boolean().default(true)
|
|
929
|
+
});
|
|
930
|
+
var setPasswordSchema = z.object({
|
|
931
|
+
identifier: identifierField,
|
|
874
932
|
password: passwordField,
|
|
875
933
|
/** Keep me signed in for longer. Default: true */
|
|
876
934
|
rememberMe: z.boolean().default(true)
|
|
@@ -919,11 +977,14 @@ var messageWithVerificationIdSchema = messageSchema.extend({
|
|
|
919
977
|
verificationId: z.string().uuid().optional()
|
|
920
978
|
});
|
|
921
979
|
var checkAccountSchema = z.object({
|
|
922
|
-
username:
|
|
980
|
+
username: identifierField
|
|
923
981
|
});
|
|
924
982
|
var checkAccountResponseSchema = z.object({
|
|
925
983
|
exists: z.boolean(),
|
|
926
|
-
verified: z.boolean()
|
|
984
|
+
verified: z.boolean(),
|
|
985
|
+
hasPassword: z.boolean(),
|
|
986
|
+
requiresPasswordSetup: z.boolean(),
|
|
987
|
+
account: authAccountSchema.nullable()
|
|
927
988
|
});
|
|
928
989
|
var updateProfileSchema = z.object({
|
|
929
990
|
fullName: z.string().min(1).max(255).optional().describe("User full name")
|
|
@@ -949,7 +1010,7 @@ var pendingAccountChangeResponseSchema = z.object({
|
|
|
949
1010
|
});
|
|
950
1011
|
|
|
951
1012
|
// src/routes/auth/handler/check-account.ts
|
|
952
|
-
import { and as
|
|
1013
|
+
import { and as and5, eq as eq5, sql as sql4 } from "drizzle-orm";
|
|
953
1014
|
|
|
954
1015
|
// src/lib/tenant.ts
|
|
955
1016
|
import { HTTPException as HTTPException2 } from "hono/http-exception";
|
|
@@ -981,22 +1042,45 @@ var checkAccountHandler = async (c) => {
|
|
|
981
1042
|
const { username } = body;
|
|
982
1043
|
const isEmail = username.includes("@");
|
|
983
1044
|
const userTypeFilter = sql4`${usersInIam.userType} @> ARRAY[${config.userType}]::text[]`;
|
|
984
|
-
const whereClause = isEmail ?
|
|
985
|
-
|
|
1045
|
+
const whereClause = isEmail ? and5(
|
|
1046
|
+
eq5(usersInIam.tenantId, resolvedTenantId),
|
|
986
1047
|
userTypeFilter,
|
|
987
1048
|
sql4`lower(${usersInIam.email}) = lower(${username})`
|
|
988
|
-
) :
|
|
989
|
-
|
|
1049
|
+
) : and5(
|
|
1050
|
+
eq5(usersInIam.tenantId, resolvedTenantId),
|
|
990
1051
|
userTypeFilter,
|
|
991
|
-
|
|
1052
|
+
eq5(usersInIam.phone, username)
|
|
992
1053
|
);
|
|
993
1054
|
const [result] = await database.select({
|
|
994
|
-
|
|
995
|
-
|
|
1055
|
+
fullName: usersInIam.fullName,
|
|
1056
|
+
email: usersInIam.email,
|
|
1057
|
+
phone: usersInIam.phone,
|
|
1058
|
+
verified: isEmail ? usersInIam.emailVerified : usersInIam.phoneVerified,
|
|
1059
|
+
password: accountsInIam.password
|
|
1060
|
+
}).from(usersInIam).leftJoin(
|
|
1061
|
+
accountsInIam,
|
|
1062
|
+
and5(
|
|
1063
|
+
eq5(accountsInIam.tenantId, resolvedTenantId),
|
|
1064
|
+
eq5(accountsInIam.userId, usersInIam.id),
|
|
1065
|
+
eq5(accountsInIam.provider, "credentials")
|
|
1066
|
+
)
|
|
1067
|
+
).where(whereClause).limit(1);
|
|
1068
|
+
const verified = result?.verified ?? false;
|
|
1069
|
+
const hasPassword = Boolean(result?.password);
|
|
996
1070
|
return c.json(
|
|
997
1071
|
{
|
|
998
1072
|
exists: !!result,
|
|
999
|
-
verified
|
|
1073
|
+
verified,
|
|
1074
|
+
hasPassword,
|
|
1075
|
+
requiresPasswordSetup: !!result && verified && !hasPassword,
|
|
1076
|
+
account: result ? {
|
|
1077
|
+
fullName: result.fullName,
|
|
1078
|
+
email: result.email,
|
|
1079
|
+
phone: result.phone,
|
|
1080
|
+
verified,
|
|
1081
|
+
hasPassword,
|
|
1082
|
+
requiresPasswordSetup: verified && !hasPassword
|
|
1083
|
+
} : null
|
|
1000
1084
|
},
|
|
1001
1085
|
200
|
|
1002
1086
|
);
|
|
@@ -1004,7 +1088,7 @@ var checkAccountHandler = async (c) => {
|
|
|
1004
1088
|
|
|
1005
1089
|
// src/routes/auth/handler/sign-in.ts
|
|
1006
1090
|
import { logger as logger2 } from "@mesob/common";
|
|
1007
|
-
import { and as
|
|
1091
|
+
import { and as and9, eq as eq9 } from "drizzle-orm";
|
|
1008
1092
|
|
|
1009
1093
|
// src/errors.ts
|
|
1010
1094
|
var AUTH_ERRORS = {
|
|
@@ -1018,15 +1102,25 @@ var AUTH_ERRORS = {
|
|
|
1018
1102
|
REQUIRES_VERIFICATION: "REQUIRES_VERIFICATION",
|
|
1019
1103
|
UNAUTHORIZED: "UNAUTHORIZED",
|
|
1020
1104
|
ACCESS_DENIED: "ACCESS_DENIED",
|
|
1021
|
-
HAS_NO_PASSWORD: "HAS_NO_PASSWORD"
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1105
|
+
HAS_NO_PASSWORD: "HAS_NO_PASSWORD",
|
|
1106
|
+
PASSWORD_ALREADY_SET: "PASSWORD_ALREADY_SET"
|
|
1107
|
+
};
|
|
1108
|
+
|
|
1109
|
+
// src/lib/normalize-auth-response.ts
|
|
1110
|
+
var normalizeAuthUser = (user) => ({
|
|
1111
|
+
id: user.id,
|
|
1112
|
+
tenantId: user.tenantId,
|
|
1113
|
+
fullName: user.fullName,
|
|
1114
|
+
email: user.email,
|
|
1115
|
+
phone: user.phone,
|
|
1116
|
+
image: user.image,
|
|
1117
|
+
emailVerified: user.emailVerified,
|
|
1118
|
+
phoneVerified: user.phoneVerified
|
|
1119
|
+
});
|
|
1120
|
+
var normalizeAuthSession = (session) => session ? {
|
|
1121
|
+
id: session.id,
|
|
1122
|
+
expiresAt: session.expiresAt
|
|
1123
|
+
} : null;
|
|
1030
1124
|
|
|
1031
1125
|
// src/lib/session.ts
|
|
1032
1126
|
import { dayjs } from "@mesob/common";
|
|
@@ -1105,7 +1199,7 @@ var getRefreshedExpiresAt = ({
|
|
|
1105
1199
|
};
|
|
1106
1200
|
|
|
1107
1201
|
// src/routes/auth/helper/session.ts
|
|
1108
|
-
import { and as
|
|
1202
|
+
import { and as and6, asc, eq as eq6, gt as gt2, inArray, sql as sql5 } from "drizzle-orm";
|
|
1109
1203
|
var createSessionRecord = async ({
|
|
1110
1204
|
tx,
|
|
1111
1205
|
tenantId,
|
|
@@ -1174,9 +1268,9 @@ var cleanupOldSessions = async ({
|
|
|
1174
1268
|
maxSessions
|
|
1175
1269
|
}) => {
|
|
1176
1270
|
const [{ count }] = await database.select({ count: sql5`count(*)` }).from(sessionsInIam).where(
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1271
|
+
and6(
|
|
1272
|
+
eq6(sessionsInIam.tenantId, tenantId),
|
|
1273
|
+
eq6(sessionsInIam.userId, userId),
|
|
1180
1274
|
gt2(sessionsInIam.expiresAt, (/* @__PURE__ */ new Date()).toISOString())
|
|
1181
1275
|
)
|
|
1182
1276
|
);
|
|
@@ -1185,9 +1279,9 @@ var cleanupOldSessions = async ({
|
|
|
1185
1279
|
}
|
|
1186
1280
|
const toDeleteCount = count - maxSessions;
|
|
1187
1281
|
const idsToDelete = await database.select({ id: sessionsInIam.id }).from(sessionsInIam).where(
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1282
|
+
and6(
|
|
1283
|
+
eq6(sessionsInIam.tenantId, tenantId),
|
|
1284
|
+
eq6(sessionsInIam.userId, userId),
|
|
1191
1285
|
gt2(sessionsInIam.expiresAt, (/* @__PURE__ */ new Date()).toISOString())
|
|
1192
1286
|
)
|
|
1193
1287
|
).orderBy(asc(sessionsInIam.createdAt)).limit(toDeleteCount);
|
|
@@ -1195,9 +1289,9 @@ var cleanupOldSessions = async ({
|
|
|
1195
1289
|
return;
|
|
1196
1290
|
}
|
|
1197
1291
|
await database.delete(sessionsInIam).where(
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1292
|
+
and6(
|
|
1293
|
+
eq6(sessionsInIam.tenantId, tenantId),
|
|
1294
|
+
eq6(sessionsInIam.userId, userId),
|
|
1201
1295
|
inArray(
|
|
1202
1296
|
sessionsInIam.id,
|
|
1203
1297
|
idsToDelete.map((s) => s.id)
|
|
@@ -1207,17 +1301,17 @@ var cleanupOldSessions = async ({
|
|
|
1207
1301
|
};
|
|
1208
1302
|
|
|
1209
1303
|
// src/routes/auth/helper/user.ts
|
|
1210
|
-
import { and as
|
|
1304
|
+
import { and as and7, eq as eq7, gt as gt3, sql as sql6 } from "drizzle-orm";
|
|
1211
1305
|
var checkExistingUserStatus = async ({
|
|
1212
1306
|
tx,
|
|
1213
1307
|
identifier,
|
|
1214
1308
|
tenantId,
|
|
1215
1309
|
isEmail
|
|
1216
1310
|
}) => {
|
|
1217
|
-
const whereClause = isEmail ?
|
|
1218
|
-
|
|
1311
|
+
const whereClause = isEmail ? and7(
|
|
1312
|
+
eq7(usersInIam.tenantId, tenantId),
|
|
1219
1313
|
sql6`lower(${usersInIam.email}) = lower(${identifier})`
|
|
1220
|
-
) :
|
|
1314
|
+
) : and7(eq7(usersInIam.tenantId, tenantId), eq7(usersInIam.phone, identifier));
|
|
1221
1315
|
const [existingUser] = await tx.select().from(usersInIam).where(whereClause).limit(1);
|
|
1222
1316
|
if (!existingUser) {
|
|
1223
1317
|
return { action: "proceed" };
|
|
@@ -1227,9 +1321,9 @@ var checkExistingUserStatus = async ({
|
|
|
1227
1321
|
return { action: "error", code: AUTH_ERRORS.USER_EXISTS };
|
|
1228
1322
|
}
|
|
1229
1323
|
const [pendingVerification] = await tx.select().from(verificationsInIam).where(
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1324
|
+
and7(
|
|
1325
|
+
eq7(verificationsInIam.userId, existingUser.id),
|
|
1326
|
+
eq7(verificationsInIam.tenantId, tenantId),
|
|
1233
1327
|
gt3(verificationsInIam.expiresAt, (/* @__PURE__ */ new Date()).toISOString())
|
|
1234
1328
|
)
|
|
1235
1329
|
).limit(1);
|
|
@@ -1249,18 +1343,18 @@ var deleteUnverifiedUser = async ({
|
|
|
1249
1343
|
tenantId
|
|
1250
1344
|
}) => {
|
|
1251
1345
|
await tx.delete(verificationsInIam).where(
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1346
|
+
and7(
|
|
1347
|
+
eq7(verificationsInIam.userId, userId),
|
|
1348
|
+
eq7(verificationsInIam.tenantId, tenantId)
|
|
1255
1349
|
)
|
|
1256
1350
|
);
|
|
1257
1351
|
await tx.delete(accountsInIam).where(
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1352
|
+
and7(
|
|
1353
|
+
eq7(accountsInIam.userId, userId),
|
|
1354
|
+
eq7(accountsInIam.tenantId, tenantId)
|
|
1261
1355
|
)
|
|
1262
1356
|
);
|
|
1263
|
-
await tx.delete(usersInIam).where(
|
|
1357
|
+
await tx.delete(usersInIam).where(and7(eq7(usersInIam.id, userId), eq7(usersInIam.tenantId, tenantId)));
|
|
1264
1358
|
};
|
|
1265
1359
|
var createUserWithAccount = async ({
|
|
1266
1360
|
tx,
|
|
@@ -1300,14 +1394,14 @@ var fetchUserForLogin = async ({
|
|
|
1300
1394
|
userType
|
|
1301
1395
|
}) => {
|
|
1302
1396
|
const userTypeFilter = sql6`${usersInIam.userType} @> ARRAY[${userType}]::text[]`;
|
|
1303
|
-
const whereClause = isEmail ?
|
|
1304
|
-
|
|
1397
|
+
const whereClause = isEmail ? and7(
|
|
1398
|
+
eq7(usersInIam.tenantId, tenantId),
|
|
1305
1399
|
userTypeFilter,
|
|
1306
1400
|
sql6`lower(${usersInIam.email}) = lower(${identifier})`
|
|
1307
|
-
) :
|
|
1308
|
-
|
|
1401
|
+
) : and7(
|
|
1402
|
+
eq7(usersInIam.tenantId, tenantId),
|
|
1309
1403
|
userTypeFilter,
|
|
1310
|
-
|
|
1404
|
+
eq7(usersInIam.phone, identifier)
|
|
1311
1405
|
);
|
|
1312
1406
|
const [row] = await database.select({
|
|
1313
1407
|
id: usersInIam.id,
|
|
@@ -1321,8 +1415,16 @@ var fetchUserForLogin = async ({
|
|
|
1321
1415
|
phoneVerified: usersInIam.phoneVerified,
|
|
1322
1416
|
lastSignInAt: usersInIam.lastSignInAt,
|
|
1323
1417
|
bannedUntil: usersInIam.bannedUntil,
|
|
1324
|
-
loginAttempt: usersInIam.loginAttempt
|
|
1325
|
-
|
|
1418
|
+
loginAttempt: usersInIam.loginAttempt,
|
|
1419
|
+
hasPassword: sql6`coalesce(${accountsInIam.password} is not null, false)`
|
|
1420
|
+
}).from(usersInIam).leftJoin(
|
|
1421
|
+
accountsInIam,
|
|
1422
|
+
and7(
|
|
1423
|
+
eq7(accountsInIam.tenantId, tenantId),
|
|
1424
|
+
eq7(accountsInIam.userId, usersInIam.id),
|
|
1425
|
+
eq7(accountsInIam.provider, "credentials")
|
|
1426
|
+
)
|
|
1427
|
+
).where(whereClause).limit(1);
|
|
1326
1428
|
return row || null;
|
|
1327
1429
|
};
|
|
1328
1430
|
var fetchUserByIdWithRoles = async ({
|
|
@@ -1343,46 +1445,14 @@ var fetchUserByIdWithRoles = async ({
|
|
|
1343
1445
|
lastSignInAt: usersInIam.lastSignInAt,
|
|
1344
1446
|
bannedUntil: usersInIam.bannedUntil,
|
|
1345
1447
|
loginAttempt: usersInIam.loginAttempt,
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
'[]'::json
|
|
1349
|
-
)`,
|
|
1350
|
-
roleCodes: sql6`COALESCE(
|
|
1351
|
-
array_to_json(array_agg(DISTINCT ${rolesInIam.code}) FILTER (WHERE ${rolesInIam.code} IS NOT NULL)),
|
|
1352
|
-
'[]'::json
|
|
1353
|
-
)`,
|
|
1354
|
-
permissions: sql6`COALESCE(
|
|
1355
|
-
array_to_json(array_agg(DISTINCT ${permissionsInIam.id}) FILTER (WHERE ${permissionsInIam.id} IS NOT NULL)),
|
|
1356
|
-
'[]'::json
|
|
1357
|
-
)`
|
|
1358
|
-
}).from(usersInIam).leftJoin(
|
|
1359
|
-
userRolesInIam,
|
|
1360
|
-
and6(
|
|
1361
|
-
eq6(userRolesInIam.userId, usersInIam.id),
|
|
1362
|
-
eq6(userRolesInIam.tenantId, tenantId)
|
|
1363
|
-
)
|
|
1364
|
-
).leftJoin(
|
|
1365
|
-
rolesInIam,
|
|
1366
|
-
and6(
|
|
1367
|
-
eq6(userRolesInIam.roleId, rolesInIam.id),
|
|
1368
|
-
eq6(rolesInIam.tenantId, tenantId)
|
|
1369
|
-
)
|
|
1370
|
-
).leftJoin(
|
|
1371
|
-
rolePermissionsInIam,
|
|
1372
|
-
and6(
|
|
1373
|
-
eq6(rolePermissionsInIam.roleId, rolesInIam.id),
|
|
1374
|
-
eq6(rolePermissionsInIam.tenantId, tenantId)
|
|
1375
|
-
)
|
|
1376
|
-
).leftJoin(
|
|
1377
|
-
permissionsInIam,
|
|
1378
|
-
eq6(rolePermissionsInIam.permissionId, permissionsInIam.id)
|
|
1379
|
-
).where(and6(eq6(usersInIam.id, userId), eq6(usersInIam.tenantId, tenantId))).groupBy(usersInIam.id).limit(1);
|
|
1448
|
+
...getUserAuthSelect(tenantId)
|
|
1449
|
+
}).from(usersInIam).where(and7(eq7(usersInIam.id, userId), eq7(usersInIam.tenantId, tenantId))).limit(1);
|
|
1380
1450
|
return result || null;
|
|
1381
1451
|
};
|
|
1382
1452
|
|
|
1383
1453
|
// src/routes/auth/helper/verification.ts
|
|
1384
1454
|
import { dayjs as dayjs2 } from "@mesob/common";
|
|
1385
|
-
import { and as
|
|
1455
|
+
import { and as and8, desc, eq as eq8 } from "drizzle-orm";
|
|
1386
1456
|
var createVerification = async ({
|
|
1387
1457
|
tx,
|
|
1388
1458
|
tenantId,
|
|
@@ -1448,10 +1518,10 @@ var checkVerificationResend = async ({
|
|
|
1448
1518
|
id: verificationsInIam.id,
|
|
1449
1519
|
createdAt: verificationsInIam.createdAt
|
|
1450
1520
|
}).from(verificationsInIam).where(
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1521
|
+
and8(
|
|
1522
|
+
eq8(verificationsInIam.tenantId, tenantId),
|
|
1523
|
+
eq8(verificationsInIam.userId, userId),
|
|
1524
|
+
eq8(verificationsInIam.type, type)
|
|
1455
1525
|
)
|
|
1456
1526
|
).orderBy(desc(verificationsInIam.createdAt)).limit(1);
|
|
1457
1527
|
if (!verification) {
|
|
@@ -1473,10 +1543,10 @@ var handleEmailVerification = async ({
|
|
|
1473
1543
|
return c.json({ error: "User email not found" }, 401);
|
|
1474
1544
|
}
|
|
1475
1545
|
await database.delete(verificationsInIam).where(
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1546
|
+
and8(
|
|
1547
|
+
eq8(verificationsInIam.tenantId, tenantId),
|
|
1548
|
+
eq8(verificationsInIam.userId, user.id),
|
|
1549
|
+
eq8(verificationsInIam.type, "email-verification")
|
|
1480
1550
|
)
|
|
1481
1551
|
);
|
|
1482
1552
|
const code = generateOtpCode(config.email.otpLength);
|
|
@@ -1511,7 +1581,7 @@ var handleEmailVerification = async ({
|
|
|
1511
1581
|
}
|
|
1512
1582
|
return c.json(
|
|
1513
1583
|
{
|
|
1514
|
-
user:
|
|
1584
|
+
user: normalizeAuthUser(user),
|
|
1515
1585
|
session: null,
|
|
1516
1586
|
verificationId: verification.id,
|
|
1517
1587
|
requiresVerification: true
|
|
@@ -1530,10 +1600,10 @@ var handlePhoneVerification = async ({
|
|
|
1530
1600
|
return c.json({ error: "User phone not found" }, 401);
|
|
1531
1601
|
}
|
|
1532
1602
|
await database.delete(verificationsInIam).where(
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1603
|
+
and8(
|
|
1604
|
+
eq8(verificationsInIam.tenantId, tenantId),
|
|
1605
|
+
eq8(verificationsInIam.userId, user.id),
|
|
1606
|
+
eq8(verificationsInIam.type, "phone-otp")
|
|
1537
1607
|
)
|
|
1538
1608
|
);
|
|
1539
1609
|
const code = generateOtpCode(config.phone.otpLength);
|
|
@@ -1568,7 +1638,7 @@ var handlePhoneVerification = async ({
|
|
|
1568
1638
|
}
|
|
1569
1639
|
return c.json(
|
|
1570
1640
|
{
|
|
1571
|
-
user:
|
|
1641
|
+
user: normalizeAuthUser(user),
|
|
1572
1642
|
session: null,
|
|
1573
1643
|
verificationId: verification.id,
|
|
1574
1644
|
requiresVerification: true
|
|
@@ -1578,177 +1648,178 @@ var handlePhoneVerification = async ({
|
|
|
1578
1648
|
};
|
|
1579
1649
|
|
|
1580
1650
|
// src/routes/auth/handler/sign-in.ts
|
|
1581
|
-
var signInHandler =
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
isEmail,
|
|
1600
|
-
userType: config.userType
|
|
1601
|
-
});
|
|
1602
|
-
if (!user) {
|
|
1603
|
-
logger2.log("[sign-in] 401: user not found", {
|
|
1651
|
+
var signInHandler = (
|
|
1652
|
+
// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: auth flow combines lockout, verification, and session creation
|
|
1653
|
+
async function signInHandler2(c) {
|
|
1654
|
+
const body = c.req.valid("json");
|
|
1655
|
+
const config = c.get("config");
|
|
1656
|
+
const database = c.get("database");
|
|
1657
|
+
const tenantId = c.get("tenantId");
|
|
1658
|
+
const resolvedTenantId = ensureTenantId(config, tenantId);
|
|
1659
|
+
const { identifier, password, rememberMe = true } = body;
|
|
1660
|
+
const isEmail = identifier.includes("@");
|
|
1661
|
+
if (isEmail && !config.email.enabled) {
|
|
1662
|
+
return c.json({ error: "Email authentication is disabled" }, 401);
|
|
1663
|
+
}
|
|
1664
|
+
if (!(isEmail || config.phone.enabled)) {
|
|
1665
|
+
return c.json({ error: "Phone authentication is disabled" }, 401);
|
|
1666
|
+
}
|
|
1667
|
+
const user = await fetchUserForLogin({
|
|
1668
|
+
database,
|
|
1604
1669
|
identifier,
|
|
1605
1670
|
tenantId: resolvedTenantId,
|
|
1671
|
+
isEmail,
|
|
1606
1672
|
userType: config.userType
|
|
1607
1673
|
});
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1674
|
+
if (!user) {
|
|
1675
|
+
logger2.log("[sign-in] 401: user not found", {
|
|
1676
|
+
identifier,
|
|
1677
|
+
tenantId: resolvedTenantId,
|
|
1678
|
+
userType: config.userType
|
|
1679
|
+
});
|
|
1680
|
+
return c.json({ error: AUTH_ERRORS.UNAUTHORIZED }, 401);
|
|
1681
|
+
}
|
|
1682
|
+
if (user.bannedUntil && new Date(user.bannedUntil) > /* @__PURE__ */ new Date()) {
|
|
1683
|
+
logger2.log("[sign-in] 401: account banned", {
|
|
1684
|
+
userId: user.id,
|
|
1618
1685
|
bannedUntil: user.bannedUntil
|
|
1619
|
-
}
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
)
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
if (
|
|
1648
|
-
|
|
1686
|
+
});
|
|
1687
|
+
return c.json(
|
|
1688
|
+
{
|
|
1689
|
+
error: "Account locked. Try again later.",
|
|
1690
|
+
bannedUntil: user.bannedUntil
|
|
1691
|
+
},
|
|
1692
|
+
401
|
|
1693
|
+
);
|
|
1694
|
+
}
|
|
1695
|
+
const [account] = await database.select({
|
|
1696
|
+
id: accountsInIam.id,
|
|
1697
|
+
tenantId: accountsInIam.tenantId,
|
|
1698
|
+
userId: accountsInIam.userId,
|
|
1699
|
+
provider: accountsInIam.provider,
|
|
1700
|
+
providerAccountId: accountsInIam.providerAccountId,
|
|
1701
|
+
password: accountsInIam.password
|
|
1702
|
+
}).from(accountsInIam).where(
|
|
1703
|
+
and9(
|
|
1704
|
+
eq9(accountsInIam.tenantId, resolvedTenantId),
|
|
1705
|
+
eq9(accountsInIam.userId, user.id),
|
|
1706
|
+
eq9(accountsInIam.provider, "credentials")
|
|
1707
|
+
)
|
|
1708
|
+
).limit(1);
|
|
1709
|
+
if (!account?.password) {
|
|
1710
|
+
logger2.log("[sign-in] 401: no credentials account", { userId: user.id });
|
|
1711
|
+
return c.json({ error: AUTH_ERRORS.HAS_NO_PASSWORD }, 401);
|
|
1712
|
+
}
|
|
1713
|
+
const passwordValid = await verifyPassword(password, account.password);
|
|
1714
|
+
if (!passwordValid) {
|
|
1715
|
+
const newAttempt = (user.loginAttempt || 0) + 1;
|
|
1716
|
+
const updateData = {
|
|
1717
|
+
loginAttempt: newAttempt
|
|
1718
|
+
};
|
|
1719
|
+
if (config.security && newAttempt >= config.security.maxLoginAttempts) {
|
|
1720
|
+
updateData.bannedUntil = addDuration(config.security.lockoutDuration);
|
|
1721
|
+
}
|
|
1722
|
+
await database.update(usersInIam).set(updateData).where(
|
|
1723
|
+
and9(
|
|
1724
|
+
eq9(usersInIam.id, user.id),
|
|
1725
|
+
eq9(usersInIam.tenantId, resolvedTenantId)
|
|
1726
|
+
)
|
|
1727
|
+
);
|
|
1728
|
+
logger2.log("[sign-in] 401: invalid password", {
|
|
1729
|
+
userId: user.id,
|
|
1730
|
+
loginAttempt: newAttempt
|
|
1731
|
+
});
|
|
1732
|
+
return c.json({ error: AUTH_ERRORS.UNAUTHORIZED }, 401);
|
|
1733
|
+
}
|
|
1734
|
+
const isVerified = isEmail ? user.emailVerified : user.phoneVerified;
|
|
1735
|
+
if (isEmail && config.email.required && !isVerified) {
|
|
1736
|
+
return handleEmailVerification({
|
|
1737
|
+
c,
|
|
1738
|
+
user: { ...user, roles: [] },
|
|
1739
|
+
config,
|
|
1740
|
+
database,
|
|
1741
|
+
tenantId: resolvedTenantId
|
|
1742
|
+
});
|
|
1743
|
+
}
|
|
1744
|
+
if (!isEmail && config.phone.required && !isVerified) {
|
|
1745
|
+
return handlePhoneVerification({
|
|
1746
|
+
c,
|
|
1747
|
+
user: { ...user, roles: [] },
|
|
1748
|
+
config,
|
|
1749
|
+
database,
|
|
1750
|
+
tenantId: resolvedTenantId
|
|
1751
|
+
});
|
|
1752
|
+
}
|
|
1753
|
+
if (config.session.maxPerUser) {
|
|
1754
|
+
await cleanupOldSessions({
|
|
1755
|
+
database,
|
|
1756
|
+
userId: user.id,
|
|
1757
|
+
tenantId: resolvedTenantId,
|
|
1758
|
+
maxSessions: config.session.maxPerUser
|
|
1759
|
+
});
|
|
1649
1760
|
}
|
|
1650
|
-
await database.update(usersInIam).set(
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1761
|
+
await database.update(usersInIam).set({ lastSignInAt: (/* @__PURE__ */ new Date()).toISOString(), loginAttempt: 0 }).where(
|
|
1762
|
+
and9(
|
|
1763
|
+
eq9(usersInIam.id, user.id),
|
|
1764
|
+
eq9(usersInIam.tenantId, resolvedTenantId)
|
|
1654
1765
|
)
|
|
1655
1766
|
);
|
|
1656
|
-
|
|
1767
|
+
const sessionToken = generateToken();
|
|
1768
|
+
const hashedToken = await hashToken(sessionToken, config.secret);
|
|
1769
|
+
const sessionDuration = getSessionDuration({
|
|
1770
|
+
sessionConfig: config.session,
|
|
1771
|
+
rememberMe
|
|
1772
|
+
});
|
|
1773
|
+
const expiresAt = addDuration(sessionDuration);
|
|
1774
|
+
const [session] = await database.insert(sessionsInIam).values({
|
|
1775
|
+
tenantId: resolvedTenantId,
|
|
1657
1776
|
userId: user.id,
|
|
1658
|
-
|
|
1777
|
+
token: hashedToken,
|
|
1778
|
+
expiresAt,
|
|
1779
|
+
userAgent: c.req.header("user-agent") || null,
|
|
1780
|
+
ip: c.req.header("cf-connecting-ip") || c.req.header("x-forwarded-for") || null,
|
|
1781
|
+
meta: { action: "sign-in", rememberMe }
|
|
1782
|
+
}).returning({
|
|
1783
|
+
id: sessionsInIam.id,
|
|
1784
|
+
tenantId: sessionsInIam.tenantId,
|
|
1785
|
+
userId: sessionsInIam.userId,
|
|
1786
|
+
expiresAt: sessionsInIam.expiresAt,
|
|
1787
|
+
createdAt: sessionsInIam.createdAt,
|
|
1788
|
+
meta: sessionsInIam.meta
|
|
1659
1789
|
});
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
const isVerified = isEmail ? user.emailVerified : user.phoneVerified;
|
|
1663
|
-
if (isEmail && config.email.required && !isVerified) {
|
|
1664
|
-
return handleEmailVerification({
|
|
1665
|
-
c,
|
|
1666
|
-
user: { ...user, roles: [] },
|
|
1667
|
-
config,
|
|
1668
|
-
database,
|
|
1669
|
-
tenantId: resolvedTenantId
|
|
1790
|
+
setSessionCookie(c, sessionToken, config, {
|
|
1791
|
+
expires: new Date(expiresAt)
|
|
1670
1792
|
});
|
|
1671
|
-
|
|
1672
|
-
if (!isEmail && config.phone.required && !isVerified) {
|
|
1673
|
-
return handlePhoneVerification({
|
|
1674
|
-
c,
|
|
1675
|
-
user: { ...user, roles: [] },
|
|
1676
|
-
config,
|
|
1793
|
+
const fullUser = await fetchUserByIdWithRoles({
|
|
1677
1794
|
database,
|
|
1795
|
+
userId: user.id,
|
|
1678
1796
|
tenantId: resolvedTenantId
|
|
1679
1797
|
});
|
|
1798
|
+
return c.json(
|
|
1799
|
+
{
|
|
1800
|
+
user: normalizeAuthUser(fullUser ?? { ...user, roles: [] }),
|
|
1801
|
+
session: normalizeAuthSession({
|
|
1802
|
+
id: session.id,
|
|
1803
|
+
expiresAt: session.expiresAt
|
|
1804
|
+
}),
|
|
1805
|
+
sessionExpiresAt: session.expiresAt
|
|
1806
|
+
},
|
|
1807
|
+
200
|
|
1808
|
+
);
|
|
1680
1809
|
}
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
)
|
|
1694
|
-
);
|
|
1695
|
-
const sessionToken = generateToken();
|
|
1696
|
-
const hashedToken = await hashToken(sessionToken, config.secret);
|
|
1697
|
-
const sessionDuration = getSessionDuration({
|
|
1698
|
-
sessionConfig: config.session,
|
|
1699
|
-
rememberMe
|
|
1700
|
-
});
|
|
1701
|
-
const expiresAt = addDuration(sessionDuration);
|
|
1702
|
-
const [session] = await database.insert(sessionsInIam).values({
|
|
1703
|
-
tenantId: resolvedTenantId,
|
|
1704
|
-
userId: user.id,
|
|
1705
|
-
token: hashedToken,
|
|
1706
|
-
expiresAt,
|
|
1707
|
-
userAgent: c.req.header("user-agent") || null,
|
|
1708
|
-
ip: c.req.header("cf-connecting-ip") || c.req.header("x-forwarded-for") || null,
|
|
1709
|
-
meta: { action: "sign-in", rememberMe }
|
|
1710
|
-
}).returning({
|
|
1711
|
-
id: sessionsInIam.id,
|
|
1712
|
-
tenantId: sessionsInIam.tenantId,
|
|
1713
|
-
userId: sessionsInIam.userId,
|
|
1714
|
-
expiresAt: sessionsInIam.expiresAt,
|
|
1715
|
-
createdAt: sessionsInIam.createdAt,
|
|
1716
|
-
meta: sessionsInIam.meta
|
|
1717
|
-
});
|
|
1718
|
-
setSessionCookie(c, sessionToken, config, {
|
|
1719
|
-
expires: new Date(expiresAt)
|
|
1720
|
-
});
|
|
1721
|
-
const fullUser = await fetchUserByIdWithRoles({
|
|
1722
|
-
database,
|
|
1723
|
-
userId: user.id,
|
|
1724
|
-
tenantId: resolvedTenantId
|
|
1725
|
-
});
|
|
1726
|
-
return c.json(
|
|
1727
|
-
{
|
|
1728
|
-
user: normalizeUser(fullUser ?? { ...user, roles: [] }),
|
|
1729
|
-
session: {
|
|
1730
|
-
id: session.id,
|
|
1731
|
-
expiresAt: session.expiresAt,
|
|
1732
|
-
createdAt: session.createdAt,
|
|
1733
|
-
meta: session.meta
|
|
1734
|
-
},
|
|
1735
|
-
sessionExpiresAt: session.expiresAt
|
|
1736
|
-
},
|
|
1737
|
-
200
|
|
1738
|
-
);
|
|
1739
|
-
};
|
|
1740
|
-
|
|
1741
|
-
// src/routes/auth/handler/sign-out.ts
|
|
1742
|
-
import { and as and9, eq as eq9, gt as gt4 } from "drizzle-orm";
|
|
1743
|
-
import { getCookie } from "hono/cookie";
|
|
1744
|
-
var signOutHandler = async (c) => {
|
|
1745
|
-
const config = c.get("config");
|
|
1746
|
-
const database = c.get("database");
|
|
1747
|
-
const tenantId = c.get("tenantId");
|
|
1748
|
-
ensureTenantId(config, tenantId);
|
|
1749
|
-
const sessionToken = getCookie(c, getSessionCookieName(config));
|
|
1750
|
-
if (!sessionToken) {
|
|
1751
|
-
return c.json({ message: "Signed out" }, 200);
|
|
1810
|
+
);
|
|
1811
|
+
|
|
1812
|
+
// src/routes/auth/handler/sign-out.ts
|
|
1813
|
+
import { and as and10, eq as eq10, gt as gt4 } from "drizzle-orm";
|
|
1814
|
+
import { getCookie } from "hono/cookie";
|
|
1815
|
+
var signOutHandler = async (c) => {
|
|
1816
|
+
const config = c.get("config");
|
|
1817
|
+
const database = c.get("database");
|
|
1818
|
+
const tenantId = c.get("tenantId");
|
|
1819
|
+
ensureTenantId(config, tenantId);
|
|
1820
|
+
const sessionToken = getCookie(c, getSessionCookieName(config));
|
|
1821
|
+
if (!sessionToken) {
|
|
1822
|
+
return c.json({ message: "Signed out" }, 200);
|
|
1752
1823
|
}
|
|
1753
1824
|
const hashedToken = await hashToken(sessionToken, config.secret);
|
|
1754
1825
|
const [session] = await database.select({
|
|
@@ -1761,16 +1832,16 @@ var signOutHandler = async (c) => {
|
|
|
1761
1832
|
userAgent: sessionsInIam.userAgent,
|
|
1762
1833
|
ip: sessionsInIam.ip
|
|
1763
1834
|
}).from(sessionsInIam).where(
|
|
1764
|
-
|
|
1765
|
-
|
|
1835
|
+
and10(
|
|
1836
|
+
eq10(sessionsInIam.token, hashedToken),
|
|
1766
1837
|
gt4(sessionsInIam.expiresAt, (/* @__PURE__ */ new Date()).toISOString())
|
|
1767
1838
|
)
|
|
1768
1839
|
).limit(1);
|
|
1769
1840
|
if (session) {
|
|
1770
1841
|
await database.delete(sessionsInIam).where(
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1842
|
+
and10(
|
|
1843
|
+
eq10(sessionsInIam.id, session.id),
|
|
1844
|
+
eq10(sessionsInIam.tenantId, session.tenantId)
|
|
1774
1845
|
)
|
|
1775
1846
|
);
|
|
1776
1847
|
}
|
|
@@ -1922,7 +1993,7 @@ var signUpHandler = async (c) => {
|
|
|
1922
1993
|
if (result.type === "pending") {
|
|
1923
1994
|
return c.json(
|
|
1924
1995
|
{
|
|
1925
|
-
user:
|
|
1996
|
+
user: normalizeAuthUser(result.user),
|
|
1926
1997
|
session: null,
|
|
1927
1998
|
verificationId: result.verificationId,
|
|
1928
1999
|
requiresVerification: true
|
|
@@ -1941,7 +2012,7 @@ var signUpHandler = async (c) => {
|
|
|
1941
2012
|
});
|
|
1942
2013
|
return c.json(
|
|
1943
2014
|
{
|
|
1944
|
-
user:
|
|
2015
|
+
user: normalizeAuthUser(result.user),
|
|
1945
2016
|
session: null,
|
|
1946
2017
|
verificationId: result.verificationId,
|
|
1947
2018
|
requiresVerification: true
|
|
@@ -1954,8 +2025,11 @@ var signUpHandler = async (c) => {
|
|
|
1954
2025
|
});
|
|
1955
2026
|
return c.json(
|
|
1956
2027
|
{
|
|
1957
|
-
user:
|
|
1958
|
-
session: {
|
|
2028
|
+
user: normalizeAuthUser(result.user),
|
|
2029
|
+
session: normalizeAuthSession({
|
|
2030
|
+
id: result.sessionId,
|
|
2031
|
+
expiresAt: result.expiresAt
|
|
2032
|
+
}),
|
|
1959
2033
|
sessionExpiresAt: result.expiresAt
|
|
1960
2034
|
},
|
|
1961
2035
|
201
|
|
@@ -2158,26 +2232,26 @@ var createDomainHandler = async (c) => {
|
|
|
2158
2232
|
};
|
|
2159
2233
|
|
|
2160
2234
|
// src/routes/domains/handler/delete-domain.ts
|
|
2161
|
-
import { and as
|
|
2235
|
+
import { and as and11, eq as eq11 } from "drizzle-orm";
|
|
2162
2236
|
var deleteDomainHandler = async (c) => {
|
|
2163
2237
|
const { id } = c.req.valid("param");
|
|
2164
2238
|
const database = c.get("database");
|
|
2165
2239
|
const tenantId = c.get("tenantId");
|
|
2166
|
-
const [existing] = await database.select().from(domainsInIam).where(
|
|
2240
|
+
const [existing] = await database.select().from(domainsInIam).where(and11(eq11(domainsInIam.id, id), eq11(domainsInIam.tenantId, tenantId))).limit(1);
|
|
2167
2241
|
if (!existing) {
|
|
2168
2242
|
return c.json({ error: "Domain not found" }, 404);
|
|
2169
2243
|
}
|
|
2170
|
-
await database.delete(domainsInIam).where(
|
|
2244
|
+
await database.delete(domainsInIam).where(and11(eq11(domainsInIam.id, id), eq11(domainsInIam.tenantId, tenantId)));
|
|
2171
2245
|
return c.json({ message: "Domain deleted" }, 200);
|
|
2172
2246
|
};
|
|
2173
2247
|
|
|
2174
2248
|
// src/routes/domains/handler/get-domain.ts
|
|
2175
|
-
import { and as
|
|
2249
|
+
import { and as and12, eq as eq12 } from "drizzle-orm";
|
|
2176
2250
|
var getDomainHandler = async (c) => {
|
|
2177
2251
|
const { id } = c.req.valid("param");
|
|
2178
2252
|
const database = c.get("database");
|
|
2179
2253
|
const tenantId = c.get("tenantId");
|
|
2180
|
-
const [domain] = await database.select().from(domainsInIam).where(
|
|
2254
|
+
const [domain] = await database.select().from(domainsInIam).where(and12(eq12(domainsInIam.id, id), eq12(domainsInIam.tenantId, tenantId))).limit(1);
|
|
2181
2255
|
if (!domain) {
|
|
2182
2256
|
return c.json({ error: "Domain not found" }, 404);
|
|
2183
2257
|
}
|
|
@@ -2185,7 +2259,7 @@ var getDomainHandler = async (c) => {
|
|
|
2185
2259
|
};
|
|
2186
2260
|
|
|
2187
2261
|
// src/routes/domains/handler/list-domains.ts
|
|
2188
|
-
import { and as
|
|
2262
|
+
import { and as and13, eq as eq13, sql as sql7 } from "drizzle-orm";
|
|
2189
2263
|
var listDomainsHandler = async (c) => {
|
|
2190
2264
|
const query = c.req.valid("query");
|
|
2191
2265
|
const database = c.get("database");
|
|
@@ -2193,26 +2267,26 @@ var listDomainsHandler = async (c) => {
|
|
|
2193
2267
|
const page = query.page || 1;
|
|
2194
2268
|
const limit = query.limit || 20;
|
|
2195
2269
|
const offset = (page - 1) * limit;
|
|
2196
|
-
const conditions = [
|
|
2270
|
+
const conditions = [eq13(domainsInIam.tenantId, tenantId)];
|
|
2197
2271
|
if (query.status) {
|
|
2198
|
-
conditions.push(
|
|
2272
|
+
conditions.push(eq13(domainsInIam.status, query.status));
|
|
2199
2273
|
}
|
|
2200
2274
|
const [domains, totalResult] = await Promise.all([
|
|
2201
|
-
database.select().from(domainsInIam).where(
|
|
2202
|
-
database.select({ count: sql7`count(*)` }).from(domainsInIam).where(
|
|
2275
|
+
database.select().from(domainsInIam).where(and13(...conditions)).limit(limit).offset(offset),
|
|
2276
|
+
database.select({ count: sql7`count(*)` }).from(domainsInIam).where(and13(...conditions))
|
|
2203
2277
|
]);
|
|
2204
2278
|
const total = Number(totalResult[0]?.count || 0);
|
|
2205
2279
|
return c.json({ domains, total, page, limit });
|
|
2206
2280
|
};
|
|
2207
2281
|
|
|
2208
2282
|
// src/routes/domains/handler/update-domain.ts
|
|
2209
|
-
import { and as
|
|
2283
|
+
import { and as and14, eq as eq14, sql as sql8 } from "drizzle-orm";
|
|
2210
2284
|
var updateDomainHandler = async (c) => {
|
|
2211
2285
|
const { id } = c.req.valid("param");
|
|
2212
2286
|
const body = c.req.valid("json");
|
|
2213
2287
|
const database = c.get("database");
|
|
2214
2288
|
const tenantId = c.get("tenantId");
|
|
2215
|
-
const [existing] = await database.select().from(domainsInIam).where(
|
|
2289
|
+
const [existing] = await database.select().from(domainsInIam).where(and14(eq14(domainsInIam.id, id), eq14(domainsInIam.tenantId, tenantId))).limit(1);
|
|
2216
2290
|
if (!existing) {
|
|
2217
2291
|
return c.json({ error: "Domain not found" }, 404);
|
|
2218
2292
|
}
|
|
@@ -2232,7 +2306,7 @@ var updateDomainHandler = async (c) => {
|
|
|
2232
2306
|
const [updated] = await database.update(domainsInIam).set({
|
|
2233
2307
|
...updateData,
|
|
2234
2308
|
updatedAt: sql8`CURRENT_TIMESTAMP`
|
|
2235
|
-
}).where(
|
|
2309
|
+
}).where(and14(eq14(domainsInIam.id, id), eq14(domainsInIam.tenantId, tenantId))).returning();
|
|
2236
2310
|
if (!updated) {
|
|
2237
2311
|
return c.json({ error: "Domain not found" }, 404);
|
|
2238
2312
|
}
|
|
@@ -2240,19 +2314,19 @@ var updateDomainHandler = async (c) => {
|
|
|
2240
2314
|
};
|
|
2241
2315
|
|
|
2242
2316
|
// src/routes/domains/handler/verify-domain.ts
|
|
2243
|
-
import { and as
|
|
2317
|
+
import { and as and15, eq as eq15, sql as sql9 } from "drizzle-orm";
|
|
2244
2318
|
var verifyDomainHandler = async (c) => {
|
|
2245
2319
|
const { id } = c.req.valid("param");
|
|
2246
2320
|
const database = c.get("database");
|
|
2247
2321
|
const tenantId = c.get("tenantId");
|
|
2248
|
-
const [existing] = await database.select().from(domainsInIam).where(
|
|
2322
|
+
const [existing] = await database.select().from(domainsInIam).where(and15(eq15(domainsInIam.id, id), eq15(domainsInIam.tenantId, tenantId))).limit(1);
|
|
2249
2323
|
if (!existing) {
|
|
2250
2324
|
return c.json({ error: "Domain not found" }, 404);
|
|
2251
2325
|
}
|
|
2252
2326
|
const [updated] = await database.update(domainsInIam).set({
|
|
2253
2327
|
status: "active",
|
|
2254
2328
|
updatedAt: sql9`CURRENT_TIMESTAMP`
|
|
2255
|
-
}).where(
|
|
2329
|
+
}).where(and15(eq15(domainsInIam.id, id), eq15(domainsInIam.tenantId, tenantId))).returning();
|
|
2256
2330
|
if (!updated) {
|
|
2257
2331
|
return c.json({ error: "Domain not found" }, 404);
|
|
2258
2332
|
}
|
|
@@ -2426,7 +2500,7 @@ var domains_route_default = domainRoutes;
|
|
|
2426
2500
|
import { createRoute as createRoute3, OpenAPIHono as OpenAPIHono3 } from "@hono/zod-openapi";
|
|
2427
2501
|
|
|
2428
2502
|
// src/routes/email/handler/verification-confirm.ts
|
|
2429
|
-
import { and as
|
|
2503
|
+
import { and as and16, eq as eq16 } from "drizzle-orm";
|
|
2430
2504
|
var emailVerificationConfirmHandler = async (c) => {
|
|
2431
2505
|
const body = c.req.valid("json");
|
|
2432
2506
|
const config = c.get("config");
|
|
@@ -2436,9 +2510,9 @@ var emailVerificationConfirmHandler = async (c) => {
|
|
|
2436
2510
|
const { verificationId, code } = body;
|
|
2437
2511
|
const result = await withTransaction(database, async (tx) => {
|
|
2438
2512
|
const [verification] = await tx.select().from(verificationsInIam).where(
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2513
|
+
and16(
|
|
2514
|
+
eq16(verificationsInIam.id, verificationId),
|
|
2515
|
+
eq16(verificationsInIam.tenantId, resolvedTenantId)
|
|
2442
2516
|
)
|
|
2443
2517
|
).limit(1);
|
|
2444
2518
|
if (!verification || verification.type !== "email-verification") {
|
|
@@ -2454,7 +2528,7 @@ var emailVerificationConfirmHandler = async (c) => {
|
|
|
2454
2528
|
};
|
|
2455
2529
|
}
|
|
2456
2530
|
if ((verification.attempt || 0) >= config.email.maxAttempts) {
|
|
2457
|
-
await tx.delete(verificationsInIam).where(
|
|
2531
|
+
await tx.delete(verificationsInIam).where(eq16(verificationsInIam.id, verificationId));
|
|
2458
2532
|
return {
|
|
2459
2533
|
status: "error",
|
|
2460
2534
|
error: AUTH_ERRORS.TOO_MANY_ATTEMPTS
|
|
@@ -2462,7 +2536,7 @@ var emailVerificationConfirmHandler = async (c) => {
|
|
|
2462
2536
|
}
|
|
2463
2537
|
const hashedCode = await hashToken(code, config.secret);
|
|
2464
2538
|
if (verification.code !== hashedCode) {
|
|
2465
|
-
await tx.update(verificationsInIam).set({ attempt: (verification.attempt || 0) + 1 }).where(
|
|
2539
|
+
await tx.update(verificationsInIam).set({ attempt: (verification.attempt || 0) + 1 }).where(eq16(verificationsInIam.id, verificationId));
|
|
2466
2540
|
return {
|
|
2467
2541
|
status: "error",
|
|
2468
2542
|
error: AUTH_ERRORS.VERIFICATION_MISMATCH
|
|
@@ -2472,12 +2546,12 @@ var emailVerificationConfirmHandler = async (c) => {
|
|
|
2472
2546
|
emailVerified: true,
|
|
2473
2547
|
lastSignInAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2474
2548
|
}).where(
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2549
|
+
and16(
|
|
2550
|
+
eq16(usersInIam.id, verification.userId),
|
|
2551
|
+
eq16(usersInIam.tenantId, resolvedTenantId)
|
|
2478
2552
|
)
|
|
2479
2553
|
);
|
|
2480
|
-
await tx.delete(verificationsInIam).where(
|
|
2554
|
+
await tx.delete(verificationsInIam).where(eq16(verificationsInIam.id, verificationId));
|
|
2481
2555
|
const user = await fetchUserWithRoles({
|
|
2482
2556
|
database: tx,
|
|
2483
2557
|
userId: verification.userId,
|
|
@@ -2512,14 +2586,8 @@ var emailVerificationConfirmHandler = async (c) => {
|
|
|
2512
2586
|
});
|
|
2513
2587
|
return c.json(
|
|
2514
2588
|
{
|
|
2515
|
-
user:
|
|
2516
|
-
session:
|
|
2517
|
-
id: result.session.id,
|
|
2518
|
-
expiresAt: result.session.expiresAt,
|
|
2519
|
-
createdAt: result.session.createdAt,
|
|
2520
|
-
userAgent: result.session.userAgent,
|
|
2521
|
-
ip: result.session.ip
|
|
2522
|
-
},
|
|
2589
|
+
user: normalizeAuthUser(result.user),
|
|
2590
|
+
session: normalizeAuthSession(result.session),
|
|
2523
2591
|
sessionExpiresAt: result.session.expiresAt
|
|
2524
2592
|
},
|
|
2525
2593
|
200
|
|
@@ -2527,7 +2595,7 @@ var emailVerificationConfirmHandler = async (c) => {
|
|
|
2527
2595
|
};
|
|
2528
2596
|
|
|
2529
2597
|
// src/routes/email/handler/verification-request.ts
|
|
2530
|
-
import { and as
|
|
2598
|
+
import { and as and17, eq as eq17, sql as sql10 } from "drizzle-orm";
|
|
2531
2599
|
var emailVerificationRequestHandler = async (c) => {
|
|
2532
2600
|
const body = c.req.valid("json");
|
|
2533
2601
|
const config = c.get("config");
|
|
@@ -2548,8 +2616,8 @@ var emailVerificationRequestHandler = async (c) => {
|
|
|
2548
2616
|
let userId = user?.id;
|
|
2549
2617
|
if (!userId) {
|
|
2550
2618
|
const [result] = await database.select({ id: usersInIam.id }).from(usersInIam).where(
|
|
2551
|
-
|
|
2552
|
-
|
|
2619
|
+
and17(
|
|
2620
|
+
eq17(usersInIam.tenantId, resolvedTenantId),
|
|
2553
2621
|
sql10`lower(${usersInIam.email}) = lower(${email})`
|
|
2554
2622
|
)
|
|
2555
2623
|
).limit(1);
|
|
@@ -2575,10 +2643,10 @@ var emailVerificationRequestHandler = async (c) => {
|
|
|
2575
2643
|
);
|
|
2576
2644
|
}
|
|
2577
2645
|
await database.delete(verificationsInIam).where(
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2646
|
+
and17(
|
|
2647
|
+
eq17(verificationsInIam.tenantId, resolvedTenantId),
|
|
2648
|
+
eq17(verificationsInIam.userId, userId),
|
|
2649
|
+
eq17(verificationsInIam.type, "email-verification")
|
|
2582
2650
|
)
|
|
2583
2651
|
);
|
|
2584
2652
|
const code = generateOtpCode(config.email.otpLength);
|
|
@@ -2610,11 +2678,11 @@ var emailVerificationRequestHandler = async (c) => {
|
|
|
2610
2678
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2611
2679
|
reason: "replaced"
|
|
2612
2680
|
}).where(
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2681
|
+
and17(
|
|
2682
|
+
eq17(accountChangesInIam.tenantId, resolvedTenantId),
|
|
2683
|
+
eq17(accountChangesInIam.userId, user.id),
|
|
2684
|
+
eq17(accountChangesInIam.changeType, "email"),
|
|
2685
|
+
eq17(accountChangesInIam.status, "pending")
|
|
2618
2686
|
)
|
|
2619
2687
|
);
|
|
2620
2688
|
await database.insert(accountChangesInIam).values({
|
|
@@ -2729,7 +2797,7 @@ var email_route_default = emailRoutes;
|
|
|
2729
2797
|
import { createRoute as createRoute4, OpenAPIHono as OpenAPIHono4 } from "@hono/zod-openapi";
|
|
2730
2798
|
|
|
2731
2799
|
// src/routes/password/handler/change.ts
|
|
2732
|
-
import { and as
|
|
2800
|
+
import { and as and18, eq as eq18, gt as gt5, ne } from "drizzle-orm";
|
|
2733
2801
|
import { getCookie as getCookie2 } from "hono/cookie";
|
|
2734
2802
|
var changePasswordHandler = async (c) => {
|
|
2735
2803
|
const body = c.req.valid("json");
|
|
@@ -2747,10 +2815,10 @@ var changePasswordHandler = async (c) => {
|
|
|
2747
2815
|
const resolvedTenantId = ensureTenantId(config, tenantId);
|
|
2748
2816
|
const { currentPassword, newPassword } = body;
|
|
2749
2817
|
const [account] = await database.select().from(accountsInIam).where(
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2818
|
+
and18(
|
|
2819
|
+
eq18(accountsInIam.tenantId, resolvedTenantId),
|
|
2820
|
+
eq18(accountsInIam.userId, userId),
|
|
2821
|
+
eq18(accountsInIam.provider, "credentials")
|
|
2754
2822
|
)
|
|
2755
2823
|
).limit(1);
|
|
2756
2824
|
if (!account?.password) {
|
|
@@ -2762,19 +2830,19 @@ var changePasswordHandler = async (c) => {
|
|
|
2762
2830
|
}
|
|
2763
2831
|
const passwordHash = await hashPassword(newPassword);
|
|
2764
2832
|
await database.update(accountsInIam).set({ password: passwordHash }).where(
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2833
|
+
and18(
|
|
2834
|
+
eq18(accountsInIam.tenantId, resolvedTenantId),
|
|
2835
|
+
eq18(accountsInIam.userId, userId),
|
|
2836
|
+
eq18(accountsInIam.provider, "credentials")
|
|
2769
2837
|
)
|
|
2770
2838
|
);
|
|
2771
2839
|
const currentSessionToken = getCookie2(c, getSessionCookieName(config));
|
|
2772
2840
|
if (currentSessionToken) {
|
|
2773
2841
|
const hashedToken = await hashToken(currentSessionToken, config.secret);
|
|
2774
2842
|
await database.delete(sessionsInIam).where(
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2843
|
+
and18(
|
|
2844
|
+
eq18(sessionsInIam.tenantId, resolvedTenantId),
|
|
2845
|
+
eq18(sessionsInIam.userId, userId),
|
|
2778
2846
|
gt5(sessionsInIam.expiresAt, (/* @__PURE__ */ new Date()).toISOString()),
|
|
2779
2847
|
ne(sessionsInIam.token, hashedToken)
|
|
2780
2848
|
)
|
|
@@ -2784,7 +2852,7 @@ var changePasswordHandler = async (c) => {
|
|
|
2784
2852
|
};
|
|
2785
2853
|
|
|
2786
2854
|
// src/routes/password/handler/forgot.ts
|
|
2787
|
-
import { and as
|
|
2855
|
+
import { and as and19, eq as eq19, sql as sql11 } from "drizzle-orm";
|
|
2788
2856
|
var forgotPasswordHandler = async (c) => {
|
|
2789
2857
|
const body = c.req.valid("json");
|
|
2790
2858
|
const config = c.get("config");
|
|
@@ -2818,19 +2886,19 @@ var forgotPasswordHandler = async (c) => {
|
|
|
2818
2886
|
)`
|
|
2819
2887
|
}).from(usersInIam).leftJoin(
|
|
2820
2888
|
userRolesInIam,
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2889
|
+
and19(
|
|
2890
|
+
eq19(userRolesInIam.userId, usersInIam.id),
|
|
2891
|
+
eq19(userRolesInIam.tenantId, resolvedTenantId)
|
|
2824
2892
|
)
|
|
2825
2893
|
).leftJoin(
|
|
2826
2894
|
rolesInIam,
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2895
|
+
and19(
|
|
2896
|
+
eq19(userRolesInIam.roleId, rolesInIam.id),
|
|
2897
|
+
eq19(rolesInIam.tenantId, resolvedTenantId)
|
|
2830
2898
|
)
|
|
2831
2899
|
).where(
|
|
2832
|
-
|
|
2833
|
-
|
|
2900
|
+
and19(
|
|
2901
|
+
eq19(usersInIam.tenantId, resolvedTenantId),
|
|
2834
2902
|
sql11`lower(${usersInIam.email}) = lower(${identifier})`
|
|
2835
2903
|
)
|
|
2836
2904
|
).groupBy(usersInIam.id).limit(1);
|
|
@@ -2853,20 +2921,20 @@ var forgotPasswordHandler = async (c) => {
|
|
|
2853
2921
|
)`
|
|
2854
2922
|
}).from(usersInIam).leftJoin(
|
|
2855
2923
|
userRolesInIam,
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2924
|
+
and19(
|
|
2925
|
+
eq19(userRolesInIam.userId, usersInIam.id),
|
|
2926
|
+
eq19(userRolesInIam.tenantId, resolvedTenantId)
|
|
2859
2927
|
)
|
|
2860
2928
|
).leftJoin(
|
|
2861
2929
|
rolesInIam,
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2930
|
+
and19(
|
|
2931
|
+
eq19(userRolesInIam.roleId, rolesInIam.id),
|
|
2932
|
+
eq19(rolesInIam.tenantId, resolvedTenantId)
|
|
2865
2933
|
)
|
|
2866
2934
|
).where(
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2935
|
+
and19(
|
|
2936
|
+
eq19(usersInIam.tenantId, resolvedTenantId),
|
|
2937
|
+
eq19(usersInIam.phone, identifier)
|
|
2870
2938
|
)
|
|
2871
2939
|
).groupBy(usersInIam.id).limit(1);
|
|
2872
2940
|
user = result || null;
|
|
@@ -2880,10 +2948,10 @@ var forgotPasswordHandler = async (c) => {
|
|
|
2880
2948
|
return c.json({ message: "If account exists, reset code sent" }, 200);
|
|
2881
2949
|
}
|
|
2882
2950
|
await database.delete(verificationsInIam).where(
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2951
|
+
and19(
|
|
2952
|
+
eq19(verificationsInIam.tenantId, resolvedTenantId),
|
|
2953
|
+
eq19(verificationsInIam.userId, user.id),
|
|
2954
|
+
eq19(verificationsInIam.type, "password-reset")
|
|
2887
2955
|
)
|
|
2888
2956
|
);
|
|
2889
2957
|
const code = generateOtpCode(config.email.otpLength);
|
|
@@ -2922,10 +2990,10 @@ var forgotPasswordHandler = async (c) => {
|
|
|
2922
2990
|
return c.json({ message: "If account exists, reset code sent" }, 200);
|
|
2923
2991
|
}
|
|
2924
2992
|
await database.delete(verificationsInIam).where(
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2993
|
+
and19(
|
|
2994
|
+
eq19(verificationsInIam.tenantId, resolvedTenantId),
|
|
2995
|
+
eq19(verificationsInIam.userId, user.id),
|
|
2996
|
+
eq19(verificationsInIam.type, "password-reset-otp")
|
|
2929
2997
|
)
|
|
2930
2998
|
);
|
|
2931
2999
|
const code = generateOtpCode(config.phone.otpLength);
|
|
@@ -2972,7 +3040,7 @@ var forgotPasswordHandler = async (c) => {
|
|
|
2972
3040
|
};
|
|
2973
3041
|
|
|
2974
3042
|
// src/routes/password/handler/reset.ts
|
|
2975
|
-
import { and as
|
|
3043
|
+
import { and as and20, eq as eq20, gt as gt6 } from "drizzle-orm";
|
|
2976
3044
|
|
|
2977
3045
|
// src/routes/password/helper/session.ts
|
|
2978
3046
|
var createPasswordResetSession = async ({
|
|
@@ -2999,14 +3067,8 @@ var createPasswordResetSession = async ({
|
|
|
2999
3067
|
});
|
|
3000
3068
|
return c.json(
|
|
3001
3069
|
{
|
|
3002
|
-
user:
|
|
3003
|
-
session:
|
|
3004
|
-
id: session.id,
|
|
3005
|
-
expiresAt: session.expiresAt,
|
|
3006
|
-
createdAt: session.createdAt,
|
|
3007
|
-
userAgent: session.userAgent,
|
|
3008
|
-
ip: session.ip
|
|
3009
|
-
},
|
|
3070
|
+
user: normalizeAuthUser(user),
|
|
3071
|
+
session: normalizeAuthSession(session),
|
|
3010
3072
|
sessionExpiresAt: session.expiresAt
|
|
3011
3073
|
},
|
|
3012
3074
|
200
|
|
@@ -3022,9 +3084,9 @@ var resetPasswordHandler = async (c) => {
|
|
|
3022
3084
|
const resolvedTenantId = ensureTenantId(config, tenantId);
|
|
3023
3085
|
const { verificationId, code, password } = body;
|
|
3024
3086
|
const [verification] = await database.select().from(verificationsInIam).where(
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3087
|
+
and20(
|
|
3088
|
+
eq20(verificationsInIam.id, verificationId),
|
|
3089
|
+
eq20(verificationsInIam.tenantId, resolvedTenantId)
|
|
3028
3090
|
)
|
|
3029
3091
|
).limit(1);
|
|
3030
3092
|
if (!verification) {
|
|
@@ -3038,34 +3100,34 @@ var resetPasswordHandler = async (c) => {
|
|
|
3038
3100
|
}
|
|
3039
3101
|
const maxAttempts = verification.type === "password-reset" ? config.email.maxAttempts : config.phone.maxAttempts;
|
|
3040
3102
|
if ((verification.attempt || 0) >= maxAttempts) {
|
|
3041
|
-
await database.delete(verificationsInIam).where(
|
|
3103
|
+
await database.delete(verificationsInIam).where(eq20(verificationsInIam.id, verificationId));
|
|
3042
3104
|
return c.json({ error: AUTH_ERRORS.TOO_MANY_ATTEMPTS }, 400);
|
|
3043
3105
|
}
|
|
3044
3106
|
const hashedCode = await hashToken(code, config.secret);
|
|
3045
3107
|
if (verification.code !== hashedCode) {
|
|
3046
|
-
await database.update(verificationsInIam).set({ attempt: (verification.attempt || 0) + 1 }).where(
|
|
3108
|
+
await database.update(verificationsInIam).set({ attempt: (verification.attempt || 0) + 1 }).where(eq20(verificationsInIam.id, verificationId));
|
|
3047
3109
|
return c.json({ error: AUTH_ERRORS.VERIFICATION_MISMATCH }, 400);
|
|
3048
3110
|
}
|
|
3049
3111
|
const passwordHash = await hashPassword(password);
|
|
3050
3112
|
await database.update(accountsInIam).set({ password: passwordHash }).where(
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3113
|
+
and20(
|
|
3114
|
+
eq20(accountsInIam.tenantId, resolvedTenantId),
|
|
3115
|
+
eq20(accountsInIam.userId, verification.userId),
|
|
3116
|
+
eq20(accountsInIam.provider, "credentials")
|
|
3055
3117
|
)
|
|
3056
3118
|
);
|
|
3057
3119
|
await database.delete(sessionsInIam).where(
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3120
|
+
and20(
|
|
3121
|
+
eq20(sessionsInIam.tenantId, resolvedTenantId),
|
|
3122
|
+
eq20(sessionsInIam.userId, verification.userId),
|
|
3061
3123
|
gt6(sessionsInIam.expiresAt, (/* @__PURE__ */ new Date()).toISOString())
|
|
3062
3124
|
)
|
|
3063
3125
|
);
|
|
3064
|
-
await database.delete(verificationsInIam).where(
|
|
3126
|
+
await database.delete(verificationsInIam).where(eq20(verificationsInIam.id, verificationId));
|
|
3065
3127
|
const [user] = await database.select().from(usersInIam).where(
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3128
|
+
and20(
|
|
3129
|
+
eq20(usersInIam.id, verification.userId),
|
|
3130
|
+
eq20(usersInIam.tenantId, resolvedTenantId)
|
|
3069
3131
|
)
|
|
3070
3132
|
).limit(1);
|
|
3071
3133
|
if (!user) {
|
|
@@ -3080,8 +3142,97 @@ var resetPasswordHandler = async (c) => {
|
|
|
3080
3142
|
});
|
|
3081
3143
|
};
|
|
3082
3144
|
|
|
3145
|
+
// src/routes/password/handler/set.ts
|
|
3146
|
+
import { and as and21, eq as eq21 } from "drizzle-orm";
|
|
3147
|
+
var setPasswordHandler = async (c) => {
|
|
3148
|
+
const body = c.req.valid("json");
|
|
3149
|
+
const config = c.get("config");
|
|
3150
|
+
const database = c.get("database");
|
|
3151
|
+
const tenantId = c.get("tenantId");
|
|
3152
|
+
const resolvedTenantId = ensureTenantId(config, tenantId);
|
|
3153
|
+
const { identifier, password, rememberMe = true } = body;
|
|
3154
|
+
const isEmail = identifier.includes("@");
|
|
3155
|
+
const user = await fetchUserForLogin({
|
|
3156
|
+
database,
|
|
3157
|
+
identifier,
|
|
3158
|
+
tenantId: resolvedTenantId,
|
|
3159
|
+
isEmail,
|
|
3160
|
+
userType: config.userType
|
|
3161
|
+
});
|
|
3162
|
+
if (!user) {
|
|
3163
|
+
return c.json({ error: AUTH_ERRORS.USER_NOT_FOUND }, 400);
|
|
3164
|
+
}
|
|
3165
|
+
const isVerified = isEmail ? user.emailVerified : user.phoneVerified;
|
|
3166
|
+
if (!isVerified) {
|
|
3167
|
+
return c.json({ error: AUTH_ERRORS.REQUIRES_VERIFICATION }, 409);
|
|
3168
|
+
}
|
|
3169
|
+
if (user.hasPassword) {
|
|
3170
|
+
return c.json({ error: AUTH_ERRORS.PASSWORD_ALREADY_SET }, 409);
|
|
3171
|
+
}
|
|
3172
|
+
const userAgent = c.req.header("user-agent") || null;
|
|
3173
|
+
const ip = c.req.header("cf-connecting-ip") || c.req.header("x-forwarded-for") || null;
|
|
3174
|
+
const result = await withTransaction(database, async (tx) => {
|
|
3175
|
+
const passwordHash = await hashPassword(password);
|
|
3176
|
+
const [account] = await tx.select({
|
|
3177
|
+
id: accountsInIam.id
|
|
3178
|
+
}).from(accountsInIam).where(
|
|
3179
|
+
and21(
|
|
3180
|
+
eq21(accountsInIam.tenantId, resolvedTenantId),
|
|
3181
|
+
eq21(accountsInIam.userId, user.id),
|
|
3182
|
+
eq21(accountsInIam.provider, "credentials")
|
|
3183
|
+
)
|
|
3184
|
+
).limit(1);
|
|
3185
|
+
if (account) {
|
|
3186
|
+
await tx.update(accountsInIam).set({
|
|
3187
|
+
password: passwordHash,
|
|
3188
|
+
providerAccountId: identifier
|
|
3189
|
+
}).where(eq21(accountsInIam.id, account.id));
|
|
3190
|
+
} else {
|
|
3191
|
+
await tx.insert(accountsInIam).values({
|
|
3192
|
+
tenantId: resolvedTenantId,
|
|
3193
|
+
userId: user.id,
|
|
3194
|
+
provider: "credentials",
|
|
3195
|
+
providerAccountId: identifier,
|
|
3196
|
+
password: passwordHash
|
|
3197
|
+
});
|
|
3198
|
+
}
|
|
3199
|
+
await tx.update(usersInIam).set({ lastSignInAt: (/* @__PURE__ */ new Date()).toISOString(), loginAttempt: 0 }).where(
|
|
3200
|
+
and21(
|
|
3201
|
+
eq21(usersInIam.id, user.id),
|
|
3202
|
+
eq21(usersInIam.tenantId, resolvedTenantId)
|
|
3203
|
+
)
|
|
3204
|
+
);
|
|
3205
|
+
return createSessionRecord({
|
|
3206
|
+
tx,
|
|
3207
|
+
tenantId: resolvedTenantId,
|
|
3208
|
+
userId: user.id,
|
|
3209
|
+
config,
|
|
3210
|
+
userAgent,
|
|
3211
|
+
ip,
|
|
3212
|
+
action: "set-password",
|
|
3213
|
+
rememberMe
|
|
3214
|
+
});
|
|
3215
|
+
});
|
|
3216
|
+
setSessionCookie(c, result.sessionToken, config, {
|
|
3217
|
+
expires: new Date(result.expiresAt)
|
|
3218
|
+
});
|
|
3219
|
+
const fullUser = await fetchUserByIdWithRoles({
|
|
3220
|
+
database,
|
|
3221
|
+
userId: user.id,
|
|
3222
|
+
tenantId: resolvedTenantId
|
|
3223
|
+
});
|
|
3224
|
+
return c.json(
|
|
3225
|
+
{
|
|
3226
|
+
user: normalizeAuthUser(fullUser ?? { ...user, roles: [] }),
|
|
3227
|
+
session: normalizeAuthSession(result.session),
|
|
3228
|
+
sessionExpiresAt: result.session.expiresAt
|
|
3229
|
+
},
|
|
3230
|
+
200
|
|
3231
|
+
);
|
|
3232
|
+
};
|
|
3233
|
+
|
|
3083
3234
|
// src/routes/password/handler/verify.ts
|
|
3084
|
-
import { and as
|
|
3235
|
+
import { and as and22, eq as eq22 } from "drizzle-orm";
|
|
3085
3236
|
var verifyPasswordHandler = async (c) => {
|
|
3086
3237
|
const body = c.req.valid("json");
|
|
3087
3238
|
const config = c.get("config");
|
|
@@ -3098,10 +3249,10 @@ var verifyPasswordHandler = async (c) => {
|
|
|
3098
3249
|
const resolvedTenantId = ensureTenantId(config, tenantId);
|
|
3099
3250
|
const { password } = body;
|
|
3100
3251
|
const [account] = await database.select().from(accountsInIam).where(
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3252
|
+
and22(
|
|
3253
|
+
eq22(accountsInIam.tenantId, resolvedTenantId),
|
|
3254
|
+
eq22(accountsInIam.userId, userId),
|
|
3255
|
+
eq22(accountsInIam.provider, "credentials")
|
|
3105
3256
|
)
|
|
3106
3257
|
).limit(1);
|
|
3107
3258
|
if (!account?.password) {
|
|
@@ -3223,97 +3374,420 @@ var changePasswordRoute = createRoute4({
|
|
|
3223
3374
|
}
|
|
3224
3375
|
}
|
|
3225
3376
|
});
|
|
3226
|
-
var
|
|
3377
|
+
var setPasswordRoute = createRoute4({
|
|
3378
|
+
method: "post",
|
|
3379
|
+
path: "/set",
|
|
3380
|
+
tags: ["Password"],
|
|
3381
|
+
summary: "Set password for an existing verified account",
|
|
3382
|
+
request: {
|
|
3383
|
+
body: {
|
|
3384
|
+
content: {
|
|
3385
|
+
"application/json": {
|
|
3386
|
+
schema: setPasswordSchema
|
|
3387
|
+
}
|
|
3388
|
+
}
|
|
3389
|
+
}
|
|
3390
|
+
},
|
|
3391
|
+
responses: {
|
|
3392
|
+
200: {
|
|
3393
|
+
content: {
|
|
3394
|
+
"application/json": {
|
|
3395
|
+
schema: authSuccessSchema
|
|
3396
|
+
}
|
|
3397
|
+
},
|
|
3398
|
+
description: "Password set and user signed in"
|
|
3399
|
+
},
|
|
3400
|
+
400: {
|
|
3401
|
+
content: { "application/json": { schema: errorResponseSchema } },
|
|
3402
|
+
description: "Account not found"
|
|
3403
|
+
},
|
|
3404
|
+
409: {
|
|
3405
|
+
content: { "application/json": { schema: errorResponseSchema } },
|
|
3406
|
+
description: "Password already exists or verification required"
|
|
3407
|
+
}
|
|
3408
|
+
}
|
|
3409
|
+
});
|
|
3410
|
+
var passwordRoutes = new OpenAPIHono4().openapi(forgotPasswordRoute, forgotPasswordHandler).openapi(resetPasswordRoute, resetPasswordHandler).openapi(verifyPasswordRoute, verifyPasswordHandler).openapi(changePasswordRoute, changePasswordHandler).openapi(setPasswordRoute, setPasswordHandler);
|
|
3227
3411
|
var password_route_default = passwordRoutes;
|
|
3228
3412
|
|
|
3229
3413
|
// src/routes/permissions/permissions.route.ts
|
|
3230
3414
|
import { createRoute as createRoute5, OpenAPIHono as OpenAPIHono5 } from "@hono/zod-openapi";
|
|
3231
3415
|
|
|
3232
3416
|
// src/routes/permissions/handler/get-permission.ts
|
|
3233
|
-
import {
|
|
3417
|
+
import { getPermissionEntries } from "@mesob/common";
|
|
3418
|
+
import { eq as eq23 } from "drizzle-orm";
|
|
3234
3419
|
var getPermissionHandler = async (c) => {
|
|
3235
3420
|
const { id } = c.req.valid("param");
|
|
3236
3421
|
const database = c.get("database");
|
|
3237
|
-
const
|
|
3422
|
+
const config = c.get("config");
|
|
3423
|
+
const [permission] = await database.select().from(permissionsInIam).where(eq23(permissionsInIam.id, id)).limit(1);
|
|
3238
3424
|
if (!permission) {
|
|
3239
|
-
|
|
3425
|
+
const configuredPermission = getPermissionEntries(config.permissions).find(
|
|
3426
|
+
(entry) => entry.code === id
|
|
3427
|
+
);
|
|
3428
|
+
if (!configuredPermission) {
|
|
3429
|
+
return c.json({ error: "Permission not found" }, 404);
|
|
3430
|
+
}
|
|
3431
|
+
return c.json(
|
|
3432
|
+
{
|
|
3433
|
+
permission: {
|
|
3434
|
+
id: configuredPermission.code,
|
|
3435
|
+
description: null,
|
|
3436
|
+
activity: configuredPermission.activity,
|
|
3437
|
+
application: configuredPermission.application,
|
|
3438
|
+
feature: configuredPermission.feature
|
|
3439
|
+
}
|
|
3440
|
+
},
|
|
3441
|
+
200
|
|
3442
|
+
);
|
|
3240
3443
|
}
|
|
3241
3444
|
return c.json({ permission }, 200);
|
|
3242
3445
|
};
|
|
3243
3446
|
|
|
3447
|
+
// src/lib/permission-catalog.ts
|
|
3448
|
+
import { getPermissionEntries as getPermissionEntries2 } from "@mesob/common";
|
|
3449
|
+
var collator = new Intl.Collator(void 0, {
|
|
3450
|
+
numeric: true,
|
|
3451
|
+
sensitivity: "base"
|
|
3452
|
+
});
|
|
3453
|
+
var sortValueMap = {
|
|
3454
|
+
id: (permission) => permission.id,
|
|
3455
|
+
application: (permission) => permission.application,
|
|
3456
|
+
feature: (permission) => permission.feature,
|
|
3457
|
+
activity: (permission) => permission.activity
|
|
3458
|
+
};
|
|
3459
|
+
async function getMergedPermissionCatalog({
|
|
3460
|
+
database,
|
|
3461
|
+
permissions
|
|
3462
|
+
}) {
|
|
3463
|
+
const [databasePermissions, configuredPermissions] = await Promise.all([
|
|
3464
|
+
database.select().from(permissionsInIam),
|
|
3465
|
+
Promise.resolve(
|
|
3466
|
+
getPermissionEntries2(permissions).map((entry) => ({
|
|
3467
|
+
id: entry.code,
|
|
3468
|
+
description: null,
|
|
3469
|
+
activity: entry.activity,
|
|
3470
|
+
application: entry.application,
|
|
3471
|
+
feature: entry.feature
|
|
3472
|
+
}))
|
|
3473
|
+
)
|
|
3474
|
+
]);
|
|
3475
|
+
return [...databasePermissions, ...configuredPermissions].filter(
|
|
3476
|
+
(permission, index2, permissionList) => {
|
|
3477
|
+
const key = [
|
|
3478
|
+
permission.application,
|
|
3479
|
+
permission.feature,
|
|
3480
|
+
permission.activity
|
|
3481
|
+
].join(":");
|
|
3482
|
+
return permissionList.findIndex((candidate) => {
|
|
3483
|
+
return [candidate.application, candidate.feature, candidate.activity].join(
|
|
3484
|
+
":"
|
|
3485
|
+
) === key;
|
|
3486
|
+
}) === index2;
|
|
3487
|
+
}
|
|
3488
|
+
);
|
|
3489
|
+
}
|
|
3490
|
+
function filterAndSortPermissions(permissions, query) {
|
|
3491
|
+
const search = query.search?.trim().toLowerCase();
|
|
3492
|
+
const filteredPermissions = search ? permissions.filter((permission) => {
|
|
3493
|
+
let fields = [
|
|
3494
|
+
permission.id,
|
|
3495
|
+
permission.application,
|
|
3496
|
+
permission.feature,
|
|
3497
|
+
permission.activity
|
|
3498
|
+
];
|
|
3499
|
+
if (query.filter === "application") {
|
|
3500
|
+
fields = [permission.application];
|
|
3501
|
+
} else if (query.filter === "feature") {
|
|
3502
|
+
fields = [permission.feature];
|
|
3503
|
+
} else if (query.filter === "activity") {
|
|
3504
|
+
fields = [permission.activity];
|
|
3505
|
+
}
|
|
3506
|
+
return fields.some((field) => field.toLowerCase().includes(search));
|
|
3507
|
+
}) : permissions;
|
|
3508
|
+
const sortGetter = sortValueMap[query.sort || "id"] ?? sortValueMap.id;
|
|
3509
|
+
return [...filteredPermissions].sort((a, b) => {
|
|
3510
|
+
const order = collator.compare(sortGetter(a), sortGetter(b)) || collator.compare(a.id, b.id);
|
|
3511
|
+
return query.order === "desc" ? -order : order;
|
|
3512
|
+
});
|
|
3513
|
+
}
|
|
3514
|
+
|
|
3244
3515
|
// src/routes/permissions/handler/list-permissions.ts
|
|
3245
|
-
import { and as and21, ilike, sql as sql12 } from "drizzle-orm";
|
|
3246
3516
|
var listPermissionsHandler = async (c) => {
|
|
3247
3517
|
const query = c.req.valid("query");
|
|
3248
3518
|
const database = c.get("database");
|
|
3519
|
+
const config = c.get("config");
|
|
3249
3520
|
const page = query.page || 1;
|
|
3250
3521
|
const limit = query.limit || 20;
|
|
3251
3522
|
const offset = (page - 1) * limit;
|
|
3252
|
-
const
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
conditions.push(ilike(permissionsInIam.feature, `%${query.feature}%`));
|
|
3260
|
-
}
|
|
3261
|
-
const [permissions, totalResult] = await Promise.all([
|
|
3262
|
-
database.select().from(permissionsInIam).where(conditions.length > 0 ? and21(...conditions) : void 0).limit(limit).offset(offset),
|
|
3263
|
-
database.select({ count: sql12`count(*)` }).from(permissionsInIam).where(conditions.length > 0 ? and21(...conditions) : void 0)
|
|
3264
|
-
]);
|
|
3265
|
-
const total = Number(totalResult[0]?.count || 0);
|
|
3523
|
+
const mergedPermissions = await getMergedPermissionCatalog({
|
|
3524
|
+
database,
|
|
3525
|
+
permissions: config.permissions
|
|
3526
|
+
});
|
|
3527
|
+
const sortedPermissions = filterAndSortPermissions(mergedPermissions, query);
|
|
3528
|
+
const total = sortedPermissions.length;
|
|
3529
|
+
const permissions = sortedPermissions.slice(offset, offset + limit);
|
|
3266
3530
|
return c.json({ permissions, total, page, limit }, 200);
|
|
3267
3531
|
};
|
|
3268
3532
|
|
|
3269
|
-
// src/
|
|
3270
|
-
import {
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
}
|
|
3280
|
-
|
|
3281
|
-
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
});
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3533
|
+
// src/lib/iam-seed.ts
|
|
3534
|
+
import { randomUUID } from "crypto";
|
|
3535
|
+
import { getPermissionEntries as getPermissionEntries3, toTitleCase } from "@mesob/common";
|
|
3536
|
+
import {
|
|
3537
|
+
and as and23,
|
|
3538
|
+
eq as eq24,
|
|
3539
|
+
inArray as inArray2,
|
|
3540
|
+
notInArray,
|
|
3541
|
+
sql as sql12
|
|
3542
|
+
} from "drizzle-orm";
|
|
3543
|
+
import { HTTPException as HTTPException3 } from "hono/http-exception";
|
|
3544
|
+
function buildPermissionDescription(code) {
|
|
3545
|
+
return {
|
|
3546
|
+
en: toTitleCase(code.replaceAll(":", " ").replaceAll("_", " "))
|
|
3547
|
+
};
|
|
3548
|
+
}
|
|
3549
|
+
function buildPermissionSeedRows(permissions) {
|
|
3550
|
+
return getPermissionEntries3(permissions).map((entry) => ({
|
|
3551
|
+
id: entry.code,
|
|
3552
|
+
application: entry.application,
|
|
3553
|
+
feature: entry.feature,
|
|
3554
|
+
activity: entry.activity,
|
|
3555
|
+
description: buildPermissionDescription(entry.code)
|
|
3556
|
+
}));
|
|
3557
|
+
}
|
|
3558
|
+
async function seedPermissions({
|
|
3559
|
+
database,
|
|
3560
|
+
permissions
|
|
3561
|
+
}) {
|
|
3562
|
+
const rows = buildPermissionSeedRows(permissions);
|
|
3563
|
+
if (!rows.length) {
|
|
3564
|
+
return [];
|
|
3565
|
+
}
|
|
3566
|
+
await database.insert(permissionsInIam).values(rows).onConflictDoUpdate({
|
|
3567
|
+
target: permissionsInIam.id,
|
|
3568
|
+
set: {
|
|
3569
|
+
application: sql12`excluded.application`,
|
|
3570
|
+
feature: sql12`excluded.feature`,
|
|
3571
|
+
activity: sql12`excluded.activity`,
|
|
3572
|
+
description: sql12`excluded.description`
|
|
3573
|
+
}
|
|
3574
|
+
});
|
|
3575
|
+
return database.select().from(permissionsInIam).where(
|
|
3576
|
+
inArray2(
|
|
3577
|
+
permissionsInIam.id,
|
|
3578
|
+
rows.map((row) => row.id)
|
|
3579
|
+
)
|
|
3580
|
+
);
|
|
3581
|
+
}
|
|
3582
|
+
async function assertPermissionsExist({
|
|
3583
|
+
database,
|
|
3584
|
+
permissionIds
|
|
3585
|
+
}) {
|
|
3586
|
+
if (!permissionIds.length) {
|
|
3587
|
+
return;
|
|
3588
|
+
}
|
|
3589
|
+
const existing = await database.select({ id: permissionsInIam.id }).from(permissionsInIam).where(inArray2(permissionsInIam.id, permissionIds));
|
|
3590
|
+
const existingIds = new Set(existing.map((permission) => permission.id));
|
|
3591
|
+
const missingPermissionIds = permissionIds.filter(
|
|
3592
|
+
(id) => !existingIds.has(id)
|
|
3593
|
+
);
|
|
3594
|
+
if (missingPermissionIds.length) {
|
|
3595
|
+
throw new HTTPException3(400, {
|
|
3596
|
+
message: `Unknown permissions: ${missingPermissionIds.join(", ")}`
|
|
3597
|
+
});
|
|
3598
|
+
}
|
|
3599
|
+
}
|
|
3600
|
+
async function syncRolePermissions({
|
|
3601
|
+
database,
|
|
3602
|
+
tenantId,
|
|
3603
|
+
roleId,
|
|
3604
|
+
permissionIds
|
|
3605
|
+
}) {
|
|
3606
|
+
const uniquePermissionIds = [...new Set(permissionIds)];
|
|
3607
|
+
await assertPermissionsExist({
|
|
3608
|
+
database,
|
|
3609
|
+
permissionIds: uniquePermissionIds
|
|
3610
|
+
});
|
|
3611
|
+
if (!uniquePermissionIds.length) {
|
|
3612
|
+
await database.delete(rolePermissionsInIam).where(
|
|
3613
|
+
and23(
|
|
3614
|
+
eq24(rolePermissionsInIam.tenantId, tenantId),
|
|
3615
|
+
eq24(rolePermissionsInIam.roleId, roleId)
|
|
3616
|
+
)
|
|
3617
|
+
);
|
|
3618
|
+
return [];
|
|
3619
|
+
}
|
|
3620
|
+
await database.delete(rolePermissionsInIam).where(
|
|
3621
|
+
and23(
|
|
3622
|
+
eq24(rolePermissionsInIam.tenantId, tenantId),
|
|
3623
|
+
eq24(rolePermissionsInIam.roleId, roleId),
|
|
3624
|
+
notInArray(rolePermissionsInIam.permissionId, uniquePermissionIds)
|
|
3625
|
+
)
|
|
3626
|
+
);
|
|
3627
|
+
await database.insert(rolePermissionsInIam).values(
|
|
3628
|
+
uniquePermissionIds.map((permissionId) => ({
|
|
3629
|
+
id: randomUUID(),
|
|
3630
|
+
tenantId,
|
|
3631
|
+
roleId,
|
|
3632
|
+
permissionId
|
|
3633
|
+
}))
|
|
3634
|
+
).onConflictDoNothing({
|
|
3635
|
+
target: [
|
|
3636
|
+
rolePermissionsInIam.tenantId,
|
|
3637
|
+
rolePermissionsInIam.permissionId,
|
|
3638
|
+
rolePermissionsInIam.roleId
|
|
3639
|
+
]
|
|
3640
|
+
});
|
|
3641
|
+
return database.select().from(rolePermissionsInIam).where(
|
|
3642
|
+
and23(
|
|
3643
|
+
eq24(rolePermissionsInIam.tenantId, tenantId),
|
|
3644
|
+
eq24(rolePermissionsInIam.roleId, roleId)
|
|
3645
|
+
)
|
|
3646
|
+
);
|
|
3647
|
+
}
|
|
3648
|
+
async function seedRoles({
|
|
3649
|
+
database,
|
|
3650
|
+
tenantId,
|
|
3651
|
+
roles
|
|
3652
|
+
}) {
|
|
3653
|
+
if (!roles.length) {
|
|
3654
|
+
return [];
|
|
3655
|
+
}
|
|
3656
|
+
await database.insert(rolesInIam).values(
|
|
3657
|
+
roles.map((role) => ({
|
|
3658
|
+
id: randomUUID(),
|
|
3659
|
+
tenantId,
|
|
3660
|
+
code: role.code,
|
|
3661
|
+
name: role.name,
|
|
3662
|
+
description: role.description ?? { en: role.code },
|
|
3663
|
+
isSystem: role.isSystem ?? false,
|
|
3664
|
+
isEditable: role.isEditable ?? true,
|
|
3665
|
+
isDeletable: role.isDeletable ?? true
|
|
3666
|
+
}))
|
|
3667
|
+
).onConflictDoUpdate({
|
|
3668
|
+
target: [rolesInIam.tenantId, rolesInIam.code],
|
|
3669
|
+
set: {
|
|
3670
|
+
name: sql12`excluded.name`,
|
|
3671
|
+
description: sql12`excluded.description`,
|
|
3672
|
+
isSystem: sql12`excluded.is_system`,
|
|
3673
|
+
isEditable: sql12`excluded.is_editable`,
|
|
3674
|
+
isDeletable: sql12`excluded.is_deletable`,
|
|
3675
|
+
updatedAt: sql12`CURRENT_TIMESTAMP`
|
|
3676
|
+
}
|
|
3677
|
+
});
|
|
3678
|
+
const seededRoles = await database.select().from(rolesInIam).where(
|
|
3679
|
+
and23(
|
|
3680
|
+
eq24(rolesInIam.tenantId, tenantId),
|
|
3681
|
+
inArray2(
|
|
3682
|
+
rolesInIam.code,
|
|
3683
|
+
roles.map((role) => role.code)
|
|
3684
|
+
)
|
|
3685
|
+
)
|
|
3686
|
+
);
|
|
3687
|
+
const roleByCode = new Map(
|
|
3688
|
+
seededRoles.map((role) => [role.code, role])
|
|
3689
|
+
);
|
|
3690
|
+
for (const role of roles) {
|
|
3691
|
+
const seededRole = roleByCode.get(role.code);
|
|
3692
|
+
if (!seededRole) {
|
|
3693
|
+
continue;
|
|
3694
|
+
}
|
|
3695
|
+
await syncRolePermissions({
|
|
3696
|
+
database,
|
|
3697
|
+
tenantId,
|
|
3698
|
+
roleId: seededRole.id,
|
|
3699
|
+
permissionIds: [...new Set(role.permissionIds ?? [])]
|
|
3700
|
+
});
|
|
3701
|
+
}
|
|
3702
|
+
return seededRoles;
|
|
3703
|
+
}
|
|
3704
|
+
|
|
3705
|
+
// src/routes/permissions/handler/seed-permissions.ts
|
|
3706
|
+
var seedPermissionsHandler = async (c) => {
|
|
3707
|
+
const config = c.get("config");
|
|
3708
|
+
const database = c.get("database");
|
|
3709
|
+
if (!config.permissions) {
|
|
3710
|
+
return c.json({ error: "No permissions configured for seeding" }, 400);
|
|
3711
|
+
}
|
|
3712
|
+
const permissions = await seedPermissions({
|
|
3713
|
+
database,
|
|
3714
|
+
permissions: config.permissions
|
|
3715
|
+
});
|
|
3716
|
+
return c.json(
|
|
3717
|
+
{
|
|
3718
|
+
permissions,
|
|
3719
|
+
total: permissions.length
|
|
3720
|
+
},
|
|
3721
|
+
200
|
|
3722
|
+
);
|
|
3723
|
+
};
|
|
3724
|
+
|
|
3725
|
+
// src/routes/permissions/permissions.schema.ts
|
|
3726
|
+
import { z as z3 } from "zod";
|
|
3727
|
+
var permissionFilterValues = [
|
|
3728
|
+
"",
|
|
3729
|
+
"application",
|
|
3730
|
+
"feature",
|
|
3731
|
+
"activity"
|
|
3732
|
+
];
|
|
3733
|
+
var permissionSortValues = [
|
|
3734
|
+
"id",
|
|
3735
|
+
"application",
|
|
3736
|
+
"feature",
|
|
3737
|
+
"activity"
|
|
3738
|
+
];
|
|
3739
|
+
var listPermissionsQuerySchema = z3.object({
|
|
3740
|
+
page: z3.coerce.number().min(1).default(1).optional(),
|
|
3741
|
+
limit: z3.coerce.number().min(1).max(100).default(20).optional(),
|
|
3742
|
+
search: z3.string().optional(),
|
|
3743
|
+
filter: z3.enum(permissionFilterValues).optional(),
|
|
3744
|
+
sort: z3.enum(permissionSortValues).optional(),
|
|
3745
|
+
order: z3.enum(["asc", "desc"]).optional()
|
|
3746
|
+
});
|
|
3747
|
+
var permissionIdParamSchema = z3.object({
|
|
3748
|
+
id: z3.string()
|
|
3749
|
+
});
|
|
3750
|
+
var permissionSchema = z3.object({
|
|
3751
|
+
id: z3.string(),
|
|
3752
|
+
description: z3.unknown(),
|
|
3753
|
+
activity: z3.string(),
|
|
3754
|
+
application: z3.string(),
|
|
3755
|
+
feature: z3.string()
|
|
3756
|
+
});
|
|
3757
|
+
var seedPermissionsResponseSchema = z3.object({
|
|
3758
|
+
permissions: z3.array(permissionSchema),
|
|
3759
|
+
total: z3.number()
|
|
3760
|
+
});
|
|
3761
|
+
var listPermissionsResponseSchema = z3.object({
|
|
3762
|
+
permissions: z3.array(permissionSchema),
|
|
3763
|
+
total: z3.number(),
|
|
3764
|
+
page: z3.number(),
|
|
3765
|
+
limit: z3.number()
|
|
3766
|
+
});
|
|
3767
|
+
var permissionResponseSchema = z3.object({
|
|
3768
|
+
permission: permissionSchema
|
|
3769
|
+
});
|
|
3770
|
+
var errorResponseSchema3 = z3.object({
|
|
3771
|
+
error: z3.string()
|
|
3772
|
+
});
|
|
3773
|
+
|
|
3774
|
+
// src/routes/permissions/permissions.route.ts
|
|
3775
|
+
var listPermissionsRoute = createRoute5({
|
|
3776
|
+
method: "get",
|
|
3777
|
+
path: "/",
|
|
3778
|
+
tags: ["Permissions"],
|
|
3779
|
+
summary: "List permissions",
|
|
3780
|
+
request: {
|
|
3781
|
+
query: listPermissionsQuerySchema
|
|
3782
|
+
},
|
|
3783
|
+
responses: {
|
|
3784
|
+
200: {
|
|
3785
|
+
content: {
|
|
3786
|
+
"application/json": {
|
|
3787
|
+
schema: listPermissionsResponseSchema
|
|
3788
|
+
}
|
|
3789
|
+
},
|
|
3790
|
+
description: "List of permissions"
|
|
3317
3791
|
}
|
|
3318
3792
|
}
|
|
3319
3793
|
});
|
|
@@ -3344,14 +3818,38 @@ var getPermissionRoute = createRoute5({
|
|
|
3344
3818
|
}
|
|
3345
3819
|
}
|
|
3346
3820
|
});
|
|
3347
|
-
var
|
|
3821
|
+
var seedPermissionsRoute = createRoute5({
|
|
3822
|
+
method: "post",
|
|
3823
|
+
path: "/seed",
|
|
3824
|
+
tags: ["Permissions"],
|
|
3825
|
+
summary: "Seed permissions from config",
|
|
3826
|
+
responses: {
|
|
3827
|
+
200: {
|
|
3828
|
+
content: {
|
|
3829
|
+
"application/json": {
|
|
3830
|
+
schema: seedPermissionsResponseSchema
|
|
3831
|
+
}
|
|
3832
|
+
},
|
|
3833
|
+
description: "Seeded permissions"
|
|
3834
|
+
},
|
|
3835
|
+
400: {
|
|
3836
|
+
content: {
|
|
3837
|
+
"application/json": {
|
|
3838
|
+
schema: errorResponseSchema3
|
|
3839
|
+
}
|
|
3840
|
+
},
|
|
3841
|
+
description: "Invalid permission config"
|
|
3842
|
+
}
|
|
3843
|
+
}
|
|
3844
|
+
});
|
|
3845
|
+
var permissionRoutes = new OpenAPIHono5().openapi(listPermissionsRoute, listPermissionsHandler).openapi(seedPermissionsRoute, seedPermissionsHandler).openapi(getPermissionRoute, getPermissionHandler);
|
|
3348
3846
|
var permissions_route_default = permissionRoutes;
|
|
3349
3847
|
|
|
3350
3848
|
// src/routes/phone/phone.route.ts
|
|
3351
3849
|
import { createRoute as createRoute6, OpenAPIHono as OpenAPIHono6 } from "@hono/zod-openapi";
|
|
3352
3850
|
|
|
3353
3851
|
// src/routes/phone/handler/verification-confirm.ts
|
|
3354
|
-
import { and as
|
|
3852
|
+
import { and as and24, eq as eq25 } from "drizzle-orm";
|
|
3355
3853
|
|
|
3356
3854
|
// src/routes/phone/helper/session.ts
|
|
3357
3855
|
var shouldCreateSession = (context) => context === "sign-in" || context === "change-phone" || context === "sign-up";
|
|
@@ -3367,9 +3865,9 @@ var phoneVerificationConfirmHandler = async (c) => {
|
|
|
3367
3865
|
const { verificationId, code, context } = body;
|
|
3368
3866
|
const result = await withTransaction(database, async (tx) => {
|
|
3369
3867
|
const [verification] = await tx.select().from(verificationsInIam).where(
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3868
|
+
and24(
|
|
3869
|
+
eq25(verificationsInIam.id, verificationId),
|
|
3870
|
+
eq25(verificationsInIam.tenantId, resolvedTenantId)
|
|
3373
3871
|
)
|
|
3374
3872
|
).limit(1);
|
|
3375
3873
|
const expectedType = `phone-otp-${context}`;
|
|
@@ -3386,7 +3884,7 @@ var phoneVerificationConfirmHandler = async (c) => {
|
|
|
3386
3884
|
};
|
|
3387
3885
|
}
|
|
3388
3886
|
if ((verification.attempt || 0) >= config.phone.maxAttempts) {
|
|
3389
|
-
await tx.delete(verificationsInIam).where(
|
|
3887
|
+
await tx.delete(verificationsInIam).where(eq25(verificationsInIam.id, verificationId));
|
|
3390
3888
|
return {
|
|
3391
3889
|
status: "error",
|
|
3392
3890
|
error: AUTH_ERRORS.TOO_MANY_ATTEMPTS
|
|
@@ -3394,21 +3892,21 @@ var phoneVerificationConfirmHandler = async (c) => {
|
|
|
3394
3892
|
}
|
|
3395
3893
|
const hashedCode = await hashToken(code, config.secret);
|
|
3396
3894
|
if (verification.code !== hashedCode) {
|
|
3397
|
-
await tx.update(verificationsInIam).set({ attempt: (verification.attempt || 0) + 1 }).where(
|
|
3895
|
+
await tx.update(verificationsInIam).set({ attempt: (verification.attempt || 0) + 1 }).where(eq25(verificationsInIam.id, verificationId));
|
|
3398
3896
|
return {
|
|
3399
3897
|
status: "error",
|
|
3400
3898
|
error: AUTH_ERRORS.VERIFICATION_MISMATCH
|
|
3401
3899
|
};
|
|
3402
3900
|
}
|
|
3403
|
-
await tx.delete(verificationsInIam).where(
|
|
3901
|
+
await tx.delete(verificationsInIam).where(eq25(verificationsInIam.id, verificationId));
|
|
3404
3902
|
if (shouldCreateSession(context) && verification.userId) {
|
|
3405
3903
|
await tx.update(usersInIam).set({
|
|
3406
3904
|
phoneVerified: true,
|
|
3407
3905
|
lastSignInAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3408
3906
|
}).where(
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3907
|
+
and24(
|
|
3908
|
+
eq25(usersInIam.id, verification.userId),
|
|
3909
|
+
eq25(usersInIam.tenantId, resolvedTenantId)
|
|
3412
3910
|
)
|
|
3413
3911
|
);
|
|
3414
3912
|
}
|
|
@@ -3450,7 +3948,7 @@ var phoneVerificationConfirmHandler = async (c) => {
|
|
|
3450
3948
|
if (!result.session) {
|
|
3451
3949
|
return c.json(
|
|
3452
3950
|
{
|
|
3453
|
-
user:
|
|
3951
|
+
user: normalizeAuthUser(result.user),
|
|
3454
3952
|
session: null
|
|
3455
3953
|
},
|
|
3456
3954
|
200
|
|
@@ -3461,14 +3959,8 @@ var phoneVerificationConfirmHandler = async (c) => {
|
|
|
3461
3959
|
});
|
|
3462
3960
|
return c.json(
|
|
3463
3961
|
{
|
|
3464
|
-
user:
|
|
3465
|
-
session:
|
|
3466
|
-
id: result.session.id,
|
|
3467
|
-
expiresAt: result.session.expiresAt,
|
|
3468
|
-
createdAt: result.session.createdAt,
|
|
3469
|
-
userAgent: result.session.userAgent,
|
|
3470
|
-
ip: result.session.ip
|
|
3471
|
-
},
|
|
3962
|
+
user: normalizeAuthUser(result.user),
|
|
3963
|
+
session: normalizeAuthSession(result.session),
|
|
3472
3964
|
sessionExpiresAt: result.session.expiresAt
|
|
3473
3965
|
},
|
|
3474
3966
|
200
|
|
@@ -3476,7 +3968,7 @@ var phoneVerificationConfirmHandler = async (c) => {
|
|
|
3476
3968
|
};
|
|
3477
3969
|
|
|
3478
3970
|
// src/routes/phone/handler/verification-request.ts
|
|
3479
|
-
import { and as
|
|
3971
|
+
import { and as and25, eq as eq26 } from "drizzle-orm";
|
|
3480
3972
|
var phoneVerificationRequestHandler = async (c) => {
|
|
3481
3973
|
const body = c.req.valid("json");
|
|
3482
3974
|
const config = c.get("config");
|
|
@@ -3501,9 +3993,9 @@ var phoneVerificationRequestHandler = async (c) => {
|
|
|
3501
3993
|
let userId = user?.id;
|
|
3502
3994
|
if (!userId) {
|
|
3503
3995
|
const [result] = await database.select({ id: usersInIam.id }).from(usersInIam).where(
|
|
3504
|
-
|
|
3505
|
-
|
|
3506
|
-
|
|
3996
|
+
and25(
|
|
3997
|
+
eq26(usersInIam.tenantId, resolvedTenantId),
|
|
3998
|
+
eq26(usersInIam.phone, phone)
|
|
3507
3999
|
)
|
|
3508
4000
|
).limit(1);
|
|
3509
4001
|
if (!result) {
|
|
@@ -3529,10 +4021,10 @@ var phoneVerificationRequestHandler = async (c) => {
|
|
|
3529
4021
|
);
|
|
3530
4022
|
}
|
|
3531
4023
|
await database.delete(verificationsInIam).where(
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
|
|
4024
|
+
and25(
|
|
4025
|
+
eq26(verificationsInIam.tenantId, resolvedTenantId),
|
|
4026
|
+
eq26(verificationsInIam.userId, userId),
|
|
4027
|
+
eq26(verificationsInIam.type, verificationType)
|
|
3536
4028
|
)
|
|
3537
4029
|
);
|
|
3538
4030
|
const code = generateOtpCode(config.phone.otpLength);
|
|
@@ -3564,11 +4056,11 @@ var phoneVerificationRequestHandler = async (c) => {
|
|
|
3564
4056
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3565
4057
|
reason: "replaced"
|
|
3566
4058
|
}).where(
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
4059
|
+
and25(
|
|
4060
|
+
eq26(accountChangesInIam.tenantId, resolvedTenantId),
|
|
4061
|
+
eq26(accountChangesInIam.userId, user.id),
|
|
4062
|
+
eq26(accountChangesInIam.changeType, "phone"),
|
|
4063
|
+
eq26(accountChangesInIam.status, "pending")
|
|
3572
4064
|
)
|
|
3573
4065
|
);
|
|
3574
4066
|
await database.insert(accountChangesInIam).values({
|
|
@@ -3684,7 +4176,7 @@ import { createRoute as createRoute7, OpenAPIHono as OpenAPIHono7 } from "@hono/
|
|
|
3684
4176
|
import { z as z4 } from "zod";
|
|
3685
4177
|
|
|
3686
4178
|
// src/routes/profile/handler/account-change-pending.ts
|
|
3687
|
-
import { and as
|
|
4179
|
+
import { and as and26, eq as eq27, gt as gt7, sql as sql13 } from "drizzle-orm";
|
|
3688
4180
|
var accountChangePendingHandler = async (c) => {
|
|
3689
4181
|
const config = c.get("config");
|
|
3690
4182
|
const database = c.get("database");
|
|
@@ -3695,17 +4187,17 @@ var accountChangePendingHandler = async (c) => {
|
|
|
3695
4187
|
}
|
|
3696
4188
|
const resolvedTenantId = ensureTenantId(config, tenantId);
|
|
3697
4189
|
await database.update(accountChangesInIam).set({ status: "expired" }).where(
|
|
3698
|
-
|
|
3699
|
-
|
|
3700
|
-
|
|
4190
|
+
and26(
|
|
4191
|
+
eq27(accountChangesInIam.tenantId, resolvedTenantId),
|
|
4192
|
+
eq27(accountChangesInIam.userId, userId),
|
|
3701
4193
|
sql13`${accountChangesInIam.expiresAt} < CURRENT_TIMESTAMP`
|
|
3702
4194
|
)
|
|
3703
4195
|
);
|
|
3704
4196
|
const [accountChange] = await database.select().from(accountChangesInIam).where(
|
|
3705
|
-
|
|
3706
|
-
|
|
3707
|
-
|
|
3708
|
-
|
|
4197
|
+
and26(
|
|
4198
|
+
eq27(accountChangesInIam.tenantId, resolvedTenantId),
|
|
4199
|
+
eq27(accountChangesInIam.userId, userId),
|
|
4200
|
+
eq27(accountChangesInIam.status, "pending")
|
|
3709
4201
|
)
|
|
3710
4202
|
).limit(1);
|
|
3711
4203
|
if (!accountChange) {
|
|
@@ -3717,11 +4209,11 @@ var accountChangePendingHandler = async (c) => {
|
|
|
3717
4209
|
id: verificationsInIam.id,
|
|
3718
4210
|
expiresAt: verificationsInIam.expiresAt
|
|
3719
4211
|
}).from(verificationsInIam).where(
|
|
3720
|
-
|
|
3721
|
-
|
|
3722
|
-
|
|
3723
|
-
|
|
3724
|
-
|
|
4212
|
+
and26(
|
|
4213
|
+
eq27(verificationsInIam.tenantId, resolvedTenantId),
|
|
4214
|
+
eq27(verificationsInIam.userId, userId),
|
|
4215
|
+
eq27(verificationsInIam.type, "email-verification"),
|
|
4216
|
+
eq27(verificationsInIam.to, accountChange.newEmail),
|
|
3725
4217
|
gt7(verificationsInIam.expiresAt, sql13`CURRENT_TIMESTAMP`)
|
|
3726
4218
|
)
|
|
3727
4219
|
).limit(1);
|
|
@@ -3732,11 +4224,11 @@ var accountChangePendingHandler = async (c) => {
|
|
|
3732
4224
|
id: verificationsInIam.id,
|
|
3733
4225
|
expiresAt: verificationsInIam.expiresAt
|
|
3734
4226
|
}).from(verificationsInIam).where(
|
|
3735
|
-
|
|
3736
|
-
|
|
3737
|
-
|
|
3738
|
-
|
|
3739
|
-
|
|
4227
|
+
and26(
|
|
4228
|
+
eq27(verificationsInIam.tenantId, resolvedTenantId),
|
|
4229
|
+
eq27(verificationsInIam.userId, userId),
|
|
4230
|
+
eq27(verificationsInIam.type, "phone-otp-change-phone"),
|
|
4231
|
+
eq27(verificationsInIam.to, accountChange.newPhone),
|
|
3740
4232
|
gt7(verificationsInIam.expiresAt, sql13`CURRENT_TIMESTAMP`)
|
|
3741
4233
|
)
|
|
3742
4234
|
).limit(1);
|
|
@@ -3759,6 +4251,13 @@ var accountChangePendingHandler = async (c) => {
|
|
|
3759
4251
|
);
|
|
3760
4252
|
};
|
|
3761
4253
|
|
|
4254
|
+
// src/lib/normalize-user.ts
|
|
4255
|
+
var normalizeUser = (user) => ({
|
|
4256
|
+
...user,
|
|
4257
|
+
roles: user.roles ?? null,
|
|
4258
|
+
roleCodes: user.roleCodes ?? null
|
|
4259
|
+
});
|
|
4260
|
+
|
|
3762
4261
|
// src/routes/profile/handler/me.ts
|
|
3763
4262
|
var meHandler = (c) => {
|
|
3764
4263
|
const user = c.get("user");
|
|
@@ -3805,7 +4304,7 @@ var sessionHandler = (c) => {
|
|
|
3805
4304
|
};
|
|
3806
4305
|
|
|
3807
4306
|
// src/routes/profile/handler/update.ts
|
|
3808
|
-
import { and as
|
|
4307
|
+
import { and as and27, eq as eq28, sql as sql14 } from "drizzle-orm";
|
|
3809
4308
|
var updateProfileHandler = async (c) => {
|
|
3810
4309
|
const body = c.req.valid("json");
|
|
3811
4310
|
const config = c.get("config");
|
|
@@ -3828,7 +4327,7 @@ var updateProfileHandler = async (c) => {
|
|
|
3828
4327
|
...updateData,
|
|
3829
4328
|
updatedAt: sql14`CURRENT_TIMESTAMP`
|
|
3830
4329
|
}).where(
|
|
3831
|
-
|
|
4330
|
+
and27(eq28(usersInIam.id, userId), eq28(usersInIam.tenantId, resolvedTenantId))
|
|
3832
4331
|
).returning({
|
|
3833
4332
|
id: usersInIam.id,
|
|
3834
4333
|
tenantId: usersInIam.tenantId,
|
|
@@ -3848,7 +4347,7 @@ var updateProfileHandler = async (c) => {
|
|
|
3848
4347
|
};
|
|
3849
4348
|
|
|
3850
4349
|
// src/routes/profile/handler/update-email.ts
|
|
3851
|
-
import { and as
|
|
4350
|
+
import { and as and28, eq as eq29, ne as ne2, sql as sql15 } from "drizzle-orm";
|
|
3852
4351
|
var updateEmailHandler = async (c) => {
|
|
3853
4352
|
const body = c.req.valid("json");
|
|
3854
4353
|
const config = c.get("config");
|
|
@@ -3866,9 +4365,9 @@ var updateEmailHandler = async (c) => {
|
|
|
3866
4365
|
const resolvedTenantId = ensureTenantId(config, tenantId);
|
|
3867
4366
|
if (user.email && session?.id) {
|
|
3868
4367
|
await database.delete(sessionsInIam).where(
|
|
3869
|
-
|
|
3870
|
-
|
|
3871
|
-
|
|
4368
|
+
and28(
|
|
4369
|
+
eq29(sessionsInIam.tenantId, resolvedTenantId),
|
|
4370
|
+
eq29(sessionsInIam.userId, userId),
|
|
3872
4371
|
ne2(sessionsInIam.id, session.id)
|
|
3873
4372
|
)
|
|
3874
4373
|
);
|
|
@@ -3878,7 +4377,7 @@ var updateEmailHandler = async (c) => {
|
|
|
3878
4377
|
emailVerified: true,
|
|
3879
4378
|
updatedAt: sql15`CURRENT_TIMESTAMP`
|
|
3880
4379
|
}).where(
|
|
3881
|
-
|
|
4380
|
+
and28(eq29(usersInIam.id, userId), eq29(usersInIam.tenantId, resolvedTenantId))
|
|
3882
4381
|
).returning({
|
|
3883
4382
|
id: usersInIam.id,
|
|
3884
4383
|
tenantId: usersInIam.tenantId,
|
|
@@ -3895,25 +4394,25 @@ var updateEmailHandler = async (c) => {
|
|
|
3895
4394
|
return c.json({ error: "User not found" }, 404);
|
|
3896
4395
|
}
|
|
3897
4396
|
await database.update(accountChangesInIam).set({ status: "applied" }).where(
|
|
3898
|
-
|
|
3899
|
-
|
|
3900
|
-
|
|
3901
|
-
|
|
3902
|
-
|
|
4397
|
+
and28(
|
|
4398
|
+
eq29(accountChangesInIam.tenantId, resolvedTenantId),
|
|
4399
|
+
eq29(accountChangesInIam.userId, userId),
|
|
4400
|
+
eq29(accountChangesInIam.changeType, "email"),
|
|
4401
|
+
eq29(accountChangesInIam.newEmail, body.email)
|
|
3903
4402
|
)
|
|
3904
4403
|
);
|
|
3905
4404
|
await database.update(accountsInIam).set({ providerAccountId: body.email }).where(
|
|
3906
|
-
|
|
3907
|
-
|
|
3908
|
-
|
|
3909
|
-
|
|
4405
|
+
and28(
|
|
4406
|
+
eq29(accountsInIam.tenantId, resolvedTenantId),
|
|
4407
|
+
eq29(accountsInIam.userId, userId),
|
|
4408
|
+
eq29(accountsInIam.provider, "credentials")
|
|
3910
4409
|
)
|
|
3911
4410
|
);
|
|
3912
4411
|
return c.json({ user: normalizeUser(updatedUser) }, 200);
|
|
3913
4412
|
};
|
|
3914
4413
|
|
|
3915
4414
|
// src/routes/profile/handler/update-phone.ts
|
|
3916
|
-
import { and as
|
|
4415
|
+
import { and as and29, eq as eq30, ne as ne3, sql as sql16 } from "drizzle-orm";
|
|
3917
4416
|
var updatePhoneHandler = async (c) => {
|
|
3918
4417
|
const body = c.req.valid("json");
|
|
3919
4418
|
const config = c.get("config");
|
|
@@ -3935,9 +4434,9 @@ var updatePhoneHandler = async (c) => {
|
|
|
3935
4434
|
}
|
|
3936
4435
|
if (user.phone && session?.id) {
|
|
3937
4436
|
await database.delete(sessionsInIam).where(
|
|
3938
|
-
|
|
3939
|
-
|
|
3940
|
-
|
|
4437
|
+
and29(
|
|
4438
|
+
eq30(sessionsInIam.tenantId, resolvedTenantId),
|
|
4439
|
+
eq30(sessionsInIam.userId, userId),
|
|
3941
4440
|
ne3(sessionsInIam.id, session.id)
|
|
3942
4441
|
)
|
|
3943
4442
|
);
|
|
@@ -3947,7 +4446,7 @@ var updatePhoneHandler = async (c) => {
|
|
|
3947
4446
|
phoneVerified: true,
|
|
3948
4447
|
updatedAt: sql16`CURRENT_TIMESTAMP`
|
|
3949
4448
|
}).where(
|
|
3950
|
-
|
|
4449
|
+
and29(eq30(usersInIam.id, userId), eq30(usersInIam.tenantId, resolvedTenantId))
|
|
3951
4450
|
).returning({
|
|
3952
4451
|
id: usersInIam.id,
|
|
3953
4452
|
tenantId: usersInIam.tenantId,
|
|
@@ -3964,18 +4463,18 @@ var updatePhoneHandler = async (c) => {
|
|
|
3964
4463
|
return c.json({ error: "User not found" }, 404);
|
|
3965
4464
|
}
|
|
3966
4465
|
await database.update(accountChangesInIam).set({ status: "applied" }).where(
|
|
3967
|
-
|
|
3968
|
-
|
|
3969
|
-
|
|
3970
|
-
|
|
3971
|
-
|
|
4466
|
+
and29(
|
|
4467
|
+
eq30(accountChangesInIam.tenantId, resolvedTenantId),
|
|
4468
|
+
eq30(accountChangesInIam.userId, userId),
|
|
4469
|
+
eq30(accountChangesInIam.changeType, "phone"),
|
|
4470
|
+
eq30(accountChangesInIam.newPhone, body.phone)
|
|
3972
4471
|
)
|
|
3973
4472
|
);
|
|
3974
4473
|
await database.update(accountsInIam).set({ providerAccountId: body.phone }).where(
|
|
3975
|
-
|
|
3976
|
-
|
|
3977
|
-
|
|
3978
|
-
|
|
4474
|
+
and29(
|
|
4475
|
+
eq30(accountsInIam.tenantId, resolvedTenantId),
|
|
4476
|
+
eq30(accountsInIam.userId, userId),
|
|
4477
|
+
eq30(accountsInIam.provider, "credentials")
|
|
3979
4478
|
)
|
|
3980
4479
|
);
|
|
3981
4480
|
return c.json({ user: normalizeUser(updatedUser) }, 200);
|
|
@@ -4225,41 +4724,41 @@ var assignRolePermissionHandler = async (c) => {
|
|
|
4225
4724
|
};
|
|
4226
4725
|
|
|
4227
4726
|
// src/routes/role-permissions/handler/list-role-permissions.ts
|
|
4228
|
-
import { and as
|
|
4727
|
+
import { and as and30, eq as eq31 } from "drizzle-orm";
|
|
4229
4728
|
var listRolePermissionsHandler = async (c) => {
|
|
4230
4729
|
const query = c.req.valid("query");
|
|
4231
4730
|
const database = c.get("database");
|
|
4232
4731
|
const tenantId = c.get("tenantId");
|
|
4233
|
-
const conditions = [
|
|
4732
|
+
const conditions = [eq31(rolePermissionsInIam.tenantId, tenantId)];
|
|
4234
4733
|
if (query.roleId) {
|
|
4235
|
-
conditions.push(
|
|
4734
|
+
conditions.push(eq31(rolePermissionsInIam.roleId, query.roleId));
|
|
4236
4735
|
}
|
|
4237
4736
|
if (query.permissionId) {
|
|
4238
|
-
conditions.push(
|
|
4737
|
+
conditions.push(eq31(rolePermissionsInIam.permissionId, query.permissionId));
|
|
4239
4738
|
}
|
|
4240
|
-
const rolePermissions = await database.select().from(rolePermissionsInIam).where(
|
|
4739
|
+
const rolePermissions = await database.select().from(rolePermissionsInIam).where(and30(...conditions));
|
|
4241
4740
|
return c.json({ rolePermissions }, 200);
|
|
4242
4741
|
};
|
|
4243
4742
|
|
|
4244
4743
|
// src/routes/role-permissions/handler/revoke-role-permission.ts
|
|
4245
|
-
import { and as
|
|
4744
|
+
import { and as and31, eq as eq32 } from "drizzle-orm";
|
|
4246
4745
|
var revokeRolePermissionHandler = async (c) => {
|
|
4247
4746
|
const { id } = c.req.valid("param");
|
|
4248
4747
|
const database = c.get("database");
|
|
4249
4748
|
const tenantId = c.get("tenantId");
|
|
4250
4749
|
const [existing] = await database.select().from(rolePermissionsInIam).where(
|
|
4251
|
-
|
|
4252
|
-
|
|
4253
|
-
|
|
4750
|
+
and31(
|
|
4751
|
+
eq32(rolePermissionsInIam.id, id),
|
|
4752
|
+
eq32(rolePermissionsInIam.tenantId, tenantId)
|
|
4254
4753
|
)
|
|
4255
4754
|
).limit(1);
|
|
4256
4755
|
if (!existing) {
|
|
4257
4756
|
return c.json({ error: "Role permission not found" }, 404);
|
|
4258
4757
|
}
|
|
4259
4758
|
await database.delete(rolePermissionsInIam).where(
|
|
4260
|
-
|
|
4261
|
-
|
|
4262
|
-
|
|
4759
|
+
and31(
|
|
4760
|
+
eq32(rolePermissionsInIam.id, id),
|
|
4761
|
+
eq32(rolePermissionsInIam.tenantId, tenantId)
|
|
4263
4762
|
)
|
|
4264
4763
|
);
|
|
4265
4764
|
return c.json({ message: "Permission revoked from role" }, 200);
|
|
@@ -4383,81 +4882,571 @@ var role_permissions_route_default = rolePermissionRoutes;
|
|
|
4383
4882
|
// src/routes/roles/roles.route.ts
|
|
4384
4883
|
import { createRoute as createRoute9, OpenAPIHono as OpenAPIHono9 } from "@hono/zod-openapi";
|
|
4385
4884
|
|
|
4885
|
+
// src/routes/roles/handler/assign-role-permissions.ts
|
|
4886
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
4887
|
+
import { and as and32, eq as eq33, inArray as inArray3 } from "drizzle-orm";
|
|
4888
|
+
var assignRolePermissionsHandler = async (c) => {
|
|
4889
|
+
const { id } = c.req.valid("param");
|
|
4890
|
+
const body = c.req.valid("json");
|
|
4891
|
+
const database = c.get("database");
|
|
4892
|
+
const config = c.get("config");
|
|
4893
|
+
const tenantId = c.get("tenantId");
|
|
4894
|
+
const resolvedTenantId = ensureTenantId(config, tenantId);
|
|
4895
|
+
const permissionIds = [...new Set(body.permissionIds)];
|
|
4896
|
+
const [role] = await database.select({
|
|
4897
|
+
id: rolesInIam.id,
|
|
4898
|
+
isEditable: rolesInIam.isEditable
|
|
4899
|
+
}).from(rolesInIam).where(and32(eq33(rolesInIam.id, id), eq33(rolesInIam.tenantId, tenantId))).limit(1);
|
|
4900
|
+
if (!role) {
|
|
4901
|
+
return c.json({ error: "Role not found" }, 404);
|
|
4902
|
+
}
|
|
4903
|
+
if (!role.isEditable) {
|
|
4904
|
+
return c.json({ error: "Role is not editable" }, 403);
|
|
4905
|
+
}
|
|
4906
|
+
await seedPermissions({
|
|
4907
|
+
database,
|
|
4908
|
+
permissions: config.permissions
|
|
4909
|
+
});
|
|
4910
|
+
const existingPermissions = permissionIds.length ? await database.select({ id: permissionsInIam.id }).from(permissionsInIam).where(inArray3(permissionsInIam.id, permissionIds)) : [];
|
|
4911
|
+
const existingPermissionIds = new Set(
|
|
4912
|
+
existingPermissions.map((permission) => permission.id)
|
|
4913
|
+
);
|
|
4914
|
+
const missingPermissionIds = permissionIds.filter(
|
|
4915
|
+
(permissionId) => !existingPermissionIds.has(permissionId)
|
|
4916
|
+
);
|
|
4917
|
+
if (missingPermissionIds.length) {
|
|
4918
|
+
return c.json(
|
|
4919
|
+
{ error: `Unknown permissions: ${missingPermissionIds.join(", ")}` },
|
|
4920
|
+
400
|
|
4921
|
+
);
|
|
4922
|
+
}
|
|
4923
|
+
const created = permissionIds.length ? await database.insert(rolePermissionsInIam).values(
|
|
4924
|
+
permissionIds.map((permissionId) => ({
|
|
4925
|
+
id: randomUUID2(),
|
|
4926
|
+
tenantId: resolvedTenantId,
|
|
4927
|
+
roleId: id,
|
|
4928
|
+
permissionId
|
|
4929
|
+
}))
|
|
4930
|
+
).onConflictDoNothing({
|
|
4931
|
+
target: [
|
|
4932
|
+
rolePermissionsInIam.tenantId,
|
|
4933
|
+
rolePermissionsInIam.permissionId,
|
|
4934
|
+
rolePermissionsInIam.roleId
|
|
4935
|
+
]
|
|
4936
|
+
}).returning({ permissionId: rolePermissionsInIam.permissionId }) : [];
|
|
4937
|
+
return c.json(
|
|
4938
|
+
{
|
|
4939
|
+
created: created.length,
|
|
4940
|
+
skipped: permissionIds.length - created.length
|
|
4941
|
+
},
|
|
4942
|
+
200
|
|
4943
|
+
);
|
|
4944
|
+
};
|
|
4945
|
+
|
|
4946
|
+
// src/routes/roles/handler/assign-role-users.ts
|
|
4947
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
4948
|
+
import { and as and33, eq as eq34, inArray as inArray4 } from "drizzle-orm";
|
|
4949
|
+
var assignRoleUsersHandler = async (c) => {
|
|
4950
|
+
const { id } = c.req.valid("param");
|
|
4951
|
+
const body = c.req.valid("json");
|
|
4952
|
+
const database = c.get("database");
|
|
4953
|
+
const config = c.get("config");
|
|
4954
|
+
const tenantId = c.get("tenantId");
|
|
4955
|
+
const resolvedTenantId = ensureTenantId(config, tenantId);
|
|
4956
|
+
const userIds = [...new Set(body.userIds)];
|
|
4957
|
+
const [role] = await database.select({
|
|
4958
|
+
id: rolesInIam.id,
|
|
4959
|
+
isEditable: rolesInIam.isEditable
|
|
4960
|
+
}).from(rolesInIam).where(and33(eq34(rolesInIam.id, id), eq34(rolesInIam.tenantId, tenantId))).limit(1);
|
|
4961
|
+
if (!role) {
|
|
4962
|
+
return c.json({ error: "Role not found" }, 404);
|
|
4963
|
+
}
|
|
4964
|
+
if (!role.isEditable) {
|
|
4965
|
+
return c.json({ error: "Role is not editable" }, 403);
|
|
4966
|
+
}
|
|
4967
|
+
const existingUsers = userIds.length ? await database.select({ id: usersInIam.id }).from(usersInIam).where(
|
|
4968
|
+
and33(
|
|
4969
|
+
eq34(usersInIam.tenantId, tenantId),
|
|
4970
|
+
inArray4(usersInIam.id, userIds)
|
|
4971
|
+
)
|
|
4972
|
+
) : [];
|
|
4973
|
+
const existingUserIds = new Set(existingUsers.map((user) => user.id));
|
|
4974
|
+
const missingUserIds = userIds.filter(
|
|
4975
|
+
(userId) => !existingUserIds.has(userId)
|
|
4976
|
+
);
|
|
4977
|
+
if (missingUserIds.length) {
|
|
4978
|
+
return c.json(
|
|
4979
|
+
{ error: `Unknown users: ${missingUserIds.join(", ")}` },
|
|
4980
|
+
400
|
|
4981
|
+
);
|
|
4982
|
+
}
|
|
4983
|
+
const created = userIds.length ? await database.insert(userRolesInIam).values(
|
|
4984
|
+
userIds.map((userId) => ({
|
|
4985
|
+
id: randomUUID3(),
|
|
4986
|
+
tenantId: resolvedTenantId,
|
|
4987
|
+
roleId: id,
|
|
4988
|
+
userId
|
|
4989
|
+
}))
|
|
4990
|
+
).onConflictDoNothing({
|
|
4991
|
+
target: [
|
|
4992
|
+
userRolesInIam.tenantId,
|
|
4993
|
+
userRolesInIam.userId,
|
|
4994
|
+
userRolesInIam.roleId
|
|
4995
|
+
]
|
|
4996
|
+
}).returning({ userId: userRolesInIam.userId }) : [];
|
|
4997
|
+
return c.json(
|
|
4998
|
+
{
|
|
4999
|
+
created: created.length,
|
|
5000
|
+
skipped: userIds.length - created.length
|
|
5001
|
+
},
|
|
5002
|
+
200
|
|
5003
|
+
);
|
|
5004
|
+
};
|
|
5005
|
+
|
|
4386
5006
|
// src/routes/roles/handler/create-role.ts
|
|
4387
|
-
import { randomUUID } from "crypto";
|
|
5007
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
4388
5008
|
var createRoleHandler = async (c) => {
|
|
4389
5009
|
const body = c.req.valid("json");
|
|
4390
5010
|
const config = c.get("config");
|
|
4391
5011
|
const database = c.get("database");
|
|
4392
5012
|
const tenantId = c.get("tenantId");
|
|
4393
5013
|
const resolvedTenantId = ensureTenantId(config, tenantId);
|
|
4394
|
-
const
|
|
4395
|
-
|
|
4396
|
-
|
|
4397
|
-
|
|
4398
|
-
|
|
4399
|
-
|
|
4400
|
-
|
|
4401
|
-
|
|
5014
|
+
const permissionIds = [...new Set(body.permissionIds ?? [])];
|
|
5015
|
+
const role = await withTransaction(database, async (tx) => {
|
|
5016
|
+
await seedPermissions({
|
|
5017
|
+
database: tx,
|
|
5018
|
+
permissions: config.permissions
|
|
5019
|
+
});
|
|
5020
|
+
const [createdRole] = await tx.insert(rolesInIam).values({
|
|
5021
|
+
id: randomUUID4(),
|
|
5022
|
+
tenantId: resolvedTenantId,
|
|
5023
|
+
name: body.name ?? { en: body.code },
|
|
5024
|
+
description: body.description ?? { en: body.code },
|
|
5025
|
+
code: body.code,
|
|
5026
|
+
isSystem: body.isSystem ?? false,
|
|
5027
|
+
isEditable: body.isEditable ?? true,
|
|
5028
|
+
isDeletable: body.isDeletable ?? true
|
|
5029
|
+
}).returning();
|
|
5030
|
+
if (body.permissionIds !== void 0) {
|
|
5031
|
+
await syncRolePermissions({
|
|
5032
|
+
database: tx,
|
|
5033
|
+
tenantId: resolvedTenantId,
|
|
5034
|
+
roleId: createdRole.id,
|
|
5035
|
+
permissionIds
|
|
5036
|
+
});
|
|
5037
|
+
}
|
|
5038
|
+
return createdRole;
|
|
5039
|
+
});
|
|
5040
|
+
return c.json(
|
|
5041
|
+
{
|
|
5042
|
+
role: {
|
|
5043
|
+
...role,
|
|
5044
|
+
permissionIds,
|
|
5045
|
+
permissionCount: permissionIds.length
|
|
5046
|
+
}
|
|
5047
|
+
},
|
|
5048
|
+
201
|
|
5049
|
+
);
|
|
4402
5050
|
};
|
|
4403
5051
|
|
|
4404
5052
|
// src/routes/roles/handler/delete-role.ts
|
|
4405
|
-
import { and as
|
|
5053
|
+
import { and as and34, eq as eq35 } from "drizzle-orm";
|
|
4406
5054
|
var deleteRoleHandler = async (c) => {
|
|
4407
5055
|
const { id } = c.req.valid("param");
|
|
4408
5056
|
const database = c.get("database");
|
|
4409
5057
|
const tenantId = c.get("tenantId");
|
|
4410
|
-
const [existing] = await database.select().from(rolesInIam).where(
|
|
5058
|
+
const [existing] = await database.select().from(rolesInIam).where(and34(eq35(rolesInIam.id, id), eq35(rolesInIam.tenantId, tenantId))).limit(1);
|
|
4411
5059
|
if (!existing) {
|
|
4412
5060
|
return c.json({ error: "Role not found" }, 404);
|
|
4413
5061
|
}
|
|
4414
|
-
|
|
5062
|
+
if (!existing.isDeletable) {
|
|
5063
|
+
return c.json({ error: "Role is not deletable" }, 403);
|
|
5064
|
+
}
|
|
5065
|
+
await database.delete(rolesInIam).where(and34(eq35(rolesInIam.id, id), eq35(rolesInIam.tenantId, tenantId)));
|
|
4415
5066
|
return c.json({ message: "Role deleted" }, 200);
|
|
4416
5067
|
};
|
|
4417
5068
|
|
|
4418
5069
|
// src/routes/roles/handler/get-role.ts
|
|
4419
|
-
import { and as
|
|
5070
|
+
import { and as and35, eq as eq36, sql as sql17 } from "drizzle-orm";
|
|
4420
5071
|
var getRoleHandler = async (c) => {
|
|
4421
5072
|
const { id } = c.req.valid("param");
|
|
4422
5073
|
const database = c.get("database");
|
|
4423
5074
|
const tenantId = c.get("tenantId");
|
|
4424
|
-
const [role] = await database.select(
|
|
5075
|
+
const [role] = await database.select({
|
|
5076
|
+
id: rolesInIam.id,
|
|
5077
|
+
tenantId: rolesInIam.tenantId,
|
|
5078
|
+
createdAt: rolesInIam.createdAt,
|
|
5079
|
+
updatedAt: rolesInIam.updatedAt,
|
|
5080
|
+
name: rolesInIam.name,
|
|
5081
|
+
description: rolesInIam.description,
|
|
5082
|
+
code: rolesInIam.code,
|
|
5083
|
+
isSystem: rolesInIam.isSystem,
|
|
5084
|
+
isEditable: rolesInIam.isEditable,
|
|
5085
|
+
isDeletable: rolesInIam.isDeletable,
|
|
5086
|
+
permissionIds: sql17`COALESCE(
|
|
5087
|
+
(
|
|
5088
|
+
select array_to_json(array_agg(${rolePermissionsInIam.permissionId}))
|
|
5089
|
+
from ${rolePermissionsInIam}
|
|
5090
|
+
where ${and35(
|
|
5091
|
+
eq36(rolePermissionsInIam.tenantId, tenantId),
|
|
5092
|
+
eq36(rolePermissionsInIam.roleId, rolesInIam.id)
|
|
5093
|
+
)}
|
|
5094
|
+
),
|
|
5095
|
+
'[]'::json
|
|
5096
|
+
)`,
|
|
5097
|
+
permissionCount: sql17`(
|
|
5098
|
+
select count(*)::int
|
|
5099
|
+
from ${rolePermissionsInIam}
|
|
5100
|
+
where ${and35(
|
|
5101
|
+
eq36(rolePermissionsInIam.tenantId, tenantId),
|
|
5102
|
+
eq36(rolePermissionsInIam.roleId, rolesInIam.id)
|
|
5103
|
+
)}
|
|
5104
|
+
)`,
|
|
5105
|
+
userCount: sql17`(
|
|
5106
|
+
select count(*)::int
|
|
5107
|
+
from ${userRolesInIam}
|
|
5108
|
+
where ${and35(
|
|
5109
|
+
eq36(userRolesInIam.tenantId, tenantId),
|
|
5110
|
+
eq36(userRolesInIam.roleId, rolesInIam.id)
|
|
5111
|
+
)}
|
|
5112
|
+
)`
|
|
5113
|
+
}).from(rolesInIam).where(and35(eq36(rolesInIam.id, id), eq36(rolesInIam.tenantId, tenantId))).limit(1);
|
|
4425
5114
|
if (!role) {
|
|
4426
5115
|
return c.json({ error: "Role not found" }, 404);
|
|
4427
5116
|
}
|
|
4428
|
-
return c.json(
|
|
5117
|
+
return c.json(
|
|
5118
|
+
{
|
|
5119
|
+
role: {
|
|
5120
|
+
...role,
|
|
5121
|
+
permissionIds: role.permissionIds ?? void 0
|
|
5122
|
+
}
|
|
5123
|
+
},
|
|
5124
|
+
200
|
|
5125
|
+
);
|
|
4429
5126
|
};
|
|
4430
5127
|
|
|
4431
|
-
// src/routes/roles/handler/list-
|
|
4432
|
-
import { and as
|
|
4433
|
-
var
|
|
5128
|
+
// src/routes/roles/handler/list-role-permissions.ts
|
|
5129
|
+
import { and as and36, eq as eq37 } from "drizzle-orm";
|
|
5130
|
+
var listRolePermissionsHandler2 = async (c) => {
|
|
5131
|
+
const { id } = c.req.valid("param");
|
|
4434
5132
|
const query = c.req.valid("query");
|
|
4435
5133
|
const database = c.get("database");
|
|
5134
|
+
const config = c.get("config");
|
|
4436
5135
|
const tenantId = c.get("tenantId");
|
|
5136
|
+
const [role] = await database.select({ id: rolesInIam.id }).from(rolesInIam).where(and36(eq37(rolesInIam.id, id), eq37(rolesInIam.tenantId, tenantId))).limit(1);
|
|
5137
|
+
if (!role) {
|
|
5138
|
+
return c.json({ error: "Role not found" }, 404);
|
|
5139
|
+
}
|
|
4437
5140
|
const page = query.page || 1;
|
|
4438
5141
|
const limit = query.limit || 20;
|
|
4439
5142
|
const offset = (page - 1) * limit;
|
|
4440
|
-
const
|
|
4441
|
-
|
|
4442
|
-
|
|
4443
|
-
|
|
5143
|
+
const [assignedPermissions, permissionCatalog] = await Promise.all([
|
|
5144
|
+
database.select({ permissionId: rolePermissionsInIam.permissionId }).from(rolePermissionsInIam).where(
|
|
5145
|
+
and36(
|
|
5146
|
+
eq37(rolePermissionsInIam.tenantId, tenantId),
|
|
5147
|
+
eq37(rolePermissionsInIam.roleId, id)
|
|
5148
|
+
)
|
|
5149
|
+
).then((rows) => new Set(rows.map((row) => row.permissionId))),
|
|
5150
|
+
getMergedPermissionCatalog({
|
|
5151
|
+
database,
|
|
5152
|
+
permissions: config.permissions
|
|
5153
|
+
})
|
|
4444
5154
|
]);
|
|
4445
|
-
const
|
|
4446
|
-
|
|
5155
|
+
const sortedPermissions = filterAndSortPermissions(
|
|
5156
|
+
permissionCatalog.filter(
|
|
5157
|
+
(permission) => assignedPermissions.has(permission.id)
|
|
5158
|
+
),
|
|
5159
|
+
query
|
|
5160
|
+
);
|
|
5161
|
+
const total = sortedPermissions.length;
|
|
5162
|
+
const permissions = sortedPermissions.slice(offset, offset + limit);
|
|
5163
|
+
return c.json({ permissions, total, page, limit }, 200);
|
|
4447
5164
|
};
|
|
4448
5165
|
|
|
4449
|
-
// src/routes/roles/handler/
|
|
4450
|
-
import { and as
|
|
4451
|
-
var
|
|
4452
|
-
|
|
4453
|
-
|
|
4454
|
-
|
|
4455
|
-
|
|
4456
|
-
|
|
4457
|
-
|
|
4458
|
-
|
|
4459
|
-
}
|
|
4460
|
-
|
|
5166
|
+
// src/routes/roles/handler/list-role-users.ts
|
|
5167
|
+
import { and as and37, asc as asc2, desc as desc2, eq as eq38, ilike, or, sql as sql18 } from "drizzle-orm";
|
|
5168
|
+
var sortColumnMap = {
|
|
5169
|
+
createdAt: usersInIam.createdAt,
|
|
5170
|
+
updatedAt: usersInIam.updatedAt,
|
|
5171
|
+
fullName: usersInIam.fullName,
|
|
5172
|
+
handle: usersInIam.handle,
|
|
5173
|
+
lastSignInAt: usersInIam.lastSignInAt
|
|
5174
|
+
};
|
|
5175
|
+
function getRoleUserSearchCondition(search, filter) {
|
|
5176
|
+
const term = `%${search}%`;
|
|
5177
|
+
if (filter === "fullName") {
|
|
5178
|
+
return ilike(usersInIam.fullName, term);
|
|
5179
|
+
}
|
|
5180
|
+
if (filter === "email") {
|
|
5181
|
+
return ilike(usersInIam.email, term);
|
|
5182
|
+
}
|
|
5183
|
+
if (filter === "phone") {
|
|
5184
|
+
return ilike(usersInIam.phone, term);
|
|
5185
|
+
}
|
|
5186
|
+
if (filter === "handle") {
|
|
5187
|
+
return ilike(usersInIam.handle, term);
|
|
5188
|
+
}
|
|
5189
|
+
return or(
|
|
5190
|
+
ilike(usersInIam.fullName, term),
|
|
5191
|
+
ilike(usersInIam.email, term),
|
|
5192
|
+
ilike(usersInIam.phone, term),
|
|
5193
|
+
ilike(usersInIam.handle, term)
|
|
5194
|
+
);
|
|
5195
|
+
}
|
|
5196
|
+
async function ensureRoleExists({
|
|
5197
|
+
database,
|
|
5198
|
+
id,
|
|
5199
|
+
tenantId
|
|
5200
|
+
}) {
|
|
5201
|
+
const [role] = await database.select({ id: rolesInIam.id }).from(rolesInIam).where(and37(eq38(rolesInIam.id, id), eq38(rolesInIam.tenantId, tenantId))).limit(1);
|
|
5202
|
+
return role;
|
|
5203
|
+
}
|
|
5204
|
+
var listRoleUsersHandler = async (c) => {
|
|
5205
|
+
const { id } = c.req.valid("param");
|
|
5206
|
+
const query = c.req.valid("query");
|
|
5207
|
+
const database = c.get("database");
|
|
5208
|
+
const tenantId = c.get("tenantId");
|
|
5209
|
+
const role = await ensureRoleExists({ database, id, tenantId });
|
|
5210
|
+
if (!role) {
|
|
5211
|
+
return c.json({ error: "Role not found" }, 404);
|
|
5212
|
+
}
|
|
5213
|
+
const page = query.page || 1;
|
|
5214
|
+
const limit = query.limit || 20;
|
|
5215
|
+
const offset = (page - 1) * limit;
|
|
5216
|
+
const conditions = [
|
|
5217
|
+
eq38(userRolesInIam.tenantId, tenantId),
|
|
5218
|
+
eq38(userRolesInIam.roleId, id),
|
|
5219
|
+
eq38(usersInIam.tenantId, tenantId)
|
|
5220
|
+
];
|
|
5221
|
+
if (query.search) {
|
|
5222
|
+
const searchCondition = getRoleUserSearchCondition(
|
|
5223
|
+
query.search,
|
|
5224
|
+
query.filter
|
|
5225
|
+
);
|
|
5226
|
+
if (searchCondition) {
|
|
5227
|
+
conditions.push(searchCondition);
|
|
5228
|
+
}
|
|
5229
|
+
}
|
|
5230
|
+
const orderDir = query.order === "asc" ? asc2 : desc2;
|
|
5231
|
+
const sortCol = query.sort && sortColumnMap[query.sort] ? sortColumnMap[query.sort] : usersInIam.createdAt;
|
|
5232
|
+
const [users, totalResult] = await Promise.all([
|
|
5233
|
+
database.select({
|
|
5234
|
+
id: usersInIam.id,
|
|
5235
|
+
tenantId: usersInIam.tenantId,
|
|
5236
|
+
fullName: usersInIam.fullName,
|
|
5237
|
+
email: usersInIam.email,
|
|
5238
|
+
phone: usersInIam.phone,
|
|
5239
|
+
handle: usersInIam.handle,
|
|
5240
|
+
image: usersInIam.image,
|
|
5241
|
+
emailVerified: usersInIam.emailVerified,
|
|
5242
|
+
phoneVerified: usersInIam.phoneVerified,
|
|
5243
|
+
lastSignInAt: usersInIam.lastSignInAt
|
|
5244
|
+
}).from(userRolesInIam).innerJoin(usersInIam, eq38(usersInIam.id, userRolesInIam.userId)).where(and37(...conditions)).orderBy(orderDir(sortCol)).limit(limit).offset(offset),
|
|
5245
|
+
database.select({ count: sql18`count(*)` }).from(userRolesInIam).innerJoin(usersInIam, eq38(usersInIam.id, userRolesInIam.userId)).where(and37(...conditions))
|
|
5246
|
+
]);
|
|
5247
|
+
const total = Number(totalResult[0]?.count || 0);
|
|
5248
|
+
return c.json(
|
|
5249
|
+
{
|
|
5250
|
+
users: users.map((user) => ({
|
|
5251
|
+
...user,
|
|
5252
|
+
roles: null
|
|
5253
|
+
})),
|
|
5254
|
+
total,
|
|
5255
|
+
page,
|
|
5256
|
+
limit
|
|
5257
|
+
},
|
|
5258
|
+
200
|
|
5259
|
+
);
|
|
5260
|
+
};
|
|
5261
|
+
|
|
5262
|
+
// src/routes/roles/handler/list-roles.ts
|
|
5263
|
+
import { and as and38, asc as asc3, desc as desc3, eq as eq39, ilike as ilike2, or as or2, sql as sql19 } from "drizzle-orm";
|
|
5264
|
+
var sortColumnMap2 = {
|
|
5265
|
+
createdAt: rolesInIam.createdAt,
|
|
5266
|
+
updatedAt: rolesInIam.updatedAt,
|
|
5267
|
+
code: rolesInIam.code
|
|
5268
|
+
};
|
|
5269
|
+
var listRolesHandler = async (c) => {
|
|
5270
|
+
const query = c.req.valid("query");
|
|
5271
|
+
const database = c.get("database");
|
|
5272
|
+
const tenantId = c.get("tenantId");
|
|
5273
|
+
const page = query.page || 1;
|
|
5274
|
+
const limit = query.limit || 20;
|
|
5275
|
+
const offset = (page - 1) * limit;
|
|
5276
|
+
const conditions = [eq39(rolesInIam.tenantId, tenantId)];
|
|
5277
|
+
if (query.search) {
|
|
5278
|
+
const term = `%${query.search}%`;
|
|
5279
|
+
const searchCondition = query.filter === "code" ? ilike2(rolesInIam.code, term) : or2(
|
|
5280
|
+
ilike2(rolesInIam.code, term),
|
|
5281
|
+
sql19`cast(${rolesInIam.name} as text) ilike ${term}`,
|
|
5282
|
+
sql19`cast(${rolesInIam.description} as text) ilike ${term}`
|
|
5283
|
+
);
|
|
5284
|
+
if (searchCondition) {
|
|
5285
|
+
conditions.push(searchCondition);
|
|
5286
|
+
}
|
|
5287
|
+
}
|
|
5288
|
+
const orderDir = query.order === "asc" ? asc3 : desc3;
|
|
5289
|
+
const sortCol = query.sort && sortColumnMap2[query.sort] ? sortColumnMap2[query.sort] : rolesInIam.createdAt;
|
|
5290
|
+
const [roles, totalResult] = await Promise.all([
|
|
5291
|
+
database.select({
|
|
5292
|
+
id: rolesInIam.id,
|
|
5293
|
+
tenantId: rolesInIam.tenantId,
|
|
5294
|
+
createdAt: rolesInIam.createdAt,
|
|
5295
|
+
updatedAt: rolesInIam.updatedAt,
|
|
5296
|
+
name: rolesInIam.name,
|
|
5297
|
+
description: rolesInIam.description,
|
|
5298
|
+
code: rolesInIam.code,
|
|
5299
|
+
isSystem: rolesInIam.isSystem,
|
|
5300
|
+
isEditable: rolesInIam.isEditable,
|
|
5301
|
+
isDeletable: rolesInIam.isDeletable,
|
|
5302
|
+
permissionCount: sql19`(
|
|
5303
|
+
select count(*)::int
|
|
5304
|
+
from ${rolePermissionsInIam}
|
|
5305
|
+
where ${and38(
|
|
5306
|
+
eq39(rolePermissionsInIam.tenantId, tenantId),
|
|
5307
|
+
eq39(rolePermissionsInIam.roleId, rolesInIam.id)
|
|
5308
|
+
)}
|
|
5309
|
+
)`,
|
|
5310
|
+
userCount: sql19`(
|
|
5311
|
+
select count(*)::int
|
|
5312
|
+
from ${userRolesInIam}
|
|
5313
|
+
where ${and38(
|
|
5314
|
+
eq39(userRolesInIam.tenantId, tenantId),
|
|
5315
|
+
eq39(userRolesInIam.roleId, rolesInIam.id)
|
|
5316
|
+
)}
|
|
5317
|
+
)`
|
|
5318
|
+
}).from(rolesInIam).where(and38(...conditions)).orderBy(orderDir(sortCol)).limit(limit).offset(offset),
|
|
5319
|
+
database.select({ count: sql19`count(*)` }).from(rolesInIam).where(and38(...conditions))
|
|
5320
|
+
]);
|
|
5321
|
+
const total = Number(totalResult[0]?.count || 0);
|
|
5322
|
+
return c.json({ roles, total, page, limit });
|
|
5323
|
+
};
|
|
5324
|
+
|
|
5325
|
+
// src/routes/roles/handler/revoke-role-permission.ts
|
|
5326
|
+
import { and as and39, eq as eq40 } from "drizzle-orm";
|
|
5327
|
+
var revokeRolePermissionHandler2 = async (c) => {
|
|
5328
|
+
const { id, permissionId } = c.req.valid("param");
|
|
5329
|
+
const database = c.get("database");
|
|
5330
|
+
const tenantId = c.get("tenantId");
|
|
5331
|
+
const [role] = await database.select({
|
|
5332
|
+
id: rolesInIam.id,
|
|
5333
|
+
isEditable: rolesInIam.isEditable
|
|
5334
|
+
}).from(rolesInIam).where(and39(eq40(rolesInIam.id, id), eq40(rolesInIam.tenantId, tenantId))).limit(1);
|
|
5335
|
+
if (!role) {
|
|
5336
|
+
return c.json({ error: "Role not found" }, 404);
|
|
5337
|
+
}
|
|
5338
|
+
if (!role.isEditable) {
|
|
5339
|
+
return c.json({ error: "Role is not editable" }, 403);
|
|
5340
|
+
}
|
|
5341
|
+
const [deleted] = await database.delete(rolePermissionsInIam).where(
|
|
5342
|
+
and39(
|
|
5343
|
+
eq40(rolePermissionsInIam.tenantId, tenantId),
|
|
5344
|
+
eq40(rolePermissionsInIam.roleId, id),
|
|
5345
|
+
eq40(rolePermissionsInIam.permissionId, permissionId)
|
|
5346
|
+
)
|
|
5347
|
+
).returning({ id: rolePermissionsInIam.id });
|
|
5348
|
+
if (!deleted) {
|
|
5349
|
+
return c.json({ error: "Role permission not found" }, 404);
|
|
5350
|
+
}
|
|
5351
|
+
return c.json({ message: "Permission revoked from role" }, 200);
|
|
5352
|
+
};
|
|
5353
|
+
|
|
5354
|
+
// src/routes/roles/handler/revoke-role-user.ts
|
|
5355
|
+
import { and as and40, eq as eq41 } from "drizzle-orm";
|
|
5356
|
+
var revokeRoleUserHandler = async (c) => {
|
|
5357
|
+
const { id, userId } = c.req.valid("param");
|
|
5358
|
+
const database = c.get("database");
|
|
5359
|
+
const tenantId = c.get("tenantId");
|
|
5360
|
+
const [role] = await database.select({
|
|
5361
|
+
id: rolesInIam.id,
|
|
5362
|
+
isEditable: rolesInIam.isEditable
|
|
5363
|
+
}).from(rolesInIam).where(and40(eq41(rolesInIam.id, id), eq41(rolesInIam.tenantId, tenantId))).limit(1);
|
|
5364
|
+
if (!role) {
|
|
5365
|
+
return c.json({ error: "Role not found" }, 404);
|
|
5366
|
+
}
|
|
5367
|
+
if (!role.isEditable) {
|
|
5368
|
+
return c.json({ error: "Role is not editable" }, 403);
|
|
5369
|
+
}
|
|
5370
|
+
const [deleted] = await database.delete(userRolesInIam).where(
|
|
5371
|
+
and40(
|
|
5372
|
+
eq41(userRolesInIam.tenantId, tenantId),
|
|
5373
|
+
eq41(userRolesInIam.roleId, id),
|
|
5374
|
+
eq41(userRolesInIam.userId, userId)
|
|
5375
|
+
)
|
|
5376
|
+
).returning({ id: userRolesInIam.id });
|
|
5377
|
+
if (!deleted) {
|
|
5378
|
+
return c.json({ error: "Role user not found" }, 404);
|
|
5379
|
+
}
|
|
5380
|
+
return c.json({ message: "User removed from role" }, 200);
|
|
5381
|
+
};
|
|
5382
|
+
|
|
5383
|
+
// src/routes/roles/handler/seed-roles.ts
|
|
5384
|
+
import { and as and41, eq as eq42, inArray as inArray5, sql as sql20 } from "drizzle-orm";
|
|
5385
|
+
var seedRolesHandler = async (c) => {
|
|
5386
|
+
const config = c.get("config");
|
|
5387
|
+
const database = c.get("database");
|
|
5388
|
+
const tenantId = c.get("tenantId");
|
|
5389
|
+
const resolvedTenantId = ensureTenantId(config, tenantId);
|
|
5390
|
+
if (!config.roles?.length) {
|
|
5391
|
+
return c.json({ error: "No roles configured for seeding" }, 400);
|
|
5392
|
+
}
|
|
5393
|
+
const rolesToSeed = config.roles;
|
|
5394
|
+
const seededRoles = await withTransaction(database, async (tx) => {
|
|
5395
|
+
await seedPermissions({
|
|
5396
|
+
database: tx,
|
|
5397
|
+
permissions: config.permissions
|
|
5398
|
+
});
|
|
5399
|
+
return seedRoles({
|
|
5400
|
+
database: tx,
|
|
5401
|
+
tenantId: resolvedTenantId,
|
|
5402
|
+
roles: rolesToSeed
|
|
5403
|
+
});
|
|
5404
|
+
});
|
|
5405
|
+
const permissionCountRows = seededRoles.length ? await database.select({
|
|
5406
|
+
roleId: rolePermissionsInIam.roleId,
|
|
5407
|
+
count: sql20`count(*)::int`
|
|
5408
|
+
}).from(rolePermissionsInIam).where(
|
|
5409
|
+
and41(
|
|
5410
|
+
eq42(rolePermissionsInIam.tenantId, resolvedTenantId),
|
|
5411
|
+
inArray5(
|
|
5412
|
+
rolePermissionsInIam.roleId,
|
|
5413
|
+
seededRoles.map((role) => role.id)
|
|
5414
|
+
)
|
|
5415
|
+
)
|
|
5416
|
+
).groupBy(rolePermissionsInIam.roleId) : [];
|
|
5417
|
+
const permissionCountByRoleId = new Map(
|
|
5418
|
+
permissionCountRows.map((row) => [row.roleId, row.count])
|
|
5419
|
+
);
|
|
5420
|
+
const roles = seededRoles.map((role) => ({
|
|
5421
|
+
...role,
|
|
5422
|
+
permissionCount: permissionCountByRoleId.get(role.id) ?? 0
|
|
5423
|
+
}));
|
|
5424
|
+
return c.json(
|
|
5425
|
+
{
|
|
5426
|
+
roles,
|
|
5427
|
+
total: roles.length
|
|
5428
|
+
},
|
|
5429
|
+
200
|
|
5430
|
+
);
|
|
5431
|
+
};
|
|
5432
|
+
|
|
5433
|
+
// src/routes/roles/handler/update-role.ts
|
|
5434
|
+
import { and as and42, eq as eq43, sql as sql21 } from "drizzle-orm";
|
|
5435
|
+
var updateRoleHandler = async (c) => {
|
|
5436
|
+
const { id } = c.req.valid("param");
|
|
5437
|
+
const body = c.req.valid("json");
|
|
5438
|
+
const database = c.get("database");
|
|
5439
|
+
const config = c.get("config");
|
|
5440
|
+
const tenantId = c.get("tenantId");
|
|
5441
|
+
const permissionIds = body.permissionIds === void 0 ? void 0 : [...new Set(body.permissionIds)];
|
|
5442
|
+
const [existing] = await database.select().from(rolesInIam).where(and42(eq43(rolesInIam.id, id), eq43(rolesInIam.tenantId, tenantId))).limit(1);
|
|
5443
|
+
if (!existing) {
|
|
5444
|
+
return c.json({ error: "Role not found" }, 404);
|
|
5445
|
+
}
|
|
5446
|
+
if (!existing.isEditable) {
|
|
5447
|
+
return c.json({ error: "Role is not editable" }, 403);
|
|
5448
|
+
}
|
|
5449
|
+
const updateData = {};
|
|
4461
5450
|
if (body.name !== void 0) {
|
|
4462
5451
|
updateData.name = body.name;
|
|
4463
5452
|
}
|
|
@@ -4467,34 +5456,98 @@ var updateRoleHandler = async (c) => {
|
|
|
4467
5456
|
if (body.code !== void 0) {
|
|
4468
5457
|
updateData.code = body.code;
|
|
4469
5458
|
}
|
|
4470
|
-
|
|
4471
|
-
|
|
4472
|
-
|
|
4473
|
-
|
|
5459
|
+
if (body.isSystem !== void 0) {
|
|
5460
|
+
updateData.isSystem = body.isSystem;
|
|
5461
|
+
}
|
|
5462
|
+
if (body.isEditable !== void 0) {
|
|
5463
|
+
updateData.isEditable = body.isEditable;
|
|
5464
|
+
}
|
|
5465
|
+
if (body.isDeletable !== void 0) {
|
|
5466
|
+
updateData.isDeletable = body.isDeletable;
|
|
5467
|
+
}
|
|
5468
|
+
const updated = await withTransaction(database, async (tx) => {
|
|
5469
|
+
await seedPermissions({
|
|
5470
|
+
database: tx,
|
|
5471
|
+
permissions: config.permissions
|
|
5472
|
+
});
|
|
5473
|
+
const [role] = await tx.update(rolesInIam).set({
|
|
5474
|
+
...updateData,
|
|
5475
|
+
updatedAt: sql21`CURRENT_TIMESTAMP`
|
|
5476
|
+
}).where(and42(eq43(rolesInIam.id, id), eq43(rolesInIam.tenantId, tenantId))).returning();
|
|
5477
|
+
if (!role) {
|
|
5478
|
+
return null;
|
|
5479
|
+
}
|
|
5480
|
+
if (permissionIds !== void 0) {
|
|
5481
|
+
await syncRolePermissions({
|
|
5482
|
+
database: tx,
|
|
5483
|
+
tenantId,
|
|
5484
|
+
roleId: role.id,
|
|
5485
|
+
permissionIds
|
|
5486
|
+
});
|
|
5487
|
+
}
|
|
5488
|
+
return role;
|
|
5489
|
+
});
|
|
4474
5490
|
if (!updated) {
|
|
4475
5491
|
return c.json({ error: "Role not found" }, 404);
|
|
4476
5492
|
}
|
|
4477
|
-
return c.json(
|
|
5493
|
+
return c.json(
|
|
5494
|
+
{
|
|
5495
|
+
role: {
|
|
5496
|
+
...updated,
|
|
5497
|
+
permissionIds,
|
|
5498
|
+
permissionCount: permissionIds?.length
|
|
5499
|
+
}
|
|
5500
|
+
},
|
|
5501
|
+
200
|
|
5502
|
+
);
|
|
4478
5503
|
};
|
|
4479
5504
|
|
|
4480
5505
|
// src/routes/roles/roles.schema.ts
|
|
4481
5506
|
import { z as z6 } from "zod";
|
|
5507
|
+
var roleFilterValues = ["", "code"];
|
|
5508
|
+
var roleSortValues = ["createdAt", "updatedAt", "code"];
|
|
4482
5509
|
var listRolesQuerySchema = z6.object({
|
|
4483
5510
|
page: z6.coerce.number().min(1).default(1).optional(),
|
|
4484
|
-
limit: z6.coerce.number().min(1).max(100).default(20).optional()
|
|
5511
|
+
limit: z6.coerce.number().min(1).max(100).default(20).optional(),
|
|
5512
|
+
search: z6.string().optional(),
|
|
5513
|
+
filter: z6.enum(roleFilterValues).optional(),
|
|
5514
|
+
sort: z6.enum(roleSortValues).optional(),
|
|
5515
|
+
order: z6.enum(["asc", "desc"]).optional()
|
|
4485
5516
|
});
|
|
4486
5517
|
var roleIdParamSchema = z6.object({
|
|
4487
5518
|
id: z6.uuid()
|
|
4488
5519
|
});
|
|
4489
5520
|
var createRoleSchema = z6.object({
|
|
4490
|
-
name: z6.unknown(),
|
|
4491
|
-
description: z6.unknown(),
|
|
4492
|
-
code: z6.string()
|
|
5521
|
+
name: z6.unknown().optional(),
|
|
5522
|
+
description: z6.unknown().optional(),
|
|
5523
|
+
code: z6.string(),
|
|
5524
|
+
permissionIds: z6.array(z6.string()).optional(),
|
|
5525
|
+
isSystem: z6.boolean().optional(),
|
|
5526
|
+
isEditable: z6.boolean().optional(),
|
|
5527
|
+
isDeletable: z6.boolean().optional()
|
|
4493
5528
|
});
|
|
4494
5529
|
var updateRoleSchema = z6.object({
|
|
4495
5530
|
name: z6.unknown().optional(),
|
|
4496
5531
|
description: z6.unknown().optional(),
|
|
4497
|
-
code: z6.string().optional()
|
|
5532
|
+
code: z6.string().optional(),
|
|
5533
|
+
permissionIds: z6.array(z6.string()).optional(),
|
|
5534
|
+
isSystem: z6.boolean().optional(),
|
|
5535
|
+
isEditable: z6.boolean().optional(),
|
|
5536
|
+
isDeletable: z6.boolean().optional()
|
|
5537
|
+
});
|
|
5538
|
+
var assignRolePermissionsSchema = z6.object({
|
|
5539
|
+
permissionIds: z6.array(z6.string()).min(1)
|
|
5540
|
+
});
|
|
5541
|
+
var assignRoleUsersSchema = z6.object({
|
|
5542
|
+
userIds: z6.array(z6.uuid()).min(1)
|
|
5543
|
+
});
|
|
5544
|
+
var rolePermissionParamSchema = z6.object({
|
|
5545
|
+
id: z6.uuid(),
|
|
5546
|
+
permissionId: z6.string()
|
|
5547
|
+
});
|
|
5548
|
+
var roleUserParamSchema = z6.object({
|
|
5549
|
+
id: z6.uuid(),
|
|
5550
|
+
userId: z6.uuid()
|
|
4498
5551
|
});
|
|
4499
5552
|
var roleSchema = z6.object({
|
|
4500
5553
|
id: z6.uuid(),
|
|
@@ -4503,7 +5556,13 @@ var roleSchema = z6.object({
|
|
|
4503
5556
|
updatedAt: z6.string(),
|
|
4504
5557
|
name: z6.unknown(),
|
|
4505
5558
|
description: z6.unknown(),
|
|
4506
|
-
code: z6.string()
|
|
5559
|
+
code: z6.string(),
|
|
5560
|
+
isSystem: z6.boolean(),
|
|
5561
|
+
isEditable: z6.boolean(),
|
|
5562
|
+
isDeletable: z6.boolean(),
|
|
5563
|
+
permissionIds: z6.array(z6.string()).optional(),
|
|
5564
|
+
permissionCount: z6.number().optional(),
|
|
5565
|
+
userCount: z6.number().optional()
|
|
4507
5566
|
});
|
|
4508
5567
|
var listRolesResponseSchema = z6.object({
|
|
4509
5568
|
roles: z6.array(roleSchema),
|
|
@@ -4514,12 +5573,45 @@ var listRolesResponseSchema = z6.object({
|
|
|
4514
5573
|
var roleResponseSchema = z6.object({
|
|
4515
5574
|
role: roleSchema
|
|
4516
5575
|
});
|
|
5576
|
+
var listRolePermissionsResponseSchema2 = z6.object({
|
|
5577
|
+
permissions: z6.array(permissionSchema),
|
|
5578
|
+
total: z6.number(),
|
|
5579
|
+
page: z6.number(),
|
|
5580
|
+
limit: z6.number()
|
|
5581
|
+
});
|
|
5582
|
+
var assignRolePermissionsResponseSchema = z6.object({
|
|
5583
|
+
created: z6.number(),
|
|
5584
|
+
skipped: z6.number()
|
|
5585
|
+
});
|
|
5586
|
+
var listRoleUsersQuerySchema = z6.object({
|
|
5587
|
+
page: z6.coerce.number().min(1).default(1).optional(),
|
|
5588
|
+
limit: z6.coerce.number().min(1).max(100).default(20).optional(),
|
|
5589
|
+
search: z6.string().optional(),
|
|
5590
|
+
filter: z6.enum(["", "fullName", "email", "phone", "handle"]).optional(),
|
|
5591
|
+
sort: z6.enum(["createdAt", "updatedAt", "fullName", "handle", "lastSignInAt"]).optional(),
|
|
5592
|
+
order: z6.enum(["asc", "desc"]).optional()
|
|
5593
|
+
});
|
|
5594
|
+
var listRoleUsersResponseSchema = z6.object({
|
|
5595
|
+
users: z6.array(userSchema),
|
|
5596
|
+
total: z6.number(),
|
|
5597
|
+
page: z6.number(),
|
|
5598
|
+
limit: z6.number()
|
|
5599
|
+
});
|
|
5600
|
+
var assignRoleUsersResponseSchema = z6.object({
|
|
5601
|
+
created: z6.number(),
|
|
5602
|
+
skipped: z6.number()
|
|
5603
|
+
});
|
|
4517
5604
|
var deleteRoleResponseSchema = z6.object({
|
|
4518
5605
|
message: z6.string()
|
|
4519
5606
|
});
|
|
5607
|
+
var seedRolesResponseSchema = z6.object({
|
|
5608
|
+
roles: z6.array(roleSchema),
|
|
5609
|
+
total: z6.number()
|
|
5610
|
+
});
|
|
4520
5611
|
var errorResponseSchema5 = z6.object({
|
|
4521
5612
|
error: z6.string()
|
|
4522
5613
|
});
|
|
5614
|
+
var listRolePermissionsQuerySchema2 = listPermissionsQuerySchema;
|
|
4523
5615
|
|
|
4524
5616
|
// src/routes/roles/roles.route.ts
|
|
4525
5617
|
var listRolesRoute = createRoute9({
|
|
@@ -4537,26 +5629,258 @@ var listRolesRoute = createRoute9({
|
|
|
4537
5629
|
schema: listRolesResponseSchema
|
|
4538
5630
|
}
|
|
4539
5631
|
},
|
|
4540
|
-
description: "List of roles"
|
|
5632
|
+
description: "List of roles"
|
|
5633
|
+
}
|
|
5634
|
+
}
|
|
5635
|
+
});
|
|
5636
|
+
var getRoleRoute = createRoute9({
|
|
5637
|
+
method: "get",
|
|
5638
|
+
path: "/{id}",
|
|
5639
|
+
tags: ["Roles"],
|
|
5640
|
+
summary: "Get role by ID",
|
|
5641
|
+
request: {
|
|
5642
|
+
params: roleIdParamSchema
|
|
5643
|
+
},
|
|
5644
|
+
responses: {
|
|
5645
|
+
200: {
|
|
5646
|
+
content: {
|
|
5647
|
+
"application/json": {
|
|
5648
|
+
schema: roleResponseSchema
|
|
5649
|
+
}
|
|
5650
|
+
},
|
|
5651
|
+
description: "Role details"
|
|
5652
|
+
},
|
|
5653
|
+
404: {
|
|
5654
|
+
content: {
|
|
5655
|
+
"application/json": {
|
|
5656
|
+
schema: errorResponseSchema5
|
|
5657
|
+
}
|
|
5658
|
+
},
|
|
5659
|
+
description: "Role not found"
|
|
5660
|
+
}
|
|
5661
|
+
}
|
|
5662
|
+
});
|
|
5663
|
+
var createRoleRoute = createRoute9({
|
|
5664
|
+
method: "post",
|
|
5665
|
+
path: "/",
|
|
5666
|
+
tags: ["Roles"],
|
|
5667
|
+
summary: "Create role",
|
|
5668
|
+
request: {
|
|
5669
|
+
body: {
|
|
5670
|
+
content: {
|
|
5671
|
+
"application/json": {
|
|
5672
|
+
schema: createRoleSchema
|
|
5673
|
+
}
|
|
5674
|
+
}
|
|
5675
|
+
}
|
|
5676
|
+
},
|
|
5677
|
+
responses: {
|
|
5678
|
+
201: {
|
|
5679
|
+
content: {
|
|
5680
|
+
"application/json": {
|
|
5681
|
+
schema: roleResponseSchema
|
|
5682
|
+
}
|
|
5683
|
+
},
|
|
5684
|
+
description: "Role created"
|
|
5685
|
+
},
|
|
5686
|
+
400: {
|
|
5687
|
+
content: {
|
|
5688
|
+
"application/json": {
|
|
5689
|
+
schema: errorResponseSchema5
|
|
5690
|
+
}
|
|
5691
|
+
},
|
|
5692
|
+
description: "Invalid permission assignment"
|
|
5693
|
+
},
|
|
5694
|
+
409: {
|
|
5695
|
+
content: {
|
|
5696
|
+
"application/json": {
|
|
5697
|
+
schema: errorResponseSchema5
|
|
5698
|
+
}
|
|
5699
|
+
},
|
|
5700
|
+
description: "Role code already exists"
|
|
5701
|
+
}
|
|
5702
|
+
}
|
|
5703
|
+
});
|
|
5704
|
+
var updateRoleRoute = createRoute9({
|
|
5705
|
+
method: "put",
|
|
5706
|
+
path: "/{id}",
|
|
5707
|
+
tags: ["Roles"],
|
|
5708
|
+
summary: "Update role",
|
|
5709
|
+
request: {
|
|
5710
|
+
params: roleIdParamSchema,
|
|
5711
|
+
body: {
|
|
5712
|
+
content: {
|
|
5713
|
+
"application/json": {
|
|
5714
|
+
schema: updateRoleSchema
|
|
5715
|
+
}
|
|
5716
|
+
}
|
|
5717
|
+
}
|
|
5718
|
+
},
|
|
5719
|
+
responses: {
|
|
5720
|
+
200: {
|
|
5721
|
+
content: {
|
|
5722
|
+
"application/json": {
|
|
5723
|
+
schema: roleResponseSchema
|
|
5724
|
+
}
|
|
5725
|
+
},
|
|
5726
|
+
description: "Role updated"
|
|
5727
|
+
},
|
|
5728
|
+
400: {
|
|
5729
|
+
content: {
|
|
5730
|
+
"application/json": {
|
|
5731
|
+
schema: errorResponseSchema5
|
|
5732
|
+
}
|
|
5733
|
+
},
|
|
5734
|
+
description: "Invalid permission assignment"
|
|
5735
|
+
},
|
|
5736
|
+
403: {
|
|
5737
|
+
content: {
|
|
5738
|
+
"application/json": {
|
|
5739
|
+
schema: errorResponseSchema5
|
|
5740
|
+
}
|
|
5741
|
+
},
|
|
5742
|
+
description: "Role is not editable"
|
|
5743
|
+
},
|
|
5744
|
+
404: {
|
|
5745
|
+
content: {
|
|
5746
|
+
"application/json": {
|
|
5747
|
+
schema: errorResponseSchema5
|
|
5748
|
+
}
|
|
5749
|
+
},
|
|
5750
|
+
description: "Role not found"
|
|
5751
|
+
}
|
|
5752
|
+
}
|
|
5753
|
+
});
|
|
5754
|
+
var deleteRoleRoute = createRoute9({
|
|
5755
|
+
method: "delete",
|
|
5756
|
+
path: "/{id}",
|
|
5757
|
+
tags: ["Roles"],
|
|
5758
|
+
summary: "Delete role",
|
|
5759
|
+
request: {
|
|
5760
|
+
params: roleIdParamSchema
|
|
5761
|
+
},
|
|
5762
|
+
responses: {
|
|
5763
|
+
200: {
|
|
5764
|
+
content: {
|
|
5765
|
+
"application/json": {
|
|
5766
|
+
schema: deleteRoleResponseSchema
|
|
5767
|
+
}
|
|
5768
|
+
},
|
|
5769
|
+
description: "Role deleted"
|
|
5770
|
+
},
|
|
5771
|
+
403: {
|
|
5772
|
+
content: {
|
|
5773
|
+
"application/json": {
|
|
5774
|
+
schema: errorResponseSchema5
|
|
5775
|
+
}
|
|
5776
|
+
},
|
|
5777
|
+
description: "Role is not deletable"
|
|
5778
|
+
},
|
|
5779
|
+
404: {
|
|
5780
|
+
content: {
|
|
5781
|
+
"application/json": {
|
|
5782
|
+
schema: errorResponseSchema5
|
|
5783
|
+
}
|
|
5784
|
+
},
|
|
5785
|
+
description: "Role not found"
|
|
5786
|
+
}
|
|
5787
|
+
}
|
|
5788
|
+
});
|
|
5789
|
+
var listRolePermissionsRoute2 = createRoute9({
|
|
5790
|
+
method: "get",
|
|
5791
|
+
path: "/{id}/permissions",
|
|
5792
|
+
tags: ["Roles"],
|
|
5793
|
+
summary: "List permissions assigned to a role",
|
|
5794
|
+
request: {
|
|
5795
|
+
params: roleIdParamSchema,
|
|
5796
|
+
query: listRolePermissionsQuerySchema2
|
|
5797
|
+
},
|
|
5798
|
+
responses: {
|
|
5799
|
+
200: {
|
|
5800
|
+
content: {
|
|
5801
|
+
"application/json": {
|
|
5802
|
+
schema: listRolePermissionsResponseSchema2
|
|
5803
|
+
}
|
|
5804
|
+
},
|
|
5805
|
+
description: "Role permissions"
|
|
5806
|
+
},
|
|
5807
|
+
404: {
|
|
5808
|
+
content: {
|
|
5809
|
+
"application/json": {
|
|
5810
|
+
schema: errorResponseSchema5
|
|
5811
|
+
}
|
|
5812
|
+
},
|
|
5813
|
+
description: "Role not found"
|
|
5814
|
+
}
|
|
5815
|
+
}
|
|
5816
|
+
});
|
|
5817
|
+
var assignRolePermissionsRoute = createRoute9({
|
|
5818
|
+
method: "post",
|
|
5819
|
+
path: "/{id}/permissions",
|
|
5820
|
+
tags: ["Roles"],
|
|
5821
|
+
summary: "Assign permissions to a role",
|
|
5822
|
+
request: {
|
|
5823
|
+
params: roleIdParamSchema,
|
|
5824
|
+
body: {
|
|
5825
|
+
content: {
|
|
5826
|
+
"application/json": {
|
|
5827
|
+
schema: assignRolePermissionsSchema
|
|
5828
|
+
}
|
|
5829
|
+
}
|
|
5830
|
+
}
|
|
5831
|
+
},
|
|
5832
|
+
responses: {
|
|
5833
|
+
200: {
|
|
5834
|
+
content: {
|
|
5835
|
+
"application/json": {
|
|
5836
|
+
schema: assignRolePermissionsResponseSchema
|
|
5837
|
+
}
|
|
5838
|
+
},
|
|
5839
|
+
description: "Permissions assigned"
|
|
5840
|
+
},
|
|
5841
|
+
400: {
|
|
5842
|
+
content: {
|
|
5843
|
+
"application/json": {
|
|
5844
|
+
schema: errorResponseSchema5
|
|
5845
|
+
}
|
|
5846
|
+
},
|
|
5847
|
+
description: "Invalid permissions"
|
|
5848
|
+
},
|
|
5849
|
+
403: {
|
|
5850
|
+
content: {
|
|
5851
|
+
"application/json": {
|
|
5852
|
+
schema: errorResponseSchema5
|
|
5853
|
+
}
|
|
5854
|
+
},
|
|
5855
|
+
description: "Role is not editable"
|
|
5856
|
+
},
|
|
5857
|
+
404: {
|
|
5858
|
+
content: {
|
|
5859
|
+
"application/json": {
|
|
5860
|
+
schema: errorResponseSchema5
|
|
5861
|
+
}
|
|
5862
|
+
},
|
|
5863
|
+
description: "Role not found"
|
|
4541
5864
|
}
|
|
4542
5865
|
}
|
|
4543
5866
|
});
|
|
4544
|
-
var
|
|
5867
|
+
var listRoleUsersRoute = createRoute9({
|
|
4545
5868
|
method: "get",
|
|
4546
|
-
path: "/{id}",
|
|
5869
|
+
path: "/{id}/users",
|
|
4547
5870
|
tags: ["Roles"],
|
|
4548
|
-
summary: "
|
|
5871
|
+
summary: "List users assigned to a role",
|
|
4549
5872
|
request: {
|
|
4550
|
-
params: roleIdParamSchema
|
|
5873
|
+
params: roleIdParamSchema,
|
|
5874
|
+
query: listRoleUsersQuerySchema
|
|
4551
5875
|
},
|
|
4552
5876
|
responses: {
|
|
4553
5877
|
200: {
|
|
4554
5878
|
content: {
|
|
4555
5879
|
"application/json": {
|
|
4556
|
-
schema:
|
|
5880
|
+
schema: listRoleUsersResponseSchema
|
|
4557
5881
|
}
|
|
4558
5882
|
},
|
|
4559
|
-
description: "Role
|
|
5883
|
+
description: "Role users"
|
|
4560
5884
|
},
|
|
4561
5885
|
404: {
|
|
4562
5886
|
content: {
|
|
@@ -4568,62 +5892,80 @@ var getRoleRoute = createRoute9({
|
|
|
4568
5892
|
}
|
|
4569
5893
|
}
|
|
4570
5894
|
});
|
|
4571
|
-
var
|
|
5895
|
+
var assignRoleUsersRoute = createRoute9({
|
|
4572
5896
|
method: "post",
|
|
4573
|
-
path: "/",
|
|
5897
|
+
path: "/{id}/users",
|
|
4574
5898
|
tags: ["Roles"],
|
|
4575
|
-
summary: "
|
|
5899
|
+
summary: "Assign users to a role",
|
|
4576
5900
|
request: {
|
|
5901
|
+
params: roleIdParamSchema,
|
|
4577
5902
|
body: {
|
|
4578
5903
|
content: {
|
|
4579
5904
|
"application/json": {
|
|
4580
|
-
schema:
|
|
5905
|
+
schema: assignRoleUsersSchema
|
|
4581
5906
|
}
|
|
4582
5907
|
}
|
|
4583
5908
|
}
|
|
4584
5909
|
},
|
|
4585
5910
|
responses: {
|
|
4586
|
-
|
|
5911
|
+
200: {
|
|
4587
5912
|
content: {
|
|
4588
5913
|
"application/json": {
|
|
4589
|
-
schema:
|
|
5914
|
+
schema: assignRoleUsersResponseSchema
|
|
4590
5915
|
}
|
|
4591
5916
|
},
|
|
4592
|
-
description: "
|
|
5917
|
+
description: "Users assigned"
|
|
4593
5918
|
},
|
|
4594
|
-
|
|
5919
|
+
400: {
|
|
4595
5920
|
content: {
|
|
4596
5921
|
"application/json": {
|
|
4597
5922
|
schema: errorResponseSchema5
|
|
4598
5923
|
}
|
|
4599
5924
|
},
|
|
4600
|
-
description: "
|
|
5925
|
+
description: "Invalid users"
|
|
5926
|
+
},
|
|
5927
|
+
403: {
|
|
5928
|
+
content: {
|
|
5929
|
+
"application/json": {
|
|
5930
|
+
schema: errorResponseSchema5
|
|
5931
|
+
}
|
|
5932
|
+
},
|
|
5933
|
+
description: "Role is not editable"
|
|
5934
|
+
},
|
|
5935
|
+
404: {
|
|
5936
|
+
content: {
|
|
5937
|
+
"application/json": {
|
|
5938
|
+
schema: errorResponseSchema5
|
|
5939
|
+
}
|
|
5940
|
+
},
|
|
5941
|
+
description: "Role not found"
|
|
4601
5942
|
}
|
|
4602
5943
|
}
|
|
4603
5944
|
});
|
|
4604
|
-
var
|
|
4605
|
-
method: "
|
|
4606
|
-
path: "/{id}",
|
|
5945
|
+
var revokeRolePermissionRoute2 = createRoute9({
|
|
5946
|
+
method: "delete",
|
|
5947
|
+
path: "/{id}/permissions/{permissionId}",
|
|
4607
5948
|
tags: ["Roles"],
|
|
4608
|
-
summary: "
|
|
5949
|
+
summary: "Revoke a permission from a role",
|
|
4609
5950
|
request: {
|
|
4610
|
-
params:
|
|
4611
|
-
body: {
|
|
4612
|
-
content: {
|
|
4613
|
-
"application/json": {
|
|
4614
|
-
schema: updateRoleSchema
|
|
4615
|
-
}
|
|
4616
|
-
}
|
|
4617
|
-
}
|
|
5951
|
+
params: rolePermissionParamSchema
|
|
4618
5952
|
},
|
|
4619
5953
|
responses: {
|
|
4620
5954
|
200: {
|
|
4621
5955
|
content: {
|
|
4622
5956
|
"application/json": {
|
|
4623
|
-
schema:
|
|
5957
|
+
schema: deleteRoleResponseSchema
|
|
4624
5958
|
}
|
|
4625
5959
|
},
|
|
4626
|
-
description: "
|
|
5960
|
+
description: "Permission revoked"
|
|
5961
|
+
},
|
|
5962
|
+
403: {
|
|
5963
|
+
content: {
|
|
5964
|
+
"application/json": {
|
|
5965
|
+
schema: errorResponseSchema5
|
|
5966
|
+
}
|
|
5967
|
+
},
|
|
5968
|
+
description: "Role is not editable"
|
|
4627
5969
|
},
|
|
4628
5970
|
404: {
|
|
4629
5971
|
content: {
|
|
@@ -4631,17 +5973,17 @@ var updateRoleRoute = createRoute9({
|
|
|
4631
5973
|
schema: errorResponseSchema5
|
|
4632
5974
|
}
|
|
4633
5975
|
},
|
|
4634
|
-
description: "Role not found"
|
|
5976
|
+
description: "Role or permission not found"
|
|
4635
5977
|
}
|
|
4636
5978
|
}
|
|
4637
5979
|
});
|
|
4638
|
-
var
|
|
5980
|
+
var revokeRoleUserRoute = createRoute9({
|
|
4639
5981
|
method: "delete",
|
|
4640
|
-
path: "/{id}",
|
|
5982
|
+
path: "/{id}/users/{userId}",
|
|
4641
5983
|
tags: ["Roles"],
|
|
4642
|
-
summary: "
|
|
5984
|
+
summary: "Remove a user from a role",
|
|
4643
5985
|
request: {
|
|
4644
|
-
params:
|
|
5986
|
+
params: roleUserParamSchema
|
|
4645
5987
|
},
|
|
4646
5988
|
responses: {
|
|
4647
5989
|
200: {
|
|
@@ -4650,7 +5992,15 @@ var deleteRoleRoute = createRoute9({
|
|
|
4650
5992
|
schema: deleteRoleResponseSchema
|
|
4651
5993
|
}
|
|
4652
5994
|
},
|
|
4653
|
-
description: "
|
|
5995
|
+
description: "User removed"
|
|
5996
|
+
},
|
|
5997
|
+
403: {
|
|
5998
|
+
content: {
|
|
5999
|
+
"application/json": {
|
|
6000
|
+
schema: errorResponseSchema5
|
|
6001
|
+
}
|
|
6002
|
+
},
|
|
6003
|
+
description: "Role is not editable"
|
|
4654
6004
|
},
|
|
4655
6005
|
404: {
|
|
4656
6006
|
content: {
|
|
@@ -4658,23 +6008,47 @@ var deleteRoleRoute = createRoute9({
|
|
|
4658
6008
|
schema: errorResponseSchema5
|
|
4659
6009
|
}
|
|
4660
6010
|
},
|
|
4661
|
-
description: "Role not found"
|
|
6011
|
+
description: "Role or user not found"
|
|
6012
|
+
}
|
|
6013
|
+
}
|
|
6014
|
+
});
|
|
6015
|
+
var seedRolesRoute = createRoute9({
|
|
6016
|
+
method: "post",
|
|
6017
|
+
path: "/seed",
|
|
6018
|
+
tags: ["Roles"],
|
|
6019
|
+
summary: "Seed tenant roles from config",
|
|
6020
|
+
responses: {
|
|
6021
|
+
200: {
|
|
6022
|
+
content: {
|
|
6023
|
+
"application/json": {
|
|
6024
|
+
schema: seedRolesResponseSchema
|
|
6025
|
+
}
|
|
6026
|
+
},
|
|
6027
|
+
description: "Seeded roles"
|
|
6028
|
+
},
|
|
6029
|
+
400: {
|
|
6030
|
+
content: {
|
|
6031
|
+
"application/json": {
|
|
6032
|
+
schema: errorResponseSchema5
|
|
6033
|
+
}
|
|
6034
|
+
},
|
|
6035
|
+
description: "Invalid role config"
|
|
4662
6036
|
}
|
|
4663
6037
|
}
|
|
4664
6038
|
});
|
|
4665
|
-
var roleRoutes = new OpenAPIHono9().openapi(listRolesRoute, listRolesHandler).openapi(getRoleRoute, getRoleHandler).openapi(createRoleRoute, createRoleHandler).openapi(updateRoleRoute, updateRoleHandler).openapi(deleteRoleRoute, deleteRoleHandler);
|
|
6039
|
+
var roleRoutes = new OpenAPIHono9().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);
|
|
4666
6040
|
var roles_route_default = roleRoutes;
|
|
4667
6041
|
|
|
4668
6042
|
// src/routes/sessions/sessions.route.ts
|
|
4669
6043
|
import { createRoute as createRoute10, OpenAPIHono as OpenAPIHono10 } from "@hono/zod-openapi";
|
|
4670
6044
|
|
|
4671
6045
|
// src/routes/sessions/handler/get-session.ts
|
|
4672
|
-
import { and as
|
|
6046
|
+
import { and as and43, eq as eq44 } from "drizzle-orm";
|
|
4673
6047
|
var getSessionHandler = async (c) => {
|
|
4674
6048
|
const { id } = c.req.valid("param");
|
|
4675
6049
|
const database = c.get("database");
|
|
4676
6050
|
const tenantId = c.get("tenantId");
|
|
4677
|
-
const [session] = await database.select().from(sessionsInIam).where(
|
|
6051
|
+
const [session] = await database.select().from(sessionsInIam).where(and43(eq44(sessionsInIam.id, id), eq44(sessionsInIam.tenantId, tenantId))).limit(1);
|
|
4678
6052
|
if (!session) {
|
|
4679
6053
|
return c.json({ error: "Session not found" }, 404);
|
|
4680
6054
|
}
|
|
@@ -4682,7 +6056,7 @@ var getSessionHandler = async (c) => {
|
|
|
4682
6056
|
};
|
|
4683
6057
|
|
|
4684
6058
|
// src/routes/sessions/handler/list-sessions.ts
|
|
4685
|
-
import { and as
|
|
6059
|
+
import { and as and44, eq as eq45, sql as sql22 } from "drizzle-orm";
|
|
4686
6060
|
var listSessionsHandler = async (c) => {
|
|
4687
6061
|
const query = c.req.valid("query");
|
|
4688
6062
|
const database = c.get("database");
|
|
@@ -4690,48 +6064,48 @@ var listSessionsHandler = async (c) => {
|
|
|
4690
6064
|
const page = query.page || 1;
|
|
4691
6065
|
const limit = query.limit || 20;
|
|
4692
6066
|
const offset = (page - 1) * limit;
|
|
4693
|
-
const conditions = [
|
|
6067
|
+
const conditions = [eq45(sessionsInIam.tenantId, tenantId)];
|
|
4694
6068
|
if (query.userId) {
|
|
4695
|
-
conditions.push(
|
|
6069
|
+
conditions.push(eq45(sessionsInIam.userId, query.userId));
|
|
4696
6070
|
}
|
|
4697
6071
|
const [sessions, totalResult] = await Promise.all([
|
|
4698
|
-
database.select().from(sessionsInIam).where(
|
|
4699
|
-
database.select({ count:
|
|
6072
|
+
database.select().from(sessionsInIam).where(and44(...conditions)).limit(limit).offset(offset),
|
|
6073
|
+
database.select({ count: sql22`count(*)` }).from(sessionsInIam).where(and44(...conditions))
|
|
4700
6074
|
]);
|
|
4701
6075
|
const total = Number(totalResult[0]?.count || 0);
|
|
4702
6076
|
return c.json({ sessions, total, page, limit }, 200);
|
|
4703
6077
|
};
|
|
4704
6078
|
|
|
4705
6079
|
// src/routes/sessions/handler/revoke-all-sessions.ts
|
|
4706
|
-
import { and as
|
|
6080
|
+
import { and as and45, eq as eq46 } from "drizzle-orm";
|
|
4707
6081
|
var revokeAllSessionsHandler = async (c) => {
|
|
4708
6082
|
const { userId } = c.req.valid("param");
|
|
4709
6083
|
const database = c.get("database");
|
|
4710
6084
|
const tenantId = c.get("tenantId");
|
|
4711
|
-
const [user] = await database.select().from(usersInIam).where(
|
|
6085
|
+
const [user] = await database.select().from(usersInIam).where(and45(eq46(usersInIam.id, userId), eq46(usersInIam.tenantId, tenantId))).limit(1);
|
|
4712
6086
|
if (!user) {
|
|
4713
6087
|
return c.json({ error: "User not found" }, 404);
|
|
4714
6088
|
}
|
|
4715
6089
|
await database.delete(sessionsInIam).where(
|
|
4716
|
-
|
|
4717
|
-
|
|
4718
|
-
|
|
6090
|
+
and45(
|
|
6091
|
+
eq46(sessionsInIam.tenantId, tenantId),
|
|
6092
|
+
eq46(sessionsInIam.userId, userId)
|
|
4719
6093
|
)
|
|
4720
6094
|
);
|
|
4721
6095
|
return c.json({ message: "All user sessions revoked" }, 200);
|
|
4722
6096
|
};
|
|
4723
6097
|
|
|
4724
6098
|
// src/routes/sessions/handler/revoke-session.ts
|
|
4725
|
-
import { and as
|
|
6099
|
+
import { and as and46, eq as eq47 } from "drizzle-orm";
|
|
4726
6100
|
var revokeSessionHandler = async (c) => {
|
|
4727
6101
|
const { id } = c.req.valid("param");
|
|
4728
6102
|
const database = c.get("database");
|
|
4729
6103
|
const tenantId = c.get("tenantId");
|
|
4730
|
-
const [existing] = await database.select().from(sessionsInIam).where(
|
|
6104
|
+
const [existing] = await database.select().from(sessionsInIam).where(and46(eq47(sessionsInIam.id, id), eq47(sessionsInIam.tenantId, tenantId))).limit(1);
|
|
4731
6105
|
if (!existing) {
|
|
4732
6106
|
return c.json({ error: "Session not found" }, 404);
|
|
4733
6107
|
}
|
|
4734
|
-
await database.delete(sessionsInIam).where(
|
|
6108
|
+
await database.delete(sessionsInIam).where(and46(eq47(sessionsInIam.id, id), eq47(sessionsInIam.tenantId, tenantId)));
|
|
4735
6109
|
return c.json({ message: "Session revoked" }, 200);
|
|
4736
6110
|
};
|
|
4737
6111
|
|
|
@@ -4933,10 +6307,11 @@ var system_route_default = tenantRoutes;
|
|
|
4933
6307
|
import { createRoute as createRoute12, OpenAPIHono as OpenAPIHono12 } from "@hono/zod-openapi";
|
|
4934
6308
|
|
|
4935
6309
|
// src/routes/tenants/handler/create-tenant.ts
|
|
4936
|
-
import { eq as
|
|
6310
|
+
import { eq as eq48 } from "drizzle-orm";
|
|
4937
6311
|
|
|
4938
6312
|
// src/lib/has-role-permission.ts
|
|
4939
|
-
import {
|
|
6313
|
+
import { grant } from "@mesob/common";
|
|
6314
|
+
import { HTTPException as HTTPException4 } from "hono/http-exception";
|
|
4940
6315
|
var toArray = (v) => {
|
|
4941
6316
|
return Array.isArray(v) ? v : [v];
|
|
4942
6317
|
};
|
|
@@ -4951,21 +6326,18 @@ var hasRole = (c, role) => {
|
|
|
4951
6326
|
};
|
|
4952
6327
|
var hasRoleThrow = (c, role) => {
|
|
4953
6328
|
if (!hasRole(c, role)) {
|
|
4954
|
-
throw new
|
|
6329
|
+
throw new HTTPException4(401, { message: "Unauthorized" });
|
|
4955
6330
|
}
|
|
4956
6331
|
};
|
|
4957
6332
|
var hasPermission = (c, permission) => {
|
|
4958
6333
|
const user = c.get("user");
|
|
4959
6334
|
const perms = user?.permissions;
|
|
4960
|
-
if (!perms?.length) {
|
|
4961
|
-
return false;
|
|
4962
|
-
}
|
|
4963
6335
|
const check2 = toArray(permission);
|
|
4964
|
-
return check2
|
|
6336
|
+
return grant(check2, perms);
|
|
4965
6337
|
};
|
|
4966
6338
|
var hasPermissionThrow = (c, permission) => {
|
|
4967
6339
|
if (!hasPermission(c, permission)) {
|
|
4968
|
-
throw new
|
|
6340
|
+
throw new HTTPException4(401, { message: "Unauthorized" });
|
|
4969
6341
|
}
|
|
4970
6342
|
};
|
|
4971
6343
|
|
|
@@ -4974,7 +6346,7 @@ var createTenantHandler = async (c) => {
|
|
|
4974
6346
|
hasRoleThrow(c, ["owner", "tenant-admin"]);
|
|
4975
6347
|
const body = c.req.valid("json");
|
|
4976
6348
|
const database = c.get("database");
|
|
4977
|
-
const [existing] = await database.select().from(tenantsInIam).where(
|
|
6349
|
+
const [existing] = await database.select().from(tenantsInIam).where(eq48(tenantsInIam.id, body.id)).limit(1);
|
|
4978
6350
|
if (existing) {
|
|
4979
6351
|
return c.json({ error: "Tenant already exists" }, 409);
|
|
4980
6352
|
}
|
|
@@ -4997,26 +6369,26 @@ var createTenantHandler = async (c) => {
|
|
|
4997
6369
|
};
|
|
4998
6370
|
|
|
4999
6371
|
// src/routes/tenants/handler/delete-tenant.ts
|
|
5000
|
-
import { eq as
|
|
6372
|
+
import { eq as eq49 } from "drizzle-orm";
|
|
5001
6373
|
var deleteTenantHandler = async (c) => {
|
|
5002
6374
|
hasRoleThrow(c, ["owner", "tenant-admin"]);
|
|
5003
6375
|
const { id } = c.req.valid("param");
|
|
5004
6376
|
const database = c.get("database");
|
|
5005
|
-
const [existing] = await database.select().from(tenantsInIam).where(
|
|
6377
|
+
const [existing] = await database.select().from(tenantsInIam).where(eq49(tenantsInIam.id, id)).limit(1);
|
|
5006
6378
|
if (!existing) {
|
|
5007
6379
|
return c.json({ error: "Tenant not found" }, 404);
|
|
5008
6380
|
}
|
|
5009
|
-
await database.delete(tenantsInIam).where(
|
|
6381
|
+
await database.delete(tenantsInIam).where(eq49(tenantsInIam.id, id));
|
|
5010
6382
|
return c.json({ message: "Tenant deleted" }, 200);
|
|
5011
6383
|
};
|
|
5012
6384
|
|
|
5013
6385
|
// src/routes/tenants/handler/get-tenant.ts
|
|
5014
|
-
import { eq as
|
|
6386
|
+
import { eq as eq50 } from "drizzle-orm";
|
|
5015
6387
|
var getTenantHandler = async (c) => {
|
|
5016
6388
|
hasRoleThrow(c, ["owner", "tenant-admin"]);
|
|
5017
6389
|
const { id } = c.req.valid("param");
|
|
5018
6390
|
const database = c.get("database");
|
|
5019
|
-
const [tenant] = await database.select().from(tenantsInIam).where(
|
|
6391
|
+
const [tenant] = await database.select().from(tenantsInIam).where(eq50(tenantsInIam.id, id)).limit(1);
|
|
5020
6392
|
if (!tenant) {
|
|
5021
6393
|
return c.json({ error: "Tenant not found" }, 404);
|
|
5022
6394
|
}
|
|
@@ -5024,11 +6396,11 @@ var getTenantHandler = async (c) => {
|
|
|
5024
6396
|
};
|
|
5025
6397
|
|
|
5026
6398
|
// src/routes/tenants/handler/list-tenants.ts
|
|
5027
|
-
import { and as
|
|
5028
|
-
var
|
|
6399
|
+
import { and as and47, asc as asc4, desc as desc4, eq as eq51, ilike as ilike3, or as or3, sql as sql23 } from "drizzle-orm";
|
|
6400
|
+
var sortColumnMap3 = {
|
|
5029
6401
|
createdAt: tenantsInIam.createdAt,
|
|
5030
6402
|
updatedAt: tenantsInIam.updatedAt,
|
|
5031
|
-
name:
|
|
6403
|
+
name: sql23`${tenantsInIam.name}::text`
|
|
5032
6404
|
};
|
|
5033
6405
|
var listTenantsHandler = async (c) => {
|
|
5034
6406
|
hasRoleThrow(c, ["owner", "tenant-admin"]);
|
|
@@ -5039,42 +6411,42 @@ var listTenantsHandler = async (c) => {
|
|
|
5039
6411
|
const offset = (page - 1) * limit;
|
|
5040
6412
|
const conditions = [];
|
|
5041
6413
|
if (query.isActive !== void 0) {
|
|
5042
|
-
conditions.push(
|
|
6414
|
+
conditions.push(eq51(tenantsInIam.isActive, query.isActive));
|
|
5043
6415
|
}
|
|
5044
6416
|
if (query.filter === "isActive:true") {
|
|
5045
|
-
conditions.push(
|
|
6417
|
+
conditions.push(eq51(tenantsInIam.isActive, true));
|
|
5046
6418
|
} else if (query.filter === "isActive:false") {
|
|
5047
|
-
conditions.push(
|
|
6419
|
+
conditions.push(eq51(tenantsInIam.isActive, false));
|
|
5048
6420
|
}
|
|
5049
6421
|
if (query.search?.trim()) {
|
|
5050
6422
|
const term = `%${query.search.trim()}%`;
|
|
5051
|
-
const searchCond =
|
|
5052
|
-
|
|
5053
|
-
|
|
6423
|
+
const searchCond = or3(
|
|
6424
|
+
ilike3(tenantsInIam.id, term),
|
|
6425
|
+
ilike3(sql23`${tenantsInIam.name}::text`, term)
|
|
5054
6426
|
);
|
|
5055
6427
|
if (searchCond) {
|
|
5056
6428
|
conditions.push(searchCond);
|
|
5057
6429
|
}
|
|
5058
6430
|
}
|
|
5059
|
-
const orderDir = query.order === "asc" ?
|
|
5060
|
-
const sortCol = query.sort &&
|
|
5061
|
-
const whereClause = conditions.length > 0 ?
|
|
6431
|
+
const orderDir = query.order === "asc" ? asc4 : desc4;
|
|
6432
|
+
const sortCol = query.sort && sortColumnMap3[query.sort] ? sortColumnMap3[query.sort] : tenantsInIam.createdAt;
|
|
6433
|
+
const whereClause = conditions.length > 0 ? and47(...conditions) : void 0;
|
|
5062
6434
|
const [tenants, totalResult] = await Promise.all([
|
|
5063
6435
|
database.select().from(tenantsInIam).where(whereClause).orderBy(orderDir(sortCol)).limit(limit).offset(offset),
|
|
5064
|
-
database.select({ count:
|
|
6436
|
+
database.select({ count: sql23`count(*)` }).from(tenantsInIam).where(whereClause)
|
|
5065
6437
|
]);
|
|
5066
6438
|
const total = Number(totalResult[0]?.count || 0);
|
|
5067
6439
|
return c.json({ tenants, total, page, limit }, 200);
|
|
5068
6440
|
};
|
|
5069
6441
|
|
|
5070
6442
|
// src/routes/tenants/handler/update-tenant.ts
|
|
5071
|
-
import { eq as
|
|
6443
|
+
import { eq as eq52, sql as sql24 } from "drizzle-orm";
|
|
5072
6444
|
var updateTenantHandler = async (c) => {
|
|
5073
6445
|
hasRoleThrow(c, ["owner", "tenant-admin"]);
|
|
5074
6446
|
const { id } = c.req.valid("param");
|
|
5075
6447
|
const body = c.req.valid("json");
|
|
5076
6448
|
const database = c.get("database");
|
|
5077
|
-
const [existing] = await database.select().from(tenantsInIam).where(
|
|
6449
|
+
const [existing] = await database.select().from(tenantsInIam).where(eq52(tenantsInIam.id, id)).limit(1);
|
|
5078
6450
|
if (!existing) {
|
|
5079
6451
|
return c.json({ error: "Tenant not found" }, 404);
|
|
5080
6452
|
}
|
|
@@ -5117,8 +6489,8 @@ var updateTenantHandler = async (c) => {
|
|
|
5117
6489
|
}
|
|
5118
6490
|
const [updated] = await database.update(tenantsInIam).set({
|
|
5119
6491
|
...updateData,
|
|
5120
|
-
updatedAt:
|
|
5121
|
-
}).where(
|
|
6492
|
+
updatedAt: sql24`CURRENT_TIMESTAMP`
|
|
6493
|
+
}).where(eq52(tenantsInIam.id, id)).returning();
|
|
5122
6494
|
if (!updated) {
|
|
5123
6495
|
return c.json({ error: "Tenant not found" }, 404);
|
|
5124
6496
|
}
|
|
@@ -5373,36 +6745,36 @@ var assignUserRoleHandler = async (c) => {
|
|
|
5373
6745
|
};
|
|
5374
6746
|
|
|
5375
6747
|
// src/routes/user-roles/handler/list-user-roles.ts
|
|
5376
|
-
import { and as
|
|
6748
|
+
import { and as and48, eq as eq53 } from "drizzle-orm";
|
|
5377
6749
|
var listUserRolesHandler = async (c) => {
|
|
5378
6750
|
const query = c.req.valid("query");
|
|
5379
6751
|
const database = c.get("database");
|
|
5380
6752
|
const tenantId = c.get("tenantId");
|
|
5381
|
-
const conditions = [
|
|
6753
|
+
const conditions = [eq53(userRolesInIam.tenantId, tenantId)];
|
|
5382
6754
|
if (query.userId) {
|
|
5383
|
-
conditions.push(
|
|
6755
|
+
conditions.push(eq53(userRolesInIam.userId, query.userId));
|
|
5384
6756
|
}
|
|
5385
6757
|
if (query.roleId) {
|
|
5386
|
-
conditions.push(
|
|
6758
|
+
conditions.push(eq53(userRolesInIam.roleId, query.roleId));
|
|
5387
6759
|
}
|
|
5388
|
-
const userRoles = await database.select().from(userRolesInIam).where(
|
|
6760
|
+
const userRoles = await database.select().from(userRolesInIam).where(and48(...conditions));
|
|
5389
6761
|
return c.json({ userRoles }, 200);
|
|
5390
6762
|
};
|
|
5391
6763
|
|
|
5392
6764
|
// src/routes/user-roles/handler/revoke-user-role.ts
|
|
5393
|
-
import { and as
|
|
6765
|
+
import { and as and49, eq as eq54 } from "drizzle-orm";
|
|
5394
6766
|
var revokeUserRoleHandler = async (c) => {
|
|
5395
6767
|
const { id } = c.req.valid("param");
|
|
5396
6768
|
const database = c.get("database");
|
|
5397
6769
|
const tenantId = c.get("tenantId");
|
|
5398
6770
|
const [existing] = await database.select().from(userRolesInIam).where(
|
|
5399
|
-
|
|
6771
|
+
and49(eq54(userRolesInIam.id, id), eq54(userRolesInIam.tenantId, tenantId))
|
|
5400
6772
|
).limit(1);
|
|
5401
6773
|
if (!existing) {
|
|
5402
6774
|
return c.json({ error: "User role not found" }, 404);
|
|
5403
6775
|
}
|
|
5404
6776
|
await database.delete(userRolesInIam).where(
|
|
5405
|
-
|
|
6777
|
+
and49(eq54(userRolesInIam.id, id), eq54(userRolesInIam.tenantId, tenantId))
|
|
5406
6778
|
);
|
|
5407
6779
|
return c.json({ message: "Role revoked from user" }, 200);
|
|
5408
6780
|
};
|
|
@@ -5526,20 +6898,20 @@ var user_roles_route_default = userRoleRoutes;
|
|
|
5526
6898
|
import { createRoute as createRoute14, OpenAPIHono as OpenAPIHono14 } from "@hono/zod-openapi";
|
|
5527
6899
|
|
|
5528
6900
|
// src/routes/users/handler/ban-user.ts
|
|
5529
|
-
import { and as
|
|
6901
|
+
import { and as and50, eq as eq55, sql as sql25 } from "drizzle-orm";
|
|
5530
6902
|
var banUserHandler = async (c) => {
|
|
5531
6903
|
const { id } = c.req.valid("param");
|
|
5532
6904
|
const body = c.req.valid("json");
|
|
5533
6905
|
const database = c.get("database");
|
|
5534
6906
|
const tenantId = c.get("tenantId");
|
|
5535
|
-
const [existing] = await database.select().from(usersInIam).where(
|
|
6907
|
+
const [existing] = await database.select().from(usersInIam).where(and50(eq55(usersInIam.id, id), eq55(usersInIam.tenantId, tenantId))).limit(1);
|
|
5536
6908
|
if (!existing) {
|
|
5537
6909
|
return c.json({ error: "User not found" }, 404);
|
|
5538
6910
|
}
|
|
5539
6911
|
const [updated] = await database.update(usersInIam).set({
|
|
5540
6912
|
bannedUntil: body.bannedUntil || null,
|
|
5541
|
-
updatedAt:
|
|
5542
|
-
}).where(
|
|
6913
|
+
updatedAt: sql25`CURRENT_TIMESTAMP`
|
|
6914
|
+
}).where(and50(eq55(usersInIam.id, id), eq55(usersInIam.tenantId, tenantId))).returning({
|
|
5543
6915
|
id: usersInIam.id,
|
|
5544
6916
|
tenantId: usersInIam.tenantId,
|
|
5545
6917
|
fullName: usersInIam.fullName,
|
|
@@ -5558,17 +6930,17 @@ var banUserHandler = async (c) => {
|
|
|
5558
6930
|
};
|
|
5559
6931
|
|
|
5560
6932
|
// src/routes/users/helper/user.ts
|
|
5561
|
-
import { and as
|
|
6933
|
+
import { and as and51, eq as eq56, sql as sql26 } from "drizzle-orm";
|
|
5562
6934
|
var checkUserExists = async ({
|
|
5563
6935
|
database,
|
|
5564
6936
|
identifier,
|
|
5565
6937
|
tenantId,
|
|
5566
6938
|
isEmail
|
|
5567
6939
|
}) => {
|
|
5568
|
-
const whereClause = isEmail ?
|
|
5569
|
-
|
|
5570
|
-
|
|
5571
|
-
) :
|
|
6940
|
+
const whereClause = isEmail ? and51(
|
|
6941
|
+
eq56(usersInIam.tenantId, tenantId),
|
|
6942
|
+
sql26`lower(${usersInIam.email}) = lower(${identifier})`
|
|
6943
|
+
) : and51(eq56(usersInIam.tenantId, tenantId), eq56(usersInIam.phone, identifier));
|
|
5572
6944
|
const [user] = await database.select().from(usersInIam).where(whereClause).limit(1);
|
|
5573
6945
|
return user || null;
|
|
5574
6946
|
};
|
|
@@ -5578,14 +6950,170 @@ var checkHandleExists = async ({
|
|
|
5578
6950
|
tenantId
|
|
5579
6951
|
}) => {
|
|
5580
6952
|
const [existingHandle] = await database.select().from(usersInIam).where(
|
|
5581
|
-
|
|
5582
|
-
|
|
5583
|
-
|
|
6953
|
+
and51(
|
|
6954
|
+
eq56(usersInIam.tenantId, tenantId),
|
|
6955
|
+
sql26`lower(${usersInIam.handle}) = lower(${handle})`
|
|
5584
6956
|
)
|
|
5585
6957
|
).limit(1);
|
|
5586
6958
|
return existingHandle || null;
|
|
5587
6959
|
};
|
|
5588
6960
|
|
|
6961
|
+
// src/routes/users/helper/invite.ts
|
|
6962
|
+
var resolveInviteUrl = ({
|
|
6963
|
+
inviteUrl,
|
|
6964
|
+
identifier,
|
|
6965
|
+
hasPassword
|
|
6966
|
+
}) => {
|
|
6967
|
+
if (inviteUrl) {
|
|
6968
|
+
return inviteUrl;
|
|
6969
|
+
}
|
|
6970
|
+
if (hasPassword) {
|
|
6971
|
+
return "/auth/sign-in";
|
|
6972
|
+
}
|
|
6973
|
+
return `/auth/set-password?identifier=${encodeURIComponent(identifier)}`;
|
|
6974
|
+
};
|
|
6975
|
+
var inviteUser = (
|
|
6976
|
+
// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: invite flow mixes validation, persistence, and delivery reporting
|
|
6977
|
+
async function inviteUser2({
|
|
6978
|
+
database,
|
|
6979
|
+
tenantId,
|
|
6980
|
+
config,
|
|
6981
|
+
payload
|
|
6982
|
+
}) {
|
|
6983
|
+
const identifier = payload.email || payload.phone;
|
|
6984
|
+
if (!identifier) {
|
|
6985
|
+
throw new Error("Either email or phone is required");
|
|
6986
|
+
}
|
|
6987
|
+
const isEmail = identifier.includes("@");
|
|
6988
|
+
if (payload.phone) {
|
|
6989
|
+
const phoneValidator = createPhoneField(config);
|
|
6990
|
+
if (!phoneValidator.validate(payload.phone)) {
|
|
6991
|
+
throw new Error("Invalid phone number format");
|
|
6992
|
+
}
|
|
6993
|
+
}
|
|
6994
|
+
const existing = await checkUserExists({
|
|
6995
|
+
database,
|
|
6996
|
+
identifier,
|
|
6997
|
+
tenantId,
|
|
6998
|
+
isEmail
|
|
6999
|
+
});
|
|
7000
|
+
if (existing) {
|
|
7001
|
+
throw new Error("User already exists");
|
|
7002
|
+
}
|
|
7003
|
+
const userHandle = payload.handle || generateHandle();
|
|
7004
|
+
const existingHandle = await checkHandleExists({
|
|
7005
|
+
database,
|
|
7006
|
+
handle: userHandle,
|
|
7007
|
+
tenantId
|
|
7008
|
+
});
|
|
7009
|
+
if (existingHandle) {
|
|
7010
|
+
throw new Error("Handle already taken");
|
|
7011
|
+
}
|
|
7012
|
+
const passwordHash = payload.password ? await hashPassword(payload.password) : null;
|
|
7013
|
+
const user = await withTransaction(database, async (tx) => {
|
|
7014
|
+
const [createdUser] = await tx.insert(usersInIam).values({
|
|
7015
|
+
tenantId,
|
|
7016
|
+
fullName: payload.fullName,
|
|
7017
|
+
handle: userHandle,
|
|
7018
|
+
email: payload.email || null,
|
|
7019
|
+
phone: payload.phone || null,
|
|
7020
|
+
image: payload.image || null,
|
|
7021
|
+
emailVerified: Boolean(payload.emailVerified),
|
|
7022
|
+
phoneVerified: Boolean(payload.phoneVerified),
|
|
7023
|
+
userType: [config.userType]
|
|
7024
|
+
}).returning();
|
|
7025
|
+
if (passwordHash) {
|
|
7026
|
+
await tx.insert(accountsInIam).values({
|
|
7027
|
+
tenantId,
|
|
7028
|
+
userId: createdUser.id,
|
|
7029
|
+
provider: "credentials",
|
|
7030
|
+
providerAccountId: identifier,
|
|
7031
|
+
password: passwordHash
|
|
7032
|
+
});
|
|
7033
|
+
}
|
|
7034
|
+
return createdUser;
|
|
7035
|
+
});
|
|
7036
|
+
const hasPassword = Boolean(passwordHash);
|
|
7037
|
+
const resolvedInviteUrl = resolveInviteUrl({
|
|
7038
|
+
inviteUrl: payload.inviteUrl,
|
|
7039
|
+
identifier,
|
|
7040
|
+
hasPassword
|
|
7041
|
+
});
|
|
7042
|
+
const delivery = {};
|
|
7043
|
+
if (payload.sendVia?.includes("email")) {
|
|
7044
|
+
if (payload.email && config.email.sendInvitation) {
|
|
7045
|
+
try {
|
|
7046
|
+
await config.email.sendInvitation({
|
|
7047
|
+
email: payload.email,
|
|
7048
|
+
fullName: payload.fullName,
|
|
7049
|
+
identifier,
|
|
7050
|
+
inviteUrl: resolvedInviteUrl,
|
|
7051
|
+
tenantId
|
|
7052
|
+
});
|
|
7053
|
+
delivery.email = "sent";
|
|
7054
|
+
} catch {
|
|
7055
|
+
delivery.email = "failed";
|
|
7056
|
+
}
|
|
7057
|
+
} else {
|
|
7058
|
+
delivery.email = "skipped";
|
|
7059
|
+
}
|
|
7060
|
+
}
|
|
7061
|
+
if (payload.sendVia?.includes("sms")) {
|
|
7062
|
+
if (payload.phone && config.phone.sendInvitation) {
|
|
7063
|
+
try {
|
|
7064
|
+
await config.phone.sendInvitation({
|
|
7065
|
+
phone: payload.phone,
|
|
7066
|
+
fullName: payload.fullName,
|
|
7067
|
+
identifier,
|
|
7068
|
+
inviteUrl: resolvedInviteUrl,
|
|
7069
|
+
tenantId
|
|
7070
|
+
});
|
|
7071
|
+
delivery.sms = "sent";
|
|
7072
|
+
} catch {
|
|
7073
|
+
delivery.sms = "failed";
|
|
7074
|
+
}
|
|
7075
|
+
} else {
|
|
7076
|
+
delivery.sms = "skipped";
|
|
7077
|
+
}
|
|
7078
|
+
}
|
|
7079
|
+
return {
|
|
7080
|
+
user: normalizeUser(user),
|
|
7081
|
+
delivery,
|
|
7082
|
+
inviteUrl: resolvedInviteUrl,
|
|
7083
|
+
hasPassword
|
|
7084
|
+
};
|
|
7085
|
+
}
|
|
7086
|
+
);
|
|
7087
|
+
|
|
7088
|
+
// src/routes/users/handler/bulk-invite-users.ts
|
|
7089
|
+
var bulkInviteUsersHandler = async (c) => {
|
|
7090
|
+
const body = c.req.valid("json");
|
|
7091
|
+
const config = c.get("config");
|
|
7092
|
+
const database = c.get("database");
|
|
7093
|
+
const tenantId = c.get("tenantId");
|
|
7094
|
+
const resolvedTenantId = ensureTenantId(config, tenantId);
|
|
7095
|
+
const invited = [];
|
|
7096
|
+
const failed = [];
|
|
7097
|
+
for (const [index2, payload] of body.users.entries()) {
|
|
7098
|
+
try {
|
|
7099
|
+
const result = await inviteUser({
|
|
7100
|
+
database,
|
|
7101
|
+
tenantId: resolvedTenantId,
|
|
7102
|
+
config,
|
|
7103
|
+
payload
|
|
7104
|
+
});
|
|
7105
|
+
invited.push(result);
|
|
7106
|
+
} catch (error) {
|
|
7107
|
+
failed.push({
|
|
7108
|
+
index: index2,
|
|
7109
|
+
identifier: payload.email || payload.phone || null,
|
|
7110
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
7111
|
+
});
|
|
7112
|
+
}
|
|
7113
|
+
}
|
|
7114
|
+
return c.json({ invited, failed }, 200);
|
|
7115
|
+
};
|
|
7116
|
+
|
|
5589
7117
|
// src/routes/users/handler/create-user.ts
|
|
5590
7118
|
var createUserHandler = async (c) => {
|
|
5591
7119
|
const body = c.req.valid("json");
|
|
@@ -5616,41 +7144,55 @@ var createUserHandler = async (c) => {
|
|
|
5616
7144
|
if (existingHandle) {
|
|
5617
7145
|
return c.json({ error: "Handle already taken" }, 409);
|
|
5618
7146
|
}
|
|
5619
|
-
const [user] = await database
|
|
5620
|
-
|
|
5621
|
-
|
|
5622
|
-
|
|
5623
|
-
|
|
5624
|
-
|
|
5625
|
-
|
|
5626
|
-
|
|
5627
|
-
|
|
5628
|
-
|
|
5629
|
-
|
|
5630
|
-
|
|
5631
|
-
|
|
5632
|
-
|
|
5633
|
-
|
|
5634
|
-
|
|
5635
|
-
|
|
5636
|
-
|
|
5637
|
-
|
|
5638
|
-
|
|
7147
|
+
const [user] = await withTransaction(database, async (tx) => {
|
|
7148
|
+
const [createdUser] = await tx.insert(usersInIam).values({
|
|
7149
|
+
tenantId: resolvedTenantId,
|
|
7150
|
+
fullName: body.fullName,
|
|
7151
|
+
handle: userHandle,
|
|
7152
|
+
email: body.email || null,
|
|
7153
|
+
phone: body.phone || null,
|
|
7154
|
+
image: body.image || null,
|
|
7155
|
+
emailVerified: Boolean(body.emailVerified),
|
|
7156
|
+
phoneVerified: Boolean(body.phoneVerified),
|
|
7157
|
+
userType: [config.userType]
|
|
7158
|
+
}).returning({
|
|
7159
|
+
id: usersInIam.id,
|
|
7160
|
+
tenantId: usersInIam.tenantId,
|
|
7161
|
+
fullName: usersInIam.fullName,
|
|
7162
|
+
email: usersInIam.email,
|
|
7163
|
+
phone: usersInIam.phone,
|
|
7164
|
+
handle: usersInIam.handle,
|
|
7165
|
+
image: usersInIam.image,
|
|
7166
|
+
emailVerified: usersInIam.emailVerified,
|
|
7167
|
+
phoneVerified: usersInIam.phoneVerified,
|
|
7168
|
+
lastSignInAt: usersInIam.lastSignInAt
|
|
7169
|
+
});
|
|
7170
|
+
if (body.password) {
|
|
7171
|
+
const passwordHash = await hashPassword(body.password);
|
|
7172
|
+
await tx.insert(accountsInIam).values({
|
|
7173
|
+
tenantId: resolvedTenantId,
|
|
7174
|
+
userId: createdUser.id,
|
|
7175
|
+
provider: "credentials",
|
|
7176
|
+
providerAccountId: identifier,
|
|
7177
|
+
password: passwordHash
|
|
7178
|
+
});
|
|
7179
|
+
}
|
|
7180
|
+
return [createdUser];
|
|
5639
7181
|
});
|
|
5640
7182
|
return c.json({ user: normalizeUser(user) }, 201);
|
|
5641
7183
|
};
|
|
5642
7184
|
|
|
5643
7185
|
// src/routes/users/handler/delete-user.ts
|
|
5644
|
-
import { and as
|
|
7186
|
+
import { and as and52, eq as eq57 } from "drizzle-orm";
|
|
5645
7187
|
var deleteUserHandler = async (c) => {
|
|
5646
7188
|
const { id } = c.req.valid("param");
|
|
5647
7189
|
const database = c.get("database");
|
|
5648
7190
|
const tenantId = c.get("tenantId");
|
|
5649
|
-
const [existing] = await database.select().from(usersInIam).where(
|
|
7191
|
+
const [existing] = await database.select().from(usersInIam).where(and52(eq57(usersInIam.id, id), eq57(usersInIam.tenantId, tenantId))).limit(1);
|
|
5650
7192
|
if (!existing) {
|
|
5651
7193
|
return c.json({ error: "User not found" }, 404);
|
|
5652
7194
|
}
|
|
5653
|
-
await database.delete(usersInIam).where(
|
|
7195
|
+
await database.delete(usersInIam).where(and52(eq57(usersInIam.id, id), eq57(usersInIam.tenantId, tenantId)));
|
|
5654
7196
|
return c.json({ message: "User deleted" }, 200);
|
|
5655
7197
|
};
|
|
5656
7198
|
|
|
@@ -5670,9 +7212,32 @@ var getUserHandler = async (c) => {
|
|
|
5670
7212
|
return c.json({ user: normalizeUser(user) }, 200);
|
|
5671
7213
|
};
|
|
5672
7214
|
|
|
7215
|
+
// src/routes/users/handler/invite-user.ts
|
|
7216
|
+
var inviteUserHandler = async (c) => {
|
|
7217
|
+
const body = c.req.valid("json");
|
|
7218
|
+
const config = c.get("config");
|
|
7219
|
+
const database = c.get("database");
|
|
7220
|
+
const tenantId = c.get("tenantId");
|
|
7221
|
+
const resolvedTenantId = ensureTenantId(config, tenantId);
|
|
7222
|
+
try {
|
|
7223
|
+
const result = await inviteUser({
|
|
7224
|
+
database,
|
|
7225
|
+
tenantId: resolvedTenantId,
|
|
7226
|
+
config,
|
|
7227
|
+
payload: body
|
|
7228
|
+
});
|
|
7229
|
+
return c.json(result, 201);
|
|
7230
|
+
} catch (error) {
|
|
7231
|
+
if (error instanceof Error) {
|
|
7232
|
+
return c.json({ error: error.message }, 409);
|
|
7233
|
+
}
|
|
7234
|
+
throw error;
|
|
7235
|
+
}
|
|
7236
|
+
};
|
|
7237
|
+
|
|
5673
7238
|
// src/routes/users/handler/list-users.ts
|
|
5674
|
-
import { and as
|
|
5675
|
-
var
|
|
7239
|
+
import { and as and53, asc as asc5, desc as desc5, eq as eq58, ilike as ilike4, sql as sql27 } from "drizzle-orm";
|
|
7240
|
+
var sortColumnMap4 = {
|
|
5676
7241
|
createdAt: usersInIam.createdAt,
|
|
5677
7242
|
updatedAt: usersInIam.updatedAt,
|
|
5678
7243
|
fullName: usersInIam.fullName,
|
|
@@ -5686,26 +7251,26 @@ var listUsersHandler = async (c) => {
|
|
|
5686
7251
|
const page = query.page || 1;
|
|
5687
7252
|
const limit = query.limit || 20;
|
|
5688
7253
|
const offset = (page - 1) * limit;
|
|
5689
|
-
const conditions = [
|
|
7254
|
+
const conditions = [eq58(usersInIam.tenantId, tenantId)];
|
|
5690
7255
|
if (query.email) {
|
|
5691
|
-
conditions.push(
|
|
7256
|
+
conditions.push(ilike4(usersInIam.email, `%${query.email}%`));
|
|
5692
7257
|
}
|
|
5693
7258
|
if (query.phone) {
|
|
5694
|
-
conditions.push(
|
|
7259
|
+
conditions.push(ilike4(usersInIam.phone, `%${query.phone}%`));
|
|
5695
7260
|
}
|
|
5696
7261
|
if (query.handle) {
|
|
5697
|
-
conditions.push(
|
|
7262
|
+
conditions.push(ilike4(usersInIam.handle, `%${query.handle}%`));
|
|
5698
7263
|
}
|
|
5699
7264
|
if (query.filter === "emailVerified") {
|
|
5700
|
-
conditions.push(
|
|
7265
|
+
conditions.push(eq58(usersInIam.emailVerified, true));
|
|
5701
7266
|
} else if (query.filter === "phoneVerified") {
|
|
5702
|
-
conditions.push(
|
|
7267
|
+
conditions.push(eq58(usersInIam.phoneVerified, true));
|
|
5703
7268
|
} else if (query.filter === "notVerified") {
|
|
5704
|
-
conditions.push(
|
|
5705
|
-
conditions.push(
|
|
7269
|
+
conditions.push(eq58(usersInIam.emailVerified, false));
|
|
7270
|
+
conditions.push(eq58(usersInIam.phoneVerified, false));
|
|
5706
7271
|
}
|
|
5707
|
-
const orderDir = query.order === "asc" ?
|
|
5708
|
-
const sortCol = query.sort &&
|
|
7272
|
+
const orderDir = query.order === "asc" ? asc5 : desc5;
|
|
7273
|
+
const sortCol = query.sort && sortColumnMap4[query.sort] ? sortColumnMap4[query.sort] : usersInIam.createdAt;
|
|
5709
7274
|
const [users, totalResult] = await Promise.all([
|
|
5710
7275
|
database.select({
|
|
5711
7276
|
id: usersInIam.id,
|
|
@@ -5718,8 +7283,8 @@ var listUsersHandler = async (c) => {
|
|
|
5718
7283
|
emailVerified: usersInIam.emailVerified,
|
|
5719
7284
|
phoneVerified: usersInIam.phoneVerified,
|
|
5720
7285
|
lastSignInAt: usersInIam.lastSignInAt
|
|
5721
|
-
}).from(usersInIam).where(
|
|
5722
|
-
database.select({ count:
|
|
7286
|
+
}).from(usersInIam).where(and53(...conditions)).orderBy(orderDir(sortCol)).limit(limit).offset(offset),
|
|
7287
|
+
database.select({ count: sql27`count(*)` }).from(usersInIam).where(and53(...conditions))
|
|
5723
7288
|
]);
|
|
5724
7289
|
const total = Number(totalResult[0]?.count || 0);
|
|
5725
7290
|
return c.json(
|
|
@@ -5737,18 +7302,18 @@ var listUsersHandler = async (c) => {
|
|
|
5737
7302
|
};
|
|
5738
7303
|
|
|
5739
7304
|
// src/routes/users/handler/search-users.ts
|
|
5740
|
-
import { and as
|
|
7305
|
+
import { and as and54, eq as eq59, ilike as ilike5, or as or4 } from "drizzle-orm";
|
|
5741
7306
|
var searchUsersHandler = async (c) => {
|
|
5742
7307
|
const query = c.req.valid("query");
|
|
5743
7308
|
const database = c.get("database");
|
|
5744
7309
|
const tenantId = c.get("tenantId");
|
|
5745
7310
|
const limit = query.limit || 20;
|
|
5746
|
-
const conditions = [
|
|
7311
|
+
const conditions = [eq59(usersInIam.tenantId, tenantId)];
|
|
5747
7312
|
if (query.search && query.search.trim().length > 0) {
|
|
5748
|
-
const searchCondition =
|
|
5749
|
-
|
|
5750
|
-
|
|
5751
|
-
|
|
7313
|
+
const searchCondition = or4(
|
|
7314
|
+
ilike5(usersInIam.fullName, `%${query.search}%`),
|
|
7315
|
+
ilike5(usersInIam.email, `%${query.search}%`),
|
|
7316
|
+
ilike5(usersInIam.handle, `%${query.search}%`)
|
|
5752
7317
|
);
|
|
5753
7318
|
if (searchCondition) {
|
|
5754
7319
|
conditions.push(searchCondition);
|
|
@@ -5761,26 +7326,26 @@ var searchUsersHandler = async (c) => {
|
|
|
5761
7326
|
phone: usersInIam.phone,
|
|
5762
7327
|
handle: usersInIam.handle,
|
|
5763
7328
|
image: usersInIam.image
|
|
5764
|
-
}).from(usersInIam).where(
|
|
7329
|
+
}).from(usersInIam).where(and54(...conditions)).limit(limit);
|
|
5765
7330
|
return c.json({ users }, 200);
|
|
5766
7331
|
};
|
|
5767
7332
|
|
|
5768
7333
|
// src/routes/users/handler/update-user.ts
|
|
5769
|
-
import { and as
|
|
7334
|
+
import { and as and55, eq as eq60, sql as sql28 } from "drizzle-orm";
|
|
5770
7335
|
var updateUserHandler = async (c) => {
|
|
5771
7336
|
const { id } = c.req.valid("param");
|
|
5772
7337
|
const body = c.req.valid("json");
|
|
5773
7338
|
const database = c.get("database");
|
|
5774
7339
|
const tenantId = c.get("tenantId");
|
|
5775
|
-
const [existing] = await database.select().from(usersInIam).where(
|
|
7340
|
+
const [existing] = await database.select().from(usersInIam).where(and55(eq60(usersInIam.id, id), eq60(usersInIam.tenantId, tenantId))).limit(1);
|
|
5776
7341
|
if (!existing) {
|
|
5777
7342
|
return c.json({ error: "User not found" }, 404);
|
|
5778
7343
|
}
|
|
5779
7344
|
if (body.handle && body.handle !== existing.handle) {
|
|
5780
7345
|
const [handleExists] = await database.select().from(usersInIam).where(
|
|
5781
|
-
|
|
5782
|
-
|
|
5783
|
-
|
|
7346
|
+
and55(
|
|
7347
|
+
eq60(usersInIam.tenantId, tenantId),
|
|
7348
|
+
sql28`lower(${usersInIam.handle}) = lower(${body.handle})`
|
|
5784
7349
|
)
|
|
5785
7350
|
).limit(1);
|
|
5786
7351
|
if (handleExists) {
|
|
@@ -5811,8 +7376,8 @@ var updateUserHandler = async (c) => {
|
|
|
5811
7376
|
}
|
|
5812
7377
|
const [updated] = await database.update(usersInIam).set({
|
|
5813
7378
|
...updateData,
|
|
5814
|
-
updatedAt:
|
|
5815
|
-
}).where(
|
|
7379
|
+
updatedAt: sql28`CURRENT_TIMESTAMP`
|
|
7380
|
+
}).where(and55(eq60(usersInIam.id, id), eq60(usersInIam.tenantId, tenantId))).returning({
|
|
5816
7381
|
id: usersInIam.id,
|
|
5817
7382
|
tenantId: usersInIam.tenantId,
|
|
5818
7383
|
fullName: usersInIam.fullName,
|
|
@@ -5865,6 +7430,7 @@ var createUserSchema = z11.object({
|
|
|
5865
7430
|
fullName: z11.string().min(1),
|
|
5866
7431
|
handle: z11.string().optional(),
|
|
5867
7432
|
image: z11.string().url().optional(),
|
|
7433
|
+
password: z11.string().min(8).max(128).optional(),
|
|
5868
7434
|
emailVerified: z11.boolean().default(false).optional(),
|
|
5869
7435
|
phoneVerified: z11.boolean().default(false).optional()
|
|
5870
7436
|
});
|
|
@@ -5895,6 +7461,46 @@ var deleteUserResponseSchema = z11.object({
|
|
|
5895
7461
|
var errorResponseSchema9 = z11.object({
|
|
5896
7462
|
error: z11.string()
|
|
5897
7463
|
});
|
|
7464
|
+
var inviteChannelSchema = z11.enum(["email", "sms"]);
|
|
7465
|
+
var inviteUserSchema = z11.object({
|
|
7466
|
+
email: z11.string().email().optional(),
|
|
7467
|
+
phone: z11.string().optional(),
|
|
7468
|
+
fullName: z11.string().min(1),
|
|
7469
|
+
handle: z11.string().optional(),
|
|
7470
|
+
image: z11.string().url().optional(),
|
|
7471
|
+
password: z11.string().min(8).max(128).optional(),
|
|
7472
|
+
emailVerified: z11.boolean().default(false).optional(),
|
|
7473
|
+
phoneVerified: z11.boolean().default(false).optional(),
|
|
7474
|
+
sendVia: z11.array(inviteChannelSchema).default([]).optional(),
|
|
7475
|
+
inviteUrl: z11.string().url().optional()
|
|
7476
|
+
}).refine((data) => data.email || data.phone, {
|
|
7477
|
+
message: "Either email or phone is required",
|
|
7478
|
+
path: ["email"]
|
|
7479
|
+
});
|
|
7480
|
+
var bulkInviteUsersSchema = z11.object({
|
|
7481
|
+
users: z11.array(inviteUserSchema).min(1)
|
|
7482
|
+
});
|
|
7483
|
+
var deliveryStateSchema = z11.enum(["sent", "skipped", "failed"]);
|
|
7484
|
+
var inviteDeliverySchema = z11.object({
|
|
7485
|
+
email: deliveryStateSchema.optional(),
|
|
7486
|
+
sms: deliveryStateSchema.optional()
|
|
7487
|
+
});
|
|
7488
|
+
var inviteUserResultSchema = z11.object({
|
|
7489
|
+
user: userSchema,
|
|
7490
|
+
delivery: inviteDeliverySchema,
|
|
7491
|
+
inviteUrl: z11.string().nullable(),
|
|
7492
|
+
hasPassword: z11.boolean()
|
|
7493
|
+
});
|
|
7494
|
+
var bulkInviteUsersResponseSchema = z11.object({
|
|
7495
|
+
invited: z11.array(inviteUserResultSchema),
|
|
7496
|
+
failed: z11.array(
|
|
7497
|
+
z11.object({
|
|
7498
|
+
index: z11.number().int().nonnegative(),
|
|
7499
|
+
identifier: z11.string().nullable(),
|
|
7500
|
+
error: z11.string()
|
|
7501
|
+
})
|
|
7502
|
+
)
|
|
7503
|
+
});
|
|
5898
7504
|
var searchUsersQuerySchema = z11.object({
|
|
5899
7505
|
search: z11.string().optional().describe("Search term"),
|
|
5900
7506
|
limit: z11.coerce.number().int().positive().optional().default(20).describe("Limit")
|
|
@@ -6113,38 +7719,96 @@ var searchUsersRoute = createRoute14({
|
|
|
6113
7719
|
}
|
|
6114
7720
|
}
|
|
6115
7721
|
});
|
|
6116
|
-
var
|
|
7722
|
+
var inviteUserRoute = createRoute14({
|
|
7723
|
+
method: "post",
|
|
7724
|
+
path: "/invite",
|
|
7725
|
+
tags: ["Users"],
|
|
7726
|
+
summary: "Invite a single user",
|
|
7727
|
+
request: {
|
|
7728
|
+
body: {
|
|
7729
|
+
content: {
|
|
7730
|
+
"application/json": {
|
|
7731
|
+
schema: inviteUserSchema
|
|
7732
|
+
}
|
|
7733
|
+
}
|
|
7734
|
+
}
|
|
7735
|
+
},
|
|
7736
|
+
responses: {
|
|
7737
|
+
201: {
|
|
7738
|
+
content: {
|
|
7739
|
+
"application/json": {
|
|
7740
|
+
schema: inviteUserResultSchema
|
|
7741
|
+
}
|
|
7742
|
+
},
|
|
7743
|
+
description: "User invited"
|
|
7744
|
+
},
|
|
7745
|
+
409: {
|
|
7746
|
+
content: {
|
|
7747
|
+
"application/json": {
|
|
7748
|
+
schema: errorResponseSchema9
|
|
7749
|
+
}
|
|
7750
|
+
},
|
|
7751
|
+
description: "User or handle already exists"
|
|
7752
|
+
}
|
|
7753
|
+
}
|
|
7754
|
+
});
|
|
7755
|
+
var bulkInviteUsersRoute = createRoute14({
|
|
7756
|
+
method: "post",
|
|
7757
|
+
path: "/invite/bulk",
|
|
7758
|
+
tags: ["Users"],
|
|
7759
|
+
summary: "Invite users in bulk",
|
|
7760
|
+
request: {
|
|
7761
|
+
body: {
|
|
7762
|
+
content: {
|
|
7763
|
+
"application/json": {
|
|
7764
|
+
schema: bulkInviteUsersSchema
|
|
7765
|
+
}
|
|
7766
|
+
}
|
|
7767
|
+
}
|
|
7768
|
+
},
|
|
7769
|
+
responses: {
|
|
7770
|
+
200: {
|
|
7771
|
+
content: {
|
|
7772
|
+
"application/json": {
|
|
7773
|
+
schema: bulkInviteUsersResponseSchema
|
|
7774
|
+
}
|
|
7775
|
+
},
|
|
7776
|
+
description: "Bulk invite results"
|
|
7777
|
+
}
|
|
7778
|
+
}
|
|
7779
|
+
});
|
|
7780
|
+
var userRoutes = new OpenAPIHono14().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);
|
|
6117
7781
|
var users_route_default = userRoutes;
|
|
6118
7782
|
|
|
6119
7783
|
// src/routes/verifications/verifications.route.ts
|
|
6120
7784
|
import { createRoute as createRoute15, OpenAPIHono as OpenAPIHono15 } from "@hono/zod-openapi";
|
|
6121
7785
|
|
|
6122
7786
|
// src/routes/verifications/handler/invalidate-verification.ts
|
|
6123
|
-
import { and as
|
|
7787
|
+
import { and as and56, eq as eq61 } from "drizzle-orm";
|
|
6124
7788
|
var invalidateVerificationHandler = async (c) => {
|
|
6125
7789
|
const { id } = c.req.valid("param");
|
|
6126
7790
|
const database = c.get("database");
|
|
6127
7791
|
const tenantId = c.get("tenantId");
|
|
6128
7792
|
const [existing] = await database.select().from(verificationsInIam).where(
|
|
6129
|
-
|
|
6130
|
-
|
|
6131
|
-
|
|
7793
|
+
and56(
|
|
7794
|
+
eq61(verificationsInIam.id, id),
|
|
7795
|
+
eq61(verificationsInIam.tenantId, tenantId)
|
|
6132
7796
|
)
|
|
6133
7797
|
).limit(1);
|
|
6134
7798
|
if (!existing) {
|
|
6135
7799
|
return c.json({ error: "Verification not found" }, 404);
|
|
6136
7800
|
}
|
|
6137
7801
|
await database.delete(verificationsInIam).where(
|
|
6138
|
-
|
|
6139
|
-
|
|
6140
|
-
|
|
7802
|
+
and56(
|
|
7803
|
+
eq61(verificationsInIam.id, id),
|
|
7804
|
+
eq61(verificationsInIam.tenantId, tenantId)
|
|
6141
7805
|
)
|
|
6142
7806
|
);
|
|
6143
7807
|
return c.json({ message: "Verification invalidated" }, 200);
|
|
6144
7808
|
};
|
|
6145
7809
|
|
|
6146
7810
|
// src/routes/verifications/handler/list-verifications.ts
|
|
6147
|
-
import { and as
|
|
7811
|
+
import { and as and57, eq as eq62, sql as sql29 } from "drizzle-orm";
|
|
6148
7812
|
var listVerificationsHandler = async (c) => {
|
|
6149
7813
|
const query = c.req.valid("query");
|
|
6150
7814
|
const database = c.get("database");
|
|
@@ -6152,27 +7816,27 @@ var listVerificationsHandler = async (c) => {
|
|
|
6152
7816
|
const page = query.page || 1;
|
|
6153
7817
|
const limit = query.limit || 20;
|
|
6154
7818
|
const offset = (page - 1) * limit;
|
|
6155
|
-
const conditions = [
|
|
7819
|
+
const conditions = [eq62(verificationsInIam.tenantId, tenantId)];
|
|
6156
7820
|
if (query.userId) {
|
|
6157
|
-
conditions.push(
|
|
7821
|
+
conditions.push(eq62(verificationsInIam.userId, query.userId));
|
|
6158
7822
|
}
|
|
6159
7823
|
if (query.type) {
|
|
6160
|
-
conditions.push(
|
|
7824
|
+
conditions.push(eq62(verificationsInIam.type, query.type));
|
|
6161
7825
|
}
|
|
6162
7826
|
if (query.status) {
|
|
6163
7827
|
if (query.status === "active") {
|
|
6164
|
-
conditions.push(
|
|
7828
|
+
conditions.push(sql29`${verificationsInIam.expiresAt} > CURRENT_TIMESTAMP`);
|
|
6165
7829
|
} else if (query.status === "expired") {
|
|
6166
7830
|
conditions.push(
|
|
6167
|
-
|
|
7831
|
+
sql29`${verificationsInIam.expiresAt} <= CURRENT_TIMESTAMP`
|
|
6168
7832
|
);
|
|
6169
7833
|
} else if (query.status === "consumed") {
|
|
6170
|
-
conditions.push(
|
|
7834
|
+
conditions.push(sql29`${verificationsInIam.attempt} >= 3`);
|
|
6171
7835
|
}
|
|
6172
7836
|
}
|
|
6173
7837
|
const [verifications, totalResult] = await Promise.all([
|
|
6174
|
-
database.select().from(verificationsInIam).where(
|
|
6175
|
-
database.select({ count:
|
|
7838
|
+
database.select().from(verificationsInIam).where(and57(...conditions)).limit(limit).offset(offset),
|
|
7839
|
+
database.select({ count: sql29`count(*)` }).from(verificationsInIam).where(and57(...conditions))
|
|
6176
7840
|
]);
|
|
6177
7841
|
const total = Number(totalResult[0]?.count || 0);
|
|
6178
7842
|
return c.json({ verifications, total, page, limit }, 200);
|
|
@@ -6473,7 +8137,7 @@ var createSessionMiddleware = () => {
|
|
|
6473
8137
|
// src/middlewares/tenant-middleware.ts
|
|
6474
8138
|
import { logger as logger3 } from "@mesob/common";
|
|
6475
8139
|
import { createMiddleware as createMiddleware2 } from "hono/factory";
|
|
6476
|
-
import { HTTPException as
|
|
8140
|
+
import { HTTPException as HTTPException5 } from "hono/http-exception";
|
|
6477
8141
|
function resolveHost(hostHeader, forwardedHost) {
|
|
6478
8142
|
const hostHeaderStr = hostHeader || "";
|
|
6479
8143
|
const forwardedHostStr = forwardedHost || "";
|
|
@@ -6537,7 +8201,7 @@ var createTenantMiddleware = (database, config) => {
|
|
|
6537
8201
|
);
|
|
6538
8202
|
c.set("host", host);
|
|
6539
8203
|
if (!host) {
|
|
6540
|
-
throw new
|
|
8204
|
+
throw new HTTPException5(400, { message: "Missing Host header" });
|
|
6541
8205
|
}
|
|
6542
8206
|
let tenantId = null;
|
|
6543
8207
|
let tenant = null;
|
|
@@ -6546,13 +8210,13 @@ var createTenantMiddleware = (database, config) => {
|
|
|
6546
8210
|
tenantId = result.tenantId;
|
|
6547
8211
|
tenant = result.tenant;
|
|
6548
8212
|
} catch {
|
|
6549
|
-
throw new
|
|
8213
|
+
throw new HTTPException5(500, { message: "Tenant resolution failed" });
|
|
6550
8214
|
}
|
|
6551
8215
|
c.set("tenantId", tenantId);
|
|
6552
8216
|
c.set("tenant", tenant);
|
|
6553
8217
|
const error = validateTenant(tenantId, tenant);
|
|
6554
8218
|
if (error) {
|
|
6555
|
-
throw new
|
|
8219
|
+
throw new HTTPException5(404, { message: error });
|
|
6556
8220
|
}
|
|
6557
8221
|
return await next();
|
|
6558
8222
|
});
|