@mesob/auth-hono 0.3.4 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{index-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 +2471 -801
- 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 +2 -2
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
|
-
|
|
1055
|
+
fullName: usersInIam.fullName,
|
|
1056
|
+
email: usersInIam.email,
|
|
1057
|
+
phone: usersInIam.phone,
|
|
1058
|
+
verified: isEmail ? usersInIam.emailVerified : usersInIam.phoneVerified,
|
|
1059
|
+
hasPassword: sql4`exists(
|
|
1060
|
+
select 1
|
|
1061
|
+
from ${accountsInIam}
|
|
1062
|
+
where ${accountsInIam.tenantId} = ${resolvedTenantId}
|
|
1063
|
+
and ${accountsInIam.userId} = ${usersInIam.id}
|
|
1064
|
+
and ${accountsInIam.provider} = 'credentials'
|
|
1065
|
+
and ${accountsInIam.password} is not null
|
|
1066
|
+
)`
|
|
995
1067
|
}).from(usersInIam).where(whereClause).limit(1);
|
|
1068
|
+
const verified = result?.verified ?? false;
|
|
1069
|
+
const hasPassword = result?.hasPassword ?? false;
|
|
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,14 +1321,18 @@ 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);
|
|
1236
1330
|
if (pendingVerification) {
|
|
1237
|
-
return {
|
|
1331
|
+
return {
|
|
1332
|
+
action: "pending",
|
|
1333
|
+
verificationId: pendingVerification.id,
|
|
1334
|
+
user: existingUser
|
|
1335
|
+
};
|
|
1238
1336
|
}
|
|
1239
1337
|
await deleteUnverifiedUser({ tx, userId: existingUser.id, tenantId });
|
|
1240
1338
|
return { action: "proceed" };
|
|
@@ -1245,18 +1343,18 @@ var deleteUnverifiedUser = async ({
|
|
|
1245
1343
|
tenantId
|
|
1246
1344
|
}) => {
|
|
1247
1345
|
await tx.delete(verificationsInIam).where(
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1346
|
+
and7(
|
|
1347
|
+
eq7(verificationsInIam.userId, userId),
|
|
1348
|
+
eq7(verificationsInIam.tenantId, tenantId)
|
|
1251
1349
|
)
|
|
1252
1350
|
);
|
|
1253
1351
|
await tx.delete(accountsInIam).where(
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1352
|
+
and7(
|
|
1353
|
+
eq7(accountsInIam.userId, userId),
|
|
1354
|
+
eq7(accountsInIam.tenantId, tenantId)
|
|
1257
1355
|
)
|
|
1258
1356
|
);
|
|
1259
|
-
await tx.delete(usersInIam).where(
|
|
1357
|
+
await tx.delete(usersInIam).where(and7(eq7(usersInIam.id, userId), eq7(usersInIam.tenantId, tenantId)));
|
|
1260
1358
|
};
|
|
1261
1359
|
var createUserWithAccount = async ({
|
|
1262
1360
|
tx,
|
|
@@ -1296,14 +1394,14 @@ var fetchUserForLogin = async ({
|
|
|
1296
1394
|
userType
|
|
1297
1395
|
}) => {
|
|
1298
1396
|
const userTypeFilter = sql6`${usersInIam.userType} @> ARRAY[${userType}]::text[]`;
|
|
1299
|
-
const whereClause = isEmail ?
|
|
1300
|
-
|
|
1397
|
+
const whereClause = isEmail ? and7(
|
|
1398
|
+
eq7(usersInIam.tenantId, tenantId),
|
|
1301
1399
|
userTypeFilter,
|
|
1302
1400
|
sql6`lower(${usersInIam.email}) = lower(${identifier})`
|
|
1303
|
-
) :
|
|
1304
|
-
|
|
1401
|
+
) : and7(
|
|
1402
|
+
eq7(usersInIam.tenantId, tenantId),
|
|
1305
1403
|
userTypeFilter,
|
|
1306
|
-
|
|
1404
|
+
eq7(usersInIam.phone, identifier)
|
|
1307
1405
|
);
|
|
1308
1406
|
const [row] = await database.select({
|
|
1309
1407
|
id: usersInIam.id,
|
|
@@ -1317,7 +1415,15 @@ var fetchUserForLogin = async ({
|
|
|
1317
1415
|
phoneVerified: usersInIam.phoneVerified,
|
|
1318
1416
|
lastSignInAt: usersInIam.lastSignInAt,
|
|
1319
1417
|
bannedUntil: usersInIam.bannedUntil,
|
|
1320
|
-
loginAttempt: usersInIam.loginAttempt
|
|
1418
|
+
loginAttempt: usersInIam.loginAttempt,
|
|
1419
|
+
hasPassword: sql6`exists(
|
|
1420
|
+
select 1
|
|
1421
|
+
from ${accountsInIam}
|
|
1422
|
+
where ${accountsInIam.tenantId} = ${tenantId}
|
|
1423
|
+
and ${accountsInIam.userId} = ${usersInIam.id}
|
|
1424
|
+
and ${accountsInIam.provider} = 'credentials'
|
|
1425
|
+
and ${accountsInIam.password} is not null
|
|
1426
|
+
)`
|
|
1321
1427
|
}).from(usersInIam).where(whereClause).limit(1);
|
|
1322
1428
|
return row || null;
|
|
1323
1429
|
};
|
|
@@ -1339,46 +1445,14 @@ var fetchUserByIdWithRoles = async ({
|
|
|
1339
1445
|
lastSignInAt: usersInIam.lastSignInAt,
|
|
1340
1446
|
bannedUntil: usersInIam.bannedUntil,
|
|
1341
1447
|
loginAttempt: usersInIam.loginAttempt,
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
'[]'::json
|
|
1345
|
-
)`,
|
|
1346
|
-
roleCodes: sql6`COALESCE(
|
|
1347
|
-
array_to_json(array_agg(DISTINCT ${rolesInIam.code}) FILTER (WHERE ${rolesInIam.code} IS NOT NULL)),
|
|
1348
|
-
'[]'::json
|
|
1349
|
-
)`,
|
|
1350
|
-
permissions: sql6`COALESCE(
|
|
1351
|
-
array_to_json(array_agg(DISTINCT ${permissionsInIam.id}) FILTER (WHERE ${permissionsInIam.id} IS NOT NULL)),
|
|
1352
|
-
'[]'::json
|
|
1353
|
-
)`
|
|
1354
|
-
}).from(usersInIam).leftJoin(
|
|
1355
|
-
userRolesInIam,
|
|
1356
|
-
and6(
|
|
1357
|
-
eq6(userRolesInIam.userId, usersInIam.id),
|
|
1358
|
-
eq6(userRolesInIam.tenantId, tenantId)
|
|
1359
|
-
)
|
|
1360
|
-
).leftJoin(
|
|
1361
|
-
rolesInIam,
|
|
1362
|
-
and6(
|
|
1363
|
-
eq6(userRolesInIam.roleId, rolesInIam.id),
|
|
1364
|
-
eq6(rolesInIam.tenantId, tenantId)
|
|
1365
|
-
)
|
|
1366
|
-
).leftJoin(
|
|
1367
|
-
rolePermissionsInIam,
|
|
1368
|
-
and6(
|
|
1369
|
-
eq6(rolePermissionsInIam.roleId, rolesInIam.id),
|
|
1370
|
-
eq6(rolePermissionsInIam.tenantId, tenantId)
|
|
1371
|
-
)
|
|
1372
|
-
).leftJoin(
|
|
1373
|
-
permissionsInIam,
|
|
1374
|
-
eq6(rolePermissionsInIam.permissionId, permissionsInIam.id)
|
|
1375
|
-
).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);
|
|
1376
1450
|
return result || null;
|
|
1377
1451
|
};
|
|
1378
1452
|
|
|
1379
1453
|
// src/routes/auth/helper/verification.ts
|
|
1380
1454
|
import { dayjs as dayjs2 } from "@mesob/common";
|
|
1381
|
-
import { and as
|
|
1455
|
+
import { and as and8, desc, eq as eq8 } from "drizzle-orm";
|
|
1382
1456
|
var createVerification = async ({
|
|
1383
1457
|
tx,
|
|
1384
1458
|
tenantId,
|
|
@@ -1444,10 +1518,10 @@ var checkVerificationResend = async ({
|
|
|
1444
1518
|
id: verificationsInIam.id,
|
|
1445
1519
|
createdAt: verificationsInIam.createdAt
|
|
1446
1520
|
}).from(verificationsInIam).where(
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1521
|
+
and8(
|
|
1522
|
+
eq8(verificationsInIam.tenantId, tenantId),
|
|
1523
|
+
eq8(verificationsInIam.userId, userId),
|
|
1524
|
+
eq8(verificationsInIam.type, type)
|
|
1451
1525
|
)
|
|
1452
1526
|
).orderBy(desc(verificationsInIam.createdAt)).limit(1);
|
|
1453
1527
|
if (!verification) {
|
|
@@ -1469,10 +1543,10 @@ var handleEmailVerification = async ({
|
|
|
1469
1543
|
return c.json({ error: "User email not found" }, 401);
|
|
1470
1544
|
}
|
|
1471
1545
|
await database.delete(verificationsInIam).where(
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1546
|
+
and8(
|
|
1547
|
+
eq8(verificationsInIam.tenantId, tenantId),
|
|
1548
|
+
eq8(verificationsInIam.userId, user.id),
|
|
1549
|
+
eq8(verificationsInIam.type, "email-verification")
|
|
1476
1550
|
)
|
|
1477
1551
|
);
|
|
1478
1552
|
const code = generateOtpCode(config.email.otpLength);
|
|
@@ -1507,7 +1581,7 @@ var handleEmailVerification = async ({
|
|
|
1507
1581
|
}
|
|
1508
1582
|
return c.json(
|
|
1509
1583
|
{
|
|
1510
|
-
user:
|
|
1584
|
+
user: normalizeAuthUser(user),
|
|
1511
1585
|
session: null,
|
|
1512
1586
|
verificationId: verification.id,
|
|
1513
1587
|
requiresVerification: true
|
|
@@ -1526,10 +1600,10 @@ var handlePhoneVerification = async ({
|
|
|
1526
1600
|
return c.json({ error: "User phone not found" }, 401);
|
|
1527
1601
|
}
|
|
1528
1602
|
await database.delete(verificationsInIam).where(
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1603
|
+
and8(
|
|
1604
|
+
eq8(verificationsInIam.tenantId, tenantId),
|
|
1605
|
+
eq8(verificationsInIam.userId, user.id),
|
|
1606
|
+
eq8(verificationsInIam.type, "phone-otp")
|
|
1533
1607
|
)
|
|
1534
1608
|
);
|
|
1535
1609
|
const code = generateOtpCode(config.phone.otpLength);
|
|
@@ -1564,7 +1638,7 @@ var handlePhoneVerification = async ({
|
|
|
1564
1638
|
}
|
|
1565
1639
|
return c.json(
|
|
1566
1640
|
{
|
|
1567
|
-
user:
|
|
1641
|
+
user: normalizeAuthUser(user),
|
|
1568
1642
|
session: null,
|
|
1569
1643
|
verificationId: verification.id,
|
|
1570
1644
|
requiresVerification: true
|
|
@@ -1574,177 +1648,178 @@ var handlePhoneVerification = async ({
|
|
|
1574
1648
|
};
|
|
1575
1649
|
|
|
1576
1650
|
// src/routes/auth/handler/sign-in.ts
|
|
1577
|
-
var signInHandler =
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
isEmail,
|
|
1596
|
-
userType: config.userType
|
|
1597
|
-
});
|
|
1598
|
-
if (!user) {
|
|
1599
|
-
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,
|
|
1600
1669
|
identifier,
|
|
1601
1670
|
tenantId: resolvedTenantId,
|
|
1671
|
+
isEmail,
|
|
1602
1672
|
userType: config.userType
|
|
1603
1673
|
});
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
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,
|
|
1614
1685
|
bannedUntil: user.bannedUntil
|
|
1615
|
-
}
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
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
|
-
if (
|
|
1644
|
-
|
|
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 (!(user.hasPassword && 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
|
+
});
|
|
1645
1760
|
}
|
|
1646
|
-
await database.update(usersInIam).set(
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
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)
|
|
1650
1765
|
)
|
|
1651
1766
|
);
|
|
1652
|
-
|
|
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,
|
|
1653
1776
|
userId: user.id,
|
|
1654
|
-
|
|
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
|
|
1655
1789
|
});
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
const isVerified = isEmail ? user.emailVerified : user.phoneVerified;
|
|
1659
|
-
if (isEmail && config.email.required && !isVerified) {
|
|
1660
|
-
return handleEmailVerification({
|
|
1661
|
-
c,
|
|
1662
|
-
user: { ...user, roles: [] },
|
|
1663
|
-
config,
|
|
1664
|
-
database,
|
|
1665
|
-
tenantId: resolvedTenantId
|
|
1790
|
+
setSessionCookie(c, sessionToken, config, {
|
|
1791
|
+
expires: new Date(expiresAt)
|
|
1666
1792
|
});
|
|
1667
|
-
|
|
1668
|
-
if (!isEmail && config.phone.required && !isVerified) {
|
|
1669
|
-
return handlePhoneVerification({
|
|
1670
|
-
c,
|
|
1671
|
-
user: { ...user, roles: [] },
|
|
1672
|
-
config,
|
|
1793
|
+
const fullUser = await fetchUserByIdWithRoles({
|
|
1673
1794
|
database,
|
|
1795
|
+
userId: user.id,
|
|
1674
1796
|
tenantId: resolvedTenantId
|
|
1675
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
|
+
);
|
|
1676
1809
|
}
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
)
|
|
1690
|
-
);
|
|
1691
|
-
const sessionToken = generateToken();
|
|
1692
|
-
const hashedToken = await hashToken(sessionToken, config.secret);
|
|
1693
|
-
const sessionDuration = getSessionDuration({
|
|
1694
|
-
sessionConfig: config.session,
|
|
1695
|
-
rememberMe
|
|
1696
|
-
});
|
|
1697
|
-
const expiresAt = addDuration(sessionDuration);
|
|
1698
|
-
const [session] = await database.insert(sessionsInIam).values({
|
|
1699
|
-
tenantId: resolvedTenantId,
|
|
1700
|
-
userId: user.id,
|
|
1701
|
-
token: hashedToken,
|
|
1702
|
-
expiresAt,
|
|
1703
|
-
userAgent: c.req.header("user-agent") || null,
|
|
1704
|
-
ip: c.req.header("cf-connecting-ip") || c.req.header("x-forwarded-for") || null,
|
|
1705
|
-
meta: { action: "sign-in", rememberMe }
|
|
1706
|
-
}).returning({
|
|
1707
|
-
id: sessionsInIam.id,
|
|
1708
|
-
tenantId: sessionsInIam.tenantId,
|
|
1709
|
-
userId: sessionsInIam.userId,
|
|
1710
|
-
expiresAt: sessionsInIam.expiresAt,
|
|
1711
|
-
createdAt: sessionsInIam.createdAt,
|
|
1712
|
-
meta: sessionsInIam.meta
|
|
1713
|
-
});
|
|
1714
|
-
setSessionCookie(c, sessionToken, config, {
|
|
1715
|
-
expires: new Date(expiresAt)
|
|
1716
|
-
});
|
|
1717
|
-
const fullUser = await fetchUserByIdWithRoles({
|
|
1718
|
-
database,
|
|
1719
|
-
userId: user.id,
|
|
1720
|
-
tenantId: resolvedTenantId
|
|
1721
|
-
});
|
|
1722
|
-
return c.json(
|
|
1723
|
-
{
|
|
1724
|
-
user: normalizeUser(fullUser ?? { ...user, roles: [] }),
|
|
1725
|
-
session: {
|
|
1726
|
-
id: session.id,
|
|
1727
|
-
expiresAt: session.expiresAt,
|
|
1728
|
-
createdAt: session.createdAt,
|
|
1729
|
-
meta: session.meta
|
|
1730
|
-
},
|
|
1731
|
-
sessionExpiresAt: session.expiresAt
|
|
1732
|
-
},
|
|
1733
|
-
200
|
|
1734
|
-
);
|
|
1735
|
-
};
|
|
1736
|
-
|
|
1737
|
-
// src/routes/auth/handler/sign-out.ts
|
|
1738
|
-
import { and as and9, eq as eq9, gt as gt4 } from "drizzle-orm";
|
|
1739
|
-
import { getCookie } from "hono/cookie";
|
|
1740
|
-
var signOutHandler = async (c) => {
|
|
1741
|
-
const config = c.get("config");
|
|
1742
|
-
const database = c.get("database");
|
|
1743
|
-
const tenantId = c.get("tenantId");
|
|
1744
|
-
ensureTenantId(config, tenantId);
|
|
1745
|
-
const sessionToken = getCookie(c, getSessionCookieName(config));
|
|
1746
|
-
if (!sessionToken) {
|
|
1747
|
-
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);
|
|
1748
1823
|
}
|
|
1749
1824
|
const hashedToken = await hashToken(sessionToken, config.secret);
|
|
1750
1825
|
const [session] = await database.select({
|
|
@@ -1757,16 +1832,16 @@ var signOutHandler = async (c) => {
|
|
|
1757
1832
|
userAgent: sessionsInIam.userAgent,
|
|
1758
1833
|
ip: sessionsInIam.ip
|
|
1759
1834
|
}).from(sessionsInIam).where(
|
|
1760
|
-
|
|
1761
|
-
|
|
1835
|
+
and10(
|
|
1836
|
+
eq10(sessionsInIam.token, hashedToken),
|
|
1762
1837
|
gt4(sessionsInIam.expiresAt, (/* @__PURE__ */ new Date()).toISOString())
|
|
1763
1838
|
)
|
|
1764
1839
|
).limit(1);
|
|
1765
1840
|
if (session) {
|
|
1766
1841
|
await database.delete(sessionsInIam).where(
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1842
|
+
and10(
|
|
1843
|
+
eq10(sessionsInIam.id, session.id),
|
|
1844
|
+
eq10(sessionsInIam.tenantId, session.tenantId)
|
|
1770
1845
|
)
|
|
1771
1846
|
);
|
|
1772
1847
|
}
|
|
@@ -1839,7 +1914,8 @@ var signUpHandler = async (c) => {
|
|
|
1839
1914
|
if (status.action === "pending") {
|
|
1840
1915
|
return {
|
|
1841
1916
|
type: "pending",
|
|
1842
|
-
verificationId: status.verificationId
|
|
1917
|
+
verificationId: status.verificationId,
|
|
1918
|
+
user: status.user
|
|
1843
1919
|
};
|
|
1844
1920
|
}
|
|
1845
1921
|
const handle = generateHandle();
|
|
@@ -1917,11 +1993,12 @@ var signUpHandler = async (c) => {
|
|
|
1917
1993
|
if (result.type === "pending") {
|
|
1918
1994
|
return c.json(
|
|
1919
1995
|
{
|
|
1920
|
-
|
|
1996
|
+
user: normalizeAuthUser(result.user),
|
|
1997
|
+
session: null,
|
|
1921
1998
|
verificationId: result.verificationId,
|
|
1922
1999
|
requiresVerification: true
|
|
1923
2000
|
},
|
|
1924
|
-
|
|
2001
|
+
201
|
|
1925
2002
|
);
|
|
1926
2003
|
}
|
|
1927
2004
|
if (result.type === "verification") {
|
|
@@ -1935,7 +2012,7 @@ var signUpHandler = async (c) => {
|
|
|
1935
2012
|
});
|
|
1936
2013
|
return c.json(
|
|
1937
2014
|
{
|
|
1938
|
-
user:
|
|
2015
|
+
user: normalizeAuthUser(result.user),
|
|
1939
2016
|
session: null,
|
|
1940
2017
|
verificationId: result.verificationId,
|
|
1941
2018
|
requiresVerification: true
|
|
@@ -1948,8 +2025,11 @@ var signUpHandler = async (c) => {
|
|
|
1948
2025
|
});
|
|
1949
2026
|
return c.json(
|
|
1950
2027
|
{
|
|
1951
|
-
user:
|
|
1952
|
-
session: {
|
|
2028
|
+
user: normalizeAuthUser(result.user),
|
|
2029
|
+
session: normalizeAuthSession({
|
|
2030
|
+
id: result.sessionId,
|
|
2031
|
+
expiresAt: result.expiresAt
|
|
2032
|
+
}),
|
|
1953
2033
|
sessionExpiresAt: result.expiresAt
|
|
1954
2034
|
},
|
|
1955
2035
|
201
|
|
@@ -2152,26 +2232,26 @@ var createDomainHandler = async (c) => {
|
|
|
2152
2232
|
};
|
|
2153
2233
|
|
|
2154
2234
|
// src/routes/domains/handler/delete-domain.ts
|
|
2155
|
-
import { and as
|
|
2235
|
+
import { and as and11, eq as eq11 } from "drizzle-orm";
|
|
2156
2236
|
var deleteDomainHandler = async (c) => {
|
|
2157
2237
|
const { id } = c.req.valid("param");
|
|
2158
2238
|
const database = c.get("database");
|
|
2159
2239
|
const tenantId = c.get("tenantId");
|
|
2160
|
-
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);
|
|
2161
2241
|
if (!existing) {
|
|
2162
2242
|
return c.json({ error: "Domain not found" }, 404);
|
|
2163
2243
|
}
|
|
2164
|
-
await database.delete(domainsInIam).where(
|
|
2244
|
+
await database.delete(domainsInIam).where(and11(eq11(domainsInIam.id, id), eq11(domainsInIam.tenantId, tenantId)));
|
|
2165
2245
|
return c.json({ message: "Domain deleted" }, 200);
|
|
2166
2246
|
};
|
|
2167
2247
|
|
|
2168
2248
|
// src/routes/domains/handler/get-domain.ts
|
|
2169
|
-
import { and as
|
|
2249
|
+
import { and as and12, eq as eq12 } from "drizzle-orm";
|
|
2170
2250
|
var getDomainHandler = async (c) => {
|
|
2171
2251
|
const { id } = c.req.valid("param");
|
|
2172
2252
|
const database = c.get("database");
|
|
2173
2253
|
const tenantId = c.get("tenantId");
|
|
2174
|
-
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);
|
|
2175
2255
|
if (!domain) {
|
|
2176
2256
|
return c.json({ error: "Domain not found" }, 404);
|
|
2177
2257
|
}
|
|
@@ -2179,7 +2259,7 @@ var getDomainHandler = async (c) => {
|
|
|
2179
2259
|
};
|
|
2180
2260
|
|
|
2181
2261
|
// src/routes/domains/handler/list-domains.ts
|
|
2182
|
-
import { and as
|
|
2262
|
+
import { and as and13, eq as eq13, sql as sql7 } from "drizzle-orm";
|
|
2183
2263
|
var listDomainsHandler = async (c) => {
|
|
2184
2264
|
const query = c.req.valid("query");
|
|
2185
2265
|
const database = c.get("database");
|
|
@@ -2187,26 +2267,26 @@ var listDomainsHandler = async (c) => {
|
|
|
2187
2267
|
const page = query.page || 1;
|
|
2188
2268
|
const limit = query.limit || 20;
|
|
2189
2269
|
const offset = (page - 1) * limit;
|
|
2190
|
-
const conditions = [
|
|
2270
|
+
const conditions = [eq13(domainsInIam.tenantId, tenantId)];
|
|
2191
2271
|
if (query.status) {
|
|
2192
|
-
conditions.push(
|
|
2272
|
+
conditions.push(eq13(domainsInIam.status, query.status));
|
|
2193
2273
|
}
|
|
2194
2274
|
const [domains, totalResult] = await Promise.all([
|
|
2195
|
-
database.select().from(domainsInIam).where(
|
|
2196
|
-
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))
|
|
2197
2277
|
]);
|
|
2198
2278
|
const total = Number(totalResult[0]?.count || 0);
|
|
2199
2279
|
return c.json({ domains, total, page, limit });
|
|
2200
2280
|
};
|
|
2201
2281
|
|
|
2202
2282
|
// src/routes/domains/handler/update-domain.ts
|
|
2203
|
-
import { and as
|
|
2283
|
+
import { and as and14, eq as eq14, sql as sql8 } from "drizzle-orm";
|
|
2204
2284
|
var updateDomainHandler = async (c) => {
|
|
2205
2285
|
const { id } = c.req.valid("param");
|
|
2206
2286
|
const body = c.req.valid("json");
|
|
2207
2287
|
const database = c.get("database");
|
|
2208
2288
|
const tenantId = c.get("tenantId");
|
|
2209
|
-
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);
|
|
2210
2290
|
if (!existing) {
|
|
2211
2291
|
return c.json({ error: "Domain not found" }, 404);
|
|
2212
2292
|
}
|
|
@@ -2226,7 +2306,7 @@ var updateDomainHandler = async (c) => {
|
|
|
2226
2306
|
const [updated] = await database.update(domainsInIam).set({
|
|
2227
2307
|
...updateData,
|
|
2228
2308
|
updatedAt: sql8`CURRENT_TIMESTAMP`
|
|
2229
|
-
}).where(
|
|
2309
|
+
}).where(and14(eq14(domainsInIam.id, id), eq14(domainsInIam.tenantId, tenantId))).returning();
|
|
2230
2310
|
if (!updated) {
|
|
2231
2311
|
return c.json({ error: "Domain not found" }, 404);
|
|
2232
2312
|
}
|
|
@@ -2234,19 +2314,19 @@ var updateDomainHandler = async (c) => {
|
|
|
2234
2314
|
};
|
|
2235
2315
|
|
|
2236
2316
|
// src/routes/domains/handler/verify-domain.ts
|
|
2237
|
-
import { and as
|
|
2317
|
+
import { and as and15, eq as eq15, sql as sql9 } from "drizzle-orm";
|
|
2238
2318
|
var verifyDomainHandler = async (c) => {
|
|
2239
2319
|
const { id } = c.req.valid("param");
|
|
2240
2320
|
const database = c.get("database");
|
|
2241
2321
|
const tenantId = c.get("tenantId");
|
|
2242
|
-
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);
|
|
2243
2323
|
if (!existing) {
|
|
2244
2324
|
return c.json({ error: "Domain not found" }, 404);
|
|
2245
2325
|
}
|
|
2246
2326
|
const [updated] = await database.update(domainsInIam).set({
|
|
2247
2327
|
status: "active",
|
|
2248
2328
|
updatedAt: sql9`CURRENT_TIMESTAMP`
|
|
2249
|
-
}).where(
|
|
2329
|
+
}).where(and15(eq15(domainsInIam.id, id), eq15(domainsInIam.tenantId, tenantId))).returning();
|
|
2250
2330
|
if (!updated) {
|
|
2251
2331
|
return c.json({ error: "Domain not found" }, 404);
|
|
2252
2332
|
}
|
|
@@ -2420,7 +2500,7 @@ var domains_route_default = domainRoutes;
|
|
|
2420
2500
|
import { createRoute as createRoute3, OpenAPIHono as OpenAPIHono3 } from "@hono/zod-openapi";
|
|
2421
2501
|
|
|
2422
2502
|
// src/routes/email/handler/verification-confirm.ts
|
|
2423
|
-
import { and as
|
|
2503
|
+
import { and as and16, eq as eq16 } from "drizzle-orm";
|
|
2424
2504
|
var emailVerificationConfirmHandler = async (c) => {
|
|
2425
2505
|
const body = c.req.valid("json");
|
|
2426
2506
|
const config = c.get("config");
|
|
@@ -2430,9 +2510,9 @@ var emailVerificationConfirmHandler = async (c) => {
|
|
|
2430
2510
|
const { verificationId, code } = body;
|
|
2431
2511
|
const result = await withTransaction(database, async (tx) => {
|
|
2432
2512
|
const [verification] = await tx.select().from(verificationsInIam).where(
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2513
|
+
and16(
|
|
2514
|
+
eq16(verificationsInIam.id, verificationId),
|
|
2515
|
+
eq16(verificationsInIam.tenantId, resolvedTenantId)
|
|
2436
2516
|
)
|
|
2437
2517
|
).limit(1);
|
|
2438
2518
|
if (!verification || verification.type !== "email-verification") {
|
|
@@ -2448,7 +2528,7 @@ var emailVerificationConfirmHandler = async (c) => {
|
|
|
2448
2528
|
};
|
|
2449
2529
|
}
|
|
2450
2530
|
if ((verification.attempt || 0) >= config.email.maxAttempts) {
|
|
2451
|
-
await tx.delete(verificationsInIam).where(
|
|
2531
|
+
await tx.delete(verificationsInIam).where(eq16(verificationsInIam.id, verificationId));
|
|
2452
2532
|
return {
|
|
2453
2533
|
status: "error",
|
|
2454
2534
|
error: AUTH_ERRORS.TOO_MANY_ATTEMPTS
|
|
@@ -2456,7 +2536,7 @@ var emailVerificationConfirmHandler = async (c) => {
|
|
|
2456
2536
|
}
|
|
2457
2537
|
const hashedCode = await hashToken(code, config.secret);
|
|
2458
2538
|
if (verification.code !== hashedCode) {
|
|
2459
|
-
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));
|
|
2460
2540
|
return {
|
|
2461
2541
|
status: "error",
|
|
2462
2542
|
error: AUTH_ERRORS.VERIFICATION_MISMATCH
|
|
@@ -2466,12 +2546,12 @@ var emailVerificationConfirmHandler = async (c) => {
|
|
|
2466
2546
|
emailVerified: true,
|
|
2467
2547
|
lastSignInAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2468
2548
|
}).where(
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2549
|
+
and16(
|
|
2550
|
+
eq16(usersInIam.id, verification.userId),
|
|
2551
|
+
eq16(usersInIam.tenantId, resolvedTenantId)
|
|
2472
2552
|
)
|
|
2473
2553
|
);
|
|
2474
|
-
await tx.delete(verificationsInIam).where(
|
|
2554
|
+
await tx.delete(verificationsInIam).where(eq16(verificationsInIam.id, verificationId));
|
|
2475
2555
|
const user = await fetchUserWithRoles({
|
|
2476
2556
|
database: tx,
|
|
2477
2557
|
userId: verification.userId,
|
|
@@ -2506,14 +2586,8 @@ var emailVerificationConfirmHandler = async (c) => {
|
|
|
2506
2586
|
});
|
|
2507
2587
|
return c.json(
|
|
2508
2588
|
{
|
|
2509
|
-
user:
|
|
2510
|
-
session:
|
|
2511
|
-
id: result.session.id,
|
|
2512
|
-
expiresAt: result.session.expiresAt,
|
|
2513
|
-
createdAt: result.session.createdAt,
|
|
2514
|
-
userAgent: result.session.userAgent,
|
|
2515
|
-
ip: result.session.ip
|
|
2516
|
-
},
|
|
2589
|
+
user: normalizeAuthUser(result.user),
|
|
2590
|
+
session: normalizeAuthSession(result.session),
|
|
2517
2591
|
sessionExpiresAt: result.session.expiresAt
|
|
2518
2592
|
},
|
|
2519
2593
|
200
|
|
@@ -2521,7 +2595,7 @@ var emailVerificationConfirmHandler = async (c) => {
|
|
|
2521
2595
|
};
|
|
2522
2596
|
|
|
2523
2597
|
// src/routes/email/handler/verification-request.ts
|
|
2524
|
-
import { and as
|
|
2598
|
+
import { and as and17, eq as eq17, sql as sql10 } from "drizzle-orm";
|
|
2525
2599
|
var emailVerificationRequestHandler = async (c) => {
|
|
2526
2600
|
const body = c.req.valid("json");
|
|
2527
2601
|
const config = c.get("config");
|
|
@@ -2542,8 +2616,8 @@ var emailVerificationRequestHandler = async (c) => {
|
|
|
2542
2616
|
let userId = user?.id;
|
|
2543
2617
|
if (!userId) {
|
|
2544
2618
|
const [result] = await database.select({ id: usersInIam.id }).from(usersInIam).where(
|
|
2545
|
-
|
|
2546
|
-
|
|
2619
|
+
and17(
|
|
2620
|
+
eq17(usersInIam.tenantId, resolvedTenantId),
|
|
2547
2621
|
sql10`lower(${usersInIam.email}) = lower(${email})`
|
|
2548
2622
|
)
|
|
2549
2623
|
).limit(1);
|
|
@@ -2569,10 +2643,10 @@ var emailVerificationRequestHandler = async (c) => {
|
|
|
2569
2643
|
);
|
|
2570
2644
|
}
|
|
2571
2645
|
await database.delete(verificationsInIam).where(
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2646
|
+
and17(
|
|
2647
|
+
eq17(verificationsInIam.tenantId, resolvedTenantId),
|
|
2648
|
+
eq17(verificationsInIam.userId, userId),
|
|
2649
|
+
eq17(verificationsInIam.type, "email-verification")
|
|
2576
2650
|
)
|
|
2577
2651
|
);
|
|
2578
2652
|
const code = generateOtpCode(config.email.otpLength);
|
|
@@ -2604,11 +2678,11 @@ var emailVerificationRequestHandler = async (c) => {
|
|
|
2604
2678
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2605
2679
|
reason: "replaced"
|
|
2606
2680
|
}).where(
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2681
|
+
and17(
|
|
2682
|
+
eq17(accountChangesInIam.tenantId, resolvedTenantId),
|
|
2683
|
+
eq17(accountChangesInIam.userId, user.id),
|
|
2684
|
+
eq17(accountChangesInIam.changeType, "email"),
|
|
2685
|
+
eq17(accountChangesInIam.status, "pending")
|
|
2612
2686
|
)
|
|
2613
2687
|
);
|
|
2614
2688
|
await database.insert(accountChangesInIam).values({
|
|
@@ -2723,7 +2797,7 @@ var email_route_default = emailRoutes;
|
|
|
2723
2797
|
import { createRoute as createRoute4, OpenAPIHono as OpenAPIHono4 } from "@hono/zod-openapi";
|
|
2724
2798
|
|
|
2725
2799
|
// src/routes/password/handler/change.ts
|
|
2726
|
-
import { and as
|
|
2800
|
+
import { and as and18, eq as eq18, gt as gt5, ne } from "drizzle-orm";
|
|
2727
2801
|
import { getCookie as getCookie2 } from "hono/cookie";
|
|
2728
2802
|
var changePasswordHandler = async (c) => {
|
|
2729
2803
|
const body = c.req.valid("json");
|
|
@@ -2741,10 +2815,10 @@ var changePasswordHandler = async (c) => {
|
|
|
2741
2815
|
const resolvedTenantId = ensureTenantId(config, tenantId);
|
|
2742
2816
|
const { currentPassword, newPassword } = body;
|
|
2743
2817
|
const [account] = await database.select().from(accountsInIam).where(
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2818
|
+
and18(
|
|
2819
|
+
eq18(accountsInIam.tenantId, resolvedTenantId),
|
|
2820
|
+
eq18(accountsInIam.userId, userId),
|
|
2821
|
+
eq18(accountsInIam.provider, "credentials")
|
|
2748
2822
|
)
|
|
2749
2823
|
).limit(1);
|
|
2750
2824
|
if (!account?.password) {
|
|
@@ -2756,19 +2830,19 @@ var changePasswordHandler = async (c) => {
|
|
|
2756
2830
|
}
|
|
2757
2831
|
const passwordHash = await hashPassword(newPassword);
|
|
2758
2832
|
await database.update(accountsInIam).set({ password: passwordHash }).where(
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2833
|
+
and18(
|
|
2834
|
+
eq18(accountsInIam.tenantId, resolvedTenantId),
|
|
2835
|
+
eq18(accountsInIam.userId, userId),
|
|
2836
|
+
eq18(accountsInIam.provider, "credentials")
|
|
2763
2837
|
)
|
|
2764
2838
|
);
|
|
2765
2839
|
const currentSessionToken = getCookie2(c, getSessionCookieName(config));
|
|
2766
2840
|
if (currentSessionToken) {
|
|
2767
2841
|
const hashedToken = await hashToken(currentSessionToken, config.secret);
|
|
2768
2842
|
await database.delete(sessionsInIam).where(
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2843
|
+
and18(
|
|
2844
|
+
eq18(sessionsInIam.tenantId, resolvedTenantId),
|
|
2845
|
+
eq18(sessionsInIam.userId, userId),
|
|
2772
2846
|
gt5(sessionsInIam.expiresAt, (/* @__PURE__ */ new Date()).toISOString()),
|
|
2773
2847
|
ne(sessionsInIam.token, hashedToken)
|
|
2774
2848
|
)
|
|
@@ -2778,7 +2852,7 @@ var changePasswordHandler = async (c) => {
|
|
|
2778
2852
|
};
|
|
2779
2853
|
|
|
2780
2854
|
// src/routes/password/handler/forgot.ts
|
|
2781
|
-
import { and as
|
|
2855
|
+
import { and as and19, eq as eq19, sql as sql11 } from "drizzle-orm";
|
|
2782
2856
|
var forgotPasswordHandler = async (c) => {
|
|
2783
2857
|
const body = c.req.valid("json");
|
|
2784
2858
|
const config = c.get("config");
|
|
@@ -2812,19 +2886,19 @@ var forgotPasswordHandler = async (c) => {
|
|
|
2812
2886
|
)`
|
|
2813
2887
|
}).from(usersInIam).leftJoin(
|
|
2814
2888
|
userRolesInIam,
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2889
|
+
and19(
|
|
2890
|
+
eq19(userRolesInIam.userId, usersInIam.id),
|
|
2891
|
+
eq19(userRolesInIam.tenantId, resolvedTenantId)
|
|
2818
2892
|
)
|
|
2819
2893
|
).leftJoin(
|
|
2820
2894
|
rolesInIam,
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2895
|
+
and19(
|
|
2896
|
+
eq19(userRolesInIam.roleId, rolesInIam.id),
|
|
2897
|
+
eq19(rolesInIam.tenantId, resolvedTenantId)
|
|
2824
2898
|
)
|
|
2825
2899
|
).where(
|
|
2826
|
-
|
|
2827
|
-
|
|
2900
|
+
and19(
|
|
2901
|
+
eq19(usersInIam.tenantId, resolvedTenantId),
|
|
2828
2902
|
sql11`lower(${usersInIam.email}) = lower(${identifier})`
|
|
2829
2903
|
)
|
|
2830
2904
|
).groupBy(usersInIam.id).limit(1);
|
|
@@ -2847,20 +2921,20 @@ var forgotPasswordHandler = async (c) => {
|
|
|
2847
2921
|
)`
|
|
2848
2922
|
}).from(usersInIam).leftJoin(
|
|
2849
2923
|
userRolesInIam,
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2924
|
+
and19(
|
|
2925
|
+
eq19(userRolesInIam.userId, usersInIam.id),
|
|
2926
|
+
eq19(userRolesInIam.tenantId, resolvedTenantId)
|
|
2853
2927
|
)
|
|
2854
2928
|
).leftJoin(
|
|
2855
2929
|
rolesInIam,
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2930
|
+
and19(
|
|
2931
|
+
eq19(userRolesInIam.roleId, rolesInIam.id),
|
|
2932
|
+
eq19(rolesInIam.tenantId, resolvedTenantId)
|
|
2859
2933
|
)
|
|
2860
2934
|
).where(
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2935
|
+
and19(
|
|
2936
|
+
eq19(usersInIam.tenantId, resolvedTenantId),
|
|
2937
|
+
eq19(usersInIam.phone, identifier)
|
|
2864
2938
|
)
|
|
2865
2939
|
).groupBy(usersInIam.id).limit(1);
|
|
2866
2940
|
user = result || null;
|
|
@@ -2874,10 +2948,10 @@ var forgotPasswordHandler = async (c) => {
|
|
|
2874
2948
|
return c.json({ message: "If account exists, reset code sent" }, 200);
|
|
2875
2949
|
}
|
|
2876
2950
|
await database.delete(verificationsInIam).where(
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2951
|
+
and19(
|
|
2952
|
+
eq19(verificationsInIam.tenantId, resolvedTenantId),
|
|
2953
|
+
eq19(verificationsInIam.userId, user.id),
|
|
2954
|
+
eq19(verificationsInIam.type, "password-reset")
|
|
2881
2955
|
)
|
|
2882
2956
|
);
|
|
2883
2957
|
const code = generateOtpCode(config.email.otpLength);
|
|
@@ -2916,10 +2990,10 @@ var forgotPasswordHandler = async (c) => {
|
|
|
2916
2990
|
return c.json({ message: "If account exists, reset code sent" }, 200);
|
|
2917
2991
|
}
|
|
2918
2992
|
await database.delete(verificationsInIam).where(
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2993
|
+
and19(
|
|
2994
|
+
eq19(verificationsInIam.tenantId, resolvedTenantId),
|
|
2995
|
+
eq19(verificationsInIam.userId, user.id),
|
|
2996
|
+
eq19(verificationsInIam.type, "password-reset-otp")
|
|
2923
2997
|
)
|
|
2924
2998
|
);
|
|
2925
2999
|
const code = generateOtpCode(config.phone.otpLength);
|
|
@@ -2966,7 +3040,7 @@ var forgotPasswordHandler = async (c) => {
|
|
|
2966
3040
|
};
|
|
2967
3041
|
|
|
2968
3042
|
// src/routes/password/handler/reset.ts
|
|
2969
|
-
import { and as
|
|
3043
|
+
import { and as and20, eq as eq20, gt as gt6 } from "drizzle-orm";
|
|
2970
3044
|
|
|
2971
3045
|
// src/routes/password/helper/session.ts
|
|
2972
3046
|
var createPasswordResetSession = async ({
|
|
@@ -2993,14 +3067,8 @@ var createPasswordResetSession = async ({
|
|
|
2993
3067
|
});
|
|
2994
3068
|
return c.json(
|
|
2995
3069
|
{
|
|
2996
|
-
user:
|
|
2997
|
-
session:
|
|
2998
|
-
id: session.id,
|
|
2999
|
-
expiresAt: session.expiresAt,
|
|
3000
|
-
createdAt: session.createdAt,
|
|
3001
|
-
userAgent: session.userAgent,
|
|
3002
|
-
ip: session.ip
|
|
3003
|
-
},
|
|
3070
|
+
user: normalizeAuthUser(user),
|
|
3071
|
+
session: normalizeAuthSession(session),
|
|
3004
3072
|
sessionExpiresAt: session.expiresAt
|
|
3005
3073
|
},
|
|
3006
3074
|
200
|
|
@@ -3016,9 +3084,9 @@ var resetPasswordHandler = async (c) => {
|
|
|
3016
3084
|
const resolvedTenantId = ensureTenantId(config, tenantId);
|
|
3017
3085
|
const { verificationId, code, password } = body;
|
|
3018
3086
|
const [verification] = await database.select().from(verificationsInIam).where(
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3087
|
+
and20(
|
|
3088
|
+
eq20(verificationsInIam.id, verificationId),
|
|
3089
|
+
eq20(verificationsInIam.tenantId, resolvedTenantId)
|
|
3022
3090
|
)
|
|
3023
3091
|
).limit(1);
|
|
3024
3092
|
if (!verification) {
|
|
@@ -3032,34 +3100,34 @@ var resetPasswordHandler = async (c) => {
|
|
|
3032
3100
|
}
|
|
3033
3101
|
const maxAttempts = verification.type === "password-reset" ? config.email.maxAttempts : config.phone.maxAttempts;
|
|
3034
3102
|
if ((verification.attempt || 0) >= maxAttempts) {
|
|
3035
|
-
await database.delete(verificationsInIam).where(
|
|
3103
|
+
await database.delete(verificationsInIam).where(eq20(verificationsInIam.id, verificationId));
|
|
3036
3104
|
return c.json({ error: AUTH_ERRORS.TOO_MANY_ATTEMPTS }, 400);
|
|
3037
3105
|
}
|
|
3038
3106
|
const hashedCode = await hashToken(code, config.secret);
|
|
3039
3107
|
if (verification.code !== hashedCode) {
|
|
3040
|
-
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));
|
|
3041
3109
|
return c.json({ error: AUTH_ERRORS.VERIFICATION_MISMATCH }, 400);
|
|
3042
3110
|
}
|
|
3043
3111
|
const passwordHash = await hashPassword(password);
|
|
3044
3112
|
await database.update(accountsInIam).set({ password: passwordHash }).where(
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3113
|
+
and20(
|
|
3114
|
+
eq20(accountsInIam.tenantId, resolvedTenantId),
|
|
3115
|
+
eq20(accountsInIam.userId, verification.userId),
|
|
3116
|
+
eq20(accountsInIam.provider, "credentials")
|
|
3049
3117
|
)
|
|
3050
3118
|
);
|
|
3051
3119
|
await database.delete(sessionsInIam).where(
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3120
|
+
and20(
|
|
3121
|
+
eq20(sessionsInIam.tenantId, resolvedTenantId),
|
|
3122
|
+
eq20(sessionsInIam.userId, verification.userId),
|
|
3055
3123
|
gt6(sessionsInIam.expiresAt, (/* @__PURE__ */ new Date()).toISOString())
|
|
3056
3124
|
)
|
|
3057
3125
|
);
|
|
3058
|
-
await database.delete(verificationsInIam).where(
|
|
3126
|
+
await database.delete(verificationsInIam).where(eq20(verificationsInIam.id, verificationId));
|
|
3059
3127
|
const [user] = await database.select().from(usersInIam).where(
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3128
|
+
and20(
|
|
3129
|
+
eq20(usersInIam.id, verification.userId),
|
|
3130
|
+
eq20(usersInIam.tenantId, resolvedTenantId)
|
|
3063
3131
|
)
|
|
3064
3132
|
).limit(1);
|
|
3065
3133
|
if (!user) {
|
|
@@ -3074,8 +3142,97 @@ var resetPasswordHandler = async (c) => {
|
|
|
3074
3142
|
});
|
|
3075
3143
|
};
|
|
3076
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
|
+
|
|
3077
3234
|
// src/routes/password/handler/verify.ts
|
|
3078
|
-
import { and as
|
|
3235
|
+
import { and as and22, eq as eq22 } from "drizzle-orm";
|
|
3079
3236
|
var verifyPasswordHandler = async (c) => {
|
|
3080
3237
|
const body = c.req.valid("json");
|
|
3081
3238
|
const config = c.get("config");
|
|
@@ -3092,10 +3249,10 @@ var verifyPasswordHandler = async (c) => {
|
|
|
3092
3249
|
const resolvedTenantId = ensureTenantId(config, tenantId);
|
|
3093
3250
|
const { password } = body;
|
|
3094
3251
|
const [account] = await database.select().from(accountsInIam).where(
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3252
|
+
and22(
|
|
3253
|
+
eq22(accountsInIam.tenantId, resolvedTenantId),
|
|
3254
|
+
eq22(accountsInIam.userId, userId),
|
|
3255
|
+
eq22(accountsInIam.provider, "credentials")
|
|
3099
3256
|
)
|
|
3100
3257
|
).limit(1);
|
|
3101
3258
|
if (!account?.password) {
|
|
@@ -3217,56 +3374,375 @@ var changePasswordRoute = createRoute4({
|
|
|
3217
3374
|
}
|
|
3218
3375
|
}
|
|
3219
3376
|
});
|
|
3220
|
-
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);
|
|
3221
3411
|
var password_route_default = passwordRoutes;
|
|
3222
3412
|
|
|
3223
3413
|
// src/routes/permissions/permissions.route.ts
|
|
3224
3414
|
import { createRoute as createRoute5, OpenAPIHono as OpenAPIHono5 } from "@hono/zod-openapi";
|
|
3225
3415
|
|
|
3226
3416
|
// src/routes/permissions/handler/get-permission.ts
|
|
3227
|
-
import {
|
|
3417
|
+
import { getPermissionEntries } from "@mesob/common";
|
|
3418
|
+
import { eq as eq23 } from "drizzle-orm";
|
|
3228
3419
|
var getPermissionHandler = async (c) => {
|
|
3229
3420
|
const { id } = c.req.valid("param");
|
|
3230
3421
|
const database = c.get("database");
|
|
3231
|
-
const
|
|
3422
|
+
const config = c.get("config");
|
|
3423
|
+
const [permission] = await database.select().from(permissionsInIam).where(eq23(permissionsInIam.id, id)).limit(1);
|
|
3232
3424
|
if (!permission) {
|
|
3233
|
-
|
|
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
|
+
);
|
|
3234
3443
|
}
|
|
3235
3444
|
return c.json({ permission }, 200);
|
|
3236
3445
|
};
|
|
3237
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
|
+
|
|
3238
3515
|
// src/routes/permissions/handler/list-permissions.ts
|
|
3239
|
-
import { and as and21, ilike, sql as sql12 } from "drizzle-orm";
|
|
3240
3516
|
var listPermissionsHandler = async (c) => {
|
|
3241
3517
|
const query = c.req.valid("query");
|
|
3242
3518
|
const database = c.get("database");
|
|
3519
|
+
const config = c.get("config");
|
|
3243
3520
|
const page = query.page || 1;
|
|
3244
3521
|
const limit = query.limit || 20;
|
|
3245
3522
|
const offset = (page - 1) * limit;
|
|
3246
|
-
const
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
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);
|
|
3530
|
+
return c.json({ permissions, total, page, limit }, 200);
|
|
3531
|
+
};
|
|
3532
|
+
|
|
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
|
+
)
|
|
3250
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
|
+
});
|
|
3251
3701
|
}
|
|
3252
|
-
|
|
3253
|
-
|
|
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);
|
|
3254
3711
|
}
|
|
3255
|
-
const
|
|
3256
|
-
database
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
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
|
+
);
|
|
3261
3723
|
};
|
|
3262
3724
|
|
|
3263
3725
|
// src/routes/permissions/permissions.schema.ts
|
|
3264
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
|
+
];
|
|
3265
3739
|
var listPermissionsQuerySchema = z3.object({
|
|
3266
3740
|
page: z3.coerce.number().min(1).default(1).optional(),
|
|
3267
3741
|
limit: z3.coerce.number().min(1).max(100).default(20).optional(),
|
|
3268
|
-
|
|
3269
|
-
|
|
3742
|
+
search: z3.string().optional(),
|
|
3743
|
+
filter: z3.enum(permissionFilterValues).optional(),
|
|
3744
|
+
sort: z3.enum(permissionSortValues).optional(),
|
|
3745
|
+
order: z3.enum(["asc", "desc"]).optional()
|
|
3270
3746
|
});
|
|
3271
3747
|
var permissionIdParamSchema = z3.object({
|
|
3272
3748
|
id: z3.string()
|
|
@@ -3278,6 +3754,10 @@ var permissionSchema = z3.object({
|
|
|
3278
3754
|
application: z3.string(),
|
|
3279
3755
|
feature: z3.string()
|
|
3280
3756
|
});
|
|
3757
|
+
var seedPermissionsResponseSchema = z3.object({
|
|
3758
|
+
permissions: z3.array(permissionSchema),
|
|
3759
|
+
total: z3.number()
|
|
3760
|
+
});
|
|
3281
3761
|
var listPermissionsResponseSchema = z3.object({
|
|
3282
3762
|
permissions: z3.array(permissionSchema),
|
|
3283
3763
|
total: z3.number(),
|
|
@@ -3338,14 +3818,38 @@ var getPermissionRoute = createRoute5({
|
|
|
3338
3818
|
}
|
|
3339
3819
|
}
|
|
3340
3820
|
});
|
|
3341
|
-
var
|
|
3342
|
-
|
|
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);
|
|
3846
|
+
var permissions_route_default = permissionRoutes;
|
|
3343
3847
|
|
|
3344
3848
|
// src/routes/phone/phone.route.ts
|
|
3345
3849
|
import { createRoute as createRoute6, OpenAPIHono as OpenAPIHono6 } from "@hono/zod-openapi";
|
|
3346
3850
|
|
|
3347
3851
|
// src/routes/phone/handler/verification-confirm.ts
|
|
3348
|
-
import { and as
|
|
3852
|
+
import { and as and24, eq as eq25 } from "drizzle-orm";
|
|
3349
3853
|
|
|
3350
3854
|
// src/routes/phone/helper/session.ts
|
|
3351
3855
|
var shouldCreateSession = (context) => context === "sign-in" || context === "change-phone" || context === "sign-up";
|
|
@@ -3361,9 +3865,9 @@ var phoneVerificationConfirmHandler = async (c) => {
|
|
|
3361
3865
|
const { verificationId, code, context } = body;
|
|
3362
3866
|
const result = await withTransaction(database, async (tx) => {
|
|
3363
3867
|
const [verification] = await tx.select().from(verificationsInIam).where(
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3868
|
+
and24(
|
|
3869
|
+
eq25(verificationsInIam.id, verificationId),
|
|
3870
|
+
eq25(verificationsInIam.tenantId, resolvedTenantId)
|
|
3367
3871
|
)
|
|
3368
3872
|
).limit(1);
|
|
3369
3873
|
const expectedType = `phone-otp-${context}`;
|
|
@@ -3380,7 +3884,7 @@ var phoneVerificationConfirmHandler = async (c) => {
|
|
|
3380
3884
|
};
|
|
3381
3885
|
}
|
|
3382
3886
|
if ((verification.attempt || 0) >= config.phone.maxAttempts) {
|
|
3383
|
-
await tx.delete(verificationsInIam).where(
|
|
3887
|
+
await tx.delete(verificationsInIam).where(eq25(verificationsInIam.id, verificationId));
|
|
3384
3888
|
return {
|
|
3385
3889
|
status: "error",
|
|
3386
3890
|
error: AUTH_ERRORS.TOO_MANY_ATTEMPTS
|
|
@@ -3388,21 +3892,21 @@ var phoneVerificationConfirmHandler = async (c) => {
|
|
|
3388
3892
|
}
|
|
3389
3893
|
const hashedCode = await hashToken(code, config.secret);
|
|
3390
3894
|
if (verification.code !== hashedCode) {
|
|
3391
|
-
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));
|
|
3392
3896
|
return {
|
|
3393
3897
|
status: "error",
|
|
3394
3898
|
error: AUTH_ERRORS.VERIFICATION_MISMATCH
|
|
3395
3899
|
};
|
|
3396
3900
|
}
|
|
3397
|
-
await tx.delete(verificationsInIam).where(
|
|
3901
|
+
await tx.delete(verificationsInIam).where(eq25(verificationsInIam.id, verificationId));
|
|
3398
3902
|
if (shouldCreateSession(context) && verification.userId) {
|
|
3399
3903
|
await tx.update(usersInIam).set({
|
|
3400
3904
|
phoneVerified: true,
|
|
3401
3905
|
lastSignInAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3402
3906
|
}).where(
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3907
|
+
and24(
|
|
3908
|
+
eq25(usersInIam.id, verification.userId),
|
|
3909
|
+
eq25(usersInIam.tenantId, resolvedTenantId)
|
|
3406
3910
|
)
|
|
3407
3911
|
);
|
|
3408
3912
|
}
|
|
@@ -3444,7 +3948,7 @@ var phoneVerificationConfirmHandler = async (c) => {
|
|
|
3444
3948
|
if (!result.session) {
|
|
3445
3949
|
return c.json(
|
|
3446
3950
|
{
|
|
3447
|
-
user:
|
|
3951
|
+
user: normalizeAuthUser(result.user),
|
|
3448
3952
|
session: null
|
|
3449
3953
|
},
|
|
3450
3954
|
200
|
|
@@ -3455,14 +3959,8 @@ var phoneVerificationConfirmHandler = async (c) => {
|
|
|
3455
3959
|
});
|
|
3456
3960
|
return c.json(
|
|
3457
3961
|
{
|
|
3458
|
-
user:
|
|
3459
|
-
session:
|
|
3460
|
-
id: result.session.id,
|
|
3461
|
-
expiresAt: result.session.expiresAt,
|
|
3462
|
-
createdAt: result.session.createdAt,
|
|
3463
|
-
userAgent: result.session.userAgent,
|
|
3464
|
-
ip: result.session.ip
|
|
3465
|
-
},
|
|
3962
|
+
user: normalizeAuthUser(result.user),
|
|
3963
|
+
session: normalizeAuthSession(result.session),
|
|
3466
3964
|
sessionExpiresAt: result.session.expiresAt
|
|
3467
3965
|
},
|
|
3468
3966
|
200
|
|
@@ -3470,7 +3968,7 @@ var phoneVerificationConfirmHandler = async (c) => {
|
|
|
3470
3968
|
};
|
|
3471
3969
|
|
|
3472
3970
|
// src/routes/phone/handler/verification-request.ts
|
|
3473
|
-
import { and as
|
|
3971
|
+
import { and as and25, eq as eq26 } from "drizzle-orm";
|
|
3474
3972
|
var phoneVerificationRequestHandler = async (c) => {
|
|
3475
3973
|
const body = c.req.valid("json");
|
|
3476
3974
|
const config = c.get("config");
|
|
@@ -3495,9 +3993,9 @@ var phoneVerificationRequestHandler = async (c) => {
|
|
|
3495
3993
|
let userId = user?.id;
|
|
3496
3994
|
if (!userId) {
|
|
3497
3995
|
const [result] = await database.select({ id: usersInIam.id }).from(usersInIam).where(
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3996
|
+
and25(
|
|
3997
|
+
eq26(usersInIam.tenantId, resolvedTenantId),
|
|
3998
|
+
eq26(usersInIam.phone, phone)
|
|
3501
3999
|
)
|
|
3502
4000
|
).limit(1);
|
|
3503
4001
|
if (!result) {
|
|
@@ -3523,10 +4021,10 @@ var phoneVerificationRequestHandler = async (c) => {
|
|
|
3523
4021
|
);
|
|
3524
4022
|
}
|
|
3525
4023
|
await database.delete(verificationsInIam).where(
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
4024
|
+
and25(
|
|
4025
|
+
eq26(verificationsInIam.tenantId, resolvedTenantId),
|
|
4026
|
+
eq26(verificationsInIam.userId, userId),
|
|
4027
|
+
eq26(verificationsInIam.type, verificationType)
|
|
3530
4028
|
)
|
|
3531
4029
|
);
|
|
3532
4030
|
const code = generateOtpCode(config.phone.otpLength);
|
|
@@ -3558,11 +4056,11 @@ var phoneVerificationRequestHandler = async (c) => {
|
|
|
3558
4056
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3559
4057
|
reason: "replaced"
|
|
3560
4058
|
}).where(
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
4059
|
+
and25(
|
|
4060
|
+
eq26(accountChangesInIam.tenantId, resolvedTenantId),
|
|
4061
|
+
eq26(accountChangesInIam.userId, user.id),
|
|
4062
|
+
eq26(accountChangesInIam.changeType, "phone"),
|
|
4063
|
+
eq26(accountChangesInIam.status, "pending")
|
|
3566
4064
|
)
|
|
3567
4065
|
);
|
|
3568
4066
|
await database.insert(accountChangesInIam).values({
|
|
@@ -3678,7 +4176,7 @@ import { createRoute as createRoute7, OpenAPIHono as OpenAPIHono7 } from "@hono/
|
|
|
3678
4176
|
import { z as z4 } from "zod";
|
|
3679
4177
|
|
|
3680
4178
|
// src/routes/profile/handler/account-change-pending.ts
|
|
3681
|
-
import { and as
|
|
4179
|
+
import { and as and26, eq as eq27, gt as gt7, sql as sql13 } from "drizzle-orm";
|
|
3682
4180
|
var accountChangePendingHandler = async (c) => {
|
|
3683
4181
|
const config = c.get("config");
|
|
3684
4182
|
const database = c.get("database");
|
|
@@ -3689,17 +4187,17 @@ var accountChangePendingHandler = async (c) => {
|
|
|
3689
4187
|
}
|
|
3690
4188
|
const resolvedTenantId = ensureTenantId(config, tenantId);
|
|
3691
4189
|
await database.update(accountChangesInIam).set({ status: "expired" }).where(
|
|
3692
|
-
|
|
3693
|
-
|
|
3694
|
-
|
|
4190
|
+
and26(
|
|
4191
|
+
eq27(accountChangesInIam.tenantId, resolvedTenantId),
|
|
4192
|
+
eq27(accountChangesInIam.userId, userId),
|
|
3695
4193
|
sql13`${accountChangesInIam.expiresAt} < CURRENT_TIMESTAMP`
|
|
3696
4194
|
)
|
|
3697
4195
|
);
|
|
3698
4196
|
const [accountChange] = await database.select().from(accountChangesInIam).where(
|
|
3699
|
-
|
|
3700
|
-
|
|
3701
|
-
|
|
3702
|
-
|
|
4197
|
+
and26(
|
|
4198
|
+
eq27(accountChangesInIam.tenantId, resolvedTenantId),
|
|
4199
|
+
eq27(accountChangesInIam.userId, userId),
|
|
4200
|
+
eq27(accountChangesInIam.status, "pending")
|
|
3703
4201
|
)
|
|
3704
4202
|
).limit(1);
|
|
3705
4203
|
if (!accountChange) {
|
|
@@ -3711,11 +4209,11 @@ var accountChangePendingHandler = async (c) => {
|
|
|
3711
4209
|
id: verificationsInIam.id,
|
|
3712
4210
|
expiresAt: verificationsInIam.expiresAt
|
|
3713
4211
|
}).from(verificationsInIam).where(
|
|
3714
|
-
|
|
3715
|
-
|
|
3716
|
-
|
|
3717
|
-
|
|
3718
|
-
|
|
4212
|
+
and26(
|
|
4213
|
+
eq27(verificationsInIam.tenantId, resolvedTenantId),
|
|
4214
|
+
eq27(verificationsInIam.userId, userId),
|
|
4215
|
+
eq27(verificationsInIam.type, "email-verification"),
|
|
4216
|
+
eq27(verificationsInIam.to, accountChange.newEmail),
|
|
3719
4217
|
gt7(verificationsInIam.expiresAt, sql13`CURRENT_TIMESTAMP`)
|
|
3720
4218
|
)
|
|
3721
4219
|
).limit(1);
|
|
@@ -3726,11 +4224,11 @@ var accountChangePendingHandler = async (c) => {
|
|
|
3726
4224
|
id: verificationsInIam.id,
|
|
3727
4225
|
expiresAt: verificationsInIam.expiresAt
|
|
3728
4226
|
}).from(verificationsInIam).where(
|
|
3729
|
-
|
|
3730
|
-
|
|
3731
|
-
|
|
3732
|
-
|
|
3733
|
-
|
|
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),
|
|
3734
4232
|
gt7(verificationsInIam.expiresAt, sql13`CURRENT_TIMESTAMP`)
|
|
3735
4233
|
)
|
|
3736
4234
|
).limit(1);
|
|
@@ -3753,6 +4251,13 @@ var accountChangePendingHandler = async (c) => {
|
|
|
3753
4251
|
);
|
|
3754
4252
|
};
|
|
3755
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
|
+
|
|
3756
4261
|
// src/routes/profile/handler/me.ts
|
|
3757
4262
|
var meHandler = (c) => {
|
|
3758
4263
|
const user = c.get("user");
|
|
@@ -3799,7 +4304,7 @@ var sessionHandler = (c) => {
|
|
|
3799
4304
|
};
|
|
3800
4305
|
|
|
3801
4306
|
// src/routes/profile/handler/update.ts
|
|
3802
|
-
import { and as
|
|
4307
|
+
import { and as and27, eq as eq28, sql as sql14 } from "drizzle-orm";
|
|
3803
4308
|
var updateProfileHandler = async (c) => {
|
|
3804
4309
|
const body = c.req.valid("json");
|
|
3805
4310
|
const config = c.get("config");
|
|
@@ -3822,7 +4327,7 @@ var updateProfileHandler = async (c) => {
|
|
|
3822
4327
|
...updateData,
|
|
3823
4328
|
updatedAt: sql14`CURRENT_TIMESTAMP`
|
|
3824
4329
|
}).where(
|
|
3825
|
-
|
|
4330
|
+
and27(eq28(usersInIam.id, userId), eq28(usersInIam.tenantId, resolvedTenantId))
|
|
3826
4331
|
).returning({
|
|
3827
4332
|
id: usersInIam.id,
|
|
3828
4333
|
tenantId: usersInIam.tenantId,
|
|
@@ -3842,7 +4347,7 @@ var updateProfileHandler = async (c) => {
|
|
|
3842
4347
|
};
|
|
3843
4348
|
|
|
3844
4349
|
// src/routes/profile/handler/update-email.ts
|
|
3845
|
-
import { and as
|
|
4350
|
+
import { and as and28, eq as eq29, ne as ne2, sql as sql15 } from "drizzle-orm";
|
|
3846
4351
|
var updateEmailHandler = async (c) => {
|
|
3847
4352
|
const body = c.req.valid("json");
|
|
3848
4353
|
const config = c.get("config");
|
|
@@ -3860,9 +4365,9 @@ var updateEmailHandler = async (c) => {
|
|
|
3860
4365
|
const resolvedTenantId = ensureTenantId(config, tenantId);
|
|
3861
4366
|
if (user.email && session?.id) {
|
|
3862
4367
|
await database.delete(sessionsInIam).where(
|
|
3863
|
-
|
|
3864
|
-
|
|
3865
|
-
|
|
4368
|
+
and28(
|
|
4369
|
+
eq29(sessionsInIam.tenantId, resolvedTenantId),
|
|
4370
|
+
eq29(sessionsInIam.userId, userId),
|
|
3866
4371
|
ne2(sessionsInIam.id, session.id)
|
|
3867
4372
|
)
|
|
3868
4373
|
);
|
|
@@ -3872,7 +4377,7 @@ var updateEmailHandler = async (c) => {
|
|
|
3872
4377
|
emailVerified: true,
|
|
3873
4378
|
updatedAt: sql15`CURRENT_TIMESTAMP`
|
|
3874
4379
|
}).where(
|
|
3875
|
-
|
|
4380
|
+
and28(eq29(usersInIam.id, userId), eq29(usersInIam.tenantId, resolvedTenantId))
|
|
3876
4381
|
).returning({
|
|
3877
4382
|
id: usersInIam.id,
|
|
3878
4383
|
tenantId: usersInIam.tenantId,
|
|
@@ -3889,25 +4394,25 @@ var updateEmailHandler = async (c) => {
|
|
|
3889
4394
|
return c.json({ error: "User not found" }, 404);
|
|
3890
4395
|
}
|
|
3891
4396
|
await database.update(accountChangesInIam).set({ status: "applied" }).where(
|
|
3892
|
-
|
|
3893
|
-
|
|
3894
|
-
|
|
3895
|
-
|
|
3896
|
-
|
|
4397
|
+
and28(
|
|
4398
|
+
eq29(accountChangesInIam.tenantId, resolvedTenantId),
|
|
4399
|
+
eq29(accountChangesInIam.userId, userId),
|
|
4400
|
+
eq29(accountChangesInIam.changeType, "email"),
|
|
4401
|
+
eq29(accountChangesInIam.newEmail, body.email)
|
|
3897
4402
|
)
|
|
3898
4403
|
);
|
|
3899
4404
|
await database.update(accountsInIam).set({ providerAccountId: body.email }).where(
|
|
3900
|
-
|
|
3901
|
-
|
|
3902
|
-
|
|
3903
|
-
|
|
4405
|
+
and28(
|
|
4406
|
+
eq29(accountsInIam.tenantId, resolvedTenantId),
|
|
4407
|
+
eq29(accountsInIam.userId, userId),
|
|
4408
|
+
eq29(accountsInIam.provider, "credentials")
|
|
3904
4409
|
)
|
|
3905
4410
|
);
|
|
3906
4411
|
return c.json({ user: normalizeUser(updatedUser) }, 200);
|
|
3907
4412
|
};
|
|
3908
4413
|
|
|
3909
4414
|
// src/routes/profile/handler/update-phone.ts
|
|
3910
|
-
import { and as
|
|
4415
|
+
import { and as and29, eq as eq30, ne as ne3, sql as sql16 } from "drizzle-orm";
|
|
3911
4416
|
var updatePhoneHandler = async (c) => {
|
|
3912
4417
|
const body = c.req.valid("json");
|
|
3913
4418
|
const config = c.get("config");
|
|
@@ -3929,9 +4434,9 @@ var updatePhoneHandler = async (c) => {
|
|
|
3929
4434
|
}
|
|
3930
4435
|
if (user.phone && session?.id) {
|
|
3931
4436
|
await database.delete(sessionsInIam).where(
|
|
3932
|
-
|
|
3933
|
-
|
|
3934
|
-
|
|
4437
|
+
and29(
|
|
4438
|
+
eq30(sessionsInIam.tenantId, resolvedTenantId),
|
|
4439
|
+
eq30(sessionsInIam.userId, userId),
|
|
3935
4440
|
ne3(sessionsInIam.id, session.id)
|
|
3936
4441
|
)
|
|
3937
4442
|
);
|
|
@@ -3941,7 +4446,7 @@ var updatePhoneHandler = async (c) => {
|
|
|
3941
4446
|
phoneVerified: true,
|
|
3942
4447
|
updatedAt: sql16`CURRENT_TIMESTAMP`
|
|
3943
4448
|
}).where(
|
|
3944
|
-
|
|
4449
|
+
and29(eq30(usersInIam.id, userId), eq30(usersInIam.tenantId, resolvedTenantId))
|
|
3945
4450
|
).returning({
|
|
3946
4451
|
id: usersInIam.id,
|
|
3947
4452
|
tenantId: usersInIam.tenantId,
|
|
@@ -3958,18 +4463,18 @@ var updatePhoneHandler = async (c) => {
|
|
|
3958
4463
|
return c.json({ error: "User not found" }, 404);
|
|
3959
4464
|
}
|
|
3960
4465
|
await database.update(accountChangesInIam).set({ status: "applied" }).where(
|
|
3961
|
-
|
|
3962
|
-
|
|
3963
|
-
|
|
3964
|
-
|
|
3965
|
-
|
|
4466
|
+
and29(
|
|
4467
|
+
eq30(accountChangesInIam.tenantId, resolvedTenantId),
|
|
4468
|
+
eq30(accountChangesInIam.userId, userId),
|
|
4469
|
+
eq30(accountChangesInIam.changeType, "phone"),
|
|
4470
|
+
eq30(accountChangesInIam.newPhone, body.phone)
|
|
3966
4471
|
)
|
|
3967
4472
|
);
|
|
3968
4473
|
await database.update(accountsInIam).set({ providerAccountId: body.phone }).where(
|
|
3969
|
-
|
|
3970
|
-
|
|
3971
|
-
|
|
3972
|
-
|
|
4474
|
+
and29(
|
|
4475
|
+
eq30(accountsInIam.tenantId, resolvedTenantId),
|
|
4476
|
+
eq30(accountsInIam.userId, userId),
|
|
4477
|
+
eq30(accountsInIam.provider, "credentials")
|
|
3973
4478
|
)
|
|
3974
4479
|
);
|
|
3975
4480
|
return c.json({ user: normalizeUser(updatedUser) }, 200);
|
|
@@ -4219,41 +4724,41 @@ var assignRolePermissionHandler = async (c) => {
|
|
|
4219
4724
|
};
|
|
4220
4725
|
|
|
4221
4726
|
// src/routes/role-permissions/handler/list-role-permissions.ts
|
|
4222
|
-
import { and as
|
|
4727
|
+
import { and as and30, eq as eq31 } from "drizzle-orm";
|
|
4223
4728
|
var listRolePermissionsHandler = async (c) => {
|
|
4224
4729
|
const query = c.req.valid("query");
|
|
4225
4730
|
const database = c.get("database");
|
|
4226
4731
|
const tenantId = c.get("tenantId");
|
|
4227
|
-
const conditions = [
|
|
4732
|
+
const conditions = [eq31(rolePermissionsInIam.tenantId, tenantId)];
|
|
4228
4733
|
if (query.roleId) {
|
|
4229
|
-
conditions.push(
|
|
4734
|
+
conditions.push(eq31(rolePermissionsInIam.roleId, query.roleId));
|
|
4230
4735
|
}
|
|
4231
4736
|
if (query.permissionId) {
|
|
4232
|
-
conditions.push(
|
|
4737
|
+
conditions.push(eq31(rolePermissionsInIam.permissionId, query.permissionId));
|
|
4233
4738
|
}
|
|
4234
|
-
const rolePermissions = await database.select().from(rolePermissionsInIam).where(
|
|
4739
|
+
const rolePermissions = await database.select().from(rolePermissionsInIam).where(and30(...conditions));
|
|
4235
4740
|
return c.json({ rolePermissions }, 200);
|
|
4236
4741
|
};
|
|
4237
4742
|
|
|
4238
4743
|
// src/routes/role-permissions/handler/revoke-role-permission.ts
|
|
4239
|
-
import { and as
|
|
4744
|
+
import { and as and31, eq as eq32 } from "drizzle-orm";
|
|
4240
4745
|
var revokeRolePermissionHandler = async (c) => {
|
|
4241
4746
|
const { id } = c.req.valid("param");
|
|
4242
4747
|
const database = c.get("database");
|
|
4243
4748
|
const tenantId = c.get("tenantId");
|
|
4244
4749
|
const [existing] = await database.select().from(rolePermissionsInIam).where(
|
|
4245
|
-
|
|
4246
|
-
|
|
4247
|
-
|
|
4750
|
+
and31(
|
|
4751
|
+
eq32(rolePermissionsInIam.id, id),
|
|
4752
|
+
eq32(rolePermissionsInIam.tenantId, tenantId)
|
|
4248
4753
|
)
|
|
4249
4754
|
).limit(1);
|
|
4250
4755
|
if (!existing) {
|
|
4251
4756
|
return c.json({ error: "Role permission not found" }, 404);
|
|
4252
4757
|
}
|
|
4253
4758
|
await database.delete(rolePermissionsInIam).where(
|
|
4254
|
-
|
|
4255
|
-
|
|
4256
|
-
|
|
4759
|
+
and31(
|
|
4760
|
+
eq32(rolePermissionsInIam.id, id),
|
|
4761
|
+
eq32(rolePermissionsInIam.tenantId, tenantId)
|
|
4257
4762
|
)
|
|
4258
4763
|
);
|
|
4259
4764
|
return c.json({ message: "Permission revoked from role" }, 200);
|
|
@@ -4377,53 +4882,390 @@ var role_permissions_route_default = rolePermissionRoutes;
|
|
|
4377
4882
|
// src/routes/roles/roles.route.ts
|
|
4378
4883
|
import { createRoute as createRoute9, OpenAPIHono as OpenAPIHono9 } from "@hono/zod-openapi";
|
|
4379
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
|
+
|
|
4380
5006
|
// src/routes/roles/handler/create-role.ts
|
|
4381
|
-
import { randomUUID } from "crypto";
|
|
5007
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
4382
5008
|
var createRoleHandler = async (c) => {
|
|
4383
5009
|
const body = c.req.valid("json");
|
|
4384
5010
|
const config = c.get("config");
|
|
4385
5011
|
const database = c.get("database");
|
|
4386
5012
|
const tenantId = c.get("tenantId");
|
|
4387
5013
|
const resolvedTenantId = ensureTenantId(config, tenantId);
|
|
4388
|
-
const
|
|
4389
|
-
|
|
4390
|
-
|
|
4391
|
-
|
|
4392
|
-
|
|
4393
|
-
|
|
4394
|
-
|
|
4395
|
-
|
|
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
|
+
);
|
|
4396
5050
|
};
|
|
4397
5051
|
|
|
4398
5052
|
// src/routes/roles/handler/delete-role.ts
|
|
4399
|
-
import { and as
|
|
5053
|
+
import { and as and34, eq as eq35 } from "drizzle-orm";
|
|
4400
5054
|
var deleteRoleHandler = async (c) => {
|
|
4401
5055
|
const { id } = c.req.valid("param");
|
|
4402
5056
|
const database = c.get("database");
|
|
4403
5057
|
const tenantId = c.get("tenantId");
|
|
4404
|
-
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);
|
|
4405
5059
|
if (!existing) {
|
|
4406
5060
|
return c.json({ error: "Role not found" }, 404);
|
|
4407
5061
|
}
|
|
4408
|
-
|
|
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)));
|
|
4409
5066
|
return c.json({ message: "Role deleted" }, 200);
|
|
4410
5067
|
};
|
|
4411
5068
|
|
|
4412
5069
|
// src/routes/roles/handler/get-role.ts
|
|
4413
|
-
import { and as
|
|
5070
|
+
import { and as and35, eq as eq36, sql as sql17 } from "drizzle-orm";
|
|
4414
5071
|
var getRoleHandler = async (c) => {
|
|
4415
5072
|
const { id } = c.req.valid("param");
|
|
4416
5073
|
const database = c.get("database");
|
|
4417
5074
|
const tenantId = c.get("tenantId");
|
|
4418
|
-
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);
|
|
5114
|
+
if (!role) {
|
|
5115
|
+
return c.json({ error: "Role not found" }, 404);
|
|
5116
|
+
}
|
|
5117
|
+
return c.json(
|
|
5118
|
+
{
|
|
5119
|
+
role: {
|
|
5120
|
+
...role,
|
|
5121
|
+
permissionIds: role.permissionIds ?? void 0
|
|
5122
|
+
}
|
|
5123
|
+
},
|
|
5124
|
+
200
|
|
5125
|
+
);
|
|
5126
|
+
};
|
|
5127
|
+
|
|
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");
|
|
5132
|
+
const query = c.req.valid("query");
|
|
5133
|
+
const database = c.get("database");
|
|
5134
|
+
const config = c.get("config");
|
|
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
|
+
}
|
|
5140
|
+
const page = query.page || 1;
|
|
5141
|
+
const limit = query.limit || 20;
|
|
5142
|
+
const offset = (page - 1) * limit;
|
|
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
|
+
})
|
|
5154
|
+
]);
|
|
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);
|
|
5164
|
+
};
|
|
5165
|
+
|
|
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 });
|
|
4419
5210
|
if (!role) {
|
|
4420
5211
|
return c.json({ error: "Role not found" }, 404);
|
|
4421
5212
|
}
|
|
4422
|
-
|
|
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
|
+
);
|
|
4423
5260
|
};
|
|
4424
5261
|
|
|
4425
5262
|
// src/routes/roles/handler/list-roles.ts
|
|
4426
|
-
import { and as
|
|
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
|
+
};
|
|
4427
5269
|
var listRolesHandler = async (c) => {
|
|
4428
5270
|
const query = c.req.valid("query");
|
|
4429
5271
|
const database = c.get("database");
|
|
@@ -4431,64 +5273,281 @@ var listRolesHandler = async (c) => {
|
|
|
4431
5273
|
const page = query.page || 1;
|
|
4432
5274
|
const limit = query.limit || 20;
|
|
4433
5275
|
const offset = (page - 1) * limit;
|
|
4434
|
-
const conditions = [
|
|
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;
|
|
4435
5290
|
const [roles, totalResult] = await Promise.all([
|
|
4436
|
-
database.select(
|
|
4437
|
-
|
|
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))
|
|
4438
5320
|
]);
|
|
4439
5321
|
const total = Number(totalResult[0]?.count || 0);
|
|
4440
5322
|
return c.json({ roles, total, page, limit });
|
|
4441
5323
|
};
|
|
4442
5324
|
|
|
4443
|
-
// src/routes/roles/handler/
|
|
4444
|
-
import { and as
|
|
4445
|
-
var
|
|
4446
|
-
const { id } = c.req.valid("param");
|
|
4447
|
-
const body = c.req.valid("json");
|
|
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");
|
|
4448
5329
|
const database = c.get("database");
|
|
4449
5330
|
const tenantId = c.get("tenantId");
|
|
4450
|
-
const [
|
|
4451
|
-
|
|
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) {
|
|
4452
5336
|
return c.json({ error: "Role not found" }, 404);
|
|
4453
5337
|
}
|
|
4454
|
-
|
|
4455
|
-
|
|
4456
|
-
updateData.name = body.name;
|
|
4457
|
-
}
|
|
4458
|
-
if (body.description !== void 0) {
|
|
4459
|
-
updateData.description = body.description;
|
|
5338
|
+
if (!role.isEditable) {
|
|
5339
|
+
return c.json({ error: "Role is not editable" }, 403);
|
|
4460
5340
|
}
|
|
4461
|
-
|
|
4462
|
-
|
|
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);
|
|
4463
5350
|
}
|
|
4464
|
-
|
|
4465
|
-
|
|
4466
|
-
|
|
4467
|
-
|
|
4468
|
-
|
|
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) {
|
|
4469
5365
|
return c.json({ error: "Role not found" }, 404);
|
|
4470
5366
|
}
|
|
4471
|
-
|
|
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);
|
|
4472
5381
|
};
|
|
4473
5382
|
|
|
4474
|
-
// src/routes/roles/roles.
|
|
4475
|
-
import {
|
|
4476
|
-
var
|
|
4477
|
-
|
|
4478
|
-
|
|
4479
|
-
|
|
4480
|
-
|
|
4481
|
-
|
|
4482
|
-
});
|
|
4483
|
-
|
|
4484
|
-
|
|
4485
|
-
|
|
4486
|
-
|
|
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 = {};
|
|
5450
|
+
if (body.name !== void 0) {
|
|
5451
|
+
updateData.name = body.name;
|
|
5452
|
+
}
|
|
5453
|
+
if (body.description !== void 0) {
|
|
5454
|
+
updateData.description = body.description;
|
|
5455
|
+
}
|
|
5456
|
+
if (body.code !== void 0) {
|
|
5457
|
+
updateData.code = body.code;
|
|
5458
|
+
}
|
|
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
|
+
});
|
|
5490
|
+
if (!updated) {
|
|
5491
|
+
return c.json({ error: "Role not found" }, 404);
|
|
5492
|
+
}
|
|
5493
|
+
return c.json(
|
|
5494
|
+
{
|
|
5495
|
+
role: {
|
|
5496
|
+
...updated,
|
|
5497
|
+
permissionIds,
|
|
5498
|
+
permissionCount: permissionIds?.length
|
|
5499
|
+
}
|
|
5500
|
+
},
|
|
5501
|
+
200
|
|
5502
|
+
);
|
|
5503
|
+
};
|
|
5504
|
+
|
|
5505
|
+
// src/routes/roles/roles.schema.ts
|
|
5506
|
+
import { z as z6 } from "zod";
|
|
5507
|
+
var roleFilterValues = ["", "code"];
|
|
5508
|
+
var roleSortValues = ["createdAt", "updatedAt", "code"];
|
|
5509
|
+
var listRolesQuerySchema = z6.object({
|
|
5510
|
+
page: z6.coerce.number().min(1).default(1).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()
|
|
5516
|
+
});
|
|
5517
|
+
var roleIdParamSchema = z6.object({
|
|
5518
|
+
id: z6.uuid()
|
|
5519
|
+
});
|
|
5520
|
+
var createRoleSchema = z6.object({
|
|
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()
|
|
4487
5528
|
});
|
|
4488
5529
|
var updateRoleSchema = z6.object({
|
|
4489
5530
|
name: z6.unknown().optional(),
|
|
4490
5531
|
description: z6.unknown().optional(),
|
|
4491
|
-
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()
|
|
4492
5551
|
});
|
|
4493
5552
|
var roleSchema = z6.object({
|
|
4494
5553
|
id: z6.uuid(),
|
|
@@ -4497,7 +5556,13 @@ var roleSchema = z6.object({
|
|
|
4497
5556
|
updatedAt: z6.string(),
|
|
4498
5557
|
name: z6.unknown(),
|
|
4499
5558
|
description: z6.unknown(),
|
|
4500
|
-
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()
|
|
4501
5566
|
});
|
|
4502
5567
|
var listRolesResponseSchema = z6.object({
|
|
4503
5568
|
roles: z6.array(roleSchema),
|
|
@@ -4508,12 +5573,45 @@ var listRolesResponseSchema = z6.object({
|
|
|
4508
5573
|
var roleResponseSchema = z6.object({
|
|
4509
5574
|
role: roleSchema
|
|
4510
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
|
+
});
|
|
4511
5604
|
var deleteRoleResponseSchema = z6.object({
|
|
4512
5605
|
message: z6.string()
|
|
4513
5606
|
});
|
|
5607
|
+
var seedRolesResponseSchema = z6.object({
|
|
5608
|
+
roles: z6.array(roleSchema),
|
|
5609
|
+
total: z6.number()
|
|
5610
|
+
});
|
|
4514
5611
|
var errorResponseSchema5 = z6.object({
|
|
4515
5612
|
error: z6.string()
|
|
4516
5613
|
});
|
|
5614
|
+
var listRolePermissionsQuerySchema2 = listPermissionsQuerySchema;
|
|
4517
5615
|
|
|
4518
5616
|
// src/routes/roles/roles.route.ts
|
|
4519
5617
|
var listRolesRoute = createRoute9({
|
|
@@ -4531,26 +5629,258 @@ var listRolesRoute = createRoute9({
|
|
|
4531
5629
|
schema: listRolesResponseSchema
|
|
4532
5630
|
}
|
|
4533
5631
|
},
|
|
4534
|
-
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"
|
|
4535
5864
|
}
|
|
4536
5865
|
}
|
|
4537
5866
|
});
|
|
4538
|
-
var
|
|
5867
|
+
var listRoleUsersRoute = createRoute9({
|
|
4539
5868
|
method: "get",
|
|
4540
|
-
path: "/{id}",
|
|
5869
|
+
path: "/{id}/users",
|
|
4541
5870
|
tags: ["Roles"],
|
|
4542
|
-
summary: "
|
|
5871
|
+
summary: "List users assigned to a role",
|
|
4543
5872
|
request: {
|
|
4544
|
-
params: roleIdParamSchema
|
|
5873
|
+
params: roleIdParamSchema,
|
|
5874
|
+
query: listRoleUsersQuerySchema
|
|
4545
5875
|
},
|
|
4546
5876
|
responses: {
|
|
4547
5877
|
200: {
|
|
4548
5878
|
content: {
|
|
4549
5879
|
"application/json": {
|
|
4550
|
-
schema:
|
|
5880
|
+
schema: listRoleUsersResponseSchema
|
|
4551
5881
|
}
|
|
4552
5882
|
},
|
|
4553
|
-
description: "Role
|
|
5883
|
+
description: "Role users"
|
|
4554
5884
|
},
|
|
4555
5885
|
404: {
|
|
4556
5886
|
content: {
|
|
@@ -4562,62 +5892,80 @@ var getRoleRoute = createRoute9({
|
|
|
4562
5892
|
}
|
|
4563
5893
|
}
|
|
4564
5894
|
});
|
|
4565
|
-
var
|
|
5895
|
+
var assignRoleUsersRoute = createRoute9({
|
|
4566
5896
|
method: "post",
|
|
4567
|
-
path: "/",
|
|
5897
|
+
path: "/{id}/users",
|
|
4568
5898
|
tags: ["Roles"],
|
|
4569
|
-
summary: "
|
|
5899
|
+
summary: "Assign users to a role",
|
|
4570
5900
|
request: {
|
|
5901
|
+
params: roleIdParamSchema,
|
|
4571
5902
|
body: {
|
|
4572
5903
|
content: {
|
|
4573
5904
|
"application/json": {
|
|
4574
|
-
schema:
|
|
5905
|
+
schema: assignRoleUsersSchema
|
|
4575
5906
|
}
|
|
4576
5907
|
}
|
|
4577
5908
|
}
|
|
4578
5909
|
},
|
|
4579
5910
|
responses: {
|
|
4580
|
-
|
|
5911
|
+
200: {
|
|
4581
5912
|
content: {
|
|
4582
5913
|
"application/json": {
|
|
4583
|
-
schema:
|
|
5914
|
+
schema: assignRoleUsersResponseSchema
|
|
4584
5915
|
}
|
|
4585
5916
|
},
|
|
4586
|
-
description: "
|
|
5917
|
+
description: "Users assigned"
|
|
4587
5918
|
},
|
|
4588
|
-
|
|
5919
|
+
400: {
|
|
4589
5920
|
content: {
|
|
4590
5921
|
"application/json": {
|
|
4591
5922
|
schema: errorResponseSchema5
|
|
4592
5923
|
}
|
|
4593
5924
|
},
|
|
4594
|
-
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"
|
|
4595
5942
|
}
|
|
4596
5943
|
}
|
|
4597
5944
|
});
|
|
4598
|
-
var
|
|
4599
|
-
method: "
|
|
4600
|
-
path: "/{id}",
|
|
5945
|
+
var revokeRolePermissionRoute2 = createRoute9({
|
|
5946
|
+
method: "delete",
|
|
5947
|
+
path: "/{id}/permissions/{permissionId}",
|
|
4601
5948
|
tags: ["Roles"],
|
|
4602
|
-
summary: "
|
|
5949
|
+
summary: "Revoke a permission from a role",
|
|
4603
5950
|
request: {
|
|
4604
|
-
params:
|
|
4605
|
-
body: {
|
|
4606
|
-
content: {
|
|
4607
|
-
"application/json": {
|
|
4608
|
-
schema: updateRoleSchema
|
|
4609
|
-
}
|
|
4610
|
-
}
|
|
4611
|
-
}
|
|
5951
|
+
params: rolePermissionParamSchema
|
|
4612
5952
|
},
|
|
4613
5953
|
responses: {
|
|
4614
5954
|
200: {
|
|
4615
5955
|
content: {
|
|
4616
5956
|
"application/json": {
|
|
4617
|
-
schema:
|
|
5957
|
+
schema: deleteRoleResponseSchema
|
|
4618
5958
|
}
|
|
4619
5959
|
},
|
|
4620
|
-
description: "
|
|
5960
|
+
description: "Permission revoked"
|
|
5961
|
+
},
|
|
5962
|
+
403: {
|
|
5963
|
+
content: {
|
|
5964
|
+
"application/json": {
|
|
5965
|
+
schema: errorResponseSchema5
|
|
5966
|
+
}
|
|
5967
|
+
},
|
|
5968
|
+
description: "Role is not editable"
|
|
4621
5969
|
},
|
|
4622
5970
|
404: {
|
|
4623
5971
|
content: {
|
|
@@ -4625,17 +5973,17 @@ var updateRoleRoute = createRoute9({
|
|
|
4625
5973
|
schema: errorResponseSchema5
|
|
4626
5974
|
}
|
|
4627
5975
|
},
|
|
4628
|
-
description: "Role not found"
|
|
5976
|
+
description: "Role or permission not found"
|
|
4629
5977
|
}
|
|
4630
5978
|
}
|
|
4631
5979
|
});
|
|
4632
|
-
var
|
|
5980
|
+
var revokeRoleUserRoute = createRoute9({
|
|
4633
5981
|
method: "delete",
|
|
4634
|
-
path: "/{id}",
|
|
5982
|
+
path: "/{id}/users/{userId}",
|
|
4635
5983
|
tags: ["Roles"],
|
|
4636
|
-
summary: "
|
|
5984
|
+
summary: "Remove a user from a role",
|
|
4637
5985
|
request: {
|
|
4638
|
-
params:
|
|
5986
|
+
params: roleUserParamSchema
|
|
4639
5987
|
},
|
|
4640
5988
|
responses: {
|
|
4641
5989
|
200: {
|
|
@@ -4644,7 +5992,15 @@ var deleteRoleRoute = createRoute9({
|
|
|
4644
5992
|
schema: deleteRoleResponseSchema
|
|
4645
5993
|
}
|
|
4646
5994
|
},
|
|
4647
|
-
description: "
|
|
5995
|
+
description: "User removed"
|
|
5996
|
+
},
|
|
5997
|
+
403: {
|
|
5998
|
+
content: {
|
|
5999
|
+
"application/json": {
|
|
6000
|
+
schema: errorResponseSchema5
|
|
6001
|
+
}
|
|
6002
|
+
},
|
|
6003
|
+
description: "Role is not editable"
|
|
4648
6004
|
},
|
|
4649
6005
|
404: {
|
|
4650
6006
|
content: {
|
|
@@ -4652,23 +6008,47 @@ var deleteRoleRoute = createRoute9({
|
|
|
4652
6008
|
schema: errorResponseSchema5
|
|
4653
6009
|
}
|
|
4654
6010
|
},
|
|
4655
|
-
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"
|
|
4656
6036
|
}
|
|
4657
6037
|
}
|
|
4658
6038
|
});
|
|
4659
|
-
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);
|
|
4660
6040
|
var roles_route_default = roleRoutes;
|
|
4661
6041
|
|
|
4662
6042
|
// src/routes/sessions/sessions.route.ts
|
|
4663
6043
|
import { createRoute as createRoute10, OpenAPIHono as OpenAPIHono10 } from "@hono/zod-openapi";
|
|
4664
6044
|
|
|
4665
6045
|
// src/routes/sessions/handler/get-session.ts
|
|
4666
|
-
import { and as
|
|
6046
|
+
import { and as and43, eq as eq44 } from "drizzle-orm";
|
|
4667
6047
|
var getSessionHandler = async (c) => {
|
|
4668
6048
|
const { id } = c.req.valid("param");
|
|
4669
6049
|
const database = c.get("database");
|
|
4670
6050
|
const tenantId = c.get("tenantId");
|
|
4671
|
-
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);
|
|
4672
6052
|
if (!session) {
|
|
4673
6053
|
return c.json({ error: "Session not found" }, 404);
|
|
4674
6054
|
}
|
|
@@ -4676,7 +6056,7 @@ var getSessionHandler = async (c) => {
|
|
|
4676
6056
|
};
|
|
4677
6057
|
|
|
4678
6058
|
// src/routes/sessions/handler/list-sessions.ts
|
|
4679
|
-
import { and as
|
|
6059
|
+
import { and as and44, eq as eq45, sql as sql22 } from "drizzle-orm";
|
|
4680
6060
|
var listSessionsHandler = async (c) => {
|
|
4681
6061
|
const query = c.req.valid("query");
|
|
4682
6062
|
const database = c.get("database");
|
|
@@ -4684,48 +6064,48 @@ var listSessionsHandler = async (c) => {
|
|
|
4684
6064
|
const page = query.page || 1;
|
|
4685
6065
|
const limit = query.limit || 20;
|
|
4686
6066
|
const offset = (page - 1) * limit;
|
|
4687
|
-
const conditions = [
|
|
6067
|
+
const conditions = [eq45(sessionsInIam.tenantId, tenantId)];
|
|
4688
6068
|
if (query.userId) {
|
|
4689
|
-
conditions.push(
|
|
6069
|
+
conditions.push(eq45(sessionsInIam.userId, query.userId));
|
|
4690
6070
|
}
|
|
4691
6071
|
const [sessions, totalResult] = await Promise.all([
|
|
4692
|
-
database.select().from(sessionsInIam).where(
|
|
4693
|
-
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))
|
|
4694
6074
|
]);
|
|
4695
6075
|
const total = Number(totalResult[0]?.count || 0);
|
|
4696
6076
|
return c.json({ sessions, total, page, limit }, 200);
|
|
4697
6077
|
};
|
|
4698
6078
|
|
|
4699
6079
|
// src/routes/sessions/handler/revoke-all-sessions.ts
|
|
4700
|
-
import { and as
|
|
6080
|
+
import { and as and45, eq as eq46 } from "drizzle-orm";
|
|
4701
6081
|
var revokeAllSessionsHandler = async (c) => {
|
|
4702
6082
|
const { userId } = c.req.valid("param");
|
|
4703
6083
|
const database = c.get("database");
|
|
4704
6084
|
const tenantId = c.get("tenantId");
|
|
4705
|
-
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);
|
|
4706
6086
|
if (!user) {
|
|
4707
6087
|
return c.json({ error: "User not found" }, 404);
|
|
4708
6088
|
}
|
|
4709
6089
|
await database.delete(sessionsInIam).where(
|
|
4710
|
-
|
|
4711
|
-
|
|
4712
|
-
|
|
6090
|
+
and45(
|
|
6091
|
+
eq46(sessionsInIam.tenantId, tenantId),
|
|
6092
|
+
eq46(sessionsInIam.userId, userId)
|
|
4713
6093
|
)
|
|
4714
6094
|
);
|
|
4715
6095
|
return c.json({ message: "All user sessions revoked" }, 200);
|
|
4716
6096
|
};
|
|
4717
6097
|
|
|
4718
6098
|
// src/routes/sessions/handler/revoke-session.ts
|
|
4719
|
-
import { and as
|
|
6099
|
+
import { and as and46, eq as eq47 } from "drizzle-orm";
|
|
4720
6100
|
var revokeSessionHandler = async (c) => {
|
|
4721
6101
|
const { id } = c.req.valid("param");
|
|
4722
6102
|
const database = c.get("database");
|
|
4723
6103
|
const tenantId = c.get("tenantId");
|
|
4724
|
-
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);
|
|
4725
6105
|
if (!existing) {
|
|
4726
6106
|
return c.json({ error: "Session not found" }, 404);
|
|
4727
6107
|
}
|
|
4728
|
-
await database.delete(sessionsInIam).where(
|
|
6108
|
+
await database.delete(sessionsInIam).where(and46(eq47(sessionsInIam.id, id), eq47(sessionsInIam.tenantId, tenantId)));
|
|
4729
6109
|
return c.json({ message: "Session revoked" }, 200);
|
|
4730
6110
|
};
|
|
4731
6111
|
|
|
@@ -4927,10 +6307,11 @@ var system_route_default = tenantRoutes;
|
|
|
4927
6307
|
import { createRoute as createRoute12, OpenAPIHono as OpenAPIHono12 } from "@hono/zod-openapi";
|
|
4928
6308
|
|
|
4929
6309
|
// src/routes/tenants/handler/create-tenant.ts
|
|
4930
|
-
import { eq as
|
|
6310
|
+
import { eq as eq48 } from "drizzle-orm";
|
|
4931
6311
|
|
|
4932
6312
|
// src/lib/has-role-permission.ts
|
|
4933
|
-
import {
|
|
6313
|
+
import { grant } from "@mesob/common";
|
|
6314
|
+
import { HTTPException as HTTPException4 } from "hono/http-exception";
|
|
4934
6315
|
var toArray = (v) => {
|
|
4935
6316
|
return Array.isArray(v) ? v : [v];
|
|
4936
6317
|
};
|
|
@@ -4945,21 +6326,18 @@ var hasRole = (c, role) => {
|
|
|
4945
6326
|
};
|
|
4946
6327
|
var hasRoleThrow = (c, role) => {
|
|
4947
6328
|
if (!hasRole(c, role)) {
|
|
4948
|
-
throw new
|
|
6329
|
+
throw new HTTPException4(401, { message: "Unauthorized" });
|
|
4949
6330
|
}
|
|
4950
6331
|
};
|
|
4951
6332
|
var hasPermission = (c, permission) => {
|
|
4952
6333
|
const user = c.get("user");
|
|
4953
6334
|
const perms = user?.permissions;
|
|
4954
|
-
if (!perms?.length) {
|
|
4955
|
-
return false;
|
|
4956
|
-
}
|
|
4957
6335
|
const check2 = toArray(permission);
|
|
4958
|
-
return check2
|
|
6336
|
+
return grant(check2, perms);
|
|
4959
6337
|
};
|
|
4960
6338
|
var hasPermissionThrow = (c, permission) => {
|
|
4961
6339
|
if (!hasPermission(c, permission)) {
|
|
4962
|
-
throw new
|
|
6340
|
+
throw new HTTPException4(401, { message: "Unauthorized" });
|
|
4963
6341
|
}
|
|
4964
6342
|
};
|
|
4965
6343
|
|
|
@@ -4968,7 +6346,7 @@ var createTenantHandler = async (c) => {
|
|
|
4968
6346
|
hasRoleThrow(c, ["owner", "tenant-admin"]);
|
|
4969
6347
|
const body = c.req.valid("json");
|
|
4970
6348
|
const database = c.get("database");
|
|
4971
|
-
const [existing] = await database.select().from(tenantsInIam).where(
|
|
6349
|
+
const [existing] = await database.select().from(tenantsInIam).where(eq48(tenantsInIam.id, body.id)).limit(1);
|
|
4972
6350
|
if (existing) {
|
|
4973
6351
|
return c.json({ error: "Tenant already exists" }, 409);
|
|
4974
6352
|
}
|
|
@@ -4991,26 +6369,26 @@ var createTenantHandler = async (c) => {
|
|
|
4991
6369
|
};
|
|
4992
6370
|
|
|
4993
6371
|
// src/routes/tenants/handler/delete-tenant.ts
|
|
4994
|
-
import { eq as
|
|
6372
|
+
import { eq as eq49 } from "drizzle-orm";
|
|
4995
6373
|
var deleteTenantHandler = async (c) => {
|
|
4996
6374
|
hasRoleThrow(c, ["owner", "tenant-admin"]);
|
|
4997
6375
|
const { id } = c.req.valid("param");
|
|
4998
6376
|
const database = c.get("database");
|
|
4999
|
-
const [existing] = await database.select().from(tenantsInIam).where(
|
|
6377
|
+
const [existing] = await database.select().from(tenantsInIam).where(eq49(tenantsInIam.id, id)).limit(1);
|
|
5000
6378
|
if (!existing) {
|
|
5001
6379
|
return c.json({ error: "Tenant not found" }, 404);
|
|
5002
6380
|
}
|
|
5003
|
-
await database.delete(tenantsInIam).where(
|
|
6381
|
+
await database.delete(tenantsInIam).where(eq49(tenantsInIam.id, id));
|
|
5004
6382
|
return c.json({ message: "Tenant deleted" }, 200);
|
|
5005
6383
|
};
|
|
5006
6384
|
|
|
5007
6385
|
// src/routes/tenants/handler/get-tenant.ts
|
|
5008
|
-
import { eq as
|
|
6386
|
+
import { eq as eq50 } from "drizzle-orm";
|
|
5009
6387
|
var getTenantHandler = async (c) => {
|
|
5010
6388
|
hasRoleThrow(c, ["owner", "tenant-admin"]);
|
|
5011
6389
|
const { id } = c.req.valid("param");
|
|
5012
6390
|
const database = c.get("database");
|
|
5013
|
-
const [tenant] = await database.select().from(tenantsInIam).where(
|
|
6391
|
+
const [tenant] = await database.select().from(tenantsInIam).where(eq50(tenantsInIam.id, id)).limit(1);
|
|
5014
6392
|
if (!tenant) {
|
|
5015
6393
|
return c.json({ error: "Tenant not found" }, 404);
|
|
5016
6394
|
}
|
|
@@ -5018,11 +6396,11 @@ var getTenantHandler = async (c) => {
|
|
|
5018
6396
|
};
|
|
5019
6397
|
|
|
5020
6398
|
// src/routes/tenants/handler/list-tenants.ts
|
|
5021
|
-
import { and as
|
|
5022
|
-
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 = {
|
|
5023
6401
|
createdAt: tenantsInIam.createdAt,
|
|
5024
6402
|
updatedAt: tenantsInIam.updatedAt,
|
|
5025
|
-
name:
|
|
6403
|
+
name: sql23`${tenantsInIam.name}::text`
|
|
5026
6404
|
};
|
|
5027
6405
|
var listTenantsHandler = async (c) => {
|
|
5028
6406
|
hasRoleThrow(c, ["owner", "tenant-admin"]);
|
|
@@ -5033,42 +6411,42 @@ var listTenantsHandler = async (c) => {
|
|
|
5033
6411
|
const offset = (page - 1) * limit;
|
|
5034
6412
|
const conditions = [];
|
|
5035
6413
|
if (query.isActive !== void 0) {
|
|
5036
|
-
conditions.push(
|
|
6414
|
+
conditions.push(eq51(tenantsInIam.isActive, query.isActive));
|
|
5037
6415
|
}
|
|
5038
6416
|
if (query.filter === "isActive:true") {
|
|
5039
|
-
conditions.push(
|
|
6417
|
+
conditions.push(eq51(tenantsInIam.isActive, true));
|
|
5040
6418
|
} else if (query.filter === "isActive:false") {
|
|
5041
|
-
conditions.push(
|
|
6419
|
+
conditions.push(eq51(tenantsInIam.isActive, false));
|
|
5042
6420
|
}
|
|
5043
6421
|
if (query.search?.trim()) {
|
|
5044
6422
|
const term = `%${query.search.trim()}%`;
|
|
5045
|
-
const searchCond =
|
|
5046
|
-
|
|
5047
|
-
|
|
6423
|
+
const searchCond = or3(
|
|
6424
|
+
ilike3(tenantsInIam.id, term),
|
|
6425
|
+
ilike3(sql23`${tenantsInIam.name}::text`, term)
|
|
5048
6426
|
);
|
|
5049
6427
|
if (searchCond) {
|
|
5050
6428
|
conditions.push(searchCond);
|
|
5051
6429
|
}
|
|
5052
6430
|
}
|
|
5053
|
-
const orderDir = query.order === "asc" ?
|
|
5054
|
-
const sortCol = query.sort &&
|
|
5055
|
-
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;
|
|
5056
6434
|
const [tenants, totalResult] = await Promise.all([
|
|
5057
6435
|
database.select().from(tenantsInIam).where(whereClause).orderBy(orderDir(sortCol)).limit(limit).offset(offset),
|
|
5058
|
-
database.select({ count:
|
|
6436
|
+
database.select({ count: sql23`count(*)` }).from(tenantsInIam).where(whereClause)
|
|
5059
6437
|
]);
|
|
5060
6438
|
const total = Number(totalResult[0]?.count || 0);
|
|
5061
6439
|
return c.json({ tenants, total, page, limit }, 200);
|
|
5062
6440
|
};
|
|
5063
6441
|
|
|
5064
6442
|
// src/routes/tenants/handler/update-tenant.ts
|
|
5065
|
-
import { eq as
|
|
6443
|
+
import { eq as eq52, sql as sql24 } from "drizzle-orm";
|
|
5066
6444
|
var updateTenantHandler = async (c) => {
|
|
5067
6445
|
hasRoleThrow(c, ["owner", "tenant-admin"]);
|
|
5068
6446
|
const { id } = c.req.valid("param");
|
|
5069
6447
|
const body = c.req.valid("json");
|
|
5070
6448
|
const database = c.get("database");
|
|
5071
|
-
const [existing] = await database.select().from(tenantsInIam).where(
|
|
6449
|
+
const [existing] = await database.select().from(tenantsInIam).where(eq52(tenantsInIam.id, id)).limit(1);
|
|
5072
6450
|
if (!existing) {
|
|
5073
6451
|
return c.json({ error: "Tenant not found" }, 404);
|
|
5074
6452
|
}
|
|
@@ -5111,8 +6489,8 @@ var updateTenantHandler = async (c) => {
|
|
|
5111
6489
|
}
|
|
5112
6490
|
const [updated] = await database.update(tenantsInIam).set({
|
|
5113
6491
|
...updateData,
|
|
5114
|
-
updatedAt:
|
|
5115
|
-
}).where(
|
|
6492
|
+
updatedAt: sql24`CURRENT_TIMESTAMP`
|
|
6493
|
+
}).where(eq52(tenantsInIam.id, id)).returning();
|
|
5116
6494
|
if (!updated) {
|
|
5117
6495
|
return c.json({ error: "Tenant not found" }, 404);
|
|
5118
6496
|
}
|
|
@@ -5367,36 +6745,36 @@ var assignUserRoleHandler = async (c) => {
|
|
|
5367
6745
|
};
|
|
5368
6746
|
|
|
5369
6747
|
// src/routes/user-roles/handler/list-user-roles.ts
|
|
5370
|
-
import { and as
|
|
6748
|
+
import { and as and48, eq as eq53 } from "drizzle-orm";
|
|
5371
6749
|
var listUserRolesHandler = async (c) => {
|
|
5372
6750
|
const query = c.req.valid("query");
|
|
5373
6751
|
const database = c.get("database");
|
|
5374
6752
|
const tenantId = c.get("tenantId");
|
|
5375
|
-
const conditions = [
|
|
6753
|
+
const conditions = [eq53(userRolesInIam.tenantId, tenantId)];
|
|
5376
6754
|
if (query.userId) {
|
|
5377
|
-
conditions.push(
|
|
6755
|
+
conditions.push(eq53(userRolesInIam.userId, query.userId));
|
|
5378
6756
|
}
|
|
5379
6757
|
if (query.roleId) {
|
|
5380
|
-
conditions.push(
|
|
6758
|
+
conditions.push(eq53(userRolesInIam.roleId, query.roleId));
|
|
5381
6759
|
}
|
|
5382
|
-
const userRoles = await database.select().from(userRolesInIam).where(
|
|
6760
|
+
const userRoles = await database.select().from(userRolesInIam).where(and48(...conditions));
|
|
5383
6761
|
return c.json({ userRoles }, 200);
|
|
5384
6762
|
};
|
|
5385
6763
|
|
|
5386
6764
|
// src/routes/user-roles/handler/revoke-user-role.ts
|
|
5387
|
-
import { and as
|
|
6765
|
+
import { and as and49, eq as eq54 } from "drizzle-orm";
|
|
5388
6766
|
var revokeUserRoleHandler = async (c) => {
|
|
5389
6767
|
const { id } = c.req.valid("param");
|
|
5390
6768
|
const database = c.get("database");
|
|
5391
6769
|
const tenantId = c.get("tenantId");
|
|
5392
6770
|
const [existing] = await database.select().from(userRolesInIam).where(
|
|
5393
|
-
|
|
6771
|
+
and49(eq54(userRolesInIam.id, id), eq54(userRolesInIam.tenantId, tenantId))
|
|
5394
6772
|
).limit(1);
|
|
5395
6773
|
if (!existing) {
|
|
5396
6774
|
return c.json({ error: "User role not found" }, 404);
|
|
5397
6775
|
}
|
|
5398
6776
|
await database.delete(userRolesInIam).where(
|
|
5399
|
-
|
|
6777
|
+
and49(eq54(userRolesInIam.id, id), eq54(userRolesInIam.tenantId, tenantId))
|
|
5400
6778
|
);
|
|
5401
6779
|
return c.json({ message: "Role revoked from user" }, 200);
|
|
5402
6780
|
};
|
|
@@ -5520,20 +6898,20 @@ var user_roles_route_default = userRoleRoutes;
|
|
|
5520
6898
|
import { createRoute as createRoute14, OpenAPIHono as OpenAPIHono14 } from "@hono/zod-openapi";
|
|
5521
6899
|
|
|
5522
6900
|
// src/routes/users/handler/ban-user.ts
|
|
5523
|
-
import { and as
|
|
6901
|
+
import { and as and50, eq as eq55, sql as sql25 } from "drizzle-orm";
|
|
5524
6902
|
var banUserHandler = async (c) => {
|
|
5525
6903
|
const { id } = c.req.valid("param");
|
|
5526
6904
|
const body = c.req.valid("json");
|
|
5527
6905
|
const database = c.get("database");
|
|
5528
6906
|
const tenantId = c.get("tenantId");
|
|
5529
|
-
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);
|
|
5530
6908
|
if (!existing) {
|
|
5531
6909
|
return c.json({ error: "User not found" }, 404);
|
|
5532
6910
|
}
|
|
5533
6911
|
const [updated] = await database.update(usersInIam).set({
|
|
5534
6912
|
bannedUntil: body.bannedUntil || null,
|
|
5535
|
-
updatedAt:
|
|
5536
|
-
}).where(
|
|
6913
|
+
updatedAt: sql25`CURRENT_TIMESTAMP`
|
|
6914
|
+
}).where(and50(eq55(usersInIam.id, id), eq55(usersInIam.tenantId, tenantId))).returning({
|
|
5537
6915
|
id: usersInIam.id,
|
|
5538
6916
|
tenantId: usersInIam.tenantId,
|
|
5539
6917
|
fullName: usersInIam.fullName,
|
|
@@ -5552,17 +6930,17 @@ var banUserHandler = async (c) => {
|
|
|
5552
6930
|
};
|
|
5553
6931
|
|
|
5554
6932
|
// src/routes/users/helper/user.ts
|
|
5555
|
-
import { and as
|
|
6933
|
+
import { and as and51, eq as eq56, sql as sql26 } from "drizzle-orm";
|
|
5556
6934
|
var checkUserExists = async ({
|
|
5557
6935
|
database,
|
|
5558
6936
|
identifier,
|
|
5559
6937
|
tenantId,
|
|
5560
6938
|
isEmail
|
|
5561
6939
|
}) => {
|
|
5562
|
-
const whereClause = isEmail ?
|
|
5563
|
-
|
|
5564
|
-
|
|
5565
|
-
) :
|
|
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));
|
|
5566
6944
|
const [user] = await database.select().from(usersInIam).where(whereClause).limit(1);
|
|
5567
6945
|
return user || null;
|
|
5568
6946
|
};
|
|
@@ -5572,14 +6950,170 @@ var checkHandleExists = async ({
|
|
|
5572
6950
|
tenantId
|
|
5573
6951
|
}) => {
|
|
5574
6952
|
const [existingHandle] = await database.select().from(usersInIam).where(
|
|
5575
|
-
|
|
5576
|
-
|
|
5577
|
-
|
|
6953
|
+
and51(
|
|
6954
|
+
eq56(usersInIam.tenantId, tenantId),
|
|
6955
|
+
sql26`lower(${usersInIam.handle}) = lower(${handle})`
|
|
5578
6956
|
)
|
|
5579
6957
|
).limit(1);
|
|
5580
6958
|
return existingHandle || null;
|
|
5581
6959
|
};
|
|
5582
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
|
+
|
|
5583
7117
|
// src/routes/users/handler/create-user.ts
|
|
5584
7118
|
var createUserHandler = async (c) => {
|
|
5585
7119
|
const body = c.req.valid("json");
|
|
@@ -5610,41 +7144,55 @@ var createUserHandler = async (c) => {
|
|
|
5610
7144
|
if (existingHandle) {
|
|
5611
7145
|
return c.json({ error: "Handle already taken" }, 409);
|
|
5612
7146
|
}
|
|
5613
|
-
const [user] = await database
|
|
5614
|
-
|
|
5615
|
-
|
|
5616
|
-
|
|
5617
|
-
|
|
5618
|
-
|
|
5619
|
-
|
|
5620
|
-
|
|
5621
|
-
|
|
5622
|
-
|
|
5623
|
-
|
|
5624
|
-
|
|
5625
|
-
|
|
5626
|
-
|
|
5627
|
-
|
|
5628
|
-
|
|
5629
|
-
|
|
5630
|
-
|
|
5631
|
-
|
|
5632
|
-
|
|
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];
|
|
5633
7181
|
});
|
|
5634
7182
|
return c.json({ user: normalizeUser(user) }, 201);
|
|
5635
7183
|
};
|
|
5636
7184
|
|
|
5637
7185
|
// src/routes/users/handler/delete-user.ts
|
|
5638
|
-
import { and as
|
|
7186
|
+
import { and as and52, eq as eq57 } from "drizzle-orm";
|
|
5639
7187
|
var deleteUserHandler = async (c) => {
|
|
5640
7188
|
const { id } = c.req.valid("param");
|
|
5641
7189
|
const database = c.get("database");
|
|
5642
7190
|
const tenantId = c.get("tenantId");
|
|
5643
|
-
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);
|
|
5644
7192
|
if (!existing) {
|
|
5645
7193
|
return c.json({ error: "User not found" }, 404);
|
|
5646
7194
|
}
|
|
5647
|
-
await database.delete(usersInIam).where(
|
|
7195
|
+
await database.delete(usersInIam).where(and52(eq57(usersInIam.id, id), eq57(usersInIam.tenantId, tenantId)));
|
|
5648
7196
|
return c.json({ message: "User deleted" }, 200);
|
|
5649
7197
|
};
|
|
5650
7198
|
|
|
@@ -5664,9 +7212,32 @@ var getUserHandler = async (c) => {
|
|
|
5664
7212
|
return c.json({ user: normalizeUser(user) }, 200);
|
|
5665
7213
|
};
|
|
5666
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
|
+
|
|
5667
7238
|
// src/routes/users/handler/list-users.ts
|
|
5668
|
-
import { and as
|
|
5669
|
-
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 = {
|
|
5670
7241
|
createdAt: usersInIam.createdAt,
|
|
5671
7242
|
updatedAt: usersInIam.updatedAt,
|
|
5672
7243
|
fullName: usersInIam.fullName,
|
|
@@ -5680,26 +7251,26 @@ var listUsersHandler = async (c) => {
|
|
|
5680
7251
|
const page = query.page || 1;
|
|
5681
7252
|
const limit = query.limit || 20;
|
|
5682
7253
|
const offset = (page - 1) * limit;
|
|
5683
|
-
const conditions = [
|
|
7254
|
+
const conditions = [eq58(usersInIam.tenantId, tenantId)];
|
|
5684
7255
|
if (query.email) {
|
|
5685
|
-
conditions.push(
|
|
7256
|
+
conditions.push(ilike4(usersInIam.email, `%${query.email}%`));
|
|
5686
7257
|
}
|
|
5687
7258
|
if (query.phone) {
|
|
5688
|
-
conditions.push(
|
|
7259
|
+
conditions.push(ilike4(usersInIam.phone, `%${query.phone}%`));
|
|
5689
7260
|
}
|
|
5690
7261
|
if (query.handle) {
|
|
5691
|
-
conditions.push(
|
|
7262
|
+
conditions.push(ilike4(usersInIam.handle, `%${query.handle}%`));
|
|
5692
7263
|
}
|
|
5693
7264
|
if (query.filter === "emailVerified") {
|
|
5694
|
-
conditions.push(
|
|
7265
|
+
conditions.push(eq58(usersInIam.emailVerified, true));
|
|
5695
7266
|
} else if (query.filter === "phoneVerified") {
|
|
5696
|
-
conditions.push(
|
|
7267
|
+
conditions.push(eq58(usersInIam.phoneVerified, true));
|
|
5697
7268
|
} else if (query.filter === "notVerified") {
|
|
5698
|
-
conditions.push(
|
|
5699
|
-
conditions.push(
|
|
7269
|
+
conditions.push(eq58(usersInIam.emailVerified, false));
|
|
7270
|
+
conditions.push(eq58(usersInIam.phoneVerified, false));
|
|
5700
7271
|
}
|
|
5701
|
-
const orderDir = query.order === "asc" ?
|
|
5702
|
-
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;
|
|
5703
7274
|
const [users, totalResult] = await Promise.all([
|
|
5704
7275
|
database.select({
|
|
5705
7276
|
id: usersInIam.id,
|
|
@@ -5712,8 +7283,8 @@ var listUsersHandler = async (c) => {
|
|
|
5712
7283
|
emailVerified: usersInIam.emailVerified,
|
|
5713
7284
|
phoneVerified: usersInIam.phoneVerified,
|
|
5714
7285
|
lastSignInAt: usersInIam.lastSignInAt
|
|
5715
|
-
}).from(usersInIam).where(
|
|
5716
|
-
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))
|
|
5717
7288
|
]);
|
|
5718
7289
|
const total = Number(totalResult[0]?.count || 0);
|
|
5719
7290
|
return c.json(
|
|
@@ -5731,18 +7302,18 @@ var listUsersHandler = async (c) => {
|
|
|
5731
7302
|
};
|
|
5732
7303
|
|
|
5733
7304
|
// src/routes/users/handler/search-users.ts
|
|
5734
|
-
import { and as
|
|
7305
|
+
import { and as and54, eq as eq59, ilike as ilike5, or as or4 } from "drizzle-orm";
|
|
5735
7306
|
var searchUsersHandler = async (c) => {
|
|
5736
7307
|
const query = c.req.valid("query");
|
|
5737
7308
|
const database = c.get("database");
|
|
5738
7309
|
const tenantId = c.get("tenantId");
|
|
5739
7310
|
const limit = query.limit || 20;
|
|
5740
|
-
const conditions = [
|
|
7311
|
+
const conditions = [eq59(usersInIam.tenantId, tenantId)];
|
|
5741
7312
|
if (query.search && query.search.trim().length > 0) {
|
|
5742
|
-
const searchCondition =
|
|
5743
|
-
|
|
5744
|
-
|
|
5745
|
-
|
|
7313
|
+
const searchCondition = or4(
|
|
7314
|
+
ilike5(usersInIam.fullName, `%${query.search}%`),
|
|
7315
|
+
ilike5(usersInIam.email, `%${query.search}%`),
|
|
7316
|
+
ilike5(usersInIam.handle, `%${query.search}%`)
|
|
5746
7317
|
);
|
|
5747
7318
|
if (searchCondition) {
|
|
5748
7319
|
conditions.push(searchCondition);
|
|
@@ -5755,26 +7326,26 @@ var searchUsersHandler = async (c) => {
|
|
|
5755
7326
|
phone: usersInIam.phone,
|
|
5756
7327
|
handle: usersInIam.handle,
|
|
5757
7328
|
image: usersInIam.image
|
|
5758
|
-
}).from(usersInIam).where(
|
|
7329
|
+
}).from(usersInIam).where(and54(...conditions)).limit(limit);
|
|
5759
7330
|
return c.json({ users }, 200);
|
|
5760
7331
|
};
|
|
5761
7332
|
|
|
5762
7333
|
// src/routes/users/handler/update-user.ts
|
|
5763
|
-
import { and as
|
|
7334
|
+
import { and as and55, eq as eq60, sql as sql28 } from "drizzle-orm";
|
|
5764
7335
|
var updateUserHandler = async (c) => {
|
|
5765
7336
|
const { id } = c.req.valid("param");
|
|
5766
7337
|
const body = c.req.valid("json");
|
|
5767
7338
|
const database = c.get("database");
|
|
5768
7339
|
const tenantId = c.get("tenantId");
|
|
5769
|
-
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);
|
|
5770
7341
|
if (!existing) {
|
|
5771
7342
|
return c.json({ error: "User not found" }, 404);
|
|
5772
7343
|
}
|
|
5773
7344
|
if (body.handle && body.handle !== existing.handle) {
|
|
5774
7345
|
const [handleExists] = await database.select().from(usersInIam).where(
|
|
5775
|
-
|
|
5776
|
-
|
|
5777
|
-
|
|
7346
|
+
and55(
|
|
7347
|
+
eq60(usersInIam.tenantId, tenantId),
|
|
7348
|
+
sql28`lower(${usersInIam.handle}) = lower(${body.handle})`
|
|
5778
7349
|
)
|
|
5779
7350
|
).limit(1);
|
|
5780
7351
|
if (handleExists) {
|
|
@@ -5805,8 +7376,8 @@ var updateUserHandler = async (c) => {
|
|
|
5805
7376
|
}
|
|
5806
7377
|
const [updated] = await database.update(usersInIam).set({
|
|
5807
7378
|
...updateData,
|
|
5808
|
-
updatedAt:
|
|
5809
|
-
}).where(
|
|
7379
|
+
updatedAt: sql28`CURRENT_TIMESTAMP`
|
|
7380
|
+
}).where(and55(eq60(usersInIam.id, id), eq60(usersInIam.tenantId, tenantId))).returning({
|
|
5810
7381
|
id: usersInIam.id,
|
|
5811
7382
|
tenantId: usersInIam.tenantId,
|
|
5812
7383
|
fullName: usersInIam.fullName,
|
|
@@ -5859,6 +7430,7 @@ var createUserSchema = z11.object({
|
|
|
5859
7430
|
fullName: z11.string().min(1),
|
|
5860
7431
|
handle: z11.string().optional(),
|
|
5861
7432
|
image: z11.string().url().optional(),
|
|
7433
|
+
password: z11.string().min(8).max(128).optional(),
|
|
5862
7434
|
emailVerified: z11.boolean().default(false).optional(),
|
|
5863
7435
|
phoneVerified: z11.boolean().default(false).optional()
|
|
5864
7436
|
});
|
|
@@ -5889,6 +7461,46 @@ var deleteUserResponseSchema = z11.object({
|
|
|
5889
7461
|
var errorResponseSchema9 = z11.object({
|
|
5890
7462
|
error: z11.string()
|
|
5891
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
|
+
});
|
|
5892
7504
|
var searchUsersQuerySchema = z11.object({
|
|
5893
7505
|
search: z11.string().optional().describe("Search term"),
|
|
5894
7506
|
limit: z11.coerce.number().int().positive().optional().default(20).describe("Limit")
|
|
@@ -6107,38 +7719,96 @@ var searchUsersRoute = createRoute14({
|
|
|
6107
7719
|
}
|
|
6108
7720
|
}
|
|
6109
7721
|
});
|
|
6110
|
-
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);
|
|
6111
7781
|
var users_route_default = userRoutes;
|
|
6112
7782
|
|
|
6113
7783
|
// src/routes/verifications/verifications.route.ts
|
|
6114
7784
|
import { createRoute as createRoute15, OpenAPIHono as OpenAPIHono15 } from "@hono/zod-openapi";
|
|
6115
7785
|
|
|
6116
7786
|
// src/routes/verifications/handler/invalidate-verification.ts
|
|
6117
|
-
import { and as
|
|
7787
|
+
import { and as and56, eq as eq61 } from "drizzle-orm";
|
|
6118
7788
|
var invalidateVerificationHandler = async (c) => {
|
|
6119
7789
|
const { id } = c.req.valid("param");
|
|
6120
7790
|
const database = c.get("database");
|
|
6121
7791
|
const tenantId = c.get("tenantId");
|
|
6122
7792
|
const [existing] = await database.select().from(verificationsInIam).where(
|
|
6123
|
-
|
|
6124
|
-
|
|
6125
|
-
|
|
7793
|
+
and56(
|
|
7794
|
+
eq61(verificationsInIam.id, id),
|
|
7795
|
+
eq61(verificationsInIam.tenantId, tenantId)
|
|
6126
7796
|
)
|
|
6127
7797
|
).limit(1);
|
|
6128
7798
|
if (!existing) {
|
|
6129
7799
|
return c.json({ error: "Verification not found" }, 404);
|
|
6130
7800
|
}
|
|
6131
7801
|
await database.delete(verificationsInIam).where(
|
|
6132
|
-
|
|
6133
|
-
|
|
6134
|
-
|
|
7802
|
+
and56(
|
|
7803
|
+
eq61(verificationsInIam.id, id),
|
|
7804
|
+
eq61(verificationsInIam.tenantId, tenantId)
|
|
6135
7805
|
)
|
|
6136
7806
|
);
|
|
6137
7807
|
return c.json({ message: "Verification invalidated" }, 200);
|
|
6138
7808
|
};
|
|
6139
7809
|
|
|
6140
7810
|
// src/routes/verifications/handler/list-verifications.ts
|
|
6141
|
-
import { and as
|
|
7811
|
+
import { and as and57, eq as eq62, sql as sql29 } from "drizzle-orm";
|
|
6142
7812
|
var listVerificationsHandler = async (c) => {
|
|
6143
7813
|
const query = c.req.valid("query");
|
|
6144
7814
|
const database = c.get("database");
|
|
@@ -6146,27 +7816,27 @@ var listVerificationsHandler = async (c) => {
|
|
|
6146
7816
|
const page = query.page || 1;
|
|
6147
7817
|
const limit = query.limit || 20;
|
|
6148
7818
|
const offset = (page - 1) * limit;
|
|
6149
|
-
const conditions = [
|
|
7819
|
+
const conditions = [eq62(verificationsInIam.tenantId, tenantId)];
|
|
6150
7820
|
if (query.userId) {
|
|
6151
|
-
conditions.push(
|
|
7821
|
+
conditions.push(eq62(verificationsInIam.userId, query.userId));
|
|
6152
7822
|
}
|
|
6153
7823
|
if (query.type) {
|
|
6154
|
-
conditions.push(
|
|
7824
|
+
conditions.push(eq62(verificationsInIam.type, query.type));
|
|
6155
7825
|
}
|
|
6156
7826
|
if (query.status) {
|
|
6157
7827
|
if (query.status === "active") {
|
|
6158
|
-
conditions.push(
|
|
7828
|
+
conditions.push(sql29`${verificationsInIam.expiresAt} > CURRENT_TIMESTAMP`);
|
|
6159
7829
|
} else if (query.status === "expired") {
|
|
6160
7830
|
conditions.push(
|
|
6161
|
-
|
|
7831
|
+
sql29`${verificationsInIam.expiresAt} <= CURRENT_TIMESTAMP`
|
|
6162
7832
|
);
|
|
6163
7833
|
} else if (query.status === "consumed") {
|
|
6164
|
-
conditions.push(
|
|
7834
|
+
conditions.push(sql29`${verificationsInIam.attempt} >= 3`);
|
|
6165
7835
|
}
|
|
6166
7836
|
}
|
|
6167
7837
|
const [verifications, totalResult] = await Promise.all([
|
|
6168
|
-
database.select().from(verificationsInIam).where(
|
|
6169
|
-
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))
|
|
6170
7840
|
]);
|
|
6171
7841
|
const total = Number(totalResult[0]?.count || 0);
|
|
6172
7842
|
return c.json({ verifications, total, page, limit }, 200);
|
|
@@ -6467,7 +8137,7 @@ var createSessionMiddleware = () => {
|
|
|
6467
8137
|
// src/middlewares/tenant-middleware.ts
|
|
6468
8138
|
import { logger as logger3 } from "@mesob/common";
|
|
6469
8139
|
import { createMiddleware as createMiddleware2 } from "hono/factory";
|
|
6470
|
-
import { HTTPException as
|
|
8140
|
+
import { HTTPException as HTTPException5 } from "hono/http-exception";
|
|
6471
8141
|
function resolveHost(hostHeader, forwardedHost) {
|
|
6472
8142
|
const hostHeaderStr = hostHeader || "";
|
|
6473
8143
|
const forwardedHostStr = forwardedHost || "";
|
|
@@ -6531,7 +8201,7 @@ var createTenantMiddleware = (database, config) => {
|
|
|
6531
8201
|
);
|
|
6532
8202
|
c.set("host", host);
|
|
6533
8203
|
if (!host) {
|
|
6534
|
-
throw new
|
|
8204
|
+
throw new HTTPException5(400, { message: "Missing Host header" });
|
|
6535
8205
|
}
|
|
6536
8206
|
let tenantId = null;
|
|
6537
8207
|
let tenant = null;
|
|
@@ -6540,13 +8210,13 @@ var createTenantMiddleware = (database, config) => {
|
|
|
6540
8210
|
tenantId = result.tenantId;
|
|
6541
8211
|
tenant = result.tenant;
|
|
6542
8212
|
} catch {
|
|
6543
|
-
throw new
|
|
8213
|
+
throw new HTTPException5(500, { message: "Tenant resolution failed" });
|
|
6544
8214
|
}
|
|
6545
8215
|
c.set("tenantId", tenantId);
|
|
6546
8216
|
c.set("tenant", tenant);
|
|
6547
8217
|
const error = validateTenant(tenantId, tenant);
|
|
6548
8218
|
if (error) {
|
|
6549
|
-
throw new
|
|
8219
|
+
throw new HTTPException5(404, { message: error });
|
|
6550
8220
|
}
|
|
6551
8221
|
return await next();
|
|
6552
8222
|
});
|