diff --git a/api/src/config/index.ts b/api/src/config/index.ts index 9a9936b6..abba4b5a 100644 --- a/api/src/config/index.ts +++ b/api/src/config/index.ts @@ -73,7 +73,7 @@ export const config: Config = { grant3rdPartyCookie: true, onlyAllowOrigins: process.env.FRONTEND_ORIGIN ? process.env.FRONTEND_ORIGIN.split(',').map((origin) => origin.trim()) - : [undefined], // ['http://example.com', 'https://example.com'], + : [], // ['http://example.com', 'https://example.com'], }, session: { secret: process.env.SESSION_SECRET || 'changeme', @@ -91,7 +91,9 @@ export const config: Config = { emails: { isEnabled: process.env.EMAIL_SMTP_ENABLED === 'true' || false, smtp: { - port: parseInt(process.env.EMAIL_SMTP_PORT) || 25, + port: process.env.EMAIL_SMTP_PORT + ? parseInt(process.env.EMAIL_SMTP_PORT) + : 25, host: process.env.EMAIL_SMTP_HOST || 'localhost', ignoreTLS: false, secure: process.env.EMAIL_SMTP_SECURE === 'true' || false, diff --git a/api/src/i18n/controllers/language.controller.spec.ts b/api/src/i18n/controllers/language.controller.spec.ts index dcaa9a7f..d260a502 100644 --- a/api/src/i18n/controllers/language.controller.spec.ts +++ b/api/src/i18n/controllers/language.controller.spec.ts @@ -69,7 +69,7 @@ describe('LanguageController', () => { }).compile(); languageService = module.get(LanguageService); languageController = module.get(LanguageController); - language = await languageService.findOne({ code: 'en' }); + language = (await languageService.findOne({ code: 'en' })) as Language; }); afterEach(jest.clearAllMocks); @@ -92,7 +92,7 @@ describe('LanguageController', () => { expect(languageService.findOne).toHaveBeenCalledWith(language.id); expect(result).toEqualPayload( - languageFixtures.find(({ code }) => code === language.code), + languageFixtures.find(({ code }) => code === language.code) as Language, ); }); }); @@ -142,7 +142,9 @@ describe('LanguageController', () => { it('should mark a language as default', async () => { jest.spyOn(languageService, 'updateOne'); const translationUpdateDto = { isDefault: true }; - const frLang = await languageService.findOne({ code: 'fr' }); + const frLang = (await languageService.findOne({ + code: 'fr', + })) as Language; const result = await languageController.updateOne( frLang.id, translationUpdateDto, @@ -157,7 +159,9 @@ describe('LanguageController', () => { ...translationUpdateDto, }); - const enLang = await languageService.findOne({ code: 'en' }); + const enLang = (await languageService.findOne({ + code: 'en', + })) as Language; expect(enLang.isDefault).toBe(false); }); @@ -171,7 +175,9 @@ describe('LanguageController', () => { describe('deleteOne', () => { it('should throw when attempting to delete the default language', async () => { - const defaultLang = await languageService.findOne({ isDefault: true }); + const defaultLang = (await languageService.findOne({ + isDefault: true, + })) as Language; await expect( languageController.deleteOne(defaultLang.id), diff --git a/api/src/i18n/controllers/translation.controller.spec.ts b/api/src/i18n/controllers/translation.controller.spec.ts index 87e56d6f..484c90a2 100644 --- a/api/src/i18n/controllers/translation.controller.spec.ts +++ b/api/src/i18n/controllers/translation.controller.spec.ts @@ -141,7 +141,9 @@ describe('TranslationController', () => { translationController = module.get( TranslationController, ); - translation = await translationService.findOne({ str: 'Welcome' }); + translation = (await translationService.findOne({ + str: 'Welcome', + })) as Translation; }); afterEach(jest.clearAllMocks); @@ -164,7 +166,9 @@ describe('TranslationController', () => { expect(translationService.findOne).toHaveBeenCalledWith(translation.id); expect(result).toEqualPayload( - translationFixtures.find(({ str }) => str === translation.str), + translationFixtures.find( + ({ str }) => str === translation.str, + ) as Translation, ); }); }); diff --git a/api/src/i18n/i18n.module.ts b/api/src/i18n/i18n.module.ts index 8ad4238f..e8b15e1e 100644 --- a/api/src/i18n/i18n.module.ts +++ b/api/src/i18n/i18n.module.ts @@ -11,6 +11,7 @@ import { forwardRef, Global, Inject, + InternalServerErrorException, Module, } from '@nestjs/common'; import { HttpAdapterHost } from '@nestjs/core'; @@ -54,6 +55,11 @@ export class I18nModule extends NativeI18nModule { static forRoot(options: I18nOptions): DynamicModule { const { imports, providers, controllers, exports } = super.forRoot(options); + if (!providers || !exports) { + throw new InternalServerErrorException( + 'I18n: Unable to find providers and/or exports forRoot()', + ); + } return { module: I18nModule, imports: (imports || []).concat([ diff --git a/api/src/i18n/services/i18n.service.ts b/api/src/i18n/services/i18n.service.ts index 9e5b93aa..39fd0fd0 100644 --- a/api/src/i18n/services/i18n.service.ts +++ b/api/src/i18n/services/i18n.service.ts @@ -6,7 +6,7 @@ * 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file). */ -import { Injectable } from '@nestjs/common'; +import { Injectable, InternalServerErrorException } from '@nestjs/common'; import { I18nService as NativeI18nService, Path, @@ -34,6 +34,11 @@ export class I18nService< ...options, }; let { lang } = options; + + if (!lang) { + throw new InternalServerErrorException('I18nService: lang is undefined'); + } + lang = this.resolveLanguage(lang); // Translate block message, button text, ... diff --git a/api/src/i18n/services/language.service.ts b/api/src/i18n/services/language.service.ts index 4b532101..7fbeeb70 100644 --- a/api/src/i18n/services/language.service.ts +++ b/api/src/i18n/services/language.service.ts @@ -7,7 +7,11 @@ */ import { CACHE_MANAGER } from '@nestjs/cache-manager'; -import { Inject, Injectable } from '@nestjs/common'; +import { + Inject, + Injectable, + InternalServerErrorException, +} from '@nestjs/common'; import { Cache } from 'cache-manager'; import { @@ -53,7 +57,13 @@ export class LanguageService extends BaseService { */ @Cacheable(DEFAULT_LANGUAGE_CACHE_KEY) async getDefaultLanguage() { - return await this.findOne({ isDefault: true }); + const defaultLanguage = await this.findOne({ isDefault: true }); + if (!defaultLanguage) { + throw new InternalServerErrorException( + 'Default language not found: getDefaultLanguage()', + ); + } + return defaultLanguage; } /** diff --git a/api/src/i18n/services/translation.service.ts b/api/src/i18n/services/translation.service.ts index a5770794..117411c6 100644 --- a/api/src/i18n/services/translation.service.ts +++ b/api/src/i18n/services/translation.service.ts @@ -100,6 +100,7 @@ export class TranslationService extends BaseService { } // Add fallback messages if ( + block.options && 'fallback' in block.options && block.options.fallback && 'message' in block.options.fallback && diff --git a/api/src/user/controllers/auth.controller.spec.ts b/api/src/user/controllers/auth.controller.spec.ts index 706de31f..5b3ca462 100644 --- a/api/src/user/controllers/auth.controller.spec.ts +++ b/api/src/user/controllers/auth.controller.spec.ts @@ -28,6 +28,7 @@ import { I18nService } from '@/i18n/services/i18n.service'; import { LanguageService } from '@/i18n/services/language.service'; import { LoggerService } from '@/logger/logger.service'; import { getRandom } from '@/utils/helpers/safeRandom'; +import { installLanguageFixtures } from '@/utils/test/fixtures/language'; import { installUserFixtures } from '@/utils/test/fixtures/user'; import { closeInMongodConnection, @@ -43,7 +44,7 @@ import { RoleRepository } from '../repositories/role.repository'; import { UserRepository } from '../repositories/user.repository'; import { InvitationModel } from '../schemas/invitation.schema'; import { PermissionModel } from '../schemas/permission.schema'; -import { RoleModel, Role } from '../schemas/role.schema'; +import { Role, RoleModel } from '../schemas/role.schema'; import { UserModel } from '../schemas/user.schema'; import { InvitationService } from '../services/invitation.service'; import { PermissionService } from '../services/permission.service'; @@ -66,7 +67,10 @@ describe('AuthController', () => { const module: TestingModule = await Test.createTestingModule({ controllers: [LocalAuthController], imports: [ - rootMongooseTestModule(installUserFixtures), + rootMongooseTestModule(async () => { + await installLanguageFixtures(); + await installUserFixtures(); + }), MongooseModule.forFeature([ UserModel, RoleModel, diff --git a/api/src/user/controllers/user.controller.spec.ts b/api/src/user/controllers/user.controller.spec.ts index d7b6504f..041d8e27 100644 --- a/api/src/user/controllers/user.controller.spec.ts +++ b/api/src/user/controllers/user.controller.spec.ts @@ -25,6 +25,7 @@ import { I18nService } from '@/i18n/services/i18n.service'; import { LanguageService } from '@/i18n/services/language.service'; import { LoggerService } from '@/logger/logger.service'; import { IGNORED_TEST_FIELDS } from '@/utils/test/constants'; +import { installLanguageFixtures } from '@/utils/test/fixtures/language'; import { installPermissionFixtures } from '@/utils/test/fixtures/permission'; import { getUserFixtures, userFixtures } from '@/utils/test/fixtures/user'; import { getPageQuery } from '@/utils/test/pagination'; @@ -45,8 +46,8 @@ import { RoleRepository } from '../repositories/role.repository'; import { UserRepository } from '../repositories/user.repository'; import { InvitationModel } from '../schemas/invitation.schema'; import { PermissionModel } from '../schemas/permission.schema'; -import { RoleModel, Role } from '../schemas/role.schema'; -import { UserModel, User } from '../schemas/user.schema'; +import { Role, RoleModel } from '../schemas/role.schema'; +import { User, UserModel } from '../schemas/user.schema'; import { PasswordResetService } from '../services/passwordReset.service'; import { PermissionService } from '../services/permission.service'; import { RoleService } from '../services/role.service'; @@ -71,7 +72,10 @@ describe('UserController', () => { const module: TestingModule = await Test.createTestingModule({ controllers: [ReadWriteUserController], imports: [ - rootMongooseTestModule(installPermissionFixtures), + rootMongooseTestModule(async () => { + await installLanguageFixtures(); + await installPermissionFixtures(); + }), MongooseModule.forFeature([ UserModel, RoleModel, diff --git a/api/src/user/services/invitation.service.spec.ts b/api/src/user/services/invitation.service.spec.ts index e15a2c11..003f605a 100644 --- a/api/src/user/services/invitation.service.spec.ts +++ b/api/src/user/services/invitation.service.spec.ts @@ -25,6 +25,7 @@ import { installInvitationFixtures, invitationsFixtures, } from '@/utils/test/fixtures/invitation'; +import { installLanguageFixtures } from '@/utils/test/fixtures/language'; import { closeInMongodConnection, rootMongooseTestModule, @@ -53,7 +54,10 @@ describe('InvitationService', () => { beforeAll(async () => { const module: TestingModule = await Test.createTestingModule({ imports: [ - rootMongooseTestModule(installInvitationFixtures), + rootMongooseTestModule(async () => { + await installLanguageFixtures(); + await installInvitationFixtures(); + }), MongooseModule.forFeature([ RoleModel, PermissionModel, diff --git a/api/src/user/services/passwordReset.service.spec.ts b/api/src/user/services/passwordReset.service.spec.ts index 0ab45ca3..cdef472a 100644 --- a/api/src/user/services/passwordReset.service.spec.ts +++ b/api/src/user/services/passwordReset.service.spec.ts @@ -25,6 +25,7 @@ import { LanguageModel } from '@/i18n/schemas/language.schema'; import { I18nService } from '@/i18n/services/i18n.service'; import { LanguageService } from '@/i18n/services/language.service'; import { LoggerService } from '@/logger/logger.service'; +import { installLanguageFixtures } from '@/utils/test/fixtures/language'; import { installUserFixtures, users } from '@/utils/test/fixtures/user'; import { closeInMongodConnection, @@ -49,7 +50,10 @@ describe('PasswordResetService', () => { beforeAll(async () => { const module: TestingModule = await Test.createTestingModule({ imports: [ - rootMongooseTestModule(installUserFixtures), + rootMongooseTestModule(async () => { + await installLanguageFixtures(); + await installUserFixtures(); + }), MongooseModule.forFeature([ UserModel, RoleModel, diff --git a/api/src/user/services/validate-account.service.spec.ts b/api/src/user/services/validate-account.service.spec.ts index b3ca7753..ac3b2ae9 100644 --- a/api/src/user/services/validate-account.service.spec.ts +++ b/api/src/user/services/validate-account.service.spec.ts @@ -22,6 +22,7 @@ import { LanguageModel } from '@/i18n/schemas/language.schema'; import { I18nService } from '@/i18n/services/i18n.service'; import { LanguageService } from '@/i18n/services/language.service'; import { LoggerService } from '@/logger/logger.service'; +import { installLanguageFixtures } from '@/utils/test/fixtures/language'; import { installUserFixtures, users } from '@/utils/test/fixtures/user'; import { closeInMongodConnection, @@ -44,7 +45,10 @@ describe('ValidateAccountService', () => { beforeAll(async () => { const module: TestingModule = await Test.createTestingModule({ imports: [ - rootMongooseTestModule(installUserFixtures), + rootMongooseTestModule(async () => { + await installLanguageFixtures(); + await installUserFixtures(); + }), MongooseModule.forFeature([ UserModel, RoleModel, diff --git a/api/src/utils/test/fixtures/language.ts b/api/src/utils/test/fixtures/language.ts index 8b2f2e44..57d64958 100644 --- a/api/src/utils/test/fixtures/language.ts +++ b/api/src/utils/test/fixtures/language.ts @@ -8,10 +8,10 @@ import mongoose from 'mongoose'; -import { LanguageUpdateDto } from '@/i18n/dto/language.dto'; +import { LanguageCreateDto } from '@/i18n/dto/language.dto'; import { LanguageModel } from '@/i18n/schemas/language.schema'; -export const languageFixtures: LanguageUpdateDto[] = [ +export const languageFixtures: LanguageCreateDto[] = [ { title: 'English', code: 'en',