diff --git a/.github/workflows/code-quality.yml b/.github/workflows/code-quality.yml index f8be2538d..e52d5c7f9 100644 --- a/.github/workflows/code-quality.yml +++ b/.github/workflows/code-quality.yml @@ -40,3 +40,6 @@ jobs: - name: Run Biome if: steps.changed.outputs.files != '' run: biome ci --formatter-enabled=false --assist-enabled=false ${{ steps.changed.outputs.files }} + - name: Typecheck backend + if: steps.changed.outputs.files != '' + run: pnpm --filter ./backend typecheck diff --git a/backend/package.json b/backend/package.json index d88ca82d3..bbdaddcd3 100644 --- a/backend/package.json +++ b/backend/package.json @@ -9,6 +9,8 @@ "scripts": { "prebuild": "rimraf dist", "build": "nest build", + "typecheck": "tsc --noEmit --incremental false -p tsconfig.src.json && tsc --noEmit --incremental false -p tsconfig.json", + "lint": "biome lint src test", "start": "nest start", "start:dev": "nest start --watch --preserveWatchOutput", "start:debug": "nest start --debug --watch", diff --git a/backend/src/entities/ai/ai.service.ts b/backend/src/entities/ai/ai.service.ts index 154d8fa90..d261d9116 100644 --- a/backend/src/entities/ai/ai.service.ts +++ b/backend/src/entities/ai/ai.service.ts @@ -5,6 +5,7 @@ import { cleanAIJsonResponse } from '../../ai-core/tools/query-validators.js'; import { QueryOrderingEnum } from '../../enums/query-ordering.enum.js'; import { WidgetTypeEnum } from '../../enums/widget-type.enum.js'; import { checkFieldAutoincrement } from '../../helpers/check-field-autoincrement.js'; +import { getErrorMessage } from '../../helpers/get-error-message.js'; import { TableSettingsEntity } from '../table-settings/common-table-settings/table-settings.entity.js'; import { TableWidgetEntity } from '../widget/table-widget.entity.js'; import { TableInformation } from './ai-data-entities/types/ai-module-types.js'; @@ -78,13 +79,13 @@ export class AiService { const batchSettings = await this.processTablesBatch(batch); allSettings.push(...batchSettings); } catch (error) { - console.warn(`Batch processing failed, falling back to individual table processing: ${error.message}`); + console.warn(`Batch processing failed, falling back to individual table processing: ${getErrorMessage(error)}`); for (const tableInfo of batch) { try { const singleTableSettings = await this.processTablesBatch([tableInfo]); allSettings.push(...singleTableSettings); } catch (singleError) { - console.error(`Error processing AI for table "${tableInfo.table_name}": ${singleError.message}`); + console.error(`Error processing AI for table "${tableInfo.table_name}": ${getErrorMessage(singleError)}`); } } } @@ -289,7 +290,7 @@ IMPORTANT: try { return JSON.parse(cleanedResponse) as AIResponse; } catch (error) { - throw new Error(`Failed to parse AI response for tables [${tableNames.join(', ')}]: ${error.message}`); + throw new Error(`Failed to parse AI response for tables [${tableNames.join(', ')}]: ${getErrorMessage(error)}`); } } 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 314dd6f3f..609a096f7 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 @@ -115,7 +115,7 @@ export class RequestInfoFromTableWithAIUseCaseV7 response.end(); } catch (error) { - await slackPostMessage(error?.message); + await slackPostMessage((error as Error)?.message); Sentry.captureException(error); if (!response.headersSent) { response.status(500).send({ error: 'An error occurred while processing your request.' }); @@ -192,7 +192,7 @@ export class RequestInfoFromTableWithAIUseCaseV7 depth++; } catch (loopError) { - this.logger.error(`Error in tool loop at depth ${depth + 1}: ${loopError.message}`); + this.logger.error(`Error in tool loop at depth ${depth + 1}: ${(loopError as Error).message}`); throw loopError; } } @@ -290,8 +290,9 @@ export class RequestInfoFromTableWithAIUseCaseV7 result = encodeError({ error: `Unknown tool: ${toolCall.name}` }); } } catch (error) { - this.logger.error(`Tool call ${toolCall.name} (${toolCall.id}) failed: ${error.message}`); - result = encodeError({ error: error.message }); + const errMessage = (error as Error).message; + this.logger.error(`Tool call ${toolCall.name} (${toolCall.id}) failed: ${errMessage}`); + result = encodeError({ error: errMessage }); } results.push({ toolCallId: toolCall.id, result }); diff --git a/backend/src/entities/cedar-authorization/cedar-authorization.service.ts b/backend/src/entities/cedar-authorization/cedar-authorization.service.ts index e51521ce9..e3b4fcaab 100644 --- a/backend/src/entities/cedar-authorization/cedar-authorization.service.ts +++ b/backend/src/entities/cedar-authorization/cedar-authorization.service.ts @@ -185,7 +185,7 @@ export class CedarAuthorizationService implements ICedarAuthorizationService, On resource: { type: 'RocketAdmin::Connection', id: 'test' }, context: {}, policies: { staticPolicies: 'permit(principal, action, resource);' }, - entities: [], + entities: [] as unknown[], schema: schema, }; const result = cedarWasm.isAuthorized(testCall as Parameters[0]); @@ -198,7 +198,7 @@ export class CedarAuthorizationService implements ICedarAuthorizationService, On } } catch (e) { if (e instanceof HttpException) throw e; - throw new HttpException({ message: `Invalid cedar schema: ${e.message}` }, HttpStatus.BAD_REQUEST); + throw new HttpException({ message: `Invalid cedar schema: ${(e as Error).message}` }, HttpStatus.BAD_REQUEST); } } @@ -291,12 +291,12 @@ export class CedarAuthorizationService implements ICedarAuthorizationService, On resource: { type: 'RocketAdmin::Connection', id: 'test' }, context: {}, policies: { staticPolicies: policyText }, - entities: [], + entities: [] as unknown[], schema: this.schema, }; cedarWasm.isAuthorized(testCall as Parameters[0]); } catch (e) { - throw new HttpException({ message: `Invalid cedar policy: ${e.message}` }, HttpStatus.BAD_REQUEST); + throw new HttpException({ message: `Invalid cedar policy: ${(e as Error).message}` }, HttpStatus.BAD_REQUEST); } } diff --git a/backend/src/entities/cedar-authorization/cedar-schema.ts b/backend/src/entities/cedar-authorization/cedar-schema.ts index c6aae8d28..996aa0828 100644 --- a/backend/src/entities/cedar-authorization/cedar-schema.ts +++ b/backend/src/entities/cedar-authorization/cedar-schema.ts @@ -2,7 +2,7 @@ export const CEDAR_SCHEMA = { RocketAdmin: { entityTypes: { User: { - memberOfTypes: [], + memberOfTypes: [] as string[], shape: { type: 'Record', attributes: { @@ -11,7 +11,7 @@ export const CEDAR_SCHEMA = { }, }, Group: { - memberOfTypes: [], + memberOfTypes: [] as string[], shape: { type: 'Record', attributes: { @@ -21,7 +21,7 @@ export const CEDAR_SCHEMA = { }, }, Connection: { - memberOfTypes: [], + memberOfTypes: [] as string[], shape: { type: 'Record', attributes: {}, diff --git a/backend/src/entities/connection/use-cases/find-all-connections.use.case.ts b/backend/src/entities/connection/use-cases/find-all-connections.use.case.ts index 0f983a221..7611fc4a8 100644 --- a/backend/src/entities/connection/use-cases/find-all-connections.use.case.ts +++ b/backend/src/entities/connection/use-cases/find-all-connections.use.case.ts @@ -110,7 +110,7 @@ export class FindAllConnectionsUseCase return Object.keys(connection).reduce((acc, key) => { if (allowedKeys.includes(key)) { // eslint-disable-next-line security/detect-object-injection - acc[key] = connection[key]; + (acc as Record)[key] = (connection as unknown as Record)[key]; } return acc; }, {} as FilteredConnection); diff --git a/backend/src/entities/connection/use-cases/find-one-connection.use.case.ts b/backend/src/entities/connection/use-cases/find-one-connection.use.case.ts index 196b8428f..c9f87b362 100644 --- a/backend/src/entities/connection/use-cases/find-one-connection.use.case.ts +++ b/backend/src/entities/connection/use-cases/find-one-connection.use.case.ts @@ -67,7 +67,7 @@ export class FindOneConnectionUseCase return Object.keys(connection).reduce((acc, key) => { if (allowedKeys.includes(key)) { // eslint-disable-next-line security/detect-object-injection - acc[key] = connection[key]; + (acc as Record)[key] = (connection as unknown as Record)[key]; } return acc; }, {} as FilteredConnection); 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 0597b67e6..13ef44a13 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 @@ -52,7 +52,7 @@ export class GetConnectionDiagramUseCase try { tables = await dao.getTablesFromDB(userEmail); } catch (e) { - throw new UnknownSQLException(e.message, ExceptionOperations.FAILED_TO_GET_TABLES); + throw new UnknownSQLException((e as Error).message, 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 5f25e5c10..d4c1d09b0 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 @@ -53,7 +53,7 @@ export class PreviewConnectionDiagramUseCase try { tables = await dao.getTablesFromDB(userEmail); } catch (e) { - throw new UnknownSQLException(e.message, ExceptionOperations.FAILED_TO_GET_TABLES); + throw new UnknownSQLException((e as Error).message, ExceptionOperations.FAILED_TO_GET_TABLES); } const realTables = tables.filter((t) => !t.isView); diff --git a/backend/src/entities/connection/use-cases/test-connection.use.case.ts b/backend/src/entities/connection/use-cases/test-connection.use.case.ts index ae5ec6d9e..6d5042f67 100644 --- a/backend/src/entities/connection/use-cases/test-connection.use.case.ts +++ b/backend/src/entities/connection/use-cases/test-connection.use.case.ts @@ -79,10 +79,11 @@ export class TestConnectionUseCase } ['password', 'privateSSHKey', 'cert'].forEach((key) => { + const data = connectionData as unknown as Record; // eslint-disable-next-line security/detect-object-injection - if (!connectionData[key]) { + if (!data[key]) { // eslint-disable-next-line security/detect-object-injection - delete connectionData[key]; + delete data[key]; } }); diff --git a/backend/src/entities/connection/use-cases/update-connection.use.case.ts b/backend/src/entities/connection/use-cases/update-connection.use.case.ts index 4edc6b893..967b90b93 100644 --- a/backend/src/entities/connection/use-cases/update-connection.use.case.ts +++ b/backend/src/entities/connection/use-cases/update-connection.use.case.ts @@ -61,12 +61,14 @@ export class UpdateConnectionUseCase .map(([key, _]) => key); const keysToKeep = ['title', 'schema', ...booleanKeys]; + const params = connection_parameters as unknown as Record; connection_parameters = Object.keys(connection_parameters).reduce( (acc, key) => { + const accRec = acc as unknown as Record; // eslint-disable-next-line security/detect-object-injection - if (connection_parameters[key] || keysToKeep.includes(key)) { + if (params[key] || keysToKeep.includes(key)) { // eslint-disable-next-line security/detect-object-injection - acc[key] = connection_parameters[key]; + accRec[key] = params[key]; } return acc; }, diff --git a/backend/src/entities/cron-jobs/cron-jobs.service.ts b/backend/src/entities/cron-jobs/cron-jobs.service.ts index c369097b8..8debd11cf 100644 --- a/backend/src/entities/cron-jobs/cron-jobs.service.ts +++ b/backend/src/entities/cron-jobs/cron-jobs.service.ts @@ -6,6 +6,7 @@ import Mail from 'nodemailer/lib/mailer/index.js'; import { Repository } from 'typeorm'; import { UseCaseType } from '../../common/data-injection.tokens.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 { ValidationHelper } from '../../helpers/validators/validation-helper.js'; import { EmailService, ICronMessagingResults } from '../email/email/email.service.js'; @@ -79,7 +80,7 @@ export class CronJobsService { const batchResults = await this.emailService.sendRemindersToUsers(emailsBatch); allMailingResults.push(...batchResults); } catch (error) { - console.error(`Error processing batch ${Math.floor(i / batchSize) + 1}: ${error.message}`); + console.error(`Error processing batch ${Math.floor(i / batchSize) + 1}: ${getErrorMessage(error)}`); Sentry.captureException(error); } await new Promise((resolve) => setTimeout(resolve, 1000)); @@ -94,15 +95,17 @@ export class CronJobsService { await slackPostMessage(`morning cron finished at ${this.getCurrentTime()}`, Constants.EXCEPTIONS_CHANNELS); } catch (innerError) { console.error('Detailed error in email processing:', innerError); - const errorMessage = innerError.stack - ? `${innerError.message}\n${innerError.stack.split('\n').slice(0, 5).join('\n')}` - : innerError.message; + const err = innerError instanceof Error ? innerError : new Error(String(innerError)); + const errorMessage = err.stack + ? `${err.message}\n${err.stack.split('\n').slice(0, 5).join('\n')}` + : err.message; await slackPostMessage(`Error in email processing: ${errorMessage}`, Constants.EXCEPTIONS_CHANNELS); Sentry.captureException(innerError); } } catch (e) { console.error('Main cron handler error:', e); - const errorMessage = e.stack ? `${e.message}\n${e.stack.split('\n').slice(0, 5).join('\n')}` : e.message; + const err = e instanceof Error ? e : new Error(String(e)); + const errorMessage = err.stack ? `${err.message}\n${err.stack.split('\n').slice(0, 5).join('\n')}` : err.message; await slackPostMessage(`Error in morning cron: ${errorMessage}`, Constants.EXCEPTIONS_CHANNELS); Sentry.captureException(e); } finally { diff --git a/backend/src/entities/demo-data/demo-data.service.ts b/backend/src/entities/demo-data/demo-data.service.ts index 9638412f0..c64f5719d 100644 --- a/backend/src/entities/demo-data/demo-data.service.ts +++ b/backend/src/entities/demo-data/demo-data.service.ts @@ -43,7 +43,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.message}`); + await slackPostMessage(`Error during demo data creation for user with ID ${userId}: ${(error as Error).message}`); } } @@ -519,7 +519,7 @@ export class DemoDataService { savedEmptyActionRules.push(savedActionRule); } const foundSavedUserBlacklistActionRule = savedEmptyActionRules.find( - (rule) => rule.table_name === 'user' && rule.rule_title === 'Blacklist', + (rule) => rule.table_name === 'user' && rule.title === 'Blacklist', ); if (foundSavedUserBlacklistActionRule) { const createActionEventData: CreateTableActionEventDS = { @@ -533,7 +533,7 @@ export class DemoDataService { await this._dbContext.actionEventsRepository.saveNewOrUpdatedActionEvent(newActionEvent); } const foundSavedUserNotificationActionRule = savedEmptyActionRules.find( - (rule) => rule.table_name === 'user' && rule.rule_title === 'Notification on new user', + (rule) => rule.table_name === 'user' && rule.title === 'Notification on new user', ); if (foundSavedUserNotificationActionRule) { const createActionEventData: Array = [ diff --git a/backend/src/entities/email/email/email.service.ts b/backend/src/entities/email/email/email.service.ts index f87233259..566aa13e4 100644 --- a/backend/src/entities/email/email/email.service.ts +++ b/backend/src/entities/email/email/email.service.ts @@ -92,7 +92,7 @@ export class EmailService { }); mailingResults.push(result); } catch (error) { - this.logger.error(`Failed to send reminder to ${email}: ${error.message}`); + this.logger.error(`Failed to send reminder to ${email}: ${(error as Error).message}`); Sentry.captureException(error); mailingResults.push(null); } diff --git a/backend/src/entities/shared-jobs/shared-jobs.service.ts b/backend/src/entities/shared-jobs/shared-jobs.service.ts index d43627ead..55ccdeadc 100644 --- a/backend/src/entities/shared-jobs/shared-jobs.service.ts +++ b/backend/src/entities/shared-jobs/shared-jobs.service.ts @@ -65,7 +65,7 @@ export class SharedJobsService { foreignKeys, }; } catch (error) { - console.error(`Error getting table information for "${table.tableName}": ${error.message}`); + console.error(`Error getting table information for "${table.tableName}": ${(error as Error).message}`); return null; } }), diff --git a/backend/src/entities/table-actions/table-action-rules-module/repository/action-rules-custom-repository.ts b/backend/src/entities/table-actions/table-action-rules-module/repository/action-rules-custom-repository.ts index 658b02f4d..7df07cd72 100644 --- a/backend/src/entities/table-actions/table-action-rules-module/repository/action-rules-custom-repository.ts +++ b/backend/src/entities/table-actions/table-action-rules-module/repository/action-rules-custom-repository.ts @@ -22,7 +22,7 @@ export const actionRulesCustomRepositoryExtension: IActionRulesRepository = { .where('connection.id = :connectionId', { connectionId }) .andWhere('action_rules.table_name = :tableName', { tableName }) .getMany(); - const connections = results.flatMap((r) => (r.connection ? [r.connection] : [])); + const connections = results.flatMap((r: ActionRulesEntity) => (r.connection ? [r.connection] : [])); await decryptConnectionsCredentialsAsync(connections); return results; }, @@ -50,7 +50,7 @@ export const actionRulesCustomRepositoryExtension: IActionRulesRepository = { .andWhere('action_rules.table_name = :tableName', { tableName }) .andWhere('action_events.event = :event', { event: TableActionEventEnum.CUSTOM }) .getMany(); - const connections = results.flatMap((r) => (r.connection ? [r.connection] : [])); + const connections = results.flatMap((r: ActionRulesEntity) => (r.connection ? [r.connection] : [])); await decryptConnectionsCredentialsAsync(connections); return results; }, diff --git a/backend/src/entities/table-actions/table-action-rules-module/use-cases/activate-actions-in-rule.use.case.ts b/backend/src/entities/table-actions/table-action-rules-module/use-cases/activate-actions-in-rule.use.case.ts index 9c1e35810..93f1e96f6 100644 --- a/backend/src/entities/table-actions/table-action-rules-module/use-cases/activate-actions-in-rule.use.case.ts +++ b/backend/src/entities/table-actions/table-action-rules-module/use-cases/activate-actions-in-rule.use.case.ts @@ -74,11 +74,12 @@ export class ActivateActionsInEventUseCase } catch (e) { operationResult = OperationResultStatusEnum.unsuccessfully; activationResults.push({ actionId: action.id, result: operationResult }); + const err = e as Error & { response?: { status?: number } }; throw new HttpException( { - message: e.message, + message: err.message, }, - e.response?.status || HttpStatus.BAD_REQUEST, + err.response?.status || HttpStatus.BAD_REQUEST, ); } finally { const eventTitle = action.action_rule.action_events?.find((e) => e.id === event_id)?.title ?? null; @@ -90,7 +91,7 @@ export class ActivateActionsInEventUseCase operationType: LogOperationTypeEnum.actionActivated, operationStatusResult: operationResult, row: primaryKey, - old_data: null, + old_data: null as Record | null, table_primary_key: primaryKey, operation_custom_action_name: eventTitle, }; diff --git a/backend/src/entities/table-actions/table-actions-module/repository/table-actions-custom-repository.extension.ts b/backend/src/entities/table-actions/table-actions-module/repository/table-actions-custom-repository.extension.ts index 8d83cf139..c28b1cdb6 100644 --- a/backend/src/entities/table-actions/table-actions-module/repository/table-actions-custom-repository.extension.ts +++ b/backend/src/entities/table-actions/table-actions-module/repository/table-actions-custom-repository.extension.ts @@ -17,7 +17,9 @@ export const tableActionsCustomRepositoryExtension: ITableActionRepository = { .where('connection.id = :connectionId', { connectionId }) .andWhere('action_rule.table_name = :tableName', { tableName }); const results = await qb.getMany(); - const connections = results.flatMap((r) => (r.action_rule?.connection ? [r.action_rule.connection] : [])); + const connections = results.flatMap((r: TableActionEntity) => + r.action_rule?.connection ? [r.action_rule.connection] : [], + ); await decryptConnectionsCredentialsAsync(connections); return results; }, @@ -32,7 +34,9 @@ export const tableActionsCustomRepositoryExtension: ITableActionRepository = { .andWhere('action_rule.table_name = :tableName', { tableName }) .andWhere('action_events.event = :eventType', { eventType: TableActionEventEnum.ADD_ROW }); const results = await qb.getMany(); - const connections = results.flatMap((r) => (r.action_rule?.connection ? [r.action_rule.connection] : [])); + const connections = results.flatMap((r: TableActionEntity) => + r.action_rule?.connection ? [r.action_rule.connection] : [], + ); await decryptConnectionsCredentialsAsync(connections); return results; }, @@ -50,7 +54,9 @@ export const tableActionsCustomRepositoryExtension: ITableActionRepository = { .andWhere('action_rule.table_name = :tableName', { tableName }) .andWhere('action_events.event = :eventType', { eventType: TableActionEventEnum.UPDATE_ROW }); const results = await qb.getMany(); - const connections = results.flatMap((r) => (r.action_rule?.connection ? [r.action_rule.connection] : [])); + const connections = results.flatMap((r: TableActionEntity) => + r.action_rule?.connection ? [r.action_rule.connection] : [], + ); await decryptConnectionsCredentialsAsync(connections); return results; }, @@ -68,7 +74,9 @@ export const tableActionsCustomRepositoryExtension: ITableActionRepository = { .andWhere('action_rule.table_name = :tableName', { tableName }) .andWhere('action_events.event = :eventType', { eventType: TableActionEventEnum.DELETE_ROW }); const results = await qb.getMany(); - const connections = results.flatMap((r) => (r.action_rule?.connection ? [r.action_rule.connection] : [])); + const connections = results.flatMap((r: TableActionEntity) => + r.action_rule?.connection ? [r.action_rule.connection] : [], + ); await decryptConnectionsCredentialsAsync(connections); return results; }, @@ -85,7 +93,9 @@ export const tableActionsCustomRepositoryExtension: ITableActionRepository = { .andWhere('action_events.id = :eventId', { eventId }) .andWhere('action_events.event = :eventType', { eventType: TableActionEventEnum.CUSTOM }); const results = await qb.getMany(); - const connections = results.flatMap((r) => (r.action_rule?.connection ? [r.action_rule.connection] : [])); + const connections = results.flatMap((r: TableActionEntity) => + r.action_rule?.connection ? [r.action_rule.connection] : [], + ); await decryptConnectionsCredentialsAsync(connections); return results; }, 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 798618aef..26aac5c62 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 @@ -37,7 +37,8 @@ export class FindTableCategoriesWithTablesUseCase try { connection = await this._dbContext.connectionRepository.findAndDecryptConnection(connectionId, masterPwd); } catch (error) { - if (error.message === Messages.MASTER_PASSWORD_MISSING) { + const errMessage = (error as Error).message; + if (errMessage === Messages.MASTER_PASSWORD_MISSING) { throw new HttpException( { message: Messages.MASTER_PASSWORD_MISSING, @@ -46,7 +47,7 @@ export class FindTableCategoriesWithTablesUseCase HttpStatus.BAD_REQUEST, ); } - if (error.message === Messages.MASTER_PASSWORD_INCORRECT) { + if (errMessage === Messages.MASTER_PASSWORD_INCORRECT) { throw new HttpException( { message: Messages.MASTER_PASSWORD_INCORRECT, @@ -75,11 +76,15 @@ export class FindTableCategoriesWithTablesUseCase tables = await dao.getTablesFromDB(userEmail); } catch (e) { Sentry.captureException(e); - throw new UnknownSQLException(e.message, ExceptionOperations.FAILED_TO_GET_TABLES); + throw new UnknownSQLException((e as Error).message, ExceptionOperations.FAILED_TO_GET_TABLES); } const tableNames = tables.map((t) => t.tableName); - const permissionsArr = await this.cedarPermissions.getUserPermissionsForAvailableTables(userId, connectionId, tableNames); + const permissionsArr = await this.cedarPermissions.getUserPermissionsForAvailableTables( + userId, + connectionId, + tableNames, + ); const tablesWithPermissions: Array = permissionsArr.map((perm) => ({ ...perm, isView: tables.find((t) => t.tableName === perm.tableName)?.isView || false, @@ -95,7 +100,9 @@ export class FindTableCategoriesWithTablesUseCase const foundTableCategories = await this._dbContext.tableCategoriesRepository.findTableCategoriesForConnection(connectionId); - const storedAllTablesCategory = foundTableCategories.find((category) => category.category_id === 'all-tables-kitten'); + const storedAllTablesCategory = foundTableCategories.find( + (category) => category.category_id === 'all-tables-kitten', + ); const otherCategories = foundTableCategories.filter((category) => category.category_id !== 'all-tables-kitten'); let allTablesOrdered: Array; @@ -159,5 +166,4 @@ export class FindTableCategoriesWithTablesUseCase }; }); } - } diff --git a/backend/src/entities/table-filters/use-cases/update-table-filters.use.case.ts b/backend/src/entities/table-filters/use-cases/update-table-filters.use.case.ts index cc0dafe38..6218c63e5 100644 --- a/backend/src/entities/table-filters/use-cases/update-table-filters.use.case.ts +++ b/backend/src/entities/table-filters/use-cases/update-table-filters.use.case.ts @@ -53,11 +53,12 @@ export class UpdateTableFiltersUseCase } const updatedFilterEntity = buildNewTableFiltersEntity(updatedTableFilterData); + const entityRec = updatedFilterEntity as unknown as Record; for (const key in updatedFilterEntity) { // eslint-disable-next-line security/detect-object-injection - if (updatedFilterEntity[key] === undefined) { + if (entityRec[key] === undefined) { // eslint-disable-next-line security/detect-object-injection - delete updatedFilterEntity[key]; + delete entityRec[key]; } } const updatedFilters = Object.assign(filtersToUpdate, updatedFilterEntity); diff --git a/backend/src/entities/table-logs/table-logs.controller.ts b/backend/src/entities/table-logs/table-logs.controller.ts index 7f7570d3a..4439897e0 100644 --- a/backend/src/entities/table-logs/table-logs.controller.ts +++ b/backend/src/entities/table-logs/table-logs.controller.ts @@ -68,7 +68,7 @@ export class TableLogsController { @ApiQuery({ name: 'affected_primary_key', required: false }) @Get('/logs/:connectionId') async findAll( - @Query() query, + @Query() query: Record, @SlugUuid('connectionId') connectionId: string, @UserId() userId: string, @Query('operationTypes', new ParseArrayPipe({ separator: ',', optional: true })) @@ -121,7 +121,7 @@ export class TableLogsController { @ApiQuery({ name: 'affected_primary_key', required: false }) @Get('/logs/export/:connectionId') async exportLogsAsCSV( - @Query() query, + @Query() query: Record, @SlugUuid('connectionId') connectionId: string, @UserId() userId: string, @Query('operationTypes', new ParseArrayPipe({ separator: ',', optional: true })) diff --git a/backend/src/entities/table-logs/table-logs.service.ts b/backend/src/entities/table-logs/table-logs.service.ts index 78a8d0425..d8945d4e4 100644 --- a/backend/src/entities/table-logs/table-logs.service.ts +++ b/backend/src/entities/table-logs/table-logs.service.ts @@ -48,6 +48,8 @@ export class TableLogsService { const sensitive_fields = tableSettings?.sensitive_fields; if (sensitive_fields && sensitive_fields.length > 0) { + const oldRec = old_data as Record; + const rowRec = row as Record; for (const fieldName of sensitive_fields) { if ( old_data && @@ -58,25 +60,25 @@ export class TableLogsService { isObjectPropertyExists(row, fieldName) ) { // eslint-disable-next-line security/detect-object-injection - if (this.compareValues(old_data[fieldName], row[fieldName])) { + if (this.compareValues(oldRec[fieldName], rowRec[fieldName])) { // eslint-disable-next-line security/detect-object-injection - old_data[fieldName] = Constants.REMOVED_SENSITIVE_FIELD_IF_CHANGED; + oldRec[fieldName] = Constants.REMOVED_SENSITIVE_FIELD_IF_CHANGED; // eslint-disable-next-line security/detect-object-injection - row[fieldName] = Constants.REMOVED_SENSITIVE_FIELD_IF_CHANGED; + rowRec[fieldName] = Constants.REMOVED_SENSITIVE_FIELD_IF_CHANGED; } else { // eslint-disable-next-line security/detect-object-injection - old_data[fieldName] = Constants.REMOVED_SENSITIVE_FIELD_IF_NOT_CHANGED; + oldRec[fieldName] = Constants.REMOVED_SENSITIVE_FIELD_IF_NOT_CHANGED; // eslint-disable-next-line security/detect-object-injection - row[fieldName] = Constants.REMOVED_SENSITIVE_FIELD_IF_CHANGED; + rowRec[fieldName] = Constants.REMOVED_SENSITIVE_FIELD_IF_CHANGED; } } else { if (old_data && typeof old_data === 'object' && isObjectPropertyExists(old_data, fieldName)) { // eslint-disable-next-line security/detect-object-injection - old_data[fieldName] = Constants.REMOVED_SENSITIVE_FIELD_IF_NOT_CHANGED; + oldRec[fieldName] = Constants.REMOVED_SENSITIVE_FIELD_IF_NOT_CHANGED; } if (row && typeof row === 'object' && isObjectPropertyExists(row, fieldName)) { // eslint-disable-next-line security/detect-object-injection - row[fieldName] = Constants.REMOVED_SENSITIVE_FIELD_IF_CHANGED; + rowRec[fieldName] = Constants.REMOVED_SENSITIVE_FIELD_IF_CHANGED; } } } 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 adf8c0a71..860ea1962 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 @@ -140,7 +140,7 @@ export class GenerateSchemaChangeUseCase previousChangeId, forwardSql: proposal.forwardSql, rollbackSql: proposal.rollbackSql ?? null, - userModifiedSql: null, + userModifiedSql: null as string | null, status: SchemaChangeStatusEnum.PENDING, changeType: proposal.changeType, targetTableName: proposal.targetTableName, diff --git a/backend/src/entities/table-schema/utils/cassandra-ddl.ts b/backend/src/entities/table-schema/utils/cassandra-ddl.ts index e1c8f57f7..aabadd8da 100644 --- a/backend/src/entities/table-schema/utils/cassandra-ddl.ts +++ b/backend/src/entities/table-schema/utils/cassandra-ddl.ts @@ -42,6 +42,6 @@ export async function executeCassandraDdl(connection: CassandraExecutionConnecti await client.connect(); await client.execute(cql); } finally { - await client.shutdown().catch(() => undefined); + await client.shutdown().catch((): void => undefined); } } 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 c75495792..7cd400651 100644 --- a/backend/src/entities/table-schema/utils/elasticsearch-schema-op.ts +++ b/backend/src/entities/table-schema/utils/elasticsearch-schema-op.ts @@ -144,7 +144,7 @@ export async function executeElasticsearchSchemaOp( try { await dispatchElasticsearchOp(client, op); } finally { - await client.close().catch(() => undefined); + await client.close().catch((): void => undefined); } } 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 62572f9da..8424bfb00 100644 --- a/backend/src/entities/table-schema/utils/mongo-schema-op.ts +++ b/backend/src/entities/table-schema/utils/mongo-schema-op.ts @@ -171,7 +171,7 @@ export async function executeMongoSchemaOp(connection: MongoExecutionConnection, try { await dispatchMongoOp(db, op); } finally { - await client.close().catch(() => undefined); + await client.close().catch((): void => undefined); } } diff --git a/backend/src/entities/table-settings/common-table-settings/dto/create-table-settings.dto.ts b/backend/src/entities/table-settings/common-table-settings/dto/create-table-settings.dto.ts index d6219a47f..163ff0137 100644 --- a/backend/src/entities/table-settings/common-table-settings/dto/create-table-settings.dto.ts +++ b/backend/src/entities/table-settings/common-table-settings/dto/create-table-settings.dto.ts @@ -8,7 +8,7 @@ export class CreateTableSettingsDto { search_fields: string[]; excluded_fields: string[]; identification_fields: string[]; - identity_column; + identity_column?: string; readonly_fields: string[]; sortable_by: string[]; autocomplete_columns: string[]; 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 77b3b9fb1..2abe808a8 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 @@ -54,7 +54,7 @@ export const tableSettingsCustomRepositoryExtension: ITableSettingsRepository = try { return await qb.getOne(); } catch (e) { - console.info(`Table setting not found exception. => `, e.message); + console.info(`Table setting not found exception. => `, (e as Error).message); throw new HttpException( { message: Messages.TABLE_SETTINGS_NOT_FOUND, diff --git a/backend/src/entities/table-settings/common-table-settings/use-cases/update-table-settings.use.case.ts b/backend/src/entities/table-settings/common-table-settings/use-cases/update-table-settings.use.case.ts index 4268789da..98435ff57 100644 --- a/backend/src/entities/table-settings/common-table-settings/use-cases/update-table-settings.use.case.ts +++ b/backend/src/entities/table-settings/common-table-settings/use-cases/update-table-settings.use.case.ts @@ -53,11 +53,12 @@ export class UpdateTableSettingsUseCase ); } const updateTableSettings = buildNewTableSettingsEntity(inputData, foundConnection); + const settingsRec = updateTableSettings as unknown as Record; for (const key in updateTableSettings) { // eslint-disable-next-line security/detect-object-injection - if (updateTableSettings[key] === undefined) { + if (settingsRec[key] === undefined) { // eslint-disable-next-line security/detect-object-injection - delete updateTableSettings[key]; + delete settingsRec[key]; } } const updated = Object.assign(settingsToUpdate, updateTableSettings); 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 d0f645773..01a907127 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 @@ -194,10 +194,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 4436bed23..577ad299a 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 @@ -119,7 +119,7 @@ export class DeleteRowFromTableUseCase try { oldRowData = await dao.getRowByPrimaryKey(tableName, primaryKey, builtTableSettings, userEmail); } catch (e) { - throw new UnknownSQLException(e.message, ExceptionOperations.FAILED_TO_DELETE_ROW_FROM_TABLE); + throw new UnknownSQLException((e as Error).message, ExceptionOperations.FAILED_TO_DELETE_ROW_FROM_TABLE); } if (!oldRowData) { @@ -139,7 +139,7 @@ export class DeleteRowFromTableUseCase }; } catch (e) { operationResult = OperationResultStatusEnum.unsuccessfully; - throw new DeleteRowException(e.message); + throw new DeleteRowException((e as Error).message); } 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 13239deb0..e59d7314d 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 @@ -117,7 +117,7 @@ export class DeleteRowsFromTableUseCase } catch (error) { throw new HttpException( { - message: Messages.BULK_DELETE_FAILED_GET_ROWS([error.message]), + message: Messages.BULK_DELETE_FAILED_GET_ROWS([(error as Error).message]), }, HttpStatus.INTERNAL_SERVER_ERROR, ); @@ -143,7 +143,7 @@ export class DeleteRowsFromTableUseCase operationStatusResult: OperationResultStatusEnum.unsuccessfully, row: primaryKey, old_data: findObjectsWithProperties(oldRowsData, primaryKey).at(0), - error: error.message, + error: (error as Error).message, 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 56cd7f98d..ac883647c 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 @@ -119,7 +119,7 @@ export class ExportCSVFromTableUseCase } // todo: temporary debug log await slackPostMessage(` - CSV Export Failed with error: ${error.message}\n + CSV Export Failed with error: ${(error as Error).message}\n Connection type: ${connection.type}\n SSH Option: ${connection.ssh}\n SSL Option: ${connection.ssl}\n @@ -127,7 +127,7 @@ export class ExportCSVFromTableUseCase throw new HttpException( { message: Messages.CSV_EXPORT_FAILED, - originalMessage: error.message, + originalMessage: (error as Error).message, }, 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 65cc04df2..f9c8cbee6 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 @@ -44,7 +44,8 @@ export class FindTablesInConnectionV2UseCase try { connection = await this._dbContext.connectionRepository.findAndDecryptConnection(connectionId, masterPwd); } catch (error) { - if (error.message === Messages.MASTER_PASSWORD_MISSING) { + const errMessage = (error as Error).message; + if (errMessage === Messages.MASTER_PASSWORD_MISSING) { throw new HttpException( { message: Messages.MASTER_PASSWORD_MISSING, @@ -53,7 +54,7 @@ export class FindTablesInConnectionV2UseCase HttpStatus.BAD_REQUEST, ); } - if (error.message === Messages.MASTER_PASSWORD_INCORRECT) { + if (errMessage === Messages.MASTER_PASSWORD_INCORRECT) { throw new HttpException( { message: Messages.MASTER_PASSWORD_INCORRECT, @@ -84,7 +85,7 @@ export class FindTablesInConnectionV2UseCase } catch (e) { operationResult = false; Sentry.captureException(e); - throw new UnknownSQLException(e.message, ExceptionOperations.FAILED_TO_GET_TABLES); + throw new UnknownSQLException((e as Error).message, 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 6023a4bf5..62e4d182b 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 @@ -45,7 +45,8 @@ export class FindTablesInConnectionUseCase try { connection = await this._dbContext.connectionRepository.findAndDecryptConnection(connectionId, masterPwd); } catch (error) { - if (error.message === Messages.MASTER_PASSWORD_MISSING) { + const errMessage = (error as Error).message; + if (errMessage === Messages.MASTER_PASSWORD_MISSING) { throw new HttpException( { message: Messages.MASTER_PASSWORD_MISSING, @@ -54,7 +55,7 @@ export class FindTablesInConnectionUseCase HttpStatus.BAD_REQUEST, ); } - if (error.message === Messages.MASTER_PASSWORD_INCORRECT) { + if (errMessage === Messages.MASTER_PASSWORD_INCORRECT) { throw new HttpException( { message: Messages.MASTER_PASSWORD_INCORRECT, @@ -88,7 +89,7 @@ export class FindTablesInConnectionUseCase } catch (e) { operationResult = false; Sentry.captureException(e); - throw new UnknownSQLException(e.message, ExceptionOperations.FAILED_TO_GET_TABLES); + throw new UnknownSQLException((e as Error).message, 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 23befe8cc..8793efc49 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 @@ -134,7 +134,7 @@ export class GetRowByPrimaryKeyUseCase try { rowData = await dao.getRowByPrimaryKey(tableName, primaryKey, builtDAOsTableSettings, userEmail); } catch (e) { - throw new UnknownSQLException(e.message, ExceptionOperations.FAILED_TO_GET_ROW_BY_PRIMARY_KEY); + throw new UnknownSQLException((e as Error).message, 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 533919d6c..2d880af6c 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 @@ -165,7 +165,7 @@ export class GetTableRowsUseCase extends AbstractUseCase = []; tableForeignKeys = await filterForeignKeysByReadPermission( - tableForeignKeys, userId, connectionId, masterPwd, this.cedarPermissions, + tableForeignKeys, + userId, + connectionId, + masterPwd, + this.cedarPermissions, ); if (tableForeignKeys && tableForeignKeys.length > 0) { transformedTableForeignKeys = await Promise.all( tableForeignKeys.map((el) => attachForeignColumnNames( - el, userEmail, connectionId, dao, + el, + userEmail, + connectionId, + dao, this._dbContext.tableSettingsRepository.findTableSettings.bind(this._dbContext.tableSettingsRepository), ).catch(() => el as ForeignKeyWithAutocompleteColumnsDS), ), @@ -99,7 +106,7 @@ export class GetTableStructureUseCase if (e instanceof HttpException) { throw e; } - throw new UnknownSQLException(e.message, ExceptionOperations.FAILED_TO_GET_TABLE_STRUCTURE); + throw new UnknownSQLException((e as Error).message, ExceptionOperations.FAILED_TO_GET_TABLE_STRUCTURE); } } } diff --git a/backend/src/entities/table/use-cases/import-csv-in-table-user.case.ts b/backend/src/entities/table/use-cases/import-csv-in-table-user.case.ts index 80175a425..7b3314b21 100644 --- a/backend/src/entities/table/use-cases/import-csv-in-table-user.case.ts +++ b/backend/src/entities/table/use-cases/import-csv-in-table-user.case.ts @@ -4,8 +4,8 @@ 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 { validateConnection, getUserEmailForAgent } from '../utils/validate-connection.util.js'; import { ImportCSVInTableDs } from '../application/data-structures/import-scv-in-table.ds.js'; +import { getUserEmailForAgent, validateConnection } from '../utils/validate-connection.util.js'; import { IImportCSVFinTable } from './table-use-cases.interface.js'; @Injectable() @@ -56,7 +56,7 @@ export class ImportCSVInTableUseCase } throw new HttpException( { - message: `${Messages.CSV_IMPORT_FAILED}. ${error.message}`, + message: `${Messages.CSV_IMPORT_FAILED}. ${(error as Error).message}`, }, HttpStatus.INTERNAL_SERVER_ERROR, ); diff --git a/backend/src/entities/table/use-cases/update-row-in-table.use.case.ts b/backend/src/entities/table/use-cases/update-row-in-table.use.case.ts index 0d625714b..7d01fc9c9 100644 --- a/backend/src/entities/table/use-cases/update-row-in-table.use.case.ts +++ b/backend/src/entities/table/use-cases/update-row-in-table.use.case.ts @@ -184,7 +184,7 @@ export class UpdateRowInTableUseCase try { oldRowData = await dao.getRowByPrimaryKey(tableName, primaryKey, builtDAOsTableSettings, userEmail); } catch (e) { - throw new UnknownSQLException(e.message, ExceptionOperations.FAILED_TO_UPDATE_ROW_IN_TABLE); + throw new UnknownSQLException((e as Error).message, ExceptionOperations.FAILED_TO_UPDATE_ROW_IN_TABLE); } if (!oldRowData) { throw new HttpException( @@ -200,9 +200,11 @@ export class UpdateRowInTableUseCase }; const futureRowData = Object.assign(oldRowData, row); - let futurePrimaryKey = {}; + let futurePrimaryKey: Record = {}; for (const primaryColumn of tablePrimaryKeys) { - futurePrimaryKey[primaryColumn.column_name] = futureRowData[primaryColumn.column_name]; + futurePrimaryKey[primaryColumn.column_name] = (futureRowData as Record)[ + primaryColumn.column_name + ]; } if (isObjectEmpty(futurePrimaryKey)) { futurePrimaryKey = primaryKey; @@ -236,7 +238,7 @@ export class UpdateRowInTableUseCase }; } catch (e) { operationResult = OperationResultStatusEnum.unsuccessfully; - throw new UnknownSQLException(e.message, ExceptionOperations.FAILED_TO_UPDATE_ROW_IN_TABLE); + throw new UnknownSQLException((e as Error).message, ExceptionOperations.FAILED_TO_UPDATE_ROW_IN_TABLE); } finally { const logRecord = { table_name: tableName, diff --git a/backend/src/entities/table/utils/find-objects-with-properties.ts b/backend/src/entities/table/utils/find-objects-with-properties.ts index 59000a102..78c96cf6d 100644 --- a/backend/src/entities/table/utils/find-objects-with-properties.ts +++ b/backend/src/entities/table/utils/find-objects-with-properties.ts @@ -1,6 +1,6 @@ export function findObjectsWithProperties(objectsArray: T[], properties: Pick): T[] { return objectsArray.filter((obj) => { // eslint-disable-next-line security/detect-object-injection - return Object.entries(properties).every(([key, value]) => obj[key] === value); + return Object.entries(properties).every(([key, value]) => (obj as Record)[key] === value); }); } 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 b5399d455..b8b1f121d 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 @@ -35,7 +35,7 @@ export class CheckUsersActionsAndMailingUsersUseCase implements ICheckUsersActio } }); } catch (error) { - console.error(`Error processing user ${user.id}: ${error.message}`); + console.error(`Error processing user ${user.id}: ${(error as Error).message}`); Sentry.captureException(error); } } 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 c29d04926..c2de892dd 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 @@ -21,7 +21,7 @@ export class LogOutUseCase extends AbstractUseCase implements I } catch (e) { throw new HttpException( { - message: Messages.FAILED_LOGOUT + ' Error: ' + e.message, + message: Messages.FAILED_LOGOUT + ' Error: ' + (e as Error).message, }, HttpStatus.INTERNAL_SERVER_ERROR, ); 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 e210b7c9b..98b9ba7cc 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 @@ -285,7 +285,7 @@ IMPORTANT GUIDELINES: result = encodeError({ error: `Unknown tool: ${toolCall.name}` }); } } catch (error) { - result = encodeError({ error: error.message }); + result = encodeError({ error: (error as Error).message }); } results.push({ toolCallId: toolCall.id, result }); @@ -306,7 +306,7 @@ IMPORTANT GUIDELINES: return parsed; } catch (error) { - this.logger.error('Error parsing AI response:', error.message); + this.logger.error('Error parsing AI response:', (error as Error).message); throw new BadRequestException( 'Failed to generate panel configuration from AI. Please try again with a different description.', ); @@ -382,7 +382,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.message }; + return { success: false, error: (error as Error).message }; } } 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 777b4f44e..f97ab50ec 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 @@ -161,7 +161,7 @@ export class GenerateTableDashboardWithAiUseCase }, }); } catch (error) { - this.logger.warn(`Panel "${panel.name}" skipped: ${error.message}`); + this.logger.warn(`Panel "${panel.name}" skipped: ${(error as Error).message}`); } } @@ -353,7 +353,7 @@ IMPORTANT GUIDELINES: result = encodeError({ error: `Unknown tool: ${toolCall.name}` }); } } catch (error) { - result = encodeError({ error: error.message }); + result = encodeError({ error: (error as Error).message }); } results.push({ toolCallId: toolCall.id, result }); @@ -394,7 +394,7 @@ IMPORTANT GUIDELINES: return parsed; } catch (error) { - this.logger.error('Error parsing dashboard AI response:', error.message); + this.logger.error('Error parsing dashboard AI response:', (error as Error).message); throw new BadRequestException('Failed to generate dashboard from AI. Please try again.'); } } @@ -454,7 +454,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.message }; + return { success: false, error: (error as Error).message }; } } diff --git a/backend/src/entities/visualizations/panel/panel.entity.ts b/backend/src/entities/visualizations/panel/panel.entity.ts index e5c9fc4eb..f6282dc71 100644 --- a/backend/src/entities/visualizations/panel/panel.entity.ts +++ b/backend/src/entities/visualizations/panel/panel.entity.ts @@ -55,7 +55,7 @@ export class PanelEntity { this.panel_options = JSON.stringify(this.panel_options); } } catch (e) { - console.error('-> Error widget options stringify ' + e.message); + console.error('-> Error widget options stringify ' + (e as Error).message); } } @@ -70,7 +70,7 @@ export class PanelEntity { this.panel_options = JSON.stringify(this.panel_options); } } catch (e) { - console.error('-> Error widget options stringify ' + e.message); + console.error('-> Error widget options stringify ' + (e as Error).message); } } @@ -87,7 +87,7 @@ export class PanelEntity { }); } } catch (e) { - console.error('-> Error widget options parse ' + e.message); + console.error('-> Error widget options parse ' + (e as Error).message); } } diff --git a/backend/src/entities/widget/table-widget.entity.ts b/backend/src/entities/widget/table-widget.entity.ts index 7d4db82c4..3043f6362 100644 --- a/backend/src/entities/widget/table-widget.entity.ts +++ b/backend/src/entities/widget/table-widget.entity.ts @@ -51,7 +51,7 @@ export class TableWidgetEntity { this.widget_options = JSON.stringify(this.widget_options); } } catch (e) { - console.error('-> Error widget options stringify ' + e.message); + console.error('-> Error widget options stringify ' + (e as Error).message); } } @@ -62,7 +62,7 @@ export class TableWidgetEntity { this.widget_options = JSON.stringify(this.widget_options); } } catch (e) { - console.error('-> Error widget options stringify ' + e.message); + console.error('-> Error widget options stringify ' + (e as Error).message); } } @@ -76,7 +76,7 @@ export class TableWidgetEntity { }); } } catch (e) { - console.error('-> Error widget options parse ' + e.message); + console.error('-> Error widget options parse ' + (e as Error).message); } } diff --git a/backend/src/helpers/constants/constants.ts b/backend/src/helpers/constants/constants.ts index 9fc561cd6..6300a1141 100644 --- a/backend/src/helpers/constants/constants.ts +++ b/backend/src/helpers/constants/constants.ts @@ -292,7 +292,7 @@ export const Constants = { }, getTestConnectionsHostNamesArr: function (): Array { - return this.getTestConnectionsArr().map((connection) => connection.host); + return this.getTestConnectionsArr().map((connection: { host: string }) => connection.host); }, APP_DOMAIN_ADDRESS: appConfig.app.domainAddress, diff --git a/backend/src/helpers/get-error-message.ts b/backend/src/helpers/get-error-message.ts new file mode 100644 index 000000000..c6112257d --- /dev/null +++ b/backend/src/helpers/get-error-message.ts @@ -0,0 +1,13 @@ +export function getErrorMessage(error: unknown): string { + if (error instanceof Error) { + return error.message; + } + if (typeof error === 'string') { + return error; + } + return String(error); +} + +export function getErrorStack(error: unknown): string | undefined { + return error instanceof Error ? error.stack : undefined; +} 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 658de04cd..a1837c985 100644 --- a/backend/src/helpers/validators/is-action-url-host-allowed.ts +++ b/backend/src/helpers/validators/is-action-url-host-allowed.ts @@ -37,7 +37,7 @@ export async function isActionUrlHostAllowed(url: string): Promise { }); }); } catch (e) { - console.error('Invalid URL format for action URL validation:', e.message); + console.error('Invalid URL format for action URL validation:', (e as Error).message); return false; } } diff --git a/backend/src/microservices/saas-microservice/use-cases/create-connection-for-hosted-db.use.case.ts b/backend/src/microservices/saas-microservice/use-cases/create-connection-for-hosted-db.use.case.ts index f5c3f33f6..faa6806f4 100644 --- a/backend/src/microservices/saas-microservice/use-cases/create-connection-for-hosted-db.use.case.ts +++ b/backend/src/microservices/saas-microservice/use-cases/create-connection-for-hosted-db.use.case.ts @@ -45,18 +45,18 @@ export class CreateConnectionForHostedDbUseCase username: username, password: password, database: databaseName, - schema: null, - sid: null, + schema: null as string | null, + sid: null as string | null, ssh: false, - privateSSHKey: null, - sshHost: null, - sshPort: null, - sshUsername: null, + privateSSHKey: null as string | null, + sshHost: null as string | null, + sshPort: null as number | null, + sshUsername: null as string | null, ssl: true, cert: cert, azure_encryption: false, - authSource: null, - dataCenter: null, + authSource: null as string | null, + dataCenter: null as string | null, }; const dao = getDataAccessObject(connectionParams); diff --git a/backend/src/shared/config/app-config.ts b/backend/src/shared/config/app-config.ts index 0a165312b..b90058127 100644 --- a/backend/src/shared/config/app-config.ts +++ b/backend/src/shared/config/app-config.ts @@ -276,7 +276,7 @@ export class AppConfig { fs.accessSync(resolvedPath, fs.constants.W_OK); console.log('PGLite directory is writable'); } catch (writeError) { - console.warn('PGLite directory exists but may not be writable:', writeError.message); + console.warn('PGLite directory exists but may not be writable:', (writeError as Error).message); } } catch (error) { console.log('PGLite directory does not exist, will be created by PGLite', error); diff --git a/backend/src/shared/services/turnstile.service.ts b/backend/src/shared/services/turnstile.service.ts index 24def62b8..abbeb3a4f 100644 --- a/backend/src/shared/services/turnstile.service.ts +++ b/backend/src/shared/services/turnstile.service.ts @@ -43,7 +43,8 @@ export class TurnstileService { } } catch (error) { if (error instanceof BadRequestException) throw error; - console.error('Turnstile verification error:', error?.response?.data || error?.message || error); + const err = error as { response?: { data?: unknown }; message?: string }; + console.error('Turnstile verification error:', err?.response?.data || err?.message || error); throw new BadRequestException('Turnstile verification failed. Please try again.'); } diff --git a/backend/src/types/basic-auth.d.ts b/backend/src/types/basic-auth.d.ts new file mode 100644 index 000000000..d22d51639 --- /dev/null +++ b/backend/src/types/basic-auth.d.ts @@ -0,0 +1,15 @@ +declare module 'basic-auth' { + import type { IncomingMessage } from 'node:http'; + + interface BasicAuthResult { + name: string; + pass: string; + } + + function auth(req: IncomingMessage): BasicAuthResult | undefined; + namespace auth { + function parse(string: string): BasicAuthResult | undefined; + } + + export = auth; +} diff --git a/backend/test/ava-tests/connection-diagram-preview.test.ts b/backend/test/ava-tests/connection-diagram-preview.test.ts index a2fa3f9a1..331e035c8 100644 --- a/backend/test/ava-tests/connection-diagram-preview.test.ts +++ b/backend/test/ava-tests/connection-diagram-preview.test.ts @@ -212,7 +212,7 @@ test('buildMermaidErDiagram: highlight tags new columns with NEW marker', (t) => addedColumns: new Map([['users', new Set(['nickname'])]]), addedForeignKeys: new Map(), }); - t.regex(diagram, /varchar nickname "[^"\n]*\[NEW\]"/); + t.regex(diagram, /varchar nickname[^\n]*\[NEW\]"/); }); test('buildMermaidErDiagram: highlight tags new FK relationships with [NEW]', (t) => { diff --git a/backend/test/ava-tests/saas-tests/custom-domains-e2e.test.ts b/backend/test/ava-tests/saas-tests/custom-domains-e2e.test.ts index 789985d1c..3374aeb53 100644 --- a/backend/test/ava-tests/saas-tests/custom-domains-e2e.test.ts +++ b/backend/test/ava-tests/saas-tests/custom-domains-e2e.test.ts @@ -128,7 +128,7 @@ test.serial(`${currentTest} - should return registered custom domain`, async (t) t.is(Object.hasOwn(registerDomainResponseRO, 'createdAt'), true); t.is(Object.keys(registerDomainResponseRO).length, 5); } catch (error) { - t.fail(error.message); + t.fail((error as Error).message); } }); @@ -166,7 +166,7 @@ test.serial(`${currentTest} - should throw exception when hostname is incorrect` ); t.is(registerDomainResponse.status, 400); } catch (error) { - t.fail(error.message); + t.fail((error as Error).message); } }); @@ -243,7 +243,7 @@ test.serial(`${currentTest} - should return found custom domain`, async (t) => { t.is(Object.hasOwn(foundCompanyFullInfoResponseRO, 'custom_domain'), true); t.is(foundCompanyFullInfoResponseRO.custom_domain, requestDomainData.hostname); } catch (error) { - t.fail(error.message); + t.fail((error as Error).message); } }); @@ -296,7 +296,7 @@ test.serial(`${currentTest} - should throw exception when company id is invalid` ); t.is(foundDomainResponse.status, 404); } catch (error) { - t.fail(error.message); + t.fail((error as Error).message); } }); @@ -362,7 +362,7 @@ test.serial(`${currentTest} - should return updated custom domain`, async (t) => t.is(Object.hasOwn(updateDomainResponseRO, 'updatedAt'), true); t.is(Object.keys(updateDomainResponseRO).length, 5); } catch (error) { - t.fail(error.message); + t.fail((error as Error).message); } }); @@ -418,7 +418,7 @@ test.serial(`${currentTest} - should throw exception when hostname is invalid`, ); t.is(updateDomainResponse.status, 400); } catch (error) { - t.fail(error.message); + t.fail((error as Error).message); } }); @@ -475,7 +475,7 @@ test.serial(`${currentTest} - should throw exception when company id is incorrec ); t.is(updateDomainResponse.status, 404); } catch (error) { - t.fail(error.message); + t.fail((error as Error).message); } }); @@ -546,6 +546,6 @@ test.serial(`${currentTest} - should delete custom domain`, async (t) => { t.is(Object.hasOwn(foundDomainResponseRO, 'domain_info'), true); t.is(foundDomainResponseRO.domain_info, null); } catch (error) { - t.fail(error.message); + t.fail((error as Error).message); } }); diff --git a/backend/test/ava-tests/saas-tests/table-widgets-e2e.test.ts b/backend/test/ava-tests/saas-tests/table-widgets-e2e.test.ts index 74331f414..83d69a334 100644 --- a/backend/test/ava-tests/saas-tests/table-widgets-e2e.test.ts +++ b/backend/test/ava-tests/saas-tests/table-widgets-e2e.test.ts @@ -1577,7 +1577,7 @@ test.serial( await dynamoDb.createTable(referencedOnTableTableNameTableParams); await dynamoDb.createTable(referencedByTableTableNameTableParams); } catch (error) { - console.error(`Error creating dynamodb table: ${error.message}`); + console.error(`Error creating dynamodb table: ${(error as Error).message}`); } const documentClient = DynamoDBDocumentClient.from(dynamoDb); diff --git a/backend/test/utils/create-test-table.ts b/backend/test/utils/create-test-table.ts index d4384e867..6f2a846b7 100644 --- a/backend/test/utils/create-test-table.ts +++ b/backend/test/utils/create-test-table.ts @@ -647,7 +647,7 @@ async function createTestDynamoDBTable( try { await dynamoDb.createTable(params); } catch (error) { - console.error(`Error creating dynamodb table: ${error.message}`); + console.error(`Error creating dynamodb table: ${(error as Error).message}`); } const insertedSearchedIds: Array<{ number: number; id: string }> = []; const documentClient = DynamoDBDocumentClient.from(dynamoDb); @@ -698,7 +698,7 @@ async function createTestDynamoDBTable( } } } catch (error) { - console.error(`Error inserting item into dynamodb table: ${error.message}`); + console.error(`Error inserting item into dynamodb table: ${(error as Error).message}`); throw error; } @@ -762,7 +762,7 @@ async function createTestCassandraTable( `CREATE TABLE IF NOT EXISTS ${testTableName} (id UUID, ${testTableColumnName} TEXT, ${testTableSecondColumnName} TEXT, age INT, created_at TIMESTAMP, updated_at TIMESTAMP, PRIMARY KEY (id, age))`, ); } catch (error) { - console.error(`Error creating Cassandra table: ${error.message}`); + console.error(`Error creating Cassandra table: ${(error as Error).message}`); throw error; } const insertedSearchedIds: Array<{ number: number; id: string }> = []; @@ -800,7 +800,7 @@ async function createTestCassandraTable( await Promise.all(chunk.map((params) => client.execute(query, params, { prepare: true }))); } } catch (error) { - console.error(`Error inserting into Cassandra table: ${error.message}`); + console.error(`Error inserting into Cassandra table: ${(error as Error).message}`); throw error; } return { diff --git a/backend/tsconfig.json b/backend/tsconfig.json index 68f10879c..2587c3152 100644 --- a/backend/tsconfig.json +++ b/backend/tsconfig.json @@ -5,7 +5,6 @@ "removeComments": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, - "allowJs": true, "esModuleInterop": true, "target": "ESNext", "sourceMap": true, @@ -14,7 +13,11 @@ "incremental": true, "moduleResolution": "NodeNext", "skipLibCheck": true, - "strict": false + "strict": false, + "forceConsistentCasingInFileNames": true, + "noFallthroughCasesInSwitch": true, + "noImplicitOverride": true, + "useUnknownInCatchVariables": true }, // "references": [ // { "path": "../shared-code" }, diff --git a/backend/tsconfig.src.json b/backend/tsconfig.src.json new file mode 100644 index 000000000..af95aca28 --- /dev/null +++ b/backend/tsconfig.src.json @@ -0,0 +1,11 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "noImplicitAny": true, + "strictBindCallApply": true, + "strictFunctionTypes": true, + "alwaysStrict": true + }, + "include": ["src"], + "exclude": ["node_modules", "dist", "../node_modules", "test"] +}