Files
gameno-api/components/files/fileController.js
T
kavehhn 1ef54ad414 fix: stream FilePond uploads to S3 and proxy private file previews
Disk-based multer plus an authenticated content endpoint match the working Node.js uploader and avoid browser signed-URL failures on SeaweedFS.
2026-08-15 18:46:26 +03:30

61 lines
2.0 KiB
JavaScript

// /components/files/fileController.js
const catchAsync = require('../../utils/catchAsync');
const fileService = require('./fileService');
const { successResponse } = require('../../utils/apiResponse');
const AppError = require('../../utils/AppError');
const pickUploadedFile = (req) => {
if (req.file) return req.file;
if (Array.isArray(req.files) && req.files.length) return req.files[0];
if (req.files && typeof req.files === 'object') {
return req.files.file?.[0]
|| req.files['filepond-image']?.[0]
|| Object.values(req.files).flat().find(Boolean)
|| null;
}
return null;
};
exports.uploadTemp = catchAsync(async (req, res, next) => {
const uploaded = pickUploadedFile(req);
if (!uploaded) {
return next(new AppError('FILE_REQUIRED'));
}
const result = await fileService.uploadTempFile(uploaded);
// 200 matches the working FilePond Node.js uploader (2xx is required)
return successResponse(res, 200, 'File uploaded to temp bucket successfully', result);
});
exports.getSignedUrl = catchAsync(async (req, res, next) => {
const { filename } = req.params;
const { bucket } = req.query;
const result = await fileService.getPresignedUrl(filename, bucket);
return successResponse(res, 200, 'Temporary presigned URL generated successfully', result);
});
exports.streamFile = catchAsync(async (req, res, next) => {
const key = req.query.key || req.params.filename;
const bucket = req.query.bucket;
if (!key) {
return next(new AppError('VALIDATION_FAILED', null, 'Filename is required'));
}
const { buffer, contentType } = await fileService.getFileContent(key, bucket);
res.set('Content-Type', contentType);
res.set('Cache-Control', 'private, max-age=60');
return res.status(200).send(buffer);
});
exports.deleteFile = catchAsync(async (req, res, next) => {
const { filename } = req.params;
const { bucket } = req.query;
await fileService.deleteFile(filename, bucket);
return successResponse(res, 200, 'File deleted successfully');
});