/**
* Files Controller
*
* Contains all the business logic for file operations:
* - File uploads (private, public, images, PDFs, avatars, banners)
* - File downloads with access control
* - File listing and directory operations
* - File deletion and cleanup
* - Language and configuration file management
* - Public file access with expiration
* - Log file access for administrators
*
* Handles various file types with different access levels and processing options.
* Integrates with S3-compatible storage for scalable file management.
*/
// @ts-check
import './../../types.js';
import { busboyUpload } from '../../utils/uploadBusboy.js';
import { uploadLanguage } from '../../utils/uploadLanguage.js';
import { UUID2hex, query } from '@commtool/sql-query';
import { isAdmin, isVisible } from '../../utils/authChecks.js';
import cryptoRandomString from 'crypto-random-string';
import { requestUpdateLogger, errorLoggerRead, errorLoggerUpdate } from '../../utils/requestLogger.js';
import path from 'path';
import fs from 'fs';
import { s3, myMinioClient } from '../../utils/s3Client.js';
const bucket = process.env.bucket ? process.env.bucket : 'kpe20';
/**
* Helper function to filter image file types
*/
const filterImage = (mimeType, extension) => {
return ['image/jpeg', 'image/png', 'image/svg+xml', 'image/gif', 'image/tiff'].includes(mimeType) &&
['jpg', 'tiff', 'png', 'svg', 'gif', 'jpeg'].includes(extension);
};
/**
* Helper function to filter image and PDF file types
*/
const filterImagePdf = (mimeType, extension) => {
return ['image/jpeg', 'image/png', 'image/svg+xml', 'image/gif', 'image/tiff', 'application/pdf'].includes(mimeType) &&
['jpg', 'tiff', 'png', 'svg', 'gif', 'jpeg', 'pdf'].includes(extension);
};
/**
* Helper function to filter JSON file types
*/
const filterJSON = (mimeType, extension) => {
return ['application/json'].includes(mimeType) && ['json'].includes(extension);
};
/**
* Middleware to check file expiration parameters
*/
export const checkExpire = async (req, res, next) => {
try {
if (['7', '30', '100', '365'].includes(req.params.expire)) {
next();
return;
} else if (await isAdmin(req.session)) {
if (req.params.expire === '0' || req.params.expire === 'dashboard') {
next();
return;
} else {
res.json({ success: false, message: 'you have to supply 7,30,100 or 365 for expiring links and 0 for none expiring links as the expire parameter' });
return;
}
}
res.status(401).json({ success: false, message: 'valid expire parameters are 7,30,100 or 365 for none admin users' });
return;
} catch (e) {
errorLoggerUpdate(e);
res.status(500).json({ success: false, message: 'Internal server error' });
}
};
/**
* Helper function to generate key components for file paths
*/
const keyComponents = (key, UID) => {
return `${UID}/${encodeURIComponent(key.replace(`${UID}/`, ''))}`;
};
/**
* URL generator for private file uploads
*/
const privateUrlGen = ({ fields, filename, extension, params }) => {
let prefix = fields.prefix;
const UID = params.UID;
if (prefix.charAt(prefix.length - 1) !== '/') {
prefix += '/';
}
return `${UID}/${prefix}${filename.filename}`;
};
/**
* URL generator for changelog file uploads
*/
const changelogUrlGen = ({ fields, filename, extension, params }) => {
const api = params.api;
return `config/${params.api}/changelog.yaml`;
};
/**
* URL generator for banner file uploads
*/
const bannerUrlGen = ({ fields, filename, extension, params }) => {
const UID = params.UID;
return `${UID}/banner/banner-${Date.now().toString()}.${extension}`;
};
/**
* URL generator for avatar file uploads
*/
const avatarUrlGen = ({ fields, filename, extension, params }) => {
const UID = params.UID;
return `${UID}/avatar/avatar.${extension}`;
};
/**
* URL generator for public file uploads
*/
const publicUrlGen = ({ fields, filename, params }) => {
const urlKey = cryptoRandomString({ length: 16, type: 'alphanumeric' });
const extension = filename.filename.split('.')[filename.filename.split('.').length - 1];
return 'public/' + params.expire + '/' + params.UID + '/' + urlKey + '.' + extension;
};
/**
* URL generator for language file uploads
*/
const languageUrlGen = ({ fields, filename, extension, params }) => {
return `${params.api}/languages/${filename.filename}`;
};
/**
* Helper function to delete directories recursively
*/
const deleteDir = async (prefix, recursive = false) => {
try {
return new Promise((fulfill, reject) => {
const objects = [];
const stream = myMinioClient.extensions.listObjectsV2WithMetadata(bucket, prefix, recursive);
stream.on('data', function (obj) {
objects.push(obj.name);
});
stream.on('end', () => {
if (objects.length > 0) {
myMinioClient.removeObjects(bucket, objects, function (err) {
if (err) {
reject(err);
} else {
fulfill({ success: true, message: 'OK', deleted: objects.length });
}
});
} else {
fulfill({ success: true, message: 'OK', deleted: 0 });
}
});
});
} catch (e) {
errorLoggerUpdate(e);
throw e;
}
};
/**
* Middleware to check file visibility permissions
*/
export const checkFileVisible = async (req, res, next) => {
try {
if (['public', 'languages'].includes(req.params.UID)) {
next();
return;
}
const UID = UUID2hex(req.params.UID);
if (isVisible(UID, UUID2hex(req.session.user))) {
next();
} else {
// Check if we have a type with public file access
const type = await query(`SELECT Type FROM ObjectBase WHERE UID=? AND Type IN(?)`, [UID, ['actionT', 'functionT']]);
if (type.length === 1) {
next();
} else {
res.status(410).json({ success: false, message: 'not accessible for this user' });
}
}
} catch (e) {
errorLoggerRead(e);
res.status(500).json({ success: false, message: 'Internal server error' });
}
};
/**
* Upload language or changelog files (POST /:type(language|changelog)/:api)
*/
export const uploadLanguageOrChangelog = async (req, res) => {
try {
let uploadedFileData;
if (req.params.type === 'language') {
uploadedFileData = await uploadLanguage(req, {
s3,
mimeFilter: filterJSON,
uploadUrlGen: languageUrlGen,
Bucket: bucket
});
} else {
uploadedFileData = await busboyUpload(req, {
s3,
uploadUrlGen: changelogUrlGen,
Bucket: bucket
});
}
const urls = uploadedFileData.map(el => process.env.baseUrl + '/api/kpe20/files/' + keyComponents(el.Key, req.params.UID)) + '/languages';
const fileNames = uploadedFileData.map(el => el.Key.split('/')[el.Key.split('/').length - 1]);
if (uploadedFileData.length > 0) {
res.json({ success: true, message: 'OK', fileNames: fileNames, urls: urls, Data: uploadedFileData });
} else {
res.json({ success: false });
}
} catch (e) {
errorLoggerUpdate(e);
res.status(500).json({ success: false, message: 'Internal server error' });
}
};
/**
* Upload private files (POST /:UID)
*/
export const uploadPrivateFiles = async (req, res) => {
try {
req.query.UID = req.params.UID;
let uploadedFileData;
const uploadConfig = {
s3,
uploadUrlGen: privateUrlGen,
Bucket: bucket,
width: req.query.width ? parseInt(req.query.width) : null,
viewportScale: req.query.scale ? req.query.scale : null
};
try {
if (req.query.metadata) {
uploadConfig.extraMetaData = JSON.parse(String(req.query.metadata));
}
} catch (e) {
res.json({ success: false, message: 'extraMetaData: invalid json' });
return;
}
uploadedFileData = await busboyUpload(req, uploadConfig);
const urls = uploadedFileData.map(el => process.env.baseUrl + '/api/kpe20/files/' + keyComponents(el.Key, req.params.UID));
const fileNames = uploadedFileData.map(el => el.Key.split('/')[el.Key.split('/').length - 1]);
if (uploadedFileData.length > 0) {
res.json({
success: true,
message: 'OK',
files: req.files,
name: uploadedFileData[0].originalname,
url: uploadedFileData[1] ? urls[1] : urls[0],
fileNames: fileNames,
urls: urls,
Data: uploadedFileData
});
} else {
res.json({ success: false });
}
} catch (e) {
errorLoggerUpdate(e);
res.status(500).json({ success: false, message: 'Internal server error' });
}
};
/**
* Upload image and PDF files (POST /imagePdf/:UID)
*/
export const uploadImagePdfFiles = async (req, res) => {
try {
req.query.UID = req.params.UID;
const uploadedFileData = await busboyUpload(req, {
mimeFilter: filterImagePdf,
s3,
uploadUrlGen: privateUrlGen,
Bucket: bucket,
width: req.query.width ? parseInt(req.query.width) : 1024,
viewportScale: req.query.scale ? req.query.scale : '1.0'
});
const urls = uploadedFileData.map(el => process.env.baseUrl + '/api/kpe20/files/' + keyComponents(el.Key, req.params.UID));
const fileNames = uploadedFileData.map(el => el.Key.split('/')[el.Key.split('/').length - 1]);
if (uploadedFileData.length > 0) {
res.json({
success: true,
message: 'OK',
name: uploadedFileData[0].originalname,
url: uploadedFileData[1] ? urls[1] : urls[0],
fileNames: fileNames,
urls: urls,
Data: uploadedFileData
});
} else {
res.json({ success: false });
}
} catch (e) {
errorLoggerUpdate(e);
res.status(500).json({ success: false, message: 'Internal server error' });
}
};
/**
* Upload banner images (POST /banner/:UID)
*/
export const uploadBannerFiles = async (req, res) => {
try {
await deleteDir(`${req.params.UID}/banner/`);
const uploadedFileData = await busboyUpload(req, {
s3,
mimeFilter: filterImage,
uploadUrlGen: bannerUrlGen,
Bucket: bucket,
width: 50
});
const urls = uploadedFileData.map(el => process.env.baseUrl + '/api/kpe20/files/' + keyComponents(el.Key, req.params.UID));
const fileNames = uploadedFileData.map(el => el.Key.split('/')[el.Key.split('/').length - 1]);
if (uploadedFileData.length > 0) {
res.json({
success: true,
message: 'OK',
files: uploadedFileData,
name: uploadedFileData[0].originalname,
fileNames: fileNames,
urls: urls,
Data: uploadedFileData,
prefix: req.params.UID + '/banner'
});
} else {
res.json({ success: false });
}
} catch (e) {
errorLoggerUpdate(e);
res.status(500).json({ success: false, message: 'Internal server error' });
}
};
/**
* Upload avatar images (POST /avatar/:UID)
*/
export const uploadAvatarFiles = async (req, res) => {
try {
const uploadedFileData = await busboyUpload(req, {
s3,
mimeFilter: filterImage,
uploadUrlGen: avatarUrlGen,
Bucket: bucket,
width: 300
});
const urls = uploadedFileData.map(el => process.env.baseUrl + '/api/kpe20/files/' + keyComponents(el.Key, req.params.UID));
const fileNames = uploadedFileData.map(el => el.Key.split('/')[el.Key.split('/').length - 1]);
if (uploadedFileData.length > 0) {
res.json({
success: true,
message: 'OK',
files: uploadedFileData,
name: uploadedFileData[0].originalname,
url: uploadedFileData[1] ? urls[1] : urls[0],
fileNames: fileNames,
urls: urls,
Data: uploadedFileData,
prefix: req.params.UID + '/avatar'
});
} else {
res.json({ success: false });
}
} catch (e) {
errorLoggerUpdate(e);
res.status(500).json({ success: false, message: 'Internal server error' });
}
};
/**
* Upload public rail files for apps (POST /rail/:app/)
*/
export const uploadPublicRailFiles = async (req, res) => {
try {
req.query.UID = req.params.UID;
const uploadedFileData = await busboyUpload(req, {
s3,
uploadUrlGen: ({ fields, filename, extension, params }) => {
return `public/${req.params.app}/${fields.prefix}/${filename.filename}`;
},
Bucket: bucket,
original: false,
width: req.query.width ? parseInt(req.query.width) : 400,
viewportScale: req.query.scale ? req.query.scale : '1.0'
});
const urls = uploadedFileData.filter(el => el.Key).map(el => process.env.baseUrl + '/api/' + el.Key);
const fileNames = uploadedFileData.map(el => {
const parts = el.Key.split('/');
return parts[parts.length - 1];
});
if (uploadedFileData.length > 0) {
res.json({
success: true,
files: uploadedFileData,
name: uploadedFileData[0].originalname.filename,
url: uploadedFileData[1] ? urls[1] : urls[0],
fileNames: fileNames,
urls: urls,
Data: uploadedFileData
});
} else {
res.json({ success: false });
}
} catch (e) {
errorLoggerUpdate(e);
res.status(500).json({ success: false, message: 'Internal server error' });
}
};
/**
* Upload private rail files (POST /rail/:UID)
*/
export const uploadPrivateRailFiles = async (req, res) => {
try {
req.query.UID = req.params.UID;
const uploadedFileData = await busboyUpload(req, {
s3,
uploadUrlGen: ({ fields, filename, extension, params }) => {
return `${req.params.UID}/${fields.prefix}/${filename.filename}`;
},
Bucket: bucket,
original: false,
width: req.query.width ? parseInt(req.query.width) : 400,
viewportScale: req.query.scale ? req.query.scale : '1.0'
});
const urls = uploadedFileData.filter(el => el.Key).map(el => process.env.baseUrl + '/api/' + el.Key);
const fileNames = uploadedFileData.map(el => {
const parts = el.Key.split('/');
return parts[parts.length - 1];
});
if (uploadedFileData.length > 0) {
res.json({
success: true,
files: uploadedFileData,
name: uploadedFileData[0].originalname.filename,
url: uploadedFileData[1] ? urls[1] : urls[0],
fileNames: fileNames,
urls: urls,
Data: uploadedFileData
});
} else {
res.json({ success: false });
}
} catch (e) {
errorLoggerUpdate(e);
res.status(500).json({ success: false, message: 'Internal server error' });
}
};
/**
* Upload public files with expiration (POST /public/:expire/:UID)
*/
export const uploadPublicFiles = async (req, res) => {
try {
req.query.UID = req.params.UID;
const uploadedFileData = await busboyUpload(req, {
s3,
mimeFilter: filterImagePdf,
uploadUrlGen: publicUrlGen,
Bucket: bucket,
width: req.query.width ? parseInt(req.query.width) : 2048,
viewportScale: req.query.scale ? req.query.scale : '1.0'
});
const urls = uploadedFileData.filter(el => el.Key).map(el => process.env.baseUrl + '/api/' + el.Key);
const fileNames = uploadedFileData.map(el => {
const parts = el.Key.split('/');
return parts[parts.length - 1];
});
if (uploadedFileData.length > 0) {
res.json({
success: true,
files: uploadedFileData,
name: uploadedFileData[0].originalname.filename,
url: uploadedFileData[1] ? urls[1] : urls[0],
fileNames: fileNames,
urls: urls,
Data: uploadedFileData
});
} else {
res.json({ success: false });
}
} catch (e) {
errorLoggerUpdate(e);
res.status(500).json({ success: false, message: 'Internal server error' });
}
};
/**
* Upload editor files (POST /editor/:expire/:UID)
*/
export const uploadEditorFiles = async (req, res) => {
try {
req.query.UID = req.params.UID;
const uploadedFileData = await busboyUpload(req, {
s3,
uploadUrlGen: publicUrlGen,
Bucket: bucket,
width: 2048
});
const urls = uploadedFileData.map(el => process.env.baseUrl + '/api/' + el.Key);
const fileNames = uploadedFileData.map(el => el.Key.split('/')[el.Key.split('/').length - 1]);
if (uploadedFileData.length > 0) {
res.json({
success: true,
name: uploadedFileData[0].originalname,
message: 'OK',
url: uploadedFileData[1] ? urls[1] : urls[0],
fileNames: fileNames,
urls: urls,
Data: uploadedFileData
});
} else {
res.json({ success: false });
}
} catch (e) {
errorLoggerUpdate(e);
res.status(500).json({ success: false, message: 'Internal server error' });
}
};
/**
* Get log files (GET /:api/logs)
*/
export const getLogFiles = async (req, res) => {
try {
const logFilePath = path.join(process.cwd(), `/logs/${req.params.api}/requests.log`);
fs.readFile(logFilePath, 'utf8', (err, data) => {
if (err) {
return res.status(500).send('Error reading log file');
}
res.setHeader('Content-Type', 'text/plain');
res.send(data);
});
} catch (e) {
errorLoggerUpdate(e);
res.status(500).json({ success: false, message: 'Internal server error' });
}
};
/**
* Get public language/config files (GET /:api/prefix1:(languages|config)/:filename/)
*/
export const getPublicLanguageFiles = async (req, res) => {
try {
myMinioClient.getObject(bucket, req.params.api + '/' + req.params.prefix1 + '/' + req.params.filename,
function (err, data) {
if (err) {
console.error(`S3 error for ${req.params.api}/${req.params.prefix1}/${req.params.filename}:`, err.code);
return res.status(410).json({ success: false, message: 'not available', code: err.code });
} else {
try {
const contentType = data.headers['x-amz-meta-mimetype'] || 'application/octet-stream';
res.set('Content-Type', contentType);
data.on('error', (streamErr) => {
console.error('Stream error:', streamErr);
if (!res.headersSent) {
res.status(500).json({ success: false, message: 'Stream error' });
}
});
data.pipe(res);
} catch (streamSetupErr) {
console.error('Error setting up stream:', streamSetupErr);
if (!res.headersSent) {
res.status(500).json({ success: false, message: 'Error preparing file' });
}
}
}
});
} catch (e) {
errorLoggerRead(e);
if (!res.headersSent) {
res.status(500).json({ success: false, message: 'Internal server error' });
}
}
};
/**
* Get public rail files (GET /rail/:app/:side/:filename)
*/
export const getPublicRailFiles = async (req, res) => {
try {
myMinioClient.getObject(bucket, 'public/' + req.params.app + '/' + req.params.side + '/' + decodeURIComponent(req.params.filename),
function (err, data) {
if (err) {
console.error(`S3 error for public/${req.params.app}/${req.params.side}/${req.params.filename}:`, err.code);
return res.status(410).json({ success: false, message: 'not available', code: err.code });
} else {
try {
const mime = data.headers['content-type'] ? data.headers['content-type'] : data.headers['x-amz-meta-mimetype'];
res.set('Content-Type', mime);
const transformHtml = (mime && mime === 'text/html' || req.params.filename.match(/.*html?$/i)) && req.query.replaceRoot;
data.on('error', function (err) {
console.error('Stream error:', err);
if (!res.headersSent) {
res.status(500).json({ success: false, message: 'Stream error' });
}
});
data.on('data', function (chunk) {
if (transformHtml) {
const text = chunk.toString('utf8');
const replacedText = text.replace(/\%root\%/ig, `${process.env.baseUrl}/api/kpe20/files/rail/${req.params.app}/${req.params.side}`);
res.write(Buffer.from(replacedText));
} else {
res.write(chunk);
}
});
data.on('end', function () {
res.end();
});
} catch (streamErr) {
console.error('Error setting up stream:', streamErr);
if (!res.headersSent) {
res.status(500).json({ success: false, message: 'Error preparing file' });
}
}
}
});
} catch (e) {
errorLoggerRead(e);
if (!res.headersSent) {
res.status(500).json({ success: false, message: 'Server error' });
}
}
};
/**
* Get private rail files (GET /rail/:UID/:side/:filename)
*/
export const getPrivateRailFiles = async (req, res) => {
try {
myMinioClient.getObject(bucket, req.params.UID + '/' + req.params.side + '/' + decodeURIComponent(req.params.filename),
function (err, data) {
if (err) {
console.error(`S3 error for ${req.params.UID}/${req.params.side}/${req.params.filename}:`, err.code);
return res.status(410).json({ success: false, message: 'not available', code: err.code });
} else {
try {
const contentType = data.headers['content-type'] ? data.headers['content-type'] : data.headers['x-amz-meta-mimetype'];
res.set('Content-Type', contentType);
const transformHtml = contentType && contentType === 'text/html' && req.query.replaceRoot;
data.on('error', function (err) {
console.error('Stream error:', err);
if (!res.headersSent) {
res.status(500).json({ success: false, message: 'Stream error' });
}
});
data.on('data', function (chunk) {
if (transformHtml) {
const text = chunk.toString('utf8');
const replacedText = text.replace('%root%', `https://kpe20.${process.env.API_BASE}/api/kpe20/files/rail/${req.params.UID}/${req.params.side}`);
res.write(Buffer.from(replacedText));
} else {
res.write(chunk);
}
});
data.on('end', function () {
res.end();
});
} catch (streamErr) {
console.error('Error setting up stream:', streamErr);
if (!res.headersSent) {
res.status(500).json({ success: false, message: 'Error preparing file' });
}
}
}
});
} catch (e) {
errorLoggerRead(e);
if (!res.headersSent) {
res.status(500).json({ success: false, message: 'Server error' });
}
}
};
/**
* Get files with prefix (GET /:UID/:prefix/:filename)
*/
export const getFileWithPrefix = async (req, res) => {
try {
const name = req.params.UID + '/' + decodeURIComponent(req.params.prefix) + '/' + req.params.filename;
myMinioClient.getObject(bucket, name,
function (err, data) {
if (err) {
res.status(410).send({ success: false, message: 'not available' });
} else {
res.set('Content-Type', data.headers['content-type'] ? data.headers['content-type'] : data.headers['x-amz-meta-mimetype']);
data.pipe(res);
}
});
} catch (e) {
errorLoggerRead(e);
res.status(500).json({ success: false, message: 'Internal server error' });
}
};
/**
* Get single file (GET /:UID/:filename)
*/
export const getSingleFile = async (req, res) => {
try {
myMinioClient.getObject(bucket, req.params.UID + '/' + decodeURIComponent(req.params.filename),
function (err, data) {
if (err) {
console.error(`S3 error for ${req.params.UID}/${req.params.filename}:`, err.code);
return res.status(410).json({ success: false, message: 'not available', code: err.code });
} else {
try {
const contentType = data.headers['content-type'] ? data.headers['content-type'] : data.headers['x-amz-meta-mimetype'];
res.set('Content-Type', contentType);
data.on('error', (streamErr) => {
console.error('Stream error:', streamErr);
if (!res.headersSent) {
res.status(500).json({ success: false, message: 'Stream error' });
}
});
data.pipe(res);
} catch (streamErr) {
console.error('Error setting up stream:', streamErr);
if (!res.headersSent) {
res.status(500).json({ success: false, message: 'Error preparing file' });
}
}
}
});
} catch (e) {
errorLoggerRead(e);
if (!res.headersSent) {
res.status(500).json({ success: false, message: 'Server error' });
}
}
};
/**
* Get public files with expiration (GET /public/:expire/:UID/:filename)
*/
export const getPublicFiles = async (req, res) => {
try {
myMinioClient.getObject(bucket, 'public/' + req.params.expire + '/' + req.params.UID + '/' + req.params.filename,
function (err, data) {
if (err) {
console.error(`S3 error for public/${req.params.expire}/${req.params.UID}/${req.params.filename}:`, err.code);
return res.status(410).json({ success: false, message: 'not available', code: err.code });
} else {
try {
const mime = data.headers['content-type'] ? data.headers['content-type'] : data.headers['x-amz-meta-mimetype'];
res.set('Content-Type', mime);
if (mime && mime.match(/^image\//)) {
res.set(`Content-Disposition: inline; filename="${data.headers.filename}"`);
} else {
res.set(`Content-Disposition: attachment; filename="${data.headers.filename}"`);
}
data.on('error', (streamErr) => {
console.error('Stream error:', streamErr);
if (!res.headersSent) {
res.status(500).json({ success: false, message: 'Stream error' });
}
});
data.pipe(res);
} catch (streamErr) {
console.error('Error setting up stream:', streamErr);
if (!res.headersSent) {
res.status(500).json({ success: false, message: 'Error preparing file' });
}
}
}
});
} catch (e) {
errorLoggerRead(e);
if (!res.headersSent) {
res.status(500).json({ success: false, message: 'Server error' });
}
}
};
/**
* List files in directory (GET /:UID)
*/
export const listFiles = async (req, res) => {
try {
const UID = req.params.UID;
if (['public'].includes(UID)) {
res.status(401).json({ success: false, message: 'only admins are authorized to list the content of the public folder' });
return;
}
const prefix = UID + (req.query.prefix !== undefined ? '/' + req.query.prefix : '');
const recursive = req.query.recursive ? req.query.recursive === 'true' : true;
const stream = myMinioClient.extensions.listObjectsV2WithMetadata(bucket, prefix, recursive);
const objectList = [];
stream.on('data', function (obj) {
const filePath = obj.name.replace(req.params.UID + '/', '');
obj.url = process.env.baseUrl + '/api/kpe20/files/' + req.params.UID + '/' + encodeURIComponent(filePath);
objectList.push(obj);
});
stream.on('end', () => {
res.json({ success: true, files: objectList });
});
} catch (e) {
errorLoggerRead(e);
res.status(500).json({ success: false, message: 'Internal server error' });
}
};
/**
* Delete public files (DELETE /public/:expire/:prefix/:filename)
*/
export const deletePublicFiles = async (req, res) => {
try {
myMinioClient.removeObject(bucket, ['public/' + req.params.expire + '/' + req.params.prefix + '/' + req.params.filename], function (err) {
if (err) {
res.json({ success: false, message: 'Unable to remove object', error: err });
return;
}
res.json({ success: true, message: 'OK' });
});
} catch (e) {
errorLoggerRead(e);
res.status(500).json({ success: false, message: 'Internal server error' });
}
};
/**
* Delete public rail files (DELETE /rail/:app/:prefix/:filename)
*/
export const deletePublicRailFiles = async (req, res) => {
try {
myMinioClient.removeObjects(bucket, [`public/${req.params.app}/${req.params.prefix}/${decodeURIComponent(req.params.filename)}`], function (err) {
if (err) {
return res.json({ message: 'Unable to remove objects', error: err });
}
res.json({ success: true, message: 'OK' });
});
} catch (e) {
errorLoggerUpdate(e);
res.status(500).json({ success: false, message: 'Internal server error' });
}
};
/**
* Delete private rail files (DELETE /rail/:UID/:prefix/:filename)
*/
export const deletePrivateRailFiles = async (req, res) => {
try {
myMinioClient.removeObjects(bucket, [`${req.params.UID}${req.params.prefix}/${decodeURIComponent(req.params.filename)}`], function (err) {
if (err) {
console.error('Unable to remove objects', err);
return res.json({ message: 'Unable to remove objects', error: err });
}
res.json({ success: true, message: 'OK' });
});
} catch (e) {
errorLoggerUpdate(e);
res.status(500).json({ success: false, message: 'Internal server error' });
}
};
/**
* Delete multiple files (DELETE /:UID)
*/
export const deleteMultipleFiles = async (req, res) => {
try {
myMinioClient.removeObjects(bucket, req.body.files.map(f => (req.params.UID + '/' + f)), function (err) {
if (err) {
return res.json({ message: 'Unable to remove objects', error: err });
}
res.json({ message: 'OK' });
});
} catch (e) {
errorLoggerUpdate(e);
res.status(500).json({ success: false, message: 'Internal server error' });
}
};
/**
* Delete directory (DELETE /dir/:UID/:prefix)
*/
export const deleteDirectory = async (req, res) => {
try {
const result = await deleteDir(`${req.params.UID}/${req.params.prefix}`, true);
res.json(result);
} catch (e) {
errorLoggerUpdate(e);
res.status(500).json({ success: false, message: 'Internal server error' });
}
};