diff --git a/n8n-n8n-1.109.2/.claude/settings.local.json b/n8n-n8n-1.109.2/.claude/settings.local.json new file mode 100644 index 00000000..fd7a9521 --- /dev/null +++ b/n8n-n8n-1.109.2/.claude/settings.local.json @@ -0,0 +1,12 @@ +{ + "permissions": { + "allow": [ + "Bash(pnpm run dev:*)", + "Bash(pnpm list:*)", + "Bash(pnpm build:*)", + "Read(//e/work/n8n_Demo/**)" + ], + "deny": [], + "ask": [] + } +} \ No newline at end of file diff --git a/n8n-n8n-1.109.2/packages/@n8n/node-cli/package.json b/n8n-n8n-1.109.2/packages/@n8n/node-cli/package.json index 3479c910..503399ab 100755 --- a/n8n-n8n-1.109.2/packages/@n8n/node-cli/package.json +++ b/n8n-n8n-1.109.2/packages/@n8n/node-cli/package.json @@ -19,7 +19,7 @@ "clean": "rimraf dist .turbo", "typecheck": "tsc --noEmit", "copy-templates": "node scripts/copy-templates.mjs", - "dev": "tsc -p tsconfig.build.json -w --onCompilationComplete \"pnpm copy-templates\"", + "dev": "tsc-watch -p tsconfig.build.json --onSuccess \"pnpm copy-templates\"", "format": "biome format --write src", "format:check": "biome ci src", "lint": "eslint src --quiet", diff --git a/n8n-n8n-1.109.2/packages/cli/src/modules/data-store/data-store-column.repository.ts b/n8n-n8n-1.109.2/packages/cli/src/modules/data-store/data-store-column.repository.ts index e8a3d828..667f4b1e 100755 --- a/n8n-n8n-1.109.2/packages/cli/src/modules/data-store/data-store-column.repository.ts +++ b/n8n-n8n-1.109.2/packages/cli/src/modules/data-store/data-store-column.repository.ts @@ -55,7 +55,7 @@ export class DataStoreColumnRepository extends Repository { dataStoreId, }); - await em.insert(DataStoreColumn, column); + await em.insert(DataStoreColumn, { ...column } as any); const queryRunner = em.queryRunner; if (!queryRunner) { diff --git a/n8n-n8n-1.109.2/packages/cli/src/modules/data-store/data-store.repository.ts b/n8n-n8n-1.109.2/packages/cli/src/modules/data-store/data-store.repository.ts index da51e829..a75dd8ed 100755 --- a/n8n-n8n-1.109.2/packages/cli/src/modules/data-store/data-store.repository.ts +++ b/n8n-n8n-1.109.2/packages/cli/src/modules/data-store/data-store.repository.ts @@ -28,7 +28,7 @@ export class DataStoreRepository extends Repository { let dataStoreId: string | undefined; await this.manager.transaction(async (em) => { const dataStore = em.create(DataStore, { name, columns, projectId }); - await em.insert(DataStore, dataStore); + await em.insert(DataStore, { ...dataStore } as any); dataStoreId = dataStore.id; const queryRunner = em.queryRunner; @@ -47,7 +47,7 @@ export class DataStoreRepository extends Repository { ); if (columnEntities.length > 0) { - await em.insert(DataStoreColumn, columnEntities); + await em.insert(DataStoreColumn, columnEntities as any); } // create user table (will create empty table with just id column if no columns) diff --git a/n8n-n8n-1.109.2/packages/nodes-base/nodes/WooCommerce/descriptions/CustomerDescription.ts b/n8n-n8n-1.109.2/packages/nodes-base/nodes/WooCommerce/descriptions/CustomerDescription.ts new file mode 100644 index 00000000..65a8e018 --- /dev/null +++ b/n8n-n8n-1.109.2/packages/nodes-base/nodes/WooCommerce/descriptions/CustomerDescription.ts @@ -0,0 +1,225 @@ +import type { INodeProperties } from 'n8n-workflow'; + +import { customerCreateFields, customerUpdateFields } from './shared'; + +export const customerOperations: INodeProperties[] = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + noDataExpression: true, + displayOptions: { + show: { + resource: ['customer'], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a customer', + action: 'Create a customer', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a customer', + action: 'Delete a customer', + }, + { + name: 'Get', + value: 'get', + description: 'Retrieve a customer', + action: 'Get a customer', + }, + { + name: 'Get Many', + value: 'getAll', + description: 'Retrieve many customers', + action: 'Get many customers', + }, + { + name: 'Update', + value: 'update', + description: 'Update a customer', + action: 'Update a customer', + }, + ], + default: 'create', + }, +]; + +export const customerFields: INodeProperties[] = [ + // ---------------------------------------- + // customer: create + // ---------------------------------------- + { + displayName: 'Email', + name: 'email', + type: 'string', + placeholder: 'name@email.com', + required: true, + default: '', + displayOptions: { + show: { + resource: ['customer'], + operation: ['create'], + }, + }, + }, + customerCreateFields, + + // ---------------------------------------- + // customer: delete + // ---------------------------------------- + { + displayName: 'Customer ID', + name: 'customerId', + description: 'ID of the customer to delete', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: ['customer'], + operation: ['delete'], + }, + }, + }, + + // ---------------------------------------- + // customer: get + // ---------------------------------------- + { + displayName: 'Customer ID', + name: 'customerId', + description: 'ID of the customer to retrieve', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: ['customer'], + operation: ['get'], + }, + }, + }, + + // ---------------------------------------- + // customer: getAll + // ---------------------------------------- + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + default: false, + description: 'Whether to return all results or only up to a given limit', + displayOptions: { + show: { + resource: ['customer'], + operation: ['getAll'], + }, + }, + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + default: 50, + description: 'Max number of results to return', + typeOptions: { + minValue: 1, + }, + displayOptions: { + show: { + resource: ['customer'], + operation: ['getAll'], + returnAll: [false], + }, + }, + }, + { + displayName: 'Filters', + name: 'filters', + type: 'collection', + placeholder: 'Add Filter', + default: {}, + displayOptions: { + show: { + resource: ['customer'], + operation: ['getAll'], + }, + }, + options: [ + { + displayName: 'Email', + name: 'email', + type: 'string', + placeholder: 'name@email.com', + default: '', + description: 'Email address to filter customers by', + }, + { + displayName: 'Sort Order', + name: 'order', + description: 'Order to sort customers in', + type: 'options', + options: [ + { + name: 'Ascending', + value: 'asc', + }, + { + name: 'Descending', + value: 'desc', + }, + ], + default: 'asc', + }, + { + displayName: 'Order By', + name: 'orderby', + description: 'Field to sort customers by', + type: 'options', + options: [ + { + name: 'ID', + value: 'id', + }, + { + name: 'Include', + value: 'include', + }, + { + name: 'Name', + value: 'name', + }, + { + name: 'Registered Date', + value: 'registered_date', + }, + ], + default: 'id', + }, + ], + }, + + // ---------------------------------------- + // customer: update + // ---------------------------------------- + { + displayName: 'Customer ID', + name: 'customerId', + description: 'ID of the customer to update', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: ['customer'], + operation: ['update'], + }, + }, + }, + customerUpdateFields, +]; diff --git a/n8n-n8n-1.109.2/packages/nodes-base/nodes/WooCommerce/descriptions/index.ts b/n8n-n8n-1.109.2/packages/nodes-base/nodes/WooCommerce/descriptions/index.ts new file mode 100644 index 00000000..184b55e6 --- /dev/null +++ b/n8n-n8n-1.109.2/packages/nodes-base/nodes/WooCommerce/descriptions/index.ts @@ -0,0 +1 @@ +export * from './CustomerDescription'; diff --git a/n8n-n8n-1.109.2/packages/nodes-base/nodes/WooCommerce/descriptions/shared.ts b/n8n-n8n-1.109.2/packages/nodes-base/nodes/WooCommerce/descriptions/shared.ts new file mode 100644 index 00000000..f8abfb82 --- /dev/null +++ b/n8n-n8n-1.109.2/packages/nodes-base/nodes/WooCommerce/descriptions/shared.ts @@ -0,0 +1,185 @@ +import type { INodeProperties } from 'n8n-workflow'; + +const customerAddressOptions: INodeProperties[] = [ + { + displayName: 'First Name', + name: 'first_name', + type: 'string', + default: '', + }, + { + displayName: 'Last Name', + name: 'last_name', + type: 'string', + default: '', + }, + { + displayName: 'Company', + name: 'company', + type: 'string', + default: '', + }, + { + displayName: 'Address 1', + name: 'address_1', + type: 'string', + default: '', + }, + { + displayName: 'Address 2', + name: 'address_2', + type: 'string', + default: '', + }, + { + displayName: 'City', + name: 'city', + type: 'string', + default: '', + }, + { + displayName: 'State', + name: 'state', + type: 'string', + default: '', + }, + { + displayName: 'Postcode', + name: 'postcode', + type: 'string', + default: '', + }, + { + displayName: 'Country', + name: 'country', + type: 'string', + default: '', + }, + { + displayName: 'Email', + name: 'email', + type: 'string', + placeholder: 'name@email.com', + default: '', + }, + { + displayName: 'Phone', + name: 'phone', + type: 'string', + default: '', + }, +]; + +const customerUpdateOptions: INodeProperties[] = [ + { + displayName: 'Billing Address', + name: 'billing', + type: 'collection', + default: {}, + placeholder: 'Add Field', + options: customerAddressOptions, + }, + { + displayName: 'First Name', + name: 'first_name', + type: 'string', + default: '', + }, + { + displayName: 'Last Name', + name: 'last_name', + type: 'string', + default: '', + }, + { + displayName: 'Metadata', + name: 'meta_data', + type: 'fixedCollection', + typeOptions: { + multipleValues: true, + }, + default: {}, + placeholder: 'Add Metadata Field', + options: [ + { + displayName: 'Metadata Fields', + name: 'meta_data_fields', + values: [ + { + displayName: 'Key', + name: 'key', + type: 'string', + default: '', + }, + { + displayName: 'Value', + name: 'value', + type: 'string', + default: '', + }, + ], + }, + ], + }, + { + displayName: 'Password', + name: 'password', + type: 'string', + typeOptions: { password: true }, + displayOptions: { + show: { + '/resource': ['customer'], + '/operation': ['create'], + }, + }, + default: '', + }, + { + displayName: 'Shipping Address', + name: 'shipping', + type: 'collection', + default: {}, + placeholder: 'Add Field', + options: customerAddressOptions, + }, +]; + +const customerCreateOptions: INodeProperties[] = [ + ...customerUpdateOptions, + { + displayName: 'Username', + name: 'username', + type: 'string', + default: '', + }, +]; + +export const customerCreateFields: INodeProperties = { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: ['customer'], + operation: ['create'], + }, + }, + options: customerCreateOptions, +}; + +export const customerUpdateFields: INodeProperties = { + displayName: 'Update Fields', + name: 'updateFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: ['customer'], + operation: ['update'], + }, + }, + options: customerUpdateOptions, +}; diff --git a/n8n-n8n-1.109.2/packages/workflow/src/errors/abstract/execution-base.error.ts b/n8n-n8n-1.109.2/packages/workflow/src/errors/abstract/execution-base.error.ts new file mode 100644 index 00000000..93f86cbd --- /dev/null +++ b/n8n-n8n-1.109.2/packages/workflow/src/errors/abstract/execution-base.error.ts @@ -0,0 +1,52 @@ +import { ApplicationError, type ReportingOptions } from '@n8n/errors'; + +import type { Functionality, IDataObject, JsonObject } from '../../interfaces'; + +interface ExecutionBaseErrorOptions extends ReportingOptions { + cause?: Error; + errorResponse?: JsonObject; +} + +export abstract class ExecutionBaseError extends ApplicationError { + description: string | null | undefined; + + override cause?: Error; + + errorResponse?: JsonObject; + + timestamp: number; + + context: IDataObject = {}; + + lineNumber: number | undefined; + + functionality: Functionality = 'regular'; + + constructor(message: string, options: ExecutionBaseErrorOptions = {}) { + super(message, options); + + this.name = this.constructor.name; + this.timestamp = Date.now(); + + const { cause, errorResponse } = options; + if (cause instanceof ExecutionBaseError) { + this.context = cause.context; + } else if (cause && !(cause instanceof Error)) { + this.cause = cause; + } + + if (errorResponse) this.errorResponse = errorResponse; + } + + toJSON?() { + return { + message: this.message, + lineNumber: this.lineNumber, + timestamp: this.timestamp, + name: this.name, + description: this.description, + context: this.context, + cause: this.cause, + }; + } +} diff --git a/n8n-n8n-1.109.2/packages/workflow/src/errors/abstract/node.error.ts b/n8n-n8n-1.109.2/packages/workflow/src/errors/abstract/node.error.ts new file mode 100644 index 00000000..8d0f9360 --- /dev/null +++ b/n8n-n8n-1.109.2/packages/workflow/src/errors/abstract/node.error.ts @@ -0,0 +1,187 @@ +import { ExecutionBaseError } from './execution-base.error'; +import type { IDataObject, INode, JsonObject } from '../../interfaces'; +import { isTraversableObject, jsonParse } from '../../utils'; + +/** + * Descriptive messages for common errors. + */ +const COMMON_ERRORS: IDataObject = { + // nodeJS errors + ECONNREFUSED: 'The service refused the connection - perhaps it is offline', + ECONNRESET: + 'The connection to the server was closed unexpectedly, perhaps it is offline. You can retry the request immediately or wait and retry later.', + ENOTFOUND: + 'The connection cannot be established, this usually occurs due to an incorrect host (domain) value', + ETIMEDOUT: + "The connection timed out, consider setting the 'Retry on Fail' option in the node settings", + ERRADDRINUSE: + 'The port is already occupied by some other application, if possible change the port or kill the application that is using it', + EADDRNOTAVAIL: 'The address is not available, ensure that you have the right IP address', + ECONNABORTED: 'The connection was aborted, perhaps the server is offline', + EHOSTUNREACH: 'The host is unreachable, perhaps the server is offline', + EAI_AGAIN: 'The DNS server returned an error, perhaps the server is offline', + ENOENT: 'The file or directory does not exist', + EISDIR: 'The file path was expected but the given path is a directory', + ENOTDIR: 'The directory path was expected but the given path is a file', + EACCES: 'Forbidden by access permissions, make sure you have the right permissions', + EEXIST: 'The file or directory already exists', + EPERM: 'Operation not permitted, make sure you have the right permissions', + // other errors + GETADDRINFO: 'The server closed the connection unexpectedly', +}; + +/** + * Base class for specific NodeError-types, with functionality for finding + * a value recursively inside an error object. + */ +export abstract class NodeError extends ExecutionBaseError { + messages: string[] = []; + + constructor( + readonly node: INode, + error: Error | JsonObject, + ) { + const isError = error instanceof Error; + const message = isError ? error.message : ''; + const options = isError ? { cause: error } : { errorResponse: error }; + super(message, options); + + if (error instanceof NodeError) { + this.tags.reWrapped = true; + } + } + + /** + * Finds property through exploration based on potential keys and traversal keys. + * Depth-first approach. + * + * This method iterates over `potentialKeys` and, if the value at the key is a + * truthy value, the type of the value is checked: + * (1) if a string or number, the value is returned as a string; or + * (2) if an array, + * its string or number elements are collected as a long string, + * its object elements are traversed recursively (restart this function + * with each object as a starting point), or + * (3) if it is an object, it traverses the object and nested ones recursively + * based on the `potentialKeys` and returns a string if found. + * + * If nothing found via `potentialKeys` this method iterates over `traversalKeys` and + * if the value at the key is a traversable object, it restarts with the object as the + * new starting point (recursion). + * If nothing found for any of the `traversalKeys`, exploration continues with remaining + * `traversalKeys`. + * + * Otherwise, if all the paths have been exhausted and no value is eligible, `null` is + * returned. + * + */ + protected findProperty( + jsonError: JsonObject, + potentialKeys: string[], + traversalKeys: string[] = [], + ): string | null { + for (const key of potentialKeys) { + let value = jsonError[key]; + if (value) { + if (typeof value === 'string') { + try { + value = jsonParse(value); + } catch (error) { + return value as string; + } + if (typeof value === 'string') return value; + } + if (typeof value === 'number') return value.toString(); + if (Array.isArray(value)) { + const resolvedErrors: string[] = value + + .map((jsonError) => { + if (typeof jsonError === 'string') return jsonError; + if (typeof jsonError === 'number') return jsonError.toString(); + if (isTraversableObject(jsonError)) { + return this.findProperty(jsonError, potentialKeys); + } + return null; + }) + .filter((errorValue): errorValue is string => errorValue !== null); + + if (resolvedErrors.length === 0) { + return null; + } + return resolvedErrors.join(' | '); + } + if (isTraversableObject(value)) { + const property = this.findProperty(value, potentialKeys); + if (property) { + return property; + } + } + } + } + + for (const key of traversalKeys) { + const value = jsonError[key]; + if (isTraversableObject(value)) { + const property = this.findProperty(value, potentialKeys, traversalKeys); + if (property) { + return property; + } + } + } + + return null; + } + + /** + * Preserve the original error message before setting the new one + */ + protected addToMessages(message: string): void { + if (message && !this.messages.includes(message)) { + this.messages.push(message); + } + } + + /** + * Set descriptive error message if code is provided or if message contains any of the common errors, + * update description to include original message plus the description + */ + protected setDescriptiveErrorMessage( + message: string, + messages: string[], + code?: string | null, + messageMapping?: { [key: string]: string }, + ): [string, string[]] { + let newMessage = message; + + if (messageMapping) { + for (const [mapKey, mapMessage] of Object.entries(messageMapping)) { + if ((message || '').toUpperCase().includes(mapKey.toUpperCase())) { + newMessage = mapMessage; + messages.push(message); + break; + } + } + if (newMessage !== message) { + return [newMessage, messages]; + } + } + + // if code is provided and it is in the list of common errors set the message and return early + if (code && typeof code === 'string' && COMMON_ERRORS[code.toUpperCase()]) { + newMessage = COMMON_ERRORS[code] as string; + messages.push(message); + return [newMessage, messages]; + } + + // check if message contains any of the common errors and set the message and description + for (const [errorCode, errorDescriptiveMessage] of Object.entries(COMMON_ERRORS)) { + if ((message || '').toUpperCase().includes(errorCode.toUpperCase())) { + newMessage = errorDescriptiveMessage as string; + messages.push(message); + break; + } + } + + return [newMessage, messages]; + } +} diff --git a/n8n-n8n-1.109.2/packages/workflow/src/errors/base/base.error.ts b/n8n-n8n-1.109.2/packages/workflow/src/errors/base/base.error.ts new file mode 100644 index 00000000..f460688b --- /dev/null +++ b/n8n-n8n-1.109.2/packages/workflow/src/errors/base/base.error.ts @@ -0,0 +1,58 @@ +import type { Event } from '@sentry/node'; +import callsites from 'callsites'; + +import type { ErrorTags, ErrorLevel, ReportingOptions } from '@n8n/errors'; + +export type BaseErrorOptions = { description?: string | undefined | null } & ErrorOptions & + ReportingOptions; +/** + * Base class for all errors + */ +export abstract class BaseError extends Error { + /** + * Error level. Defines which level the error should be logged/reported + * @default 'error' + */ + level: ErrorLevel; + + /** + * Whether the error should be reported to Sentry. + * @default true + */ + readonly shouldReport: boolean; + + readonly description: string | null | undefined; + + readonly tags: ErrorTags; + + readonly extra?: Event['extra']; + + readonly packageName?: string; + + constructor( + message: string, + { + level = 'error', + description, + shouldReport, + tags = {}, + extra, + ...rest + }: BaseErrorOptions = {}, + ) { + super(message, rest); + + this.level = level; + this.shouldReport = shouldReport ?? (level === 'error' || level === 'fatal'); + this.description = description; + this.tags = tags; + this.extra = extra; + + try { + const filePath = callsites()[2].getFileName() ?? ''; + const match = /packages\/([^\/]+)\//.exec(filePath)?.[1]; + + if (match) this.tags.packageName = match; + } catch {} + } +} diff --git a/n8n-n8n-1.109.2/packages/workflow/src/errors/base/operational.error.ts b/n8n-n8n-1.109.2/packages/workflow/src/errors/base/operational.error.ts new file mode 100644 index 00000000..c67848dc --- /dev/null +++ b/n8n-n8n-1.109.2/packages/workflow/src/errors/base/operational.error.ts @@ -0,0 +1,21 @@ +import type { BaseErrorOptions } from './base.error'; +import { BaseError } from './base.error'; + +export type OperationalErrorOptions = Omit & { + level?: 'info' | 'warning' | 'error'; +}; + +/** + * Error that indicates a transient issue, like a network request failing, + * a database query timing out, etc. These are expected to happen, are + * transient by nature and should be handled gracefully. + * + * Default level: warning + */ +export class OperationalError extends BaseError { + constructor(message: string, opts: OperationalErrorOptions = {}) { + opts.level = opts.level ?? 'warning'; + + super(message, opts); + } +} diff --git a/n8n-n8n-1.109.2/packages/workflow/src/errors/base/unexpected.error.ts b/n8n-n8n-1.109.2/packages/workflow/src/errors/base/unexpected.error.ts new file mode 100644 index 00000000..a2045690 --- /dev/null +++ b/n8n-n8n-1.109.2/packages/workflow/src/errors/base/unexpected.error.ts @@ -0,0 +1,21 @@ +import type { BaseErrorOptions } from './base.error'; +import { BaseError } from './base.error'; + +export type UnexpectedErrorOptions = Omit & { + level?: 'error' | 'fatal'; +}; + +/** + * Error that indicates something is wrong in the code: logic mistakes, + * unhandled cases, assertions that fail. These are not recoverable and + * should be brought to developers' attention. + * + * Default level: error + */ +export class UnexpectedError extends BaseError { + constructor(message: string, opts: UnexpectedErrorOptions = {}) { + opts.level = opts.level ?? 'error'; + + super(message, opts); + } +} diff --git a/n8n-n8n-1.109.2/packages/workflow/src/errors/base/user.error.ts b/n8n-n8n-1.109.2/packages/workflow/src/errors/base/user.error.ts new file mode 100644 index 00000000..725f4169 --- /dev/null +++ b/n8n-n8n-1.109.2/packages/workflow/src/errors/base/user.error.ts @@ -0,0 +1,24 @@ +import type { BaseErrorOptions } from './base.error'; +import { BaseError } from './base.error'; + +export type UserErrorOptions = Omit & { + level?: 'info' | 'warning'; + description?: string | null | undefined; +}; + +/** + * Error that indicates the user performed an action that caused an error. + * E.g. provided invalid input, tried to access a resource they’re not + * authorized to, or violates a business rule. + * + * Default level: info + */ +export class UserError extends BaseError { + declare readonly description: string | null | undefined; + + constructor(message: string, opts: UserErrorOptions = {}) { + opts.level = opts.level ?? 'info'; + + super(message, opts); + } +} diff --git a/n8n-n8n-1.109.2/start.bat b/n8n-n8n-1.109.2/start.bat index 90ba46dd..e69de29b 100644 --- a/n8n-n8n-1.109.2/start.bat +++ b/n8n-n8n-1.109.2/start.bat @@ -1,475 +0,0 @@ -@echo off -chcp 65001 >nul 2>&1 -REM n8n 中文版快速启动脚本 (Windows版 - 增强兼容性) -REM 作者: 小齐 -REM 最后更新: 2025-09-08 -REM 兼容性: Windows 7/8/10/11, PowerShell/CMD - -setlocal enabledelayedexpansion - -REM 显示启动横幅 -:show_banner -echo ====================================== -echo n8n 中文版快速启动脚本 -echo 版本: n8n-1.109.2 with 中文翻译 -echo 维护者: xiaoqi -echo ====================================== -echo. - -REM 解析命令行参数 -set "DEV_MODE=" -set "FORCE_BUILD=" -set "CHECK_ONLY=" -set "SKIP_DEPS=" -set "USE_NPM=" - -:parse_args -if "%~1"=="" goto main_execution -if /I "%~1"=="-h" goto show_help -if /I "%~1"=="--help" goto show_help -if /I "%~1"=="-d" ( - set "DEV_MODE=true" - shift - goto parse_args -) -if /I "%~1"=="--dev" ( - set "DEV_MODE=true" - shift - goto parse_args -) -if /I "%~1"=="-b" ( - set "FORCE_BUILD=true" - shift - goto parse_args -) -if /I "%~1"=="--build" ( - set "FORCE_BUILD=true" - shift - goto parse_args -) -if /I "%~1"=="-c" ( - set "CHECK_ONLY=true" - shift - goto parse_args -) -if /I "%~1"=="--check" ( - set "CHECK_ONLY=true" - shift - goto parse_args -) -if /I "%~1"=="--skip-deps" ( - set "SKIP_DEPS=true" - shift - goto parse_args -) -if /I "%~1"=="--npm" ( - set "USE_NPM=true" - shift - goto parse_args -) -echo [错误] 未知选项: %~1 -goto show_help - -:main_execution -REM 检查操作系统版本 -call :check_os_version - -REM 检查并切换到正确目录 -call :check_and_change_directory -if errorlevel 1 goto error_exit - -REM 检查依赖 -if not defined SKIP_DEPS ( - echo [信息] 正在检查系统依赖... - call :check_dependencies - if errorlevel 1 goto error_exit -) - -REM 检查端口占用 -call :check_port -if errorlevel 1 goto error_exit - -REM 停止现有进程 -call :stop_existing - -REM 设置环境变量 -call :setup_environment - -REM 仅检查模式 -if defined CHECK_ONLY ( - echo [成功] 系统状态检查完成,一切正常 - goto end -) - -REM 安装依赖(如果需要) -if not exist "node_modules\" ( - echo [信息] 检测到缺少依赖,正在安装... - if defined USE_NPM ( - call :install_with_npm - ) else ( - call :install_with_pnpm - ) - if errorlevel 1 goto error_exit -) - -REM 强制构建或检查构建状态 -if defined FORCE_BUILD ( - echo [信息] 强制重新构建项目... - call :build_project - if errorlevel 1 goto error_exit -) else ( - call :check_build - if errorlevel 1 goto error_exit -) - -REM 开发模式或正常启动 -if defined DEV_MODE ( - echo [信息] 正在以开发模式启动 n8n... - echo [警告] 开发模式启动时间较长,请耐心等待... - if defined USE_NPM ( - call npm run dev - ) else ( - call pnpm dev - ) -) else ( - call :start_n8n -) -goto end - -REM ========== 函数定义 ========== - -:check_os_version -REM 检查 Windows 版本兼容性 -ver | findstr /i "5\.1\." >nul -if not errorlevel 1 ( - echo [警告] 检测到 Windows XP,可能存在兼容性问题 -) -ver | findstr /i "6\.0\." >nul -if not errorlevel 1 ( - echo [信息] 检测到 Windows Vista -) -ver | findstr /i "6\.1\." >nul -if not errorlevel 1 ( - echo [信息] 检测到 Windows 7 -) -ver | findstr /i "6\.2\." >nul -if not errorlevel 1 ( - echo [信息] 检测到 Windows 8 -) -ver | findstr /i "6\.3\." >nul -if not errorlevel 1 ( - echo [信息] 检测到 Windows 8.1 -) -ver | findstr /i "10\.0\." >nul -if not errorlevel 1 ( - echo [信息] 检测到 Windows 10/11 -) -exit /b 0 - -:check_and_change_directory -REM 检查当前目录是否为 n8n 项目目录 -if exist "package.json" if exist "packages" ( - echo [信息] 检测到当前已在 n8n 项目目录 - exit /b 0 -) - -REM 获取脚本所在目录(兼容不同 Windows 版本) -set "SCRIPT_DIR=%~dp0" -if "%SCRIPT_DIR:~-1%"=="\" set "SCRIPT_DIR=%SCRIPT_DIR:~0,-1%" - -REM 如果脚本在 n8n-n8n-1.109.2 目录内 -echo %SCRIPT_DIR% | findstr /I /C:"n8n-n8n-1.109.2" >nul -if not errorlevel 1 ( - echo [信息] 切换到脚本所在的 n8n 项目目录 - cd /d "%SCRIPT_DIR%" 2>nul - if errorlevel 1 ( - REM 兼容旧版 Windows - cd "%SCRIPT_DIR%" 2>nul - if errorlevel 1 ( - echo [错误] 无法切换到目录: %SCRIPT_DIR% - exit /b 1 - ) - ) - echo [成功] 已切换到正确的 n8n 项目目录 - exit /b 0 -) - -REM 尝试找到 n8n-n8n-1.109.2 目录 -if exist "n8n-n8n-1.109.2\" ( - cd n8n-n8n-1.109.2 -) else if exist "..\n8n-n8n-1.109.2\" ( - cd ..\n8n-n8n-1.109.2 -) else if exist "..\..\n8n-n8n-1.109.2\" ( - cd ..\..\n8n-n8n-1.109.2 -) else ( - echo [错误] 未找到 n8n-n8n-1.109.2 目录 - echo [信息] 请确保在正确的项目目录下运行此脚本 - exit /b 1 -) - -REM 验证目录结构 -if not exist "package.json" ( - echo [错误] 目录结构不正确,缺少 package.json - exit /b 1 -) -if not exist "packages" ( - echo [错误] 目录结构不正确,缺少 packages 目录 - exit /b 1 -) - -echo [成功] 已切换到正确的 n8n 项目目录 -exit /b 0 - -:check_dependencies -REM 检查 Node.js -where node >nul 2>&1 -if errorlevel 1 ( - REM 尝试常见的 Node.js 安装路径 - if exist "C:\Program Files\nodejs\node.exe" ( - set "PATH=C:\Program Files\nodejs;%PATH%" - ) else if exist "C:\Program Files (x86)\nodejs\node.exe" ( - set "PATH=C:\Program Files (x86)\nodejs;%PATH%" - ) else ( - echo [错误] Node.js 未安装或未在 PATH 中 - echo [信息] 请访问 https://nodejs.org 下载安装 - exit /b 1 - ) -) - -echo [信息] Node.js 版本: -node --version - -REM 检查包管理器 -if not defined USE_NPM ( - where pnpm >nul 2>&1 - if errorlevel 1 ( - echo [警告] pnpm 未安装,尝试使用 npm - set "USE_NPM=true" - ) else ( - echo [信息] 使用 pnpm 作为包管理器 - ) -) else ( - echo [信息] 使用 npm 作为包管理器 -) - -echo [成功] 依赖检查通过 -exit /b 0 - -:install_with_pnpm -echo [信息] 使用 pnpm 安装依赖... -where pnpm >nul 2>&1 -if errorlevel 1 ( - echo [信息] 正在安装 pnpm... - call npm install -g pnpm@8 - if errorlevel 1 ( - echo [错误] pnpm 安装失败 - exit /b 1 - ) -) -call pnpm install --no-frozen-lockfile -exit /b %errorlevel% - -:install_with_npm -echo [警告] 使用 npm 安装(可能不兼容 workspace) -echo [信息] 建议安装 pnpm: npm install -g pnpm -call npm install --legacy-peer-deps -exit /b %errorlevel% - -:check_port -REM 检查端口 5678 是否被占用 -netstat -ano 2>nul | findstr ":5678" >nul 2>&1 -if not errorlevel 1 ( - echo [警告] 端口 5678 已被占用 - echo [信息] 正在查看占用进程... - netstat -ano | findstr ":5678" - echo. - - set /p "kill_process=是否终止占用进程?(y/N): " - if /I "!kill_process!"=="y" ( - echo [信息] 正在终止占用进程... - for /f "tokens=5" %%a in ('netstat -ano ^| findstr ":5678"') do ( - taskkill /PID %%a /F >nul 2>&1 - if errorlevel 1 ( - REM Windows 7 兼容性 - tskill %%a >nul 2>&1 - ) - ) - echo [成功] 进程已终止 - timeout /t 2 /nobreak >nul 2>&1 - if errorlevel 1 ( - REM Windows 7 兼容性 - ping -n 3 127.0.0.1 >nul - ) - ) else ( - echo [错误] 无法启动 n8n,端口被占用 - exit /b 1 - ) -) -exit /b 0 - -:stop_existing -echo [信息] 正在停止现有的 n8n 进程... - -REM 停止可能存在的 n8n 相关进程 -taskkill /F /IM node.exe /FI "WINDOWTITLE eq *n8n*" >nul 2>&1 -taskkill /F /IM node.exe /FI "WINDOWTITLE eq *pnpm*" >nul 2>&1 -taskkill /F /IM node.exe /FI "WINDOWTITLE eq *npm*" >nul 2>&1 - -REM 等待 -timeout /t 2 /nobreak >nul 2>&1 -if errorlevel 1 ( - ping -n 3 127.0.0.1 >nul -) - -echo [成功] 已停止现有进程 -exit /b 0 - -:setup_environment -echo [信息] 正在设置环境变量... - -set "N8N_DEFAULT_LOCALE=zh-CN" -set "N8N_SECURE_COOKIE=false" -set "DB_SQLITE_POOL_SIZE=5" -set "N8N_RUNNERS_ENABLED=true" -set "N8N_BLOCK_ENV_ACCESS_IN_NODE=false" -set "NODE_OPTIONS=--max-old-space-size=4096" - -echo [成功] 环境变量已设置 -echo - N8N_DEFAULT_LOCALE=zh-CN -echo - N8N_SECURE_COOKIE=false -echo - DB_SQLITE_POOL_SIZE=5 -echo - NODE_OPTIONS=--max-old-space-size=4096 -exit /b 0 - -:check_build -if not exist "packages\cli\dist\" goto need_build -if not exist "packages\core\dist\" goto need_build -if not exist "packages\workflow\dist\" goto need_build -exit /b 0 - -:need_build -echo [警告] 检测到项目需要构建 -set /p "build_project=是否现在构建项目?这可能需要几分钟时间 (y/N): " -if /I "!build_project!"=="y" ( - call :build_project -) -exit /b 0 - -:build_project -echo [信息] 正在构建项目... -if defined USE_NPM ( - call npm run build > build.log 2>&1 -) else ( - call pnpm build > build.log 2>&1 -) -if errorlevel 1 ( - echo [错误] 构建失败,请查看 build.log - type build.log | more - exit /b 1 -) -echo [成功] 项目构建完成 -exit /b 0 - -:start_n8n -echo [信息] 正在启动 n8n 中文版... - -REM 创建日志文件名(兼容不同日期格式) -set "LOG_FILE=n8n-%date:~0,4%%date:~5,2%%date:~8,2%-%time:~0,2%%time:~3,2%.log" -set "LOG_FILE=%LOG_FILE: =0%" - -echo [信息] 正在启动 n8n 服务... -echo [信息] 日志文件: %LOG_FILE% - -REM 启动 n8n -if defined USE_NPM ( - start "n8n Server" /B cmd /c "npm start > %LOG_FILE% 2>&1" -) else ( - start "n8n Server" /B cmd /c "pnpm start > %LOG_FILE% 2>&1" -) - -REM 等待启动 -echo 等待 n8n 启动 -set "count=0" -:wait_loop -set /a count+=1 -if %count% gtr 60 goto start_failed - -REM 检查端口是否开启 -netstat -an 2>nul | findstr ":5678" | findstr "LISTENING" >nul 2>&1 -if not errorlevel 1 goto start_success - -REM 显示进度 -set /p "=." nul 2>&1 -if errorlevel 1 ( - ping -n 2 127.0.0.1 >nul -) -goto wait_loop - -:start_success -echo. -echo [成功] n8n 启动成功! -echo. -echo ========== 启动信息 ========== -echo 访问地址: http://localhost:5678 -echo 界面语言: 中文 (zh-CN) -echo 日志文件: %LOG_FILE% -echo ============================= -echo. -echo [信息] 使用 Ctrl+C 停止服务 -echo [提示] 首次访问需要设置账户 -echo. -pause -exit /b 0 - -:start_failed -echo. -echo [错误] n8n 启动失败 -echo [信息] 查看日志文件: %LOG_FILE% -if exist "%LOG_FILE%" ( - echo. - echo ===== 错误日志 ===== - type "%LOG_FILE%" | more -) -pause -exit /b 1 - -:show_help -echo 用法: %~nx0 [选项] -echo. -echo 选项: -echo -h, --help 显示此帮助信息 -echo -d, --dev 使用开发模式启动 (支持热重载) -echo -b, --build 强制重新构建项目 -echo -c, --check 仅检查系统状态,不启动 -echo --skip-deps 跳过依赖检查 -echo --npm 使用 npm 替代 pnpm -echo. -echo 示例: -echo %~nx0 # 正常启动 -echo %~nx0 -d # 开发模式启动 -echo %~nx0 -b # 重新构建并启动 -echo %~nx0 --npm # 使用 npm 启动 -echo. -echo 故障排除: -echo 1. 如果 pnpm 安装失败,使用 --npm 选项 -echo 2. 如果端口被占用,脚本会提示终止进程 -echo 3. 构建失败时查看 build.log 文件 -echo. -goto end - -:error_exit -echo [错误] 脚本执行失败 -echo. -echo 常见问题解决: -echo 1. 确保 Node.js 版本为 18.x 或 20.x -echo 2. 尝试使用管理员权限运行 -echo 3. 使用 --npm 选项如果 pnpm 有问题 -echo 4. 检查网络连接和代理设置 -echo. -pause -exit /b 1 - -:end -endlocal -exit /b 0 \ No newline at end of file