@loomcore/api 0.1.22 → 0.1.24

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.
Files changed (29) hide show
  1. package/dist/__tests__/common-test.utils.js +13 -23
  2. package/dist/__tests__/models/test-item.model.d.ts +1 -1
  3. package/dist/__tests__/postgres-test-migrations/004-create-test-users-table.migration.js +0 -1
  4. package/dist/__tests__/postgres.test-database.js +6 -2
  5. package/dist/__tests__/test-objects.d.ts +9 -41
  6. package/dist/__tests__/test-objects.js +96 -63
  7. package/dist/controllers/auth.controller.js +2 -2
  8. package/dist/databases/postgres/migrations/001-create-migrations-table.migration.js +18 -20
  9. package/dist/databases/postgres/migrations/002-create-organizations-table.migration.d.ts +1 -3
  10. package/dist/databases/postgres/migrations/002-create-organizations-table.migration.js +41 -48
  11. package/dist/databases/postgres/migrations/003-create-users-table.migration.js +42 -47
  12. package/dist/databases/postgres/migrations/004-create-refresh-tokens-table.migration.js +34 -37
  13. package/dist/databases/postgres/migrations/005-create-meta-org.migration.js +24 -23
  14. package/dist/databases/postgres/migrations/006-create-admin-user.migration.d.ts +1 -2
  15. package/dist/databases/postgres/migrations/006-create-admin-user.migration.js +16 -7
  16. package/dist/databases/postgres/migrations/007-create-roles-table.migration.d.ts +21 -0
  17. package/dist/databases/postgres/migrations/007-create-roles-table.migration.js +62 -0
  18. package/dist/databases/postgres/migrations/008-create-user-roles-table.migration.d.ts +21 -0
  19. package/dist/databases/postgres/migrations/008-create-user-roles-table.migration.js +65 -0
  20. package/dist/databases/postgres/migrations/009-create-features-table.migration.d.ts +21 -0
  21. package/dist/databases/postgres/migrations/009-create-features-table.migration.js +62 -0
  22. package/dist/databases/postgres/migrations/010-create-authorizations-table.migration.d.ts +21 -0
  23. package/dist/databases/postgres/migrations/010-create-authorizations-table.migration.js +74 -0
  24. package/dist/databases/postgres/migrations/setup-for-auth.migration.js +14 -5
  25. package/dist/databases/postgres/migrations/setup-for-multitenant.migration.d.ts +1 -1
  26. package/dist/databases/postgres/migrations/setup-for-multitenant.migration.js +16 -6
  27. package/dist/models/refresh-token.model.d.ts +1 -1
  28. package/dist/services/auth.service.js +0 -3
  29. package/package.json +6 -6
@@ -1,4 +1,5 @@
1
1
  import { randomUUID } from "crypto";
