@loomcore/api 0.1.27 → 0.1.29
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__/common-test.utils.d.ts +7 -2
- package/dist/__tests__/common-test.utils.js +65 -11
- package/dist/__tests__/postgres-test-migrations/run-test-migrations.js +0 -2
- package/dist/__tests__/postgres.test-database.js +7 -6
- package/dist/__tests__/test-express-app.js +2 -31
- package/dist/__tests__/test-objects.d.ts +1 -0
- package/dist/__tests__/test-objects.js +5 -1
- package/dist/config/base-api-config.js +1 -1
- package/dist/controllers/api.controller.d.ts +2 -3
- package/dist/controllers/api.controller.js +12 -12
- package/dist/controllers/auth.controller.js +2 -2
- package/dist/controllers/users.controller.d.ts +2 -1
- package/dist/controllers/users.controller.js +2 -2
- package/dist/databases/postgres/migrations/003-create-users-table.migration.js +5 -2
- package/dist/databases/postgres/migrations/004-create-refresh-tokens-table.migration.js +5 -1
- package/dist/databases/postgres/migrations/005-create-meta-org.migration.d.ts +1 -5
- package/dist/databases/postgres/migrations/005-create-meta-org.migration.js +7 -7
- package/dist/databases/postgres/migrations/006-create-admin-user.migration.d.ts +6 -3
- package/dist/databases/postgres/migrations/006-create-admin-user.migration.js +16 -13
- package/dist/databases/postgres/migrations/007-create-roles-table.migration.d.ts +1 -1
- package/dist/databases/postgres/migrations/007-create-roles-table.migration.js +6 -3
- package/dist/databases/postgres/migrations/008-create-user-roles-table.migration.d.ts +1 -1
- package/dist/databases/postgres/migrations/008-create-user-roles-table.migration.js +6 -2
- package/dist/databases/postgres/migrations/009-create-features-table.migration.d.ts +1 -1
- package/dist/databases/postgres/migrations/009-create-features-table.migration.js +6 -3
- package/dist/databases/postgres/migrations/010-create-authorizations-table.migration.d.ts +1 -1
- package/dist/databases/postgres/migrations/010-create-authorizations-table.migration.js +6 -3
- package/dist/databases/postgres/migrations/011-create-admin-authorization.migration.d.ts +3 -3
- package/dist/databases/postgres/migrations/011-create-admin-authorization.migration.js +22 -9
- package/dist/databases/postgres/migrations/database-builder.d.ts +15 -0
- package/dist/databases/postgres/migrations/database-builder.interface.d.ts +10 -0
- package/dist/databases/postgres/migrations/database-builder.interface.js +1 -0
- package/dist/databases/postgres/migrations/database-builder.js +62 -0
- package/dist/databases/postgres/migrations/setup-for-auth.migration.d.ts +5 -2
- package/dist/databases/postgres/migrations/setup-for-auth.migration.js +12 -18
- package/dist/databases/postgres/migrations/setup-for-multitenant.migration.d.ts +5 -3
- package/dist/databases/postgres/migrations/setup-for-multitenant.migration.js +7 -9
- package/dist/models/base-api-config.interface.d.ts +11 -5
- package/dist/services/email.service.js +6 -3
- package/dist/utils/api.utils.d.ts +1 -2
- package/dist/utils/api.utils.js +2 -3
- package/package.json +2 -2
- package/dist/__tests__/postgres-test-migrations/004-create-test-users-table.migration.d.ts +0 -21
- package/dist/__tests__/postgres-test-migrations/004-create-test-users-table.migration.js +0 -75
|
@@ -2,33 +2,34 @@ import { PostgresDatabase } from "../index.js";
|
|
|
2
2
|
import { randomUUID } from "crypto";
|
|
3
3
|
import { getSystemUserContext } from "@loomcore/common/models";
|
|
4
4
|
import { AuthService } from "../../../services/auth.service.js";
|
|
5
|
+
import { config } from "../../../config/index.js";
|
|
5
6
|
export class CreateAdminUserMigration {
|
|
6
7
|
client;
|
|
7
8
|
constructor(client) {
|
|
8
9
|
this.client = client;
|
|
10
|
+
const database = new PostgresDatabase(this.client);
|
|
11
|
+
this.authService = new AuthService(database);
|
|
9
12
|
}
|
|
13
|
+
authService;
|
|
10
14
|
index = 6;
|
|
11
|
-
async execute(
|
|
15
|
+
async execute() {
|
|
12
16
|
const _id = randomUUID().toString();
|
|
13
17
|
const systemUserContext = getSystemUserContext();
|
|
14
18
|
let createdUser;
|
|
15
19
|
try {
|
|
16
|
-
|
|
17
|
-
const authService = new AuthService(database);
|
|
18
|
-
createdUser = await authService.createUser(systemUserContext, {
|
|
20
|
+
createdUser = await this.authService.createUser(systemUserContext, {
|
|
19
21
|
_id: _id,
|
|
20
|
-
_orgId:
|
|
21
|
-
email:
|
|
22
|
-
password:
|
|
22
|
+
_orgId: systemUserContext._orgId,
|
|
23
|
+
email: config.adminUser?.email,
|
|
24
|
+
password: config.adminUser?.password,
|
|
23
25
|
firstName: 'Admin',
|
|
24
26
|
lastName: 'User',
|
|
25
27
|
displayName: 'Admin User',
|
|
26
28
|
});
|
|
27
29
|
}
|
|
28
30
|
catch (error) {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
};
|
|
31
|
+
console.error(`Error creating admin user: ${error.message}`);
|
|
32
|
+
return { success: false, error: new Error(`Error creating admin user: ${error.message}`) };
|
|
32
33
|
}
|
|
33
34
|
try {
|
|
34
35
|
const result = await this.client.query(`
|
|
@@ -36,13 +37,15 @@ export class CreateAdminUserMigration {
|
|
|
36
37
|
VALUES ('${_id}', ${this.index}, TRUE, FALSE);
|
|
37
38
|
`);
|
|
38
39
|
if (result.rowCount === 0) {
|
|
39
|
-
|
|
40
|
+
console.error(`Error inserting migration ${this.index} to migrations table: No row returned`);
|
|
41
|
+
return { success: false, error: new Error(`Error inserting migration ${this.index} to migrations table: No row returned`) };
|
|
40
42
|
}
|
|
41
43
|
}
|
|
42
44
|
catch (error) {
|
|
43
|
-
|
|
45
|
+
console.error(`Error inserting migration ${this.index} to migrations table: ${error.message}`);
|
|
46
|
+
return { success: false, error: new Error(`Error inserting migration ${this.index} to migrations table: ${error.message}`) };
|
|
44
47
|
}
|
|
45
|
-
return { success: true,
|
|
48
|
+
return { success: true, error: null };
|
|
46
49
|
}
|
|
47
50
|
async revert() {
|
|
48
51
|
throw new Error('Not implemented');
|
|
@@ -1,24 +1,27 @@
|
|
|
1
1
|
import { randomUUID } from "crypto";
|
|
2
2
|
import { doesTableExist } from "../utils/does-table-exist.util.js";
|
|
3
|
+
import { config } from "../../../config/index.js";
|
|
3
4
|
export class CreateRoleTableMigration {
|
|
4
5
|
client;
|
|
5
6
|
constructor(client) {
|
|
6
7
|
this.client = client;
|
|
7
8
|
}
|
|
8
9
|
index = 7;
|
|
9
|
-
async execute(
|
|
10
|
+
async execute() {
|
|
10
11
|
const _id = randomUUID().toString();
|
|
11
12
|
try {
|
|
12
13
|
await this.client.query('BEGIN');
|
|
13
14
|
const tableExists = await doesTableExist(this.client, 'roles');
|
|
14
15
|
if (!tableExists) {
|
|
16
|
+
const fkConstraint = config.app.isMultiTenant
|
|
17
|
+
? ',\n CONSTRAINT "fk_roles_organization" FOREIGN KEY ("_orgId") REFERENCES "organizations"("_id") ON DELETE CASCADE'
|
|
18
|
+
: '';
|
|
15
19
|
await this.client.query(`
|
|
16
20
|
CREATE TABLE "roles" (
|
|
17
21
|
"_id" VARCHAR(255) PRIMARY KEY,
|
|
18
22
|
"_orgId" VARCHAR(255),
|
|
19
23
|
"name" VARCHAR(255) NOT NULL,
|
|
20
|
-
CONSTRAINT "
|
|
21
|
-
CONSTRAINT "uk_roles_name" UNIQUE ("_orgId", "name")
|
|
24
|
+
CONSTRAINT "uk_roles_name" UNIQUE ("_orgId", "name")${fkConstraint}
|
|
22
25
|
)
|
|
23
26
|
`);
|
|
24
27
|
}
|
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
import { randomUUID } from "crypto";
|
|
2
2
|
import { doesTableExist } from "../utils/does-table-exist.util.js";
|
|
3
|
+
import { config } from "../../../config/index.js";
|
|
3
4
|
export class CreateUserRolesTableMigration {
|
|
4
5
|
client;
|
|
5
6
|
constructor(client) {
|
|
6
7
|
this.client = client;
|
|
7
8
|
}
|
|
8
9
|
index = 8;
|
|
9
|
-
async execute(
|
|
10
|
+
async execute() {
|
|
10
11
|
const _id = randomUUID().toString();
|
|
11
12
|
try {
|
|
12
13
|
await this.client.query('BEGIN');
|
|
13
14
|
const tableExists = await doesTableExist(this.client, 'user_roles');
|
|
14
15
|
if (!tableExists) {
|
|
16
|
+
const fkConstraint = config.app.isMultiTenant
|
|
17
|
+
? '\n CONSTRAINT "fk_user_roles_organization" FOREIGN KEY ("_orgId") REFERENCES "organizations"("_id") ON DELETE CASCADE,'
|
|
18
|
+
: '';
|
|
15
19
|
await this.client.query(`
|
|
16
20
|
CREATE TABLE "user_roles" (
|
|
17
21
|
"_id" VARCHAR(255) PRIMARY KEY,
|
|
@@ -24,7 +28,7 @@ export class CreateUserRolesTableMigration {
|
|
|
24
28
|
"_updatedBy" VARCHAR(255) NOT NULL,
|
|
25
29
|
"_deleted" TIMESTAMP,
|
|
26
30
|
"_deletedBy" VARCHAR(255),
|
|
27
|
-
|
|
31
|
+
${fkConstraint}
|
|
28
32
|
CONSTRAINT "fk_user_roles_user" FOREIGN KEY ("_userId") REFERENCES "users"("_id") ON DELETE CASCADE,
|
|
29
33
|
CONSTRAINT "fk_user_roles_role" FOREIGN KEY ("_roleId") REFERENCES "roles"("_id") ON DELETE CASCADE,
|
|
30
34
|
CONSTRAINT "uk_user_roles" UNIQUE ("_orgId", "_userId", "_roleId")
|
|
@@ -1,24 +1,27 @@
|
|
|
1
1
|
import { randomUUID } from "crypto";
|
|
2
2
|
import { doesTableExist } from "../utils/does-table-exist.util.js";
|
|
3
|
+
import { config } from "../../../config/index.js";
|
|
3
4
|
export class CreateFeaturesTableMigration {
|
|
4
5
|
client;
|
|
5
6
|
constructor(client) {
|
|
6
7
|
this.client = client;
|
|
7
8
|
}
|
|
8
9
|
index = 9;
|
|
9
|
-
async execute(
|
|
10
|
+
async execute() {
|
|
10
11
|
const _id = randomUUID().toString();
|
|
11
12
|
try {
|
|
12
13
|
await this.client.query('BEGIN');
|
|
13
14
|
const tableExists = await doesTableExist(this.client, 'features');
|
|
14
15
|
if (!tableExists) {
|
|
16
|
+
const fkConstraint = config.app.isMultiTenant
|
|
17
|
+
? ',\n CONSTRAINT "fk_features_organization" FOREIGN KEY ("_orgId") REFERENCES "organizations"("_id") ON DELETE CASCADE'
|
|
18
|
+
: '';
|
|
15
19
|
await this.client.query(`
|
|
16
20
|
CREATE TABLE "features" (
|
|
17
21
|
"_id" VARCHAR(255) PRIMARY KEY,
|
|
18
22
|
"_orgId" VARCHAR(255),
|
|
19
23
|
"name" VARCHAR(255) NOT NULL,
|
|
20
|
-
CONSTRAINT "
|
|
21
|
-
CONSTRAINT "uk_features" UNIQUE ("_orgId", "name")
|
|
24
|
+
CONSTRAINT "uk_features" UNIQUE ("_orgId", "name")${fkConstraint}
|
|
22
25
|
)
|
|
23
26
|
`);
|
|
24
27
|
}
|
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
import { randomUUID } from "crypto";
|
|
2
2
|
import { doesTableExist } from "../utils/does-table-exist.util.js";
|
|
3
|
+
import { config } from "../../../config/index.js";
|
|
3
4
|
export class CreateAuthorizationsTableMigration {
|
|
4
5
|
client;
|
|
5
6
|
constructor(client) {
|
|
6
7
|
this.client = client;
|
|
7
8
|
}
|
|
8
9
|
index = 10;
|
|
9
|
-
async execute(
|
|
10
|
+
async execute() {
|
|
10
11
|
const _id = randomUUID().toString();
|
|
11
12
|
try {
|
|
12
13
|
await this.client.query('BEGIN');
|
|
13
14
|
const tableExists = await doesTableExist(this.client, 'authorizations');
|
|
14
15
|
if (!tableExists) {
|
|
16
|
+
const fkConstraint = config.app.isMultiTenant
|
|
17
|
+
? ',\n CONSTRAINT "fk_authorizations_organization" FOREIGN KEY ("_orgId") REFERENCES "organizations"("_id") ON DELETE CASCADE'
|
|
18
|
+
: '';
|
|
15
19
|
await this.client.query(`
|
|
16
20
|
CREATE TABLE "authorizations" (
|
|
17
21
|
"_id" VARCHAR(255) PRIMARY KEY,
|
|
@@ -27,10 +31,9 @@ export class CreateAuthorizationsTableMigration {
|
|
|
27
31
|
"_updatedBy" VARCHAR(255) NOT NULL,
|
|
28
32
|
"_deleted" TIMESTAMP,
|
|
29
33
|
"_deletedBy" VARCHAR(255),
|
|
30
|
-
CONSTRAINT "fk_authorizations_organization" FOREIGN KEY ("_orgId") REFERENCES "organizations"("_id") ON DELETE CASCADE,
|
|
31
34
|
CONSTRAINT "fk_authorizations_role" FOREIGN KEY ("_roleId") REFERENCES "roles"("_id") ON DELETE CASCADE,
|
|
32
35
|
CONSTRAINT "fk_authorizations_feature" FOREIGN KEY ("_featureId") REFERENCES "features"("_id") ON DELETE CASCADE,
|
|
33
|
-
CONSTRAINT "uk_authorizations" UNIQUE ("_orgId", "_roleId", "_featureId")
|
|
36
|
+
CONSTRAINT "uk_authorizations" UNIQUE ("_orgId", "_roleId", "_featureId")${fkConstraint}
|
|
34
37
|
)
|
|
35
38
|
`);
|
|
36
39
|
}
|
|
@@ -2,9 +2,9 @@ import { Client } from "pg";
|
|
|
2
2
|
import { IMigration } from "./migration.interface.js";
|
|
3
3
|
export declare class CreateAdminAuthorizationMigration implements IMigration {
|
|
4
4
|
private readonly client;
|
|
5
|
-
|
|
6
|
-
private
|
|
7
|
-
|
|
5
|
+
constructor(client: Client);
|
|
6
|
+
private organizationService;
|
|
7
|
+
private authService;
|
|
8
8
|
index: number;
|
|
9
9
|
execute(): Promise<{
|
|
10
10
|
success: boolean;
|
|
@@ -1,23 +1,36 @@
|
|
|
1
1
|
import { randomUUID } from "crypto";
|
|
2
|
+
import { AuthService, OrganizationService } from "../../../services/index.js";
|
|
3
|
+
import { PostgresDatabase } from "../index.js";
|
|
4
|
+
import { config } from "../../../config/index.js";
|
|
5
|
+
import { EmptyUserContext } from "@loomcore/common/models";
|
|
2
6
|
export class CreateAdminAuthorizationMigration {
|
|
3
7
|
client;
|
|
4
|
-
|
|
5
|
-
metaOrgId;
|
|
6
|
-
constructor(client, adminUserId, metaOrgId) {
|
|
8
|
+
constructor(client) {
|
|
7
9
|
this.client = client;
|
|
8
|
-
|
|
9
|
-
this.
|
|
10
|
+
const database = new PostgresDatabase(this.client);
|
|
11
|
+
this.organizationService = new OrganizationService(database);
|
|
12
|
+
this.authService = new AuthService(database);
|
|
10
13
|
}
|
|
14
|
+
organizationService;
|
|
15
|
+
authService;
|
|
11
16
|
index = 11;
|
|
12
17
|
async execute() {
|
|
13
18
|
const _id = randomUUID().toString();
|
|
14
19
|
try {
|
|
20
|
+
const metaOrg = await this.organizationService.getMetaOrg(EmptyUserContext).catch(() => null);
|
|
21
|
+
if (!config.adminUser?.email) {
|
|
22
|
+
return { success: false, error: new Error('Create admin authorization: Admin user email not found in config') };
|
|
23
|
+
}
|
|
24
|
+
const adminUser = await this.authService.getUserByEmail(config.adminUser?.email);
|
|
25
|
+
if (!adminUser) {
|
|
26
|
+
return { success: false, error: new Error('Create admin authorization: Admin user not found') };
|
|
27
|
+
}
|
|
15
28
|
await this.client.query('BEGIN');
|
|
16
29
|
const roleId = randomUUID().toString();
|
|
17
30
|
const roleResult = await this.client.query(`
|
|
18
31
|
INSERT INTO "roles" ("_id", "_orgId", "name")
|
|
19
32
|
VALUES ($1, $2, 'admin')
|
|
20
|
-
`, [roleId,
|
|
33
|
+
`, [roleId, metaOrg?._id ?? null]);
|
|
21
34
|
if (roleResult.rowCount === 0) {
|
|
22
35
|
await this.client.query('ROLLBACK');
|
|
23
36
|
return { success: false, error: new Error('Failed to create admin role') };
|
|
@@ -26,7 +39,7 @@ export class CreateAdminAuthorizationMigration {
|
|
|
26
39
|
const userRoleResult = await this.client.query(`
|
|
27
40
|
INSERT INTO "user_roles" ("_id", "_orgId", "_userId", "_roleId", "_created", "_createdBy", "_updated", "_updatedBy")
|
|
28
41
|
VALUES ($1, $2, $3, $4, NOW(), 'system', NOW(), 'system')
|
|
29
|
-
`, [userRoleId,
|
|
42
|
+
`, [userRoleId, metaOrg?._id ?? null, adminUser?._id, roleId]);
|
|
30
43
|
if (userRoleResult.rowCount === 0) {
|
|
31
44
|
await this.client.query('ROLLBACK');
|
|
32
45
|
return { success: false, error: new Error('Failed to create user role') };
|
|
@@ -35,7 +48,7 @@ export class CreateAdminAuthorizationMigration {
|
|
|
35
48
|
const featureResult = await this.client.query(`
|
|
36
49
|
INSERT INTO "features" ("_id", "_orgId", "name")
|
|
37
50
|
VALUES ($1, $2, 'admin')
|
|
38
|
-
`, [featureId,
|
|
51
|
+
`, [featureId, metaOrg?._id ?? null]);
|
|
39
52
|
if (featureResult.rowCount === 0) {
|
|
40
53
|
await this.client.query('ROLLBACK');
|
|
41
54
|
return { success: false, error: new Error('Failed to create admin feature') };
|
|
@@ -47,7 +60,7 @@ export class CreateAdminAuthorizationMigration {
|
|
|
47
60
|
"_created", "_createdBy", "_updated", "_updatedBy"
|
|
48
61
|
)
|
|
49
62
|
VALUES ($1, $2, $3, $4, NOW(), 'system', NOW(), 'system')
|
|
50
|
-
`, [authorizationId,
|
|
63
|
+
`, [authorizationId, metaOrg?._id ?? null, roleId, featureId]);
|
|
51
64
|
if (authorizationResult.rowCount === 0) {
|
|
52
65
|
await this.client.query('ROLLBACK');
|
|
53
66
|
return { success: false, error: new Error('Failed to create admin authorization') };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Client } from "pg";
|
|
2
|
+
import { IDatabaseBuilder } from "./database-builder.interface.js";
|
|
3
|
+
import { IMigration } from "./migration.interface.js";
|
|
4
|
+
export declare class DatabaseBuilder implements IDatabaseBuilder {
|
|
5
|
+
private client;
|
|
6
|
+
private migrationsToRun;
|
|
7
|
+
constructor(client: Client);
|
|
8
|
+
withAuth(): IDatabaseBuilder;
|
|
9
|
+
withMultitenant(): IDatabaseBuilder;
|
|
10
|
+
withMigrations(migrations: IMigration[]): IDatabaseBuilder;
|
|
11
|
+
build(): Promise<{
|
|
12
|
+
success: boolean;
|
|
13
|
+
error: Error | null;
|
|
14
|
+
}>;
|
|
15
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { IMigration } from "./migration.interface.js";
|
|
2
|
+
export interface IDatabaseBuilder {
|
|
3
|
+
withAuth(): IDatabaseBuilder;
|
|
4
|
+
withMultitenant(): IDatabaseBuilder;
|
|
5
|
+
withMigrations(migrations: IMigration[]): IDatabaseBuilder;
|
|
6
|
+
build(): Promise<{
|
|
7
|
+
success: boolean;
|
|
8
|
+
error: Error | null;
|
|
9
|
+
}>;
|
|
10
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { CreateMigrationTableMigration } from "./001-create-migrations-table.migration.js";
|
|
2
|
+
import { CreateUsersTableMigration } from "./003-create-users-table.migration.js";
|
|
3
|
+
import { CreateAdminUserMigration } from "./006-create-admin-user.migration.js";
|
|
4
|
+
import { CreateUserRolesTableMigration } from "./008-create-user-roles-table.migration.js";
|
|
5
|
+
import { CreateAuthorizationsTableMigration } from "./010-create-authorizations-table.migration.js";
|
|
6
|
+
import { CreateFeaturesTableMigration } from "./009-create-features-table.migration.js";
|
|
7
|
+
import { CreateRoleTableMigration } from "./007-create-roles-table.migration.js";
|
|
8
|
+
import { CreateRefreshTokenTableMigration } from "./004-create-refresh-tokens-table.migration.js";
|
|
9
|
+
import { CreateAdminAuthorizationMigration } from "./011-create-admin-authorization.migration.js";
|
|
10
|
+
import { CreateMetaOrgMigration } from "./005-create-meta-org.migration.js";
|
|
11
|
+
import { CreateOrganizationsTableMigration } from "./002-create-organizations-table.migration.js";
|
|
12
|
+
import { doesTableExist } from "../utils/index.js";
|
|
13
|
+
export class DatabaseBuilder {
|
|
14
|
+
client;
|
|
15
|
+
migrationsToRun = [];
|
|
16
|
+
constructor(client) {
|
|
17
|
+
this.client = client;
|
|
18
|
+
}
|
|
19
|
+
withAuth() {
|
|
20
|
+
this.migrationsToRun.push(new CreateMigrationTableMigration(this.client));
|
|
21
|
+
this.migrationsToRun.push(new CreateUsersTableMigration(this.client));
|
|
22
|
+
this.migrationsToRun.push(new CreateRefreshTokenTableMigration(this.client));
|
|
23
|
+
this.migrationsToRun.push(new CreateAdminUserMigration(this.client));
|
|
24
|
+
this.migrationsToRun.push(new CreateRoleTableMigration(this.client));
|
|
25
|
+
this.migrationsToRun.push(new CreateUserRolesTableMigration(this.client));
|
|
26
|
+
this.migrationsToRun.push(new CreateFeaturesTableMigration(this.client));
|
|
27
|
+
this.migrationsToRun.push(new CreateAuthorizationsTableMigration(this.client));
|
|
28
|
+
this.migrationsToRun.push(new CreateAdminAuthorizationMigration(this.client));
|
|
29
|
+
return this;
|
|
30
|
+
}
|
|
31
|
+
withMultitenant() {
|
|
32
|
+
this.migrationsToRun.push(new CreateMigrationTableMigration(this.client));
|
|
33
|
+
this.migrationsToRun.push(new CreateOrganizationsTableMigration(this.client));
|
|
34
|
+
this.migrationsToRun.push(new CreateMetaOrgMigration(this.client));
|
|
35
|
+
return this;
|
|
36
|
+
}
|
|
37
|
+
withMigrations(migrations) {
|
|
38
|
+
this.migrationsToRun.push(...migrations);
|
|
39
|
+
return this;
|
|
40
|
+
}
|
|
41
|
+
async build() {
|
|
42
|
+
let runMigrations = [];
|
|
43
|
+
if (await doesTableExist(this.client, 'migrations')) {
|
|
44
|
+
const migrations = await this.client.query(`
|
|
45
|
+
SELECT "_id", "index"
|
|
46
|
+
FROM migrations
|
|
47
|
+
WHERE "hasRun" = TRUE AND "reverted" = FALSE
|
|
48
|
+
`);
|
|
49
|
+
runMigrations = migrations.rows.map((row) => {
|
|
50
|
+
return row.index;
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
const orderedMigrations = this.migrationsToRun.filter((migration) => !runMigrations.includes(migration.index)).sort((migration) => migration.index);
|
|
54
|
+
for (const migration of orderedMigrations) {
|
|
55
|
+
const result = await migration.execute();
|
|
56
|
+
if (!result.success) {
|
|
57
|
+
return result;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return { success: true, error: null };
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { Client } from "pg";
|
|
2
|
-
export declare function setupDatabaseForAuth(client: Client
|
|
2
|
+
export declare function setupDatabaseForAuth(client: Client): Promise<{
|
|
3
3
|
success: boolean;
|
|
4
|
-
error: Error
|
|
4
|
+
error: Error;
|
|
5
|
+
} | {
|
|
6
|
+
success: boolean;
|
|
7
|
+
error: null;
|
|
5
8
|
}>;
|
|
@@ -8,7 +8,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
|
|
11
|
+
export async function setupDatabaseForAuth(client) {
|
|
12
12
|
let runMigrations = [];
|
|
13
13
|
if (await doesTableExist(client, 'migrations')) {
|
|
14
14
|
const migrations = await client.query(`
|
|
@@ -20,13 +20,12 @@ export async function setupDatabaseForAuth(client, adminEmail, adminPassword, me
|
|
|
20
20
|
return row.index;
|
|
21
21
|
});
|
|
22
22
|
}
|
|
23
|
-
let adminUserId;
|
|
24
23
|
if (!runMigrations.includes(1)) {
|
|
25
24
|
const migration = new CreateMigrationTableMigration(client);
|
|
26
25
|
const result = await migration.execute();
|
|
27
26
|
if (!result.success) {
|
|
28
27
|
console.error('setupDatabaseForAuth: error creating migrations table', result.error);
|
|
29
|
-
return
|
|
28
|
+
return result;
|
|
30
29
|
}
|
|
31
30
|
}
|
|
32
31
|
if (!runMigrations.includes(3)) {
|
|
@@ -34,7 +33,7 @@ export async function setupDatabaseForAuth(client, adminEmail, adminPassword, me
|
|
|
34
33
|
const result = await migration.execute();
|
|
35
34
|
if (!result.success) {
|
|
36
35
|
console.error('setupDatabaseForAuth: error creating users table', result.error);
|
|
37
|
-
return
|
|
36
|
+
return result;
|
|
38
37
|
}
|
|
39
38
|
}
|
|
40
39
|
if (!runMigrations.includes(4)) {
|
|
@@ -42,24 +41,23 @@ export async function setupDatabaseForAuth(client, adminEmail, adminPassword, me
|
|
|
42
41
|
const result = await migration.execute();
|
|
43
42
|
if (!result.success) {
|
|
44
43
|
console.error('setupDatabaseForAuth: error creating refresh_tokens table', result.error);
|
|
45
|
-
return
|
|
44
|
+
return result;
|
|
46
45
|
}
|
|
47
46
|
}
|
|
48
47
|
if (!runMigrations.includes(6)) {
|
|
49
48
|
const migration = new CreateAdminUserMigration(client);
|
|
50
|
-
const result = await migration.execute(
|
|
49
|
+
const result = await migration.execute();
|
|
51
50
|
if (!result.success) {
|
|
52
51
|
console.error('setupDatabaseForAuth: error creating admin user', result.error);
|
|
53
|
-
return
|
|
52
|
+
return result;
|
|
54
53
|
}
|
|
55
|
-
adminUserId = result.adminUserId;
|
|
56
54
|
}
|
|
57
55
|
if (!runMigrations.includes(7)) {
|
|
58
56
|
const migration = new CreateRoleTableMigration(client);
|
|
59
57
|
const result = await migration.execute();
|
|
60
58
|
if (!result.success) {
|
|
61
59
|
console.error('setupDatabaseForAuth: error creating roles table', result.error);
|
|
62
|
-
return
|
|
60
|
+
return result;
|
|
63
61
|
}
|
|
64
62
|
}
|
|
65
63
|
if (!runMigrations.includes(8)) {
|
|
@@ -67,7 +65,7 @@ export async function setupDatabaseForAuth(client, adminEmail, adminPassword, me
|
|
|
67
65
|
const result = await migration.execute();
|
|
68
66
|
if (!result.success) {
|
|
69
67
|
console.error('setupDatabaseForAuth: error creating user_roles table', result.error);
|
|
70
|
-
return
|
|
68
|
+
return result;
|
|
71
69
|
}
|
|
72
70
|
}
|
|
73
71
|
if (!runMigrations.includes(9)) {
|
|
@@ -75,7 +73,7 @@ export async function setupDatabaseForAuth(client, adminEmail, adminPassword, me
|
|
|
75
73
|
const result = await migration.execute();
|
|
76
74
|
if (!result.success) {
|
|
77
75
|
console.error('setupDatabaseForAuth: error creating features table', result.error);
|
|
78
|
-
return
|
|
76
|
+
return result;
|
|
79
77
|
}
|
|
80
78
|
}
|
|
81
79
|
if (!runMigrations.includes(10)) {
|
|
@@ -83,19 +81,15 @@ export async function setupDatabaseForAuth(client, adminEmail, adminPassword, me
|
|
|
83
81
|
const result = await migration.execute();
|
|
84
82
|
if (!result.success) {
|
|
85
83
|
console.error('setupDatabaseForAuth: error creating authorizations table', result.error);
|
|
86
|
-
return
|
|
84
|
+
return result;
|
|
87
85
|
}
|
|
88
86
|
}
|
|
89
87
|
if (!runMigrations.includes(11)) {
|
|
90
|
-
|
|
91
|
-
console.error('setupDatabaseForAuth: Admin user ID is required');
|
|
92
|
-
return { success: false, error: new Error('Admin user ID is required') };
|
|
93
|
-
}
|
|
94
|
-
const migration = new CreateAdminAuthorizationMigration(client, adminUserId, metaOrgId);
|
|
88
|
+
const migration = new CreateAdminAuthorizationMigration(client);
|
|
95
89
|
const result = await migration.execute();
|
|
96
90
|
if (!result.success) {
|
|
97
91
|
console.error('setupDatabaseForAuth: error creating admin authorization', result.error);
|
|
98
|
-
return
|
|
92
|
+
return result;
|
|
99
93
|
}
|
|
100
94
|
}
|
|
101
95
|
return { success: true, error: null };
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { Client } from "pg";
|
|
2
|
-
export declare function setupDatabaseForMultitenant(client: Client
|
|
2
|
+
export declare function setupDatabaseForMultitenant(client: Client): Promise<{
|
|
3
3
|
success: boolean;
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
error: Error;
|
|
5
|
+
} | {
|
|
6
|
+
success: boolean;
|
|
7
|
+
error: null;
|
|
6
8
|
}>;
|
|
@@ -2,9 +2,8 @@ import { CreateMigrationTableMigration } from "./001-create-migrations-table.mig
|
|
|
2
2
|
import { CreateOrganizationsTableMigration } from "./002-create-organizations-table.migration.js";
|
|
3
3
|
import { doesTableExist } from "../utils/does-table-exist.util.js";
|
|
4
4
|
import { CreateMetaOrgMigration } from "./005-create-meta-org.migration.js";
|
|
5
|
-
export async function setupDatabaseForMultitenant(client
|
|
5
|
+
export async function setupDatabaseForMultitenant(client) {
|
|
6
6
|
let runMigrations = [];
|
|
7
|
-
let metaOrgId;
|
|
8
7
|
if (await doesTableExist(client, 'migrations')) {
|
|
9
8
|
const migrations = await client.query(`
|
|
10
9
|
SELECT "_id", "index"
|
|
@@ -20,7 +19,7 @@ export async function setupDatabaseForMultitenant(client, metaOrgName, metaOrgCo
|
|
|
20
19
|
const result = await createMigrationTableMigration.execute();
|
|
21
20
|
if (!result.success) {
|
|
22
21
|
console.log('setupDatabaseForMultitenant: error creating migration table', result.error);
|
|
23
|
-
return
|
|
22
|
+
return result;
|
|
24
23
|
}
|
|
25
24
|
}
|
|
26
25
|
if (!runMigrations.includes(2)) {
|
|
@@ -28,17 +27,16 @@ export async function setupDatabaseForMultitenant(client, metaOrgName, metaOrgCo
|
|
|
28
27
|
const result = await createOrganizationTableMigration.execute();
|
|
29
28
|
if (!result.success) {
|
|
30
29
|
console.log('setupDatabaseForMultitenant: error creating organizations table', result.error);
|
|
31
|
-
return
|
|
30
|
+
return result;
|
|
32
31
|
}
|
|
33
32
|
}
|
|
34
33
|
if (!runMigrations.includes(5)) {
|
|
35
|
-
const createMetaOrgMigration = new CreateMetaOrgMigration(client
|
|
34
|
+
const createMetaOrgMigration = new CreateMetaOrgMigration(client);
|
|
36
35
|
const result = await createMetaOrgMigration.execute();
|
|
37
|
-
if (!result.success
|
|
36
|
+
if (!result.success) {
|
|
38
37
|
console.log('setupDatabaseForMultitenant: error creating meta org', result.error);
|
|
39
|
-
return
|
|
38
|
+
return result;
|
|
40
39
|
}
|
|
41
|
-
metaOrgId = result.metaOrgId;
|
|
42
40
|
}
|
|
43
|
-
return { success: true,
|
|
41
|
+
return { success: true, error: null };
|
|
44
42
|
}
|
|
@@ -17,6 +17,12 @@ export interface IBaseApiConfig {
|
|
|
17
17
|
};
|
|
18
18
|
app: {
|
|
19
19
|
isMultiTenant: boolean;
|
|
20
|
+
metaOrgName?: string;
|
|
21
|
+
metaOrgCode?: string;
|
|
22
|
+
};
|
|
23
|
+
adminUser?: {
|
|
24
|
+
email: string;
|
|
25
|
+
password: string;
|
|
20
26
|
};
|
|
21
27
|
auth: {
|
|
22
28
|
jwtExpirationInSeconds: number;
|
|
@@ -24,10 +30,10 @@ export interface IBaseApiConfig {
|
|
|
24
30
|
deviceIdCookieMaxAgeInDays: number;
|
|
25
31
|
passwordResetTokenExpirationInMinutes: number;
|
|
26
32
|
};
|
|
27
|
-
email
|
|
28
|
-
emailApiKey
|
|
29
|
-
emailApiSecret
|
|
30
|
-
fromAddress
|
|
31
|
-
systemEmailAddress
|
|
33
|
+
email?: {
|
|
34
|
+
emailApiKey: string;
|
|
35
|
+
emailApiSecret: string;
|
|
36
|
+
fromAddress: string;
|
|
37
|
+
systemEmailAddress: string;
|
|
32
38
|
};
|
|
33
39
|
}
|
|
@@ -5,16 +5,19 @@ export class EmailService {
|
|
|
5
5
|
mailjet;
|
|
6
6
|
constructor() {
|
|
7
7
|
this.mailjet = new Mailjet.default({
|
|
8
|
-
apiKey: config.email
|
|
9
|
-
apiSecret: config.email
|
|
8
|
+
apiKey: config.email?.emailApiKey || '',
|
|
9
|
+
apiSecret: config.email?.emailApiSecret || ''
|
|
10
10
|
});
|
|
11
11
|
}
|
|
12
12
|
async sendHtmlEmail(emailAddress, subject, body) {
|
|
13
|
+
if (!config.email?.fromAddress) {
|
|
14
|
+
throw new ServerError('From address is not set in the config');
|
|
15
|
+
}
|
|
13
16
|
const messageData = {
|
|
14
17
|
Messages: [
|
|
15
18
|
{
|
|
16
19
|
From: {
|
|
17
|
-
Email: config.email
|
|
20
|
+
Email: config.email?.fromAddress,
|
|
18
21
|
Name: config.appName || 'Application'
|
|
19
22
|
},
|
|
20
23
|
To: [
|