diff --git a/package-lock.json b/package-lock.json index 58157fcdba06e..eb76f1bcbeb9f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8090,6 +8090,22 @@ "htmlparser2": "^6.0.0" } }, + "node_modules/@types/send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.1.tgz", + "integrity": "sha512-Cwo8LE/0rnvX7kIIa3QHCkcuF21c05Ayb0ZfxPiv0W8VRiZiNW/WuRupHKpqqGVGf7SUA44QSOUKaEd9lIrd/Q==", + "dev": true, + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/send/node_modules/@types/mime": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", + "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==", + "dev": true + }, "node_modules/@types/serve-static": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.0.tgz", @@ -46399,6 +46415,7 @@ "@types/parseurl": "^1.3.1", "@types/passport-jwt": "^3.0.6", "@types/psl": "^1.1.0", + "@types/send": "^0.17.1", "@types/shelljs": "^0.8.11", "@types/superagent": "4.1.13", "@types/supertest": "^2.0.11", @@ -54522,6 +54539,24 @@ "htmlparser2": "^6.0.0" } }, + "@types/send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.1.tgz", + "integrity": "sha512-Cwo8LE/0rnvX7kIIa3QHCkcuF21c05Ayb0ZfxPiv0W8VRiZiNW/WuRupHKpqqGVGf7SUA44QSOUKaEd9lIrd/Q==", + "dev": true, + "requires": { + "@types/mime": "^1", + "@types/node": "*" + }, + "dependencies": { + "@types/mime": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", + "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==", + "dev": true + } + } + }, "@types/serve-static": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.0.tgz", @@ -71503,6 +71538,7 @@ "@types/parseurl": "^1.3.1", "@types/passport-jwt": "^3.0.6", "@types/psl": "^1.1.0", + "@types/send": "^0.17.1", "@types/shelljs": "^0.8.11", "@types/superagent": "4.1.13", "@types/supertest": "^2.0.11", diff --git a/packages/cli/package.json b/packages/cli/package.json index 5a7ca465956d2..b393035cab4cf 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -86,6 +86,7 @@ "@types/parseurl": "^1.3.1", "@types/passport-jwt": "^3.0.6", "@types/psl": "^1.1.0", + "@types/send": "^0.17.1", "@types/shelljs": "^0.8.11", "@types/superagent": "4.1.13", "@types/supertest": "^2.0.11", diff --git a/packages/cli/src/Server.ts b/packages/cli/src/Server.ts index 34e6440cd9bf2..1a36411e4e52c 100644 --- a/packages/cli/src/Server.ts +++ b/packages/cli/src/Server.ts @@ -30,14 +30,15 @@ /* eslint-disable no-await-in-loop */ import { exec as callbackExec } from 'child_process'; -import { promises, readFileSync } from 'fs'; +import { existsSync, readFileSync } from 'fs'; +import { access as fsAccess, readFile, writeFile, mkdir } from 'fs/promises'; import os from 'os'; import { dirname as pathDirname, join as pathJoin, resolve as pathResolve } from 'path'; import { createHmac } from 'crypto'; import { promisify } from 'util'; import cookieParser from 'cookie-parser'; import express from 'express'; -import _ from 'lodash'; +import send from 'send'; import { FindManyOptions, getConnectionManager, In } from 'typeorm'; // eslint-disable-next-line import/no-extraneous-dependencies import axios, { AxiosRequestConfig } from 'axios'; @@ -397,15 +398,12 @@ class App { this.endpointWebhook, this.endpointWebhookTest, this.endpointPresetCredentials, - ]; - if (!config.getEnv('publicApi.disabled')) { - ignoredEndpoints.push(this.publicApiEndpoint); - } - // eslint-disable-next-line prefer-spread - ignoredEndpoints.push.apply(ignoredEndpoints, excludeEndpoints.split(':')); + config.getEnv('publicApi.disabled') ? this.publicApiEndpoint : '', + ...excludeEndpoints.split(':'), + ].filter((u) => !!u); // eslint-disable-next-line no-useless-escape - const authIgnoreRegex = new RegExp(`^\/(${_(ignoredEndpoints).compact().join('|')})\/?.*$`); + const authIgnoreRegex = new RegExp(`^\/(${ignoredEndpoints.join('|')})\/?.*$`); // Check for basic auth credentials if activated const basicAuthActive = config.getEnv('security.basicAuth.active'); @@ -664,18 +662,7 @@ class App { history({ rewrites: [ { - from: new RegExp( - `^/(${[ - 'healthz', - 'metrics', - 'css', - 'js', - this.restEndpoint, - this.endpointWebhook, - this.endpointWebhookTest, - ...(excludeEndpoints.length ? excludeEndpoints.split(':') : []), - ].join('|')})/?.*$`, - ), + from: new RegExp(`^/(${[this.restEndpoint, ...ignoredEndpoints].join('|')})/?.*$`), to: (context) => { return context.parsedUrl.pathname!.toString(); }, @@ -967,7 +954,7 @@ class App { const headersPath = pathJoin(packagesPath, 'nodes-base', 'dist', 'nodes', 'headers'); try { - await promises.access(`${headersPath}.js`); + await fsAccess(`${headersPath}.js`); } catch (_) { return; // no headers available } @@ -1192,12 +1179,12 @@ class App { timezone, ); - const signatureMethod = _.get(oauthCredentials, 'signatureMethod') as string; + const signatureMethod = oauthCredentials.signatureMethod as string; const oAuthOptions: clientOAuth1.Options = { consumer: { - key: _.get(oauthCredentials, 'consumerKey') as string, - secret: _.get(oauthCredentials, 'consumerSecret') as string, + key: oauthCredentials.consumerKey as string, + secret: oauthCredentials.consumerSecret as string, }, signature_method: signatureMethod, // eslint-disable-next-line @typescript-eslint/naming-convention @@ -1220,7 +1207,7 @@ class App { const options: RequestOptions = { method: 'POST', - url: _.get(oauthCredentials, 'requestTokenUrl') as string, + url: oauthCredentials.requestTokenUrl as string, data: oauthRequestData, }; @@ -1237,9 +1224,7 @@ class App { const responseJson = Object.fromEntries(paramsParser.entries()); - const returnUri = `${_.get(oauthCredentials, 'authUrl')}?oauth_token=${ - responseJson.oauth_token - }`; + const returnUri = `${oauthCredentials.authUrl}?oauth_token=${responseJson.oauth_token}`; // Encrypt the data const credentials = new Credentials( @@ -1332,7 +1317,7 @@ class App { const options: AxiosRequestConfig = { method: 'POST', - url: _.get(oauthCredentials, 'accessTokenUrl') as string, + url: oauthCredentials.accessTokenUrl as string, params: { oauth_token, oauth_verifier, @@ -1753,35 +1738,11 @@ class App { if (!config.getEnv('endpoints.disableUi')) { // Read the index file and replace the path placeholder - const editorUiPath = require.resolve('n8n-editor-ui'); - const filePath = pathJoin(pathDirname(editorUiPath), 'dist', 'index.html'); const n8nPath = config.getEnv('path'); const basePathRegEx = /\/{{BASE_PATH}}\//g; - - let readIndexFile = readFileSync(filePath, 'utf8'); - readIndexFile = readIndexFile.replace(basePathRegEx, n8nPath); - readIndexFile = readIndexFile.replace(/\/favicon.ico/g, `${n8nPath}favicon.ico`); - - const cssPath = pathJoin(pathDirname(editorUiPath), 'dist', '**/*.css'); - const cssFiles: Record = {}; - glob.sync(cssPath).forEach((filePath) => { - let readFile = readFileSync(filePath, 'utf8'); - readFile = readFile.replace(basePathRegEx, n8nPath); - cssFiles[filePath.replace(pathJoin(pathDirname(editorUiPath), 'dist'), '')] = readFile; - }); - - const jsPath = pathJoin(pathDirname(editorUiPath), 'dist', '**/*.js'); - const jsFiles: Record = {}; - glob.sync(jsPath).forEach((filePath) => { - let readFile = readFileSync(filePath, 'utf8'); - readFile = readFile.replace(basePathRegEx, n8nPath); - jsFiles[filePath.replace(pathJoin(pathDirname(editorUiPath), 'dist'), '')] = readFile; - }); - const hooksUrls = config.getEnv('externalFrontendHooksUrls'); let scriptsString = ''; - if (hooksUrls) { scriptsString = hooksUrls.split(';').reduce((acc, curr) => { return `${acc}`; @@ -1802,42 +1763,38 @@ class App { scriptsString += phLoadingScript; } + const editorUiDistDir = pathJoin(pathDirname(require.resolve('n8n-editor-ui')), 'dist'); + const generatedStaticDir = pathJoin(__dirname, '../public'); + const firstLinkedScriptSegment = '