From f87436441f929da56fe396c499c57abc19b086b3 Mon Sep 17 00:00:00 2001 From: Artem Niehrieiev Date: Thu, 28 May 2026 08:46:17 +0000 Subject: [PATCH] refactor: update return types and error handling across various use cases - Updated `findTableSettingsOrReturnEmpty` to return a more specific type. - Changed middleware configuration methods to return `void` instead of `any`. - Enhanced error handling in multiple use cases by utilizing `getErrorMessage` for consistent error messages. - Improved type safety in helper functions and entities. - Added type definitions for Express request to include decoded Cognito data. - Adjusted TypeScript configuration to extend from `tsconfig.src.json`. --- .../authorization/auth-with-api.middleware.ts | 4 +- backend/src/authorization/auth.middleware.ts | 4 +- .../authorization/basic-auth.middleware.ts | 4 +- .../cognito-decoded.interface.ts | 15 ++++-- .../non-scoped-auth.middleware.ts | 4 +- .../src/authorization/saas-auth.middleware.ts | 4 +- .../temporary-auth.middleware.ts | 4 +- .../src/decorators/body-email.decorator.ts | 2 +- backend/src/decorators/gclid-decorator.ts | 2 +- .../decorators/master-password.decorator.ts | 2 +- backend/src/decorators/user-id.decorator.ts | 2 +- backend/src/entities/ai/ai.module.ts | 2 +- ...est-info-from-table-with-ai-v7.use.case.ts | 5 +- .../entities/api-key/api-key.controller.ts | 2 +- .../src/entities/api-key/api-key.module.ts | 2 +- .../cedar-authorization.service.ts | 5 +- .../company-info/company-info.module.ts | 2 +- .../get-connection-diagram.use.case.ts | 3 +- .../preview-connection-diagram.use.case.ts | 3 +- .../utils/apply-proposed-ddl.util.ts | 5 +- .../convention/conversion.controller.ts | 3 +- .../entities/convention/conversion.module.ts | 2 +- .../custom-field/custom-field.module.ts | 2 +- .../entities/demo-data/demo-data.service.ts | 3 +- .../entities/demo-data/demo-deta.module.ts | 2 +- .../src/entities/email/email/email.service.ts | 3 +- .../src/entities/logging/winston-logger.ts | 52 ++++++++++++------- .../entities/permission/permission.module.ts | 2 +- .../entities/s3-widget/s3-widget.module.ts | 2 +- .../shared-jobs/shared-jobs.module.ts | 2 +- .../shared-jobs/shared-jobs.service.ts | 3 +- .../action-rules.module.ts | 2 +- .../table-action.module.ts | 2 +- .../table-categories.module.ts | 2 +- ...d-table-categories-with-tables.use.case.ts | 5 +- .../table-filters/table-filters.module.ts | 2 +- .../entities/table-logs/table-logs.module.ts | 2 +- .../ai/run-schema-change-ai-loop.ts | 5 +- .../generate-schema-change.use-case.ts | 7 +-- .../rollback-batch-schema-changes.use-case.ts | 3 +- .../table-schema/utils/dynamodb-schema-op.ts | 3 +- .../utils/elasticsearch-schema-op.ts | 3 +- .../table-schema/utils/mongo-schema-op.ts | 3 +- .../utils/validate-proposed-ddl.ts | 3 +- ...le-settings-custom-repository-extension.ts | 8 ++- .../table-settings.repository.interface.ts | 5 +- .../table-settings.module.ts | 2 +- .../personal-table-settings.module.ts | 2 +- backend/src/entities/table/table.module.ts | 2 +- .../use-cases/add-row-in-table.use.case.ts | 5 +- .../bulk-update-rows-in-table.use.case.ts | 3 +- .../delete-row-from-table.use.case.ts | 5 +- .../delete-rows-from-table.use.case.ts | 5 +- .../export-csv-from-table.use.case.ts | 5 +- .../find-tables-in-connection-v2.use.case.ts | 5 +- .../find-tables-in-connection.use.case.ts | 5 +- .../get-row-by-primary-key.use.case.ts | 3 +- .../use-cases/get-table-rows.use.case.ts | 5 +- ...-table-structure-without-cache.use.case.ts | 3 +- .../use-cases/get-table-structure.use.case.ts | 3 +- .../import-csv-in-table-user.case.ts | 3 +- .../use-cases/update-row-in-table.use.case.ts | 5 +- .../table/utils/process-found-rows-util.ts | 2 +- ...sers-actions-and-mailing-users.use.case.ts | 3 +- .../sign-in-audit.module.ts | 2 +- .../user/use-cases/log-out.use.case.ts | 3 +- backend/src/entities/user/user.controller.ts | 4 +- ...enerate-panel-position-with-ai.use.case.ts | 7 +-- ...nerate-table-dashboard-with-ai.use.case.ts | 9 ++-- .../visualizations/panel/panel.entity.ts | 7 +-- .../panel/utils/collect-query-tables.util.ts | 3 +- .../entities/widget/table-widget.entity.ts | 7 +-- .../entities/widget/table-widget.module.ts | 2 +- .../src/exceptions/all-exceptions.filter.ts | 26 ++++++++-- backend/src/helpers/compare-array-elements.ts | 4 +- backend/src/helpers/enum-to-string.ts | 2 +- .../get-property-value-by-descriptor.ts | 4 +- backend/src/helpers/is-object-empty.ts | 2 +- .../src/helpers/slack/slack-post-message.ts | 2 +- .../validators/is-action-url-host-allowed.ts | 3 +- backend/src/types/express.d.ts | 7 +++ backend/tsconfig.build.json | 2 +- 82 files changed, 230 insertions(+), 139 deletions(-) create mode 100644 backend/src/types/express.d.ts diff --git a/backend/src/authorization/auth-with-api.middleware.ts b/backend/src/authorization/auth-with-api.middleware.ts index 954a797d3..df4f1305b 100644 --- a/backend/src/authorization/auth-with-api.middleware.ts +++ b/backend/src/authorization/auth-with-api.middleware.ts @@ -9,7 +9,7 @@ import { } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import Sentry from '@sentry/minimal'; -import { Request, Response } from 'express'; +import { NextFunction, Request, Response } from 'express'; import jwt from 'jsonwebtoken'; import { Repository } from 'typeorm'; import { JwtScopesEnum } from '../entities/user/enums/jwt-scopes.enum.js'; @@ -29,7 +29,7 @@ export class AuthWithApiMiddleware implements NestMiddleware { private readonly userRepository: Repository, ) {} - async use(req: IRequestWithCognitoInfo, _res: Response, next: (err?: any, res?: any) => void): Promise { + async use(req: IRequestWithCognitoInfo, _res: Response, next: NextFunction): Promise { try { await this.authenticateRequest(req); next(); diff --git a/backend/src/authorization/auth.middleware.ts b/backend/src/authorization/auth.middleware.ts index ef422e2ad..e713b3ac3 100644 --- a/backend/src/authorization/auth.middleware.ts +++ b/backend/src/authorization/auth.middleware.ts @@ -8,7 +8,7 @@ import { } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import Sentry from '@sentry/minimal'; -import { Response } from 'express'; +import { NextFunction, Response } from 'express'; import jwt from 'jsonwebtoken'; import { Repository } from 'typeorm'; import { LogOutEntity } from '../entities/log-out/log-out.entity.js'; @@ -29,7 +29,7 @@ export class AuthMiddleware implements NestMiddleware { @InjectRepository(LogOutEntity) private readonly logOutRepository: Repository, ) {} - async use(req: IRequestWithCognitoInfo, _res: Response, next: (err?: any, res?: any) => void): Promise { + async use(req: IRequestWithCognitoInfo, _res: Response, next: NextFunction): Promise { let token: string; try { token = req.cookies[Constants.JWT_COOKIE_KEY_NAME]; diff --git a/backend/src/authorization/basic-auth.middleware.ts b/backend/src/authorization/basic-auth.middleware.ts index 7e3297bb9..8679e56fe 100644 --- a/backend/src/authorization/basic-auth.middleware.ts +++ b/backend/src/authorization/basic-auth.middleware.ts @@ -1,12 +1,12 @@ import { Injectable, NestMiddleware, UnauthorizedException } from '@nestjs/common'; import auth from 'basic-auth'; -import { Request, Response } from 'express'; +import { NextFunction, Request, Response } from 'express'; import { Messages } from '../exceptions/text/messages.js'; import { appConfig } from '../shared/config/app-config.js'; @Injectable() export class BasicAuthMiddleware implements NestMiddleware { - use(req: Request, _res: Response, next: (err?: any, res?: any) => void): void { + use(req: Request, _res: Response, next: NextFunction): void { const basicAuthLogin = appConfig.auth.basicAuthLogin; const basicAuthPassword = appConfig.auth.basicAuthPassword; const userCredentials = auth(req); diff --git a/backend/src/authorization/cognito-decoded.interface.ts b/backend/src/authorization/cognito-decoded.interface.ts index 431b37940..a79e804ba 100644 --- a/backend/src/authorization/cognito-decoded.interface.ts +++ b/backend/src/authorization/cognito-decoded.interface.ts @@ -1,4 +1,5 @@ import type { Request } from 'express'; + export interface ICognitoDecodedData { at_hash: string; sub: string; @@ -14,8 +15,16 @@ export interface ICognitoDecodedData { email: string; } -export interface IRequestWithCognitoInfo extends Request { - query: any; +/** + * Request type with the cognito-derived JWT payload attached by auth middleware. + * + * `params` and `query` are narrowed to `Record`. Express's stock + * `query` type is `ParsedQs` (allows arrays and nested objects), but this codebase only + * issues simple `?key=value` URLs — narrowing here surfaces accidental multi-value usage + * without forcing every callsite to narrow inline. + */ +export interface IRequestWithCognitoInfo extends Omit { + query: Record; + params: Record; decoded: Partial; - params: any; } diff --git a/backend/src/authorization/non-scoped-auth.middleware.ts b/backend/src/authorization/non-scoped-auth.middleware.ts index 36177eb17..3fbd36442 100644 --- a/backend/src/authorization/non-scoped-auth.middleware.ts +++ b/backend/src/authorization/non-scoped-auth.middleware.ts @@ -7,7 +7,7 @@ import { } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import Sentry from '@sentry/minimal'; -import { Response } from 'express'; +import { NextFunction, Response } from 'express'; import jwt from 'jsonwebtoken'; import { Repository } from 'typeorm'; import { LogOutEntity } from '../entities/log-out/log-out.entity.js'; @@ -24,7 +24,7 @@ export class NonScopedAuthMiddleware implements NestMiddleware { @InjectRepository(LogOutEntity) private readonly logOutRepository: Repository, ) {} - async use(req: IRequestWithCognitoInfo, _res: Response, next: (err?: any, res?: any) => void): Promise { + async use(req: IRequestWithCognitoInfo, _res: Response, next: NextFunction): Promise { console.log(`auth middleware triggered ->: ${new Date().toISOString()}`); let token: string; try { diff --git a/backend/src/authorization/saas-auth.middleware.ts b/backend/src/authorization/saas-auth.middleware.ts index c36de63b6..4cc039a2f 100644 --- a/backend/src/authorization/saas-auth.middleware.ts +++ b/backend/src/authorization/saas-auth.middleware.ts @@ -1,5 +1,5 @@ import { Injectable, NestMiddleware, UnauthorizedException } from '@nestjs/common'; -import { Response } from 'express'; +import { NextFunction, Response } from 'express'; import jwt from 'jsonwebtoken'; import { Messages } from '../exceptions/text/messages.js'; import { appConfig } from '../shared/config/app-config.js'; @@ -8,7 +8,7 @@ import { extractTokenFromHeader } from './utils/extract-token-from-header.js'; @Injectable() export class SaaSAuthMiddleware implements NestMiddleware { - use(req: IRequestWithCognitoInfo, _res: Response, next: (err?: any, res?: any) => void): void { + use(req: IRequestWithCognitoInfo, _res: Response, next: NextFunction): void { console.log(`saas auth middleware triggered ->: ${new Date().toISOString()}`); const token = extractTokenFromHeader(req); if (!token) { diff --git a/backend/src/authorization/temporary-auth.middleware.ts b/backend/src/authorization/temporary-auth.middleware.ts index 244d87b26..8d0aa8980 100644 --- a/backend/src/authorization/temporary-auth.middleware.ts +++ b/backend/src/authorization/temporary-auth.middleware.ts @@ -7,7 +7,7 @@ import { } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import Sentry from '@sentry/minimal'; -import { Response } from 'express'; +import { NextFunction, Response } from 'express'; import jwt from 'jsonwebtoken'; import { Repository } from 'typeorm'; import { LogOutEntity } from '../entities/log-out/log-out.entity.js'; @@ -26,7 +26,7 @@ export class TemporaryAuthMiddleware implements NestMiddleware { @InjectRepository(LogOutEntity) private readonly logOutRepository: Repository, ) {} - async use(req: IRequestWithCognitoInfo, _res: Response, next: (err?: any, res?: any) => void): Promise { + async use(req: IRequestWithCognitoInfo, _res: Response, next: NextFunction): Promise { console.log(`temporary auth middleware triggered ->: ${new Date().toISOString()}`); let token: string; try { diff --git a/backend/src/decorators/body-email.decorator.ts b/backend/src/decorators/body-email.decorator.ts index a6007e9b8..ce2eaf282 100644 --- a/backend/src/decorators/body-email.decorator.ts +++ b/backend/src/decorators/body-email.decorator.ts @@ -4,7 +4,7 @@ import { Messages } from '../exceptions/text/messages.js'; import { isObjectPropertyExists } from '../helpers/validators/is-object-property-exists-validator.js'; import { ValidationHelper } from '../helpers/validators/validation-helper.js'; -export const BodyEmail = createParamDecorator((_data: any, ctx: ExecutionContext): string => { +export const BodyEmail = createParamDecorator((_data: unknown, ctx: ExecutionContext): string => { const request: IRequestWithCognitoInfo = ctx.switchToHttp().getRequest(); const body = request.body; if (isObjectPropertyExists(body, 'email')) { diff --git a/backend/src/decorators/gclid-decorator.ts b/backend/src/decorators/gclid-decorator.ts index 78bfc829c..c8807e720 100644 --- a/backend/src/decorators/gclid-decorator.ts +++ b/backend/src/decorators/gclid-decorator.ts @@ -1,7 +1,7 @@ import { createParamDecorator, ExecutionContext } from '@nestjs/common'; import { IRequestWithCognitoInfo } from '../authorization/cognito-decoded.interface.js'; -export const GCLlId = createParamDecorator((_data: any, ctx: ExecutionContext): string => { +export const GCLlId = createParamDecorator((_data: unknown, ctx: ExecutionContext): string => { const request: IRequestWithCognitoInfo = ctx.switchToHttp().getRequest(); if (request.headers) { return (request.headers.gclid as string | undefined) ?? null; diff --git a/backend/src/decorators/master-password.decorator.ts b/backend/src/decorators/master-password.decorator.ts index 162b2c0e7..472cbfec7 100644 --- a/backend/src/decorators/master-password.decorator.ts +++ b/backend/src/decorators/master-password.decorator.ts @@ -1,7 +1,7 @@ import { createParamDecorator, ExecutionContext } from '@nestjs/common'; import { IRequestWithCognitoInfo } from '../authorization/cognito-decoded.interface.js'; -export const MasterPassword = createParamDecorator((_data: any, ctx: ExecutionContext): string => { +export const MasterPassword = createParamDecorator((_data: unknown, ctx: ExecutionContext): string => { const request: IRequestWithCognitoInfo = ctx.switchToHttp().getRequest(); const masterPwd = request.headers.masterpwd as string | undefined; return masterPwd ? masterPwd : null; diff --git a/backend/src/decorators/user-id.decorator.ts b/backend/src/decorators/user-id.decorator.ts index 90a1d7478..14faf1109 100644 --- a/backend/src/decorators/user-id.decorator.ts +++ b/backend/src/decorators/user-id.decorator.ts @@ -3,7 +3,7 @@ import { IRequestWithCognitoInfo } from '../authorization/cognito-decoded.interf import { Messages } from '../exceptions/text/messages.js'; import { ValidationHelper } from '../helpers/validators/validation-helper.js'; -export const UserId = createParamDecorator((_data: any, ctx: ExecutionContext): string => { +export const UserId = createParamDecorator((_data: unknown, ctx: ExecutionContext): string => { const request: IRequestWithCognitoInfo = ctx.switchToHttp().getRequest(); const userId = request.decoded?.sub; if (!userId) { diff --git a/backend/src/entities/ai/ai.module.ts b/backend/src/entities/ai/ai.module.ts index f46b6fde1..558def74c 100644 --- a/backend/src/entities/ai/ai.module.ts +++ b/backend/src/entities/ai/ai.module.ts @@ -50,7 +50,7 @@ import { UserAIRequestsControllerV2 } from './user-ai-requests-v2.controller.js' controllers: [UserAIRequestsControllerV2, UserAiChatController], }) export class AIModule implements NestModule { - public configure(consumer: MiddlewareConsumer): any { + public configure(consumer: MiddlewareConsumer): void { consumer .apply(AuthMiddleware) .forRoutes( diff --git a/backend/src/entities/ai/use-cases/request-info-from-table-with-ai-v7.use.case.ts b/backend/src/entities/ai/use-cases/request-info-from-table-with-ai-v7.use.case.ts index 609a096f7..538b06cc5 100644 --- a/backend/src/entities/ai/use-cases/request-info-from-table-with-ai-v7.use.case.ts +++ b/backend/src/entities/ai/use-cases/request-info-from-table-with-ai-v7.use.case.ts @@ -19,6 +19,7 @@ import AbstractUseCase from '../../../common/abstract-use.case.js'; import { IGlobalDatabaseContext } from '../../../common/application/global-database-context.interface.js'; import { BaseType } from '../../../common/data-injection.tokens.js'; import { Messages } from '../../../exceptions/text/messages.js'; +import { getErrorMessage } from '../../../helpers/get-error-message.js'; import { isConnectionTypeAgent } from '../../../helpers/is-connection-entity-agent.js'; import { slackPostMessage } from '../../../helpers/slack/slack-post-message.js'; import { ConnectionEntity } from '../../connection/connection.entity.js'; @@ -192,7 +193,7 @@ export class RequestInfoFromTableWithAIUseCaseV7 depth++; } catch (loopError) { - this.logger.error(`Error in tool loop at depth ${depth + 1}: ${(loopError as Error).message}`); + this.logger.error(`Error in tool loop at depth ${depth + 1}: ${getErrorMessage(loopError)}`); throw loopError; } } @@ -290,7 +291,7 @@ export class RequestInfoFromTableWithAIUseCaseV7 result = encodeError({ error: `Unknown tool: ${toolCall.name}` }); } } catch (error) { - const errMessage = (error as Error).message; + const errMessage = getErrorMessage(error); this.logger.error(`Tool call ${toolCall.name} (${toolCall.id}) failed: ${errMessage}`); result = encodeError({ error: errMessage }); } diff --git a/backend/src/entities/api-key/api-key.controller.ts b/backend/src/entities/api-key/api-key.controller.ts index 500ca1c42..59328ed8d 100644 --- a/backend/src/entities/api-key/api-key.controller.ts +++ b/backend/src/entities/api-key/api-key.controller.ts @@ -92,7 +92,7 @@ export class ApiKeyController { description: 'Api key is valid.', }) @Get('/check/apikey') - public async checkApiKey(): Promise { + public async checkApiKey(): Promise<{ result: boolean; message: string }> { return { result: true, message: 'Api key is valid', diff --git a/backend/src/entities/api-key/api-key.module.ts b/backend/src/entities/api-key/api-key.module.ts index 36fb2631b..acdd2efa5 100644 --- a/backend/src/entities/api-key/api-key.module.ts +++ b/backend/src/entities/api-key/api-key.module.ts @@ -40,7 +40,7 @@ import { GetUserApiKeyUseCase } from './use-cases/get-user-api-key.use.case.js'; controllers: [ApiKeyController], }) export class ApiKeyModule implements NestModule { - public configure(consumer: MiddlewareConsumer): any { + public configure(consumer: MiddlewareConsumer): void { consumer .apply(AuthMiddleware) .forRoutes( diff --git a/backend/src/entities/cedar-authorization/cedar-authorization.service.ts b/backend/src/entities/cedar-authorization/cedar-authorization.service.ts index e3b4fcaab..2ee0f30a0 100644 --- a/backend/src/entities/cedar-authorization/cedar-authorization.service.ts +++ b/backend/src/entities/cedar-authorization/cedar-authorization.service.ts @@ -4,6 +4,7 @@ import { IGlobalDatabaseContext } from '../../common/application/global-database import { BaseType } from '../../common/data-injection.tokens.js'; import { Messages } from '../../exceptions/text/messages.js'; import { Cacher } from '../../helpers/cache/cacher.js'; +import { getErrorMessage } from '../../helpers/get-error-message.js'; import { GroupEntity } from '../group/group.entity.js'; import { IComplexPermission } from '../permission/permission.interface.js'; import { @@ -198,7 +199,7 @@ export class CedarAuthorizationService implements ICedarAuthorizationService, On } } catch (e) { if (e instanceof HttpException) throw e; - throw new HttpException({ message: `Invalid cedar schema: ${(e as Error).message}` }, HttpStatus.BAD_REQUEST); + throw new HttpException({ message: `Invalid cedar schema: ${getErrorMessage(e)}` }, HttpStatus.BAD_REQUEST); } } @@ -296,7 +297,7 @@ export class CedarAuthorizationService implements ICedarAuthorizationService, On }; cedarWasm.isAuthorized(testCall as Parameters[0]); } catch (e) { - throw new HttpException({ message: `Invalid cedar policy: ${(e as Error).message}` }, HttpStatus.BAD_REQUEST); + throw new HttpException({ message: `Invalid cedar policy: ${getErrorMessage(e)}` }, HttpStatus.BAD_REQUEST); } } diff --git a/backend/src/entities/company-info/company-info.module.ts b/backend/src/entities/company-info/company-info.module.ts index ff7042c6b..5f0f4daf6 100644 --- a/backend/src/entities/company-info/company-info.module.ts +++ b/backend/src/entities/company-info/company-info.module.ts @@ -176,7 +176,7 @@ import { VerifyInviteUserInCompanyAndConnectionGroupUseCase } from './use-cases/ controllers: [CompanyInfoController], }) export class CompanyInfoModule implements NestModule { - public configure(consumer: MiddlewareConsumer): any { + public configure(consumer: MiddlewareConsumer): void { consumer .apply(AuthMiddleware) .forRoutes( diff --git a/backend/src/entities/connection/use-cases/get-connection-diagram.use.case.ts b/backend/src/entities/connection/use-cases/get-connection-diagram.use.case.ts index 13ef44a13..720f1515c 100644 --- a/backend/src/entities/connection/use-cases/get-connection-diagram.use.case.ts +++ b/backend/src/entities/connection/use-cases/get-connection-diagram.use.case.ts @@ -11,6 +11,7 @@ import { BaseType } from '../../../common/data-injection.tokens.js'; import { ExceptionOperations } from '../../../exceptions/custom-exceptions/exception-operation.js'; import { UnknownSQLException } from '../../../exceptions/custom-exceptions/unknown-sql-exception.js'; import { Messages } from '../../../exceptions/text/messages.js'; +import { getErrorMessage } from '../../../helpers/get-error-message.js'; import { isConnectionTypeAgent } from '../../../helpers/is-connection-entity-agent.js'; import { GetConnectionDiagramDs } from '../application/data-structures/get-connection-diagram.ds.js'; import { ConnectionDiagramResponseDTO } from '../application/dto/connection-diagram-response.dto.js'; @@ -52,7 +53,7 @@ export class GetConnectionDiagramUseCase try { tables = await dao.getTablesFromDB(userEmail); } catch (e) { - throw new UnknownSQLException((e as Error).message, ExceptionOperations.FAILED_TO_GET_TABLES); + throw new UnknownSQLException(getErrorMessage(e), ExceptionOperations.FAILED_TO_GET_TABLES); } const realTables = tables.filter((t) => !t.isView); diff --git a/backend/src/entities/connection/use-cases/preview-connection-diagram.use.case.ts b/backend/src/entities/connection/use-cases/preview-connection-diagram.use.case.ts index d4c1d09b0..84d8421f1 100644 --- a/backend/src/entities/connection/use-cases/preview-connection-diagram.use.case.ts +++ b/backend/src/entities/connection/use-cases/preview-connection-diagram.use.case.ts @@ -11,6 +11,7 @@ import { BaseType } from '../../../common/data-injection.tokens.js'; import { ExceptionOperations } from '../../../exceptions/custom-exceptions/exception-operation.js'; import { UnknownSQLException } from '../../../exceptions/custom-exceptions/unknown-sql-exception.js'; import { Messages } from '../../../exceptions/text/messages.js'; +import { getErrorMessage } from '../../../helpers/get-error-message.js'; import { isConnectionTypeAgent } from '../../../helpers/is-connection-entity-agent.js'; import { PreviewConnectionDiagramDs } from '../application/data-structures/preview-connection-diagram.ds.js'; import { ConnectionDiagramPreviewResponseDTO } from '../application/dto/connection-diagram-preview-response.dto.js'; @@ -53,7 +54,7 @@ export class PreviewConnectionDiagramUseCase try { tables = await dao.getTablesFromDB(userEmail); } catch (e) { - throw new UnknownSQLException((e as Error).message, ExceptionOperations.FAILED_TO_GET_TABLES); + throw new UnknownSQLException(getErrorMessage(e), ExceptionOperations.FAILED_TO_GET_TABLES); } const realTables = tables.filter((t) => !t.isView); diff --git a/backend/src/entities/connection/utils/apply-proposed-ddl.util.ts b/backend/src/entities/connection/utils/apply-proposed-ddl.util.ts index 56bc50a67..ebcc55f5d 100644 --- a/backend/src/entities/connection/utils/apply-proposed-ddl.util.ts +++ b/backend/src/entities/connection/utils/apply-proposed-ddl.util.ts @@ -3,6 +3,7 @@ import { PrimaryKeyDS } from '@rocketadmin/shared-code/dist/src/data-access-laye import { TableStructureDS } from '@rocketadmin/shared-code/dist/src/data-access-layer/shared/data-structures/table-structure.ds.js'; import { ConnectionTypesEnum } from '@rocketadmin/shared-code/dist/src/shared/enums/connection-types-enum.js'; import sqlParser from 'node-sql-parser'; +import { getErrorMessage } from '../../../helpers/get-error-message.js'; import { connectionTypeToParserDialect } from '../../table-schema/utils/assert-dialect-supported.js'; import { MermaidTableInput } from './build-mermaid-er-diagram.util.js'; @@ -65,7 +66,7 @@ export function applyProposedDdl( diff.statementResults.push({ sql, status: 'error', - message: `parse error: ${(err as Error).message}`, + message: `parse error: ${getErrorMessage(err)}`, }); continue; } @@ -82,7 +83,7 @@ export function applyProposedDdl( diff.statementResults.push({ sql, status: 'error', - message: (err as Error).message, + message: getErrorMessage(err), }); } } diff --git a/backend/src/entities/convention/conversion.controller.ts b/backend/src/entities/convention/conversion.controller.ts index 09f87e348..ddf4fdd7f 100644 --- a/backend/src/entities/convention/conversion.controller.ts +++ b/backend/src/entities/convention/conversion.controller.ts @@ -20,11 +20,10 @@ export class ConversionController { ) {} @Get('/conversions') - async getConversions(@Res() res: Response): Promise { + async getConversions(@Res() res: Response): Promise { const csvData = await this.getConversionsUseCase.execute(undefined, InTransactionEnum.OFF); res.setHeader('Content-Type', 'text/csv'); res.setHeader('Content-Disposition', 'attachment; filename=conversions.csv'); res.status(200).end(csvData); - return; } } diff --git a/backend/src/entities/convention/conversion.module.ts b/backend/src/entities/convention/conversion.module.ts index 3c2affcc7..d2673b345 100644 --- a/backend/src/entities/convention/conversion.module.ts +++ b/backend/src/entities/convention/conversion.module.ts @@ -24,7 +24,7 @@ import { GetConversionsUseCase } from './use-cases/get-conversions.use.case.js'; controllers: [ConversionController], }) export class ConversionModule implements NestModule { - public configure(consumer: MiddlewareConsumer): any { + public configure(consumer: MiddlewareConsumer): void { consumer.apply(BasicAuthMiddleware).forRoutes({ path: '/conversions', method: RequestMethod.GET }); } } diff --git a/backend/src/entities/custom-field/custom-field.module.ts b/backend/src/entities/custom-field/custom-field.module.ts index 55e2650db..3fc5fb954 100644 --- a/backend/src/entities/custom-field/custom-field.module.ts +++ b/backend/src/entities/custom-field/custom-field.module.ts @@ -62,7 +62,7 @@ import { UpdateCustomFieldUseCase } from './use-cases/update-custom-field.use.ca exports: [], }) export class CustomFieldModule { - public configure(consumer: MiddlewareConsumer): any { + public configure(consumer: MiddlewareConsumer): void { consumer .apply(AuthMiddleware) .forRoutes( diff --git a/backend/src/entities/demo-data/demo-data.service.ts b/backend/src/entities/demo-data/demo-data.service.ts index c64f5719d..e731deeb5 100644 --- a/backend/src/entities/demo-data/demo-data.service.ts +++ b/backend/src/entities/demo-data/demo-data.service.ts @@ -8,6 +8,7 @@ import { TableActionEventEnum } from '../../enums/table-action-event-enum.js'; import { TableActionTypeEnum } from '../../enums/table-action-type.enum.js'; import { isTest } from '../../helpers/app/is-test.js'; import { Constants } from '../../helpers/constants/constants.js'; +import { getErrorMessage } from '../../helpers/get-error-message.js'; import { slackPostMessage } from '../../helpers/slack/slack-post-message.js'; import { generateCedarPolicyForGroup } from '../cedar-authorization/cedar-policy-generator.js'; import { ConnectionEntity } from '../connection/connection.entity.js'; @@ -43,7 +44,7 @@ export class DemoDataService { return await this.createDemoData(userId); } catch (error) { console.error(`Error during demo data creation for user with ID ${userId}:`, error); - await slackPostMessage(`Error during demo data creation for user with ID ${userId}: ${(error as Error).message}`); + await slackPostMessage(`Error during demo data creation for user with ID ${userId}: ${getErrorMessage(error)}`); } } diff --git a/backend/src/entities/demo-data/demo-deta.module.ts b/backend/src/entities/demo-data/demo-deta.module.ts index 36b834594..535e9a243 100644 --- a/backend/src/entities/demo-data/demo-deta.module.ts +++ b/backend/src/entities/demo-data/demo-deta.module.ts @@ -19,5 +19,5 @@ import { DemoDataService } from './demo-data.service.js'; exports: [DemoDataService], }) export class DemoDataModule { - public configure(_consumer: MiddlewareConsumer): any {} + public configure(_consumer: MiddlewareConsumer): void {} } diff --git a/backend/src/entities/email/email/email.service.ts b/backend/src/entities/email/email/email.service.ts index 566aa13e4..c5e13d4b1 100644 --- a/backend/src/entities/email/email/email.service.ts +++ b/backend/src/entities/email/email/email.service.ts @@ -8,6 +8,7 @@ import { BaseType } from '../../../common/data-injection.tokens.js'; import { TableActionEventEnum } from '../../../enums/table-action-event-enum.js'; import { isTest } from '../../../helpers/app/is-test.js'; import { Constants } from '../../../helpers/constants/constants.js'; +import { getErrorMessage } from '../../../helpers/get-error-message.js'; import { appConfig } from '../../../shared/config/app-config.js'; import { WinstonLogger } from '../../logging/winston-logger.js'; import { UserInfoMessageData } from '../../table-actions/table-actions-module/table-action-activation.service.js'; @@ -92,7 +93,7 @@ export class EmailService { }); mailingResults.push(result); } catch (error) { - this.logger.error(`Failed to send reminder to ${email}: ${(error as Error).message}`); + this.logger.error(`Failed to send reminder to ${email}: ${getErrorMessage(error)}`); Sentry.captureException(error); mailingResults.push(null); } diff --git a/backend/src/entities/logging/winston-logger.ts b/backend/src/entities/logging/winston-logger.ts index 5b3b90fbd..f20ab066c 100644 --- a/backend/src/entities/logging/winston-logger.ts +++ b/backend/src/entities/logging/winston-logger.ts @@ -4,6 +4,20 @@ import { slackPostMessage } from '../../helpers/slack/slack-post-message.js'; import { appConfig } from '../../shared/config/app-config.js'; import { LoggerTransports } from './logger-transports.config.js'; +function formatMessage(message: unknown): string { + if (typeof message === 'string') { + return message; + } + if (message instanceof Error) { + return message.stack ?? message.message; + } + try { + return JSON.stringify(message); + } catch { + return String(message); + } +} + @Injectable() export class WinstonLogger implements LoggerService { private readonly logger: winston.Logger; @@ -15,25 +29,26 @@ export class WinstonLogger implements LoggerService { }); } - public log(message: any, ...optionalParams: any[]) { - this.logger.info(message, ...optionalParams); + public log(message: unknown, ...optionalParams: unknown[]): void { + this.logger.info(formatMessage(message), ...optionalParams); } - public info(message: any, ...optionalParams: any[]) { - this.logger.info(message, ...optionalParams); + public info(message: unknown, ...optionalParams: unknown[]): void { + this.logger.info(formatMessage(message), ...optionalParams); } - public error(message: any, ...optionalParams: any[]) { - this.logger.error(message, ...optionalParams); + public error(message: unknown, ...optionalParams: unknown[]): void { + this.logger.error(formatMessage(message), ...optionalParams); } - public warn(message: any, ...optionalParams: any[]) { - this.logger.warn(message, ...optionalParams); + public warn(message: unknown, ...optionalParams: unknown[]): void { + this.logger.warn(formatMessage(message), ...optionalParams); } - public logWithSlack(message: any, ...optionalParams: any[]) { - this.logger.error(message, ...optionalParams); - slackPostMessage(message).catch((error) => { + public logWithSlack(message: unknown, ...optionalParams: unknown[]): void { + const formatted = formatMessage(message); + this.logger.error(formatted, ...optionalParams); + slackPostMessage(formatted).catch((error) => { this.logger.error('Failed to send Slack message', error); }); } @@ -42,17 +57,18 @@ export class WinstonLogger implements LoggerService { this.logger.info(`\n ${str} \n`); } - public debug(message: any, ...optionalParams: any[]) { - this.logger.debug(message, ...optionalParams); + public debug(message: unknown, ...optionalParams: unknown[]): void { + this.logger.debug(formatMessage(message), ...optionalParams); } - public verbose(message: any, ...optionalParams: any[]) { - this.logger.verbose(message, ...optionalParams); + public verbose(message: unknown, ...optionalParams: unknown[]): void { + this.logger.verbose(formatMessage(message), ...optionalParams); } - public fatal(message: any, ...optionalParams: any[]) { - this.logger.error(message, ...optionalParams); - slackPostMessage(message).catch((error) => { + public fatal(message: unknown, ...optionalParams: unknown[]): void { + const formatted = formatMessage(message); + this.logger.error(formatted, ...optionalParams); + slackPostMessage(formatted).catch((error) => { this.logger.error('Failed to send Slack message', error); }); } diff --git a/backend/src/entities/permission/permission.module.ts b/backend/src/entities/permission/permission.module.ts index df516629c..85b387985 100644 --- a/backend/src/entities/permission/permission.module.ts +++ b/backend/src/entities/permission/permission.module.ts @@ -47,7 +47,7 @@ import { CreateOrUpdatePermissionsUseCase } from './use-cases/create-or-update-p exports: [], }) export class PermissionModule implements NestModule { - public configure(consumer: MiddlewareConsumer): any { + public configure(consumer: MiddlewareConsumer): void { consumer.apply(AuthMiddleware).forRoutes({ path: 'permissions/:slug', method: RequestMethod.PUT }); } } diff --git a/backend/src/entities/s3-widget/s3-widget.module.ts b/backend/src/entities/s3-widget/s3-widget.module.ts index 13627b995..7cff3f318 100644 --- a/backend/src/entities/s3-widget/s3-widget.module.ts +++ b/backend/src/entities/s3-widget/s3-widget.module.ts @@ -31,7 +31,7 @@ import { GetS3UploadUrlUseCase } from './use-cases/get-s3-upload-url.use.case.js exports: [S3HelperService], }) export class S3WidgetModule { - public configure(consumer: MiddlewareConsumer): any { + public configure(consumer: MiddlewareConsumer): void { consumer .apply(AuthMiddleware) .forRoutes( diff --git a/backend/src/entities/shared-jobs/shared-jobs.module.ts b/backend/src/entities/shared-jobs/shared-jobs.module.ts index 1c3d2f66c..36f943bde 100644 --- a/backend/src/entities/shared-jobs/shared-jobs.module.ts +++ b/backend/src/entities/shared-jobs/shared-jobs.module.ts @@ -18,5 +18,5 @@ import { SharedJobsService } from './shared-jobs.service.js'; exports: [SharedJobsService], }) export class SharedJobsModule { - public configure(_consumer: MiddlewareConsumer): any {} + public configure(_consumer: MiddlewareConsumer): void {} } diff --git a/backend/src/entities/shared-jobs/shared-jobs.service.ts b/backend/src/entities/shared-jobs/shared-jobs.service.ts index 55ccdeadc..02c579a71 100644 --- a/backend/src/entities/shared-jobs/shared-jobs.service.ts +++ b/backend/src/entities/shared-jobs/shared-jobs.service.ts @@ -10,6 +10,7 @@ import { IGlobalDatabaseContext } from '../../common/application/global-database import { BaseType } from '../../common/data-injection.tokens.js'; import { WidgetTypeEnum } from '../../enums/widget-type.enum.js'; import { isTest } from '../../helpers/app/is-test.js'; +import { getErrorMessage } from '../../helpers/get-error-message.js'; import { ValidationHelper } from '../../helpers/validators/validation-helper.js'; import { AiService } from '../ai/ai.service.js'; import { ConnectionEntity } from '../connection/connection.entity.js'; @@ -65,7 +66,7 @@ export class SharedJobsService { foreignKeys, }; } catch (error) { - console.error(`Error getting table information for "${table.tableName}": ${(error as Error).message}`); + console.error(`Error getting table information for "${table.tableName}": ${getErrorMessage(error)}`); return null; } }), diff --git a/backend/src/entities/table-actions/table-action-rules-module/action-rules.module.ts b/backend/src/entities/table-actions/table-action-rules-module/action-rules.module.ts index ec89eb22c..f64e275e7 100644 --- a/backend/src/entities/table-actions/table-action-rules-module/action-rules.module.ts +++ b/backend/src/entities/table-actions/table-action-rules-module/action-rules.module.ts @@ -54,7 +54,7 @@ import { UpdateRuleUseCase } from './use-cases/update-action-rule-with-actions-a controllers: [ActionRulesController], }) export class TableTriggersModule { - public configure(consumer: MiddlewareConsumer): any { + public configure(consumer: MiddlewareConsumer): void { consumer .apply(AuthMiddleware) .forRoutes( diff --git a/backend/src/entities/table-actions/table-actions-module/table-action.module.ts b/backend/src/entities/table-actions/table-actions-module/table-action.module.ts index c35aeb7e6..cf2a1f248 100644 --- a/backend/src/entities/table-actions/table-actions-module/table-action.module.ts +++ b/backend/src/entities/table-actions/table-actions-module/table-action.module.ts @@ -21,7 +21,7 @@ import { TableActionActivationService } from './table-action-activation.service. exports: [TableActionActivationService], }) export class TableActionModule implements NestModule { - public configure(consumer: MiddlewareConsumer): any { + public configure(consumer: MiddlewareConsumer): void { consumer.apply(AuthMiddleware).forRoutes(); } } diff --git a/backend/src/entities/table-categories/table-categories.module.ts b/backend/src/entities/table-categories/table-categories.module.ts index 0663dbeb9..8297ad82b 100644 --- a/backend/src/entities/table-categories/table-categories.module.ts +++ b/backend/src/entities/table-categories/table-categories.module.ts @@ -33,7 +33,7 @@ import { FindTableCategoriesWithTablesUseCase } from './use-cases/find-table-cat controllers: [TableCategoriesController], }) export class TableCategoriesModule { - public configure(consumer: MiddlewareConsumer): any { + public configure(consumer: MiddlewareConsumer): void { consumer .apply(AuthMiddleware) .forRoutes( diff --git a/backend/src/entities/table-categories/use-cases/find-table-categories-with-tables.use.case.ts b/backend/src/entities/table-categories/use-cases/find-table-categories-with-tables.use.case.ts index 26aac5c62..06dece9e1 100644 --- a/backend/src/entities/table-categories/use-cases/find-table-categories-with-tables.use.case.ts +++ b/backend/src/entities/table-categories/use-cases/find-table-categories-with-tables.use.case.ts @@ -8,6 +8,7 @@ import { BaseType } from '../../../common/data-injection.tokens.js'; import { ExceptionOperations } from '../../../exceptions/custom-exceptions/exception-operation.js'; import { UnknownSQLException } from '../../../exceptions/custom-exceptions/unknown-sql-exception.js'; import { Messages } from '../../../exceptions/text/messages.js'; +import { getErrorMessage } from '../../../helpers/get-error-message.js'; import { isConnectionTypeAgent } from '../../../helpers/is-connection-entity-agent.js'; import { CedarPermissionsService } from '../../cedar-authorization/cedar-permissions.service.js'; import { ConnectionEntity } from '../../connection/connection.entity.js'; @@ -37,7 +38,7 @@ export class FindTableCategoriesWithTablesUseCase try { connection = await this._dbContext.connectionRepository.findAndDecryptConnection(connectionId, masterPwd); } catch (error) { - const errMessage = (error as Error).message; + const errMessage = getErrorMessage(error); if (errMessage === Messages.MASTER_PASSWORD_MISSING) { throw new HttpException( { @@ -76,7 +77,7 @@ export class FindTableCategoriesWithTablesUseCase tables = await dao.getTablesFromDB(userEmail); } catch (e) { Sentry.captureException(e); - throw new UnknownSQLException((e as Error).message, ExceptionOperations.FAILED_TO_GET_TABLES); + throw new UnknownSQLException(getErrorMessage(e), ExceptionOperations.FAILED_TO_GET_TABLES); } const tableNames = tables.map((t) => t.tableName); diff --git a/backend/src/entities/table-filters/table-filters.module.ts b/backend/src/entities/table-filters/table-filters.module.ts index 7deb4699e..2703247f8 100644 --- a/backend/src/entities/table-filters/table-filters.module.ts +++ b/backend/src/entities/table-filters/table-filters.module.ts @@ -49,7 +49,7 @@ import { UpdateTableFiltersUseCase } from './use-cases/update-table-filters.use. controllers: [TableFiltersController], }) export class TableFiltersModule { - public configure(consumer: MiddlewareConsumer): any { + public configure(consumer: MiddlewareConsumer): void { consumer .apply(AuthMiddleware) .forRoutes( diff --git a/backend/src/entities/table-logs/table-logs.module.ts b/backend/src/entities/table-logs/table-logs.module.ts index 54761f0ec..84407cd09 100644 --- a/backend/src/entities/table-logs/table-logs.module.ts +++ b/backend/src/entities/table-logs/table-logs.module.ts @@ -43,7 +43,7 @@ import { FindLogsUseCase } from './use-cases/find-logs.use.case.js'; exports: [TableLogsService], }) export class TableLogsModule { - public configure(consumer: MiddlewareConsumer): any { + public configure(consumer: MiddlewareConsumer): void { consumer .apply(AuthMiddleware) .forRoutes( diff --git a/backend/src/entities/table-schema/ai/run-schema-change-ai-loop.ts b/backend/src/entities/table-schema/ai/run-schema-change-ai-loop.ts index 7286985da..0e21869c8 100644 --- a/backend/src/entities/table-schema/ai/run-schema-change-ai-loop.ts +++ b/backend/src/entities/table-schema/ai/run-schema-change-ai-loop.ts @@ -7,6 +7,7 @@ import { AIProviderType } from '../../../ai-core/interfaces/ai-service.interface import { AICoreService } from '../../../ai-core/services/ai-core.service.js'; import { MessageBuilder } from '../../../ai-core/utils/message-builder.js'; import { encodeError, encodeToToon } from '../../../ai-core/utils/toon-encoder.js'; +import { getErrorMessage } from '../../../helpers/get-error-message.js'; import { isDynamoDbSchemaChangeType, isElasticsearchSchemaChangeType, @@ -239,8 +240,8 @@ async function executeInspectionToolCalls( result = encodeError({ error: `Unknown tool: ${tc.name}` }); } } catch (err) { - logger?.error(`Tool call ${tc.name} failed: ${(err as Error).message}`); - result = encodeError({ error: (err as Error).message }); + logger?.error(`Tool call ${tc.name} failed: ${getErrorMessage(err)}`); + result = encodeError({ error: getErrorMessage(err) }); } results.push({ toolCallId: tc.id, result }); } diff --git a/backend/src/entities/table-schema/use-cases/generate-schema-change.use-case.ts b/backend/src/entities/table-schema/use-cases/generate-schema-change.use-case.ts index 860ea1962..2dc613a1c 100644 --- a/backend/src/entities/table-schema/use-cases/generate-schema-change.use-case.ts +++ b/backend/src/entities/table-schema/use-cases/generate-schema-change.use-case.ts @@ -11,6 +11,7 @@ import AbstractUseCase from '../../../common/abstract-use.case.js'; import { IGlobalDatabaseContext } from '../../../common/application/global-database-context.interface.js'; import { BaseType } from '../../../common/data-injection.tokens.js'; import { Messages } from '../../../exceptions/text/messages.js'; +import { getErrorMessage } from '../../../helpers/get-error-message.js'; import { MessageRole } from '../../ai/ai-conversation-history/ai-chat-messages/message-role.enum.js'; import { runSchemaChangeAiLoop } from '../ai/run-schema-change-ai-loop.js'; import { buildSchemaChangePrompt } from '../ai/schema-change-prompts.js'; @@ -118,8 +119,8 @@ export class GenerateSchemaChangeUseCase }); proposals = result.proposals; } catch (err) { - this.logger.error(`AI loop failed: ${(err as Error).message}`); - throw new BadRequestException(`AI generation failed: ${(err as Error).message}`); + this.logger.error(`AI loop failed: ${getErrorMessage(err)}`); + throw new BadRequestException(`AI generation failed: ${getErrorMessage(err)}`); } for (let i = 0; i < proposals.length; i++) { @@ -316,7 +317,7 @@ User request: `; } } } catch (err) { - const message = (err as Error).message ?? 'validation error'; + const message = getErrorMessage(err) ?? 'validation error'; throw new BadRequestException(`${fieldHint}: ${message}`); } } diff --git a/backend/src/entities/table-schema/use-cases/rollback-batch-schema-changes.use-case.ts b/backend/src/entities/table-schema/use-cases/rollback-batch-schema-changes.use-case.ts index efe4edc80..642b2cf48 100644 --- a/backend/src/entities/table-schema/use-cases/rollback-batch-schema-changes.use-case.ts +++ b/backend/src/entities/table-schema/use-cases/rollback-batch-schema-changes.use-case.ts @@ -12,6 +12,7 @@ import AbstractUseCase from '../../../common/abstract-use.case.js'; import { IGlobalDatabaseContext } from '../../../common/application/global-database-context.interface.js'; import { BaseType } from '../../../common/data-injection.tokens.js'; import { Messages } from '../../../exceptions/text/messages.js'; +import { getErrorMessage } from '../../../helpers/get-error-message.js'; import { RollbackBatchSchemaChangeDs } from '../application/data-structures/rollback-batch-schema-change.ds.js'; import { SchemaChangeBatchResponseDto } from '../application/data-transfer-objects/schema-change-batch-response.dto.js'; import { SchemaChangeStatusEnum, SchemaChangeTypeEnum } from '../table-schema-change-enums.js'; @@ -101,7 +102,7 @@ export class RollbackBatchSchemaChangesUseCase appliedAt: new Date(), }); } catch (err) { - const message = (err as Error).message; + const message = getErrorMessage(err); this.logger.error(`Batch ${batchId}: rollback failed for change ${item.id}: ${message}`); failures.push({ changeId: item.id, error: message }); } diff --git a/backend/src/entities/table-schema/utils/dynamodb-schema-op.ts b/backend/src/entities/table-schema/utils/dynamodb-schema-op.ts index 55b729f8e..716943d12 100644 --- a/backend/src/entities/table-schema/utils/dynamodb-schema-op.ts +++ b/backend/src/entities/table-schema/utils/dynamodb-schema-op.ts @@ -23,6 +23,7 @@ import { } from '@aws-sdk/client-dynamodb'; import { BadRequestException } from '@nestjs/common'; import { isTest } from '../../../helpers/app/is-test.js'; +import { getErrorMessage } from '../../../helpers/get-error-message.js'; import { SchemaChangeTypeEnum } from '../table-schema-change-enums.js'; export interface DynamoDbExecutionConnection { @@ -96,7 +97,7 @@ export function validateProposedDynamoDbOp(input: ValidateDynamoDbOpInput): Dyna try { parsed = JSON.parse(opJson); } catch (err) { - throw new BadRequestException(`Proposed DynamoDB operation is not valid JSON: ${(err as Error).message}`); + throw new BadRequestException(`Proposed DynamoDB operation is not valid JSON: ${getErrorMessage(err)}`); } if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) { diff --git a/backend/src/entities/table-schema/utils/elasticsearch-schema-op.ts b/backend/src/entities/table-schema/utils/elasticsearch-schema-op.ts index 7cd400651..d41564fd1 100644 --- a/backend/src/entities/table-schema/utils/elasticsearch-schema-op.ts +++ b/backend/src/entities/table-schema/utils/elasticsearch-schema-op.ts @@ -1,5 +1,6 @@ import { Client } from '@elastic/elasticsearch'; import { BadRequestException } from '@nestjs/common'; +import { getErrorMessage } from '../../../helpers/get-error-message.js'; import { SchemaChangeTypeEnum } from '../table-schema-change-enums.js'; export interface ElasticsearchExecutionConnection { @@ -54,7 +55,7 @@ export function validateProposedElasticsearchOp(input: ValidateElasticsearchOpIn try { parsed = JSON.parse(opJson); } catch (err) { - throw new BadRequestException(`Proposed Elasticsearch operation is not valid JSON: ${(err as Error).message}`); + throw new BadRequestException(`Proposed Elasticsearch operation is not valid JSON: ${getErrorMessage(err)}`); } if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) { diff --git a/backend/src/entities/table-schema/utils/mongo-schema-op.ts b/backend/src/entities/table-schema/utils/mongo-schema-op.ts index 8424bfb00..798997660 100644 --- a/backend/src/entities/table-schema/utils/mongo-schema-op.ts +++ b/backend/src/entities/table-schema/utils/mongo-schema-op.ts @@ -1,5 +1,6 @@ import { BadRequestException } from '@nestjs/common'; import { Db, Document, IndexDirection, MongoClient, MongoClientOptions } from 'mongodb'; +import { getErrorMessage } from '../../../helpers/get-error-message.js'; import { SchemaChangeTypeEnum } from '../table-schema-change-enums.js'; export interface MongoExecutionConnection { @@ -65,7 +66,7 @@ export function validateProposedMongoOp(input: ValidateMongoOpInput): MongoSchem try { parsed = JSON.parse(opJson); } catch (err) { - throw new BadRequestException(`Proposed Mongo operation is not valid JSON: ${(err as Error).message}`); + throw new BadRequestException(`Proposed Mongo operation is not valid JSON: ${getErrorMessage(err)}`); } if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) { diff --git a/backend/src/entities/table-schema/utils/validate-proposed-ddl.ts b/backend/src/entities/table-schema/utils/validate-proposed-ddl.ts index e50e0be73..ecb69a2c7 100644 --- a/backend/src/entities/table-schema/utils/validate-proposed-ddl.ts +++ b/backend/src/entities/table-schema/utils/validate-proposed-ddl.ts @@ -4,6 +4,7 @@ import sqlParser from 'node-sql-parser'; const { Parser } = sqlParser; +import { getErrorMessage } from '../../../helpers/get-error-message.js'; import { isMongoSchemaChangeType, SchemaChangeTypeEnum } from '../table-schema-change-enums.js'; import { connectionTypeToParserDialect } from './assert-dialect-supported.js'; @@ -109,7 +110,7 @@ export function validateProposedDdl(opts: ValidateProposedDdlOptions): void { // block legitimate statements. // eslint-disable-next-line no-console console.warn( - `[validate-proposed-ddl] parser could not analyze SQL (${(err as Error).message}); falling back to regex checks. sql=${trimmed.slice(0, 200)}`, + `[validate-proposed-ddl] parser could not analyze SQL (${getErrorMessage(err)}); falling back to regex checks. sql=${trimmed.slice(0, 200)}`, ); return; } diff --git a/backend/src/entities/table-settings/common-table-settings/repository/table-settings-custom-repository-extension.ts b/backend/src/entities/table-settings/common-table-settings/repository/table-settings-custom-repository-extension.ts index 2abe808a8..d8cd459a0 100644 --- a/backend/src/entities/table-settings/common-table-settings/repository/table-settings-custom-repository-extension.ts +++ b/backend/src/entities/table-settings/common-table-settings/repository/table-settings-custom-repository-extension.ts @@ -1,5 +1,6 @@ import { HttpException, HttpStatus } from '@nestjs/common'; import { Messages } from '../../../../exceptions/text/messages.js'; +import { getErrorMessage } from '../../../../helpers/get-error-message.js'; import { ConnectionEntity } from '../../../connection/connection.entity.js'; import { CreateTableSettingsDs } from '../../application/data-structures/create-table-settings.ds.js'; import { TableSettingsEntity } from '../table-settings.entity.js'; @@ -54,7 +55,7 @@ export const tableSettingsCustomRepositoryExtension: ITableSettingsRepository = try { return await qb.getOne(); } catch (e) { - console.info(`Table setting not found exception. => `, (e as Error).message); + console.info(`Table setting not found exception. => `, getErrorMessage(e)); throw new HttpException( { message: Messages.TABLE_SETTINGS_NOT_FOUND, @@ -83,7 +84,10 @@ export const tableSettingsCustomRepositoryExtension: ITableSettingsRepository = }, //todo: remove after dao's and table settings refactor - async findTableSettingsOrReturnEmpty(connectionId: string, tableName: string): Promise { + async findTableSettingsOrReturnEmpty( + connectionId: string, + tableName: string, + ): Promise> { const foundSettings = await this.findTableSettings(connectionId, tableName); return foundSettings ? foundSettings : {}; }, diff --git a/backend/src/entities/table-settings/common-table-settings/repository/table-settings.repository.interface.ts b/backend/src/entities/table-settings/common-table-settings/repository/table-settings.repository.interface.ts index a9aa66af1..47bf412cc 100644 --- a/backend/src/entities/table-settings/common-table-settings/repository/table-settings.repository.interface.ts +++ b/backend/src/entities/table-settings/common-table-settings/repository/table-settings.repository.interface.ts @@ -23,7 +23,10 @@ export interface ITableSettingsRepository { findTableSettingsInConnectionPure(connectionId: string): Promise>; - findTableSettingsOrReturnEmpty(connectionId: string, tableName: string): Promise; + findTableSettingsOrReturnEmpty( + connectionId: string, + tableName: string, + ): Promise>; removeTableSettings(tableSettings: TableSettingsEntity): Promise; diff --git a/backend/src/entities/table-settings/common-table-settings/table-settings.module.ts b/backend/src/entities/table-settings/common-table-settings/table-settings.module.ts index 92019b8b6..ec7860f1f 100644 --- a/backend/src/entities/table-settings/common-table-settings/table-settings.module.ts +++ b/backend/src/entities/table-settings/common-table-settings/table-settings.module.ts @@ -62,7 +62,7 @@ import { UpdateTableSettingsUseCase } from './use-cases/update-table-settings.us exports: [], }) export class TableSettingsModule { - public configure(consumer: MiddlewareConsumer): any { + public configure(consumer: MiddlewareConsumer): void { consumer .apply(AuthMiddleware) .forRoutes( diff --git a/backend/src/entities/table-settings/personal-table-settings/personal-table-settings.module.ts b/backend/src/entities/table-settings/personal-table-settings/personal-table-settings.module.ts index 069274893..e70fcc098 100644 --- a/backend/src/entities/table-settings/personal-table-settings/personal-table-settings.module.ts +++ b/backend/src/entities/table-settings/personal-table-settings/personal-table-settings.module.ts @@ -29,7 +29,7 @@ import { FindPersonalTableSettingsUseCase } from './use-cases/find-personal-tabl exports: [], }) export class PersonalTableSettingsModule { - public configure(consumer: MiddlewareConsumer): any { + public configure(consumer: MiddlewareConsumer): void { consumer .apply(AuthMiddleware) .forRoutes( diff --git a/backend/src/entities/table/table.module.ts b/backend/src/entities/table/table.module.ts index 1261bb57f..8734482fd 100644 --- a/backend/src/entities/table/table.module.ts +++ b/backend/src/entities/table/table.module.ts @@ -108,7 +108,7 @@ import { UpdateRowInTableUseCase } from './use-cases/update-row-in-table.use.cas controllers: [TableController], }) export class TableModule { - public configure(consumer: MiddlewareConsumer): any { + public configure(consumer: MiddlewareConsumer): void { consumer .apply(AuthWithApiMiddleware) .forRoutes( diff --git a/backend/src/entities/table/use-cases/add-row-in-table.use.case.ts b/backend/src/entities/table/use-cases/add-row-in-table.use.case.ts index 01a907127..1a8a0ff2c 100644 --- a/backend/src/entities/table/use-cases/add-row-in-table.use.case.ts +++ b/backend/src/entities/table/use-cases/add-row-in-table.use.case.ts @@ -10,6 +10,7 @@ import { LogOperationTypeEnum } from '../../../enums/log-operation-type.enum.js' import { OperationResultStatusEnum } from '../../../enums/operation-result-status.enum.js'; import { TableActionEventEnum } from '../../../enums/table-action-event-enum.js'; import { Messages } from '../../../exceptions/text/messages.js'; +import { getErrorMessage } from '../../../helpers/get-error-message.js'; import { isObjectEmpty } from '../../../helpers/is-object-empty.js'; import { toPrettyErrorsMsg } from '../../../helpers/to-pretty-errors-msg.js'; import { AmplitudeService } from '../../amplitude/amplitude.service.js'; @@ -194,10 +195,10 @@ export class AddRowInTableUseCase extends AbstractUseCase { return { diff --git a/backend/src/entities/table/use-cases/delete-row-from-table.use.case.ts b/backend/src/entities/table/use-cases/delete-row-from-table.use.case.ts index 577ad299a..c1280b9d0 100644 --- a/backend/src/entities/table/use-cases/delete-row-from-table.use.case.ts +++ b/backend/src/entities/table/use-cases/delete-row-from-table.use.case.ts @@ -13,6 +13,7 @@ import { ExceptionOperations } from '../../../exceptions/custom-exceptions/excep import { UnknownSQLException } from '../../../exceptions/custom-exceptions/unknown-sql-exception.js'; import { Messages } from '../../../exceptions/text/messages.js'; import { compareArrayElements } from '../../../helpers/compare-array-elements.js'; +import { getErrorMessage } from '../../../helpers/get-error-message.js'; import { AmplitudeService } from '../../amplitude/amplitude.service.js'; import { isTestConnectionUtil } from '../../connection/utils/is-test-connection-util.js'; import { TableActionActivationService } from '../../table-actions/table-actions-module/table-action-activation.service.js'; @@ -119,7 +120,7 @@ export class DeleteRowFromTableUseCase try { oldRowData = await dao.getRowByPrimaryKey(tableName, primaryKey, builtTableSettings, userEmail); } catch (e) { - throw new UnknownSQLException((e as Error).message, ExceptionOperations.FAILED_TO_DELETE_ROW_FROM_TABLE); + throw new UnknownSQLException(getErrorMessage(e), ExceptionOperations.FAILED_TO_DELETE_ROW_FROM_TABLE); } if (!oldRowData) { @@ -139,7 +140,7 @@ export class DeleteRowFromTableUseCase }; } catch (e) { operationResult = OperationResultStatusEnum.unsuccessfully; - throw new DeleteRowException((e as Error).message); + throw new DeleteRowException(getErrorMessage(e)); } finally { const logRecord = { table_name: tableName, diff --git a/backend/src/entities/table/use-cases/delete-rows-from-table.use.case.ts b/backend/src/entities/table/use-cases/delete-rows-from-table.use.case.ts index e59d7314d..b1fd27fae 100644 --- a/backend/src/entities/table/use-cases/delete-rows-from-table.use.case.ts +++ b/backend/src/entities/table/use-cases/delete-rows-from-table.use.case.ts @@ -9,6 +9,7 @@ import { LogOperationTypeEnum } from '../../../enums/log-operation-type.enum.js' import { OperationResultStatusEnum } from '../../../enums/operation-result-status.enum.js'; import { Messages } from '../../../exceptions/text/messages.js'; import { compareArrayElements } from '../../../helpers/compare-array-elements.js'; +import { getErrorMessage } from '../../../helpers/get-error-message.js'; import { AmplitudeService } from '../../amplitude/amplitude.service.js'; import { isTestConnectionUtil } from '../../connection/utils/is-test-connection-util.js'; import { TableLogsService } from '../../table-logs/table-logs.service.js'; @@ -117,7 +118,7 @@ export class DeleteRowsFromTableUseCase } catch (error) { throw new HttpException( { - message: Messages.BULK_DELETE_FAILED_GET_ROWS([(error as Error).message]), + message: Messages.BULK_DELETE_FAILED_GET_ROWS([getErrorMessage(error)]), }, HttpStatus.INTERNAL_SERVER_ERROR, ); @@ -143,7 +144,7 @@ export class DeleteRowsFromTableUseCase operationStatusResult: OperationResultStatusEnum.unsuccessfully, row: primaryKey, old_data: findObjectsWithProperties(oldRowsData, primaryKey).at(0), - error: (error as Error).message, + error: getErrorMessage(error), affected_primary_key: primaryKey as unknown as string, }); }); diff --git a/backend/src/entities/table/use-cases/export-csv-from-table.use.case.ts b/backend/src/entities/table/use-cases/export-csv-from-table.use.case.ts index ac883647c..1a3e0fb49 100644 --- a/backend/src/entities/table/use-cases/export-csv-from-table.use.case.ts +++ b/backend/src/entities/table/use-cases/export-csv-from-table.use.case.ts @@ -9,6 +9,7 @@ import { LogOperationTypeEnum } from '../../../enums/log-operation-type.enum.js' import { OperationResultStatusEnum } from '../../../enums/operation-result-status.enum.js'; import { Messages } from '../../../exceptions/text/messages.js'; import { hexToBinary, isBinary } from '../../../helpers/binary-to-hex.js'; +import { getErrorMessage } from '../../../helpers/get-error-message.js'; import { isConnectionTypeAgent } from '../../../helpers/is-connection-entity-agent.js'; import { isObjectEmpty } from '../../../helpers/is-object-empty.js'; import { slackPostMessage } from '../../../helpers/slack/slack-post-message.js'; @@ -119,7 +120,7 @@ export class ExportCSVFromTableUseCase } // todo: temporary debug log await slackPostMessage(` - CSV Export Failed with error: ${(error as Error).message}\n + CSV Export Failed with error: ${getErrorMessage(error)}\n Connection type: ${connection.type}\n SSH Option: ${connection.ssh}\n SSL Option: ${connection.ssl}\n @@ -127,7 +128,7 @@ export class ExportCSVFromTableUseCase throw new HttpException( { message: Messages.CSV_EXPORT_FAILED, - originalMessage: (error as Error).message, + originalMessage: getErrorMessage(error), }, HttpStatus.INTERNAL_SERVER_ERROR, ); diff --git a/backend/src/entities/table/use-cases/find-tables-in-connection-v2.use.case.ts b/backend/src/entities/table/use-cases/find-tables-in-connection-v2.use.case.ts index f9c8cbee6..5252217b5 100644 --- a/backend/src/entities/table/use-cases/find-tables-in-connection-v2.use.case.ts +++ b/backend/src/entities/table/use-cases/find-tables-in-connection-v2.use.case.ts @@ -10,6 +10,7 @@ import { ExceptionOperations } from '../../../exceptions/custom-exceptions/excep import { UnknownSQLException } from '../../../exceptions/custom-exceptions/unknown-sql-exception.js'; import { Messages } from '../../../exceptions/text/messages.js'; import { isTest as isTestEnv } from '../../../helpers/app/is-test.js'; +import { getErrorMessage } from '../../../helpers/get-error-message.js'; import { isConnectionTypeAgent } from '../../../helpers/is-connection-entity-agent.js'; import { AmplitudeService } from '../../amplitude/amplitude.service.js'; import { CedarPermissionsService } from '../../cedar-authorization/cedar-permissions.service.js'; @@ -44,7 +45,7 @@ export class FindTablesInConnectionV2UseCase try { connection = await this._dbContext.connectionRepository.findAndDecryptConnection(connectionId, masterPwd); } catch (error) { - const errMessage = (error as Error).message; + const errMessage = getErrorMessage(error); if (errMessage === Messages.MASTER_PASSWORD_MISSING) { throw new HttpException( { @@ -85,7 +86,7 @@ export class FindTablesInConnectionV2UseCase } catch (e) { operationResult = false; Sentry.captureException(e); - throw new UnknownSQLException((e as Error).message, ExceptionOperations.FAILED_TO_GET_TABLES); + throw new UnknownSQLException(getErrorMessage(e), ExceptionOperations.FAILED_TO_GET_TABLES); } finally { if (!connection.isTestConnection && tables && tables.length) { this.logger.log({ diff --git a/backend/src/entities/table/use-cases/find-tables-in-connection.use.case.ts b/backend/src/entities/table/use-cases/find-tables-in-connection.use.case.ts index 62e4d182b..8ad454bbd 100644 --- a/backend/src/entities/table/use-cases/find-tables-in-connection.use.case.ts +++ b/backend/src/entities/table/use-cases/find-tables-in-connection.use.case.ts @@ -11,6 +11,7 @@ import { ExceptionOperations } from '../../../exceptions/custom-exceptions/excep import { UnknownSQLException } from '../../../exceptions/custom-exceptions/unknown-sql-exception.js'; import { Messages } from '../../../exceptions/text/messages.js'; import { isTest as isTestEnv } from '../../../helpers/app/is-test.js'; +import { getErrorMessage } from '../../../helpers/get-error-message.js'; import { isConnectionTypeAgent } from '../../../helpers/is-connection-entity-agent.js'; import { AmplitudeService } from '../../amplitude/amplitude.service.js'; import { CedarPermissionsService } from '../../cedar-authorization/cedar-permissions.service.js'; @@ -45,7 +46,7 @@ export class FindTablesInConnectionUseCase try { connection = await this._dbContext.connectionRepository.findAndDecryptConnection(connectionId, masterPwd); } catch (error) { - const errMessage = (error as Error).message; + const errMessage = getErrorMessage(error); if (errMessage === Messages.MASTER_PASSWORD_MISSING) { throw new HttpException( { @@ -89,7 +90,7 @@ export class FindTablesInConnectionUseCase } catch (e) { operationResult = false; Sentry.captureException(e); - throw new UnknownSQLException((e as Error).message, ExceptionOperations.FAILED_TO_GET_TABLES); + throw new UnknownSQLException(getErrorMessage(e), ExceptionOperations.FAILED_TO_GET_TABLES); } finally { if (!connection.isTestConnection && tables && tables.length) { this.logger.log({ diff --git a/backend/src/entities/table/use-cases/get-row-by-primary-key.use.case.ts b/backend/src/entities/table/use-cases/get-row-by-primary-key.use.case.ts index 8793efc49..382bdc6a8 100644 --- a/backend/src/entities/table/use-cases/get-row-by-primary-key.use.case.ts +++ b/backend/src/entities/table/use-cases/get-row-by-primary-key.use.case.ts @@ -11,6 +11,7 @@ import { ExceptionOperations } from '../../../exceptions/custom-exceptions/excep import { UnknownSQLException } from '../../../exceptions/custom-exceptions/unknown-sql-exception.js'; import { Messages } from '../../../exceptions/text/messages.js'; import { compareArrayElements } from '../../../helpers/compare-array-elements.js'; +import { getErrorMessage } from '../../../helpers/get-error-message.js'; import { CedarPermissionsService } from '../../cedar-authorization/cedar-permissions.service.js'; import { buildActionEventDto } from '../../table-actions/table-action-rules-module/utils/build-found-action-event-dto.util.js'; import { GetRowByPrimaryKeyDs } from '../application/data-structures/get-row-by-primary-key.ds.js'; @@ -134,7 +135,7 @@ export class GetRowByPrimaryKeyUseCase try { rowData = await dao.getRowByPrimaryKey(tableName, primaryKey, builtDAOsTableSettings, userEmail); } catch (e) { - throw new UnknownSQLException((e as Error).message, ExceptionOperations.FAILED_TO_GET_ROW_BY_PRIMARY_KEY); + throw new UnknownSQLException(getErrorMessage(e), ExceptionOperations.FAILED_TO_GET_ROW_BY_PRIMARY_KEY); } if (!rowData) { diff --git a/backend/src/entities/table/use-cases/get-table-rows.use.case.ts b/backend/src/entities/table/use-cases/get-table-rows.use.case.ts index 2d880af6c..e01ed4fef 100644 --- a/backend/src/entities/table/use-cases/get-table-rows.use.case.ts +++ b/backend/src/entities/table/use-cases/get-table-rows.use.case.ts @@ -21,6 +21,7 @@ import { UnknownSQLException } from '../../../exceptions/custom-exceptions/unkno import { Messages } from '../../../exceptions/text/messages.js'; import { hexToBinary, isBinary } from '../../../helpers/binary-to-hex.js'; import { Constants } from '../../../helpers/constants/constants.js'; +import { getErrorMessage } from '../../../helpers/get-error-message.js'; import { isObjectEmpty } from '../../../helpers/is-object-empty.js'; import { AmplitudeService } from '../../amplitude/amplitude.service.js'; import { CedarPermissionsService } from '../../cedar-authorization/cedar-permissions.service.js'; @@ -165,7 +166,7 @@ export class GetTableRowsUseCase extends AbstractUseCase isObjectPropertyExists(row, fieldName) && getPropertyValueByDescriptor(row, fieldName)) - .map((fieldName) => getPropertyValueByDescriptor(row, fieldName)); + .map((fieldName) => String(getPropertyValueByDescriptor(row, fieldName))); const generatedUrlString = replaceTextInCurlies( field.template_string, diff --git a/backend/src/entities/user-actions/use-cases/check-users-actions-and-mailing-users.use.case.ts b/backend/src/entities/user-actions/use-cases/check-users-actions-and-mailing-users.use.case.ts index b8b1f121d..846caf30e 100644 --- a/backend/src/entities/user-actions/use-cases/check-users-actions-and-mailing-users.use.case.ts +++ b/backend/src/entities/user-actions/use-cases/check-users-actions-and-mailing-users.use.case.ts @@ -5,6 +5,7 @@ import PQueue from 'p-queue'; import { Repository } from 'typeorm'; import { UserActionEnum } from '../../../enums/user-action.enum.js'; import { Constants } from '../../../helpers/constants/constants.js'; +import { getErrorMessage } from '../../../helpers/get-error-message.js'; import { getUniqArrayStrings } from '../../../helpers/get-uniq-array-strings.js'; import { UserEntity } from '../../user/user.entity.js'; import { UserActionEntity } from '../user-action.entity.js'; @@ -35,7 +36,7 @@ export class CheckUsersActionsAndMailingUsersUseCase implements ICheckUsersActio } }); } catch (error) { - console.error(`Error processing user ${user.id}: ${(error as Error).message}`); + console.error(`Error processing user ${user.id}: ${getErrorMessage(error)}`); Sentry.captureException(error); } } diff --git a/backend/src/entities/user-sign-in-audit/sign-in-audit.module.ts b/backend/src/entities/user-sign-in-audit/sign-in-audit.module.ts index c09fde1ec..20ee5a81a 100644 --- a/backend/src/entities/user-sign-in-audit/sign-in-audit.module.ts +++ b/backend/src/entities/user-sign-in-audit/sign-in-audit.module.ts @@ -28,7 +28,7 @@ import { FindSignInAuditLogsUseCase } from './use-cases/find-sign-in-audit-logs. exports: [SignInAuditService], }) export class SignInAuditModule { - public configure(consumer: MiddlewareConsumer): any { + public configure(consumer: MiddlewareConsumer): void { consumer.apply(AuthMiddleware).forRoutes({ path: '/sign-in-audit/logs', method: RequestMethod.GET }); } } diff --git a/backend/src/entities/user/use-cases/log-out.use.case.ts b/backend/src/entities/user/use-cases/log-out.use.case.ts index c2de892dd..5940bc59e 100644 --- a/backend/src/entities/user/use-cases/log-out.use.case.ts +++ b/backend/src/entities/user/use-cases/log-out.use.case.ts @@ -3,6 +3,7 @@ import AbstractUseCase from '../../../common/abstract-use.case.js'; import { IGlobalDatabaseContext } from '../../../common/application/global-database-context.interface.js'; import { BaseType } from '../../../common/data-injection.tokens.js'; import { Messages } from '../../../exceptions/text/messages.js'; +import { getErrorMessage } from '../../../helpers/get-error-message.js'; import { ILogOut } from './user-use-cases.interfaces.js'; @Injectable() @@ -21,7 +22,7 @@ export class LogOutUseCase extends AbstractUseCase implements I } catch (e) { throw new HttpException( { - message: Messages.FAILED_LOGOUT + ' Error: ' + (e as Error).message, + message: Messages.FAILED_LOGOUT + ' Error: ' + getErrorMessage(e), }, HttpStatus.INTERNAL_SERVER_ERROR, ); diff --git a/backend/src/entities/user/user.controller.ts b/backend/src/entities/user/user.controller.ts index 1e26473a8..d087c24a6 100644 --- a/backend/src/entities/user/user.controller.ts +++ b/backend/src/entities/user/user.controller.ts @@ -191,7 +191,7 @@ export class UserController { description: 'Logged out.', }) @Post('user/logout/') - async logOut(@Req() request: Request, @Res({ passthrough: true }) response: Response): Promise { + async logOut(@Req() request: Request, @Res({ passthrough: true }) response: Response): Promise { const token = request.cookies[Constants.JWT_COOKIE_KEY_NAME]; if (!token) { throw new HttpException( @@ -448,7 +448,7 @@ export class UserController { @Res({ passthrough: true }) response: Response, @UserId() userId: string, @Body() otpTokenData: OtpTokenDto, - ): Promise { + ): Promise { const { otpToken } = otpTokenData; const ipAddress = (request.headers['x-forwarded-for'] as string) || request.ip; const userAgent = request.headers['user-agent'] as string; diff --git a/backend/src/entities/visualizations/panel-position/use-cases/generate-panel-position-with-ai.use.case.ts b/backend/src/entities/visualizations/panel-position/use-cases/generate-panel-position-with-ai.use.case.ts index 98b9ba7cc..962d05d08 100644 --- a/backend/src/entities/visualizations/panel-position/use-cases/generate-panel-position-with-ai.use.case.ts +++ b/backend/src/entities/visualizations/panel-position/use-cases/generate-panel-position-with-ai.use.case.ts @@ -15,6 +15,7 @@ import { IGlobalDatabaseContext } from '../../../../common/application/global-da import { BaseType } from '../../../../common/data-injection.tokens.js'; import { DashboardWidgetTypeEnum } from '../../../../enums/dashboard-widget-type.enum.js'; import { Messages } from '../../../../exceptions/text/messages.js'; +import { getErrorMessage } from '../../../../helpers/get-error-message.js'; import { isConnectionTypeAgent } from '../../../../helpers/is-connection-entity-agent.js'; import { validateQuerySafety } from '../../panel/utils/check-query-is-safe.util.js'; import { GeneratePanelPositionWithAiDs } from '../data-structures/generate-panel-position-with-ai.ds.js'; @@ -285,7 +286,7 @@ IMPORTANT GUIDELINES: result = encodeError({ error: `Unknown tool: ${toolCall.name}` }); } } catch (error) { - result = encodeError({ error: (error as Error).message }); + result = encodeError({ error: getErrorMessage(error) }); } results.push({ toolCallId: toolCall.id, result }); @@ -306,7 +307,7 @@ IMPORTANT GUIDELINES: return parsed; } catch (error) { - this.logger.error('Error parsing AI response:', (error as Error).message); + this.logger.error('Error parsing AI response:', getErrorMessage(error)); throw new BadRequestException( 'Failed to generate panel configuration from AI. Please try again with a different description.', ); @@ -382,7 +383,7 @@ IMPORTANT GUIDELINES: const result = await (dao as IDataAccessObject).executeRawQuery(explainQuery, ''); return { success: true, result: JSON.stringify(result, null, 2) }; } catch (error) { - return { success: false, error: (error as Error).message }; + return { success: false, error: getErrorMessage(error) }; } } diff --git a/backend/src/entities/visualizations/panel-position/use-cases/generate-table-dashboard-with-ai.use.case.ts b/backend/src/entities/visualizations/panel-position/use-cases/generate-table-dashboard-with-ai.use.case.ts index f97ab50ec..fdff0b07a 100644 --- a/backend/src/entities/visualizations/panel-position/use-cases/generate-table-dashboard-with-ai.use.case.ts +++ b/backend/src/entities/visualizations/panel-position/use-cases/generate-table-dashboard-with-ai.use.case.ts @@ -15,6 +15,7 @@ import { IGlobalDatabaseContext } from '../../../../common/application/global-da import { BaseType } from '../../../../common/data-injection.tokens.js'; import { DashboardWidgetTypeEnum } from '../../../../enums/dashboard-widget-type.enum.js'; import { Messages } from '../../../../exceptions/text/messages.js'; +import { getErrorMessage } from '../../../../helpers/get-error-message.js'; import { isConnectionTypeAgent } from '../../../../helpers/is-connection-entity-agent.js'; import { DashboardEntity } from '../../dashboard/dashboard.entity.js'; import { PanelEntity } from '../../panel/panel.entity.js'; @@ -161,7 +162,7 @@ export class GenerateTableDashboardWithAiUseCase }, }); } catch (error) { - this.logger.warn(`Panel "${panel.name}" skipped: ${(error as Error).message}`); + this.logger.warn(`Panel "${panel.name}" skipped: ${getErrorMessage(error)}`); } } @@ -353,7 +354,7 @@ IMPORTANT GUIDELINES: result = encodeError({ error: `Unknown tool: ${toolCall.name}` }); } } catch (error) { - result = encodeError({ error: (error as Error).message }); + result = encodeError({ error: getErrorMessage(error) }); } results.push({ toolCallId: toolCall.id, result }); @@ -394,7 +395,7 @@ IMPORTANT GUIDELINES: return parsed; } catch (error) { - this.logger.error('Error parsing dashboard AI response:', (error as Error).message); + this.logger.error('Error parsing dashboard AI response:', getErrorMessage(error)); throw new BadRequestException('Failed to generate dashboard from AI. Please try again.'); } } @@ -454,7 +455,7 @@ IMPORTANT GUIDELINES: const result = await (dao as IDataAccessObject).executeRawQuery(explainQuery, ''); return { success: true, result: JSON.stringify(result, null, 2) }; } catch (error) { - return { success: false, error: (error as Error).message }; + return { success: false, error: getErrorMessage(error) }; } } diff --git a/backend/src/entities/visualizations/panel/panel.entity.ts b/backend/src/entities/visualizations/panel/panel.entity.ts index f6282dc71..7290a98b5 100644 --- a/backend/src/entities/visualizations/panel/panel.entity.ts +++ b/backend/src/entities/visualizations/panel/panel.entity.ts @@ -13,6 +13,7 @@ import { } from 'typeorm'; import { DashboardWidgetTypeEnum } from '../../../enums/dashboard-widget-type.enum.js'; import { Encryptor } from '../../../helpers/encryption/encryptor.js'; +import { getErrorMessage } from '../../../helpers/get-error-message.js'; import { ConnectionEntity } from '../../connection/connection.entity.js'; import { PanelPositionEntity } from '../panel-position/panel-position.entity.js'; @@ -55,7 +56,7 @@ export class PanelEntity { this.panel_options = JSON.stringify(this.panel_options); } } catch (e) { - console.error('-> Error widget options stringify ' + (e as Error).message); + console.error('-> Error widget options stringify ' + getErrorMessage(e)); } } @@ -70,7 +71,7 @@ export class PanelEntity { this.panel_options = JSON.stringify(this.panel_options); } } catch (e) { - console.error('-> Error widget options stringify ' + (e as Error).message); + console.error('-> Error widget options stringify ' + getErrorMessage(e)); } } @@ -87,7 +88,7 @@ export class PanelEntity { }); } } catch (e) { - console.error('-> Error widget options parse ' + (e as Error).message); + console.error('-> Error widget options parse ' + getErrorMessage(e)); } } diff --git a/backend/src/entities/visualizations/panel/utils/collect-query-tables.util.ts b/backend/src/entities/visualizations/panel/utils/collect-query-tables.util.ts index 98784a54c..4f776d2da 100644 --- a/backend/src/entities/visualizations/panel/utils/collect-query-tables.util.ts +++ b/backend/src/entities/visualizations/panel/utils/collect-query-tables.util.ts @@ -1,5 +1,6 @@ import { ConnectionTypesEnum } from '@rocketadmin/shared-code/dist/src/shared/enums/connection-types-enum.js'; import sqlParser from 'node-sql-parser'; +import { getErrorMessage } from '../../../../helpers/get-error-message.js'; import { connectionTypeToParserDialect } from '../../../table-schema/utils/assert-dialect-supported.js'; const { Parser } = sqlParser; @@ -42,7 +43,7 @@ export function collectQueryTables(query: string, connectionType: ConnectionType rawTableList = parser.tableList(query, { database: dialect }); cteNames = collectCteNames(parser, query, dialect); } catch (error) { - return { kind: 'indeterminate', reason: `parse error: ${(error as Error).message}` }; + return { kind: 'indeterminate', reason: `parse error: ${getErrorMessage(error)}` }; } const tables = new Set(); diff --git a/backend/src/entities/widget/table-widget.entity.ts b/backend/src/entities/widget/table-widget.entity.ts index 3043f6362..e31b22df7 100644 --- a/backend/src/entities/widget/table-widget.entity.ts +++ b/backend/src/entities/widget/table-widget.entity.ts @@ -13,6 +13,7 @@ import { UpdateDateColumn, } from 'typeorm'; import { WidgetTypeEnum } from '../../enums/widget-type.enum.js'; +import { getErrorMessage } from '../../helpers/get-error-message.js'; import { TableSettingsEntity } from '../table-settings/common-table-settings/table-settings.entity.js'; @Entity('table_widget') @@ -51,7 +52,7 @@ export class TableWidgetEntity { this.widget_options = JSON.stringify(this.widget_options); } } catch (e) { - console.error('-> Error widget options stringify ' + (e as Error).message); + console.error('-> Error widget options stringify ' + getErrorMessage(e)); } } @@ -62,7 +63,7 @@ export class TableWidgetEntity { this.widget_options = JSON.stringify(this.widget_options); } } catch (e) { - console.error('-> Error widget options stringify ' + (e as Error).message); + console.error('-> Error widget options stringify ' + getErrorMessage(e)); } } @@ -76,7 +77,7 @@ export class TableWidgetEntity { }); } } catch (e) { - console.error('-> Error widget options parse ' + (e as Error).message); + console.error('-> Error widget options parse ' + getErrorMessage(e)); } } diff --git a/backend/src/entities/widget/table-widget.module.ts b/backend/src/entities/widget/table-widget.module.ts index 7b52b8428..e61466d4a 100644 --- a/backend/src/entities/widget/table-widget.module.ts +++ b/backend/src/entities/widget/table-widget.module.ts @@ -31,7 +31,7 @@ import { FindTableWidgetsUseCase } from './use-cases/find-table-widgets.use.case exports: [], }) export class TableWidgetModule { - public configure(consumer: MiddlewareConsumer): any { + public configure(consumer: MiddlewareConsumer): void { consumer .apply(AuthMiddleware) .forRoutes( diff --git a/backend/src/exceptions/all-exceptions.filter.ts b/backend/src/exceptions/all-exceptions.filter.ts index adb4b49df..00ab8d6f2 100644 --- a/backend/src/exceptions/all-exceptions.filter.ts +++ b/backend/src/exceptions/all-exceptions.filter.ts @@ -1,22 +1,38 @@ import { ArgumentsHost, Catch, ExceptionFilter, HttpException, HttpStatus } from '@nestjs/common'; import Sentry from '@sentry/minimal'; import { WinstonLogger } from '../entities/logging/winston-logger.js'; +import { getErrorMessage } from '../helpers/get-error-message.js'; import { Messages } from './text/messages.js'; import { processExceptionMessage } from './utils/process-exception-message.js'; export type ExceptionType = 'no_master_key' | 'invalid_master_key' | 'query_timeout'; + +interface RocketadminException { + response?: { type?: string }; + originalMessage?: string; + internalCode?: string | number; +} + +function asRocketadminException(exception: unknown): RocketadminException { + if (exception && typeof exception === 'object') { + return exception as RocketadminException; + } + return {}; +} + @Catch() export class AllExceptionsFilter implements ExceptionFilter { constructor(private readonly logger: WinstonLogger) {} - async catch(exception: any, host: ArgumentsHost) { + async catch(exception: unknown, host: ArgumentsHost) { const ctx = host.switchToHttp(); const response = ctx.getResponse(); const request = ctx.getRequest(); - let text = exception.message; + let text = getErrorMessage(exception); text = processExceptionMessage(text); - const type = exception?.response?.type; - const originalMessage = exception?.originalMessage; - const internalCode = exception?.internalCode; + const meta = asRocketadminException(exception); + const type = meta.response?.type; + const originalMessage = meta.originalMessage; + const internalCode = meta.internalCode; const status = exception instanceof HttpException ? exception.getStatus() : HttpStatus.INTERNAL_SERVER_ERROR; const sentryContextObject = { extra: { diff --git a/backend/src/helpers/compare-array-elements.ts b/backend/src/helpers/compare-array-elements.ts index 22ce708b6..481b86453 100644 --- a/backend/src/helpers/compare-array-elements.ts +++ b/backend/src/helpers/compare-array-elements.ts @@ -1,9 +1,9 @@ -export function compareArrayElements(arr1: Array, arr2: Array): boolean { +export function compareArrayElements(arr1: ReadonlyArray, arr2: ReadonlyArray): boolean { if (arr1.length !== arr2.length) { return false; } for (let i = 0; i < arr1.length; i++) { - const include = arr2.includes(arr1.at(i)); + const include = arr2.includes(arr1[i]); if (!include) { return false; } diff --git a/backend/src/helpers/enum-to-string.ts b/backend/src/helpers/enum-to-string.ts index 93e5f42bc..ab093d415 100644 --- a/backend/src/helpers/enum-to-string.ts +++ b/backend/src/helpers/enum-to-string.ts @@ -1,3 +1,3 @@ -export function enumToString(en: any): string { +export function enumToString>(en: E): string { return Object.values(en).join(', '); } diff --git a/backend/src/helpers/get-property-value-by-descriptor.ts b/backend/src/helpers/get-property-value-by-descriptor.ts index d346603b0..9a565e5d8 100644 --- a/backend/src/helpers/get-property-value-by-descriptor.ts +++ b/backend/src/helpers/get-property-value-by-descriptor.ts @@ -1,3 +1,3 @@ -export function getPropertyValueByDescriptor(obj: T, propName: string): any { - return Object.getOwnPropertyDescriptor(obj, propName).value; +export function getPropertyValueByDescriptor(obj: T, propName: string): unknown { + return Object.getOwnPropertyDescriptor(obj, propName)?.value; } diff --git a/backend/src/helpers/is-object-empty.ts b/backend/src/helpers/is-object-empty.ts index 1ec19d280..35123bb58 100644 --- a/backend/src/helpers/is-object-empty.ts +++ b/backend/src/helpers/is-object-empty.ts @@ -1,4 +1,4 @@ -export function isObjectEmpty(obj: any): boolean { +export function isObjectEmpty(obj: object | null | undefined): boolean { if (!obj) { return true; } diff --git a/backend/src/helpers/slack/slack-post-message.ts b/backend/src/helpers/slack/slack-post-message.ts index 533e14941..b8b3391c1 100644 --- a/backend/src/helpers/slack/slack-post-message.ts +++ b/backend/src/helpers/slack/slack-post-message.ts @@ -2,7 +2,7 @@ import axios from 'axios'; import { appConfig } from '../../shared/config/app-config.js'; import { Constants } from '../constants/constants.js'; -export async function slackPostMessage(message: string, channel = Constants.DEFAULT_SLACK_CHANNEL): Promise { +export async function slackPostMessage(message: string, channel = Constants.DEFAULT_SLACK_CHANNEL): Promise { try { const slackBotToken = appConfig.thirdParty.slackBotAccessToken; if (appConfig.isTest || !slackBotToken) { diff --git a/backend/src/helpers/validators/is-action-url-host-allowed.ts b/backend/src/helpers/validators/is-action-url-host-allowed.ts index a1837c985..48b98b86f 100644 --- a/backend/src/helpers/validators/is-action-url-host-allowed.ts +++ b/backend/src/helpers/validators/is-action-url-host-allowed.ts @@ -3,6 +3,7 @@ import ipRangeCheck from 'ip-range-check'; import { isSaaS } from '../app/is-saas.js'; import { isTest } from '../app/is-test.js'; import { Constants } from '../constants/constants.js'; +import { getErrorMessage } from '../get-error-message.js'; export async function isActionUrlHostAllowed(url: string): Promise { if (isTest()) { @@ -37,7 +38,7 @@ export async function isActionUrlHostAllowed(url: string): Promise { }); }); } catch (e) { - console.error('Invalid URL format for action URL validation:', (e as Error).message); + console.error('Invalid URL format for action URL validation:', getErrorMessage(e)); return false; } } diff --git a/backend/src/types/express.d.ts b/backend/src/types/express.d.ts new file mode 100644 index 000000000..d700787db --- /dev/null +++ b/backend/src/types/express.d.ts @@ -0,0 +1,7 @@ +import type { ICognitoDecodedData } from '../authorization/cognito-decoded.interface.js'; + +declare module 'express' { + interface Request { + decoded?: Partial; + } +} diff --git a/backend/tsconfig.build.json b/backend/tsconfig.build.json index 4491981e0..a2fc4c977 100644 --- a/backend/tsconfig.build.json +++ b/backend/tsconfig.build.json @@ -1,4 +1,4 @@ { - "extends": "./tsconfig.json", + "extends": "./tsconfig.src.json", "exclude": ["node_modules", "test", "dist", "**/*spec.ts"] }