const mime = require("mime-types"); const fs = require('fs'); const Path = require('path'); const Security = require('../src/security.js'); const MediaService = require('../model/mediaService.js'); const MediaFileMetaModel = require('../model/mediaItemMeta.js').MediaFileMetaModel; const MediaFileTagModel = require('../model/mediaItemTag.js').MediaFileTagModel; const { AccessModel, ACCESS_TYPE, ACCESS_GRANT } = require('../model/access.js'); module.exports = { register: app => { app.router.get("/api/access/list", (req, res) => { app.routerUtils.onApiRequest(req, res); app.routerUtils.jsonResponse(res, req.sessionObj?.accessList || {}); }); app.router.post("/api/access/link", async (req, res) => { // /api/access/link, post: { linkIds: [string] (JSON) } app.routerUtils.onApiRequest(req, res); if (!req.post?.linkIds?.length) return app.routerUtils.httpResponse(res, 400, "Missing argument"); for (let i of JSON.parse(req.post.linkIds)) { if (await app.databaseHelper.findOne(AccessModel, { type: ACCESS_TYPE.link, typeData: i })) Security.addLinkToSession(req, i); } app.routerUtils.jsonResponse(res, req.sessionObj.accessList); }); app.router.del("/api/access/:id", (req, res) => { app.routerUtils.onApiRequest(req, res); const result = Security.removeFromSession(req, req.params.id); app.routerUtils.jsonResponse(res, result); }); app.router.del("/api/media/:id/tag/:tag", async (req, res) => { app.routerUtils.onApiRequest(req, res); if (!req.params.id ||!req.params.tag) return app.routerUtils.onBadRequest(res); let data = await MediaService.fetchOne(app, req.params.id, req.sessionObj?.accessList); if (!data || data.accessType !== ACCESS_GRANT.write) return app.routerUtils.onPageNotFound(res); await app.databaseHelper.remove(MediaFileTagModel, { md5sum: data.md5sum, tag: decodeURIComponent(req.params.tag), fromMeta: 0 }); app.routerUtils.jsonResponse(res, await MediaService.fetchOne(app, req.params.id, req.sessionObj?.accessList)); }); app.router.put("/api/media/:id/tag", async (req, res) => { app.routerUtils.onApiRequest(req, res); if (!req.params.id ||!req.body?.tag) return app.routerUtils.onBadRequest(res); let data = await MediaService.fetchOne(app, req.params.id, req.sessionObj?.accessList); if (!data || data.accessType !== ACCESS_GRANT.write) return app.routerUtils.onPageNotFound(res); const requestedTag = req.body.tag; for (let existingTag of [...data.tags, ...data.fixedTags]) { if (existingTag === requestedTag || existingTag.startsWith(`${requestedTag}/`)) { return app.routerUtils.jsonResponse(res, data); } } let tag = new MediaFileTagModel(data.md5sum, requestedTag, false); await app.databaseHelper.insertOne(tag); app.routerUtils.jsonResponse(res, await MediaService.fetchOne(app, req.params.id, req.sessionObj?.accessList)); }); app.router.patch("/api/media/:id/meta/:key", async (req, res) => { app.routerUtils.onApiRequest(req, res); if (!req.params.id ||!req.params.key || !Number.isInteger(req.body?.value?.length)) return app.routerUtils.onBadRequest(res); let data = await MediaService.fetchOne(app, req.params.id, req.sessionObj?.accessList); if (!data || data.accessType !== ACCESS_GRANT.write) return app.routerUtils.onPageNotFound(res); if (!req.body.value) { await app.databaseHelper.remove(MediaFileMetaModel, { md5sum: data.md5sum, key: req.params.key, fromFile: 0 }); } else { let newMediaItemMedia = new MediaFileMetaModel(data.md5sum, req.params.key, req.body.value, false); await app.databaseHelper.upsertOne(newMediaItemMedia); } app.routerUtils.jsonResponse(res, await MediaService.fetchOne(app, req.params.id, req.sessionObj?.accessList)); }); app.router.get("/api/media/list", async (req, res) => { app.routerUtils.onApiRequest(req, res); let first = undefined, last = undefined; if (req.body?.chronology !== undefined) { let range = await MediaService.getMediaRange(app); first = range[0]; last = range[1]; } let fromDate = parseInt(req.body?.from); let count = parseInt(req.body?.count); app.routerUtils.jsonResponse(res, { data: await MediaService.fetchMediasWithAccess( app, isNaN(fromDate) ? 0 : fromDate, isNaN(count) ? 25 : Math.min(150, count), req.sessionObj?.accessList), first: first, last: last }); }); app.router.get("/api/media/:md5sum", async (req, res) => { app.routerUtils.onApiRequest(req, res); let data = await MediaService.fetchOne(app, req.params.md5sum, req.sessionObj?.accessList); if (!data) return app.routerUtils.onPageNotFound(res); app.routerUtils.jsonResponse(res, data); }); app.router.get("/api/media/thumbnail/:md5sum.jpg", async (req, res) => { app.routerUtils.onApiRequest(req, res); let data = await MediaService.fetchOne(app, req.params.md5sum, req.sessionObj?.accessList); if (!data) return app.routerUtils.onPageNotFound(res); try { let thumbnail = null; try { thumbnail = await (await app.libraryManager.findMedia(data.path))?.createThumbnail(req.body?.w || 0, req.body?.h || 0, req.body?.q || 6); } catch (err) { return app.routerUtils.apiError(res); } if (!thumbnail) return app.routerUtils.onPageNotFound(res); res.setHeader("Content-Type", "image/jpeg"); res.setHeader("Content-Length", fs.statSync(thumbnail.name)?.size || undefined); res.setHeader("Cache-Control", "private, max-age=2630000"); // 1 month cache let rd = fs.createReadStream(thumbnail.name); rd.once('end', () => thumbnail.removeCallback()); rd.pipe(res); } catch (err) { console.error(err); app.routerUtils.onPageNotFound(res); } }); app.router.get("/api/media/original/:md5sum", async (req, res) => { app.routerUtils.onApiRequest(req, res); let data = await MediaService.fetchOne(app, req.params.md5sum, req.sessionObj?.accessList); if (!data) return app.routerUtils.onPageNotFound(res); const fileName = Path.basename(data.path); res.setHeader("Content-Type", mime.lookup(data.path)); res.setHeader("Content-Length", fs.statSync(data.path)?.size || undefined); res.setHeader("Content-Disposition", `attachment; filename="${fileName}"`); res.setHeader("Cache-Control", "private, max-age=2630000"); // 1 month cache fs.createReadStream(data.path).pipe(res); }); }};