From 8953bf089dfd8eeaeacc39cab94139636daff5ed Mon Sep 17 00:00:00 2001 From: Jaakko Husso Date: Tue, 21 Oct 2025 09:57:33 +0300 Subject: [PATCH] refactor(core): Drop unused chat hub columns (no-changelog) (#20973) --- packages/@n8n/api-types/src/chat-hub.ts | 4 +- packages/@n8n/api-types/src/index.ts | 2 +- .../1760965142113-DropUnusedChatHubColumns.ts | 36 ++++++++++++ .../1760965142113-DropUnusedChatHubColumns.ts | 57 +++++++++++++++++++ .../@n8n/db/src/migrations/mysqldb/index.ts | 2 + .../db/src/migrations/postgresdb/index.ts | 2 + .../@n8n/db/src/migrations/sqlite/index.ts | 2 + .../chat-hub/chat-hub-message.entity.ts | 6 +- .../src/modules/chat-hub/chat-hub.service.ts | 16 +++--- .../chat-hub/chat-message.repository.ts | 4 +- .../src/features/ai/chatHub/chat.store.ts | 8 +-- 11 files changed, 119 insertions(+), 20 deletions(-) create mode 100644 packages/@n8n/db/src/migrations/common/1760965142113-DropUnusedChatHubColumns.ts create mode 100644 packages/@n8n/db/src/migrations/mysqldb/1760965142113-DropUnusedChatHubColumns.ts diff --git a/packages/@n8n/api-types/src/chat-hub.ts b/packages/@n8n/api-types/src/chat-hub.ts index 9777a936cca..03772660ecf 100644 --- a/packages/@n8n/api-types/src/chat-hub.ts +++ b/packages/@n8n/api-types/src/chat-hub.ts @@ -94,7 +94,7 @@ export class ChatHubChangeConversationTitleRequest extends Z.class({ }) {} export type ChatHubMessageType = 'human' | 'ai' | 'system' | 'tool' | 'generic'; -export type ChatHubMessageState = 'success' | 'error' | 'pending'; +export type ChatHubMessageStatus = 'success' | 'error' | 'running' | 'cancelled'; export type ChatSessionId = string; // UUID export type ChatMessageId = string; // UUID @@ -122,7 +122,7 @@ export interface ChatHubMessageDto { model: string | null; workflowId: string | null; executionId: number | null; - state: ChatHubMessageState; + status: ChatHubMessageStatus; createdAt: string; updatedAt: string; diff --git a/packages/@n8n/api-types/src/index.ts b/packages/@n8n/api-types/src/index.ts index 5ab3a6452bf..2c4e45fa191 100644 --- a/packages/@n8n/api-types/src/index.ts +++ b/packages/@n8n/api-types/src/index.ts @@ -12,7 +12,7 @@ export { chatHubProviderSchema, type ChatHubProvider, type ChatHubMessageType, - type ChatHubMessageState, + type ChatHubMessageStatus, PROVIDER_CREDENTIAL_TYPE_MAP, chatModelsRequestSchema, type ChatModelsRequest, diff --git a/packages/@n8n/db/src/migrations/common/1760965142113-DropUnusedChatHubColumns.ts b/packages/@n8n/db/src/migrations/common/1760965142113-DropUnusedChatHubColumns.ts new file mode 100644 index 00000000000..2d53ee65305 --- /dev/null +++ b/packages/@n8n/db/src/migrations/common/1760965142113-DropUnusedChatHubColumns.ts @@ -0,0 +1,36 @@ +import type { MigrationContext, ReversibleMigration } from '../migration-types'; + +const table = { + messages: 'chat_hub_messages', +} as const; + +export class DropUnusedChatHubColumns1760965142113 implements ReversibleMigration { + async up({ schemaBuilder: { dropColumns, addColumns, column } }: MigrationContext) { + await dropColumns(table.messages, ['turnId', 'runIndex', 'state']); + await addColumns(table.messages, [ + column('status') + .varchar(16) + .default("'success'") + .notNull.comment( + 'ChatHubMessageStatus enum, eg. "success", "error", "running", "cancelled"', + ), + ]); + } + + async down({ + schemaBuilder: { dropColumns, addColumns, column, addForeignKey }, + }: MigrationContext) { + await dropColumns(table.messages, ['status']); + await addColumns(table.messages, [ + column('turnId').uuid, + column('runIndex') + .int.notNull.default(0) + .comment('The nth attempt this message has been generated/retried this turn'), + column('state') + .varchar(16) + .default("'active'") + .notNull.comment('ChatHubMessageState enum: "active", "superseded", "hidden", "deleted"'), + ]); + await addForeignKey(table.messages, 'turnId', [table.messages, 'id'], undefined, 'CASCADE'); + } +} diff --git a/packages/@n8n/db/src/migrations/mysqldb/1760965142113-DropUnusedChatHubColumns.ts b/packages/@n8n/db/src/migrations/mysqldb/1760965142113-DropUnusedChatHubColumns.ts new file mode 100644 index 00000000000..db5e60ab833 --- /dev/null +++ b/packages/@n8n/db/src/migrations/mysqldb/1760965142113-DropUnusedChatHubColumns.ts @@ -0,0 +1,57 @@ +import type { MigrationContext, ReversibleMigration } from '../migration-types'; + +const table = { + messages: 'chat_hub_messages', +} as const; + +export class DropUnusedChatHubColumns1760965142113 implements ReversibleMigration { + async up({ + runQuery, + tablePrefix, + schemaBuilder: { addColumns, dropColumns, column }, + }: MigrationContext) { + // MySQL needs to drop foreign keys before dropping a column (turnId) that uses it. + // Name of the FK depends on table prefix, so we can't just hardcode it. + const tableName = `${tablePrefix}${table.messages}`; + const foreignKeys: Array<{ name: string }> = await runQuery( + `SELECT CONSTRAINT_NAME AS name + FROM information_schema.KEY_COLUMN_USAGE + WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = '${tableName}' + AND COLUMN_NAME = 'turnId' + AND REFERENCED_TABLE_NAME IS NOT NULL;`, + ); + + // There should only be one, but just in case handle multiple + for (const { name } of foreignKeys) { + await runQuery(`ALTER TABLE \`${tableName}\` DROP FOREIGN KEY \`${name}\`;`); + } + await dropColumns(table.messages, ['turnId', 'runIndex', 'state']); + + await addColumns(table.messages, [ + column('status') + .varchar(16) + .default("'success'") + .notNull.comment( + 'ChatHubMessageStatus enum, eg. "success", "error", "running", "cancelled"', + ), + ]); + } + + async down({ + schemaBuilder: { dropColumns, addColumns, column, addForeignKey }, + }: MigrationContext) { + await dropColumns(table.messages, ['status']); + await addColumns(table.messages, [ + column('turnId').uuid, + column('runIndex') + .int.notNull.default(0) + .comment('The nth attempt this message has been generated/retried this turn'), + column('state') + .varchar(16) + .default("'active'") + .notNull.comment('ChatHubMessageState enum: "active", "superseded", "hidden", "deleted"'), + ]); + await addForeignKey(table.messages, 'turnId', [table.messages, 'id'], undefined, 'CASCADE'); + } +} diff --git a/packages/@n8n/db/src/migrations/mysqldb/index.ts b/packages/@n8n/db/src/migrations/mysqldb/index.ts index 00939760938..17f776db6c6 100644 --- a/packages/@n8n/db/src/migrations/mysqldb/index.ts +++ b/packages/@n8n/db/src/migrations/mysqldb/index.ts @@ -1,3 +1,4 @@ +import { DropUnusedChatHubColumns1760965142113 } from './1760965142113-DropUnusedChatHubColumns'; import { AddAudienceColumnToApiKeys1758731786132 } from '../common/1758731786132-AddAudienceColumnToApiKey'; import { CreateWorkflowDependencyTable1760314000000 } from '../common/1760314000000-CreateWorkflowDependencyTable'; import { AddMfaColumns1690000000030 } from './../common/1690000000040-AddMfaColumns'; @@ -213,4 +214,5 @@ export const mysqlMigrations: Migration[] = [ CreateChatHubTables1760019379982, UniqueRoleNames1760020838000, CreateWorkflowDependencyTable1760314000000, + DropUnusedChatHubColumns1760965142113, ]; diff --git a/packages/@n8n/db/src/migrations/postgresdb/index.ts b/packages/@n8n/db/src/migrations/postgresdb/index.ts index d8e0807e0aa..4242a170df1 100644 --- a/packages/@n8n/db/src/migrations/postgresdb/index.ts +++ b/packages/@n8n/db/src/migrations/postgresdb/index.ts @@ -103,6 +103,7 @@ import { ChangeValueTypesForInsights1759399811000 } from '../common/175939981100 import { CreateChatHubTables1760019379982 } from '../common/1760019379982-CreateChatHubTables'; import { UniqueRoleNames1760020838000 } from '../common/1760020838000-UniqueRoleNames'; import { CreateWorkflowDependencyTable1760314000000 } from '../common/1760314000000-CreateWorkflowDependencyTable'; +import { DropUnusedChatHubColumns1760965142113 } from '../common/1760965142113-DropUnusedChatHubColumns'; import type { Migration } from '../migration-types'; export const postgresMigrations: Migration[] = [ @@ -211,4 +212,5 @@ export const postgresMigrations: Migration[] = [ CreateChatHubTables1760019379982, UniqueRoleNames1760020838000, CreateWorkflowDependencyTable1760314000000, + DropUnusedChatHubColumns1760965142113, ]; diff --git a/packages/@n8n/db/src/migrations/sqlite/index.ts b/packages/@n8n/db/src/migrations/sqlite/index.ts index 1045cffeb5f..09c6567dbb9 100644 --- a/packages/@n8n/db/src/migrations/sqlite/index.ts +++ b/packages/@n8n/db/src/migrations/sqlite/index.ts @@ -101,6 +101,7 @@ import { LinkRoleToProjectRelationTable1753953244168 } from './../common/1753953 import { AddProjectIdToVariableTable1758794506893 } from './1758794506893-AddProjectIdToVariableTable'; import { CreateChatHubTables1760019379982 } from '../common/1760019379982-CreateChatHubTables'; import { CreateWorkflowDependencyTable1760314000000 } from '../common/1760314000000-CreateWorkflowDependencyTable'; +import { DropUnusedChatHubColumns1760965142113 } from '../common/1760965142113-DropUnusedChatHubColumns'; const sqliteMigrations: Migration[] = [ InitialMigration1588102412422, @@ -205,6 +206,7 @@ const sqliteMigrations: Migration[] = [ CreateChatHubTables1760019379982, UniqueRoleNames1760020838000, CreateWorkflowDependencyTable1760314000000, + DropUnusedChatHubColumns1760965142113, ]; export { sqliteMigrations }; diff --git a/packages/cli/src/modules/chat-hub/chat-hub-message.entity.ts b/packages/cli/src/modules/chat-hub/chat-hub-message.entity.ts index d2b8f5848b0..6c805727ea0 100644 --- a/packages/cli/src/modules/chat-hub/chat-hub-message.entity.ts +++ b/packages/cli/src/modules/chat-hub/chat-hub-message.entity.ts @@ -1,4 +1,4 @@ -import type { ChatHubProvider, ChatHubMessageType, ChatHubMessageState } from '@n8n/api-types'; +import type { ChatHubProvider, ChatHubMessageType, ChatHubMessageStatus } from '@n8n/api-types'; import { ExecutionEntity, WithTimestamps, WorkflowEntity } from '@n8n/db'; import { Column, @@ -156,8 +156,8 @@ export class ChatHubMessage extends WithTimestamps { revisions?: Array>; /** - * State of the message, e.g. 'success', 'error'. + * Status of the message, e.g. 'running', 'success', 'error', 'cancelled'. */ @Column({ type: 'varchar', length: 16, default: 'success' }) - state: ChatHubMessageState; + status: ChatHubMessageStatus; } diff --git a/packages/cli/src/modules/chat-hub/chat-hub.service.ts b/packages/cli/src/modules/chat-hub/chat-hub.service.ts index cf125f28f82..121c8f851b8 100644 --- a/packages/cli/src/modules/chat-hub/chat-hub.service.ts +++ b/packages/cli/src/modules/chat-hub/chat-hub.service.ts @@ -9,7 +9,7 @@ import { type ChatMessageId, type ChatSessionId, ChatHubConversationModel, - ChatHubMessageState, + ChatHubMessageStatus, } from '@n8n/api-types'; import { Logger } from '@n8n/backend-common'; import { @@ -568,7 +568,7 @@ export class ChatHubService { message, selectedModel, retryOfMessageId, - state: 'error', + status: 'error', }); throw new OperationalError(`Chat workflow execution failed: ${message}`); @@ -583,7 +583,7 @@ export class ChatHubService { message, selectedModel, retryOfMessageId, - state: 'success', + status: 'success', }); } @@ -756,7 +756,7 @@ export class ChatHubService { sessionId: payload.sessionId, type: 'human', name: user.firstName || 'User', - state: 'success', + status: 'success', content: payload.message, previousMessageId, revisionOfMessageId, @@ -772,7 +772,7 @@ export class ChatHubService { message, selectedModel, retryOfMessageId, - state, + status, }: { id: ChatMessageId; sessionId: ChatSessionId; @@ -782,7 +782,7 @@ export class ChatHubService { selectedModel: ModelWithCredentials; retryOfMessageId?: ChatMessageId; editOfMessageId?: ChatMessageId; - state?: ChatHubMessageState; + status?: ChatHubMessageStatus; }) { await this.messageRepository.createChatMessage({ id, @@ -791,7 +791,7 @@ export class ChatHubService { executionId: parseInt(executionId, 10), type: 'ai', name: 'AI', - state, + status, content: message, retryOfMessageId, ...selectedModel, @@ -937,7 +937,7 @@ export class ChatHubService { model: message.model, workflowId: message.workflowId, executionId: message.executionId, - state: message.state, + status: message.status, createdAt: message.createdAt.toISOString(), updatedAt: message.updatedAt.toISOString(), diff --git a/packages/cli/src/modules/chat-hub/chat-message.repository.ts b/packages/cli/src/modules/chat-hub/chat-message.repository.ts index bfcd743ca1f..de4fc7abe53 100644 --- a/packages/cli/src/modules/chat-hub/chat-message.repository.ts +++ b/packages/cli/src/modules/chat-hub/chat-message.repository.ts @@ -1,4 +1,4 @@ -import type { ChatHubMessageState, ChatMessageId, ChatSessionId } from '@n8n/api-types'; +import type { ChatHubMessageStatus, ChatMessageId, ChatSessionId } from '@n8n/api-types'; import { withTransaction } from '@n8n/db'; import { Service } from '@n8n/di'; import { DataSource, EntityManager, Repository } from '@n8n/typeorm'; @@ -26,7 +26,7 @@ export class ChatHubMessageRepository extends Repository { async updateChatMessage( id: ChatMessageId, - fields: { state: ChatHubMessageState }, + fields: { status: ChatHubMessageStatus }, trx?: EntityManager, ) { return await withTransaction(this.manager, trx, async (em) => { diff --git a/packages/frontend/editor-ui/src/features/ai/chatHub/chat.store.ts b/packages/frontend/editor-ui/src/features/ai/chatHub/chat.store.ts index 03b6cdcf65d..ea8d2c0f3bb 100644 --- a/packages/frontend/editor-ui/src/features/ai/chatHub/chat.store.ts +++ b/packages/frontend/editor-ui/src/features/ai/chatHub/chat.store.ts @@ -245,7 +245,7 @@ export const useChatStore = defineStore(CHAT_STORE, () => { model: null, workflowId: null, executionId: null, - state: 'success', + status: 'success', createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), previousMessageId: replyToMessageId, @@ -337,7 +337,7 @@ export const useChatStore = defineStore(CHAT_STORE, () => { model: model?.model ?? null, workflowId: null, executionId: null, - state: 'success', + status: 'success', createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), previousMessageId, @@ -358,7 +358,7 @@ export const useChatStore = defineStore(CHAT_STORE, () => { model: null, workflowId: null, executionId: null, - state: 'error', + status: 'error', createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), previousMessageId: messageId, @@ -410,7 +410,7 @@ export const useChatStore = defineStore(CHAT_STORE, () => { model: null, workflowId: null, executionId: null, - state: 'success', + status: 'success', createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), previousMessageId,