assets: go 15GB limit

Signed-off-by: Kenneth Obsequio <k80308392@gmail.com>
This commit is contained in:
2026-08-01 12:18:23 +08:00
parent 2bff5c030a
commit 08dd687912
3 changed files with 33 additions and 85 deletions
+30 -33
View File
@@ -1,7 +1,6 @@
// controllers/admin/assets.controller.js
const path = require("path");
const crypto = require("crypto");
const sequelize = require("../../config/db.config");
const Asset = require("../../models/assets/assets.mdl");
const chibi = require("../../services/chibisafe.service");
@@ -57,10 +56,6 @@ function resolveExtension(originalName = "") {
return path.extname(originalName).replace(".", "").toLowerCase() || null;
}
function resolveChecksum(buffer) {
return crypto.createHash("sha256").update(buffer).digest("hex");
}
function streamToBuffer(stream) {
return new Promise((resolve, reject) => {
const chunks = [];
@@ -112,32 +107,33 @@ async function rollbackUploads(uploads = []) {
}
}
// ─── uploadToProvider ─────────────────────────────────────────────────────────
// ─── finalizeReplacementUpload ─────────────────────────────────────────────────
//
// Uploads a single file to the resolved provider.
// Returns { file_url, storage_key, mime_type, extension, checksum, file_type }
// Used by updateAsset() when replacing an asset's file (or a video's
// thumbnail): the browser already PUT the new file straight to storage via a
// presigned URL (see presignAssetUpload) — this reads back what actually
// landed there (HeadObjectCommand, no download) instead of ever buffering the
// file through this backend, the same strategy finalizeAssetFromStorage()
// uses for brand-new assets.
// Returns { file_url, storage_key, mime_type, extension, checksum, file_type, originalname }
//
async function uploadToProvider(file, ownerType, storageProvider) {
const mime_type = file.mimetype;
const extension = resolveExtension(file.originalname);
const checksum = resolveChecksum(file.buffer);
const file_type = resolveFileType(mime_type);
async function finalizeReplacementUpload(storage_key, original_name, mimetype, storageProvider) {
const svc = getProvider(storageProvider);
if (!svc || !svc.getFileMetadata) {
throw Object.assign(new Error("Presigned uploads are only supported for S3 storage."), { status: 400 });
}
const svc = getProvider(storageProvider);
const result = await svc.uploadFile({
buffer: file.buffer,
originalname: file.originalname,
mimetype: mime_type,
ownerType,
});
const meta = await svc.getFileMetadata(storage_key);
const mime_type = meta.mimetype || mimetype || "application/octet-stream";
return {
file_url: result.url,
storage_key: result.uuid, // chibisafe UUID or S3 key — both stored as storage_key in DB
file_url: await svc.buildPublicUrl(storage_key),
storage_key,
mime_type,
extension,
checksum,
file_type,
extension: resolveExtension(original_name || storage_key),
checksum: meta.checksum,
file_type: resolveFileType(mime_type),
originalname: original_name || storage_key,
};
}
@@ -657,13 +653,15 @@ exports.updateAsset = async (req, res) => {
const asset = await Asset.findOne({ where: { asset_id: assetId, ...notDeleted } });
if (!asset) return R.error(res, "Asset not found.", 404);
const file = req.files?.file?.[0] ?? req.file ?? null;
// Browser already PUT the replacement file straight to storage via a
// presigned URL (see presignAssetUpload) — this is plain JSON, no
// multer/file buffer involved, same pattern as POST /admin/assets.
const { storage_key, original_name, mimetype } = req.body;
const isVideo = asset.file_type === "video";
const isDocument = asset.file_type === "document";
if (isDocument && file) return R.error(res, "Document files cannot be replaced.", 400);
if (isVideo && file && !req.body.is_thumbnail) return R.error(res, "Video files cannot be replaced. Upload a new asset instead.", 400);
if (file && !file.buffer) return R.error(res, "File buffer is required.", 400);
if (isDocument && storage_key) return R.error(res, "Document files cannot be replaced.", 400);
if (isVideo && storage_key && !req.body.is_thumbnail) return R.error(res, "Video files cannot be replaced. Upload a new asset instead.", 400);
const storageProvider = asset.storage_provider;
const usesProvider = ["chibisafe", "s3"].includes(storageProvider);
@@ -673,9 +671,8 @@ exports.updateAsset = async (req, res) => {
let uploaded = null;
if (file && usesProvider) {
const ownerType = isVideo ? "thumbnail" : resolveFileType(file.mimetype);
uploaded = await uploadToProvider(file, ownerType, storageProvider);
if (storage_key && usesProvider) {
uploaded = await finalizeReplacementUpload(storage_key, original_name, mimetype, storageProvider);
newUpload = { key: uploaded.storage_key, provider: storageProvider };
}
@@ -683,7 +680,7 @@ exports.updateAsset = async (req, res) => {
const t = await sequelize.transaction();
try {
await applyAssetUpdate(asset, uploaded ? { ...file, ...uploaded } : null, req.body);
await applyAssetUpdate(asset, uploaded, req.body);
await asset.save({ transaction: t });
await t.commit();
} catch (dbErr) {