@loomcore/api 0.1.24 → 0.1.25
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/__tests__/test-objects.js +2 -0
- package/dist/databases/postgres/migrations/006-create-admin-user.migration.d.ts +4 -8
- package/dist/databases/postgres/migrations/006-create-admin-user.migration.js +10 -22
- package/dist/databases/postgres/migrations/011-create-admin-authorization.migration.d.ts +23 -0
- package/dist/databases/postgres/migrations/011-create-admin-authorization.migration.js +117 -0
- package/dist/databases/postgres/migrations/setup-for-auth.migration.d.ts +1 -1
- package/dist/databases/postgres/migrations/setup-for-auth.migration.js +41 -19
- package/dist/databases/postgres/postgres.database.d.ts +1 -0
- package/dist/databases/postgres/postgres.database.js +61 -0
- package/dist/models/feature.model.d.ts +7 -0
- package/dist/models/feature.model.js +6 -0
- package/dist/models/role.model.d.ts +7 -0
- package/dist/models/role.model.js +6 -0
- package/dist/models/user-role.model.d.ts +8 -0
- package/dist/models/user-role.model.js +7 -0
- package/dist/services/index.d.ts +1 -1
- package/dist/services/index.js +1 -1
- package/dist/services/user-service/user.service.d.ts +12 -0
- package/dist/services/user-service/user.service.js +65 -0
- package/package.json +2 -2
- package/dist/services/user.service.d.ts +0 -8
- package/dist/services/user.service.js +0 -22
|
@@ -28,6 +28,7 @@ export function getTestMetaOrgUser() {
|
|
|
28
28
|
authorizations: [{
|
|
29
29
|
_id: '6939c54e57a1c6576a40c590',
|
|
30
30
|
_orgId: getTestMetaOrg()._id,
|
|
31
|
+
role: 'metaorgUser',
|
|
31
32
|
feature: 'metaorgUser',
|
|
32
33
|
config: {},
|
|
33
34
|
_created: new Date(),
|
|
@@ -76,6 +77,7 @@ export function getTestOrgUser() {
|
|
|
76
77
|
authorizations: [{
|
|
77
78
|
_id: '6939c54e57a1c6576a40c591',
|
|
78
79
|
_orgId: getTestOrg()._id,
|
|
80
|
+
role: 'testOrgUser',
|
|
79
81
|
feature: 'testOrgUser',
|
|
80
82
|
config: {},
|
|
81
83
|
_created: new Date(),
|
|
@@ -2,16 +2,12 @@ import { Client } from "pg";
|
|
|
2
2
|
import { IMigration } from "../index.js";
|
|
3
3
|
export declare class CreateAdminUserMigration implements IMigration {
|
|
4
4
|
private readonly client;
|
|
5
|
-
|
|
6
|
-
private readonly adminPassword;
|
|
7
|
-
constructor(client: Client, adminEmail: string, adminPassword: string);
|
|
5
|
+
constructor(client: Client);
|
|
8
6
|
index: number;
|
|
9
|
-
execute(): Promise<{
|
|
7
|
+
execute(adminEmail?: string, adminPassword?: string): Promise<{
|
|
10
8
|
success: boolean;
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
success: boolean;
|
|
14
|
-
error: null;
|
|
9
|
+
adminUserId: string | undefined;
|
|
10
|
+
error: Error | null;
|
|
15
11
|
}>;
|
|
16
12
|
revert(): Promise<{
|
|
17
13
|
success: boolean;
|
|
@@ -4,42 +4,30 @@ import { getSystemUserContext } from "@loomcore/common/models";
|
|
|
4
4
|
import { AuthService } from "../../../services/auth.service.js";
|
|
5
5
|
export class CreateAdminUserMigration {
|
|
6
6
|
client;
|
|
7
|
-
|
|
8
|
-
adminPassword;
|
|
9
|
-
constructor(client, adminEmail, adminPassword) {
|
|
7
|
+
constructor(client) {
|
|
10
8
|
this.client = client;
|
|
11
|
-
this.adminEmail = adminEmail;
|
|
12
|
-
this.adminPassword = adminPassword;
|
|
13
9
|
}
|
|
14
10
|
index = 6;
|
|
15
|
-
async execute() {
|
|
11
|
+
async execute(adminEmail, adminPassword) {
|
|
16
12
|
const _id = randomUUID().toString();
|
|
17
13
|
const systemUserContext = getSystemUserContext();
|
|
14
|
+
let createdUser;
|
|
18
15
|
try {
|
|
19
16
|
const database = new PostgresDatabase(this.client);
|
|
20
17
|
const authService = new AuthService(database);
|
|
21
|
-
|
|
18
|
+
createdUser = await authService.createUser(systemUserContext, {
|
|
22
19
|
_id: _id,
|
|
23
20
|
_orgId: systemUserContext._orgId,
|
|
24
|
-
email:
|
|
25
|
-
password:
|
|
21
|
+
email: adminEmail,
|
|
22
|
+
password: adminPassword,
|
|
26
23
|
firstName: 'Admin',
|
|
27
24
|
lastName: 'User',
|
|
28
25
|
displayName: 'Admin User',
|
|
29
|
-
authorizations: [{
|
|
30
|
-
_id: randomUUID().toString(),
|
|
31
|
-
feature: 'metaorgAdmin',
|
|
32
|
-
config: {},
|
|
33
|
-
_created: new Date(),
|
|
34
|
-
_createdBy: systemUserContext.user._id,
|
|
35
|
-
_updated: new Date(),
|
|
36
|
-
_updatedBy: systemUserContext.user._id
|
|
37
|
-
}],
|
|
38
26
|
});
|
|
39
27
|
}
|
|
40
28
|
catch (error) {
|
|
41
29
|
return {
|
|
42
|
-
success: false, error: new Error(`Error creating admin user: ${error.message}`)
|
|
30
|
+
success: false, adminUserId: undefined, error: new Error(`Error creating admin user: ${error.message}`)
|
|
43
31
|
};
|
|
44
32
|
}
|
|
45
33
|
try {
|
|
@@ -48,13 +36,13 @@ export class CreateAdminUserMigration {
|
|
|
48
36
|
VALUES ('${_id}', ${this.index}, TRUE, FALSE);
|
|
49
37
|
`);
|
|
50
38
|
if (result.rowCount === 0) {
|
|
51
|
-
return { success: false, error: new Error(`Error inserting migration ${this.index} to migrations table: No row returned`) };
|
|
39
|
+
return { success: false, adminUserId: undefined, error: new Error(`Error inserting migration ${this.index} to migrations table: No row returned`) };
|
|
52
40
|
}
|
|
53
41
|
}
|
|
54
42
|
catch (error) {
|
|
55
|
-
return { success: false, error: new Error(`Error inserting migration ${this.index} to migrations table: ${error.message}`) };
|
|
43
|
+
return { success: false, adminUserId: undefined, error: new Error(`Error inserting migration ${this.index} to migrations table: ${error.message}`) };
|
|
56
44
|
}
|
|
57
|
-
return { success: true, error: null };
|
|
45
|
+
return { success: true, adminUserId: createdUser?._id, error: null };
|
|
58
46
|
}
|
|
59
47
|
async revert() {
|
|
60
48
|
throw new Error('Not implemented');
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Client } from "pg";
|
|
2
|
+
import { IMigration } from "./migration.interface.js";
|
|
3
|
+
export declare class CreateAdminAuthorizationMigration implements IMigration {
|
|
4
|
+
private readonly client;
|
|
5
|
+
private readonly adminUserId;
|
|
6
|
+
private readonly metaOrgId?;
|
|
7
|
+
constructor(client: Client, adminUserId: string, metaOrgId?: string | undefined);
|
|
8
|
+
index: number;
|
|
9
|
+
execute(): Promise<{
|
|
10
|
+
success: boolean;
|
|
11
|
+
error: Error;
|
|
12
|
+
} | {
|
|
13
|
+
success: boolean;
|
|
14
|
+
error: null;
|
|
15
|
+
}>;
|
|
16
|
+
revert(metaOrgId?: string): Promise<{
|
|
17
|
+
success: boolean;
|
|
18
|
+
error: Error;
|
|
19
|
+
} | {
|
|
20
|
+
success: boolean;
|
|
21
|
+
error: null;
|
|
22
|
+
}>;
|
|
23
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { randomUUID } from "crypto";
|
|
2
|
+
export class CreateAdminAuthorizationMigration {
|
|
3
|
+
client;
|
|
4
|
+
adminUserId;
|
|
5
|
+
metaOrgId;
|
|
6
|
+
constructor(client, adminUserId, metaOrgId) {
|
|
7
|
+
this.client = client;
|
|
8
|
+
this.adminUserId = adminUserId;
|
|
9
|
+
this.metaOrgId = metaOrgId;
|
|
10
|
+
}
|
|
11
|
+
index = 11;
|
|
12
|
+
async execute() {
|
|
13
|
+
const _id = randomUUID().toString();
|
|
14
|
+
try {
|
|
15
|
+
await this.client.query('BEGIN');
|
|
16
|
+
const roleId = randomUUID().toString();
|
|
17
|
+
const roleResult = await this.client.query(`
|
|
18
|
+
INSERT INTO "roles" ("_id", "_orgId", "name")
|
|
19
|
+
VALUES ($1, $2, 'admin')
|
|
20
|
+
`, [roleId, this.metaOrgId]);
|
|
21
|
+
if (roleResult.rows.length === 0) {
|
|
22
|
+
await this.client.query('ROLLBACK');
|
|
23
|
+
return { success: false, error: new Error('Failed to create admin role') };
|
|
24
|
+
}
|
|
25
|
+
const userRoleId = randomUUID().toString();
|
|
26
|
+
const userRoleResult = await this.client.query(`
|
|
27
|
+
INSERT INTO "user_roles" ("_id", "_orgId", "_userId", "_roleId")
|
|
28
|
+
VALUES ($1, $2, $3, $4)
|
|
29
|
+
`, [userRoleId, this.metaOrgId, this.adminUserId, roleId]);
|
|
30
|
+
if (userRoleResult.rows.length === 0) {
|
|
31
|
+
await this.client.query('ROLLBACK');
|
|
32
|
+
return { success: false, error: new Error('Failed to create user role') };
|
|
33
|
+
}
|
|
34
|
+
const featureId = randomUUID().toString();
|
|
35
|
+
const featureResult = await this.client.query(`
|
|
36
|
+
INSERT INTO "features" ("_id", "_orgId", "name")
|
|
37
|
+
VALUES ($1, $2, 'admin')
|
|
38
|
+
`, [featureId, this.metaOrgId]);
|
|
39
|
+
if (featureResult.rows.length === 0) {
|
|
40
|
+
await this.client.query('ROLLBACK');
|
|
41
|
+
return { success: false, error: new Error('Failed to create admin feature') };
|
|
42
|
+
}
|
|
43
|
+
const authorizationId = randomUUID().toString();
|
|
44
|
+
const authorizationResult = await this.client.query(`
|
|
45
|
+
INSERT INTO "authorizations" (
|
|
46
|
+
"_id", "_orgId", "_roleId", "_featureId",
|
|
47
|
+
"_created", "_createdBy", "_updated", "_updatedBy"
|
|
48
|
+
)
|
|
49
|
+
VALUES ($1, $2, $3, $4, NOW(), 'system', NOW(), 'system')
|
|
50
|
+
`, [authorizationId, this.metaOrgId, roleId, featureId]);
|
|
51
|
+
if (authorizationResult.rows.length === 0) {
|
|
52
|
+
await this.client.query('ROLLBACK');
|
|
53
|
+
return { success: false, error: new Error('Failed to create admin authorization') };
|
|
54
|
+
}
|
|
55
|
+
const migrationResult = await this.client.query(`
|
|
56
|
+
INSERT INTO "migrations" ("_id", "index", "hasRun", "reverted")
|
|
57
|
+
VALUES ($1, $2, TRUE, FALSE)
|
|
58
|
+
`, [_id, this.index]);
|
|
59
|
+
if (migrationResult.rowCount === 0) {
|
|
60
|
+
await this.client.query('ROLLBACK');
|
|
61
|
+
return { success: false, error: new Error(`Error inserting migration ${this.index} to migrations table: No row returned`) };
|
|
62
|
+
}
|
|
63
|
+
await this.client.query('COMMIT');
|
|
64
|
+
return { success: true, error: null };
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
await this.client.query('ROLLBACK');
|
|
68
|
+
return { success: false, error: new Error(`Error executing migration ${this.index}: ${error.message}`) };
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
async revert(metaOrgId) {
|
|
72
|
+
try {
|
|
73
|
+
await this.client.query('BEGIN');
|
|
74
|
+
await this.client.query(`
|
|
75
|
+
DELETE FROM "authorizations"
|
|
76
|
+
WHERE "_orgId" = $1
|
|
77
|
+
AND "_featureId" IN (
|
|
78
|
+
SELECT "_id" FROM "features"
|
|
79
|
+
WHERE "_orgId" = $1 AND "name" = 'admin'
|
|
80
|
+
)
|
|
81
|
+
AND "_roleId" IN (
|
|
82
|
+
SELECT "_id" FROM "roles"
|
|
83
|
+
WHERE "_orgId" = $1 AND "name" = 'admin'
|
|
84
|
+
)
|
|
85
|
+
`, [metaOrgId]);
|
|
86
|
+
await this.client.query(`
|
|
87
|
+
DELETE FROM "features"
|
|
88
|
+
WHERE "_orgId" = $1 AND "name" = 'admin'
|
|
89
|
+
`, [metaOrgId]);
|
|
90
|
+
await this.client.query(`
|
|
91
|
+
DELETE FROM "user_roles"
|
|
92
|
+
WHERE "_orgId" = $1
|
|
93
|
+
AND "_roleId" IN (
|
|
94
|
+
SELECT "_id" FROM "roles"
|
|
95
|
+
WHERE "_orgId" = $1 AND "name" = 'admin'
|
|
96
|
+
)
|
|
97
|
+
`, [metaOrgId]);
|
|
98
|
+
await this.client.query(`
|
|
99
|
+
DELETE FROM "roles"
|
|
100
|
+
WHERE "_orgId" = $1 AND "name" = 'admin'
|
|
101
|
+
`, [metaOrgId]);
|
|
102
|
+
const updateResult = await this.client.query(`
|
|
103
|
+
UPDATE "migrations" SET "reverted" = TRUE WHERE "index" = $1
|
|
104
|
+
`, [this.index]);
|
|
105
|
+
if (updateResult.rowCount === 0) {
|
|
106
|
+
await this.client.query('ROLLBACK');
|
|
107
|
+
return { success: false, error: new Error(`Error updating migration record for index ${this.index}: No row returned`) };
|
|
108
|
+
}
|
|
109
|
+
await this.client.query('COMMIT');
|
|
110
|
+
return { success: true, error: null };
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
await this.client.query('ROLLBACK');
|
|
114
|
+
return { success: false, error: new Error(`Error reverting migration ${this.index}: ${error.message}`) };
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Client } from "pg";
|
|
2
|
-
export declare function setupDatabaseForAuth(client: Client, adminUsername: string, adminPassword: string): Promise<{
|
|
2
|
+
export declare function setupDatabaseForAuth(client: Client, adminUsername: string, adminPassword: string, metaOrgId?: string): Promise<{
|
|
3
3
|
success: boolean;
|
|
4
4
|
error: Error | null;
|
|
5
5
|
}>;
|
|
@@ -7,7 +7,8 @@ import { CreateUserRolesTableMigration } from "./008-create-user-roles-table.mig
|
|
|
7
7
|
import { CreateRoleTableMigration } from "./007-create-roles-table.migration.js";
|
|
8
8
|
import { CreateAuthorizationsTableMigration } from "./010-create-authorizations-table.migration.js";
|
|
9
9
|
import { CreateFeaturesTableMigration } from "./009-create-features-table.migration.js";
|
|
10
|
-
|
|
10
|
+
import { CreateAdminAuthorizationMigration } from "./011-create-admin-authorization.migration.js";
|
|
11
|
+
export async function setupDatabaseForAuth(client, adminUsername, adminPassword, metaOrgId) {
|
|
11
12
|
let runMigrations = [];
|
|
12
13
|
if (await doesTableExist(client, 'migrations')) {
|
|
13
14
|
const migrations = await client.query(`
|
|
@@ -19,24 +20,45 @@ export async function setupDatabaseForAuth(client, adminUsername, adminPassword)
|
|
|
19
20
|
return row.index;
|
|
20
21
|
});
|
|
21
22
|
}
|
|
22
|
-
let
|
|
23
|
-
if (!runMigrations.includes(1))
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
if (!runMigrations.includes(
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
if (!runMigrations.includes(
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
if (!runMigrations.includes(
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
23
|
+
let adminUserId;
|
|
24
|
+
if (!runMigrations.includes(1)) {
|
|
25
|
+
const migration = new CreateMigrationTableMigration(client);
|
|
26
|
+
await migration.execute();
|
|
27
|
+
}
|
|
28
|
+
if (!runMigrations.includes(3)) {
|
|
29
|
+
const migration = new CreateUsersTableMigration(client);
|
|
30
|
+
await migration.execute();
|
|
31
|
+
}
|
|
32
|
+
if (!runMigrations.includes(4)) {
|
|
33
|
+
const migration = new CreateRefreshTokenTableMigration(client);
|
|
34
|
+
await migration.execute();
|
|
35
|
+
}
|
|
36
|
+
if (!runMigrations.includes(6)) {
|
|
37
|
+
const migration = new CreateAdminUserMigration(client);
|
|
38
|
+
const result = await migration.execute(adminUsername, adminPassword);
|
|
39
|
+
adminUserId = result.adminUserId;
|
|
40
|
+
}
|
|
41
|
+
if (!runMigrations.includes(7)) {
|
|
42
|
+
const migration = new CreateRoleTableMigration(client);
|
|
43
|
+
await migration.execute();
|
|
44
|
+
}
|
|
45
|
+
if (!runMigrations.includes(8)) {
|
|
46
|
+
const migration = new CreateUserRolesTableMigration(client);
|
|
47
|
+
await migration.execute();
|
|
48
|
+
}
|
|
49
|
+
if (!runMigrations.includes(9)) {
|
|
50
|
+
const migration = new CreateFeaturesTableMigration(client);
|
|
51
|
+
await migration.execute();
|
|
52
|
+
}
|
|
53
|
+
if (!runMigrations.includes(10)) {
|
|
54
|
+
const migration = new CreateAuthorizationsTableMigration(client);
|
|
55
|
+
await migration.execute();
|
|
56
|
+
}
|
|
57
|
+
if (!runMigrations.includes(11)) {
|
|
58
|
+
if (!adminUserId) {
|
|
59
|
+
return { success: false, error: new Error('Admin user ID is required') };
|
|
60
|
+
}
|
|
61
|
+
const migration = new CreateAdminAuthorizationMigration(client, adminUserId, metaOrgId);
|
|
40
62
|
await migration.execute();
|
|
41
63
|
}
|
|
42
64
|
return { success: true, error: null };
|
|
@@ -28,4 +28,5 @@ export declare class PostgresDatabase implements IDatabase {
|
|
|
28
28
|
deleteMany(queryObject: IQueryOptions, pluralResourceName: string): Promise<DeleteResult>;
|
|
29
29
|
find<T extends IEntity>(queryObject: IQueryOptions, pluralResourceName: string): Promise<T[]>;
|
|
30
30
|
findOne<T extends IEntity>(queryObject: IQueryOptions, pluralResourceName: string): Promise<T | null>;
|
|
31
|
+
getUserAuthorizations(userIds: string[], orgId?: string): Promise<Map<string, any[]>>;
|
|
31
32
|
}
|
|
@@ -66,4 +66,65 @@ export class PostgresDatabase {
|
|
|
66
66
|
async findOne(queryObject, pluralResourceName) {
|
|
67
67
|
return findOneQuery(this.client, queryObject, pluralResourceName);
|
|
68
68
|
}
|
|
69
|
+
async getUserAuthorizations(userIds, orgId) {
|
|
70
|
+
if (userIds.length === 0) {
|
|
71
|
+
return new Map();
|
|
72
|
+
}
|
|
73
|
+
const now = new Date();
|
|
74
|
+
const placeholders = userIds.map((_, i) => `$${i + 1}`).join(', ');
|
|
75
|
+
let query = `
|
|
76
|
+
SELECT DISTINCT
|
|
77
|
+
ur."_userId" as "userId",
|
|
78
|
+
r."name" as "role",
|
|
79
|
+
f."name" as "feature",
|
|
80
|
+
a."config",
|
|
81
|
+
a."_id",
|
|
82
|
+
a."_orgId",
|
|
83
|
+
a."_created",
|
|
84
|
+
a."_createdBy",
|
|
85
|
+
a."_updated",
|
|
86
|
+
a."_updatedBy"
|
|
87
|
+
FROM "user_roles" ur
|
|
88
|
+
INNER JOIN "roles" r ON ur."_roleId" = r."_id"
|
|
89
|
+
INNER JOIN "authorizations" a ON r."_id" = a."_roleId"
|
|
90
|
+
INNER JOIN "features" f ON a."_featureId" = f."_id"
|
|
91
|
+
WHERE ur."_userId" IN (${placeholders})
|
|
92
|
+
AND ur."_deleted" IS NULL
|
|
93
|
+
AND r."_deleted" IS NULL
|
|
94
|
+
AND a."_deleted" IS NULL
|
|
95
|
+
AND f."_deleted" IS NULL
|
|
96
|
+
AND (a."startDate" IS NULL OR a."startDate" <= $${userIds.length + 1})
|
|
97
|
+
AND (a."endDate" IS NULL OR a."endDate" >= $${userIds.length + 1})
|
|
98
|
+
`;
|
|
99
|
+
const values = [...userIds, now];
|
|
100
|
+
if (orgId) {
|
|
101
|
+
query += ` AND ur."_orgId" = $${userIds.length + 2} AND r."_orgId" = $${userIds.length + 2} AND a."_orgId" = $${userIds.length + 2} AND f."_orgId" = $${userIds.length + 2}`;
|
|
102
|
+
values.push(orgId);
|
|
103
|
+
}
|
|
104
|
+
const result = await this.client.query(query, values);
|
|
105
|
+
const authorizationsMap = new Map();
|
|
106
|
+
for (const row of result.rows) {
|
|
107
|
+
const userId = row.userId;
|
|
108
|
+
if (!authorizationsMap.has(userId)) {
|
|
109
|
+
authorizationsMap.set(userId, []);
|
|
110
|
+
}
|
|
111
|
+
authorizationsMap.get(userId).push({
|
|
112
|
+
_id: row._id,
|
|
113
|
+
_orgId: row._orgId,
|
|
114
|
+
role: row.role,
|
|
115
|
+
feature: row.feature,
|
|
116
|
+
config: row.config || undefined,
|
|
117
|
+
_created: row._created,
|
|
118
|
+
_createdBy: row._createdBy,
|
|
119
|
+
_updated: row._updated,
|
|
120
|
+
_updatedBy: row._updatedBy,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
for (const userId of userIds) {
|
|
124
|
+
if (!authorizationsMap.has(userId)) {
|
|
125
|
+
authorizationsMap.set(userId, []);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return authorizationsMap;
|
|
129
|
+
}
|
|
69
130
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { IEntity } from "@loomcore/common/models";
|
|
2
|
+
import { TSchema } from "@sinclair/typebox";
|
|
3
|
+
export interface IFeature extends IEntity {
|
|
4
|
+
name: string;
|
|
5
|
+
}
|
|
6
|
+
export declare const FeatureSchema: TSchema;
|
|
7
|
+
export declare const FeatureModelSpec: import("@loomcore/common/models").IModelSpec<TSchema>;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { IEntity } from "@loomcore/common/models";
|
|
2
|
+
import { TSchema } from "@sinclair/typebox";
|
|
3
|
+
export interface IRole extends IEntity {
|
|
4
|
+
name: string;
|
|
5
|
+
}
|
|
6
|
+
export declare const RoleSchema: TSchema;
|
|
7
|
+
export declare const RoleModelSpec: import("@loomcore/common/models").IModelSpec<TSchema>;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { IEntity } from "@loomcore/common/models";
|
|
2
|
+
import { TSchema } from "@sinclair/typebox";
|
|
3
|
+
export interface IUserRole extends IEntity {
|
|
4
|
+
userId: string;
|
|
5
|
+
roleId: string;
|
|
6
|
+
}
|
|
7
|
+
export declare const UserRoleSchema: TSchema;
|
|
8
|
+
export declare const UserRoleModelSpec: import("@loomcore/common/models").IModelSpec<TSchema>;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { entityUtils } from "@loomcore/common/utils";
|
|
2
|
+
import { Type } from "@sinclair/typebox";
|
|
3
|
+
export const UserRoleSchema = Type.Object({
|
|
4
|
+
userId: Type.String({ minLength: 1 }),
|
|
5
|
+
roleId: Type.String({ minLength: 1 }),
|
|
6
|
+
});
|
|
7
|
+
export const UserRoleModelSpec = entityUtils.getModelSpec(UserRoleSchema);
|
package/dist/services/index.d.ts
CHANGED
|
@@ -7,4 +7,4 @@ export * from './multi-tenant-api.service.js';
|
|
|
7
7
|
export * from './organization.service.js';
|
|
8
8
|
export * from './password-reset-token.service.js';
|
|
9
9
|
export * from './tenant-query-decorator.js';
|
|
10
|
-
export * from './user.service.js';
|
|
10
|
+
export * from './user-service/user.service.js';
|
package/dist/services/index.js
CHANGED
|
@@ -7,4 +7,4 @@ export * from './multi-tenant-api.service.js';
|
|
|
7
7
|
export * from './organization.service.js';
|
|
8
8
|
export * from './password-reset-token.service.js';
|
|
9
9
|
export * from './tenant-query-decorator.js';
|
|
10
|
-
export * from './user.service.js';
|
|
10
|
+
export * from './user-service/user.service.js';
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { IUser, IUserContext, IPagedResult, IQueryOptions } from '@loomcore/common/models';
|
|
2
|
+
import { MultiTenantApiService } from '../index.js';
|
|
3
|
+
import { IDatabase } from '../../databases/models/index.js';
|
|
4
|
+
export declare class UserService extends MultiTenantApiService<IUser> {
|
|
5
|
+
constructor(database: IDatabase);
|
|
6
|
+
fullUpdateById(userContext: IUserContext, id: string, entity: IUser): Promise<IUser>;
|
|
7
|
+
getById(userContext: IUserContext, id: string): Promise<IUser>;
|
|
8
|
+
get(userContext: IUserContext, queryOptions: IQueryOptions): Promise<IPagedResult<IUser>>;
|
|
9
|
+
getAll(userContext: IUserContext): Promise<IUser[]>;
|
|
10
|
+
preprocessEntity(userContext: IUserContext, entity: Partial<IUser>, isCreate: boolean, allowId?: boolean): Promise<Partial<IUser>>;
|
|
11
|
+
private addAuthorizationsToUsers;
|
|
12
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { Value } from '@sinclair/typebox/value';
|
|
2
|
+
import { UserSpec, PublicUserSchema } from '@loomcore/common/models';
|
|
3
|
+
import { MultiTenantApiService } from '../index.js';
|
|
4
|
+
import { IdNotFoundError, ServerError } from '../../errors/index.js';
|
|
5
|
+
import { PostgresDatabase } from '../../databases/postgres/postgres.database.js';
|
|
6
|
+
export class UserService extends MultiTenantApiService {
|
|
7
|
+
constructor(database) {
|
|
8
|
+
super(database, 'users', 'user', UserSpec);
|
|
9
|
+
}
|
|
10
|
+
async fullUpdateById(userContext, id, entity) {
|
|
11
|
+
throw new ServerError('Cannot full update a user. Either use PATCH or /auth/change-password to update password.');
|
|
12
|
+
}
|
|
13
|
+
async getById(userContext, id) {
|
|
14
|
+
const { operations, queryObject } = this.prepareQuery(userContext, {}, []);
|
|
15
|
+
const user = await this.database.getById(operations, queryObject, id, this.pluralResourceName);
|
|
16
|
+
if (!user) {
|
|
17
|
+
throw new IdNotFoundError();
|
|
18
|
+
}
|
|
19
|
+
const usersWithAuth = await this.addAuthorizationsToUsers(userContext, [user]);
|
|
20
|
+
return usersWithAuth[0];
|
|
21
|
+
}
|
|
22
|
+
async get(userContext, queryOptions) {
|
|
23
|
+
const { operations, queryObject } = this.prepareQuery(userContext, queryOptions, []);
|
|
24
|
+
const pagedResult = await this.database.get(operations, queryObject, this.modelSpec, this.pluralResourceName);
|
|
25
|
+
const transformedEntities = await this.addAuthorizationsToUsers(userContext, pagedResult.entities || []);
|
|
26
|
+
return {
|
|
27
|
+
...pagedResult,
|
|
28
|
+
entities: transformedEntities
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
async getAll(userContext) {
|
|
32
|
+
const { operations } = this.prepareQuery(userContext, {}, []);
|
|
33
|
+
const users = await this.database.getAll(operations, this.pluralResourceName);
|
|
34
|
+
return this.addAuthorizationsToUsers(userContext, users);
|
|
35
|
+
}
|
|
36
|
+
async preprocessEntity(userContext, entity, isCreate, allowId = false) {
|
|
37
|
+
const preparedEntity = await super.preprocessEntity(userContext, entity, isCreate);
|
|
38
|
+
if (preparedEntity.email) {
|
|
39
|
+
preparedEntity.email = preparedEntity.email.toLowerCase();
|
|
40
|
+
}
|
|
41
|
+
if (!isCreate) {
|
|
42
|
+
return Value.Clean(PublicUserSchema, preparedEntity);
|
|
43
|
+
}
|
|
44
|
+
return preparedEntity;
|
|
45
|
+
}
|
|
46
|
+
async addAuthorizationsToUsers(userContext, users) {
|
|
47
|
+
if (users.length === 0) {
|
|
48
|
+
return users;
|
|
49
|
+
}
|
|
50
|
+
if (!(this.database instanceof PostgresDatabase)) {
|
|
51
|
+
return users.map(user => this.postprocessEntity(userContext, user));
|
|
52
|
+
}
|
|
53
|
+
const userIds = users.map(user => user._id);
|
|
54
|
+
const orgId = userContext._orgId;
|
|
55
|
+
const authorizationsMap = await this.database.getUserAuthorizations(userIds, orgId);
|
|
56
|
+
return users.map(user => {
|
|
57
|
+
const authorizations = authorizationsMap.get(user._id) || [];
|
|
58
|
+
const userWithAuth = {
|
|
59
|
+
...user,
|
|
60
|
+
authorizations
|
|
61
|
+
};
|
|
62
|
+
return this.postprocessEntity(userContext, userWithAuth);
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@loomcore/api",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.25",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Loom Core Api - An opinionated Node.js api using Typescript, Express, and MongoDb or PostgreSQL",
|
|
6
6
|
"scripts": {
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"qs": "^6.14.0"
|
|
54
54
|
},
|
|
55
55
|
"peerDependencies": {
|
|
56
|
-
"@loomcore/common": "^0.0.
|
|
56
|
+
"@loomcore/common": "^0.0.28",
|
|
57
57
|
"@sinclair/typebox": "0.34.33",
|
|
58
58
|
"cookie-parser": "^1.4.6",
|
|
59
59
|
"cors": "^2.8.5",
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { IUser, IUserContext } from '@loomcore/common/models';
|
|
2
|
-
import { MultiTenantApiService } from '../services/index.js';
|
|
3
|
-
import { IDatabase } from '../databases/models/index.js';
|
|
4
|
-
export declare class UserService extends MultiTenantApiService<IUser> {
|
|
5
|
-
constructor(database: IDatabase);
|
|
6
|
-
fullUpdateById(userContext: IUserContext, id: string, entity: IUser): Promise<IUser>;
|
|
7
|
-
preprocessEntity(userContext: IUserContext, entity: Partial<IUser>, isCreate: boolean, allowId?: boolean): Promise<Partial<IUser>>;
|
|
8
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { Value } from '@sinclair/typebox/value';
|
|
2
|
-
import { UserSpec, PublicUserSchema } from '@loomcore/common/models';
|
|
3
|
-
import { MultiTenantApiService } from '../services/index.js';
|
|
4
|
-
import { ServerError } from '../errors/index.js';
|
|
5
|
-
export class UserService extends MultiTenantApiService {
|
|
6
|
-
constructor(database) {
|
|
7
|
-
super(database, 'users', 'user', UserSpec);
|
|
8
|
-
}
|
|
9
|
-
async fullUpdateById(userContext, id, entity) {
|
|
10
|
-
throw new ServerError('Cannot full update a user. Either use PATCH or /auth/change-password to update password.');
|
|
11
|
-
}
|
|
12
|
-
async preprocessEntity(userContext, entity, isCreate, allowId = false) {
|
|
13
|
-
const preparedEntity = await super.preprocessEntity(userContext, entity, isCreate);
|
|
14
|
-
if (preparedEntity.email) {
|
|
15
|
-
preparedEntity.email = preparedEntity.email.toLowerCase();
|
|
16
|
-
}
|
|
17
|
-
if (!isCreate) {
|
|
18
|
-
return Value.Clean(PublicUserSchema, preparedEntity);
|
|
19
|
-
}
|
|
20
|
-
return preparedEntity;
|
|
21
|
-
}
|
|
22
|
-
}
|