@loomcore/api 0.1.28 → 0.1.30
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 +1 -1
- package/dist/__tests__/common-test.utils.js +3 -3
- package/dist/config/base-api-config.js +1 -1
- 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.js +1 -1
- package/dist/databases/postgres/migrations/006-create-admin-user.migration.d.ts +1 -0
- package/dist/databases/postgres/migrations/006-create-admin-user.migration.js +4 -3
- package/dist/databases/postgres/migrations/007-create-roles-table.migration.js +5 -2
- package/dist/databases/postgres/migrations/008-create-user-roles-table.migration.js +5 -1
- package/dist/databases/postgres/migrations/009-create-features-table.migration.js +5 -2
- package/dist/databases/postgres/migrations/010-create-authorizations-table.migration.js +5 -2
- package/dist/databases/postgres/migrations/011-create-admin-authorization.migration.js +7 -7
- 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/index.d.ts +1 -2
- package/dist/databases/postgres/migrations/index.js +1 -2
- package/dist/models/base-api-config.interface.d.ts +5 -5
- package/dist/services/email.service.js +6 -3
- package/package.json +1 -1
|
@@ -25,7 +25,7 @@ export declare class CategoryService extends GenericApiService<ICategory> {
|
|
|
25
25
|
export declare class CategoryController extends ApiController<ICategory> {
|
|
26
26
|
constructor(app: Application, database: IDatabase);
|
|
27
27
|
}
|
|
28
|
-
export declare function setupTestConfig(): void;
|
|
28
|
+
export declare function setupTestConfig(isMultiTenant?: boolean): void;
|
|
29
29
|
export declare class ProductService extends GenericApiService<IProduct> {
|
|
30
30
|
private db;
|
|
31
31
|
constructor(database: IDatabase);
|
|
@@ -155,7 +155,7 @@ export class CategoryController extends ApiController {
|
|
|
155
155
|
super('categories', app, categoryService, 'category', CategorySpec);
|
|
156
156
|
}
|
|
157
157
|
}
|
|
158
|
-
export function setupTestConfig() {
|
|
158
|
+
export function setupTestConfig(isMultiTenant = true) {
|
|
159
159
|
setBaseApiConfig({
|
|
160
160
|
env: 'test',
|
|
161
161
|
hostName: 'localhost',
|
|
@@ -173,7 +173,7 @@ export function setupTestConfig() {
|
|
|
173
173
|
debug: {
|
|
174
174
|
showErrors: false
|
|
175
175
|
},
|
|
176
|
-
app: { isMultiTenant:
|
|
176
|
+
app: { isMultiTenant: isMultiTenant },
|
|
177
177
|
auth: {
|
|
178
178
|
jwtExpirationInSeconds: 3600,
|
|
179
179
|
refreshTokenExpirationInDays: 7,
|
|
@@ -183,7 +183,7 @@ export function setupTestConfig() {
|
|
|
183
183
|
email: {
|
|
184
184
|
emailApiKey: 'WeDontHaveAKeyYet',
|
|
185
185
|
emailApiSecret: 'WeDontHaveASecretYet',
|
|
186
|
-
fromAddress:
|
|
186
|
+
fromAddress: 'test@test.com',
|
|
187
187
|
systemEmailAddress: 'system@test.com'
|
|
188
188
|
},
|
|
189
189
|
adminUser: {
|
|
@@ -23,7 +23,7 @@ export async function initSystemUserContext(database) {
|
|
|
23
23
|
throw new Error('BaseApiConfig has not been set. Call setBaseApiConfig first.');
|
|
24
24
|
}
|
|
25
25
|
if (!isSystemUserContextSet) {
|
|
26
|
-
const systemEmail = config.email
|
|
26
|
+
const systemEmail = config.email?.systemEmailAddress || 'system@example.com';
|
|
27
27
|
let metaOrgId = undefined;
|
|
28
28
|
if (config.app.isMultiTenant) {
|
|
29
29
|
const { OrganizationService } = await import('../services/organization.service.js');
|
|
@@ -1,5 +1,6 @@
|
|
|
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 CreateUsersTableMigration {
|
|
4
5
|
client;
|
|
5
6
|
constructor(client) {
|
|
@@ -12,6 +13,9 @@ export class CreateUsersTableMigration {
|
|
|
12
13
|
await this.client.query('BEGIN');
|
|
13
14
|
const tableExists = await doesTableExist(this.client, 'users');
|
|
14
15
|
if (!tableExists) {
|
|
16
|
+
const fkConstraint = config.app.isMultiTenant
|
|
17
|
+
? ',\n CONSTRAINT "fk_users_organization" FOREIGN KEY ("_orgId") REFERENCES "organizations"("_id") ON DELETE CASCADE'
|
|
18
|
+
: '';
|
|
15
19
|
await this.client.query(`
|
|
16
20
|
CREATE TABLE "users" (
|
|
17
21
|
"_id" VARCHAR(255) PRIMARY KEY,
|
|
@@ -29,8 +33,7 @@ export class CreateUsersTableMigration {
|
|
|
29
33
|
"_updatedBy" VARCHAR(255) NOT NULL,
|
|
30
34
|
"_deleted" TIMESTAMP,
|
|
31
35
|
"_deletedBy" VARCHAR(255),
|
|
32
|
-
CONSTRAINT "
|
|
33
|
-
CONSTRAINT "uk_users_email" UNIQUE ("_orgId", "email")
|
|
36
|
+
CONSTRAINT "uk_users_email" UNIQUE ("_orgId", "email")${fkConstraint}
|
|
34
37
|
)
|
|
35
38
|
`);
|
|
36
39
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
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 CreateRefreshTokenTableMigration {
|
|
4
5
|
client;
|
|
5
6
|
constructor(client) {
|
|
@@ -12,6 +13,9 @@ export class CreateRefreshTokenTableMigration {
|
|
|
12
13
|
await this.client.query('BEGIN');
|
|
13
14
|
const tableExists = await doesTableExist(this.client, 'refreshTokens');
|
|
14
15
|
if (!tableExists) {
|
|
16
|
+
const fkConstraint = config.app.isMultiTenant
|
|
17
|
+
? ',\n CONSTRAINT "fk_refreshTokens_organization" FOREIGN KEY ("_orgId") REFERENCES "organizations"("_id") ON DELETE CASCADE'
|
|
18
|
+
: '';
|
|
15
19
|
await this.client.query(`
|
|
16
20
|
CREATE TABLE "refreshTokens" (
|
|
17
21
|
"_id" VARCHAR(255) PRIMARY KEY,
|
|
@@ -21,7 +25,7 @@ export class CreateRefreshTokenTableMigration {
|
|
|
21
25
|
"userId" VARCHAR(255) NOT NULL,
|
|
22
26
|
"expiresOn" BIGINT NOT NULL,
|
|
23
27
|
"created" TIMESTAMP NOT NULL,
|
|
24
|
-
"createdBy" VARCHAR(255) NOT NULL
|
|
28
|
+
"createdBy" VARCHAR(255) NOT NULL${fkConstraint}
|
|
25
29
|
)
|
|
26
30
|
`);
|
|
27
31
|
}
|
|
@@ -20,6 +20,7 @@ export class CreateMetaOrgMigration {
|
|
|
20
20
|
await this.client.query('ROLLBACK');
|
|
21
21
|
return { success: false, error: new Error(`Error creating meta org: No row returned`) };
|
|
22
22
|
}
|
|
23
|
+
initializeSystemUserContext(config.email?.systemEmailAddress || 'system@example.com', orgResult.rows[0]._id);
|
|
23
24
|
const migrationResult = await this.client.query(`
|
|
24
25
|
INSERT INTO "migrations" ("_id", "index", "hasRun", "reverted")
|
|
25
26
|
VALUES ('${_id}', ${this.index}, TRUE, FALSE);
|
|
@@ -28,7 +29,6 @@ export class CreateMetaOrgMigration {
|
|
|
28
29
|
await this.client.query('ROLLBACK');
|
|
29
30
|
return { success: false, error: new Error(`Error inserting migration ${this.index} to migrations table: No row returned`) };
|
|
30
31
|
}
|
|
31
|
-
initializeSystemUserContext(config.email.systemEmailAddress || 'system@example.com', orgResult.rows[0]._id);
|
|
32
32
|
await this.client.query('COMMIT');
|
|
33
33
|
return { success: true, error: null };
|
|
34
34
|
}
|
|
@@ -7,16 +7,17 @@ export class CreateAdminUserMigration {
|
|
|
7
7
|
client;
|
|
8
8
|
constructor(client) {
|
|
9
9
|
this.client = client;
|
|
10
|
+
const database = new PostgresDatabase(this.client);
|
|
11
|
+
this.authService = new AuthService(database);
|
|
10
12
|
}
|
|
13
|
+
authService;
|
|
11
14
|
index = 6;
|
|
12
15
|
async execute() {
|
|
13
16
|
const _id = randomUUID().toString();
|
|
14
17
|
const systemUserContext = getSystemUserContext();
|
|
15
18
|
let createdUser;
|
|
16
19
|
try {
|
|
17
|
-
|
|
18
|
-
const authService = new AuthService(database);
|
|
19
|
-
createdUser = await authService.createUser(systemUserContext, {
|
|
20
|
+
createdUser = await this.authService.createUser(systemUserContext, {
|
|
20
21
|
_id: _id,
|
|
21
22
|
_orgId: systemUserContext._orgId,
|
|
22
23
|
email: config.adminUser?.email,
|
|
@@ -1,5 +1,6 @@
|
|
|
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) {
|
|
@@ -12,13 +13,15 @@ export class CreateRoleTableMigration {
|
|
|
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,5 +1,6 @@
|
|
|
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) {
|
|
@@ -12,6 +13,9 @@ export class CreateUserRolesTableMigration {
|
|
|
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,5 +1,6 @@
|
|
|
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) {
|
|
@@ -12,13 +13,15 @@ export class CreateFeaturesTableMigration {
|
|
|
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,5 +1,6 @@
|
|
|
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) {
|
|
@@ -12,6 +13,9 @@ export class CreateAuthorizationsTableMigration {
|
|
|
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
|
}
|
|
@@ -17,20 +17,20 @@ export class CreateAdminAuthorizationMigration {
|
|
|
17
17
|
async execute() {
|
|
18
18
|
const _id = randomUUID().toString();
|
|
19
19
|
try {
|
|
20
|
-
const metaOrg = await this.organizationService.getMetaOrg(EmptyUserContext);
|
|
20
|
+
const metaOrg = await this.organizationService.getMetaOrg(EmptyUserContext).catch(() => null);
|
|
21
21
|
if (!config.adminUser?.email) {
|
|
22
22
|
return { success: false, error: new Error('Create admin authorization: Admin user email not found in config') };
|
|
23
23
|
}
|
|
24
24
|
const adminUser = await this.authService.getUserByEmail(config.adminUser?.email);
|
|
25
|
-
if (!
|
|
26
|
-
return { success: false, error: new Error('Create admin authorization:
|
|
25
|
+
if (!adminUser) {
|
|
26
|
+
return { success: false, error: new Error('Create admin authorization: Admin user not found') };
|
|
27
27
|
}
|
|
28
28
|
await this.client.query('BEGIN');
|
|
29
29
|
const roleId = randomUUID().toString();
|
|
30
30
|
const roleResult = await this.client.query(`
|
|
31
31
|
INSERT INTO "roles" ("_id", "_orgId", "name")
|
|
32
32
|
VALUES ($1, $2, 'admin')
|
|
33
|
-
`, [roleId, metaOrg?._id]);
|
|
33
|
+
`, [roleId, metaOrg?._id ?? null]);
|
|
34
34
|
if (roleResult.rowCount === 0) {
|
|
35
35
|
await this.client.query('ROLLBACK');
|
|
36
36
|
return { success: false, error: new Error('Failed to create admin role') };
|
|
@@ -39,7 +39,7 @@ export class CreateAdminAuthorizationMigration {
|
|
|
39
39
|
const userRoleResult = await this.client.query(`
|
|
40
40
|
INSERT INTO "user_roles" ("_id", "_orgId", "_userId", "_roleId", "_created", "_createdBy", "_updated", "_updatedBy")
|
|
41
41
|
VALUES ($1, $2, $3, $4, NOW(), 'system', NOW(), 'system')
|
|
42
|
-
`, [userRoleId, metaOrg?._id, adminUser?._id, roleId]);
|
|
42
|
+
`, [userRoleId, metaOrg?._id ?? null, adminUser?._id, roleId]);
|
|
43
43
|
if (userRoleResult.rowCount === 0) {
|
|
44
44
|
await this.client.query('ROLLBACK');
|
|
45
45
|
return { success: false, error: new Error('Failed to create user role') };
|
|
@@ -48,7 +48,7 @@ export class CreateAdminAuthorizationMigration {
|
|
|
48
48
|
const featureResult = await this.client.query(`
|
|
49
49
|
INSERT INTO "features" ("_id", "_orgId", "name")
|
|
50
50
|
VALUES ($1, $2, 'admin')
|
|
51
|
-
`, [featureId, metaOrg?._id]);
|
|
51
|
+
`, [featureId, metaOrg?._id ?? null]);
|
|
52
52
|
if (featureResult.rowCount === 0) {
|
|
53
53
|
await this.client.query('ROLLBACK');
|
|
54
54
|
return { success: false, error: new Error('Failed to create admin feature') };
|
|
@@ -60,7 +60,7 @@ export class CreateAdminAuthorizationMigration {
|
|
|
60
60
|
"_created", "_createdBy", "_updated", "_updatedBy"
|
|
61
61
|
)
|
|
62
62
|
VALUES ($1, $2, $3, $4, NOW(), 'system', NOW(), 'system')
|
|
63
|
-
`, [authorizationId, metaOrg?._id, roleId, featureId]);
|
|
63
|
+
`, [authorizationId, metaOrg?._id ?? null, roleId, featureId]);
|
|
64
64
|
if (authorizationResult.rowCount === 0) {
|
|
65
65
|
await this.client.query('ROLLBACK');
|
|
66
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
|
+
}
|
|
@@ -30,10 +30,10 @@ export interface IBaseApiConfig {
|
|
|
30
30
|
deviceIdCookieMaxAgeInDays: number;
|
|
31
31
|
passwordResetTokenExpirationInMinutes: number;
|
|
32
32
|
};
|
|
33
|
-
email
|
|
34
|
-
emailApiKey
|
|
35
|
-
emailApiSecret
|
|
36
|
-
fromAddress
|
|
37
|
-
systemEmailAddress
|
|
33
|
+
email?: {
|
|
34
|
+
emailApiKey: string;
|
|
35
|
+
emailApiSecret: string;
|
|
36
|
+
fromAddress: string;
|
|
37
|
+
systemEmailAddress: string;
|
|
38
38
|
};
|
|
39
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: [
|