diff --git a/packages/twenty-server/src/commands/command-logger.ts b/packages/twenty-server/src/commands/command-logger.ts index fa1b393908..b78d3b66d7 100644 --- a/packages/twenty-server/src/commands/command-logger.ts +++ b/packages/twenty-server/src/commands/command-logger.ts @@ -9,6 +9,16 @@ import { kebabCase } from 'src/utils/kebab-case'; export class CommandLogger { constructor(private readonly className: string) {} + async createSubDirectory(subDirectory: string): Promise { + const path = `./logs/${kebabCase(this.className)}/${subDirectory}`; + + if (existsSync(path) === false) { + await fs.mkdir(path, { recursive: true }); + } + + return; + } + async writeLog( fileName: string, data: unknown, diff --git a/packages/twenty-server/src/core/workspace/services/workspace.service.ts b/packages/twenty-server/src/core/workspace/services/workspace.service.ts index 285cec1b3c..722adf242e 100644 --- a/packages/twenty-server/src/core/workspace/services/workspace.service.ts +++ b/packages/twenty-server/src/core/workspace/services/workspace.service.ts @@ -64,4 +64,10 @@ export class WorkspaceService extends TypeOrmQueryService { return workspace; } + + async getWorkspaceIds() { + return this.workspaceRepository + .find() + .then((workspaces) => workspaces.map((workspace) => workspace.id)); + } } diff --git a/packages/twenty-server/src/workspace/workspace-sync-metadata/commands/services/sync-workspace-logger.service.ts b/packages/twenty-server/src/workspace/workspace-sync-metadata/commands/services/sync-workspace-logger.service.ts index c579bb820b..db2f01742c 100644 --- a/packages/twenty-server/src/workspace/workspace-sync-metadata/commands/services/sync-workspace-logger.service.ts +++ b/packages/twenty-server/src/workspace/workspace-sync-metadata/commands/services/sync-workspace-logger.service.ts @@ -13,66 +13,70 @@ export class SyncWorkspaceLoggerService { constructor() {} async saveLogs( + workspaceId: string, storage: WorkspaceSyncStorage, workspaceMigrations: WorkspaceMigrationEntity[], ) { + // Create sub directory + await this.commandLogger.createSubDirectory(workspaceId); + // Save workspace migrations await this.commandLogger.writeLog( - 'workspace-migrations', + `${workspaceId}/workspace-migrations`, workspaceMigrations, ); // Save object metadata create collection await this.commandLogger.writeLog( - 'object-metadata-create-collection', + `${workspaceId}/object-metadata-create-collection`, storage.objectMetadataCreateCollection, ); // Save object metadata update collection await this.commandLogger.writeLog( - 'object-metadata-update-collection', + `${workspaceId}/object-metadata-update-collection`, storage.objectMetadataUpdateCollection, ); // Save object metadata delete collection await this.commandLogger.writeLog( - 'object-metadata-delete-collection', + `${workspaceId}/object-metadata-delete-collection`, storage.objectMetadataDeleteCollection, ); // Save field metadata create collection await this.commandLogger.writeLog( - 'field-metadata-create-collection', + `${workspaceId}/field-metadata-create-collection`, storage.fieldMetadataCreateCollection, ); // Save field metadata update collection await this.commandLogger.writeLog( - 'field-metadata-update-collection', + `${workspaceId}/field-metadata-update-collection`, storage.fieldMetadataUpdateCollection, ); // Save field metadata delete collection await this.commandLogger.writeLog( - 'field-metadata-delete-collection', + `${workspaceId}/field-metadata-delete-collection`, storage.fieldMetadataDeleteCollection, ); // Save relation metadata create collection await this.commandLogger.writeLog( - 'relation-metadata-create-collection', + `${workspaceId}/relation-metadata-create-collection`, storage.relationMetadataCreateCollection, ); // Save relation metadata update collection await this.commandLogger.writeLog( - 'relation-metadata-update-collection', + `${workspaceId}/relation-metadata-update-collection`, storage.relationMetadataUpdateCollection, ); // Save relation metadata delete collection await this.commandLogger.writeLog( - 'relation-metadata-delete-collection', + `${workspaceId}/relation-metadata-delete-collection`, storage.relationMetadataDeleteCollection, ); } diff --git a/packages/twenty-server/src/workspace/workspace-sync-metadata/commands/sync-workspace-metadata.command.ts b/packages/twenty-server/src/workspace/workspace-sync-metadata/commands/sync-workspace-metadata.command.ts index d8962e4a04..a2036a107b 100644 --- a/packages/twenty-server/src/workspace/workspace-sync-metadata/commands/sync-workspace-metadata.command.ts +++ b/packages/twenty-server/src/workspace/workspace-sync-metadata/commands/sync-workspace-metadata.command.ts @@ -5,12 +5,13 @@ import { Command, CommandRunner, Option } from 'nest-commander'; import { DataSourceService } from 'src/metadata/data-source/data-source.service'; import { WorkspaceSyncMetadataService } from 'src/workspace/workspace-sync-metadata/workspace-sync-metadata.service'; import { WorkspaceHealthService } from 'src/workspace/workspace-health/workspace-health.service'; +import { WorkspaceService } from 'src/core/workspace/services/workspace.service'; import { SyncWorkspaceLoggerService } from './services/sync-workspace-logger.service'; // TODO: implement dry-run interface RunWorkspaceMigrationsOptions { - workspaceId: string; + workspaceId?: string; dryRun?: boolean; force?: boolean; } @@ -27,6 +28,7 @@ export class SyncWorkspaceMetadataCommand extends CommandRunner { private readonly workspaceHealthService: WorkspaceHealthService, private readonly dataSourceService: DataSourceService, private readonly syncWorkspaceLoggerService: SyncWorkspaceLoggerService, + private readonly workspaceService: WorkspaceService, ) { super(); } @@ -35,56 +37,63 @@ export class SyncWorkspaceMetadataCommand extends CommandRunner { _passedParam: string[], options: RunWorkspaceMigrationsOptions, ): Promise { - const issues = await this.workspaceHealthService.healthCheck( - options.workspaceId, - ); + const workspaceIds = options.workspaceId + ? [options.workspaceId] + : await this.workspaceService.getWorkspaceIds(); - // Security: abort if there are issues. - if (issues.length > 0) { - if (!options.force) { - this.logger.error( - `Workspace contains ${issues.length} issues, aborting.`, + for (const workspaceId of workspaceIds) { + const issues = await this.workspaceHealthService.healthCheck(workspaceId); + + // Security: abort if there are issues. + if (issues.length > 0) { + if (!options.force) { + this.logger.error( + `Workspace contains ${issues.length} issues, aborting.`, + ); + + this.logger.log( + 'If you want to force the migration, use --force flag', + ); + this.logger.log( + 'Please use `workspace:health` command to check issues and fix them before running this command.', + ); + + return; + } + + this.logger.warn( + `Workspace contains ${issues.length} issues, sync has been forced.`, ); - - this.logger.log('If you want to force the migration, use --force flag'); - this.logger.log( - 'Please use `workspace:health` command to check issues and fix them before running this command.', - ); - - return; } - this.logger.warn( - `Workspace contains ${issues.length} issues, sync has been forced.`, - ); - } + const dataSourceMetadata = + await this.dataSourceService.getLastDataSourceMetadataFromWorkspaceIdOrFail( + workspaceId, + ); - const dataSourceMetadata = - await this.dataSourceService.getLastDataSourceMetadataFromWorkspaceIdOrFail( - options.workspaceId, - ); + const { storage, workspaceMigrations } = + await this.workspaceSyncMetadataService.syncStandardObjectsAndFieldsMetadata( + { + workspaceId, + dataSourceId: dataSourceMetadata.id, + }, + { applyChanges: !options.dryRun }, + ); - const { storage, workspaceMigrations } = - await this.workspaceSyncMetadataService.syncStandardObjectsAndFieldsMetadata( - { - workspaceId: options.workspaceId, - dataSourceId: dataSourceMetadata.id, - }, - { applyChanges: !options.dryRun }, - ); - - if (options.dryRun) { - await this.syncWorkspaceLoggerService.saveLogs( - storage, - workspaceMigrations, - ); + if (options.dryRun) { + await this.syncWorkspaceLoggerService.saveLogs( + workspaceId, + storage, + workspaceMigrations, + ); + } } } @Option({ flags: '-w, --workspace-id [workspace_id]', description: 'workspace id', - required: true, + required: false, }) parseWorkspaceId(value: string): string { return value; diff --git a/packages/twenty-server/src/workspace/workspace-sync-metadata/commands/workspace-sync-metadata-commands.module.ts b/packages/twenty-server/src/workspace/workspace-sync-metadata/commands/workspace-sync-metadata-commands.module.ts index f8ac2f6580..a2c5024936 100644 --- a/packages/twenty-server/src/workspace/workspace-sync-metadata/commands/workspace-sync-metadata-commands.module.ts +++ b/packages/twenty-server/src/workspace/workspace-sync-metadata/commands/workspace-sync-metadata-commands.module.ts @@ -3,6 +3,7 @@ import { Module } from '@nestjs/common'; import { DataSourceModule } from 'src/metadata/data-source/data-source.module'; import { WorkspaceSyncMetadataModule } from 'src/workspace/workspace-sync-metadata/workspace-sync-metadata.module'; import { WorkspaceHealthModule } from 'src/workspace/workspace-health/workspace-health.module'; +import { WorkspaceModule } from 'src/core/workspace/workspace.module'; import { SyncWorkspaceMetadataCommand } from './sync-workspace-metadata.command'; @@ -12,6 +13,7 @@ import { SyncWorkspaceLoggerService } from './services/sync-workspace-logger.ser imports: [ WorkspaceSyncMetadataModule, WorkspaceHealthModule, + WorkspaceModule, DataSourceModule, ], providers: [SyncWorkspaceMetadataCommand, SyncWorkspaceLoggerService],