@fonderie/auth 7.8.1 → 7.9.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/brain/outcomes.md +1 -1
- package/brain/signatures.md +2 -0
- package/dist/index.cjs +48 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +13 -1
- package/dist/index.d.ts +13 -1
- package/dist/index.js +48 -6
- package/dist/index.js.map +1 -1
- package/dist/middlewares/index.cjs +24 -0
- package/dist/middlewares/index.cjs.map +1 -1
- package/dist/middlewares/index.js +24 -0
- package/dist/middlewares/index.js.map +1 -1
- package/dist/migrations/sql/017_users_created_idx.sql +7 -0
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -236,12 +236,24 @@ declare function startUserRetention(store: IStoreAdapter, options: IUserRetentio
|
|
|
236
236
|
|
|
237
237
|
declare function validateAuthConfig(config: IAuthConfig): void;
|
|
238
238
|
|
|
239
|
+
interface IUserPage {
|
|
240
|
+
users: Array<IUser & {
|
|
241
|
+
createdAtRaw?: string;
|
|
242
|
+
}>;
|
|
243
|
+
hasMore: boolean;
|
|
244
|
+
}
|
|
245
|
+
|
|
239
246
|
interface IAdminUserDTO extends IUserDTO {
|
|
240
247
|
suspended: boolean;
|
|
241
248
|
deletedAt: string | null;
|
|
242
249
|
createdAt: string;
|
|
243
250
|
}
|
|
244
251
|
declare function toAdminUserDTO(user: IUser): IAdminUserDTO;
|
|
252
|
+
interface IAdminUserPageDTO {
|
|
253
|
+
users: IAdminUserDTO[];
|
|
254
|
+
nextCursor: string | null;
|
|
255
|
+
}
|
|
256
|
+
declare function toAdminUserPageDTO(page: IUserPage): IAdminUserPageDTO;
|
|
245
257
|
declare function describeAuthAdminRoutes(store: IStoreAdapter): IAdminRoute[];
|
|
246
258
|
|
|
247
|
-
export { AuthModule, type ChangePasswordInput, DEFAULT_TEMPLATES, type IAdminUserDTO, IAuthConfig, type IImportUser, type ILoginEventDTO, type ILoginHistoryPageDTO, type IPurgeOptions, type ISessionDTO, IUser, IUserDTO, type IUserRetentionScheduleOptions, type LoginInput, type RegisterInput, type ResetPasswordInput, describeAuthAdminRoutes, importUser, normalizeEmail, normalizeEmailSafe, purgeSoftDeletedUsers, schemas, startUserRetention, toAdminUserDTO, validateAuthConfig };
|
|
259
|
+
export { AuthModule, type ChangePasswordInput, DEFAULT_TEMPLATES, type IAdminUserDTO, IAuthConfig, type IImportUser, type ILoginEventDTO, type ILoginHistoryPageDTO, type IPurgeOptions, type ISessionDTO, IUser, IUserDTO, type IUserRetentionScheduleOptions, type LoginInput, type RegisterInput, type ResetPasswordInput, describeAuthAdminRoutes, importUser, normalizeEmail, normalizeEmailSafe, purgeSoftDeletedUsers, schemas, startUserRetention, toAdminUserDTO, toAdminUserPageDTO, validateAuthConfig };
|
package/dist/index.d.ts
CHANGED
|
@@ -236,12 +236,24 @@ declare function startUserRetention(store: IStoreAdapter, options: IUserRetentio
|
|
|
236
236
|
|
|
237
237
|
declare function validateAuthConfig(config: IAuthConfig): void;
|
|
238
238
|
|
|
239
|
+
interface IUserPage {
|
|
240
|
+
users: Array<IUser & {
|
|
241
|
+
createdAtRaw?: string;
|
|
242
|
+
}>;
|
|
243
|
+
hasMore: boolean;
|
|
244
|
+
}
|
|
245
|
+
|
|
239
246
|
interface IAdminUserDTO extends IUserDTO {
|
|
240
247
|
suspended: boolean;
|
|
241
248
|
deletedAt: string | null;
|
|
242
249
|
createdAt: string;
|
|
243
250
|
}
|
|
244
251
|
declare function toAdminUserDTO(user: IUser): IAdminUserDTO;
|
|
252
|
+
interface IAdminUserPageDTO {
|
|
253
|
+
users: IAdminUserDTO[];
|
|
254
|
+
nextCursor: string | null;
|
|
255
|
+
}
|
|
256
|
+
declare function toAdminUserPageDTO(page: IUserPage): IAdminUserPageDTO;
|
|
245
257
|
declare function describeAuthAdminRoutes(store: IStoreAdapter): IAdminRoute[];
|
|
246
258
|
|
|
247
|
-
export { AuthModule, type ChangePasswordInput, DEFAULT_TEMPLATES, type IAdminUserDTO, IAuthConfig, type IImportUser, type ILoginEventDTO, type ILoginHistoryPageDTO, type IPurgeOptions, type ISessionDTO, IUser, IUserDTO, type IUserRetentionScheduleOptions, type LoginInput, type RegisterInput, type ResetPasswordInput, describeAuthAdminRoutes, importUser, normalizeEmail, normalizeEmailSafe, purgeSoftDeletedUsers, schemas, startUserRetention, toAdminUserDTO, validateAuthConfig };
|
|
259
|
+
export { AuthModule, type ChangePasswordInput, DEFAULT_TEMPLATES, type IAdminUserDTO, IAuthConfig, type IImportUser, type ILoginEventDTO, type ILoginHistoryPageDTO, type IPurgeOptions, type ISessionDTO, IUser, IUserDTO, type IUserRetentionScheduleOptions, type LoginInput, type RegisterInput, type ResetPasswordInput, describeAuthAdminRoutes, importUser, normalizeEmail, normalizeEmailSafe, purgeSoftDeletedUsers, schemas, startUserRetention, toAdminUserDTO, toAdminUserPageDTO, validateAuthConfig };
|
package/dist/index.js
CHANGED
|
@@ -541,6 +541,29 @@ var UserModel = class {
|
|
|
541
541
|
this.store = store;
|
|
542
542
|
}
|
|
543
543
|
store;
|
|
544
|
+
// One page of users, newest first. Keyset-paginated on (created_at, id) —
|
|
545
|
+
// the same cursor contract as login history and the audit log. The +1
|
|
546
|
+
// over-fetch that detects a next page lives here so no outer clamp can
|
|
547
|
+
// shave it off. Soft-deleted rows are excluded, as in every other finder.
|
|
548
|
+
async list(query = {}) {
|
|
549
|
+
const limit = Math.min(query.limit ?? 50, MAX_LIST_LIMIT);
|
|
550
|
+
const params = [];
|
|
551
|
+
const where = ["deleted_at IS NULL"];
|
|
552
|
+
if (query.cursor) {
|
|
553
|
+
params.push(query.cursor.createdAt, query.cursor.id);
|
|
554
|
+
where.push(`(created_at, id) < ($${params.length - 1}::timestamptz, $${params.length}::uuid)`);
|
|
555
|
+
}
|
|
556
|
+
params.push(limit + 1);
|
|
557
|
+
const rows = await this.store.query(
|
|
558
|
+
`SELECT ${USER_COLUMNS}, created_at::text AS "createdAtRaw"
|
|
559
|
+
FROM fonderie_users
|
|
560
|
+
WHERE ${where.join(" AND ")}
|
|
561
|
+
ORDER BY created_at DESC, id DESC
|
|
562
|
+
LIMIT $${params.length}`,
|
|
563
|
+
params
|
|
564
|
+
);
|
|
565
|
+
return { users: rows.slice(0, limit), hasMore: rows.length > limit };
|
|
566
|
+
}
|
|
544
567
|
async findById(id) {
|
|
545
568
|
const [row] = await this.store.query(
|
|
546
569
|
`SELECT ${USER_COLUMNS} FROM fonderie_users WHERE id = $1 AND deleted_at IS NULL`,
|
|
@@ -813,6 +836,7 @@ var UserModel = class {
|
|
|
813
836
|
return row ?? null;
|
|
814
837
|
}
|
|
815
838
|
};
|
|
839
|
+
var MAX_LIST_LIMIT = 200;
|
|
816
840
|
|
|
817
841
|
// src/models/session.model.ts
|
|
818
842
|
var SessionModel = class {
|
|
@@ -3192,7 +3216,7 @@ function extractToken(request) {
|
|
|
3192
3216
|
}
|
|
3193
3217
|
|
|
3194
3218
|
// src/admin.ts
|
|
3195
|
-
import { HTTP as HTTP7, setApiResponse as setApiResponse7 } from "@fonderie/core";
|
|
3219
|
+
import { HTTP as HTTP7, encodeKeysetCursor as encodeKeysetCursor2, setApiResponse as setApiResponse7 } from "@fonderie/core";
|
|
3196
3220
|
function toAdminUserDTO(user) {
|
|
3197
3221
|
return {
|
|
3198
3222
|
...toUserDTO(user),
|
|
@@ -3201,6 +3225,11 @@ function toAdminUserDTO(user) {
|
|
|
3201
3225
|
createdAt: user.createdAt.toISOString()
|
|
3202
3226
|
};
|
|
3203
3227
|
}
|
|
3228
|
+
function toAdminUserPageDTO(page) {
|
|
3229
|
+
const last = page.users[page.users.length - 1];
|
|
3230
|
+
const nextCursor = page.hasMore && last ? encodeKeysetCursor2(last.createdAtRaw ?? last.createdAt.toISOString(), last.id) : null;
|
|
3231
|
+
return { users: page.users.map(toAdminUserDTO), nextCursor };
|
|
3232
|
+
}
|
|
3204
3233
|
var ADMIN_PREFIX = "/_admin";
|
|
3205
3234
|
var idOf = (ctx) => ctx.meta.params?.["id"] ?? "";
|
|
3206
3235
|
var NOT_FOUND = () => setApiResponse7(HTTP7.NOT_FOUND, "NOT_FOUND", "No such user");
|
|
@@ -3223,12 +3252,24 @@ function adminRouteTable(store) {
|
|
|
3223
3252
|
[
|
|
3224
3253
|
"GET",
|
|
3225
3254
|
"/_admin/users",
|
|
3255
|
+
// With an email, the exact lookup support reaches for. Without one, a
|
|
3256
|
+
// page of users — an operator who has to know a name before they can
|
|
3257
|
+
// see anything cannot discover who signed up this morning.
|
|
3226
3258
|
async (ctx) => {
|
|
3227
|
-
const
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3259
|
+
const params = new URL(ctx.request.url).searchParams;
|
|
3260
|
+
const email2 = params.get("email")?.trim().toLowerCase();
|
|
3261
|
+
if (email2) {
|
|
3262
|
+
const user = await users.findByEmail(email2);
|
|
3263
|
+
return user ? setApiResponse7(HTTP7.OK, "USER", "User", toAdminUserDTO(user)) : NOT_FOUND();
|
|
3264
|
+
}
|
|
3265
|
+
const rawLimit = Number(params.get("limit") ?? 50);
|
|
3266
|
+
const limit = Number.isFinite(rawLimit) ? Math.min(Math.max(Math.trunc(rawLimit), 1), 200) : 50;
|
|
3267
|
+
const cursorParam = params.get("cursor");
|
|
3268
|
+
const cursor = cursorParam ? decodeLoginCursor(cursorParam) : null;
|
|
3269
|
+
if (cursorParam && !cursor)
|
|
3270
|
+
return setApiResponse7(HTTP7.UNPROCESSABLE, "INVALID_PARAMETER", "Invalid cursor");
|
|
3271
|
+
const page = await users.list({ limit, ...cursor ? { cursor } : {} });
|
|
3272
|
+
return setApiResponse7(HTTP7.OK, "USERS", "Users", toAdminUserPageDTO(page));
|
|
3232
3273
|
}
|
|
3233
3274
|
],
|
|
3234
3275
|
[
|
|
@@ -3591,6 +3632,7 @@ export {
|
|
|
3591
3632
|
schemas_exports as schemas,
|
|
3592
3633
|
startUserRetention,
|
|
3593
3634
|
toAdminUserDTO,
|
|
3635
|
+
toAdminUserPageDTO,
|
|
3594
3636
|
toUserDTO,
|
|
3595
3637
|
validate,
|
|
3596
3638
|
validateAuthConfig,
|