burdy
Version:
Open Source Headless Platform
453 lines (452 loc) • 21.6 kB
JavaScript
;
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;