1
0

api.js 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. const mime = require("mime-types");
  2. const fs = require('fs');
  3. const Path = require('path');
  4. const Security = require('../src/security.js');
  5. const MediaService = require('../model/mediaService.js');
  6. const MediaFileMetaModel = require('../model/mediaItemMeta.js').MediaFileMetaModel;
  7. const MediaFileTagModel = require('../model/mediaItemTag.js').MediaFileTagModel;
  8. const { AccessModel, ACCESS_TYPE, ACCESS_GRANT } = require('../model/access.js');
  9. function MediaToJson(mediaData) {
  10. if (!mediaData)
  11. return null;
  12. if (mediaData.accessType === ACCESS_GRANT.readNoMeta)
  13. mediaData.meta = {
  14. height: mediaData.meta?.height,
  15. width: mediaData.meta?.width
  16. };
  17. return mediaData;
  18. }
  19. module.exports = { register: app => {
  20. app.router.get("/api/access/list", (req, res) => {
  21. app.routerUtils.onApiRequest(req, res);
  22. app.routerUtils.jsonResponse(res, req.sessionObj?.accessList || {});
  23. });
  24. app.router.post("/api/access/link", async (req, res) => { // /api/access/link, post: { linkIds: [string] (JSON) }
  25. app.routerUtils.onApiRequest(req, res);
  26. if (!req.post?.linkIds?.length)
  27. return app.routerUtils.httpResponse(res, 400, "Missing argument");
  28. try {
  29. for (let i of JSON.parse(req.post.linkIds)) {
  30. if (await app.databaseHelper.findOne(AccessModel, { type: ACCESS_TYPE.link, typeData: i }))
  31. Security.addLinkToSession(req, i);
  32. }
  33. }
  34. catch (err) {
  35. return app.routerUtils.onBadRequest(res);
  36. }
  37. app.routerUtils.jsonResponse(res, req.sessionObj.accessList);
  38. });
  39. app.router.del("/api/access/:id", (req, res) => {
  40. app.routerUtils.onApiRequest(req, res);
  41. const result = Security.removeFromSession(req, req.params.id);
  42. app.routerUtils.jsonResponse(res, result);
  43. });
  44. app.router.post("/api/media/:id/tag/del/:tag", async (req, res) => {
  45. app.routerUtils.onApiRequest(req, res);
  46. if (!req.params.id ||!req.params.tag)
  47. return app.routerUtils.onBadRequest(res);
  48. let checksum = [ req.params.id ];
  49. if (req.params.id === "list") {
  50. if (!req.body?.['list[]'])
  51. return app.routerUtils.onBadRequest(res);
  52. checksum = req.body['list[]'];
  53. }
  54. let data = await MediaService.fetchMultiple(app, checksum, req.sessionObj?.accessList);
  55. data = Object.keys(data).map(x => data[x]).filter(x => x.ACCESS_TYPE != ACCESS_GRANT.write);
  56. await app.databaseHelper.remove(MediaFileTagModel, { md5sum: data.map(x => x.fixedSum), tag: decodeURIComponent(req.params.tag), fromMeta: 0 });
  57. const allMedias = await MediaService.fetchMultiple(app, checksum, req.sessionObj?.accessList);
  58. app.routerUtils.jsonResponse(res, Object.keys(allMedias).map(x => allMedias[x]).map(x => MediaToJson(x)));
  59. });
  60. app.router.put("/api/media/:id/tag", async (req, res) => {
  61. app.routerUtils.onApiRequest(req, res);
  62. const requestedTag = req.body?.tag;
  63. if (!req.params.id ||!requestedTag)
  64. return app.routerUtils.onBadRequest(res);
  65. let checksum = [ req.params.id ];
  66. if (req.params.id === "list") {
  67. if (!req.body?.['list[]'])
  68. return app.routerUtils.onBadRequest(res);
  69. checksum = req.body['list[]'];
  70. }
  71. let data = await MediaService.fetchMultiple(app, checksum, req.sessionObj?.accessList);
  72. data = Object.keys(data)
  73. .map(x => data[x])
  74. .filter(x => {
  75. if (x.ACCESS_TYPE != ACCESS_GRANT.write)
  76. return true;
  77. for (let existingTag of [...x.tags, ...x.fixedTags]) {
  78. if (existingTag === requestedTag || existingTag.startsWith(`${requestedTag}/`)) {
  79. return true;
  80. }
  81. }
  82. });
  83. let tag = data.map(x => new MediaFileTagModel(x.fixedSum, requestedTag, false));
  84. await app.databaseHelper.insertMultipleSameTable(tag);
  85. const allMedias = await MediaService.fetchMultiple(app, checksum, req.sessionObj?.accessList);
  86. app.routerUtils.jsonResponse(res, Object.keys(allMedias).map(x => allMedias[x]).map(x => MediaToJson(x)));
  87. });
  88. app.router.patch("/api/media/:id/meta/:key", async (req, res) => {
  89. app.routerUtils.onApiRequest(req, res);
  90. if (!req.params.id ||!req.params.key || !Number.isInteger(req.body?.value?.length))
  91. return app.routerUtils.onBadRequest(res);
  92. let data = await MediaService.fetchOne(app, req.params.id, req.sessionObj?.accessList);
  93. if (!data || data.accessType !== ACCESS_GRANT.write)
  94. return app.routerUtils.onPageNotFound(res);
  95. if (!req.body.value) {
  96. await app.databaseHelper.remove(MediaFileMetaModel, { md5sum: data.fixedSum, key: req.params.key, fromFile: 0 });
  97. } else {
  98. let newMediaItemMedia = new MediaFileMetaModel(data.fixedSum, req.params.key, req.body.value, false);
  99. await app.databaseHelper.upsertOne(newMediaItemMedia);
  100. }
  101. app.routerUtils.jsonResponse(res, MediaToJson(await MediaService.fetchOne(app, req.params.id, req.sessionObj?.accessList)));
  102. });
  103. app.router.get("/api/media/list", async (req, res) => {
  104. app.routerUtils.onApiRequest(req, res);
  105. let first = undefined,
  106. last = undefined;
  107. if (req.body?.chronology !== undefined) {
  108. let range = await MediaService.getMediaRange(app);
  109. first = range[0];
  110. last = range[1];
  111. }
  112. let fromDate = parseInt(req.body?.from);
  113. let count = parseInt(req.body?.count);
  114. app.routerUtils.jsonResponse(res, {
  115. data: (await MediaService.fetchMediasWithAccess(
  116. app,
  117. isNaN(fromDate) ? 0 : fromDate,
  118. isNaN(count) ? 25 : Math.min(350, count),
  119. req.sessionObj?.accessList)).map(MediaToJson),
  120. first: first,
  121. last: last
  122. });
  123. });
  124. app.router.get("/api/media/:md5sum", async (req, res) => {
  125. app.routerUtils.onApiRequest(req, res);
  126. let data = MediaToJson(await MediaService.fetchOne(app, req.params.md5sum, req.sessionObj?.accessList));
  127. if (!data)
  128. return app.routerUtils.onPageNotFound(res);
  129. app.routerUtils.jsonResponse(res, data);
  130. });
  131. app.router.get("/api/media/thumbnail/:md5sum.jpg", async (req, res) => {
  132. app.routerUtils.onApiRequest(req, res);
  133. let data = await MediaService.fetchOne(app, req.params.md5sum, req.sessionObj?.accessList);
  134. if (!data)
  135. return app.routerUtils.onPageNotFound(res);
  136. try {
  137. let thumbnail = null;
  138. req.body = req.body || {};
  139. req.body.w = parseInt(req.body.w || 0);
  140. req.body.h = parseInt(req.body.h || 0);
  141. req.body.q = parseInt(req.body.q || 6);
  142. try {
  143. thumbnail = await (await app.libraryManager.findMedia(data.path))?.createThumbnail(req.body.w, req.body.h, req.body.q);
  144. } catch (err) {
  145. return app.routerUtils.apiError(res);
  146. }
  147. if (!thumbnail)
  148. return app.routerUtils.onPageNotFound(res);
  149. res.setHeader("Content-Type", "image/jpeg");
  150. res.setHeader("Content-Length", fs.statSync(thumbnail.name)?.size || undefined);
  151. res.setHeader("Cache-Control", "private, max-age=2630000"); // 1 month cache
  152. let rd = fs.createReadStream(thumbnail.name);
  153. rd.once('end', () => thumbnail.removeCallback());
  154. rd.pipe(res);
  155. }
  156. catch (err) {
  157. console.error(err);
  158. app.routerUtils.onPageNotFound(res);
  159. }
  160. });
  161. app.router.get("/api/media/original/:md5sum", async (req, res) => {
  162. app.routerUtils.onApiRequest(req, res);
  163. let data = await MediaService.fetchOne(app, req.params.md5sum, req.sessionObj?.accessList);
  164. if (!data)
  165. return app.routerUtils.onPageNotFound(res);
  166. const fileName = Path.basename(data.path);
  167. res.setHeader("Cache-Control", "private, max-age=2630000"); // 1 month cache
  168. if (data.accessType === ACCESS_GRANT.readNoMeta || req.body?.trim !== undefined) {
  169. console.log("remove meta");//-> trim metadata
  170. }
  171. res.setHeader("Content-Disposition", `attachment; filename="${fileName}"`);
  172. res.setHeader("Content-Type", mime.lookup(data.path));
  173. res.setHeader("Content-Length", fs.statSync(data.path)?.size || undefined);
  174. fs.createReadStream(data.path).pipe(res);
  175. });
  176. }};