mirror of
https://github.com/n8n-io/n8n.git
synced 2025-01-12 13:27:31 -08:00
fix(core): Handle missing binary metadata in download urls (#5242)
This commit is contained in:
parent
ac460aa841
commit
21579a8a2a
|
@ -95,6 +95,7 @@ import {
|
|||
import { credentialsController } from '@/credentials/credentials.controller';
|
||||
import { oauth2CredentialController } from '@/credentials/oauth2Credential.api';
|
||||
import type {
|
||||
BinaryDataRequest,
|
||||
CurlHelper,
|
||||
ExecutionRequest,
|
||||
NodeListSearchRequest,
|
||||
|
@ -1305,19 +1306,24 @@ class Server extends AbstractServer {
|
|||
// Download binary
|
||||
this.app.get(
|
||||
`/${this.restEndpoint}/data/:path`,
|
||||
async (req: express.Request, res: express.Response): Promise<void> => {
|
||||
async (req: BinaryDataRequest, res: express.Response): Promise<void> => {
|
||||
// TODO UM: check if this needs permission check for UM
|
||||
const identifier = req.params.path;
|
||||
const binaryDataManager = BinaryDataManager.getInstance();
|
||||
const binaryPath = binaryDataManager.getBinaryPath(identifier);
|
||||
const { mimeType, fileName, fileSize } = await binaryDataManager.getBinaryMetadata(
|
||||
identifier,
|
||||
);
|
||||
let { mode, fileName, mimeType } = req.query;
|
||||
if (!fileName || !mimeType) {
|
||||
try {
|
||||
const metadata = await binaryDataManager.getBinaryMetadata(identifier);
|
||||
fileName = metadata.fileName;
|
||||
mimeType = metadata.mimeType;
|
||||
res.setHeader('Content-Length', metadata.fileSize);
|
||||
} catch {}
|
||||
}
|
||||
if (mimeType) res.setHeader('Content-Type', mimeType);
|
||||
if (req.query.mode === 'download' && fileName) {
|
||||
if (mode === 'download') {
|
||||
res.setHeader('Content-Disposition', `attachment; filename="${fileName}"`);
|
||||
}
|
||||
res.setHeader('Content-Length', fileSize);
|
||||
res.sendFile(binaryPath);
|
||||
},
|
||||
);
|
||||
|
|
11
packages/cli/src/requests.d.ts
vendored
11
packages/cli/src/requests.d.ts
vendored
|
@ -350,3 +350,14 @@ export declare namespace CurlHelper {
|
|||
export declare namespace LicenseRequest {
|
||||
type Activate = AuthenticatedRequest<{}, {}, { activationKey: string }, {}>;
|
||||
}
|
||||
|
||||
export type BinaryDataRequest = AuthenticatedRequest<
|
||||
{ path: string },
|
||||
{},
|
||||
{},
|
||||
{
|
||||
mode: 'view' | 'download';
|
||||
fileName?: string;
|
||||
mimeType?: string;
|
||||
}
|
||||
>;
|
||||
|
|
|
@ -237,7 +237,12 @@ export interface IRestApi {
|
|||
deleteExecutions(sendData: IExecutionDeleteFilter): Promise<void>;
|
||||
retryExecution(id: string, loadWorkflow?: boolean): Promise<boolean>;
|
||||
getTimezones(): Promise<IDataObject>;
|
||||
getBinaryUrl(dataPath: string, mode: 'view' | 'download'): string;
|
||||
getBinaryUrl(
|
||||
dataPath: string,
|
||||
mode: 'view' | 'download',
|
||||
fileName?: string,
|
||||
mimeType?: string,
|
||||
): string;
|
||||
getExecutionEvents(id: string): Promise<IAbstractEventMessage[]>;
|
||||
}
|
||||
|
||||
|
|
|
@ -45,18 +45,18 @@ export default mixins(restApi).extend({
|
|||
};
|
||||
},
|
||||
async mounted() {
|
||||
const id = this.binaryData?.id;
|
||||
const isJSONData = this.binaryData.fileType === 'json';
|
||||
const { id, data, fileName, fileType, mimeType } = (this.binaryData || {}) as IBinaryData;
|
||||
const isJSONData = fileType === 'json';
|
||||
|
||||
if (!id) {
|
||||
if (isJSONData) {
|
||||
this.jsonData = jsonParse(atob(this.binaryData.data));
|
||||
this.jsonData = jsonParse(atob(data));
|
||||
} else {
|
||||
this.embedSource = 'data:' + this.binaryData.mimeType + ';base64,' + this.binaryData.data;
|
||||
this.embedSource = 'data:' + mimeType + ';base64,' + data;
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
const binaryUrl = this.restApi().getBinaryUrl(id, 'view');
|
||||
const binaryUrl = this.restApi().getBinaryUrl(id, 'view', fileName, mimeType);
|
||||
if (isJSONData) {
|
||||
this.jsonData = await (await fetch(binaryUrl)).json();
|
||||
} else {
|
||||
|
|
|
@ -1206,7 +1206,7 @@ export default mixins(externalHooks, genericHelpers, nodeHelpers, pinData).exten
|
|||
const { id, data, fileName, fileExtension, mimeType } = this.binaryData[index][key];
|
||||
|
||||
if (id) {
|
||||
const url = this.restApi().getBinaryUrl(id, 'download');
|
||||
const url = this.restApi().getBinaryUrl(id, 'download', fileName, mimeType);
|
||||
saveAs(url, [fileName, fileExtension].join('.'));
|
||||
return;
|
||||
} else {
|
||||
|
|
|
@ -202,8 +202,15 @@ export const restApi = Vue.extend({
|
|||
},
|
||||
|
||||
// Binary data
|
||||
getBinaryUrl: (dataPath, mode): string =>
|
||||
self.rootStore.getRestApiContext.baseUrl + `/data/${dataPath}?mode=${mode}`,
|
||||
getBinaryUrl: (dataPath, mode, fileName, mimeType): string => {
|
||||
let restUrl = self.rootStore.getRestUrl;
|
||||
if (restUrl.startsWith('/')) restUrl = window.location.origin + restUrl;
|
||||
const url = new URL(`${restUrl}/data/${dataPath}`);
|
||||
url.searchParams.append('mode', mode);
|
||||
if (fileName) url.searchParams.append('fileName', fileName);
|
||||
if (mimeType) url.searchParams.append('mimeType', mimeType);
|
||||
return url.toString();
|
||||
},
|
||||
|
||||
// Returns all the available timezones
|
||||
getExecutionEvents: (id: string): Promise<IAbstractEventMessage[]> => {
|
||||
|
|
Loading…
Reference in a new issue