@mesob/auth-hono 0.4.4 → 0.4.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -862,8 +862,17 @@ var userSchema = z.object({
862
862
  emailVerified: z.boolean(),
863
863
  phoneVerified: z.boolean(),
864
864
  lastSignInAt: z.string().datetime().nullable(),
865
+ createdAt: z.string().datetime().nullable().optional(),
866
+ userType: z.array(z.string()).optional(),
865
867
  roles: z.array(z.string()).nullable().optional(),
866
868
  roleCodes: z.array(z.string()).nullable().optional(),
869
+ userRoles: z.array(
870
+ z.object({
871
+ code: z.string(),
872
+ name: z.record(z.string(), z.string())
873
+ })
874
+ ).nullable().optional(),
875
+ activeSessionCount: z.number().int().min(0).optional(),
867
876
  permissions: z.array(z.string()).nullable().optional()
868
877
  });
869
878
  var sessionSchema = z.object({
@@ -3547,13 +3556,18 @@ function buildPermissionDescription(code) {
3547
3556
  };
3548
3557
  }
3549
3558
  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
- }));
3559
+ const entries = getPermissionEntries3(permissions);
3560
+ const byId = /* @__PURE__ */ new Map();
3561
+ for (const entry of entries) {
3562
+ byId.set(entry.code, {
3563
+ id: entry.code,
3564
+ application: entry.application,
3565
+ feature: entry.feature,
3566
+ activity: entry.activity,
3567
+ description: buildPermissionDescription(entry.code)
3568
+ });
3569
+ }
3570
+ return [...byId.values()];
3557
3571
  }
