index.js 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. import express from 'express';
  2. import { MemoryFileBackend, wrapBackend } from './backends/index.js';
  3. import { errorGuard } from '../error.js';
  4. /* ROADMAP
  5. - Add security
  6. */
  7. // In ms
  8. //const FILE_CACHE_EXPIRATION = 60_000;
  9. /**
  10. *
  11. * @param {object} options
  12. */
  13. export const fileStorage = (backend = MemoryFileBackend(), { prefix }) => {
  14. const app = express.Router();
  15. // Store a file
  16. app.post(
  17. `/`,
  18. backend.uploadManager,
  19. errorGuard(async (req, res) => {
  20. const { siteId, boxId, resourceId, file, authenticatedUser } = req;
  21. const wrappedBackend = wrapBackend(backend, siteId, authenticatedUser);
  22. const filename = await wrappedBackend.store(boxId, resourceId, file);
  23. const pathPrefix = `${siteId}/${prefix}/${boxId}/${resourceId}/file`;
  24. res.send(`${pathPrefix}/${filename}`);
  25. })
  26. );
  27. // List stored file under namespace
  28. app.get(
  29. `/`,
  30. errorGuard(async (req, res) => {
  31. const { siteId, boxId, resourceId, authenticatedUser } = req;
  32. const wrappedBackend = wrapBackend(backend, siteId, authenticatedUser);
  33. const result = await wrappedBackend.list(boxId, resourceId);
  34. const pathPrefix = `${siteId}/${prefix}/${boxId}/${resourceId}/file`;
  35. res.json(result.map((filename) => `${pathPrefix}/${filename}`));
  36. })
  37. );
  38. // Get one file
  39. app.get(
  40. `/:filename`,
  41. errorGuard(async (req, res, next) => {
  42. const {
  43. siteId,
  44. boxId,
  45. resourceId,
  46. authenticatedUser,
  47. params: { filename },
  48. } = req;
  49. const wrappedBackend = wrapBackend(backend, siteId, authenticatedUser);
  50. if (!(await wrappedBackend.exists(boxId, resourceId, filename))) {
  51. res.status(404).send('Not found');
  52. return;
  53. }
  54. const {
  55. stream,
  56. redirectTo,
  57. mimetype,
  58. length,
  59. lastModified,
  60. eTag,
  61. statusCode = 200,
  62. } = await wrappedBackend.get(boxId, resourceId, filename, req.headers);
  63. // Here the backend respond with another url so we redirect to it
  64. if (redirectTo) {
  65. res.redirect(redirectTo);
  66. return;
  67. }
  68. if (length !== undefined) {
  69. res.set('Content-Length', length);
  70. }
  71. if (lastModified !== undefined) {
  72. res.set('Last-Modified', lastModified);
  73. }
  74. if (eTag !== undefined) {
  75. res.set('ETag', eTag);
  76. }
  77. res.set('Content-Type', mimetype);
  78. // Set a minimal cache
  79. /* res.setHeader(
  80. 'Cache-Control',
  81. 'public, max-age=' + FILE_CACHE_EXPIRATION / 1000
  82. );
  83. res.setHeader(
  84. 'Expires',
  85. new Date(Date.now() + FILE_CACHE_EXPIRATION).toUTCString()
  86. );*/
  87. if (statusCode < 300) {
  88. res.status(statusCode);
  89. stream.on('error', next).pipe(res);
  90. } else {
  91. if (statusCode === 304) {
  92. res.status(statusCode);
  93. res.end();
  94. } else {
  95. res.status(statusCode);
  96. res.end('Unknown Error');
  97. }
  98. }
  99. })
  100. );
  101. // Delete an entry
  102. app.delete(
  103. `/:filename`,
  104. errorGuard(async (req, res) => {
  105. const {
  106. siteId,
  107. boxId,
  108. resourceId,
  109. authenticatedUser,
  110. params: { filename },
  111. } = req;
  112. const wrappedBackend = wrapBackend(backend, siteId, authenticatedUser);
  113. if (!(await wrappedBackend.exists(boxId, resourceId, filename))) {
  114. res.status(404).send('Not found');
  115. return;
  116. }
  117. await wrappedBackend.delete(boxId, resourceId, filename);
  118. res.json({ message: 'Deleted' });
  119. })
  120. );
  121. return app;
  122. };
  123. export default fileStorage;