fix: proxy file uploads server-side via action=upload, fixes CORS for all roles

This commit is contained in:
Krao Hasanee
2026-06-02 14:19:59 -04:00
parent e11ef45140
commit ae093a9b1c
3 changed files with 54 additions and 37 deletions
+43 -9
View File
@@ -363,9 +363,28 @@ function toListResponse(vPath, entries, { readOnly = false } = {}) {
};
}
export const config = { api: { bodyParser: false, sizeLimit: '50mb' } };
async function readBody(req) {
return new Promise((resolve, reject) => {
const chunks = [];
req.on('data', chunk => chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)));
req.on('end', () => resolve(Buffer.concat(chunks)));
req.on('error', reject);
});
}
export default async function handler(req, res) {
try {
const queryToken = String(req.query?.sb_access_token || req.body?.sb_access_token || '').trim();
const rawBody = await readBody(req);
const body = (() => {
if (!rawBody.length) return {};
const ct = (req.headers['content-type'] || '').split(';')[0].trim();
if (ct === 'application/json') { try { return JSON.parse(rawBody.toString()); } catch { return {}; } }
return {};
})();
const queryToken = String(req.query?.sb_access_token || body?.sb_access_token || '').trim();
const authHeader = req.headers.authorization || (queryToken ? `Bearer ${queryToken}` : '');
if (!authHeader) return json(res, 401, { error: 'No authorization header' });
@@ -382,7 +401,7 @@ export default async function handler(req, res) {
}
const action = req.query.action || (req.method === 'GET' ? 'list' : '');
const requestedPath = req.query.path || req.body?.path || '/';
const requestedPath = req.query.path || body?.path || '/';
let externalProjects = [];
if (auth.profile.role === 'external') {
@@ -467,8 +486,23 @@ export default async function handler(req, res) {
return json(res, 200, { token, url: config.url, fbPath: resolved.fbPath });
}
if (req.method === 'POST' && action === 'upload') {
const resolved = toFbPath();
if (resolved.virtual) return json(res, 400, { error: 'Cannot upload to virtual directory' });
const contentType = (req.headers['content-type'] || 'application/octet-stream').split(';')[0].trim();
const uploadUrl = `${config.url}/api/resources?source=${FB_SOURCE}&path=${encodeURIComponent(resolved.fbPath)}&override=true`;
let token = await getToken(config);
let upRes = await fetch(uploadUrl, { method: 'POST', headers: { Authorization: `Bearer ${token}`, 'Content-Type': contentType }, body: rawBody });
if (upRes.status === 401) {
token = await getToken(config, true);
upRes = await fetch(uploadUrl, { method: 'POST', headers: { Authorization: `Bearer ${token}`, 'Content-Type': contentType }, body: rawBody });
}
if (!upRes.ok) { const t = await upRes.text(); return json(res, upRes.status, { error: t || `Upload failed (${upRes.status})` }); }
return json(res, 200, { success: true });
}
if (req.method === 'POST' && action === 'mkdir') {
const folderName = safeName(req.body?.name, '');
const folderName = safeName(body?.name, '');
if (!folderName) return json(res, 400, { error: 'Folder name required' });
const resolved = toFbPath();
@@ -490,7 +524,7 @@ export default async function handler(req, res) {
}
if (req.method === 'POST' && action === 'rename') {
const newName = safeName(req.body?.name, '');
const newName = safeName(body?.name, '');
if (!newName) return json(res, 400, { error: 'New name required' });
const resolved = toFbPath();
@@ -511,8 +545,8 @@ export default async function handler(req, res) {
if (req.method === 'POST' && action === 'archive-move') {
// Moves srcPath into dstParentPath. If destination already exists, merges contents recursively.
const srcVPath = req.body?.srcPath;
const dstParentVPath = req.body?.dstParentPath;
const srcVPath = body?.srcPath;
const dstParentVPath = body?.dstParentPath;
if (!srcVPath || !dstParentVPath) return json(res, 400, { error: 'srcPath and dstParentPath required' });
const resolvedSrc = toFbPath(srcVPath);
@@ -558,9 +592,9 @@ export default async function handler(req, res) {
}
if (req.method === 'POST' && action === 'move') {
const srcPath = req.body?.srcPath;
const dstPath = req.body?.dstPath;
const mode = req.body?.mode === 'copy' ? 'copy' : 'move';
const srcPath = body?.srcPath;
const dstPath = body?.dstPath;
const mode = body?.mode === 'copy' ? 'copy' : 'move';
if (!srcPath || !dstPath) return json(res, 400, { error: 'srcPath and dstPath required' });
const resolvedSrc = toFbPath(srcPath);