Spaces:
Running
Running
File size: 5,680 Bytes
519a20c |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
import fs from 'node:fs';
import path from 'node:path';
import { Buffer } from 'node:buffer';
import express from 'express';
import sanitize from 'sanitize-filename';
import { clientRelativePath, removeFileExtension, getImages, isPathUnderParent } from '../util.js';
/**
* Ensure the directory for the provided file path exists.
* If not, it will recursively create the directory.
*
* @param {string} filePath - The full path of the file for which the directory should be ensured.
*/
function ensureDirectoryExistence(filePath) {
const dirname = path.dirname(filePath);
if (fs.existsSync(dirname)) {
return true;
}
ensureDirectoryExistence(dirname);
fs.mkdirSync(dirname);
}
export const router = express.Router();
/**
* Endpoint to handle image uploads.
* The image should be provided in the request body in base64 format.
* Optionally, a character name can be provided to save the image in a sub-folder.
*
* @route POST /api/images/upload
* @param {Object} request.body - The request payload.
* @param {string} request.body.image - The base64 encoded image data.
* @param {string} [request.body.ch_name] - Optional character name to determine the sub-directory.
* @returns {Object} response - The response object containing the path where the image was saved.
*/
router.post('/upload', async (request, response) => {
// Check for image data
if (!request.body || !request.body.image) {
return response.status(400).send({ error: 'No image data provided' });
}
try {
// Extracting the base64 data and the image format
const splitParts = request.body.image.split(',');
const format = splitParts[0].split(';')[0].split('/')[1];
const base64Data = splitParts[1];
const validFormat = ['png', 'jpg', 'webp', 'jpeg', 'gif', 'mp4', 'avi', 'mov', 'wmv', 'flv', 'webm', '3gp', 'mkv'].includes(format);
if (!validFormat) {
return response.status(400).send({ error: 'Invalid image format' });
}
// Constructing filename and path
let filename;
if (request.body.filename) {
filename = `${removeFileExtension(request.body.filename)}.${format}`;
} else {
filename = `${Date.now()}.${format}`;
}
// if character is defined, save to a sub folder for that character
let pathToNewFile = path.join(request.user.directories.userImages, sanitize(filename));
if (request.body.ch_name) {
pathToNewFile = path.join(request.user.directories.userImages, sanitize(request.body.ch_name), sanitize(filename));
}
ensureDirectoryExistence(pathToNewFile);
const imageBuffer = Buffer.from(base64Data, 'base64');
await fs.promises.writeFile(pathToNewFile, new Uint8Array(imageBuffer));
response.send({ path: clientRelativePath(request.user.directories.root, pathToNewFile) });
} catch (error) {
console.error(error);
response.status(500).send({ error: 'Failed to save the image' });
}
});
router.post('/list/:folder?', (request, response) => {
try {
if (request.params.folder) {
if (request.body.folder) {
return response.status(400).send({ error: 'Folder specified in both URL and body' });
}
console.warn('Deprecated: Use POST /api/images/list with folder in request body');
request.body.folder = request.params.folder;
}
if (!request.body.folder) {
return response.status(400).send({ error: 'No folder specified' });
}
const directoryPath = path.join(request.user.directories.userImages, sanitize(request.body.folder));
const sort = request.body.sortField || 'date';
const order = request.body.sortOrder || 'asc';
if (!fs.existsSync(directoryPath)) {
fs.mkdirSync(directoryPath, { recursive: true });
}
const images = getImages(directoryPath, sort);
if (order === 'desc') {
images.reverse();
}
return response.send(images);
} catch (error) {
console.error(error);
return response.status(500).send({ error: 'Unable to retrieve files' });
}
});
router.post('/folders', (request, response) => {
try {
const directoryPath = request.user.directories.userImages;
if (!fs.existsSync(directoryPath)) {
fs.mkdirSync(directoryPath, { recursive: true });
}
const folders = fs.readdirSync(directoryPath, { withFileTypes: true })
.filter(dirent => dirent.isDirectory())
.map(dirent => dirent.name);
return response.send(folders);
} catch (error) {
console.error(error);
return response.status(500).send({ error: 'Unable to retrieve folders' });
}
});
router.post('/delete', async (request, response) => {
try {
if (!request.body.path) {
return response.status(400).send('No path specified');
}
const pathToDelete = path.join(request.user.directories.root, request.body.path);
if (!isPathUnderParent(request.user.directories.userImages, pathToDelete)) {
return response.status(400).send('Invalid path');
}
if (!fs.existsSync(pathToDelete)) {
return response.status(404).send('File not found');
}
fs.unlinkSync(pathToDelete);
console.info(`Deleted image: ${request.body.path} from ${request.user.profile.handle}`);
return response.sendStatus(200);
} catch (error) {
console.error(error);
return response.sendStatus(500);
}
});
|