2
+ import { doesTableExist } from "../utils/does-table-exist.util.js";
2
3
  export class CreateUsersTableMigration {
3
4
  client;
4
5
  constructor(client) {
@@ -8,72 +9,66 @@ export class CreateUsersTableMigration {
8
9
  async execute(_orgId) {
9
10
  const _id = randomUUID().toString();
10
11
  try {
11
- await this.client.query(`
12
- CREATE TABLE "users" (
13
- "_id" VARCHAR(255) PRIMARY KEY,
14
- "_orgId" VARCHAR(255),
15
- "email" VARCHAR(255) NOT NULL,
16
- "firstName" VARCHAR(255),
17
- "lastName" VARCHAR(255),
18
- "displayName" VARCHAR(255),
19
- "password" VARCHAR(255) NOT NULL,
20
- "roles" TEXT[],
21
- "_lastLoggedIn" TIMESTAMP,
22
- "_lastPasswordChange" TIMESTAMP,
23
- "_created" TIMESTAMP NOT NULL,
24
- "_createdBy" VARCHAR(255) NOT NULL,
25
- "_updated" TIMESTAMP NOT NULL,
26
- "_updatedBy" VARCHAR(255) NOT NULL,
27
- "_deleted" TIMESTAMP,
28
- "_deletedBy" VARCHAR(255)
29
- )
30
- `);
31
- }
32
- catch (error) {
33
- if (error.code === '42P07' || error.data?.error?.includes('already exists')) {
34
- console.log(`Users table already exists`);
35
- }
36
- else {
37
- return { success: false, error: new Error(`Error creating users table: ${error.message}`) };
12
+ await this.client.query('BEGIN');
13
+ const tableExists = await doesTableExist(this.client, 'users');
14
+ if (!tableExists) {
15
+ await this.client.query(`
16
+ CREATE TABLE "users" (
17
+ "_id" VARCHAR(255) PRIMARY KEY,
18
+ "_orgId" VARCHAR(255),
19
+ "email" VARCHAR(255) NOT NULL,
20
+ "firstName" VARCHAR(255),
21
+ "lastName" VARCHAR(255),
22
+ "displayName" VARCHAR(255),
23
+ "password" VARCHAR(255) NOT NULL,
24
+ "_lastLoggedIn" TIMESTAMP,
25
+ "_lastPasswordChange" TIMESTAMP,
26
+ "_created" TIMESTAMP NOT NULL,
27
+ "_createdBy" VARCHAR(255) NOT NULL,
28
+ "_updated" TIMESTAMP NOT NULL,
29
+ "_updatedBy" VARCHAR(255) NOT NULL,
30
+ "_deleted" TIMESTAMP,
31
+ "_deletedBy" VARCHAR(255),
32
+ CONSTRAINT "fk_users_organization" FOREIGN KEY ("_orgId") REFERENCES "organizations"("_id") ON DELETE CASCADE,
33
+ CONSTRAINT "uk_users_email" UNIQUE ("_orgId", "email")
34
+ )
35
+ `);
38
36
  }
39
- }
40
- try {
41
37
  const result = await this.client.query(`
42
- INSERT INTO "migrations" ("_id", "index", "hasRun", "reverted")
43
- VALUES ('${_id}', ${this.index}, TRUE, FALSE);
44
- `);
38
+ INSERT INTO "migrations" ("_id", "index", "hasRun", "reverted")
39
+ VALUES ('${_id}', ${this.index}, TRUE, FALSE);
40
+ `);
45
41
  if (result.rowCount === 0) {
42
+ await this.client.query('ROLLBACK');
46
43
  return { success: false, error: new Error(`Error inserting migration ${this.index} to migrations table: No row returned`) };
47
44
  }
45
+ await this.client.query('COMMIT');
46
+ return { success: true, error: null };
48
47
  }
49
48
  catch (error) {
50
- return { success: false, error: new Error(`Error inserting migration ${this.index} to migrations table: ${error.message}`) };
49
+ await this.client.query('ROLLBACK');
50
+ return { success: false, error: new Error(`Error executing migration ${this.index}: ${error.message}`) };
51
51
  }
52
- return { success: true, error: null };
53
52
  }
54
53
  async revert() {
55
54
  try {
56
- const result = await this.client.query(`
55
+ await this.client.query('BEGIN');
56
+ await this.client.query(`
57
57
  DROP TABLE "users";
58
58
  `);
59
- if (result.rowCount === 0) {
60
- return { success: false, error: new Error(`Error dropping users table: No row returned`) };
61
- }
62
- }
63
- catch (error) {
64
- return { success: false, error: new Error(`Error dropping users table: ${error.message}`) };
65
- }
66
- try {
67
- const result = await this.client.query(`
59
+ const updateResult = await this.client.query(`
68
60
  UPDATE "migrations" SET "reverted" = TRUE WHERE "index" = '${this.index}';
69
61
  `);
70
- if (result.rowCount === 0) {
62
+ if (updateResult.rowCount === 0) {
63
+ await this.client.query('ROLLBACK');
71
64
  return { success: false, error: new Error(`Error updating migration record for index ${this.index}: No row returned`) };
72
65
  }
66
+ await this.client.query('COMMIT');
67
+ return { success: true, error: null };
73
68
  }
74
69
  catch (error) {
75
- return { success: false, error: new Error(`Error updating migration record for index ${this.index}: ${error.message}`) };
70
+ await this.client.query('ROLLBACK');
71
+ return { success: false, error: new Error(`Error reverting migration ${this.index}: ${error.message}`) };
76
72
  }
77
- return { success: true, error: null };
78
73
  }
79
74
  }
@@ -1,4 +1,5 @@
1
1
  import { randomUUID } from "crypto";
2
+ import { doesTableExist } from "../utils/does-table-exist.util.js";
2
3
  export class CreateRefreshTokenTableMigration {
3
4
  client;
4
5
  constructor(client) {
@@ -8,64 +9,60 @@ export class CreateRefreshTokenTableMigration {
8
9
  async execute(_orgId) {
9
10
  const _id = randomUUID().toString();
10
11
  try {
11
- await this.client.query(`
12
- CREATE TABLE "refreshTokens" (
13
- "_id" VARCHAR(255) PRIMARY KEY,
14
- "_orgId" VARCHAR(255),
15
- "token" VARCHAR(255) NOT NULL,
16
- "deviceId" VARCHAR(255) NOT NULL,
17
- "userId" VARCHAR(255) NOT NULL,
18
- "expiresOn" BIGINT NOT NULL,
19
- "created" TIMESTAMP NOT NULL,
20
- "createdBy" VARCHAR(255) NOT NULL
21
- )
22
- `);
23
- }
24
- catch (error) {
25
- if (error.code === '42P07' || error.data?.error?.includes('already exists')) {
26
- console.log(`Refresh token table already exists`);
27
- }
28
- else {
29
- return { success: false, error: new Error(`Error creating refresh token table: ${error.message}`) };
12
+ await this.client.query('BEGIN');
13
+ const tableExists = await doesTableExist(this.client, 'refreshTokens');
14
+ if (!tableExists) {
15
+ await this.client.query(`
16
+ CREATE TABLE "refreshTokens" (
17
+ "_id" VARCHAR(255) PRIMARY KEY,
18
+ "_orgId" VARCHAR(255),
19
+ "token" VARCHAR(255) NOT NULL,
20
+ "deviceId" VARCHAR(255) NOT NULL,
21
+ "userId" VARCHAR(255) NOT NULL,
22
+ "expiresOn" BIGINT NOT NULL,
23
+ "created" TIMESTAMP NOT NULL,
24
+ "createdBy" VARCHAR(255) NOT NULL
25
+ )
26
+ `);
30
27
  }
31
- }
32
- try {
33
28
  const result = await this.client.query(`
34
29
  INSERT INTO "migrations" ("_id", "index", "hasRun", "reverted")
35
30
  VALUES ('${_id}', ${this.index}, TRUE, FALSE);
36
31
  `);
37
32
  if (result.rowCount === 0) {
33
+ await this.client.query('ROLLBACK');
38
34
  return { success: false, error: new Error(`Error inserting migration ${this.index} to migrations table: No row returned`) };
39
35
  }
36
+ await this.client.query('COMMIT');
37
+ return { success: true, error: null };
40
38
  }
41
39
  catch (error) {
42
- return { success: false, error: new Error(`Error inserting migration ${this.index} to migrations table: ${error.message}`) };
40
+ await this.client.query('ROLLBACK');
41
+ return { success: false, error: new Error(`Error executing migration ${this.index}: ${error.message}`) };
43
42
  }
44
- return { success: true, error: null };
45
43
  }
46
44
  async revert() {
47
45
  try {
48
- const result = await this.client.query(`
49
- DROP TABLE "refreshTokens";
50
- `);
51
- if (result.rowCount === 0) {
52
- return { success: false, error: new Error(`Error dropping refresh token table: No row returned`) };
46
+ await this.client.query('BEGIN');
47
+ const tableExists = await doesTableExist(this.client, 'refreshTokens');
48
+ if (tableExists) {
49
+ await this.client.query(`
50
+ DROP TABLE "refreshTokens";
51
+ `);
53
52
  }
54
- }
55
- catch (error) {
56
- return { success: false, error: new Error(`Error dropping refresh token table: ${error.message}`) };
57
- }
58
- try {
59
- const result = await this.client.query(`
53
+ const updateResult = await this.client.query(`
60
54
  UPDATE "migrations" SET "reverted" = TRUE WHERE "index" = '${this.index}';
61
55
  `);
62
- if (result.rowCount === 0) {
56
+ if (updateResult.rowCount === 0) {
57
+ await this.client.query('ROLLBACK');
63
58
  return { success: false, error: new Error(`Error updating migration record for index ${this.index}: No row returned`) };
64
59
  }
60
+ await this.client.query('COMMIT');
61
+ return { success: true, error: null };
65
62
  }
66
63
  catch (error) {
67
- return { success: false, error: new Error(`Error updating migration record: ${error.message}`) };
64
+ await this.client.query('ROLLBACK');
65
+ return { success: false, error: new Error(`Error reverting migration ${this.index}: ${error.message}`) };
68
66
  }
69
- return { success: true, error: null };
70
67
  }
71
68
  }
@@ -12,52 +12,53 @@ export class CreateMetaOrgMigration {
12
12
  async execute() {
13
13
  const _id = randomUUID().toString();
14
14
  try {
15
- const result = await this.client.query(`
15
+ await this.client.query('BEGIN');
16
+ const orgResult = await this.client.query(`
16
17
  INSERT INTO "organizations" ("_id", "name", "code", "status", "isMetaOrg", "_created", "_createdBy", "_updated", "_updatedBy")
17
- VALUES ('${_id}', '${this.orgName}', '${this.orgCode}', 1, true, NOW(), 'system', NOW(), 'system');`);
18
- if (result.rowCount === 0) {
18
+ VALUES ('${_id}', '${this.orgName}', '${this.orgCode}', 1, true, NOW(), 'system', NOW(), 'system');
19
+ `);
20
+ if (orgResult.rowCount === 0) {
21
+ await this.client.query('ROLLBACK');
19
22
  return { success: false, error: new Error(`Error creating meta org: No row returned`) };
20
23
  }
21
- }
22
- catch (error) {
23
- return { success: false, error: new Error(`Error creating meta org: ${error.message}`) };
24
- }
25
- try {
26
- const result = await this.client.query(`
24
+ const migrationResult = await this.client.query(`
27
25
  INSERT INTO "migrations" ("_id", "index", "hasRun", "reverted")
28
26
  VALUES ('${_id}', ${this.index}, TRUE, FALSE);
29
27
  `);
30
- if (result.rowCount === 0) {
28
+ if (migrationResult.rowCount === 0) {
29
+ await this.client.query('ROLLBACK');
31
30
  return { success: false, error: new Error(`Error inserting migration ${this.index} to migrations table: No row returned`) };
32
31
  }
32
+ await this.client.query('COMMIT');
33
+ return { success: true, metaOrgId: _id, error: null };
33
34
  }
34
35
  catch (error) {
35
- return { success: false, error: new Error(`Error inserting migration ${this.index} to migrations table: ${error.message}`) };
36
+ await this.client.query('ROLLBACK');
37
+ return { success: false, error: new Error(`Error executing migration ${this.index}: ${error.message}`) };
36
38
  }
37
- return { success: true, metaOrgId: _id, error: null };
38
39
  }
39
40
  async revert() {
40
41
  try {
41
- const result = await this.client.query(`DELETE FROM "organizations" WHERE "isMetaOrg" = TRUE;`);
42
- if (result.rowCount === 0) {
42
+ await this.client.query('BEGIN');
43
+ const deleteResult = await this.client.query(`DELETE FROM "organizations" WHERE "isMetaOrg" = TRUE;`);
44
+ if (deleteResult.rowCount === 0) {
45
+ await this.client.query('ROLLBACK');
43
46
  return { success: false, error: new Error(`Error reverting meta org: No row returned`) };
44
47
  }
45
- }
46
- catch (error) {
47
- return { success: false, error: new Error(`Error reverting meta org: ${error.message}`) };
48
- }
49
- try {
50
- const result = await this.client.query(`
48
+ const updateResult = await this.client.query(`
51
49
  UPDATE "migrations" SET "reverted" = TRUE WHERE "index" = '${this.index}';
52
50
  `);
53
- if (result.rowCount === 0) {
51
+ if (updateResult.rowCount === 0) {
52
+ await this.client.query('ROLLBACK');
54
53
  return { success: false, error: new Error(`Error updating migration record for index ${this.index}: No row returned`) };
55
54
  }
55
+ await this.client.query('COMMIT');
56
+ return { success: true, error: null };
56
57
  }
57
58
  catch (error) {
58
- return { success: false, error: new Error(`Error updating migration record for index ${this.index}: ${error.message}`) };
59
+ await this.client.query('ROLLBACK');
60
+ return { success: false, error: new Error(`Error reverting migration ${this.index}: ${error.message}`) };
59
61
  }
60
- return { success: true, error: null };
61
62
  }
62
63
  }
63
64
  export default CreateMetaOrgMigration;
@@ -4,8 +4,7 @@ export declare class CreateAdminUserMigration implements IMigration {
4
4
  private readonly client;
5
5
  private readonly adminEmail;
6
6
  private readonly adminPassword;
7
- private readonly _orgId?;
8
- constructor(client: Client, adminEmail: string, adminPassword: string, _orgId?: string | undefined);
7
+ constructor(client: Client, adminEmail: string, adminPassword: string);
9
8
  index: number;
10
9
  execute(): Promise<{
11
10
  success: boolean;
@@ -6,32 +6,41 @@ export class CreateAdminUserMigration {
6
6
  client;
7
7
  adminEmail;
8
8
  adminPassword;
9
- _orgId;
10
- constructor(client, adminEmail, adminPassword, _orgId) {
9
+ constructor(client, adminEmail, adminPassword) {
11
10
  this.client = client;
12
11
  this.adminEmail = adminEmail;
13
12
  this.adminPassword = adminPassword;
14
- this._orgId = _orgId;
15
13
  }
16
14
  index = 6;
17
15
  async execute() {
18
16
  const _id = randomUUID().toString();
17
+ const systemUserContext = getSystemUserContext();
19
18
  try {
20
19
  const database = new PostgresDatabase(this.client);
21
20
  const authService = new AuthService(database);
22
- const adminUser = await authService.createUser(getSystemUserContext(), {
21
+ const adminUser = await authService.createUser(systemUserContext, {
23
22
  _id: _id,
24
- _orgId: this._orgId,
23
+ _orgId: systemUserContext._orgId,
25
24
  email: this.adminEmail,
26
25
  password: this.adminPassword,
27
26
  firstName: 'Admin',
28
27
  lastName: 'User',
29
28
  displayName: 'Admin User',
30
- roles: ['admin'],
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
+ }],
31
38
  });
32
39
  }
33
40
  catch (error) {
34
- return { success: false, error: new Error(`Error creating admin user: ${error.message}`) };
41
+ return {
42
+ success: false, error: new Error(`Error creating admin user: ${error.message}`)
43
+ };
35
44
  }
36
45
  try {
37
46
  const result = await this.client.query(`
@@ -0,0 +1,21 @@
1
+ import { Client } from "pg";
2
+ import { IMigration } from "./migration.interface.js";
3
+ export declare class CreateRoleTableMigration implements IMigration {
4
+ private readonly client;
5
+ constructor(client: Client);
6
+ index: number;
7
+ execute(_orgId?: string): Promise<{
8
+ success: boolean;
9
+ error: Error;
10
+ } | {
11
+ success: boolean;
12
+ error: null;
13
+ }>;
14
+ revert(): Promise<{
15
+ success: boolean;
16
+ error: Error;
17
+ } | {
18
+ success: boolean;
19
+ error: null;
20
+ }>;
21
+ }
@@ -0,0 +1,62 @@
1
+ import { randomUUID } from "crypto";
2
+ import { doesTableExist } from "../utils/does-table-exist.util.js";
3
+ export class CreateRoleTableMigration {
4
+ client;
5
+ constructor(client) {
6
+ this.client = client;
7
+ }
8
+ index = 7;
9
+ async execute(_orgId) {
10
+ const _id = randomUUID().toString();
11
+ try {
12
+ await this.client.query('BEGIN');
13
+ const tableExists = await doesTableExist(this.client, 'roles');
14
+ if (!tableExists) {
15
+ await this.client.query(`
16
+ CREATE TABLE "roles" (
17
+ "_id" VARCHAR(255) PRIMARY KEY,
18
+ "_orgId" VARCHAR(255),
19
+ "name" VARCHAR(255) NOT NULL,
20
+ CONSTRAINT "fk_roles_organization" FOREIGN KEY ("_orgId") REFERENCES "organizations"("_id") ON DELETE CASCADE,
21
+ CONSTRAINT "uk_roles_name" UNIQUE ("_orgId", "name")
22
+ )
23
+ `);
24
+ }
25
+ const result = await this.client.query(`
26
+ INSERT INTO "migrations" ("_id", "index", "hasRun", "reverted")
27
+ VALUES ('${_id}', ${this.index}, TRUE, FALSE);
28
+ `);
29
+ if (result.rowCount === 0) {
30
+ await this.client.query('ROLLBACK');
31
+ return { success: false, error: new Error(`Error inserting migration ${this.index} to migrations table: No row returned`) };
32
+ }
33
+ await this.client.query('COMMIT');
34
+ return { success: true, error: null };
35
+ }
36
+ catch (error) {
37
+ await this.client.query('ROLLBACK');
38
+ return { success: false, error: new Error(`Error executing migration ${this.index}: ${error.message}`) };
39
+ }
40
+ }
41
+ async revert() {
42
+ try {
43
+ await this.client.query('BEGIN');
44
+ await this.client.query(`
45
+ DROP TABLE "roles";
46
+ `);
47
+ const updateResult = await this.client.query(`
48
+ UPDATE "migrations" SET "reverted" = TRUE WHERE "index" = '${this.index}';
49
+ `);
50
+ if (updateResult.rowCount === 0) {
51
+ await this.client.query('ROLLBACK');
52
+ return { success: false, error: new Error(`Error updating migration record for index ${this.index}: No row returned`) };
53
+ }
54
+ await this.client.query('COMMIT');
55
+ return { success: true, error: null };
56
+ }
57
+ catch (error) {
58
+ await this.client.query('ROLLBACK');
59
+ return { success: false, error: new Error(`Error reverting migration ${this.index}: ${error.message}`) };
60
+ }
61
+ }
62
+ }
@@ -0,0 +1,21 @@
1
+ import { Client } from "pg";
2
+ import { IMigration } from "./migration.interface.js";
3
+ export declare class CreateUserRolesTableMigration implements IMigration {
4
+ private readonly client;
5
+ constructor(client: Client);
6
+ index: number;
7
+ execute(_orgId?: string): Promise<{
8
+ success: boolean;
9
+ error: Error;
10
+ } | {
11
+ success: boolean;
12
+ error: null;
13
+ }>;
14
+ revert(): Promise<{
15
+ success: boolean;
16
+ error: Error;
17
+ } | {
18
+ success: boolean;
19
+ error: null;
20
+ }>;
21
+ }
@@ -0,0 +1,65 @@
1
+ import { randomUUID } from "crypto";
2
+ import { doesTableExist } from "../utils/does-table-exist.util.js";
3
+ export class CreateUserRolesTableMigration {
4
+ client;
5
+ constructor(client) {
6
+ this.client = client;
7
+ }
8
+ index = 8;
9
+ async execute(_orgId) {
10
+ const _id = randomUUID().toString();
11
+ try {
12
+ await this.client.query('BEGIN');
13
+ const tableExists = await doesTableExist(this.client, 'user_roles');
14
+ if (!tableExists) {
15
+ await this.client.query(`
16
+ CREATE TABLE "user_roles" (
17
+ "_id" VARCHAR(255) PRIMARY KEY,
18
+ "_orgId" VARCHAR(255),
19
+ "_userId" VARCHAR(255) NOT NULL,
20
+ "_roleId" VARCHAR(255) NOT NULL,
21
+ CONSTRAINT "fk_user_roles_organization" FOREIGN KEY ("_orgId") REFERENCES "organizations"("_id") ON DELETE CASCADE
22
+ CONSTRAINT "fk_user_roles_user" FOREIGN KEY ("_userId") REFERENCES "users"("_id") ON DELETE CASCADE,
23
+ CONSTRAINT "fk_user_roles_role" FOREIGN KEY ("_roleId") REFERENCES "roles"("_id") ON DELETE CASCADE,
24
+ CONSTRAINT "uk_user_roles" UNIQUE ("_orgId", "_userId", "_roleId")
25
+ )
26
+ `);
27
+ }
28
+ const result = await this.client.query(`
29
+ INSERT INTO "migrations" ("_id", "index", "hasRun", "reverted")
30
+ VALUES ('${_id}', ${this.index}, TRUE, FALSE);
31
+ `);
32
+ if (result.rowCount === 0) {
33
+ await this.client.query('ROLLBACK');
34
+ return { success: false, error: new Error(`Error inserting migration ${this.index} to migrations table: No row returned`) };
35
+ }
36
+ await this.client.query('COMMIT');
37
+ return { success: true, error: null };
38
+ }
39
+ catch (error) {
40
+ await this.client.query('ROLLBACK');
41
+ return { success: false, error: new Error(`Error executing migration ${this.index}: ${error.message}`) };
42
+ }
43
+ }
44
+ async revert() {
45
+ try {
46
+ await this.client.query('BEGIN');
47
+ await this.client.query(`
48
+ DROP TABLE "user_roles";
49
+ `);
50
+ const updateResult = await this.client.query(`
51
+ UPDATE "migrations" SET "reverted" = TRUE WHERE "index" = '${this.index}';
52
+ `);
53
+ if (updateResult.rowCount === 0) {
54
+ await this.client.query('ROLLBACK');
55
+ return { success: false, error: new Error(`Error updating migration record for index ${this.index}: No row returned`) };
56
+ }
57
+ await this.client.query('COMMIT');
58
+ return { success: true, error: null };
59
+ }
60
+ catch (error) {
61
+ await this.client.query('ROLLBACK');
62
+ return { success: false, error: new Error(`Error reverting migration ${this.index}: ${error.message}`) };
63
+ }
64
+ }
65
+ }
@@ -0,0 +1,21 @@
1
+ import { Client } from "pg";
2
+ import { IMigration } from "./migration.interface.js";
3
+ export declare class CreateFeaturesTableMigration implements IMigration {
4
+ private readonly client;
5
+ constructor(client: Client);
6
+ index: number;
7
+ execute(_orgId?: string): Promise<{
8
+ success: boolean;
9
+ error: Error;
10
+ } | {
11
+ success: boolean;
12
+ error: null;
13
+ }>;
14
+ revert(): Promise<{
15
+ success: boolean;
16
+ error: Error;
17
+ } | {
18
+ success: boolean;
19
+ error: null;
20
+ }>;
21
+ }
@@ -0,0 +1,62 @@
1
+ import { randomUUID } from "crypto";
2
+ import { doesTableExist } from "../utils/does-table-exist.util.js";
3
+ export class CreateFeaturesTableMigration {
4
+ client;
5
+ constructor(client) {
6
+ this.client = client;
7
+ }
8
+ index = 9;
9
+ async execute(_orgId) {
10
+ const _id = randomUUID().toString();
11
+ try {
12
+ await this.client.query('BEGIN');
13
+ const tableExists = await doesTableExist(this.client, 'features');
14
+ if (!tableExists) {
15
+ await this.client.query(`
16
+ CREATE TABLE "features" (
17
+ "_id" VARCHAR(255) PRIMARY KEY,
18
+ "_orgId" VARCHAR(255),
19
+ "name" VARCHAR(255) NOT NULL,
20
+ CONSTRAINT "fk_features_organization" FOREIGN KEY ("_orgId") REFERENCES "organizations"("_id") ON DELETE CASCADE,
21
+ CONSTRAINT "uk_features" UNIQUE ("_orgId", "name")
22
+ )
23
+ `);
24
+ }
25
+ const result = await this.client.query(`
26
+ INSERT INTO "migrations" ("_id", "index", "hasRun", "reverted")
27
+ VALUES ('${_id}', ${this.index}, TRUE, FALSE);
28
+ `);
29
+ if (result.rowCount === 0) {
30
+ await this.client.query('ROLLBACK');
31
+ return { success: false, error: new Error(`Error inserting migration ${this.index} to migrations table: No row returned`) };
32
+ }
33
+ await this.client.query('COMMIT');
34
+ return { success: true, error: null };
35
+ }
36
+ catch (error) {
37
+ await this.client.query('ROLLBACK');
38
+ return { success: false, error: new Error(`Error executing migration ${this.index}: ${error.message}`) };
39
+ }
40
+ }
41
+ async revert() {
42
+ try {
43
+ await this.client.query('BEGIN');
44
+ await this.client.query(`
45
+ DROP TABLE "features";
46
+ `);
47
+ const updateResult = await this.client.query(`
48
+ UPDATE "migrations" SET "reverted" = TRUE WHERE "index" = '${this.index}';
49
+ `);
50
+ if (updateResult.rowCount === 0) {
51
+ await this.client.query('ROLLBACK');
52
+ return { success: false, error: new Error(`Error updating migration record for index ${this.index}: No row returned`) };
53
+ }
54
+ await this.client.query('COMMIT');
55
+ return { success: true, error: null };
56
+ }
57
+ catch (error) {
58
+ await this.client.query('ROLLBACK');
59
+ return { success: false, error: new Error(`Error reverting migration ${this.index}: ${error.message}`) };
60
+ }
61
+ }
62
+ }
@@ -0,0 +1,21 @@
1
+ import { Client } from "pg";
2
+ import { IMigration } from "./migration.interface.js";
3
+ export declare class CreateAuthorizationsTableMigration implements IMigration {
4
+ private readonly client;
5
+ constructor(client: Client);
6
+ index: number;
7
+ execute(_orgId?: string): Promise<{
8
+ success: boolean;
9
+ error: Error;
10
+ } | {
11
+ success: boolean;
12
+ error: null;
13
+ }>;
14
+ revert(): Promise<{
15
+ success: boolean;
16
+ error: Error;
17
+ } | {
18
+ success: boolean;
19
+ error: null;
20
+ }>;
21
+ }