mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-16 17:46:45 +00:00
88 lines
2.9 KiB
TypeScript
88 lines
2.9 KiB
TypeScript
import { BinaryDataQueryDto, BinaryDataSignedQueryDto, ViewableMimeTypes } from '@n8n/api-types';
|
|
import { Get, Query, RestController } from '@n8n/decorators';
|
|
import { Request, Response } from 'express';
|
|
import { JsonWebTokenError } from 'jsonwebtoken';
|
|
import { BinaryDataService, FileNotFoundError, isValidNonDefaultMode } from 'n8n-core';
|
|
|
|
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
|
|
|
|
@RestController('/binary-data')
|
|
export class BinaryDataController {
|
|
constructor(private readonly binaryDataService: BinaryDataService) {}
|
|
|
|
@Get('/')
|
|
async get(
|
|
_: Request,
|
|
res: Response,
|
|
@Query { id: binaryDataId, action, fileName, mimeType }: BinaryDataQueryDto,
|
|
) {
|
|
try {
|
|
this.validateBinaryDataId(binaryDataId);
|
|
await this.setContentHeaders(binaryDataId, action, res, fileName, mimeType);
|
|
return await this.binaryDataService.getAsStream(binaryDataId);
|
|
} catch (error) {
|
|
if (error instanceof FileNotFoundError) return res.status(404).end();
|
|
if (error instanceof BadRequestError) return res.status(400).end(error.message);
|
|
else throw error;
|
|
}
|
|
}
|
|
|
|
@Get('/signed', { skipAuth: true })
|
|
async getSigned(_: Request, res: Response, @Query { token }: BinaryDataSignedQueryDto) {
|
|
try {
|
|
const binaryDataId = this.binaryDataService.validateSignedToken(token);
|
|
this.validateBinaryDataId(binaryDataId);
|
|
await this.setContentHeaders(binaryDataId, 'download', res);
|
|
return await this.binaryDataService.getAsStream(binaryDataId);
|
|
} catch (error) {
|
|
if (error instanceof FileNotFoundError) return res.status(404).end();
|
|
if (error instanceof BadRequestError || error instanceof JsonWebTokenError)
|
|
return res.status(400).end(error.message);
|
|
else throw error;
|
|
}
|
|
}
|
|
|
|
private validateBinaryDataId(binaryDataId: string) {
|
|
if (!binaryDataId) {
|
|
throw new BadRequestError('Missing binary data ID');
|
|
}
|
|
|
|
if (!binaryDataId.includes(':')) {
|
|
throw new BadRequestError('Missing binary data mode');
|
|
}
|
|
|
|
const [mode] = binaryDataId.split(':');
|
|
if (!isValidNonDefaultMode(mode)) {
|
|
throw new BadRequestError('Invalid binary data mode');
|
|
}
|
|
}
|
|
|
|
private async setContentHeaders(
|
|
binaryDataId: string,
|
|
action: 'view' | 'download',
|
|
res: Response,
|
|
fileName?: string,
|
|
mimeType?: string,
|
|
) {
|
|
try {
|
|
const metadata = await this.binaryDataService.getMetadata(binaryDataId);
|
|
fileName = metadata.fileName ?? fileName;
|
|
mimeType = metadata.mimeType ?? mimeType;
|
|
res.setHeader('Content-Length', metadata.fileSize);
|
|
} catch {}
|
|
|
|
if (action === 'view' && (!mimeType || !ViewableMimeTypes.includes(mimeType.toLowerCase()))) {
|
|
throw new BadRequestError('Content not viewable');
|
|
}
|
|
|
|
if (mimeType) {
|
|
res.setHeader('Content-Type', mimeType);
|
|
}
|
|
|
|
if (action === 'download' && fileName) {
|
|
const encodedFilename = encodeURIComponent(fileName);
|
|
res.setHeader('Content-Disposition', `attachment; filename="${encodedFilename}"`);
|
|
}
|
|
}
|
|
}
|