diff --git a/src/api-key/api-key.service.spec.ts b/src/api-key/api-key.service.spec.ts index af5e935d..2c913f38 100644 --- a/src/api-key/api-key.service.spec.ts +++ b/src/api-key/api-key.service.spec.ts @@ -2,7 +2,6 @@ import { Test, TestingModule } from '@nestjs/testing'; import { getRepositoryToken } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; -import { ForbiddenException } from '@nestjs/common'; import { APIKeyService } from './api-key.service'; import { APIKey, @@ -74,8 +73,10 @@ describe('APIKeyService', () => { t: jest.fn((key: string, options?: any) => { // Return mock translations for test purposes const translations: Record = { - 'apikey.errors.noPermissionForNamespace': 'User {{userId}} does not have permission to namespace {{namespaceId}}', - 'apikey.errors.noWritePermission': 'User {{userId}} does not have write permission to resource {{resourceId}} in namespace {{namespaceId}}', + 'apikey.errors.noPermissionForNamespace': + 'User {{userId}} does not have permission to namespace {{namespaceId}}', + 'apikey.errors.noWritePermission': + 'User {{userId}} does not have write permission to resource {{resourceId}} in namespace {{namespaceId}}', }; let translation = translations[key] || key; if (options?.args) { diff --git a/src/auth/api-key/api-key-auth.guard.spec.ts b/src/auth/api-key/api-key-auth.guard.spec.ts index aa970797..1640c2ff 100644 --- a/src/auth/api-key/api-key-auth.guard.spec.ts +++ b/src/auth/api-key/api-key-auth.guard.spec.ts @@ -1,9 +1,5 @@ import { Test, TestingModule } from '@nestjs/testing'; -import { - ExecutionContext, - UnauthorizedException, - ForbiddenException, -} from '@nestjs/common'; +import { ExecutionContext } from '@nestjs/common'; import { Reflector } from '@nestjs/core'; import { APIKeyAuthGuard } from 'omniboxd/auth'; import { APIKeyService } from 'omniboxd/api-key/api-key.service'; @@ -20,7 +16,6 @@ describe('APIKeyAuthGuard', () => { let guard: APIKeyAuthGuard; let apiKeyService: jest.Mocked; let reflector: jest.Mocked; - let i18nService: jest.Mocked; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ @@ -44,11 +39,14 @@ describe('APIKeyAuthGuard', () => { t: jest.fn((key: string) => { // Return mock translations for test purposes const translations: Record = { - 'apikey.errors.authorizationHeaderRequired': 'Authorization header is required', + 'apikey.errors.authorizationHeaderRequired': + 'Authorization header is required', 'apikey.errors.invalidApiKeyFormat': 'Invalid API key format', 'apikey.errors.invalidApiKey': 'Invalid API key', - 'apikey.errors.noPermissionForTarget': 'No permission for target {{target}}', - 'apikey.errors.noSpecificPermission': 'No {{permission}} permission for target {{target}}', + 'apikey.errors.noPermissionForTarget': + 'No permission for target {{target}}', + 'apikey.errors.noSpecificPermission': + 'No {{permission}} permission for target {{target}}', }; return translations[key] || key; }), @@ -60,7 +58,6 @@ describe('APIKeyAuthGuard', () => { guard = module.get(APIKeyAuthGuard); apiKeyService = module.get(APIKeyService); reflector = module.get(Reflector); - i18nService = module.get(I18nService); }); const createMockExecutionContext = ( @@ -111,9 +108,7 @@ describe('APIKeyAuthGuard', () => { .mockReturnValueOnce({ enabled: true }); // apiKeyAuthOptions = { enabled: true } const context = createMockExecutionContext(); - await expect(guard.canActivate(context)).rejects.toThrow( - AppException, - ); + await expect(guard.canActivate(context)).rejects.toThrow(AppException); }); it('should throw AppException when API key does not start with sk-', async () => { @@ -122,9 +117,7 @@ describe('APIKeyAuthGuard', () => { .mockReturnValueOnce({ enabled: true }); // apiKeyAuthOptions = { enabled: true } const context = createMockExecutionContext('Bearer invalid-key'); - await expect(guard.canActivate(context)).rejects.toThrow( - AppException, - ); + await expect(guard.canActivate(context)).rejects.toThrow(AppException); }); it('should throw AppException when API key is not found', async () => { @@ -134,9 +127,7 @@ describe('APIKeyAuthGuard', () => { const context = createMockExecutionContext('Bearer sk-validformat'); apiKeyService.findByValue.mockResolvedValue(null); - await expect(guard.canActivate(context)).rejects.toThrow( - AppException, - ); + await expect(guard.canActivate(context)).rejects.toThrow(AppException); }); it('should allow valid API key and set apiKey and user on request', async () => { @@ -245,9 +236,7 @@ describe('APIKeyAuthGuard', () => { const context = createMockExecutionContext('Bearer sk-validkey'); apiKeyService.findByValue.mockResolvedValue(mockApiKey); - await expect(guard.canActivate(context)).rejects.toThrow( - AppException, - ); + await expect(guard.canActivate(context)).rejects.toThrow(AppException); }); it('should throw AppException when API key lacks specific permission type', async () => { @@ -283,9 +272,7 @@ describe('APIKeyAuthGuard', () => { const context = createMockExecutionContext('Bearer sk-validkey'); apiKeyService.findByValue.mockResolvedValue(mockApiKey); - await expect(guard.canActivate(context)).rejects.toThrow( - AppException, - ); + await expect(guard.canActivate(context)).rejects.toThrow(AppException); }); it('should allow API key with multiple matching permissions', async () => { diff --git a/src/auth/api-key/api-key-auth.guard.ts b/src/auth/api-key/api-key-auth.guard.ts index fd0d58e1..18c35847 100644 --- a/src/auth/api-key/api-key-auth.guard.ts +++ b/src/auth/api-key/api-key-auth.guard.ts @@ -77,7 +77,7 @@ export class APIKeyAuthGuard implements CanActivate { apiKeyAuthOptions.permissions && apiKeyAuthOptions.permissions.length > 0 ) { - await this.validatePermissions( + this.validatePermissions( apiKey.attrs.permissions || [], apiKeyAuthOptions.permissions, ); @@ -90,10 +90,10 @@ export class APIKeyAuthGuard implements CanActivate { return true; } - private async validatePermissions( + private validatePermissions( apiKeyPermissions: APIKeyPermission[], requiredPermissions: APIKeyPermission[], - ): Promise { + ): void { for (const required of requiredPermissions) { const apiKeyPermission = apiKeyPermissions.find( (p) => p.target === required.target, diff --git a/src/auth/auth.module.ts b/src/auth/auth.module.ts index dc05b384..9a17df0a 100644 --- a/src/auth/auth.module.ts +++ b/src/auth/auth.module.ts @@ -23,6 +23,7 @@ import { SocialService } from 'omniboxd/auth/social.service'; import { APIKeyModule } from 'omniboxd/api-key/api-key.module'; import { APIKeyAuthGuard } from 'omniboxd/auth/api-key/api-key-auth.guard'; import { CookieAuthGuard } from 'omniboxd/auth/cookie/cookie-auth.guard'; +import { CacheService } from 'omniboxd/common/cache.service'; @Module({ exports: [AuthService, WechatService, GoogleService, SocialService], @@ -39,6 +40,7 @@ import { CookieAuthGuard } from 'omniboxd/auth/cookie/cookie-auth.guard'; GoogleService, JwtStrategy, LocalStrategy, + CacheService, { provide: APP_GUARD, useClass: JwtAuthGuard, diff --git a/src/auth/auth.service.ts b/src/auth/auth.service.ts index d13f13cf..ad0a40bf 100644 --- a/src/auth/auth.service.ts +++ b/src/auth/auth.service.ts @@ -288,7 +288,7 @@ export class AuthService { } } - async jwtVerify(token: string) { + jwtVerify(token: string) { if (!token) { const message = this.i18n.t('auth.errors.noToken'); throw new AppException(message, 'NO_TOKEN', HttpStatus.UNAUTHORIZED); diff --git a/src/auth/cookie/cookie-auth.guard.spec.ts b/src/auth/cookie/cookie-auth.guard.spec.ts index fc24f4f0..0adb8f0c 100644 --- a/src/auth/cookie/cookie-auth.guard.spec.ts +++ b/src/auth/cookie/cookie-auth.guard.spec.ts @@ -1,5 +1,5 @@ import { Test, TestingModule } from '@nestjs/testing'; -import { ExecutionContext, UnauthorizedException } from '@nestjs/common'; +import { ExecutionContext } from '@nestjs/common'; import { Reflector } from '@nestjs/core'; import { CookieAuthGuard } from 'omniboxd/auth/cookie/cookie-auth.guard'; import { AuthService } from 'omniboxd/auth/auth.service'; @@ -10,7 +10,6 @@ describe('CookieAuthGuard', () => { let guard: CookieAuthGuard; let reflector: jest.Mocked; let authService: jest.Mocked; - let i18nService: jest.Mocked; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ @@ -34,7 +33,8 @@ describe('CookieAuthGuard', () => { t: jest.fn((key: string) => { // Return mock translations for test purposes const translations: Record = { - 'auth.errors.tokenCookieRequired': 'Authentication token cookie is required', + 'auth.errors.tokenCookieRequired': + 'Authentication token cookie is required', 'auth.errors.tokenInvalid': 'Invalid or expired token', 'auth.errors.invalidTokenPayload': 'Invalid token payload', }; @@ -48,7 +48,6 @@ describe('CookieAuthGuard', () => { guard = module.get(CookieAuthGuard); reflector = module.get(Reflector); authService = module.get(AuthService); - i18nService = module.get(I18nService); }); const createMockExecutionContext = (cookies: any = {}): ExecutionContext => { @@ -94,9 +93,7 @@ describe('CookieAuthGuard', () => { .mockReturnValueOnce({ enabled: true }); // cookieAuthOptions with default onAuthFail = 'reject' const context = createMockExecutionContext(); - await expect(guard.canActivate(context)).rejects.toThrow( - AppException, - ); + await expect(guard.canActivate(context)).rejects.toThrow(AppException); }); it('should throw AppException when token is invalid', async () => { @@ -106,9 +103,7 @@ describe('CookieAuthGuard', () => { const context = createMockExecutionContext({ token: 'invalid-token' }); authService.jwtVerify.mockRejectedValue(new Error('Invalid token')); - await expect(guard.canActivate(context)).rejects.toThrow( - AppException, - ); + await expect(guard.canActivate(context)).rejects.toThrow(AppException); }); it('should throw AppException when token payload is invalid', async () => { @@ -118,9 +113,7 @@ describe('CookieAuthGuard', () => { const context = createMockExecutionContext({ token: 'valid-token' }); authService.jwtVerify.mockResolvedValue({}); // No sub field - await expect(guard.canActivate(context)).rejects.toThrow( - AppException, - ); + await expect(guard.canActivate(context)).rejects.toThrow(AppException); }); it('should successfully authenticate with valid token and set cookie auth data', async () => { diff --git a/src/auth/social.service.ts b/src/auth/social.service.ts index 9d06903e..326a7b9d 100644 --- a/src/auth/social.service.ts +++ b/src/auth/social.service.ts @@ -2,12 +2,10 @@ import { EntityManager } from 'typeorm'; import generateId from 'omniboxd/utils/generate-id'; import { UserService } from 'omniboxd/user/user.service'; import { WechatCheckResponseDto } from 'omniboxd/auth/dto/wechat-login.dto'; -import { HttpStatus, Inject, Injectable } from '@nestjs/common'; +import { HttpStatus, Injectable } from '@nestjs/common'; import { AppException } from 'omniboxd/common/exceptions/app.exception'; import { I18nService } from 'nestjs-i18n'; -import { Cache, CACHE_MANAGER } from '@nestjs/cache-manager'; -import { KVStore } from 'omniboxd/common/kv-store'; -import { ConfigService } from '@nestjs/config'; +import { CacheService } from 'omniboxd/common/cache.service'; export interface UserSocialState { type: string; @@ -18,22 +16,15 @@ export interface UserSocialState { @Injectable() export class SocialService { + private readonly namespace = '/social/states'; private readonly minUsernameLength = 2; private readonly maxUsernameLength = 32; - private readonly kvStore: KVStore; constructor( private readonly userService: UserService, private readonly i18n: I18nService, - @Inject(CACHE_MANAGER) private readonly cacheManager: Cache, - private readonly configService: ConfigService, - ) { - this.kvStore = new KVStore( - this.cacheManager, - '/social/states', - this.configService, - ); - } + private readonly cacheService: CacheService, + ) {} /** * Generate a kv state, return a kv key @@ -44,7 +35,8 @@ export class SocialService { async generateState(type: string, prefix: string = ''): Promise { const key = `${prefix ? prefix + '_' : ''}${generateId()}`; const expiresIn = 5 * 60 * 1000; // Expires in 5 minutes - await this.kvStore.set( + await this.cacheService.set( + this.namespace, key, { type, @@ -57,13 +49,18 @@ export class SocialService { } async getState(state: string) { - return await this.kvStore.get(state); + return await this.cacheService.get(this.namespace, state); } async updateState(state: string, data: UserSocialState) { const ttl = data.expiresIn - (Date.now() - data.createdAt); if (ttl > 0) { - await this.kvStore.set(state, data, ttl); + await this.cacheService.set( + this.namespace, + state, + data, + ttl, + ); } } diff --git a/src/common/cache.service.ts b/src/common/cache.service.ts new file mode 100644 index 00000000..2148e688 --- /dev/null +++ b/src/common/cache.service.ts @@ -0,0 +1,36 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { Cache, CACHE_MANAGER } from '@nestjs/cache-manager'; +import { ConfigService } from '@nestjs/config'; + +@Injectable() +export class CacheService { + private readonly env: string; + + constructor( + @Inject(CACHE_MANAGER) private readonly cacheManager: Cache, + private readonly configService: ConfigService, + ) { + this.env = this.configService.get('ENV', 'unknown'); + } + + private getKey(namespace: string, key: string): string { + return `/${this.env}${namespace}/${key}`; + } + + async get(namespace: string, key: string): Promise { + return await this.cacheManager.get(this.getKey(namespace, key)); + } + + async set( + namespace: string, + key: string, + value: T, + ttl?: number, + ): Promise { + await this.cacheManager.set(this.getKey(namespace, key), value, ttl); + } + + async delete(namespace: string, key: string): Promise { + await this.cacheManager.del(this.getKey(namespace, key)); + } +} diff --git a/src/common/kv-store.ts b/src/common/kv-store.ts deleted file mode 100644 index a1783d6d..00000000 --- a/src/common/kv-store.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { Inject, Injectable } from '@nestjs/common'; -import { Cache, CACHE_MANAGER } from '@nestjs/cache-manager'; -import { ConfigService } from '@nestjs/config'; - -@Injectable() -export class KVStore { - private readonly env: string; - - constructor( - @Inject(CACHE_MANAGER) private readonly cacheManager: Cache, - private readonly namespace: string, - private readonly configService: ConfigService, - ) { - this.env = this.configService.get('ENV', 'unknown'); - } - - private getKey(key: string): string { - return `/${this.env}${this.namespace}/${key}`; - } - - async get(key: string): Promise { - return await this.cacheManager.get(this.getKey(key)); - } - - async set(key: string, value: T, ttl?: number): Promise { - await this.cacheManager.set(this.getKey(key), value, ttl); - } - - async delete(key: string): Promise { - await this.cacheManager.del(this.getKey(key)); - } -} diff --git a/src/decorators/user-id.decorator.ts b/src/decorators/user-id.decorator.ts index 72e3e6f4..7b697d89 100644 --- a/src/decorators/user-id.decorator.ts +++ b/src/decorators/user-id.decorator.ts @@ -18,7 +18,11 @@ export const UserId = createParamDecorator( if (!userId && !optional) { // Note: Decorators cannot easily inject I18nService, using static message - throw new AppException('Not authorized', 'NOT_AUTHORIZED', HttpStatus.UNAUTHORIZED); + throw new AppException( + 'Not authorized', + 'NOT_AUTHORIZED', + HttpStatus.UNAUTHORIZED, + ); } return userId; diff --git a/src/feedback/feedback.controller.ts b/src/feedback/feedback.controller.ts index 211202c9..f2bdb9b6 100644 --- a/src/feedback/feedback.controller.ts +++ b/src/feedback/feedback.controller.ts @@ -35,7 +35,14 @@ export class FeedbackController { if (file.mimetype.startsWith('image/')) { cb(null, true); } else { - cb(new AppException('Only image files are allowed', 'ONLY_IMAGE_FILES_ALLOWED', HttpStatus.BAD_REQUEST), false); + cb( + new AppException( + 'Only image files are allowed', + 'ONLY_IMAGE_FILES_ALLOWED', + HttpStatus.BAD_REQUEST, + ), + false, + ); } }, }), diff --git a/src/permissions/permissions.controller.ts b/src/permissions/permissions.controller.ts index 1d27f3c8..ce523a06 100644 --- a/src/permissions/permissions.controller.ts +++ b/src/permissions/permissions.controller.ts @@ -15,9 +15,7 @@ import { PermissionDto } from './dto/permission.dto'; @Controller('api/v1/namespaces/:namespaceId/resources/:resourceId/permissions') export class PermissionsController { - constructor( - private readonly permissionsService: PermissionsService, - ) {} + constructor(private readonly permissionsService: PermissionsService) {} @Get() async listPermissions( diff --git a/src/search/search.service.ts b/src/search/search.service.ts index a9608093..8e3dd91d 100644 --- a/src/search/search.service.ts +++ b/src/search/search.service.ts @@ -43,7 +43,11 @@ export class SearchService { const baseUrl = this.configService.get('OBB_WIZARD_BASE_URL'); if (!baseUrl) { const message = this.i18n.t('system.errors.missingWizardBaseUrl'); - throw new AppException(message, 'MISSING_WIZARD_BASE_URL', HttpStatus.INTERNAL_SERVER_ERROR); + throw new AppException( + message, + 'MISSING_WIZARD_BASE_URL', + HttpStatus.INTERNAL_SERVER_ERROR, + ); } this.wizardApiService = new WizardAPIService(baseUrl, this.i18n); } diff --git a/src/user/user.module.ts b/src/user/user.module.ts index 57476235..1865a8d8 100644 --- a/src/user/user.module.ts +++ b/src/user/user.module.ts @@ -6,10 +6,11 @@ import { User } from 'omniboxd/user/entities/user.entity'; import { UserController } from 'omniboxd/user/user.controller'; import { UserOption } from 'omniboxd/user/entities/user-option.entity'; import { UserBinding } from 'omniboxd/user/entities/user-binding.entity'; +import { CacheService } from 'omniboxd/common/cache.service'; @Module({ exports: [UserService], - providers: [UserService], + providers: [UserService, CacheService], controllers: [UserController], imports: [ TypeOrmModule.forFeature([User, UserOption, UserBinding]), diff --git a/src/user/user.service.ts b/src/user/user.service.ts index 247c2807..f52fbb34 100644 --- a/src/user/user.service.ts +++ b/src/user/user.service.ts @@ -16,18 +16,17 @@ import { Injectable, HttpStatus } from '@nestjs/common'; import { isUsernameBlocked } from 'omniboxd/utils/blocked-usernames'; import { AppException } from 'omniboxd/common/exceptions/app.exception'; import { I18nService } from 'nestjs-i18n'; +import { CacheService } from 'omniboxd/common/cache.service'; + +interface EmailVerificationState { + code: string; + createdAt: number; + expiresIn: number; +} @Injectable() export class UserService { - private readonly emailStates = new Map< - string, - { - code: string; - createdAt: number; - expiresIn: number; - } - >(); - + private readonly namespace = '/user/email-verification'; private readonly alphaRegex = /[a-zA-Z]/; private readonly numberRegex = /\d/; @@ -40,6 +39,7 @@ export class UserService { private userBindingRepository: Repository, private readonly mailService: MailService, private readonly i18n: I18nService, + private readonly cacheService: CacheService, ) {} async verify(email: string, password: string) { @@ -63,7 +63,7 @@ export class UserService { return account; } - async validatePassword(password: string) { + validatePassword(password: string) { if (!password || password.length < 8) { const message = this.i18n.t('user.errors.passwordTooShort'); throw new AppException( @@ -106,7 +106,7 @@ export class UserService { ); } - await this.validatePassword(account.password); + this.validatePassword(account.password); const hash = await bcrypt.hash(account.password, 10); const newUser = repo.create({ @@ -293,15 +293,6 @@ export class UserService { }); } - private cleanExpiresState() { - const now = Date.now(); - for (const [state, info] of this.emailStates.entries()) { - if (now - info.createdAt > info.expiresIn) { - this.emailStates.delete(state); - } - } - } - async validateEmail(userId: string, email: string) { if (!isEmail(email)) { const message = this.i18n.t('user.errors.invalidEmailFormat'); @@ -323,14 +314,18 @@ export class UserService { } const code = generateId(6, '0123456789'); - - this.emailStates.set(email, { - code, - createdAt: Date.now(), - expiresIn: 5 * 60 * 1000, - }); - - this.cleanExpiresState(); + const expiresIn = 5 * 60 * 1000; + + await this.cacheService.set( + this.namespace, + email, + { + code, + createdAt: Date.now(), + expiresIn, + }, + expiresIn, + ); await this.mailService.validateEmail(email, code); @@ -370,7 +365,10 @@ export class UserService { HttpStatus.BAD_REQUEST, ); } - const emailState = this.emailStates.get(account.email); + const emailState = await this.cacheService.get( + this.namespace, + account.email, + ); if (!emailState) { const message = this.i18n.t('user.errors.pleaseVerifyEmail'); throw new AppException( @@ -387,7 +385,7 @@ export class UserService { HttpStatus.BAD_REQUEST, ); } - this.emailStates.delete(account.email); + await this.cacheService.delete(this.namespace, account.email); existUser.email = account.email; } return await this.userRepository.update(id, existUser); diff --git a/src/websocket/ws-jwt.guard.ts b/src/websocket/ws-jwt.guard.ts index a5a663c2..1cc6f082 100644 --- a/src/websocket/ws-jwt.guard.ts +++ b/src/websocket/ws-jwt.guard.ts @@ -17,7 +17,7 @@ export class WsJwtGuard implements CanActivate { private readonly i18n: I18nService, ) {} - async canActivate(context: ExecutionContext): Promise { + canActivate(context: ExecutionContext): boolean { const wsAuthConfig = this.reflector.getAllAndOverride( WS_AUTH_CONFIG_KEY, [context.getHandler(), context.getClass()], @@ -30,7 +30,7 @@ export class WsJwtGuard implements CanActivate { const message = this.i18n.t('auth.errors.noToken'); throw new WsException(`Unauthorized: ${message}`); } - const payload = await this.authService.jwtVerify(token); + const payload = this.authService.jwtVerify(token); client.data.userId = payload.sub; return true; } catch (error) { diff --git a/src/wizard/open.wizard.service.ts b/src/wizard/open.wizard.service.ts index 71490072..253a9ecd 100644 --- a/src/wizard/open.wizard.service.ts +++ b/src/wizard/open.wizard.service.ts @@ -44,7 +44,7 @@ export class OpenWizardService { 'ask', ); - return await this.mergeChunks(chunks); + return this.mergeChunks(chunks); } private async resolveConversationId( @@ -67,7 +67,7 @@ export class OpenWizardService { } } - private async mergeChunks(chunks: ChatResponse[]): Promise { + private mergeChunks(chunks: ChatResponse[]) { const messages: any[] = []; let currentMessage: any = null; diff --git a/src/wizard/processors/collect.processor.spec.ts b/src/wizard/processors/collect.processor.spec.ts index b124741d..955e31d9 100644 --- a/src/wizard/processors/collect.processor.spec.ts +++ b/src/wizard/processors/collect.processor.spec.ts @@ -1,6 +1,5 @@ /* eslint-disable @typescript-eslint/unbound-method */ import { Test, TestingModule } from '@nestjs/testing'; -import { BadRequestException } from '@nestjs/common'; import { CollectProcessor } from './collect.processor'; import { NamespaceResourcesService } from 'omniboxd/namespace-resources/namespace-resources.service'; import { ResourcesService } from 'omniboxd/resources/resources.service'; @@ -109,9 +108,7 @@ describe('CollectProcessor', () => { it('should throw AppException when payload is null', async () => { const task = createMockTask({ payload: null }); - await expect(processor.process(task)).rejects.toThrow( - AppException, - ); + await expect(processor.process(task)).rejects.toThrow(AppException); await expect(processor.process(task)).rejects.toThrow( 'Invalid task payload', ); @@ -120,9 +117,7 @@ describe('CollectProcessor', () => { it('should throw AppException when payload has no resource_id or resourceId', async () => { const task = createMockTask({ payload: {} }); - await expect(processor.process(task)).rejects.toThrow( - AppException, - ); + await expect(processor.process(task)).rejects.toThrow(AppException); await expect(processor.process(task)).rejects.toThrow( 'Invalid task payload', ); diff --git a/src/wizard/processors/extract-tags.processor.spec.ts b/src/wizard/processors/extract-tags.processor.spec.ts index 83d8640c..872d0e22 100644 --- a/src/wizard/processors/extract-tags.processor.spec.ts +++ b/src/wizard/processors/extract-tags.processor.spec.ts @@ -1,6 +1,5 @@ /* eslint-disable @typescript-eslint/unbound-method */ import { Test, TestingModule } from '@nestjs/testing'; -import { BadRequestException } from '@nestjs/common'; import { ExtractTagsProcessor } from './extract-tags.processor'; import { NamespaceResourcesService } from 'omniboxd/namespace-resources/namespace-resources.service'; import { TagService } from 'omniboxd/tag/tag.service'; @@ -27,7 +26,8 @@ describe('ExtractTagsProcessor', () => { t: jest.fn((key: string) => { // Return mock translations for test purposes const translations: Record = { - 'wizard.errors.invalidTaskPayload': 'Invalid task payload: missing resource_id', + 'wizard.errors.invalidTaskPayload': + 'Invalid task payload: missing resource_id', }; return translations[key] || key; }), @@ -53,7 +53,11 @@ describe('ExtractTagsProcessor', () => { namespaceResourcesService = module.get(NamespaceResourcesService); tagService = module.get(TagService); i18nService = module.get(I18nService); - processor = new ExtractTagsProcessor(namespaceResourcesService, tagService, i18nService); + processor = new ExtractTagsProcessor( + namespaceResourcesService, + tagService, + i18nService, + ); }); afterEach(() => { diff --git a/src/wizard/processors/reader.processor.spec.ts b/src/wizard/processors/reader.processor.spec.ts index 2eebf6af..7e2667ff 100644 --- a/src/wizard/processors/reader.processor.spec.ts +++ b/src/wizard/processors/reader.processor.spec.ts @@ -1,6 +1,5 @@ /* eslint-disable @typescript-eslint/unbound-method, @typescript-eslint/no-require-imports */ import { Test, TestingModule } from '@nestjs/testing'; -import { BadRequestException } from '@nestjs/common'; import { ReaderProcessor } from './reader.processor'; import { NamespaceResourcesService } from 'omniboxd/namespace-resources/namespace-resources.service'; import { ResourcesService } from 'omniboxd/resources/resources.service'; @@ -284,9 +283,7 @@ describe('ReaderProcessor', () => { }, }); - await expect(processor.process(task)).rejects.toThrow( - AppException, - ); + await expect(processor.process(task)).rejects.toThrow(AppException); await expect(processor.process(task)).rejects.toThrow( 'Invalid task payload', ); diff --git a/src/wizard/stream.service.ts b/src/wizard/stream.service.ts index cde5690f..93b047d4 100644 --- a/src/wizard/stream.service.ts +++ b/src/wizard/stream.service.ts @@ -206,10 +206,7 @@ export class StreamService { }; } - async findOneOrFail( - messages: Message[], - messageId: string, - ): Promise { + findOneOrFail(messages: Message[], messageId: string): Message { const message = messages.find((m) => m.id === messageId); if (!message) { const errorMessage = this.i18n.t('system.errors.messageNotFound'); @@ -222,14 +219,11 @@ export class StreamService { return message; } - async getMessages( - allMessages: Message[], - parentMessageId: string, - ): Promise { + getMessages(allMessages: Message[], parentMessageId: string): Message[] { const messages: Message[] = []; let parentId: string | null = parentMessageId; while (parentId) { - const message = await this.findOneOrFail(allMessages, parentId); + const message = this.findOneOrFail(allMessages, parentId); messages.unshift(message); parentId = message.parentId; } @@ -355,7 +349,7 @@ export class StreamService { userId || undefined, requestDto.conversation_id, ); - messages = await this.getMessages(allMessages, parentId); + messages = this.getMessages(allMessages, parentId); } const handlerContext: HandlerContext = { diff --git a/src/wizard/wizard.service.ts b/src/wizard/wizard.service.ts index eb7fa7cb..d51cc34d 100644 --- a/src/wizard/wizard.service.ts +++ b/src/wizard/wizard.service.ts @@ -86,7 +86,11 @@ export class WizardService { const baseUrl = this.configService.get('OBB_WIZARD_BASE_URL'); if (!baseUrl) { const message = this.i18n.t('system.errors.missingWizardBaseUrl'); - throw new AppException(message, 'MISSING_WIZARD_BASE_URL', HttpStatus.INTERNAL_SERVER_ERROR); + throw new AppException( + message, + 'MISSING_WIZARD_BASE_URL', + HttpStatus.INTERNAL_SERVER_ERROR, + ); } this.streamService = new StreamService( baseUrl,