diff --git a/packages/cli/src/Interfaces.ts b/packages/cli/src/Interfaces.ts index b6f1dd984ac1d..63f0a6b6beea0 100644 --- a/packages/cli/src/Interfaces.ts +++ b/packages/cli/src/Interfaces.ts @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/naming-convention */ import type { Application } from 'express'; import type { ExecutionError, @@ -82,6 +81,7 @@ export interface ICredentialsOverwrite { [key: string]: ICredentialDataDecryptedObject; } +/* eslint-disable @typescript-eslint/naming-convention */ export interface IDatabaseCollections { AuthIdentity: AuthIdentityRepository; AuthProviderSyncHistory: AuthProviderSyncHistoryRepository; @@ -103,6 +103,7 @@ export interface IDatabaseCollections { WorkflowStatistics: WorkflowStatisticsRepository; WorkflowTagMapping: WorkflowTagMappingRepository; } +/* eslint-enable @typescript-eslint/naming-convention */ // ---------------------------------- // tags @@ -789,3 +790,5 @@ export interface N8nApp { externalHooks: IExternalHooksClass; activeWorkflowRunner: ActiveWorkflowRunner; } + +export type UserSettings = Pick; diff --git a/packages/cli/src/databases/migrations/mysqldb/1681134145996-AddUserActivatedProperty.ts b/packages/cli/src/databases/migrations/mysqldb/1681134145996-AddUserActivatedProperty.ts new file mode 100644 index 0000000000000..e1c193a959935 --- /dev/null +++ b/packages/cli/src/databases/migrations/mysqldb/1681134145996-AddUserActivatedProperty.ts @@ -0,0 +1,60 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; +import { getTablePrefix, logMigrationEnd, logMigrationStart } from '@db/utils/migrationHelpers'; +import type { UserSettings } from '@/Interfaces'; + +export class AddUserActivatedProperty1681134145996 implements MigrationInterface { + name = 'AddUserActivatedProperty1681134145996'; + + async up(queryRunner: QueryRunner): Promise { + logMigrationStart(this.name); + + const tablePrefix = getTablePrefix(); + + const activatedUsers: UserSettings[] = await queryRunner.query( + `SELECT DISTINCT sw.userId AS id, + JSON_SET(COALESCE(u.settings, '{}'), '$.userActivated', true) AS settings + FROM ${tablePrefix}workflow_statistics AS ws + JOIN ${tablePrefix}shared_workflow as sw + ON ws.workflowId = sw.workflowId + JOIN ${tablePrefix}role AS r + ON r.id = sw.roleId + JOIN ${tablePrefix}user AS u + ON u.id = sw.userId + WHERE ws.name = 'production_success' + AND r.name = 'owner' + AND r.scope = 'workflow'`, + ); + + const updatedUsers = activatedUsers.map((user) => + queryRunner.query( + `UPDATE ${tablePrefix}user SET settings = '${JSON.stringify(user.settings)}' WHERE id = '${ + user.id + }' `, + ), + ); + + await Promise.all(updatedUsers); + + if (!activatedUsers.length) { + await queryRunner.query( + `UPDATE ${tablePrefix}user SET settings = JSON_SET(COALESCE(settings, '{}'), '$.userActivated', false)`, + ); + } else { + const activatedUserIds = activatedUsers.map((user) => `'${user.id}'`).join(','); + + await queryRunner.query( + `UPDATE ${tablePrefix}user SET settings = JSON_SET(COALESCE(settings, '{}'), '$.userActivated', false) WHERE id NOT IN (${activatedUserIds})`, + ); + } + + logMigrationEnd(this.name); + } + + async down(queryRunner: QueryRunner): Promise { + const tablePrefix = getTablePrefix(); + await queryRunner.query( + `UPDATE ${tablePrefix}user SET settings = JSON_REMOVE(settings, '$.userActivated')`, + ); + await queryRunner.query(`UPDATE ${tablePrefix}user SET settings = NULL WHERE settings = '{}'`); + } +} diff --git a/packages/cli/src/databases/migrations/mysqldb/index.ts b/packages/cli/src/databases/migrations/mysqldb/index.ts index 6d97fbceb14cb..3f53f8b24af55 100644 --- a/packages/cli/src/databases/migrations/mysqldb/index.ts +++ b/packages/cli/src/databases/migrations/mysqldb/index.ts @@ -36,6 +36,7 @@ import { MigrateExecutionStatus1676996103000 } from './1676996103000-MigrateExec import { UpdateRunningExecutionStatus1677236788851 } from './1677236788851-UpdateRunningExecutionStatus'; import { CreateExecutionMetadataTable1679416281779 } from './1679416281779-CreateExecutionMetadataTable'; import { CreateVariables1677501636753 } from './1677501636753-CreateVariables'; +import { AddUserActivatedProperty1681134145996 } from './1681134145996-AddUserActivatedProperty'; export const mysqlMigrations = [ InitialMigration1588157391238, @@ -76,4 +77,5 @@ export const mysqlMigrations = [ UpdateRunningExecutionStatus1677236788851, CreateExecutionMetadataTable1679416281779, CreateVariables1677501636753, + AddUserActivatedProperty1681134145996, ]; diff --git a/packages/cli/src/databases/migrations/postgresdb/1681134145996-AddUserActivatedProperty.ts b/packages/cli/src/databases/migrations/postgresdb/1681134145996-AddUserActivatedProperty.ts new file mode 100644 index 0000000000000..bf4293d4ddaae --- /dev/null +++ b/packages/cli/src/databases/migrations/postgresdb/1681134145996-AddUserActivatedProperty.ts @@ -0,0 +1,62 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; +import { getTablePrefix, logMigrationEnd, logMigrationStart } from '@db/utils/migrationHelpers'; +import type { UserSettings } from '@/Interfaces'; + +export class AddUserActivatedProperty1681134145996 implements MigrationInterface { + name = 'AddUserActivatedProperty1681134145996'; + + async up(queryRunner: QueryRunner): Promise { + logMigrationStart(this.name); + + const tablePrefix = getTablePrefix(); + + const activatedUsers: UserSettings[] = await queryRunner.query( + `SELECT DISTINCT sw."userId" AS id, + JSONB_SET(COALESCE(u.settings::jsonb, '{}'), '{userActivated}', 'true', true) as settings + FROM ${tablePrefix}workflow_statistics ws + JOIN ${tablePrefix}shared_workflow sw + ON ws."workflowId" = sw."workflowId" + JOIN ${tablePrefix}role r + ON r.id = sw."roleId" + JOIN "${tablePrefix}user" u + ON u.id = sw."userId" + WHERE ws.name = 'production_success' + AND r.name = 'owner' + AND r.scope = 'workflow'`, + ); + + const updatedUsers = activatedUsers.map((user) => + queryRunner.query( + `UPDATE "${tablePrefix}user" SET settings = '${JSON.stringify( + user.settings, + )}' WHERE id = '${user.id}' `, + ), + ); + + await Promise.all(updatedUsers); + + if (!activatedUsers.length) { + await queryRunner.query( + `UPDATE "${tablePrefix}user" SET settings = JSONB_SET(COALESCE(settings::jsonb, '{}'), '{userActivated}', 'false', true)`, + ); + } else { + const activatedUserIds = activatedUsers.map((user) => `'${user.id}'`).join(','); + + await queryRunner.query( + `UPDATE "${tablePrefix}user" SET settings = JSONB_SET(COALESCE(settings::jsonb, '{}'), '{userActivated}', 'false', true) WHERE id NOT IN (${activatedUserIds})`, + ); + } + + logMigrationEnd(this.name); + } + + async down(queryRunner: QueryRunner): Promise { + const tablePrefix = getTablePrefix(); + await queryRunner.query( + `UPDATE "${tablePrefix}user" SET settings = settings::jsonb - 'userActivated'`, + ); + await queryRunner.query( + `UPDATE "${tablePrefix}user" SET settings = NULL WHERE settings::jsonb = '{}'::jsonb`, + ); + } +} diff --git a/packages/cli/src/databases/migrations/postgresdb/index.ts b/packages/cli/src/databases/migrations/postgresdb/index.ts index 175ff14848473..231518df99249 100644 --- a/packages/cli/src/databases/migrations/postgresdb/index.ts +++ b/packages/cli/src/databases/migrations/postgresdb/index.ts @@ -34,6 +34,7 @@ import { MigrateExecutionStatus1676996103000 } from './1676996103000-MigrateExec import { UpdateRunningExecutionStatus1677236854063 } from './1677236854063-UpdateRunningExecutionStatus'; import { CreateExecutionMetadataTable1679416281778 } from './1679416281778-CreateExecutionMetadataTable'; import { CreateVariables1677501636754 } from './1677501636754-CreateVariables'; +import { AddUserActivatedProperty1681134145996 } from './1681134145996-AddUserActivatedProperty'; export const postgresMigrations = [ InitialMigration1587669153312, @@ -72,4 +73,5 @@ export const postgresMigrations = [ UpdateRunningExecutionStatus1677236854063, CreateExecutionMetadataTable1679416281778, CreateVariables1677501636754, + AddUserActivatedProperty1681134145996, ]; diff --git a/packages/cli/src/databases/migrations/sqlite/1681134145996-AddUserActivatedProperty.ts b/packages/cli/src/databases/migrations/sqlite/1681134145996-AddUserActivatedProperty.ts new file mode 100644 index 0000000000000..335c4b29a8e0f --- /dev/null +++ b/packages/cli/src/databases/migrations/sqlite/1681134145996-AddUserActivatedProperty.ts @@ -0,0 +1,57 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; +import { getTablePrefix, logMigrationEnd, logMigrationStart } from '@db/utils/migrationHelpers'; +import type { UserSettings } from '@/Interfaces'; + +export class AddUserActivatedProperty1681134145996 implements MigrationInterface { + name = 'AddUserActivatedProperty1681134145996'; + + async up(queryRunner: QueryRunner): Promise { + logMigrationStart(this.name); + + const tablePrefix = getTablePrefix(); + + const activatedUsers: UserSettings[] = await queryRunner.query( + `SELECT DISTINCT sw.userId AS id, + JSON_SET(COALESCE(u.settings, '{}'), '$.userActivated', JSON('true')) AS settings + FROM ${tablePrefix}workflow_statistics AS ws + JOIN ${tablePrefix}shared_workflow AS sw + ON ws.workflowId = sw.workflowId + JOIN ${tablePrefix}role AS r + ON r.id = sw.roleId + JOIN ${tablePrefix}user AS u + ON u.id = sw.userId + WHERE ws.name = 'production_success' + AND r.name = 'owner' + AND r.scope = "workflow"`, + ); + + const updatedUsers = activatedUsers.map((user) => + queryRunner.query( + `UPDATE ${tablePrefix}user SET settings = '${user.settings}' WHERE id = '${user.id}' `, + ), + ); + + await Promise.all(updatedUsers); + + if (!activatedUsers.length) { + await queryRunner.query( + `UPDATE ${tablePrefix}user SET settings = JSON_SET(COALESCE(settings, '{}'), '$.userActivated', JSON('false'))`, + ); + } else { + const activatedUserIds = activatedUsers.map((user) => `'${user.id}'`).join(','); + await queryRunner.query( + `UPDATE ${tablePrefix}user SET settings = JSON_SET(COALESCE(settings, '{}'), '$.userActivated', JSON('false')) WHERE id NOT IN (${activatedUserIds})`, + ); + } + + logMigrationEnd(this.name); + } + + async down(queryRunner: QueryRunner): Promise { + const tablePrefix = getTablePrefix(); + await queryRunner.query( + `UPDATE ${tablePrefix}user SET settings = JSON_REMOVE(settings, '$.userActivated')`, + ); + await queryRunner.query(`UPDATE ${tablePrefix}user SET settings = NULL WHERE settings = '{}'`); + } +} diff --git a/packages/cli/src/databases/migrations/sqlite/index.ts b/packages/cli/src/databases/migrations/sqlite/index.ts index b7e324823fb86..e78aad2bc69ac 100644 --- a/packages/cli/src/databases/migrations/sqlite/index.ts +++ b/packages/cli/src/databases/migrations/sqlite/index.ts @@ -33,6 +33,7 @@ import { MigrateExecutionStatus1676996103000 } from './1676996103000-MigrateExec import { UpdateRunningExecutionStatus1677237073720 } from './1677237073720-UpdateRunningExecutionStatus'; import { CreateExecutionMetadataTable1679416281777 } from './1679416281777-CreateExecutionMetadataTable'; import { CreateVariables1677501636752 } from './1677501636752-CreateVariables'; +import { AddUserActivatedProperty1681134145996 } from './1681134145996-AddUserActivatedProperty'; const sqliteMigrations = [ InitialMigration1588102412422, @@ -70,6 +71,7 @@ const sqliteMigrations = [ UpdateRunningExecutionStatus1677237073720, CreateVariables1677501636752, CreateExecutionMetadataTable1679416281777, + AddUserActivatedProperty1681134145996, ]; export { sqliteMigrations };