fixed some more bugs
This commit is contained in:
+109
-33
@@ -1,10 +1,10 @@
|
||||
import express from 'express';
|
||||
import * as multer from 'multer';
|
||||
import fs from 'fs/promises';
|
||||
import fsSync from 'fs';
|
||||
import { randomUUID } from 'crypto';
|
||||
import { createClient } from 'webdav';
|
||||
import { pathKey, fanoutPath, ensureDir, getRow, upsertRow, CACHE_DIR } from './cache.ts';
|
||||
import { checkIfHasPerms, statOne, toRegExp } from './helpers.ts';
|
||||
import { checkIfHasPerms, getDirectoryFileCount, statOne, toRegExp } from './helpers.ts';
|
||||
import path from 'path';
|
||||
|
||||
type statRetType = {
|
||||
@@ -17,14 +17,26 @@ type statRetType = {
|
||||
"mime"?: string
|
||||
};
|
||||
|
||||
type ReqFileType = {
|
||||
filename: string; // Name of the form field associated with this file.
|
||||
originalname?: string; // Name of the file on the uploader's computer.
|
||||
mimetype: string; // Value of the Content-Type header for this file.
|
||||
size?: number; // Size of the file in bytes.
|
||||
buffer: Buffer; // A Buffer containing the file content.
|
||||
localFilename: string; // the temporary ID assigned to the file
|
||||
};
|
||||
|
||||
declare global {
|
||||
namespace Express {
|
||||
interface Request {
|
||||
fobj?: {
|
||||
fpath: any,
|
||||
deep?: boolean,
|
||||
bypasscache?: boolean
|
||||
}
|
||||
bypasscache?: boolean,
|
||||
startInd?: number,
|
||||
limit?: number
|
||||
},
|
||||
file: ReqFileType
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -54,28 +66,25 @@ app.use((req, res, next) => {
|
||||
return res.sendFile('openapi.json', { root: '.' });
|
||||
}
|
||||
|
||||
// GET has no body (independant handlers)
|
||||
else if (req.method === 'GET') return next();
|
||||
|
||||
// GET/PUT has independant handlers
|
||||
else if (['GET', 'PUT'].includes(req.method)) return next();
|
||||
const pth = checkIfHasPerms(req);
|
||||
|
||||
if (!pth && req.body?.fpath) {
|
||||
return res.status(401).send(`Not allowed to access "${req.body.fpath}"`);
|
||||
}
|
||||
else if (!pth) {
|
||||
return res.status(404).send("Unknown path");
|
||||
}
|
||||
else if (typeof pth === 'string') {
|
||||
console.warn(`somehow got string out of checkIfHasPerms:\npth: ${pth}\nreq.body: ${req.body}`);
|
||||
return res.sendStatus(400);
|
||||
}
|
||||
|
||||
req.fobj = pth;
|
||||
next();
|
||||
});
|
||||
|
||||
//@ts-ignore stupid
|
||||
const multerInstance = multer.default({
|
||||
storage: multer.diskStorage({
|
||||
destination: CACHE_DIR
|
||||
})
|
||||
});
|
||||
|
||||
export const DIRS: string[] = (() => {
|
||||
try {
|
||||
if (!_DIRS) return null;
|
||||
@@ -91,7 +100,7 @@ if (!DIRS) {
|
||||
|
||||
const client = createClient(process.env.NEXTCLOUD_WEBDAV_ADDR!, {
|
||||
username: process.env.NEXTCLOUD_APP_ID!,
|
||||
password: process.env.NEXTCLOUD_APP_PASS!,
|
||||
password: process.env.NEXTCLOUD_APP_PASS!
|
||||
});
|
||||
|
||||
app.post('/file', async (req, res) => {
|
||||
@@ -103,7 +112,8 @@ app.post('/file', async (req, res) => {
|
||||
const { etag, size, mime } = await statOne(fpath, client).catch((err) => {
|
||||
if (typeof err === 'string') {
|
||||
console.error(err);
|
||||
res.sendStatus(404).send(err);
|
||||
|
||||
if (!res.headersSent) res.sendStatus(404).send(err);
|
||||
return { etag: undefined, size: -1, mime: -1 };
|
||||
}
|
||||
|
||||
@@ -145,55 +155,121 @@ app.post('/file', async (req, res) => {
|
||||
res.send(buf);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
res.sendStatus(500);
|
||||
if (!res.headersSent) res.sendStatus(500);
|
||||
}
|
||||
});
|
||||
|
||||
app.post('/dir', async (req, res) => {
|
||||
try {
|
||||
if (!req.fobj) return res.sendStatus(401);
|
||||
const { fpath, deep } = req.fobj,
|
||||
{ startInd = 0, limit = 250 } = req.body;
|
||||
|
||||
const { fpath, deep } = req.fobj;
|
||||
if (!fpath) return res.sendStatus(400);
|
||||
else if (!(await client.exists(fpath)) {
|
||||
return res.status(404).send("Directory not found");
|
||||
}
|
||||
if (!fpath) return res.sendStatus(400);
|
||||
|
||||
const filesInDir = await client.getDirectoryContents(fpath, {
|
||||
deep
|
||||
});
|
||||
if (!(await client.exists(fpath))) {
|
||||
return res.status(404).send("Directory not found");
|
||||
}
|
||||
|
||||
res.json(filesInDir);
|
||||
const getFilesInDir = async () => {
|
||||
for (let i = 0; i < 3; i++) {
|
||||
try {
|
||||
return await client.getDirectoryContents(fpath, {
|
||||
deep
|
||||
});
|
||||
}
|
||||
catch (err: any) {
|
||||
if (err.response?.status === 500) {
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
continue;
|
||||
}
|
||||
|
||||
console.error(`error for "${fpath}":`);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
const counts = await getDirectoryFileCount('/Photos', client);
|
||||
|
||||
let err = "failed to find dir contents (perhaps the server returned 500 too many times?)";
|
||||
err += `\n"${fpath}" contains the following, consider listing directories individually (instead of using 'deep' if too many are present: ${JSON.stringify(counts)}`;
|
||||
|
||||
res.status(500).send(err);
|
||||
}
|
||||
|
||||
const ret = await getFilesInDir();
|
||||
if (!ret) return;
|
||||
|
||||
const filesInDir = Array.isArray(ret) ? ret : ret.data;
|
||||
res.json(filesInDir.slice(startInd, limit));
|
||||
}
|
||||
catch (err) {
|
||||
console.error(err);
|
||||
res.sendStatus(500);
|
||||
res.sendStatus(500);
|
||||
}
|
||||
});
|
||||
|
||||
app.put('/file', multerInstance.single('file'), async (req, res) => {
|
||||
if (!req.fobj) return res.sendStatus(401);
|
||||
if (!req.file) return res.status(400).send("missing file");
|
||||
// openwebui doesn't really help with `multerInstance.single('file'),`
|
||||
const ensureMockFile = async (req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||
try {
|
||||
const { file, fdir } = req.body as { fdir: any, file: { filename: any, buffer: any, mimetype: any } };
|
||||
if (!file) return res.status(400).send("missing file");
|
||||
if (typeof fdir !== 'string') {
|
||||
return res.status(400).send("Missing fdir in request");
|
||||
}
|
||||
if (typeof file.filename !== 'string') {
|
||||
return res.status(400).send("Invalid filename");
|
||||
}
|
||||
if (typeof file.buffer === 'undefined') {
|
||||
return res.status(400).send("Invalid file upload payload");
|
||||
}
|
||||
|
||||
if (!checkIfHasPerms(fdir)) return res.sendStatus(401);
|
||||
|
||||
const fname = `${randomUUID().replaceAll('-', '')}_${file.filename}`,
|
||||
fpath = path.join(CACHE_DIR, fname);
|
||||
|
||||
await fs.writeFile(fpath, file.buffer).catch((err) => {
|
||||
console.error(err);
|
||||
res.sendStatus(500);
|
||||
});
|
||||
|
||||
if (res.headersSent) return;
|
||||
req.file = { ...file, localFilename: fname, mimetype: req.body.mimetype || '' };
|
||||
|
||||
next();
|
||||
}
|
||||
catch (err) {
|
||||
console.error(err);
|
||||
if (!res.headersSent) res.sendStatus(500);
|
||||
}
|
||||
};
|
||||
|
||||
app.put('/file', ensureMockFile, async (req, res) => {
|
||||
const file = req.file;
|
||||
if (!file) return;
|
||||
|
||||
const { fdir, createnewdirs } = req.body as {
|
||||
fdir: string,
|
||||
createnewdirs: boolean
|
||||
};
|
||||
|
||||
if (!fdir) return (res.headersSent || res.status(400).send("missing fdir in body"));
|
||||
|
||||
if (createnewdirs && !(await client.exists(fdir))) {
|
||||
await client.createDirectory(fdir, {
|
||||
recursive: true
|
||||
});
|
||||
}
|
||||
|
||||
const fname = req.file.filename || req.file.originalname,
|
||||
fpath = path.join(fdir, fname);
|
||||
const fname = file.filename || file.originalname,
|
||||
fpath = path.join(fdir, fname!);
|
||||
|
||||
if (await client.exists(fpath)) {
|
||||
return res.status(503).send('file already exists, you do not have permissions to overwrite files');
|
||||
}
|
||||
|
||||
const sstr = fsSync.createReadStream(req.file.path).pipe(client.createWriteStream(fpath, {
|
||||
const sstr = fsSync.createReadStream(path.join(CACHE_DIR, file.localFilename)).pipe(client.createWriteStream(fpath, {
|
||||
overwrite: false
|
||||
}, (r) => {
|
||||
if (res.headersSent) return;
|
||||
|
||||
Reference in New Issue
Block a user