@mesob/auth-hono 0.4.3 → 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 +195 -55
- package/dist/index.js.map +1 -1
- package/dist/lib/iam-seed.js +12 -7
- package/dist/lib/iam-seed.js.map +1 -1
- package/package.json +2 -2
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({
|
|
@@ -1059,10 +1068,10 @@ var checkAccountHandler = async (c) => {
|
|
|
1059
1068
|
hasPassword: sql4`exists(
|
|
1060
1069
|
select 1
|
|
1061
1070
|
from ${accountsInIam}
|
|
1062
|
-
where ${accountsInIam.tenantId
|
|
1063
|
-
and ${accountsInIam.userId
|
|
1064
|
-
and ${accountsInIam.provider
|
|
1065
|
-
and ${accountsInIam.password} is not null
|
|
1071
|
+
where ${eq5(accountsInIam.tenantId, resolvedTenantId)}
|
|
1072
|
+
and ${eq5(accountsInIam.userId, usersInIam.id)}
|
|
1073
|
+
and ${eq5(accountsInIam.provider, "credentials")}
|
|
1074
|
+
and ${sql4`${accountsInIam.password} is not null`}
|
|
1066
1075
|
)`
|
|
1067
1076
|
}).from(usersInIam).where(whereClause).limit(1);
|
|
1068
1077
|
const verified = result?.verified ?? false;
|
|
@@ -1419,10 +1428,10 @@ var fetchUserForLogin = async ({
|
|
|
1419
1428
|
hasPassword: sql6`exists(
|
|
1420
1429
|
select 1
|
|
1421
1430
|
from ${accountsInIam}
|
|
1422
|
-
where ${accountsInIam.tenantId
|
|
1423
|
-
and ${accountsInIam.userId
|
|
1424
|
-
and ${accountsInIam.provider
|
|
1425
|
-
and ${accountsInIam.password} is not null
|
|
1431
|
+
where ${eq7(accountsInIam.tenantId, tenantId)}
|
|
1432
|
+
and ${eq7(accountsInIam.userId, usersInIam.id)}
|
|
1433
|
+
and ${eq7(accountsInIam.provider, "credentials")}
|
|
1434
|
+
and ${sql6`${accountsInIam.password} is not null`}
|
|
1426
1435
|
)`
|
|
1427
1436
|
}).from(usersInIam).where(whereClause).limit(1);
|
|
1428
1437
|
return row || null;
|
|
@@ -3547,13 +3556,18 @@ function buildPermissionDescription(code) {
|
|
|
3547
3556
|
};
|
|
3548
3557
|
}
|
|
3549
3558
|
function buildPermissionSeedRows(permissions) {
|
|
3550
|
-
|
|
3551
|
-
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
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
|
|
7241
|
-
|
|
7242
|
-
|
|
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
|
-
|
|
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 === "
|
|
7265
|
-
conditions.push(
|
|
7266
|
-
|
|
7267
|
-
|
|
7268
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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
|
-
|
|
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
|
-
"
|
|
7405
|
-
"
|
|
7406
|
-
|
|
7407
|
-
|
|
7408
|
-
"",
|
|
7409
|
-
"
|
|
7410
|
-
"
|
|
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()
|