@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.
Files changed (22) hide show
  1. package/dist/__tests__/common-test.utils.d.ts +1 -1
  2. package/dist/__tests__/common-test.utils.js +3 -3
  3. package/dist/config/base-api-config.js +1 -1
  4. package/dist/databases/postgres/migrations/003-create-users-table.migration.js +5 -2
  5. package/dist/databases/postgres/migrations/004-create-refresh-tokens-table.migration.js +5 -1
  6. package/dist/databases/postgres/migrations/005-create-meta-org.migration.js +1 -1
  7. package/dist/databases/postgres/migrations/006-create-admin-user.migration.d.ts +1 -0
  8. package/dist/databases/postgres/migrations/006-create-admin-user.migration.js +4 -3
  9. package/dist/databases/postgres/migrations/007-create-roles-table.migration.js +5 -2
  10. package/dist/databases/postgres/migrations/008-create-user-roles-table.migration.js +5 -1
  11. package/dist/databases/postgres/migrations/009-create-features-table.migration.js +5 -2
  12. package/dist/databases/postgres/migrations/010-create-authorizations-table.migration.js +5 -2
  13. package/dist/databases/postgres/migrations/011-create-admin-authorization.migration.js +7 -7
  14. package/dist/databases/postgres/migrations/database-builder.d.ts +15 -0
  15. package/dist/databases/postgres/migrations/database-builder.interface.d.ts +10 -0
  16. package/dist/databases/postgres/migrations/database-builder.interface.js +1 -0
  17. package/dist/databases/postgres/migrations/database-builder.js +62 -0
  18. package/dist/databases/postgres/migrations/index.d.ts +1 -2
  19. package/dist/databases/postgres/migrations/index.js +1 -2
  20. package/dist/models/base-api-config.interface.d.ts +5 -5
  21. package/dist/services/email.service.js +6 -3
  22. 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: true },
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: undefined,
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.systemEmailAddress || 'system@example.com';
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 "fk_users_organization" FOREIGN KEY ("_orgId") REFERENCES "organizations"("_id") ON DELETE CASCADE,
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
  }
@@ -3,6 +3,7 @@ import { IMigration } from "../index.js";
3
3
  export declare class CreateAdminUserMigration implements IMigration {
4
4
  private readonly client;
5
5
  constructor(client: Client);
6
+ private authService;
6
7
  index: number;
7
8
  execute(): Promise<{
8
9
  success: boolean;
@@ -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
- const database = new PostgresDatabase(this.client);
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 "fk_roles_organization" FOREIGN KEY ("_orgId") REFERENCES "organizations"("_id") ON DELETE CASCADE,
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
- CONSTRAINT "fk_user_roles_organization" FOREIGN KEY ("_orgId") REFERENCES "organizations"("_id") ON DELETE CASCADE,
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 "fk_features_organization" FOREIGN KEY ("_orgId") REFERENCES "organizations"("_id") ON DELETE CASCADE,
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 (!metaOrg || !adminUser) {
26
- return { success: false, error: new Error('Create admin authorization: Meta organization or admin user not found') };
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,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,3 +1,2 @@
1
1
  export * from './migration.interface.js';
2
- export * from './setup-for-multitenant.migration.js';
3
- export * from './setup-for-auth.migration.js';
2
+ export * from './database-builder.js';
@@ -1,3 +1,2 @@
1
1
  export * from './migration.interface.js';
2
- export * from './setup-for-multitenant.migration.js';
3
- export * from './setup-for-auth.migration.js';
2
+ export * from './database-builder.js';
@@ -30,10 +30,10 @@ export interface IBaseApiConfig {
30
30
  deviceIdCookieMaxAgeInDays: number;
31
31
  passwordResetTokenExpirationInMinutes: number;
32
32
  };
33
- email: {
34
- emailApiKey?: string;
35
- emailApiSecret?: string;
36
- fromAddress?: string;
37
- systemEmailAddress?: string;
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.emailApiKey || '',
9
- apiSecret: config.email.emailApiSecret || ''
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.fromAddress,
20
+ Email: config.email?.fromAddress,
18
21
  Name: config.appName || 'Application'
19
22
  },
20
23
  To: [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@loomcore/api",
3
- "version": "0.1.28",
3
+ "version": "0.1.30",
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": {