@byline/db-postgres 0.9.3
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/LICENSE +373 -0
- package/README.md +18 -0
- package/dist/database/schema/auth.d.ts +857 -0
- package/dist/database/schema/auth.d.ts.map +1 -0
- package/dist/database/schema/auth.js +176 -0
- package/dist/database/schema/auth.js.map +1 -0
- package/dist/database/schema/index.d.ts +2955 -0
- package/dist/database/schema/index.d.ts.map +1 -0
- package/dist/database/schema/index.js +500 -0
- package/dist/database/schema/index.js.map +1 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +30 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/test-helper.d.ts +17 -0
- package/dist/lib/test-helper.d.ts.map +1 -0
- package/dist/lib/test-helper.js +47 -0
- package/dist/lib/test-helper.js.map +1 -0
- package/dist/modules/admin/admin-permissions-repository.d.ts +17 -0
- package/dist/modules/admin/admin-permissions-repository.d.ts.map +1 -0
- package/dist/modules/admin/admin-permissions-repository.js +76 -0
- package/dist/modules/admin/admin-permissions-repository.js.map +1 -0
- package/dist/modules/admin/admin-roles-repository.d.ts +12 -0
- package/dist/modules/admin/admin-roles-repository.d.ts.map +1 -0
- package/dist/modules/admin/admin-roles-repository.js +168 -0
- package/dist/modules/admin/admin-roles-repository.js.map +1 -0
- package/dist/modules/admin/admin-store.d.ts +20 -0
- package/dist/modules/admin/admin-store.d.ts.map +1 -0
- package/dist/modules/admin/admin-store.js +28 -0
- package/dist/modules/admin/admin-store.js.map +1 -0
- package/dist/modules/admin/admin-users-repository.d.ts +12 -0
- package/dist/modules/admin/admin-users-repository.d.ts.map +1 -0
- package/dist/modules/admin/admin-users-repository.js +208 -0
- package/dist/modules/admin/admin-users-repository.js.map +1 -0
- package/dist/modules/admin/index.d.ts +27 -0
- package/dist/modules/admin/index.d.ts.map +1 -0
- package/dist/modules/admin/index.js +27 -0
- package/dist/modules/admin/index.js.map +1 -0
- package/dist/modules/admin/refresh-tokens-repository.d.ts +16 -0
- package/dist/modules/admin/refresh-tokens-repository.d.ts.map +1 -0
- package/dist/modules/admin/refresh-tokens-repository.js +132 -0
- package/dist/modules/admin/refresh-tokens-repository.js.map +1 -0
- package/dist/modules/admin/tests/auth-integration.test.d.ts +9 -0
- package/dist/modules/admin/tests/auth-integration.test.d.ts.map +1 -0
- package/dist/modules/admin/tests/auth-integration.test.js +392 -0
- package/dist/modules/admin/tests/auth-integration.test.js.map +1 -0
- package/dist/modules/admin/tests/session-provider.test.d.ts +9 -0
- package/dist/modules/admin/tests/session-provider.test.d.ts.map +1 -0
- package/dist/modules/admin/tests/session-provider.test.js +370 -0
- package/dist/modules/admin/tests/session-provider.test.js.map +1 -0
- package/dist/modules/storage/@types.d.ts +116 -0
- package/dist/modules/storage/@types.d.ts.map +1 -0
- package/dist/modules/storage/@types.js +9 -0
- package/dist/modules/storage/@types.js.map +1 -0
- package/dist/modules/storage/storage-commands.d.ts +136 -0
- package/dist/modules/storage/storage-commands.d.ts.map +1 -0
- package/dist/modules/storage/storage-commands.js +272 -0
- package/dist/modules/storage/storage-commands.js.map +1 -0
- package/dist/modules/storage/storage-flatten.d.ts +19 -0
- package/dist/modules/storage/storage-flatten.d.ts.map +1 -0
- package/dist/modules/storage/storage-flatten.js +261 -0
- package/dist/modules/storage/storage-flatten.js.map +1 -0
- package/dist/modules/storage/storage-insert.d.ts +22 -0
- package/dist/modules/storage/storage-insert.d.ts.map +1 -0
- package/dist/modules/storage/storage-insert.js +115 -0
- package/dist/modules/storage/storage-insert.js.map +1 -0
- package/dist/modules/storage/storage-queries.d.ts +377 -0
- package/dist/modules/storage/storage-queries.d.ts.map +1 -0
- package/dist/modules/storage/storage-queries.js +976 -0
- package/dist/modules/storage/storage-queries.js.map +1 -0
- package/dist/modules/storage/storage-restore.d.ts +19 -0
- package/dist/modules/storage/storage-restore.d.ts.map +1 -0
- package/dist/modules/storage/storage-restore.js +350 -0
- package/dist/modules/storage/storage-restore.js.map +1 -0
- package/dist/modules/storage/storage-store-manifest.d.ts +71 -0
- package/dist/modules/storage/storage-store-manifest.d.ts.map +1 -0
- package/dist/modules/storage/storage-store-manifest.js +294 -0
- package/dist/modules/storage/storage-store-manifest.js.map +1 -0
- package/dist/modules/storage/storage-utils.d.ts +23 -0
- package/dist/modules/storage/storage-utils.d.ts.map +1 -0
- package/dist/modules/storage/storage-utils.js +72 -0
- package/dist/modules/storage/storage-utils.js.map +1 -0
- package/dist/modules/storage/tests/storage-field-types.test.d.ts +9 -0
- package/dist/modules/storage/tests/storage-field-types.test.d.ts.map +1 -0
- package/dist/modules/storage/tests/storage-field-types.test.js +146 -0
- package/dist/modules/storage/tests/storage-field-types.test.js.map +1 -0
- package/dist/modules/storage/tests/storage-flatten-reconstruct.test.d.ts +9 -0
- package/dist/modules/storage/tests/storage-flatten-reconstruct.test.d.ts.map +1 -0
- package/dist/modules/storage/tests/storage-flatten-reconstruct.test.js +327 -0
- package/dist/modules/storage/tests/storage-flatten-reconstruct.test.js.map +1 -0
- package/dist/modules/storage/tests/storage-store-manifest.test.d.ts +9 -0
- package/dist/modules/storage/tests/storage-store-manifest.test.d.ts.map +1 -0
- package/dist/modules/storage/tests/storage-store-manifest.test.js +141 -0
- package/dist/modules/storage/tests/storage-store-manifest.test.js.map +1 -0
- package/dist/modules/storage/tests/storage-versioning.test.d.ts +9 -0
- package/dist/modules/storage/tests/storage-versioning.test.d.ts.map +1 -0
- package/dist/modules/storage/tests/storage-versioning.test.js +336 -0
- package/dist/modules/storage/tests/storage-versioning.test.js.map +1 -0
- package/package.json +81 -0
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { drizzle } from 'drizzle-orm/node-postgres';
|
|
2
|
+
import pg from 'pg';
|
|
3
|
+
import * as schema from '../database/schema/index.js';
|
|
4
|
+
import { createCommandBuilders } from '../modules/storage/storage-commands.js';
|
|
5
|
+
import { createQueryBuilders } from '../modules/storage/storage-queries.js';
|
|
6
|
+
let pool;
|
|
7
|
+
let db;
|
|
8
|
+
let commandBuilders;
|
|
9
|
+
let queryBuilders;
|
|
10
|
+
export function setupTestDB(collections = []) {
|
|
11
|
+
if (!pool) {
|
|
12
|
+
pool = new pg.Pool({
|
|
13
|
+
connectionString: process.env.POSTGRES_CONNECTION_STRING,
|
|
14
|
+
// Integration tests share the dev database with the running webapp,
|
|
15
|
+
// and node:test may run multiple test files in separate processes.
|
|
16
|
+
// A pool-per-file of 20 connections × N files + the webapp's own
|
|
17
|
+
// pool of 20 blows past Postgres's default `max_connections=100`
|
|
18
|
+
// and throws `FATAL: sorry, too many clients already`. The tests
|
|
19
|
+
// are serial and run one query at a time, so a small pool is
|
|
20
|
+
// sufficient — keep total test connections low regardless of
|
|
21
|
+
// process / file parallelism.
|
|
22
|
+
max: 4,
|
|
23
|
+
idleTimeoutMillis: 2000,
|
|
24
|
+
connectionTimeoutMillis: 1000,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
if (!db) {
|
|
28
|
+
db = drizzle(pool, { schema });
|
|
29
|
+
}
|
|
30
|
+
if (!commandBuilders) {
|
|
31
|
+
commandBuilders = createCommandBuilders(db);
|
|
32
|
+
}
|
|
33
|
+
// Recreate queryBuilders when collections are provided so that
|
|
34
|
+
// DocumentQueries can resolve collection definitions by path.
|
|
35
|
+
queryBuilders = createQueryBuilders(db, collections);
|
|
36
|
+
return { pool, db, commandBuilders, queryBuilders };
|
|
37
|
+
}
|
|
38
|
+
export async function teardownTestDB() {
|
|
39
|
+
if (pool) {
|
|
40
|
+
await pool.end();
|
|
41
|
+
pool = undefined;
|
|
42
|
+
db = undefined;
|
|
43
|
+
commandBuilders = undefined;
|
|
44
|
+
queryBuilders = undefined;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=test-helper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-helper.js","sourceRoot":"","sources":["../../src/lib/test-helper.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAuB,MAAM,2BAA2B,CAAA;AACxE,OAAO,EAAE,MAAM,IAAI,CAAA;AAEnB,OAAO,KAAK,MAAM,MAAM,6BAA6B,CAAA;AACrD,OAAO,EAAE,qBAAqB,EAAE,MAAM,wCAAwC,CAAA;AAC9E,OAAO,EAAE,mBAAmB,EAAE,MAAM,uCAAuC,CAAA;AAE3E,IAAI,IAAa,CAAA;AACjB,IAAI,EAAiC,CAAA;AACrC,IAAI,eAAyD,CAAA;AAC7D,IAAI,aAAqD,CAAA;AAEzD,MAAM,UAAU,WAAW,CAAC,cAAsC,EAAE;IAClE,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,IAAI,GAAG,IAAI,EAAE,CAAC,IAAI,CAAC;YACjB,gBAAgB,EAAE,OAAO,CAAC,GAAG,CAAC,0BAA0B;YACxD,oEAAoE;YACpE,mEAAmE;YACnE,iEAAiE;YACjE,iEAAiE;YACjE,iEAAiE;YACjE,6DAA6D;YAC7D,6DAA6D;YAC7D,8BAA8B;YAC9B,GAAG,EAAE,CAAC;YACN,iBAAiB,EAAE,IAAI;YACvB,uBAAuB,EAAE,IAAI;SAC9B,CAAC,CAAA;IACJ,CAAC;IAED,IAAI,CAAC,EAAE,EAAE,CAAC;QACR,EAAE,GAAG,OAAO,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,CAAC,CAAA;IAChC,CAAC;IAED,IAAI,CAAC,eAAe,EAAE,CAAC;QACrB,eAAe,GAAG,qBAAqB,CAAC,EAAE,CAAC,CAAA;IAC7C,CAAC;IAED,+DAA+D;IAC/D,8DAA8D;IAC9D,aAAa,GAAG,mBAAmB,CAAC,EAAE,EAAE,WAAW,CAAC,CAAA;IAEpD,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,eAAe,EAAE,aAAa,EAAE,CAAA;AACrD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc;IAClC,IAAI,IAAI,EAAE,CAAC;QACT,MAAM,IAAI,CAAC,GAAG,EAAE,CAAA;QAChB,IAAI,GAAG,SAAgB,CAAA;QACvB,EAAE,GAAG,SAAgB,CAAA;QACrB,eAAe,GAAG,SAAgB,CAAA;QAClC,aAAa,GAAG,SAAgB,CAAA;IAClC,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This Source Code is subject to the terms of the Mozilla Public
|
|
3
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
5
|
+
*
|
|
6
|
+
* Copyright (c) Infonomic Company Limited
|
|
7
|
+
*/
|
|
8
|
+
import type { AdminPermissionsRepository } from '@byline/admin/admin-permissions';
|
|
9
|
+
import type { NodePgDatabase } from 'drizzle-orm/node-postgres';
|
|
10
|
+
import type * as schema from '../../database/schema/index.js';
|
|
11
|
+
/**
|
|
12
|
+
* Postgres implementation of `AdminPermissionsRepository` — per-role
|
|
13
|
+
* ability grants and the distinct-abilities-for-user join that drives
|
|
14
|
+
* `resolveActor()`.
|
|
15
|
+
*/
|
|
16
|
+
export declare function createAdminPermissionsRepository(db: NodePgDatabase<typeof schema>): AdminPermissionsRepository;
|
|
17
|
+
//# sourceMappingURL=admin-permissions-repository.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"admin-permissions-repository.d.ts","sourceRoot":"","sources":["../../../src/modules/admin/admin-permissions-repository.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,iCAAiC,CAAA;AAEjF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAA;AAI/D,OAAO,KAAK,KAAK,MAAM,MAAM,gCAAgC,CAAA;AAE7D;;;;GAIG;AACH,wBAAgB,gCAAgC,CAC9C,EAAE,EAAE,cAAc,CAAC,OAAO,MAAM,CAAC,GAChC,0BAA0B,CAwE5B"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This Source Code is subject to the terms of the Mozilla Public
|
|
3
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
5
|
+
*
|
|
6
|
+
* Copyright (c) Infonomic Company Limited
|
|
7
|
+
*/
|
|
8
|
+
import { and, eq } from 'drizzle-orm';
|
|
9
|
+
import { v7 as uuidv7 } from 'uuid';
|
|
10
|
+
import { adminPermissions, adminRoleAdminUser } from '../../database/schema/auth.js';
|
|
11
|
+
/**
|
|
12
|
+
* Postgres implementation of `AdminPermissionsRepository` — per-role
|
|
13
|
+
* ability grants and the distinct-abilities-for-user join that drives
|
|
14
|
+
* `resolveActor()`.
|
|
15
|
+
*/
|
|
16
|
+
export function createAdminPermissionsRepository(db) {
|
|
17
|
+
return {
|
|
18
|
+
async grantAbility(roleId, ability) {
|
|
19
|
+
await db
|
|
20
|
+
.insert(adminPermissions)
|
|
21
|
+
.values({ id: uuidv7(), admin_role_id: roleId, ability })
|
|
22
|
+
.onConflictDoNothing({
|
|
23
|
+
target: [adminPermissions.admin_role_id, adminPermissions.ability],
|
|
24
|
+
});
|
|
25
|
+
},
|
|
26
|
+
async revokeAbility(roleId, ability) {
|
|
27
|
+
await db
|
|
28
|
+
.delete(adminPermissions)
|
|
29
|
+
.where(and(eq(adminPermissions.admin_role_id, roleId), eq(adminPermissions.ability, ability)));
|
|
30
|
+
},
|
|
31
|
+
async listAbilities(roleId) {
|
|
32
|
+
const rows = await db
|
|
33
|
+
.select({ ability: adminPermissions.ability })
|
|
34
|
+
.from(adminPermissions)
|
|
35
|
+
.where(eq(adminPermissions.admin_role_id, roleId));
|
|
36
|
+
return rows.map((r) => r.ability);
|
|
37
|
+
},
|
|
38
|
+
async setAbilities(roleId, abilities) {
|
|
39
|
+
await db.transaction(async (tx) => {
|
|
40
|
+
await tx.delete(adminPermissions).where(eq(adminPermissions.admin_role_id, roleId));
|
|
41
|
+
if (abilities.length === 0)
|
|
42
|
+
return;
|
|
43
|
+
const rows = abilities.map((ability) => ({
|
|
44
|
+
id: uuidv7(),
|
|
45
|
+
admin_role_id: roleId,
|
|
46
|
+
ability,
|
|
47
|
+
}));
|
|
48
|
+
await tx.insert(adminPermissions).values(rows);
|
|
49
|
+
});
|
|
50
|
+
},
|
|
51
|
+
async listAbilitiesForUser(userId) {
|
|
52
|
+
const rows = await db
|
|
53
|
+
.selectDistinct({ ability: adminPermissions.ability })
|
|
54
|
+
.from(adminPermissions)
|
|
55
|
+
.innerJoin(adminRoleAdminUser, eq(adminRoleAdminUser.admin_role_id, adminPermissions.admin_role_id))
|
|
56
|
+
.where(eq(adminRoleAdminUser.admin_user_id, userId));
|
|
57
|
+
return rows.map((r) => r.ability);
|
|
58
|
+
},
|
|
59
|
+
async listRolesForAbility(ability) {
|
|
60
|
+
const rows = await db
|
|
61
|
+
.select({ admin_role_id: adminPermissions.admin_role_id })
|
|
62
|
+
.from(adminPermissions)
|
|
63
|
+
.where(eq(adminPermissions.ability, ability));
|
|
64
|
+
return rows.map((r) => r.admin_role_id);
|
|
65
|
+
},
|
|
66
|
+
async listUsersForAbility(ability) {
|
|
67
|
+
const rows = await db
|
|
68
|
+
.selectDistinct({ admin_user_id: adminRoleAdminUser.admin_user_id })
|
|
69
|
+
.from(adminRoleAdminUser)
|
|
70
|
+
.innerJoin(adminPermissions, eq(adminPermissions.admin_role_id, adminRoleAdminUser.admin_role_id))
|
|
71
|
+
.where(eq(adminPermissions.ability, ability));
|
|
72
|
+
return rows.map((r) => r.admin_user_id);
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=admin-permissions-repository.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"admin-permissions-repository.js","sourceRoot":"","sources":["../../../src/modules/admin/admin-permissions-repository.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This Source Code is subject to the terms of the Mozilla Public
|
|
3
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
5
|
+
*
|
|
6
|
+
* Copyright (c) Infonomic Company Limited
|
|
7
|
+
*/
|
|
8
|
+
import { type AdminRolesRepository } from '@byline/admin/admin-roles';
|
|
9
|
+
import type { NodePgDatabase } from 'drizzle-orm/node-postgres';
|
|
10
|
+
import type * as schema from '../../database/schema/index.js';
|
|
11
|
+
export declare function createAdminRolesRepository(db: NodePgDatabase<typeof schema>): AdminRolesRepository;
|
|
12
|
+
//# sourceMappingURL=admin-roles-repository.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"admin-roles-repository.d.ts","sourceRoot":"","sources":["../../../src/modules/admin/admin-roles-repository.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAEL,KAAK,oBAAoB,EAE1B,MAAM,2BAA2B,CAAA;AAElC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAA;AAI/D,OAAO,KAAK,KAAK,MAAM,MAAM,gCAAgC,CAAA;AAwB7D,wBAAgB,0BAA0B,CACxC,EAAE,EAAE,cAAc,CAAC,OAAO,MAAM,CAAC,GAChC,oBAAoB,CAkJtB"}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This Source Code is subject to the terms of the Mozilla Public
|
|
3
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
5
|
+
*
|
|
6
|
+
* Copyright (c) Infonomic Company Limited
|
|
7
|
+
*/
|
|
8
|
+
import { ERR_ADMIN_ROLE_VERSION_CONFLICT, } from '@byline/admin/admin-roles';
|
|
9
|
+
import { and, asc, eq, sql } from 'drizzle-orm';
|
|
10
|
+
import { v7 as uuidv7 } from 'uuid';
|
|
11
|
+
import { adminRoleAdminUser, adminRoles } from '../../database/schema/auth.js';
|
|
12
|
+
/**
|
|
13
|
+
* Postgres implementation of `AdminRolesRepository` — role CRUD,
|
|
14
|
+
* reorder, and role ↔ user assignments. Ability grants live on
|
|
15
|
+
* `AdminPermissionsRepository` (see `admin-permissions-repository.ts`).
|
|
16
|
+
*
|
|
17
|
+
* Optimistic-concurrency writes (`update`, `delete`) gate on
|
|
18
|
+
* `WHERE id = $id AND vid = $expected` — zero rows means another writer
|
|
19
|
+
* bumped the row first; throw `VERSION_CONFLICT`. Reorder is vid-less
|
|
20
|
+
* (see contract docs) and runs as a single transaction.
|
|
21
|
+
*/
|
|
22
|
+
const PUBLIC_ROLE_COLUMNS = {
|
|
23
|
+
id: adminRoles.id,
|
|
24
|
+
vid: adminRoles.vid,
|
|
25
|
+
name: adminRoles.name,
|
|
26
|
+
machine_name: adminRoles.machine_name,
|
|
27
|
+
description: adminRoles.description,
|
|
28
|
+
order: adminRoles.order,
|
|
29
|
+
created_at: adminRoles.created_at,
|
|
30
|
+
updated_at: adminRoles.updated_at,
|
|
31
|
+
};
|
|
32
|
+
export function createAdminRolesRepository(db) {
|
|
33
|
+
return {
|
|
34
|
+
// -----------------------------------------------------------------
|
|
35
|
+
// Role CRUD
|
|
36
|
+
// -----------------------------------------------------------------
|
|
37
|
+
async create(input) {
|
|
38
|
+
const [row] = await db
|
|
39
|
+
.insert(adminRoles)
|
|
40
|
+
.values({
|
|
41
|
+
id: uuidv7(),
|
|
42
|
+
name: input.name,
|
|
43
|
+
machine_name: input.machine_name,
|
|
44
|
+
description: input.description ?? null,
|
|
45
|
+
order: input.order ?? 0,
|
|
46
|
+
})
|
|
47
|
+
.returning(PUBLIC_ROLE_COLUMNS);
|
|
48
|
+
if (!row)
|
|
49
|
+
throw new Error('createAdminRole: insert returned no row');
|
|
50
|
+
return row;
|
|
51
|
+
},
|
|
52
|
+
async getById(id) {
|
|
53
|
+
const [row] = await db
|
|
54
|
+
.select(PUBLIC_ROLE_COLUMNS)
|
|
55
|
+
.from(adminRoles)
|
|
56
|
+
.where(eq(adminRoles.id, id));
|
|
57
|
+
return row ?? null;
|
|
58
|
+
},
|
|
59
|
+
async getByMachineName(machineName) {
|
|
60
|
+
const [row] = await db
|
|
61
|
+
.select(PUBLIC_ROLE_COLUMNS)
|
|
62
|
+
.from(adminRoles)
|
|
63
|
+
.where(eq(adminRoles.machine_name, machineName));
|
|
64
|
+
return row ?? null;
|
|
65
|
+
},
|
|
66
|
+
async list() {
|
|
67
|
+
return db
|
|
68
|
+
.select(PUBLIC_ROLE_COLUMNS)
|
|
69
|
+
.from(adminRoles)
|
|
70
|
+
.orderBy(asc(adminRoles.order), asc(adminRoles.created_at));
|
|
71
|
+
},
|
|
72
|
+
async update(id, expectedVid, patch) {
|
|
73
|
+
const updateSet = {
|
|
74
|
+
updated_at: new Date(),
|
|
75
|
+
vid: sql `${adminRoles.vid} + 1`,
|
|
76
|
+
};
|
|
77
|
+
if (patch.name !== undefined)
|
|
78
|
+
updateSet.name = patch.name;
|
|
79
|
+
if (patch.description !== undefined)
|
|
80
|
+
updateSet.description = patch.description;
|
|
81
|
+
if (patch.order !== undefined)
|
|
82
|
+
updateSet.order = patch.order;
|
|
83
|
+
const [row] = await db
|
|
84
|
+
.update(adminRoles)
|
|
85
|
+
.set(updateSet)
|
|
86
|
+
.where(and(eq(adminRoles.id, id), eq(adminRoles.vid, expectedVid)))
|
|
87
|
+
.returning(PUBLIC_ROLE_COLUMNS);
|
|
88
|
+
if (!row)
|
|
89
|
+
throw ERR_ADMIN_ROLE_VERSION_CONFLICT();
|
|
90
|
+
return row;
|
|
91
|
+
},
|
|
92
|
+
async delete(id, expectedVid) {
|
|
93
|
+
// Cascades remove role ↔ user assignments and per-role permissions.
|
|
94
|
+
const result = await db
|
|
95
|
+
.delete(adminRoles)
|
|
96
|
+
.where(and(eq(adminRoles.id, id), eq(adminRoles.vid, expectedVid)))
|
|
97
|
+
.returning({ id: adminRoles.id });
|
|
98
|
+
if (result.length === 0)
|
|
99
|
+
throw ERR_ADMIN_ROLE_VERSION_CONFLICT();
|
|
100
|
+
},
|
|
101
|
+
async reorder(ids) {
|
|
102
|
+
if (ids.length === 0)
|
|
103
|
+
return;
|
|
104
|
+
// Single transaction so the list is never observed half-reordered.
|
|
105
|
+
// No vid gate: see `AdminRolesRepository.reorder` contract docs.
|
|
106
|
+
await db.transaction(async (tx) => {
|
|
107
|
+
const now = new Date();
|
|
108
|
+
for (let i = 0; i < ids.length; i++) {
|
|
109
|
+
await tx
|
|
110
|
+
.update(adminRoles)
|
|
111
|
+
.set({
|
|
112
|
+
order: i,
|
|
113
|
+
updated_at: now,
|
|
114
|
+
vid: sql `${adminRoles.vid} + 1`,
|
|
115
|
+
})
|
|
116
|
+
.where(eq(adminRoles.id, ids[i]));
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
},
|
|
120
|
+
// -----------------------------------------------------------------
|
|
121
|
+
// Role ↔ user assignments
|
|
122
|
+
// -----------------------------------------------------------------
|
|
123
|
+
async assignToUser(roleId, userId) {
|
|
124
|
+
await db
|
|
125
|
+
.insert(adminRoleAdminUser)
|
|
126
|
+
.values({ admin_role_id: roleId, admin_user_id: userId })
|
|
127
|
+
.onConflictDoNothing({
|
|
128
|
+
target: [adminRoleAdminUser.admin_role_id, adminRoleAdminUser.admin_user_id],
|
|
129
|
+
});
|
|
130
|
+
},
|
|
131
|
+
async unassignFromUser(roleId, userId) {
|
|
132
|
+
await db
|
|
133
|
+
.delete(adminRoleAdminUser)
|
|
134
|
+
.where(and(eq(adminRoleAdminUser.admin_role_id, roleId), eq(adminRoleAdminUser.admin_user_id, userId)));
|
|
135
|
+
},
|
|
136
|
+
async listRolesForUser(userId) {
|
|
137
|
+
const rows = await db
|
|
138
|
+
.select(PUBLIC_ROLE_COLUMNS)
|
|
139
|
+
.from(adminRoles)
|
|
140
|
+
.innerJoin(adminRoleAdminUser, eq(adminRoleAdminUser.admin_role_id, adminRoles.id))
|
|
141
|
+
.where(eq(adminRoleAdminUser.admin_user_id, userId))
|
|
142
|
+
.orderBy(asc(adminRoles.order));
|
|
143
|
+
return rows;
|
|
144
|
+
},
|
|
145
|
+
async listUsersForRole(roleId) {
|
|
146
|
+
const rows = await db
|
|
147
|
+
.select({ admin_user_id: adminRoleAdminUser.admin_user_id })
|
|
148
|
+
.from(adminRoleAdminUser)
|
|
149
|
+
.where(eq(adminRoleAdminUser.admin_role_id, roleId));
|
|
150
|
+
return rows.map((r) => r.admin_user_id);
|
|
151
|
+
},
|
|
152
|
+
async setRolesForUser(userId, roleIds) {
|
|
153
|
+
// Single transaction — user assignments are never observed
|
|
154
|
+
// half-edited. Symmetric to AdminPermissionsRepository.setAbilities.
|
|
155
|
+
await db.transaction(async (tx) => {
|
|
156
|
+
await tx.delete(adminRoleAdminUser).where(eq(adminRoleAdminUser.admin_user_id, userId));
|
|
157
|
+
if (roleIds.length === 0)
|
|
158
|
+
return;
|
|
159
|
+
const rows = roleIds.map((admin_role_id) => ({
|
|
160
|
+
admin_role_id,
|
|
161
|
+
admin_user_id: userId,
|
|
162
|
+
}));
|
|
163
|
+
await tx.insert(adminRoleAdminUser).values(rows);
|
|
164
|
+
});
|
|
165
|
+
},
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
//# sourceMappingURL=admin-roles-repository.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"admin-roles-repository.js","sourceRoot":"","sources":["../../../src/modules/admin/admin-roles-repository.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This Source Code is subject to the terms of the Mozilla Public
|
|
3
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
5
|
+
*
|
|
6
|
+
* Copyright (c) Infonomic Company Limited
|
|
7
|
+
*/
|
|
8
|
+
import type { AdminStore } from '@byline/admin';
|
|
9
|
+
import type { NodePgDatabase } from 'drizzle-orm/node-postgres';
|
|
10
|
+
import type * as schema from '../../database/schema/index.js';
|
|
11
|
+
/**
|
|
12
|
+
* Wire the four admin repositories against a Drizzle handle and return the
|
|
13
|
+
* `AdminStore` bundle expected by `@byline/admin` — specifically by the
|
|
14
|
+
* built-in `JwtSessionProvider`, by `seedSuperAdmin`, and (later) by the
|
|
15
|
+
* admin-user / admin-role commands.
|
|
16
|
+
*
|
|
17
|
+
* Construct once per process, alongside the `pgAdapter` call.
|
|
18
|
+
*/
|
|
19
|
+
export declare function createAdminStore(db: NodePgDatabase<typeof schema>): AdminStore;
|
|
20
|
+
//# sourceMappingURL=admin-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"admin-store.d.ts","sourceRoot":"","sources":["../../../src/modules/admin/admin-store.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAC/C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAA;AAM/D,OAAO,KAAK,KAAK,MAAM,MAAM,gCAAgC,CAAA;AAE7D;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,cAAc,CAAC,OAAO,MAAM,CAAC,GAAG,UAAU,CAO9E"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This Source Code is subject to the terms of the Mozilla Public
|
|
3
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
5
|
+
*
|
|
6
|
+
* Copyright (c) Infonomic Company Limited
|
|
7
|
+
*/
|
|
8
|
+
import { createAdminPermissionsRepository } from './admin-permissions-repository.js';
|
|
9
|
+
import { createAdminRolesRepository } from './admin-roles-repository.js';
|
|
10
|
+
import { createAdminUsersRepository } from './admin-users-repository.js';
|
|
11
|
+
import { createRefreshTokensRepository } from './refresh-tokens-repository.js';
|
|
12
|
+
/**
|
|
13
|
+
* Wire the four admin repositories against a Drizzle handle and return the
|
|
14
|
+
* `AdminStore` bundle expected by `@byline/admin` — specifically by the
|
|
15
|
+
* built-in `JwtSessionProvider`, by `seedSuperAdmin`, and (later) by the
|
|
16
|
+
* admin-user / admin-role commands.
|
|
17
|
+
*
|
|
18
|
+
* Construct once per process, alongside the `pgAdapter` call.
|
|
19
|
+
*/
|
|
20
|
+
export function createAdminStore(db) {
|
|
21
|
+
return {
|
|
22
|
+
adminUsers: createAdminUsersRepository(db),
|
|
23
|
+
adminRoles: createAdminRolesRepository(db),
|
|
24
|
+
adminPermissions: createAdminPermissionsRepository(db),
|
|
25
|
+
refreshTokens: createRefreshTokensRepository(db),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=admin-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"admin-store.js","sourceRoot":"","sources":["../../../src/modules/admin/admin-store.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,OAAO,EAAE,gCAAgC,EAAE,MAAM,mCAAmC,CAAA;AACpF,OAAO,EAAE,0BAA0B,EAAE,MAAM,6BAA6B,CAAA;AACxE,OAAO,EAAE,0BAA0B,EAAE,MAAM,6BAA6B,CAAA;AACxE,OAAO,EAAE,6BAA6B,EAAE,MAAM,gCAAgC,CAAA;AAG9E;;;;;;;GAOG;AACH,MAAM,UAAU,gBAAgB,CAAC,EAAiC;IAChE,OAAO;QACL,UAAU,EAAE,0BAA0B,CAAC,EAAE,CAAC;QAC1C,UAAU,EAAE,0BAA0B,CAAC,EAAE,CAAC;QAC1C,gBAAgB,EAAE,gCAAgC,CAAC,EAAE,CAAC;QACtD,aAAa,EAAE,6BAA6B,CAAC,EAAE,CAAC;KACjD,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This Source Code is subject to the terms of the Mozilla Public
|
|
3
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
5
|
+
*
|
|
6
|
+
* Copyright (c) Infonomic Company Limited
|
|
7
|
+
*/
|
|
8
|
+
import { type AdminUsersRepository } from '@byline/admin/admin-users';
|
|
9
|
+
import type { NodePgDatabase } from 'drizzle-orm/node-postgres';
|
|
10
|
+
import type * as schema from '../../database/schema/index.js';
|
|
11
|
+
export declare function createAdminUsersRepository(db: NodePgDatabase<typeof schema>): AdminUsersRepository;
|
|
12
|
+
//# sourceMappingURL=admin-users-repository.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"admin-users-repository.d.ts","sourceRoot":"","sources":["../../../src/modules/admin/admin-users-repository.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAEL,KAAK,oBAAoB,EAE1B,MAAM,2BAA2B,CAAA;AAElC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAA;AAI/D,OAAO,KAAK,KAAK,MAAM,MAAM,gCAAgC,CAAA;AA+C7D,wBAAgB,0BAA0B,CACxC,EAAE,EAAE,cAAc,CAAC,OAAO,MAAM,CAAC,GAChC,oBAAoB,CA2KtB"}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This Source Code is subject to the terms of the Mozilla Public
|
|
3
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
5
|
+
*
|
|
6
|
+
* Copyright (c) Infonomic Company Limited
|
|
7
|
+
*/
|
|
8
|
+
import { ERR_ADMIN_USER_VERSION_CONFLICT, } from '@byline/admin/admin-users';
|
|
9
|
+
import { and, asc, desc, eq, ilike, or, sql } from 'drizzle-orm';
|
|
10
|
+
import { v7 as uuidv7 } from 'uuid';
|
|
11
|
+
import { adminUsers } from '../../database/schema/auth.js';
|
|
12
|
+
/**
|
|
13
|
+
* Postgres implementation of `AdminUsersRepository`.
|
|
14
|
+
*
|
|
15
|
+
* The DB column for the password hash is `password`; the public interface
|
|
16
|
+
* exposes it as `password_hash`. The mapping happens entirely inside this
|
|
17
|
+
* factory — callers speak the interface shape and never see the column
|
|
18
|
+
* name.
|
|
19
|
+
*
|
|
20
|
+
* Password hashing is *not* done here — the interface takes a pre-hashed
|
|
21
|
+
* PHC string. Callers (seed, admin-user commands) hash first via
|
|
22
|
+
* `hashPassword` from `@byline/admin/auth`.
|
|
23
|
+
*
|
|
24
|
+
* Optimistic-concurrency writes (`update`, `setPasswordHash`, `delete`)
|
|
25
|
+
* guard the `UPDATE ... WHERE id = $id AND vid = $expected` pattern —
|
|
26
|
+
* zero rows returned means another writer bumped the row first; throw
|
|
27
|
+
* `VERSION_CONFLICT`.
|
|
28
|
+
*/
|
|
29
|
+
const PUBLIC_COLUMNS = {
|
|
30
|
+
id: adminUsers.id,
|
|
31
|
+
vid: adminUsers.vid,
|
|
32
|
+
given_name: adminUsers.given_name,
|
|
33
|
+
family_name: adminUsers.family_name,
|
|
34
|
+
username: adminUsers.username,
|
|
35
|
+
email: adminUsers.email,
|
|
36
|
+
remember_me: adminUsers.remember_me,
|
|
37
|
+
last_login: adminUsers.last_login,
|
|
38
|
+
last_login_ip: adminUsers.last_login_ip,
|
|
39
|
+
failed_login_attempts: adminUsers.failed_login_attempts,
|
|
40
|
+
is_super_admin: adminUsers.is_super_admin,
|
|
41
|
+
is_enabled: adminUsers.is_enabled,
|
|
42
|
+
is_email_verified: adminUsers.is_email_verified,
|
|
43
|
+
created_at: adminUsers.created_at,
|
|
44
|
+
updated_at: adminUsers.updated_at,
|
|
45
|
+
};
|
|
46
|
+
const ORDER_COLUMN = {
|
|
47
|
+
given_name: adminUsers.given_name,
|
|
48
|
+
family_name: adminUsers.family_name,
|
|
49
|
+
email: adminUsers.email,
|
|
50
|
+
username: adminUsers.username,
|
|
51
|
+
created_at: adminUsers.created_at,
|
|
52
|
+
updated_at: adminUsers.updated_at,
|
|
53
|
+
};
|
|
54
|
+
export function createAdminUsersRepository(db) {
|
|
55
|
+
return {
|
|
56
|
+
async create(input) {
|
|
57
|
+
const [row] = await db
|
|
58
|
+
.insert(adminUsers)
|
|
59
|
+
.values({
|
|
60
|
+
id: uuidv7(),
|
|
61
|
+
email: input.email.toLowerCase(),
|
|
62
|
+
password: input.password_hash,
|
|
63
|
+
given_name: input.given_name ?? null,
|
|
64
|
+
family_name: input.family_name ?? null,
|
|
65
|
+
username: input.username ?? null,
|
|
66
|
+
is_super_admin: input.is_super_admin ?? false,
|
|
67
|
+
is_enabled: input.is_enabled ?? false,
|
|
68
|
+
is_email_verified: input.is_email_verified ?? false,
|
|
69
|
+
})
|
|
70
|
+
.returning(PUBLIC_COLUMNS);
|
|
71
|
+
if (!row)
|
|
72
|
+
throw new Error('createAdminUser: insert returned no row');
|
|
73
|
+
return row;
|
|
74
|
+
},
|
|
75
|
+
async getById(id) {
|
|
76
|
+
const [row] = await db.select(PUBLIC_COLUMNS).from(adminUsers).where(eq(adminUsers.id, id));
|
|
77
|
+
return row ?? null;
|
|
78
|
+
},
|
|
79
|
+
async getByEmail(email) {
|
|
80
|
+
const [row] = await db
|
|
81
|
+
.select(PUBLIC_COLUMNS)
|
|
82
|
+
.from(adminUsers)
|
|
83
|
+
.where(eq(adminUsers.email, email.toLowerCase()));
|
|
84
|
+
return row ?? null;
|
|
85
|
+
},
|
|
86
|
+
async getByUsername(username) {
|
|
87
|
+
const [row] = await db
|
|
88
|
+
.select(PUBLIC_COLUMNS)
|
|
89
|
+
.from(adminUsers)
|
|
90
|
+
.where(eq(adminUsers.username, username));
|
|
91
|
+
return row ?? null;
|
|
92
|
+
},
|
|
93
|
+
async getByEmailForSignIn(email) {
|
|
94
|
+
const [row] = await db
|
|
95
|
+
.select({ ...PUBLIC_COLUMNS, password_hash: adminUsers.password })
|
|
96
|
+
.from(adminUsers)
|
|
97
|
+
.where(eq(adminUsers.email, email.toLowerCase()));
|
|
98
|
+
return row ?? null;
|
|
99
|
+
},
|
|
100
|
+
async getByIdForSignIn(id) {
|
|
101
|
+
const [row] = await db
|
|
102
|
+
.select({ ...PUBLIC_COLUMNS, password_hash: adminUsers.password })
|
|
103
|
+
.from(adminUsers)
|
|
104
|
+
.where(eq(adminUsers.id, id));
|
|
105
|
+
return row ?? null;
|
|
106
|
+
},
|
|
107
|
+
async list(options) {
|
|
108
|
+
const needle = options.query?.trim();
|
|
109
|
+
const filter = needle && needle.length > 0
|
|
110
|
+
? or(ilike(adminUsers.email, `%${needle}%`), ilike(adminUsers.given_name, `%${needle}%`), ilike(adminUsers.family_name, `%${needle}%`), ilike(adminUsers.username, `%${needle}%`))
|
|
111
|
+
: undefined;
|
|
112
|
+
const sortCol = ORDER_COLUMN[options.order];
|
|
113
|
+
const orderExpr = options.desc ? desc(sortCol) : asc(sortCol);
|
|
114
|
+
const offset = Math.max(0, (options.page - 1) * options.pageSize);
|
|
115
|
+
const query = db.select(PUBLIC_COLUMNS).from(adminUsers);
|
|
116
|
+
const filtered = filter ? query.where(filter) : query;
|
|
117
|
+
return filtered.orderBy(orderExpr).limit(options.pageSize).offset(offset);
|
|
118
|
+
},
|
|
119
|
+
async count(options) {
|
|
120
|
+
const needle = options?.query?.trim();
|
|
121
|
+
const filter = needle && needle.length > 0
|
|
122
|
+
? or(ilike(adminUsers.email, `%${needle}%`), ilike(adminUsers.given_name, `%${needle}%`), ilike(adminUsers.family_name, `%${needle}%`), ilike(adminUsers.username, `%${needle}%`))
|
|
123
|
+
: undefined;
|
|
124
|
+
const base = db.select({ value: sql `count(*)::int` }).from(adminUsers);
|
|
125
|
+
const [row] = await (filter ? base.where(filter) : base);
|
|
126
|
+
return row?.value ?? 0;
|
|
127
|
+
},
|
|
128
|
+
async update(id, expectedVid, patch) {
|
|
129
|
+
const updateSet = {
|
|
130
|
+
updated_at: new Date(),
|
|
131
|
+
vid: sql `${adminUsers.vid} + 1`,
|
|
132
|
+
};
|
|
133
|
+
if (patch.given_name !== undefined)
|
|
134
|
+
updateSet.given_name = patch.given_name;
|
|
135
|
+
if (patch.family_name !== undefined)
|
|
136
|
+
updateSet.family_name = patch.family_name;
|
|
137
|
+
if (patch.username !== undefined)
|
|
138
|
+
updateSet.username = patch.username;
|
|
139
|
+
if (patch.email !== undefined)
|
|
140
|
+
updateSet.email = patch.email.toLowerCase();
|
|
141
|
+
if (patch.is_super_admin !== undefined)
|
|
142
|
+
updateSet.is_super_admin = patch.is_super_admin;
|
|
143
|
+
if (patch.is_enabled !== undefined)
|
|
144
|
+
updateSet.is_enabled = patch.is_enabled;
|
|
145
|
+
if (patch.is_email_verified !== undefined)
|
|
146
|
+
updateSet.is_email_verified = patch.is_email_verified;
|
|
147
|
+
if (patch.remember_me !== undefined)
|
|
148
|
+
updateSet.remember_me = patch.remember_me;
|
|
149
|
+
const [row] = await db
|
|
150
|
+
.update(adminUsers)
|
|
151
|
+
.set(updateSet)
|
|
152
|
+
.where(and(eq(adminUsers.id, id), eq(adminUsers.vid, expectedVid)))
|
|
153
|
+
.returning(PUBLIC_COLUMNS);
|
|
154
|
+
if (!row)
|
|
155
|
+
throw ERR_ADMIN_USER_VERSION_CONFLICT();
|
|
156
|
+
return row;
|
|
157
|
+
},
|
|
158
|
+
async setPasswordHash(id, expectedVid, passwordHash) {
|
|
159
|
+
const [row] = await db
|
|
160
|
+
.update(adminUsers)
|
|
161
|
+
.set({
|
|
162
|
+
password: passwordHash,
|
|
163
|
+
updated_at: new Date(),
|
|
164
|
+
vid: sql `${adminUsers.vid} + 1`,
|
|
165
|
+
})
|
|
166
|
+
.where(and(eq(adminUsers.id, id), eq(adminUsers.vid, expectedVid)))
|
|
167
|
+
.returning(PUBLIC_COLUMNS);
|
|
168
|
+
if (!row)
|
|
169
|
+
throw ERR_ADMIN_USER_VERSION_CONFLICT();
|
|
170
|
+
return row;
|
|
171
|
+
},
|
|
172
|
+
async setEnabled(id, enabled) {
|
|
173
|
+
await db
|
|
174
|
+
.update(adminUsers)
|
|
175
|
+
.set({ is_enabled: enabled, updated_at: new Date(), vid: sql `${adminUsers.vid} + 1` })
|
|
176
|
+
.where(eq(adminUsers.id, id));
|
|
177
|
+
},
|
|
178
|
+
async recordLoginSuccess(id, ip) {
|
|
179
|
+
await db
|
|
180
|
+
.update(adminUsers)
|
|
181
|
+
.set({
|
|
182
|
+
last_login: new Date(),
|
|
183
|
+
last_login_ip: ip,
|
|
184
|
+
failed_login_attempts: 0,
|
|
185
|
+
updated_at: new Date(),
|
|
186
|
+
})
|
|
187
|
+
.where(eq(adminUsers.id, id));
|
|
188
|
+
},
|
|
189
|
+
async recordLoginFailure(id) {
|
|
190
|
+
await db
|
|
191
|
+
.update(adminUsers)
|
|
192
|
+
.set({
|
|
193
|
+
failed_login_attempts: sql `${adminUsers.failed_login_attempts} + 1`,
|
|
194
|
+
updated_at: new Date(),
|
|
195
|
+
})
|
|
196
|
+
.where(eq(adminUsers.id, id));
|
|
197
|
+
},
|
|
198
|
+
async delete(id, expectedVid) {
|
|
199
|
+
const result = await db
|
|
200
|
+
.delete(adminUsers)
|
|
201
|
+
.where(and(eq(adminUsers.id, id), eq(adminUsers.vid, expectedVid)))
|
|
202
|
+
.returning({ id: adminUsers.id });
|
|
203
|
+
if (result.length === 0)
|
|
204
|
+
throw ERR_ADMIN_USER_VERSION_CONFLICT();
|
|
205
|
+
},
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
//# sourceMappingURL=admin-users-repository.js.map
|