UNPKG

burdy

Version:

Open Source Headless Platform

453 lines (452 loc) 21.6 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.generateUniqueName = void 0; const express_1 = __importDefault(require("express")); const auth_middleware_1 = __importDefault(require("../middleware/auth.middleware")); const async_middleware_1 = __importDefault(require("../middleware/async.middleware")); const async_1 = __importDefault(require("async")); const typeorm_1 = require("typeorm"); const asset_model_1 = __importDefault(require("../models/asset.model")); const path_1 = __importDefault(require("path")); const bad_request_error_1 = __importDefault(require("../errors/bad-request-error")); const yup = __importStar(require("yup")); const image_size_1 = __importDefault(require("image-size")); const tag_model_1 = __importDefault(require("../models/tag.model")); const file_driver_1 = __importDefault(require("../drivers/file.driver")); const orm_helpers_1 = require("../common/orm-helpers"); const mappers_1 = require("../common/mappers"); const hooks_1 = __importDefault(require("../../shared/features/hooks")); const app = (0, express_1.default)(); const FOLDER_MIME_TYPE = 'application/vnd.burdy.folder'; const IMAGE_MIME_TYPES = ['image/jpeg', 'image/png', 'image/webp', 'image/gif']; const generateUniqueName = (manager, name, parent, counter = 1) => __awaiter(void 0, void 0, void 0, function* () { const ext = path_1.default.extname(name); const index = name.lastIndexOf(ext); let proposedName = name.substring(0, index); if (index > -1 && ext.length > 0) { proposedName = `${proposedName} (${counter})${ext}`; } else { proposedName = `${proposedName} (${counter})`; } const searchObj = { name: proposedName, }; if (parent) { searchObj.parentId = parent; } const asset = yield manager.findOne(asset_model_1.default, searchObj); if (asset) { counter++; proposedName = yield (0, exports.generateUniqueName)(manager, name, parent, counter); } return proposedName; }); exports.generateUniqueName = generateUniqueName; const getParent = (manager, components, parent) => __awaiter(void 0, void 0, void 0, function* () { if (components.length > 0) { let newParent; const name = components.shift(); const npath = parent ? `${parent.npath}/${name}` : name; try { newParent = yield manager.findOne(asset_model_1.default, { npath, }); if (!newParent) { newParent = yield manager.save(asset_model_1.default, { parent, name, mimeType: FOLDER_MIME_TYPE, npath, }); } } catch (err) { newParent = yield manager.findOne(asset_model_1.default, { npath, }); } parent = yield getParent(manager, components, newParent); } return parent; }); app.get('/assets', (0, auth_middleware_1.default)(), (0, async_middleware_1.default)((req, res) => __awaiter(void 0, void 0, void 0, function* () { const assetRepository = (0, typeorm_1.getRepository)(asset_model_1.default); const databaseType = (0, typeorm_1.getConnection)().options.type; const { id, parentId, mimeType, search, npath } = req.query; const qb = assetRepository .createQueryBuilder('asset') .leftJoinAndSelect('asset.meta', 'meta') .leftJoinAndSelect('asset.tags', 'tags'); if (id) { qb.andWhereInIds(id.split(',')); } if (npath) { qb.andWhere('asset.npath IN (:...npath)', { npath: npath.split(',') }); } qb.leftJoinAndSelect((q) => q .select([ 'npath', databaseType === 'postgres' ? '"parentId"' : 'parentId', databaseType === 'postgres' ? '"mimeType"' : 'mimeType', ]) .from(asset_model_1.default, 'thumbnail') .where('thumbnail.mimeType IN (:...mimeTypes)', { mimeTypes: IMAGE_MIME_TYPES, }), 'thumbnail', databaseType === 'postgres' ? 'thumbnail."parentId" = asset.id' : 'thumbnail.parentId = asset.id'); qb.addSelect('thumbnail.npath', 'asset_thumbnail'); if ((search === null || search === void 0 ? void 0 : search.length) > 0) { qb.andWhere('asset.mimeType != :mimeType', { mimeType: FOLDER_MIME_TYPE, }).andWhere('LOWER(asset.name) LIKE :search', { search: `%${search.toLowerCase()}%`, }); } if ((mimeType === null || mimeType === void 0 ? void 0 : mimeType.length) > 0) { qb.andWhere('asset.mimeType IN (:...mimeTypes)', { mimeTypes: [...mimeType.split(','), FOLDER_MIME_TYPE], }); } if (!((search === null || search === void 0 ? void 0 : search.length) > 0) && !(id || npath)) { if (parentId) { qb.andWhere('asset.parentId = :parentId', { parentId }); } else { qb.andWhere('asset.parentId IS NULL'); } } const assets = yield qb.orderBy('asset.name', 'DESC').getMany(); return res.send(assets.map(mappers_1.mapAsset)); }))); app.get('/assets/ancestors', (0, auth_middleware_1.default)(), (0, async_middleware_1.default)((req, res) => __awaiter(void 0, void 0, void 0, function* () { const assetTreeRepository = yield (0, typeorm_1.getTreeRepository)(asset_model_1.default); let asset; let ancestorsTree; const { id } = req.query; if (id) { asset = yield assetTreeRepository.findOne({ relations: ['tags'], where: { id, }, }); if (!asset) throw new bad_request_error_1.default('invalid_asset'); ancestorsTree = yield assetTreeRepository.findAncestorsTree(asset); } res.send(asset_model_1.default.getAncestorsList(ancestorsTree).reverse().map(mappers_1.mapAsset)); }))); app.get('/assets/single', (0, auth_middleware_1.default)(), (0, async_middleware_1.default)((req, res) => __awaiter(void 0, void 0, void 0, function* () { const assetRepository = (0, typeorm_1.getRepository)(asset_model_1.default); const { attachment, npath } = req.query; const asset = yield assetRepository.findOne({ relations: ['tags'], where: { npath }, }); if (!asset) throw new bad_request_error_1.default('invalid_asset'); if (asset.mimeType === FOLDER_MIME_TYPE) throw new bad_request_error_1.default('invalid_asset'); let content; if (asset.document) { content = yield file_driver_1.default.getInstance().read(asset.document); } res.set('Content-Type', asset.mimeType); res.set('Content-Length', `${asset.contentLength}`); if (IMAGE_MIME_TYPES.indexOf(asset.mimeType) === -1 || attachment) { res.attachment(asset.name); } res.send(content); }))); app.get('/assets/:id', (0, auth_middleware_1.default)(), (0, async_middleware_1.default)((req, res) => __awaiter(void 0, void 0, void 0, function* () { const assetRepository = (0, typeorm_1.getRepository)(asset_model_1.default); const { attachment } = req.query; const asset = yield assetRepository.findOne({ relations: ['tags'], where: { id: req.params.id, }, }); if (!asset) throw new bad_request_error_1.default('invalid_asset'); if (asset.mimeType === FOLDER_MIME_TYPE) throw new bad_request_error_1.default('invalid_asset'); let content; if (asset.document) { content = yield file_driver_1.default.getInstance().read(asset.document); } res.set('Content-Type', asset.mimeType); res.set('Content-Length', `${asset.contentLength}`); if (IMAGE_MIME_TYPES.indexOf(asset.mimeType) === -1 || attachment) { res.attachment(asset.name); } res.send(content); }))); const getKeyName = (key = '') => { return key.split('/').pop(); }; // Create folder, assets app.post('/assets', (0, auth_middleware_1.default)(['assets_create']), file_driver_1.default.getInstance().getUpload().single('file'), (0, async_middleware_1.default)((req, res) => __awaiter(void 0, void 0, void 0, function* () { var _a, _b; const entityManager = (0, typeorm_1.getManager)(); const params = req.body; const { duplicateName } = params; try { let asset; yield entityManager.transaction((tManager) => __awaiter(void 0, void 0, void 0, function* () { var _c, _d, _e, _f, _g, _h, _j; let parent; if (params.parentId) { parent = yield tManager.findOne(asset_model_1.default, { id: params.parentId }); if (!parent) throw new bad_request_error_1.default('invalid_parent'); } const nameComponents = params.name .split('/') .filter((cmp) => (cmp === null || cmp === void 0 ? void 0 : cmp.length) > 0); let name = nameComponents.pop(); if (nameComponents.length > 0) { parent = yield getParent(entityManager, nameComponents, parent); } const searchObj = { name, }; if (parent) { searchObj.parentId = parent.id; } asset = yield tManager.findOne(asset_model_1.default, searchObj); if (asset) { if (duplicateName) { name = yield (0, exports.generateUniqueName)(tManager, name, parent); } else { throw new bad_request_error_1.default('duplicate_name'); } } const assetObj = { name, mimeType: params.mimeType, author: (_c = req === null || req === void 0 ? void 0 : req.data) === null || _c === void 0 ? void 0 : _c.user, }; if (parent) { assetObj.parent = parent; assetObj.npath = `${parent.npath}/${name}`; } else { assetObj.npath = name; } if ((params === null || params === void 0 ? void 0 : params.mimeType) !== FOLDER_MIME_TYPE) { if (!req.file) throw new bad_request_error_1.default('invalid_file'); const stat = yield file_driver_1.default.getInstance().stat(((_d = req === null || req === void 0 ? void 0 : req.file) === null || _d === void 0 ? void 0 : _d.filename) || getKeyName((_e = req === null || req === void 0 ? void 0 : req.file) === null || _e === void 0 ? void 0 : _e.key)); if (!stat) throw new bad_request_error_1.default('invalid_file'); if (IMAGE_MIME_TYPES.indexOf(params.mimeType) > -1) { const file = yield file_driver_1.default.getInstance().read(((_f = req === null || req === void 0 ? void 0 : req.file) === null || _f === void 0 ? void 0 : _f.filename) || getKeyName((_g = req === null || req === void 0 ? void 0 : req.file) === null || _g === void 0 ? void 0 : _g.key)); const dimensions = (0, image_size_1.default)(file); assetObj.meta = [ { key: 'height', value: dimensions.height, }, { key: 'width', value: dimensions.width, }, ]; } assetObj.provider = file_driver_1.default.getInstance().getName(); assetObj.contentLength = stat.contentLength; assetObj.document = ((_h = req === null || req === void 0 ? void 0 : req.file) === null || _h === void 0 ? void 0 : _h.filename) || getKeyName((_j = req === null || req === void 0 ? void 0 : req.file) === null || _j === void 0 ? void 0 : _j.key); } asset = yield tManager.save(asset_model_1.default, assetObj); })); return res.send((0, mappers_1.mapAsset)(asset)); } catch (err) { yield file_driver_1.default.getInstance().delete(((_a = req === null || req === void 0 ? void 0 : req.file) === null || _a === void 0 ? void 0 : _a.filename) || getKeyName((_b = req === null || req === void 0 ? void 0 : req.file) === null || _b === void 0 ? void 0 : _b.key)); throw err; } }))); app.put('/assets/:id/rename', (0, auth_middleware_1.default)(['assets_update']), (0, async_middleware_1.default)((req, res) => __awaiter(void 0, void 0, void 0, function* () { yield req.validate({ name: yup.string().required(), }, 'body'); const entityManager = (0, typeorm_1.getManager)(); let updatedAsset; yield entityManager.transaction((tManager) => __awaiter(void 0, void 0, void 0, function* () { var _k; const asset = yield entityManager.findOne(asset_model_1.default, { id: (_k = req === null || req === void 0 ? void 0 : req.params) === null || _k === void 0 ? void 0 : _k.id }); if (!asset) throw new bad_request_error_1.default('invalid_asset'); asset.name = req.body.name; const originalNPath = asset.npath; const components = asset.npath.split('/'); components.pop(); components.push(req.body.name); const npath = components.join('/'); asset.npath = npath; yield tManager.save(asset_model_1.default, asset); const result = yield tManager.query((0, orm_helpers_1.getReplaceChildrenQuery)('asset', 'npath', originalNPath, npath)); // if not at least one entry (the very object we're renaming) was updated, then the query or the data is wrong if (result[1] < 1) { // throw new Error('Something went wrong during rename.'); } updatedAsset = yield tManager.findOne(asset_model_1.default, { id: req.params.id }); })); return res.send((0, mappers_1.mapAsset)(updatedAsset)); }))); app.put('/assets/:id', (0, auth_middleware_1.default)(['assets_update']), (0, async_middleware_1.default)((req, res) => __awaiter(void 0, void 0, void 0, function* () { yield req.validate({ alt: yup.string().max(256), copyright: yup.string().max(256), }, 'body'); const entityManager = (0, typeorm_1.getManager)(); yield entityManager.transaction((tManager) => __awaiter(void 0, void 0, void 0, function* () { var _l, _m, _o, _p, _q, _r, _s; const asset = yield entityManager.findOne(asset_model_1.default, { relations: ['meta'], where: { id: (_l = req === null || req === void 0 ? void 0 : req.params) === null || _l === void 0 ? void 0 : _l.id, }, }); if (!asset) throw new bad_request_error_1.default('invalid_asset'); const meta = []; if ((_m = req === null || req === void 0 ? void 0 : req.body) === null || _m === void 0 ? void 0 : _m.alt) { meta.push({ key: 'alt', value: (_o = req === null || req === void 0 ? void 0 : req.body) === null || _o === void 0 ? void 0 : _o.alt, }); } if ((_p = req === null || req === void 0 ? void 0 : req.body) === null || _p === void 0 ? void 0 : _p.copyright) { meta.push({ key: 'copyright', value: (_q = req === null || req === void 0 ? void 0 : req.body) === null || _q === void 0 ? void 0 : _q.copyright, }); } yield (0, orm_helpers_1.updateMeta)(tManager, asset_model_1.default, asset, meta, /^(alt|copyright)/); if (Array.isArray((_r = req === null || req === void 0 ? void 0 : req.body) === null || _r === void 0 ? void 0 : _r.tags)) { const tags = yield tManager.getRepository(tag_model_1.default).find({ where: { id: (0, typeorm_1.In)((_s = req === null || req === void 0 ? void 0 : req.body) === null || _s === void 0 ? void 0 : _s.tags.map((tag) => tag === null || tag === void 0 ? void 0 : tag.id).filter((id) => !!id)), }, }); asset.tags = tags; } yield tManager.save(asset); return res.send((0, mappers_1.mapAsset)(asset)); })); }))); app.delete('/assets', (0, auth_middleware_1.default)(['assets_delete']), (0, async_middleware_1.default)((req, res) => __awaiter(void 0, void 0, void 0, function* () { var _t; const ids = (_t = req === null || req === void 0 ? void 0 : req.body) !== null && _t !== void 0 ? _t : []; if (!ids || (ids === null || ids === void 0 ? void 0 : ids.length) === 0) return res.send([]); const entityManager = (0, typeorm_1.getManager)(); const documents = []; const toDeleteAssets = []; yield entityManager.transaction((tManager) => __awaiter(void 0, void 0, void 0, function* () { const assetTreeRepository = tManager.getTreeRepository(asset_model_1.default); const assets = yield tManager.findByIds(asset_model_1.default, ids); if (assets.length === 0) return []; yield async_1.default.eachSeries(assets, (asset, next) => __awaiter(void 0, void 0, void 0, function* () { const children = yield assetTreeRepository.findDescendants(asset); children.forEach((child) => { toDeleteAssets.push(child); if (child.document) { documents.push(child.document); } }); next(); })); return tManager.delete(asset_model_1.default, { id: (0, typeorm_1.In)(toDeleteAssets.map((asset) => asset.id)), }); })); file_driver_1.default.getInstance().delete(documents); return res.send(toDeleteAssets.map((asset) => asset.id)); }))); app.get('/uploads/*', (0, async_middleware_1.default)((req, res) => __awaiter(void 0, void 0, void 0, function* () { var _u, _v; const assetRepository = (0, typeorm_1.getRepository)(asset_model_1.default); const videoRange = req.headers.range; const asset = yield assetRepository.findOne({ npath: req.params[0] }); if (!asset) throw new bad_request_error_1.default('invalid_asset'); if (asset.mimeType === FOLDER_MIME_TYPE) throw new bad_request_error_1.default('invalid_asset'); yield hooks_1.default.doAction('public/getAsset', asset); if (videoRange) { const parts = videoRange.replace(/bytes=/, '').split('-'); const start = parseInt(parts[0], 10); const end = parts[1] ? parseInt(parts[1], 10) : asset.contentLength - 1; const chunksize = end - start + 1; const file = file_driver_1.default.getInstance().createReadStream(asset.document, { range: videoRange, start, end, }); const head = { 'Content-Range': `bytes ${start}-${end}/${asset.contentLength}`, 'Accept-Ranges': 'bytes', 'Content-Length': chunksize, 'Content-Type': asset.mimeType, 'Cache-Control': ((_u = process.env.ASSETS_CACHE_CONTROL) === null || _u === void 0 ? void 0 : _u.length) > 0 ? process.env.ASSETS_CACHE_CONTROL : 'no-cache', }; res.writeHead(206, head); file.pipe(res); } else { const head = { 'Content-Length': asset.contentLength, 'Content-Type': asset.mimeType, 'Cache-Control': ((_v = process.env.ASSETS_CACHE_CONTROL) === null || _v === void 0 ? void 0 : _v.length) > 0 ? process.env.ASSETS_CACHE_CONTROL : 'no-cache', }; res.writeHead(200, head); file_driver_1.default.getInstance().createReadStream(asset.document).pipe(res); } }))); exports.default = app;