feat: improve upload admin workflow
Add users.is_admin SQLite migration for promoted admins. Support curl PUT request uploads, save uploads into admin file-manager paths, default 25 GiB uploads, tabbed UI, and local dev preview.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import fs from 'node:fs';
|
||||
import { Readable } from 'node:stream';
|
||||
import { Readable, Transform } from 'node:stream';
|
||||
import { pipeline } from 'node:stream/promises';
|
||||
|
||||
import Busboy from 'busboy';
|
||||
|
||||
@@ -108,3 +109,43 @@ export async function streamMultipartToPath(request, options) {
|
||||
Readable.fromWeb(request.body).pipe(parser);
|
||||
});
|
||||
}
|
||||
|
||||
export async function streamBodyToPath(request, options) {
|
||||
const { targetPath, maxBytes = 0 } = options;
|
||||
|
||||
if (!request.body) {
|
||||
throw new Error('missing-body');
|
||||
}
|
||||
|
||||
let sizeBytes = 0;
|
||||
const counter = new Transform({
|
||||
transform(chunk, encoding, callback) {
|
||||
sizeBytes += chunk.length;
|
||||
|
||||
if (maxBytes > 0 && sizeBytes > maxBytes) {
|
||||
callback(new Error('file-too-large'));
|
||||
return;
|
||||
}
|
||||
|
||||
callback(null, chunk);
|
||||
},
|
||||
});
|
||||
|
||||
try {
|
||||
await pipeline(
|
||||
Readable.fromWeb(request.body),
|
||||
counter,
|
||||
fs.createWriteStream(targetPath, { flags: 'wx' })
|
||||
);
|
||||
} catch (error) {
|
||||
await fs.promises.rm(targetPath, { force: true }).catch(() => undefined);
|
||||
throw error;
|
||||
}
|
||||
|
||||
if (sizeBytes <= 0) {
|
||||
await fs.promises.rm(targetPath, { force: true }).catch(() => undefined);
|
||||
throw new Error('missing-file');
|
||||
}
|
||||
|
||||
return { sizeBytes };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user