3558
3572
  async function seedPermissions({
3559
3573
  database,
@@ -7236,22 +7250,78 @@ var inviteUserHandler = async (c) => {
7236
7250
  };
7237
7251
 
7238
7252
  // src/routes/users/handler/list-users.ts
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 = {
7241
- createdAt: usersInIam.createdAt,
7242
- updatedAt: usersInIam.updatedAt,
7253
+ import { and as and53, asc as asc5, desc as desc5, eq as eq58, ilike as ilike4, inArray as inArray6, or as or4, sql as sql27 } from "drizzle-orm";
7254
+ var userSelect = {
7255
+ id: usersInIam.id,
7256
+ tenantId: usersInIam.tenantId,
7243
7257
  fullName: usersInIam.fullName,
7258
+ email: usersInIam.email,
7259
+ phone: usersInIam.phone,
7244
7260
  handle: usersInIam.handle,
7245
- lastSignInAt: usersInIam.lastSignInAt
7261
+ image: usersInIam.image,
7262
+ emailVerified: usersInIam.emailVerified,
7263
+ phoneVerified: usersInIam.phoneVerified,
7264
+ lastSignInAt: usersInIam.lastSignInAt,
7265
+ createdAt: usersInIam.createdAt,
7266
+ userType: usersInIam.userType,
7267
+ roleCount: sql27`(select count(*)::int from ${userRolesInIam} where ${userRolesInIam.userId} = ${usersInIam.id} and ${userRolesInIam.tenantId} = ${usersInIam.tenantId})`.as(
7268
+ "roleCount"
7269
+ ),
7270
+ activeSessionCount: sql27`(select count(*)::int from ${sessionsInIam} where ${sessionsInIam.userId} = ${usersInIam.id} and ${sessionsInIam.tenantId} = ${usersInIam.tenantId} and ${sessionsInIam.expiresAt} > now())`.as(
7271
+ "activeSessionCount"
7272
+ )
7246
7273
  };
7274
+ var sortColumnMap4 = {
7275
+ fullName: usersInIam.fullName,
7276
+ email: usersInIam.email,
7277
+ phone: usersInIam.phone,
7278
+ userType: sql27`(${usersInIam.userType})[1]`,
7279
+ roleCount: sql27`(select count(*)::int from ${userRolesInIam} where ${userRolesInIam.userId} = ${usersInIam.id} and ${userRolesInIam.tenantId} = ${usersInIam.tenantId})`,
7280
+ lastSignInAt: usersInIam.lastSignInAt,
7281
+ activeSessionCount: sql27`(select count(*)::int from ${sessionsInIam} where ${sessionsInIam.userId} = ${usersInIam.id} and ${sessionsInIam.tenantId} = ${usersInIam.tenantId} and ${sessionsInIam.expiresAt} > now())`,
7282
+ createdAt: usersInIam.createdAt
7283
+ };
7284
+ function roleNameToLocaleRecord(name, code) {
7285
+ if (typeof name === "string") {
7286
+ return { en: name };
7287
+ }
7288
+ if (name && typeof name === "object" && !Array.isArray(name)) {
7289
+ const rec = name;
7290
+ const out = {};
7291
+ for (const [k, v] of Object.entries(rec)) {
7292
+ if (typeof v === "string") {
7293
+ out[k] = v;
7294
+ }
7295
+ }
7296
+ return Object.keys(out).length > 0 ? out : { en: code };
7297
+ }
7298
+ return { en: code };
7299
+ }
7247
7300
  var listUsersHandler = async (c) => {
7248
7301
  const query = c.req.valid("query");
7249
7302
  const database = c.get("database");
7250
7303
  const tenantId = c.get("tenantId");
7304
+ const config = c.get("config");
7251
7305
  const page = query.page || 1;
7252
7306
  const limit = query.limit || 20;
7253
7307
  const offset = (page - 1) * limit;
7308
+ const userTypeFilter = (query.userType && query.userType !== "all" ? query.userType : null) ?? config.userType;
7254
7309
  const conditions = [eq58(usersInIam.tenantId, tenantId)];
7310
+ if (userTypeFilter) {
7311
+ conditions.push(
7312
+ sql27`${usersInIam.userType} @> ARRAY[${userTypeFilter}]::text[]`
7313
+ );
7314
+ }
7315
+ if (query.search?.trim()) {
7316
+ const term = `%${query.search.trim().replace(/[%_\\]/g, (c2) => `\\${c2}`)}%`;
7317
+ conditions.push(
7318
+ or4(
7319
+ ilike4(usersInIam.fullName, term),
7320
+ ilike4(usersInIam.email, term),
7321
+ ilike4(usersInIam.phone, term)
7322
+ )
7323
+ );
7324
+ }
7255
7325
  if (query.email) {
7256
7326
  conditions.push(ilike4(usersInIam.email, `%${query.email}%`));
7257
7327
  }
@@ -7261,37 +7331,58 @@ var listUsersHandler = async (c) => {
7261
7331
  if (query.handle) {
7262
7332
  conditions.push(ilike4(usersInIam.handle, `%${query.handle}%`));
7263
7333
  }
7264
- if (query.filter === "emailVerified") {
7265
- conditions.push(eq58(usersInIam.emailVerified, true));
7266
- } else if (query.filter === "phoneVerified") {
7267
- conditions.push(eq58(usersInIam.phoneVerified, true));
7268
- } else if (query.filter === "notVerified") {
7334
+ if (query.filter === "verified") {
7335
+ conditions.push(
7336
+ or4(
7337
+ eq58(usersInIam.emailVerified, true),
7338
+ eq58(usersInIam.phoneVerified, true)
7339
+ )
7340
+ );
7341
+ } else if (query.filter === "unverified") {
7269
7342
  conditions.push(eq58(usersInIam.emailVerified, false));
7270
7343
  conditions.push(eq58(usersInIam.phoneVerified, false));
7271
7344
  }
7272
7345
  const orderDir = query.order === "asc" ? asc5 : desc5;
7273
- const sortCol = query.sort && sortColumnMap4[query.sort] ? sortColumnMap4[query.sort] : usersInIam.createdAt;
7346
+ const sortCol = query.sort && sortColumnMap4[query.sort] ? sortColumnMap4[query.sort] : usersInIam.fullName;
7347
+ const whereClause = and53(...conditions);
7274
7348
  const [users, totalResult] = await Promise.all([
7275
- database.select({
7276
- id: usersInIam.id,
7277
- tenantId: usersInIam.tenantId,
7278
- fullName: usersInIam.fullName,
7279
- email: usersInIam.email,
7280
- phone: usersInIam.phone,
7281
- handle: usersInIam.handle,
7282
- image: usersInIam.image,
7283
- emailVerified: usersInIam.emailVerified,
7284
- phoneVerified: usersInIam.phoneVerified,
7285
- lastSignInAt: usersInIam.lastSignInAt
7286
- }).from(usersInIam).where(and53(...conditions)).orderBy(orderDir(sortCol)).limit(limit).offset(offset),
7287
- database.select({ count: sql27`count(*)` }).from(usersInIam).where(and53(...conditions))
7349
+ database.select(userSelect).from(usersInIam).where(whereClause).orderBy(orderDir(sortCol)).limit(limit).offset(offset),
7350
+ database.select({ count: sql27`count(*)` }).from(usersInIam).where(whereClause)
7288
7351
  ]);
7289
7352
  const total = Number(totalResult[0]?.count || 0);
7353
+ const userIds = users.map((u) => u.id);
7354
+ const roleRows = userIds.length > 0 ? await database.select({
7355
+ userId: userRolesInIam.userId,
7356
+ code: rolesInIam.code,
7357
+ name: rolesInIam.name
7358
+ }).from(userRolesInIam).innerJoin(
7359
+ rolesInIam,
7360
+ and53(
7361
+ eq58(rolesInIam.tenantId, userRolesInIam.tenantId),
7362
+ eq58(rolesInIam.id, userRolesInIam.roleId)
7363
+ )
7364
+ ).where(
7365
+ and53(
7366
+ eq58(userRolesInIam.tenantId, tenantId),
7367
+ inArray6(userRolesInIam.userId, userIds)
7368
+ )
7369
+ ) : [];
7370
+ const userRolesMap = /* @__PURE__ */ new Map();
7371
+ for (const r of roleRows) {
7372
+ const list = userRolesMap.get(r.userId) ?? [];
7373
+ list.push({
7374
+ code: r.code,
7375
+ name: roleNameToLocaleRecord(r.name, r.code)
7376
+ });
7377
+ userRolesMap.set(r.userId, list);
7378
+ }
7290
7379
  return c.json(
7291
7380
  {
7292
7381
  users: users.map((u) => ({
7293
7382
  ...u,
7294
- roles: null
7383
+ roles: null,
7384
+ userRoles: userRolesMap.get(u.id) ?? [],
7385
+ activeSessionCount: Number(u.activeSessionCount) ?? 0
7295
7386
  })),
7296
7387
  total,
7297
7388
  page,
@@ -7302,7 +7393,7 @@ var listUsersHandler = async (c) => {
7302
7393
  };
7303
7394
 
7304
7395
  // src/routes/users/handler/search-users.ts
7305
- import { and as and54, eq as eq59, ilike as ilike5, or as or4 } from "drizzle-orm";
7396
+ import { and as and54, eq as eq59, ilike as ilike5, or as or5 } from "drizzle-orm";
7306
7397
  var searchUsersHandler = async (c) => {
7307
7398
  const query = c.req.valid("query");
7308
7399
  const database = c.get("database");
@@ -7310,7 +7401,7 @@ var searchUsersHandler = async (c) => {
7310
7401
  const limit = query.limit || 20;
7311
7402
  const conditions = [eq59(usersInIam.tenantId, tenantId)];
7312
7403
  if (query.search && query.search.trim().length > 0) {
7313
- const searchCondition = or4(
7404
+ const searchCondition = or5(
7314
7405
  ilike5(usersInIam.fullName, `%${query.search}%`),
7315
7406
  ilike5(usersInIam.email, `%${query.search}%`),
7316
7407
  ilike5(usersInIam.handle, `%${query.search}%`)
@@ -7331,7 +7422,7 @@ var searchUsersHandler = async (c) => {
7331
7422
  };
7332
7423
 
7333
7424
  // src/routes/users/handler/update-user.ts
7334
- import { and as and55, eq as eq60, sql as sql28 } from "drizzle-orm";
7425
+ import { and as and55, eq as eq60, inArray as inArray7, sql as sql28 } from "drizzle-orm";
7335
7426
  var updateUserHandler = async (c) => {
7336
7427
  const { id } = c.req.valid("param");
7337
7428
  const body = c.req.valid("json");
@@ -7392,32 +7483,80 @@ var updateUserHandler = async (c) => {
7392
7483
  if (!updated) {
7393
7484
  return c.json({ error: "User not found" }, 404);
7394
7485
  }
7395
- return c.json({ user: normalizeUser(updated) }, 200);
7486
+ if (body.roleIds !== void 0) {
7487
+ const roleIds = body.roleIds;
7488
+ if (roleIds.length > 0) {
7489
+ const validRoles = await database.select({ id: rolesInIam.id, code: rolesInIam.code }).from(rolesInIam).where(
7490
+ and55(
7491
+ eq60(rolesInIam.tenantId, tenantId),
7492
+ inArray7(rolesInIam.id, roleIds)
7493
+ )
7494
+ );
7495
+ const assignableIds = new Set(
7496
+ validRoles.filter((r) => (r.code ?? "").toLowerCase() !== "owner").map((r) => r.id)
7497
+ );
7498
+ const toInsert = roleIds.filter((rid) => assignableIds.has(rid));
7499
+ await database.delete(userRolesInIam).where(
7500
+ and55(
7501
+ eq60(userRolesInIam.tenantId, tenantId),
7502
+ eq60(userRolesInIam.userId, id)
7503
+ )
7504
+ );
7505
+ if (toInsert.length > 0) {
7506
+ await database.insert(userRolesInIam).values(
7507
+ toInsert.map((roleId) => ({
7508
+ tenantId,
7509
+ userId: id,
7510
+ roleId
7511
+ }))
7512
+ );
7513
+ }
7514
+ } else {
7515
+ await database.delete(userRolesInIam).where(
7516
+ and55(
7517
+ eq60(userRolesInIam.tenantId, tenantId),
7518
+ eq60(userRolesInIam.userId, id)
7519
+ )
7520
+ );
7521
+ }
7522
+ }
7523
+ const userWithRoles = await fetchUserWithRoles({
7524
+ database,
7525
+ userId: id,
7526
+ tenantId
7527
+ });
7528
+ return c.json(
7529
+ {
7530
+ user: normalizeUser(userWithRoles ?? updated)
7531
+ },
7532
+ 200
7533
+ );
7396
7534
  };
7397
7535
 
7398
7536
  // src/routes/users/users.schema.ts
7399
7537
  import { z as z11 } from "zod";
7400
7538
  var sortableFields = [
7401
- "createdAt",
7402
- "updatedAt",
7403
7539
  "fullName",
7404
- "handle",
7405
- "lastSignInAt"
7406
- ];
7407
- var filterValues = [
7408
- "",
7409
- "emailVerified",
7410
- "phoneVerified",
7411
- "notVerified"
7540
+ "email",
7541
+ "phone",
7542
+ "userType",
7543
+ "roleCount",
7544
+ "lastSignInAt",
7545
+ "activeSessionCount",
7546
+ "createdAt"
7412
7547
  ];
7548
+ var filterValues = ["", "verified", "unverified"];
7549
+ var userTypeValues = ["all", "employee", "candidate", "admin"];
7413
7550
  var listUsersQuerySchema = z11.object({
7414
7551
  page: z11.coerce.number().min(1).default(1).optional(),
7415
7552
  limit: z11.coerce.number().min(1).max(100).default(20).optional(),
7416
7553
  tenantId: z11.string().optional(),
7554
+ search: z11.string().optional(),
7417
7555
  email: z11.string().optional(),
7418
7556
  phone: z11.string().optional(),
7419
7557
  handle: z11.string().optional(),
7420
7558
  filter: z11.enum(filterValues).optional(),
7559
+ userType: z11.enum(userTypeValues).optional(),
7421
7560
  sort: z11.enum(sortableFields).optional(),
7422
7561
  order: z11.enum(["asc", "desc"]).optional()
7423
7562
  });
@@ -7441,7 +7580,8 @@ var updateUserSchema = z11.object({
7441
7580
  handle: z11.string().optional(),
7442
7581
  image: z11.string().url().nullable().optional(),
7443
7582
  emailVerified: z11.boolean().optional(),
7444
- phoneVerified: z11.boolean().optional()
7583
+ phoneVerified: z11.boolean().optional(),
7584
+ roleIds: z11.array(z11.string().uuid()).optional()
7445
7585
  });
7446
7586
  var banUserSchema = z11.object({
7447
7587
  bannedUntil: z11.string().datetime().nullable().optional()