mirror of
https://github.com/rgrgogu/new_starr.git
synced 2026-09-27 00:12:54 +08:00
98 lines
3.9 KiB
JavaScript
98 lines
3.9 KiB
JavaScript
/***********************************************************************************************************************************************************************
|
|
* File Name: media.controller.js (admin)
|
|
* Type of Program: Controller
|
|
* Description: Issues short-lived JWT stream tokens for admin asset preview.
|
|
* Works identically to the client media token flow but is scoped to
|
|
* admin-authenticated requests and allows any asset regardless of
|
|
* is_public. The stream endpoint (/api/client/media/stream/:token)
|
|
* is shared — the JWT payload shape is identical.
|
|
*
|
|
* Author: Kenneth Obsequio (@lash0000)
|
|
* Date Created: Jun. 22, 2026
|
|
***********************************************************************************************************************************************************************/
|
|
"use strict";
|
|
|
|
const { Op } = require("sequelize");
|
|
|
|
const R = require("../../utils/response.util");
|
|
const mdl_Assets = require("../../models/assets/assets.mdl");
|
|
const mediaToken = require("../../services/mediaToken.service");
|
|
|
|
// ─── POST /admin/media/token ──────────────────────────────────────────────────
|
|
|
|
exports.issueToken = async (req, res) => {
|
|
try {
|
|
const { asset_id } = req.body;
|
|
if (!asset_id) return R.error(res, "asset_id is required.", 400);
|
|
|
|
const asset = await mdl_Assets.findOne({
|
|
where: { asset_id, deletedAt: null },
|
|
attributes: ["asset_id", "file_type", "storage_provider", "storage_key", "mime_type", "thumbnail_storage_key"],
|
|
});
|
|
|
|
if (!asset) return R.error(res, "File not found.", 404);
|
|
|
|
if (!mediaToken.SUPPORTED_TYPES.includes(asset.file_type)) {
|
|
return R.error(res, `File type "${asset.file_type}" is not supported.`, 400);
|
|
}
|
|
|
|
if (asset.storage_provider !== "s3") {
|
|
return R.error(res, "Token flow is for S3 files only. Use the raw file_url for other providers.", 400);
|
|
}
|
|
|
|
const ip = mediaToken.resolveIp(req);
|
|
const { token, thumbnail_url } = await mediaToken.issueForAsset(asset, req.user.user_id, ip);
|
|
|
|
return R.success(res, "Token issued.", {
|
|
token,
|
|
provider: "s3",
|
|
file_type: asset.file_type,
|
|
thumbnail_url,
|
|
});
|
|
|
|
} catch (err) {
|
|
console.error("[ADMIN][MEDIA][TOKEN]", err);
|
|
return R.error(res, "Could not issue media token.", 500);
|
|
}
|
|
};
|
|
|
|
// ─── POST /admin/media/tokens (batch) ───────────────────────────────────────
|
|
//
|
|
// Accepts { asset_ids: [id, ...] } — S3 assets only, max 50.
|
|
// Returns { tokens: { [asset_id]: token } }
|
|
// One round-trip instead of N per-card requests.
|
|
|
|
exports.issueTokensBatch = async (req, res) => {
|
|
try {
|
|
const { asset_ids } = req.body;
|
|
if (!Array.isArray(asset_ids) || !asset_ids.length)
|
|
return R.error(res, "asset_ids must be a non-empty array.", 400);
|
|
if (asset_ids.length > 50)
|
|
return R.error(res, "Maximum 50 asset_ids per batch.", 400);
|
|
|
|
const assets = await mdl_Assets.findAll({
|
|
where: {
|
|
asset_id: { [Op.in]: asset_ids },
|
|
storage_provider: "s3",
|
|
deletedAt: null,
|
|
},
|
|
attributes: ["asset_id", "file_type", "storage_key", "mime_type", "thumbnail_storage_key"],
|
|
});
|
|
|
|
const ip = mediaToken.resolveIp(req);
|
|
const tokens = {};
|
|
const thumbnails = {};
|
|
|
|
await Promise.all(assets.map(async (asset) => {
|
|
const { token, thumbnail_url } = await mediaToken.issueForAsset(asset, req.user.user_id, ip);
|
|
tokens[String(asset.asset_id)] = token;
|
|
if (thumbnail_url) thumbnails[String(asset.asset_id)] = thumbnail_url;
|
|
}));
|
|
|
|
return R.success(res, "Tokens issued.", { tokens, thumbnails });
|
|
} catch (err) {
|
|
console.error("[ADMIN][MEDIA][TOKENS BATCH]", err);
|
|
return R.error(res, "Could not issue media tokens.", 500);
|
|
}
|
|
};
|