revised thing

Signed-off-by: Kenneth Obsequio <k80308392@gmail.com>
This commit is contained in:
2026-08-09 17:33:38 +08:00
parent 30ec1330c6
commit 9018d6d158
11 changed files with 82 additions and 82 deletions
@@ -122,7 +122,7 @@ async function applyAdvertisementFields(advertisement, body) {
} else {
const asset = await mdl_Assets.findOne({ where: { asset_id: body.image_asset_id, deletedAt: null } });
if (!asset) {
const err = new Error("Selected image asset was not found.");
const err = new Error("Selected image file was not found.");
err.status = 400;
throw err;
}
+29 -29
View File
@@ -245,10 +245,10 @@ exports.getAssets = async (req, res) => {
}
const data = await attachStreamTokens(result.data, req);
return R.success(res, "Assets retrieved.", { ...result, data });
return R.success(res, "Files retrieved.", { ...result, data });
} catch (err) {
console.error("[ASSET][GET ALL]", err);
return R.error(res, "Could not retrieve assets.", 500);
return R.error(res, "Could not retrieve files.", 500);
}
};
@@ -257,7 +257,7 @@ exports.getAssets = async (req, res) => {
exports.getAsset = async (req, res) => {
try {
const { assetId } = req.params;
if (!assetId || assetId === "undefined") return R.error(res, "Invalid asset ID.", 400);
if (!assetId || assetId === "undefined") return R.error(res, "Invalid file ID.", 400);
const asset = await Asset.findOne({
where: { asset_id: assetId, ...notDeleted },
@@ -270,7 +270,7 @@ exports.getAsset = async (req, res) => {
],
});
if (!asset) return R.error(res, "Asset not found.", 404);
if (!asset) return R.error(res, "File not found.", 404);
const json = asset.toJSON();
@@ -298,7 +298,7 @@ exports.getAsset = async (req, res) => {
delete json.storage_key;
redactS3Url(json);
return R.success(res, "Asset retrieved.", { data: json });
return R.success(res, "File retrieved.", { data: json });
} catch (err) {
console.error("[ASSET][GET ONE]", err);
return R.error(res, "Internal server error.", 500);
@@ -578,7 +578,7 @@ exports.uploadAsset = async (req, res) => {
const { storage_key, thumbnail_storage_key, original_name } = req.body;
const asset = await finalizeAssetFromStorage({ storage_key, thumbnail_storage_key, original_name, body: req.body, user: req.user });
invalidateListCache();
return R.success(res, "Asset uploaded.", { data: asset }, 201);
return R.success(res, "File uploaded.", { data: asset }, 201);
} catch (err) {
console.error("[ASSET][UPLOAD]", err);
@@ -594,10 +594,10 @@ exports.updateAsset = async (req, res) => {
try {
const { assetId } = req.params;
if (!assetId || assetId === "undefined") return R.error(res, "Invalid asset ID.", 400);
if (!assetId || assetId === "undefined") return R.error(res, "Invalid file ID.", 400);
const asset = await Asset.findOne({ where: { asset_id: assetId, ...notDeleted } });
if (!asset) return R.error(res, "Asset not found.", 404);
if (!asset) return R.error(res, "File not found.", 404);
// Browser already PUT the replacement file straight to storage via a
// presigned URL (see presignAssetUpload) — this is plain JSON, no
@@ -607,7 +607,7 @@ exports.updateAsset = async (req, res) => {
const isDocument = asset.file_type === "document";
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);
if (isVideo && storage_key && !req.body.is_thumbnail) return R.error(res, "Video files cannot be replaced. Upload a new file instead.", 400);
const storageProvider = asset.storage_provider;
const usesProvider = ["chibisafe", "s3"].includes(storageProvider);
@@ -641,7 +641,7 @@ exports.updateAsset = async (req, res) => {
invalidateListCache();
logActivity(req.user?.user_id, 'update_asset', { entityType: 'asset', entityId: Number(assetId) });
return R.success(res, "Asset updated.", { data: asset });
return R.success(res, "File updated.", { data: asset });
} catch (err) {
if (newUpload) await rollbackUploads([newUpload]);
@@ -656,16 +656,16 @@ exports.updateAsset = async (req, res) => {
exports.archiveAsset = async (req, res) => {
try {
const { assetId } = req.params;
if (!assetId || assetId === "undefined") return R.error(res, "Invalid asset ID.", 400);
if (!assetId || assetId === "undefined") return R.error(res, "Invalid file ID.", 400);
const asset = await Asset.findOne({ where: { asset_id: assetId, ...notDeleted } });
if (!asset) return R.error(res, "Asset not found.", 404);
if (!asset) return R.error(res, "File not found.", 404);
await asset.update({ deletedBy: req.body.deletedBy ?? null });
await asset.destroy();
invalidateListCache();
logActivity(req.user?.user_id, 'archive_asset', { entityType: 'asset', entityId: Number(assetId) });
return R.success(res, "Asset archived.");
return R.success(res, "File archived.");
} catch (err) {
console.error("[ASSET][ARCHIVE]", err);
return R.error(res, "Internal server error.", 500);
@@ -680,7 +680,7 @@ exports.archiveAssets = async (req, res) => {
if (!Array.isArray(ids) || !ids.length) return R.error(res, "ids must be a non-empty array.", 400);
const assets = await Asset.findAll({ where: { asset_id: { [Op.in]: ids }, ...notDeleted } });
if (!assets.length) return R.error(res, "No assets found.", 404);
if (!assets.length) return R.error(res, "No files found.", 404);
const activeIds = assets.map((a) => a.asset_id);
@@ -689,7 +689,7 @@ exports.archiveAssets = async (req, res) => {
invalidateListCache();
logActivity(req.user?.user_id, 'bulk_archive_assets', { entityType: 'asset', details: { ids: activeIds, count: activeIds.length } });
return R.success(res, `${activeIds.length} asset(s) archived.`, {
return R.success(res, `${activeIds.length} file(s) archived.`, {
archived_ids: activeIds,
skipped_ids: ids.filter((id) => !activeIds.includes(id)),
});
@@ -706,14 +706,14 @@ exports.restoreAsset = async (req, res) => {
const { assetId } = req.params;
const asset = await Asset.findOne({ where: { asset_id: assetId }, paranoid: false });
if (!asset) return R.error(res, "Asset not found.", 404);
if (!asset.deletedAt) return R.error(res, "Asset is not archived.", 400);
if (!asset) return R.error(res, "File not found.", 404);
if (!asset.deletedAt) return R.error(res, "File is not archived.", 400);
await asset.restore();
await asset.update({ deletedBy: null });
invalidateListCache();
logActivity(req.user?.user_id, 'restore_asset', { entityType: 'asset', entityId: Number(assetId) });
return R.success(res, "Asset restored.", { data: asset });
return R.success(res, "File restored.", { data: asset });
} catch (err) {
console.error("[ASSET][RESTORE]", err);
return R.error(res, "Internal server error.", 500);
@@ -728,10 +728,10 @@ exports.restoreAssets = async (req, res) => {
if (!Array.isArray(ids) || !ids.length) return R.error(res, "ids must be a non-empty array.", 400);
const assets = await Asset.findAll({ where: { asset_id: { [Op.in]: ids } }, paranoid: false });
if (!assets.length) return R.error(res, "No assets found.", 404);
if (!assets.length) return R.error(res, "No files found.", 404);
const archivedAssets = assets.filter((a) => a.deletedAt);
if (!archivedAssets.length) return R.error(res, "All selected assets are already active.", 400);
if (!archivedAssets.length) return R.error(res, "All selected files are already active.", 400);
const archivedIds = archivedAssets.map((a) => a.asset_id);
@@ -740,7 +740,7 @@ exports.restoreAssets = async (req, res) => {
invalidateListCache();
logActivity(req.user?.user_id, 'bulk_restore_assets', { entityType: 'asset', details: { ids: archivedIds, count: archivedIds.length } });
return R.success(res, `${archivedIds.length} asset(s) restored.`, {
return R.success(res, `${archivedIds.length} file(s) restored.`, {
restored_ids: archivedIds,
skipped_ids: ids.filter((id) => !archivedIds.includes(id)),
});
@@ -757,8 +757,8 @@ exports.permanentlyDeleteAsset = async (req, res) => {
const { assetId } = req.params;
const asset = await Asset.findOne({ where: { asset_id: assetId }, paranoid: false });
if (!asset) return R.error(res, "Asset not found.", 404);
if (!asset.deletedAt) return R.error(res, "Asset must be archived before it can be permanently deleted.", 400);
if (!asset) return R.error(res, "File not found.", 404);
if (!asset.deletedAt) return R.error(res, "File must be archived before it can be permanently deleted.", 400);
const { storage_provider, storage_key, thumbnail_storage_key } = asset;
@@ -778,7 +778,7 @@ exports.permanentlyDeleteAsset = async (req, res) => {
invalidateListCache();
logActivity(req.user?.user_id, 'permanently_delete_asset', { entityType: 'asset', entityId: Number(assetId) });
return R.success(res, "Asset permanently deleted.");
return R.success(res, "File permanently deleted.");
} catch (err) {
console.error("[ASSET][PERMANENT DELETE]", err);
return R.error(res, "Internal server error.", 500);
@@ -793,10 +793,10 @@ exports.permanentlyDeleteAssets = async (req, res) => {
if (!Array.isArray(ids) || !ids.length) return R.error(res, "ids must be a non-empty array.", 400);
const assets = await Asset.findAll({ where: { asset_id: { [Op.in]: ids } }, paranoid: false });
if (!assets.length) return R.error(res, "No assets found.", 404);
if (!assets.length) return R.error(res, "No files found.", 404);
const archivedAssets = assets.filter((a) => a.deletedAt);
if (!archivedAssets.length) return R.error(res, "All selected assets must be archived before they can be permanently deleted.", 400);
if (!archivedAssets.length) return R.error(res, "All selected files must be archived before they can be permanently deleted.", 400);
const archivedIds = archivedAssets.map((a) => a.asset_id);
@@ -817,7 +817,7 @@ exports.permanentlyDeleteAssets = async (req, res) => {
invalidateListCache();
logActivity(req.user?.user_id, 'bulk_permanently_delete_assets', { entityType: 'asset', details: { ids: archivedIds, count: archivedIds.length } });
return R.success(res, `${archivedIds.length} asset(s) permanently deleted.`, {
return R.success(res, `${archivedIds.length} file(s) permanently deleted.`, {
deleted_ids: archivedIds,
skipped_ids: ids.filter((id) => !archivedIds.includes(id)),
});
@@ -840,10 +840,10 @@ exports.getArchivedAssets = async (req, res) => {
findOptions: { paranoid: false, where: { deletedAt: { [Op.ne]: null } } },
});
result.data = result.data.map(redactS3Url);
return R.success(res, "Archived assets retrieved.", result);
return R.success(res, "Archived files retrieved.", result);
} catch (err) {
console.error("[ASSET][GET ARCHIVED]", err);
return R.error(res, "Could not retrieve archived assets.", 500);
return R.error(res, "Could not retrieve archived files.", 500);
}
};
+3 -3
View File
@@ -30,14 +30,14 @@ exports.issueToken = async (req, res) => {
attributes: ["asset_id", "file_type", "storage_provider", "storage_key", "mime_type", "thumbnail_storage_key"],
});
if (!asset) return R.error(res, "Asset not found.", 404);
if (!asset) return R.error(res, "File not found.", 404);
if (!mediaToken.SUPPORTED_TYPES.includes(asset.file_type)) {
return R.error(res, `Asset type "${asset.file_type}" is not supported.`, 400);
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 assets only. Use the raw file_url for other providers.", 400);
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);
@@ -73,7 +73,7 @@ async function validateImageAssetId(image_asset_id) {
if (!image_asset_id) return null;
const asset = await mdl_Assets.findOne({ where: { asset_id: image_asset_id, deletedAt: null } });
if (!asset) {
const err = new Error("Selected image asset was not found.");
const err = new Error("Selected image file was not found.");
err.status = 400;
throw err;
}
+17 -17
View File
@@ -20,10 +20,10 @@ exports.getCategories = async (req, res) => {
include: withBadge,
order: [['rank', 'ASC']],
});
return R.success(res, 'Tier categories retrieved.', categories);
return R.success(res, 'Subscription categories retrieved.', categories);
} catch (err) {
console.error('[ADMIN][GET TIER CATEGORIES]', err);
return R.error(res, 'Could not retrieve tier categories.', 500);
return R.error(res, 'Could not retrieve subscription categories.', 500);
}
};
@@ -32,11 +32,11 @@ exports.getCategories = async (req, res) => {
exports.getCategory = async (req, res) => {
try {
const cat = await mdl_TierCategories.findByPk(req.params.id, { include: withBadge });
if (!cat) return R.error(res, 'Tier category not found.', 404);
return R.success(res, 'Tier category retrieved.', cat);
if (!cat) return R.error(res, 'Subscription category not found.', 404);
return R.success(res, 'Subscription category retrieved.', cat);
} catch (err) {
console.error('[ADMIN][GET TIER CATEGORY]', err);
return R.error(res, 'Could not retrieve tier category.', 500);
return R.error(res, 'Could not retrieve subscription category.', 500);
}
};
@@ -48,10 +48,10 @@ exports.createCategory = async (req, res) => {
if (!slug || !name) return R.error(res, 'slug and name are required.', 400);
const parsedRank = Number(rank ?? 1);
if (parsedRank <= 0) return R.error(res, 'Non-default tier categories must have rank greater than 0.', 400);
if (parsedRank <= 0) return R.error(res, 'Non-default subscription categories must have rank greater than 0.', 400);
const exists = await mdl_TierCategories.findOne({ where: { slug } });
if (exists) return R.error(res, `A tier category with slug "${slug}" already exists.`, 409);
if (exists) return R.error(res, `A subscription category with slug "${slug}" already exists.`, 409);
const cat = await mdl_TierCategories.create({
slug, name,
@@ -69,10 +69,10 @@ exports.createCategory = async (req, res) => {
logActivity(req.user?.user_id, 'create_tier_category', { entityType: 'tier_category', details: { slug, name } });
const result = await mdl_TierCategories.findByPk(cat.tier_category_id, { include: withBadge });
return R.success(res, 'Tier category created.', result, 201);
return R.success(res, 'Subscription category created.', result, 201);
} catch (err) {
console.error('[ADMIN][CREATE TIER CATEGORY]', err);
return R.error(res, 'Could not create tier category.', 500);
return R.error(res, 'Could not create subscription category.', 500);
}
};
@@ -81,13 +81,13 @@ exports.createCategory = async (req, res) => {
exports.updateCategory = async (req, res) => {
try {
const cat = await mdl_TierCategories.findByPk(req.params.id);
if (!cat) return R.error(res, 'Tier category not found.', 404);
if (!cat) return R.error(res, 'Subscription category not found.', 404);
const { name, description, rank, color, badge_asset_id, badge_icon, badge_label, is_active, is_special } = req.body;
if (!cat.is_default && rank !== undefined) {
const parsedRank = Number(rank);
if (parsedRank <= 0) return R.error(res, 'Non-default tier categories must have rank greater than 0.', 400);
if (parsedRank <= 0) return R.error(res, 'Non-default subscription categories must have rank greater than 0.', 400);
}
await cat.update({
@@ -106,10 +106,10 @@ exports.updateCategory = async (req, res) => {
logActivity(req.user?.user_id, 'update_tier_category', { entityType: 'tier_category', details: { id: cat.tier_category_id, slug: cat.slug } });
const result = await mdl_TierCategories.findByPk(cat.tier_category_id, { include: withBadge });
return R.success(res, 'Tier category updated.', result);
return R.success(res, 'Subscription category updated.', result);
} catch (err) {
console.error('[ADMIN][UPDATE TIER CATEGORY]', err);
return R.error(res, 'Could not update tier category.', 500);
return R.error(res, 'Could not update subscription category.', 500);
}
};
@@ -118,8 +118,8 @@ exports.updateCategory = async (req, res) => {
exports.deleteCategory = async (req, res) => {
try {
const cat = await mdl_TierCategories.findByPk(req.params.id);
if (!cat) return R.error(res, 'Tier category not found.', 404);
if (cat.is_default) return R.error(res, 'The default (Free) tier category cannot be deleted.', 400);
if (!cat) return R.error(res, 'Subscription category not found.', 404);
if (cat.is_default) return R.error(res, 'The default (Free) subscription category cannot be deleted.', 400);
// Block deletion if active plans still reference this category
const activePlans = await mdl_TierPlans.count({
@@ -130,9 +130,9 @@ exports.deleteCategory = async (req, res) => {
await cat.destroy();
logActivity(req.user?.user_id, 'delete_tier_category', { entityType: 'tier_category', details: { slug: cat.slug } });
return R.success(res, 'Tier category deleted.');
return R.success(res, 'Subscription category deleted.');
} catch (err) {
console.error('[ADMIN][DELETE TIER CATEGORY]', err);
return R.error(res, 'Could not delete tier category.', 500);
return R.error(res, 'Could not delete subscription category.', 500);
}
};
+12 -12
View File
@@ -117,10 +117,10 @@ exports.createPlan = async (req, res) => {
const category = await mdl_TierCategories.findByPk(tier_category_id);
if (!category || !category.is_active)
return R.error(res, 'Tier category not found or inactive.', 404);
return R.error(res, 'Subscription category not found or inactive.', 404);
if (category.is_default)
return R.error(res, 'Plans cannot be created under the default (Free) tier. Free access is automatic.', 400);
return R.error(res, 'Plans cannot be created under the default (Free) subscription. Free access is automatic.', 400);
const duration_days = computeDurationDays(duration_value, duration_unit);
@@ -166,9 +166,9 @@ exports.updatePlan = async (req, res) => {
if (updates.tier_category_id) {
const category = await mdl_TierCategories.findByPk(updates.tier_category_id);
if (!category || !category.is_active)
return R.error(res, 'Tier category not found or inactive.', 404);
return R.error(res, 'Subscription category not found or inactive.', 404);
if (category.is_default)
return R.error(res, 'Plans cannot be moved to the Free tier category.', 400);
return R.error(res, 'Plans cannot be moved to the Free subscription category.', 400);
updates.tier = category.slug;
}
@@ -428,10 +428,10 @@ exports.getUserTiers = async (req, res) => {
],
order: [['createdAt', 'DESC']],
});
return R.success(res, 'User tiers retrieved.', tiers);
return R.success(res, 'User subscriptions retrieved.', tiers);
} catch (err) {
console.error('[ADMIN][GET USER TIERS]', err);
return R.error(res, 'Could not retrieve user tiers.', 500);
return R.error(res, 'Could not retrieve user subscriptions.', 500);
}
};
@@ -472,18 +472,18 @@ exports.grantTier = async (req, res) => {
await snapshotPlanGrants(newTier, plan_id);
logActivity(req.user.user_id, 'grant_tier', { entityType: 'tier', details: { user_id, tier, plan_id } });
return R.success(res, 'Tier granted.', newTier, 201);
return R.success(res, 'Subscription granted.', newTier, 201);
} catch (err) {
console.error('[ADMIN][GRANT TIER]', err);
return R.error(res, 'Could not grant tier.', 500);
return R.error(res, 'Could not grant subscription.', 500);
}
};
exports.revokeTier = async (req, res) => {
try {
const tierRecord = await mdl_UserTiers.findByPk(req.params.tid);
if (!tierRecord) return R.error(res, 'Tier record not found.', 404);
if (tierRecord.status !== 'active') return R.error(res, 'Tier is not active.', 400);
if (!tierRecord) return R.error(res, 'Subscription record not found.', 404);
if (tierRecord.status !== 'active') return R.error(res, 'Subscription is not active.', 400);
await tierRecord.update({
status: 'revoked',
@@ -510,10 +510,10 @@ exports.revokeTier = async (req, res) => {
}
logActivity(req.user.user_id, 'revoke_tier', { entityType: 'tier', details: { user_id: tierRecord.user_id, tier: tierRecord.tier } });
return R.success(res, remainingActive === 0 ? 'Tier revoked. User downgraded to free.' : 'Tier revoked.');
return R.success(res, remainingActive === 0 ? 'Subscription revoked. User downgraded to free.' : 'Subscription revoked.');
} catch (err) {
console.error('[ADMIN][REVOKE TIER]', err);
return R.error(res, 'Could not revoke tier.', 500);
return R.error(res, 'Could not revoke subscription.', 500);
}
};