Files
alrahma_sunday_school_api/apps/api/dist/lib/storage.js
T
2026-06-11 03:22:12 -04:00

131 lines
5.4 KiB
JavaScript

"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getStorageRoot = getStorageRoot;
exports.getPublicStorageRoot = getPublicStorageRoot;
exports.getPrivateStorageRoot = getPrivateStorageRoot;
exports.getLegacyStorageRoots = getLegacyStorageRoots;
exports.assertStorageConfiguration = assertStorageConfiguration;
exports.getProtectedCustomerLicenseImageUrl = getProtectedCustomerLicenseImageUrl;
exports.uploadImage = uploadImage;
exports.resolveStoredFilePath = resolveStoredFilePath;
exports.deleteImage = deleteImage;
const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
const crypto_1 = __importDefault(require("crypto"));
const APP_PACKAGE_ROOT = path_1.default.resolve(__dirname, '..', '..', '..');
const APP_SOURCE_ROOT = path_1.default.join(APP_PACKAGE_ROOT, 'src');
const APP_DIST_ROOT = path_1.default.join(APP_PACKAGE_ROOT, 'dist');
const DEFAULT_STORAGE_ROOT = path_1.default.join(APP_PACKAGE_ROOT, 'storage');
const LEGACY_STORAGE_ROOTS = [
path_1.default.join(APP_SOURCE_ROOT, 'lib', 'storage'),
];
function getStorageRoot() {
return process.env.FILE_STORAGE_ROOT
? path_1.default.resolve(process.env.FILE_STORAGE_ROOT)
: DEFAULT_STORAGE_ROOT;
}
function getPublicStorageRoot() {
return path_1.default.join(getStorageRoot(), 'public');
}
function getPrivateStorageRoot() {
return path_1.default.join(getStorageRoot(), 'private');
}
function getLegacyStorageRoots() {
return LEGACY_STORAGE_ROOTS;
}
function isWithinPath(targetPath, parentPath) {
const relative = path_1.default.relative(parentPath, targetPath);
return relative === '' || (!relative.startsWith('..') && !path_1.default.isAbsolute(relative));
}
function assertStorageConfiguration() {
const storageRoot = getStorageRoot();
if (process.env.NODE_ENV === 'production' && !process.env.FILE_STORAGE_ROOT) {
throw new Error('FILE_STORAGE_ROOT must be set in production so uploads are stored on the mounted volume.');
}
if (process.env.NODE_ENV === 'production') {
const forbiddenRoots = [APP_PACKAGE_ROOT, APP_SOURCE_ROOT, APP_DIST_ROOT];
const invalidRoot = forbiddenRoots.find((root) => isWithinPath(storageRoot, root));
if (invalidRoot) {
throw new Error(`FILE_STORAGE_ROOT must point outside the API app tree in production. Received ${storageRoot}, which is inside ${invalidRoot}.`);
}
}
return storageRoot;
}
function ensureStorageRoot(visibility) {
assertStorageConfiguration();
const root = visibility === 'public' ? getPublicStorageRoot() : getPrivateStorageRoot();
fs_1.default.mkdirSync(root, { recursive: true });
return root;
}
function inferVisibility(folder) {
const normalized = folder.replace(/\\/g, '/');
if (/\/customers\//.test(`/${normalized}/`))
return 'private';
if (/\/licenses?\//.test(`/${normalized}/`))
return 'private';
if (/\/contracts?\//.test(`/${normalized}/`))
return 'private';
if (/\/documents?\//.test(`/${normalized}/`))
return 'private';
if (/\/inspections?\//.test(`/${normalized}/`))
return 'private';
if (/\/internal\//.test(`/${normalized}/`))
return 'private';
return 'public';
}
function getApiBase() {
return (process.env.API_URL ?? 'http://localhost:4000').replace(/\/$/, '');
}
function getDashboardBase() {
return (process.env.DASHBOARD_URL ??
process.env.NEXT_PUBLIC_DASHBOARD_URL ??
'http://localhost:3000/dashboard').replace(/\/$/, '');
}
function getStorageRelativePath(imageUrl) {
const marker = '/storage/';
const idx = imageUrl.indexOf(marker);
if (idx === -1)
return null;
return imageUrl.slice(idx + marker.length);
}
function getProtectedCustomerLicenseImageUrl(customerId) {
return `${getDashboardBase()}/api/v1/customers/${customerId}/license-image`;
}
async function uploadImage(buffer, folder, publicId, visibility = inferVisibility(folder)) {
const storageRoot = ensureStorageRoot(visibility);
const folderPath = path_1.default.join(storageRoot, folder);
if (!isWithinPath(folderPath, storageRoot)) {
throw new Error('Upload path escapes storage root');
}
fs_1.default.mkdirSync(folderPath, { recursive: true });
const filename = publicId
? `${publicId}.jpg`
: `${crypto_1.default.randomBytes(16).toString('hex')}.jpg`;
fs_1.default.writeFileSync(path_1.default.join(folderPath, filename), buffer);
return `${getApiBase()}/storage/${folder}/${filename}`;
}
function resolveStoredFilePath(imageUrl) {
const relative = getStorageRelativePath(imageUrl);
if (!relative)
return null;
const roots = [getPublicStorageRoot(), getPrivateStorageRoot(), ...getLegacyStorageRoots(), assertStorageConfiguration()];
for (const root of roots) {
const filePath = path_1.default.join(root, relative);
if (!isWithinPath(filePath, root))
continue;
if (fs_1.default.existsSync(filePath)) {
return filePath;
}
}
return null;
}
async function deleteImage(imageUrl) {
const filePath = resolveStoredFilePath(imageUrl);
if (filePath && fs_1.default.existsSync(filePath)) {
fs_1.default.unlinkSync(filePath);
}
}
//# sourceMappingURL=storage.js.map