mirror of
https://github.com/diced/zipline.git
synced 2026-04-28 10:43:06 -07:00
fix: better error handling for uploading
This commit is contained in:
@@ -59,6 +59,8 @@ export const API_ERRORS = {
|
||||
1058: 'From date must be before to date',
|
||||
1059: 'From date must be in the past',
|
||||
1060: 'Passkey has legacy registration data and cannot be used',
|
||||
1061: 'Invalid multipart/form-data request',
|
||||
1062: 'No files in multipart/form-data request',
|
||||
|
||||
// 2xxx, session errors
|
||||
2000: 'Invalid login session',
|
||||
|
||||
@@ -7,6 +7,9 @@ import { Config } from '../config/validate';
|
||||
import { sanitizeFilename } from '../fs';
|
||||
import { formatFileName } from '../uploader/formatFileName';
|
||||
import { guess } from '../mimes';
|
||||
import { log } from '../logger';
|
||||
|
||||
const logger = log('upload');
|
||||
|
||||
const commonDoubleExts = [
|
||||
'.tar.gz',
|
||||
@@ -79,25 +82,34 @@ export async function getFilename(
|
||||
extension: string,
|
||||
override?: string,
|
||||
): Promise<{ error: string } | { fileName: string }> {
|
||||
let fileName = override ? sanitizeFilename(override) : formatFileName(format, originalName);
|
||||
if (!fileName) return { error: 'invalid file name' };
|
||||
try {
|
||||
let fileName = override ? sanitizeFilename(override) : formatFileName(format, originalName);
|
||||
|
||||
let fullFileName = `${fileName}${extension}`;
|
||||
let existing = await prisma.file.findFirst({ where: { name: fullFileName } });
|
||||
|
||||
if (existing && (override || format === 'name')) {
|
||||
return { error: 'file with the same name already exists' };
|
||||
}
|
||||
|
||||
while (existing && format === 'random') {
|
||||
fileName = formatFileName(format, originalName);
|
||||
if (!fileName) return { error: 'invalid file name' };
|
||||
|
||||
fullFileName = `${fileName}${extension}`;
|
||||
existing = await prisma.file.findFirst({ where: { name: fullFileName } });
|
||||
}
|
||||
let fullFileName = `${fileName}${extension}`;
|
||||
let existing = await prisma.file.findFirst({ where: { name: fullFileName } });
|
||||
|
||||
return { fileName };
|
||||
if (existing && (override || format === 'name')) {
|
||||
return { error: 'file with the same name already exists' };
|
||||
}
|
||||
|
||||
while (existing && format === 'random') {
|
||||
fileName = formatFileName(format, originalName);
|
||||
if (!fileName) return { error: 'invalid file name' };
|
||||
|
||||
fullFileName = `${fileName}${extension}`;
|
||||
existing = await prisma.file.findFirst({ where: { name: fullFileName } });
|
||||
}
|
||||
|
||||
return { fileName };
|
||||
} catch (e) {
|
||||
logger.warn(`error generating file name: ${e}`);
|
||||
|
||||
return {
|
||||
error: e instanceof URIError ? 'invalid file name: make sure it is URL encoded' : 'invalid file name',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export async function getMimetype(
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import ms from 'ms';
|
||||
import { Config } from '../config/validate';
|
||||
import { checkOutput, COMPRESS_TYPES, CompressType } from '../compress';
|
||||
import { config } from '../config';
|
||||
import { sanitizeExtension, sanitizeFilename } from '../fs';
|
||||
import { Config } from '../config/validate';
|
||||
import { sanitizeExtension } from '../fs';
|
||||
|
||||
// from ms@3.0.0-canary.1
|
||||
type Unit =
|
||||
@@ -257,10 +257,9 @@ export function parseHeaders(headers: UploadHeaders, fileConfig: Config['files']
|
||||
|
||||
const filename = headers['x-zipline-filename'];
|
||||
if (filename) {
|
||||
const fn = sanitizeFilename(filename);
|
||||
if (!fn) return headerError('x-zipline-filename', 'Invalid filename');
|
||||
// checks aren't needed here as they are sanitized later in getFilename
|
||||
|
||||
response.overrides.filename = fn;
|
||||
response.overrides.filename = filename;
|
||||
}
|
||||
|
||||
const extension = headers['x-zipline-file-extension'];
|
||||
|
||||
@@ -16,6 +16,7 @@ import { onUpload } from '@/lib/webhooks';
|
||||
import { Prisma } from '@/prisma/client';
|
||||
import { userMiddleware } from '@/server/middleware/user';
|
||||
import typedPlugin from '@/server/typedPlugin';
|
||||
import { SavedMultipartFile } from '@fastify/multipart';
|
||||
import { stat } from 'fs/promises';
|
||||
import { z } from 'zod';
|
||||
|
||||
@@ -99,7 +100,18 @@ export default typedPlugin(
|
||||
if (!req.user && !folder.allowUploads) throw new ApiError(3002);
|
||||
}
|
||||
|
||||
const files = await req.saveRequestFiles({ tmpdir: config.core.tempDirectory });
|
||||
let files: SavedMultipartFile[] = [];
|
||||
try {
|
||||
files = await req.saveRequestFiles({ tmpdir: config.core.tempDirectory });
|
||||
} catch (e) {
|
||||
logger.warn('error parsing multipart/form-data request', {
|
||||
error: e instanceof Error ? e.message : e,
|
||||
});
|
||||
|
||||
if (e instanceof Error && e.message.startsWith('Multipart:')) throw new ApiError(1061);
|
||||
}
|
||||
|
||||
if (!files.length) throw new ApiError(1062);
|
||||
|
||||
const totalFileSize = files.reduce((acc, x) => acc + x.file.bytesRead, 0);
|
||||
const quotaCheck = await checkQuota(req.user, totalFileSize, files.length);
|
||||
|
||||
Reference in New Issue
Block a user