feat(API): Add route for schema static files (#12770)

This commit is contained in:
Elias Meire
2025-01-28 09:53:04 +01:00
committed by GitHub
parent 1d33b9f4a7
commit d981b5659a
8 changed files with 203 additions and 6 deletions

View File

@@ -211,4 +211,44 @@ describe('LoadNodesAndCredentials', () => {
expect(result.description.displayName).toBe('Special @#$% Node Tool');
});
});
describe('resolveSchema', () => {
let instance: LoadNodesAndCredentials;
beforeEach(() => {
instance = new LoadNodesAndCredentials(mock(), mock(), mock(), mock());
instance.knownNodes['n8n-nodes-base.test'] = {
className: 'Test',
sourcePath: '/nodes-base/dist/nodes/Test/Test.node.js',
};
});
it('should return undefined if the node is not known', () => {
const result = instance.resolveSchema({
node: 'n8n-nodes-base.doesNotExist',
version: '1.0.0',
resource: 'account',
operation: 'get',
});
expect(result).toBeUndefined();
});
it('should return the correct path if the node is known', () => {
const result = instance.resolveSchema({
node: 'n8n-nodes-base.test',
version: '1.0.0',
resource: 'account',
operation: 'get',
});
expect(result).toEqual('/nodes-base/dist/nodes/Test/__schema__/v1.0.0/account/get.json');
});
it('should return the correct path if there is no resource or operation', () => {
const result = instance.resolveSchema({
node: 'n8n-nodes-base.test',
version: '1.0.0',
});
expect(result).toEqual('/nodes-base/dist/nodes/Test/__schema__/v1.0.0.json');
});
});
});

View File

@@ -174,6 +174,29 @@ export class LoadNodesAndCredentials {
return isContainedWithin(loader.directory, filePath) ? filePath : undefined;
}
resolveSchema({
node,
version,
resource,
operation,
}: {
node: string;
version: string;
resource?: string;
operation?: string;
}): string | undefined {
const nodePath = this.known.nodes[node]?.sourcePath;
if (!nodePath) {
return undefined;
}
const nodeParentPath = path.dirname(nodePath);
const schemaPath = ['__schema__', `v${version}`, resource, operation].filter(Boolean).join('/');
const filePath = path.resolve(nodeParentPath, schemaPath + '.json');
return isContainedWithin(nodeParentPath, filePath) ? filePath : undefined;
}
getCustomDirectories(): string[] {
const customDirectories = [this.instanceSettings.customExtensionDir];

View File

@@ -322,8 +322,27 @@ export class Server extends AbstractServer {
res.sendStatus(404);
};
const serveSchemas: express.RequestHandler = async (req, res) => {
const { node, version, resource, operation } = req.params;
const filePath = this.loadNodesAndCredentials.resolveSchema({
node,
resource,
operation,
version,
});
if (filePath) {
try {
await fsAccess(filePath);
return res.sendFile(filePath, cacheOptions);
} catch {}
}
res.sendStatus(404);
};
this.app.use('/icons/@:scope/:packageName/*/*.(svg|png)', serveIcons);
this.app.use('/icons/:packageName/*/*.(svg|png)', serveIcons);
this.app.use('/schemas/:node/:version/:resource?/:operation?.json', serveSchemas);
const isTLSEnabled =
this.globalConfig.protocol === 'https' && !!(this.sslKey && this.sslCert);