@flusys/nestjs-storage 7.1.0 → 8.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (202) hide show
  1. package/config/index.d.ts +3 -3
  2. package/controllers/file-manager.controller.d.ts +4 -4
  3. package/controllers/folder.controller.d.ts +2 -2
  4. package/controllers/index.d.ts +4 -4
  5. package/controllers/storage-config.controller.d.ts +2 -2
  6. package/controllers/upload.controller.d.ts +3 -3
  7. package/docs/index.d.ts +1 -1
  8. package/docs/storage-swagger.config.d.ts +2 -2
  9. package/dtos/index.d.ts +4 -4
  10. package/entities/file-manager-with-company.entity.d.ts +2 -2
  11. package/entities/file-manager.entity.d.ts +1 -1
  12. package/entities/folder-with-company.entity.d.ts +1 -1
  13. package/entities/index.d.ts +15 -15
  14. package/entities/storage-config-with-company.entity.d.ts +1 -1
  15. package/enums/index.d.ts +1 -1
  16. package/fesm/493.js +2768 -0
  17. package/fesm/537.js +1157 -0
  18. package/fesm/562.js +786 -0
  19. package/fesm/{providers/azure-provider.optional.js → chunks/190.js} +33 -30
  20. package/fesm/chunks/252.js +12 -0
  21. package/fesm/chunks/327.js +12 -0
  22. package/fesm/chunks/385.js +12 -0
  23. package/fesm/{providers/s3-provider.optional.js → chunks/591.js} +37 -34
  24. package/fesm/chunks/746.js +12 -0
  25. package/fesm/chunks/798.js +12 -0
  26. package/fesm/{providers/sftp-provider.optional.js → chunks/918.js} +35 -31
  27. package/fesm/config/index.js +119 -3
  28. package/fesm/controllers/index.js +16 -4
  29. package/fesm/docs/index.js +206 -1
  30. package/fesm/dtos/index.js +879 -4
  31. package/fesm/entities/index.js +463 -18
  32. package/fesm/enums/index.js +36 -1
  33. package/fesm/index.js +398 -12
  34. package/fesm/interfaces/index.js +19 -5
  35. package/fesm/modules/index.js +16 -1
  36. package/fesm/providers/index.js +364 -3
  37. package/fesm/runtime.js +252 -0
  38. package/fesm/services/index.js +16 -6
  39. package/fesm/utils/index.js +149 -1
  40. package/index.d.ts +11 -11
  41. package/interfaces/file-manager.interface.d.ts +1 -1
  42. package/interfaces/folder.interface.d.ts +1 -1
  43. package/interfaces/index.d.ts +5 -5
  44. package/interfaces/storage-config.interface.d.ts +1 -1
  45. package/interfaces/storage-module-options.interface.d.ts +3 -3
  46. package/interfaces/storage-provider.interface.d.ts +1 -1
  47. package/middlewares/file-serve.middleware.d.ts +4 -3
  48. package/middlewares/index.d.ts +1 -1
  49. package/modules/index.d.ts +1 -1
  50. package/modules/storage.module.d.ts +2 -2
  51. package/package.json +30 -41
  52. package/providers/azure-provider.optional.d.ts +2 -2
  53. package/providers/index.d.ts +3 -3
  54. package/providers/local-provider.d.ts +2 -2
  55. package/providers/s3-provider.optional.d.ts +2 -2
  56. package/providers/sftp-provider.optional.d.ts +2 -2
  57. package/providers/storage-factory.service.d.ts +3 -3
  58. package/providers/storage-provider.registry.d.ts +2 -2
  59. package/services/file-manager.service.d.ts +9 -8
  60. package/services/folder.service.d.ts +6 -6
  61. package/services/index.d.ts +6 -6
  62. package/services/storage-config.service.d.ts +3 -3
  63. package/services/storage-datasource.provider.d.ts +3 -3
  64. package/services/storage-provider-config.service.d.ts +7 -7
  65. package/services/upload.service.d.ts +6 -6
  66. package/utils/image-compressor.util.d.ts +1 -1
  67. package/utils/index.d.ts +1 -1
  68. package/cjs/config/event-actions.js +0 -34
  69. package/cjs/config/index.js +0 -20
  70. package/cjs/config/message-keys.js +0 -46
  71. package/cjs/config/storage.constants.js +0 -28
  72. package/cjs/controllers/file-manager.controller.js +0 -152
  73. package/cjs/controllers/file-manager.controller.spec.js +0 -135
  74. package/cjs/controllers/folder.controller.js +0 -102
  75. package/cjs/controllers/folder.controller.spec.js +0 -86
  76. package/cjs/controllers/index.js +0 -21
  77. package/cjs/controllers/storage-config.controller.js +0 -102
  78. package/cjs/controllers/storage-config.controller.spec.js +0 -85
  79. package/cjs/controllers/upload.controller.js +0 -234
  80. package/cjs/controllers/upload.controller.spec.js +0 -132
  81. package/cjs/docs/index.js +0 -11
  82. package/cjs/docs/storage-swagger.config.js +0 -188
  83. package/cjs/docs/storage-swagger.config.spec.js +0 -72
  84. package/cjs/dtos/file-manager.dto.js +0 -327
  85. package/cjs/dtos/folder.dto.js +0 -105
  86. package/cjs/dtos/index.js +0 -21
  87. package/cjs/dtos/storage-config.dto.js +0 -170
  88. package/cjs/dtos/upload.dto.js +0 -272
  89. package/cjs/entities/file-manager-with-company.entity.js +0 -61
  90. package/cjs/entities/file-manager.entity.js +0 -122
  91. package/cjs/entities/folder-with-company.entity.js +0 -55
  92. package/cjs/entities/folder.entity.js +0 -64
  93. package/cjs/entities/index.js +0 -63
  94. package/cjs/entities/index.spec.js +0 -18
  95. package/cjs/entities/storage-config-with-company.entity.js +0 -51
  96. package/cjs/entities/storage-config.entity.js +0 -96
  97. package/cjs/enums/file-location.enum.js +0 -17
  98. package/cjs/enums/index.js +0 -18
  99. package/cjs/index.js +0 -29
  100. package/cjs/interfaces/file-manager.interface.js +0 -4
  101. package/cjs/interfaces/folder.interface.js +0 -4
  102. package/cjs/interfaces/index.js +0 -22
  103. package/cjs/interfaces/storage-config.interface.js +0 -4
  104. package/cjs/interfaces/storage-module-options.interface.js +0 -4
  105. package/cjs/interfaces/storage-provider.interface.js +0 -4
  106. package/cjs/middlewares/file-serve.middleware.js +0 -241
  107. package/cjs/middlewares/file-serve.middleware.spec.js +0 -211
  108. package/cjs/middlewares/index.js +0 -18
  109. package/cjs/modules/index.js +0 -18
  110. package/cjs/modules/storage.module.js +0 -165
  111. package/cjs/modules/storage.module.spec.js +0 -184
  112. package/cjs/providers/azure-provider.optional.js +0 -155
  113. package/cjs/providers/azure-provider.optional.spec.js +0 -228
  114. package/cjs/providers/index.js +0 -26
  115. package/cjs/providers/local-provider.js +0 -230
  116. package/cjs/providers/local-provider.spec.js +0 -239
  117. package/cjs/providers/s3-provider.optional.js +0 -175
  118. package/cjs/providers/s3-provider.optional.spec.js +0 -243
  119. package/cjs/providers/sftp-provider.optional.js +0 -203
  120. package/cjs/providers/sftp-provider.optional.spec.js +0 -217
  121. package/cjs/providers/storage-factory.service.js +0 -175
  122. package/cjs/providers/storage-factory.service.spec.js +0 -253
  123. package/cjs/providers/storage-provider.registry.js +0 -41
  124. package/cjs/providers/storage-provider.registry.spec.js +0 -67
  125. package/cjs/services/file-manager.service.js +0 -387
  126. package/cjs/services/file-manager.service.spec.js +0 -580
  127. package/cjs/services/folder.service.js +0 -101
  128. package/cjs/services/folder.service.spec.js +0 -167
  129. package/cjs/services/index.js +0 -23
  130. package/cjs/services/storage-config.service.js +0 -129
  131. package/cjs/services/storage-config.service.spec.js +0 -257
  132. package/cjs/services/storage-datasource.provider.js +0 -137
  133. package/cjs/services/storage-datasource.provider.spec.js +0 -217
  134. package/cjs/services/storage-provider-config.service.js +0 -171
  135. package/cjs/services/storage-provider-config.service.spec.js +0 -265
  136. package/cjs/services/upload.service.js +0 -376
  137. package/cjs/services/upload.service.spec.js +0 -426
  138. package/cjs/utils/file-validator.util.js +0 -440
  139. package/cjs/utils/file-validator.util.spec.js +0 -259
  140. package/cjs/utils/image-compressor.util.js +0 -162
  141. package/cjs/utils/image-compressor.util.spec.js +0 -166
  142. package/cjs/utils/index.js +0 -18
  143. package/fesm/config/event-actions.js +0 -13
  144. package/fesm/config/message-keys.js +0 -22
  145. package/fesm/config/storage.constants.js +0 -7
  146. package/fesm/controllers/file-manager.controller.js +0 -142
  147. package/fesm/controllers/file-manager.controller.spec.js +0 -131
  148. package/fesm/controllers/folder.controller.js +0 -92
  149. package/fesm/controllers/folder.controller.spec.js +0 -82
  150. package/fesm/controllers/storage-config.controller.js +0 -92
  151. package/fesm/controllers/storage-config.controller.spec.js +0 -81
  152. package/fesm/controllers/upload.controller.js +0 -224
  153. package/fesm/controllers/upload.controller.spec.js +0 -128
  154. package/fesm/docs/storage-swagger.config.js +0 -181
  155. package/fesm/docs/storage-swagger.config.spec.js +0 -68
  156. package/fesm/dtos/file-manager.dto.js +0 -300
  157. package/fesm/dtos/folder.dto.js +0 -84
  158. package/fesm/dtos/storage-config.dto.js +0 -149
  159. package/fesm/dtos/upload.dto.js +0 -245
  160. package/fesm/entities/file-manager-with-company.entity.js +0 -51
  161. package/fesm/entities/file-manager.entity.js +0 -112
  162. package/fesm/entities/folder-with-company.entity.js +0 -45
  163. package/fesm/entities/folder.entity.js +0 -54
  164. package/fesm/entities/index.spec.js +0 -14
  165. package/fesm/entities/storage-config-with-company.entity.js +0 -41
  166. package/fesm/entities/storage-config.entity.js +0 -86
  167. package/fesm/enums/file-location.enum.js +0 -7
  168. package/fesm/interfaces/file-manager.interface.js +0 -1
  169. package/fesm/interfaces/folder.interface.js +0 -1
  170. package/fesm/interfaces/storage-config.interface.js +0 -1
  171. package/fesm/interfaces/storage-module-options.interface.js +0 -3
  172. package/fesm/interfaces/storage-provider.interface.js +0 -4
  173. package/fesm/middlewares/file-serve.middleware.js +0 -190
  174. package/fesm/middlewares/file-serve.middleware.spec.js +0 -207
  175. package/fesm/middlewares/index.js +0 -1
  176. package/fesm/modules/storage.module.js +0 -156
  177. package/fesm/modules/storage.module.spec.js +0 -180
  178. package/fesm/providers/azure-provider.optional.spec.js +0 -224
  179. package/fesm/providers/local-provider.js +0 -182
  180. package/fesm/providers/local-provider.spec.js +0 -194
  181. package/fesm/providers/s3-provider.optional.spec.js +0 -239
  182. package/fesm/providers/sftp-provider.optional.spec.js +0 -213
  183. package/fesm/providers/storage-factory.service.js +0 -124
  184. package/fesm/providers/storage-factory.service.spec.js +0 -249
  185. package/fesm/providers/storage-provider.registry.js +0 -34
  186. package/fesm/providers/storage-provider.registry.spec.js +0 -63
  187. package/fesm/services/file-manager.service.js +0 -336
  188. package/fesm/services/file-manager.service.spec.js +0 -576
  189. package/fesm/services/folder.service.js +0 -91
  190. package/fesm/services/folder.service.spec.js +0 -163
  191. package/fesm/services/storage-config.service.js +0 -119
  192. package/fesm/services/storage-config.service.spec.js +0 -253
  193. package/fesm/services/storage-datasource.provider.js +0 -86
  194. package/fesm/services/storage-datasource.provider.spec.js +0 -172
  195. package/fesm/services/storage-provider-config.service.js +0 -161
  196. package/fesm/services/storage-provider-config.service.spec.js +0 -261
  197. package/fesm/services/upload.service.js +0 -366
  198. package/fesm/services/upload.service.spec.js +0 -422
  199. package/fesm/utils/file-validator.util.js +0 -433
  200. package/fesm/utils/file-validator.util.spec.js +0 -255
  201. package/fesm/utils/image-compressor.util.js +0 -114
  202. package/fesm/utils/image-compressor.util.spec.js +0 -162
@@ -1,54 +0,0 @@
1
- function _define_property(obj, key, value) {
2
- if (key in obj) {
3
- Object.defineProperty(obj, key, {
4
- value: value,
5
- enumerable: true,
6
- configurable: true,
7
- writable: true
8
- });
9
- } else {
10
- obj[key] = value;
11
- }
12
- return obj;
13
- }
14
- function _ts_decorate(decorators, target, key, desc) {
15
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
16
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
17
- else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
18
- return c > 3 && r && Object.defineProperty(target, key, r), r;
19
- }
20
- function _ts_metadata(k, v) {
21
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
22
- }
23
- import { Identity } from '@flusys/nestjs-shared/entities';
24
- import { Column, Entity, OneToMany } from 'typeorm';
25
- export class Folder extends Identity {
26
- constructor(...args){
27
- super(...args), _define_property(this, "name", void 0), _define_property(this, "slug", void 0), _define_property(this, "fileManager", void 0);
28
- }
29
- }
30
- _ts_decorate([
31
- Column({
32
- type: 'varchar',
33
- length: 255,
34
- nullable: false
35
- }),
36
- _ts_metadata("design:type", String)
37
- ], Folder.prototype, "name", void 0);
38
- _ts_decorate([
39
- Column({
40
- type: 'varchar',
41
- length: 255,
42
- nullable: false
43
- }),
44
- _ts_metadata("design:type", String)
45
- ], Folder.prototype, "slug", void 0);
46
- _ts_decorate([
47
- OneToMany('FileManager', (fileManager)=>fileManager.folder),
48
- _ts_metadata("design:type", Array)
49
- ], Folder.prototype, "fileManager", void 0);
50
- Folder = _ts_decorate([
51
- Entity({
52
- name: 'folder'
53
- })
54
- ], Folder);
@@ -1,14 +0,0 @@
1
- import { getStorageEntitiesByConfig, StorageCoreEntities, StorageCompanyEntities } from './index';
2
- describe('getStorageEntitiesByConfig', ()=>{
3
- it('returns the core entities when the company feature is disabled', ()=>{
4
- expect(getStorageEntitiesByConfig(false)).toBe(StorageCoreEntities);
5
- });
6
- it('returns the company-scoped entities when the company feature is enabled', ()=>{
7
- expect(getStorageEntitiesByConfig(true)).toBe(StorageCompanyEntities);
8
- });
9
- it('core and company entity sets are disjoint (never mixed)', ()=>{
10
- const core = getStorageEntitiesByConfig(false);
11
- const company = getStorageEntitiesByConfig(true);
12
- expect(core.some((e)=>company.includes(e))).toBe(false);
13
- });
14
- });
@@ -1,41 +0,0 @@
1
- function _define_property(obj, key, value) {
2
- if (key in obj) {
3
- Object.defineProperty(obj, key, {
4
- value: value,
5
- enumerable: true,
6
- configurable: true,
7
- writable: true
8
- });
9
- } else {
10
- obj[key] = value;
11
- }
12
- return obj;
13
- }
14
- function _ts_decorate(decorators, target, key, desc) {
15
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
16
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
17
- else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
18
- return c > 3 && r && Object.defineProperty(target, key, r), r;
19
- }
20
- function _ts_metadata(k, v) {
21
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
22
- }
23
- import { Column, Entity } from 'typeorm';
24
- import { StorageConfig } from './storage-config.entity';
25
- export class StorageConfigWithCompany extends StorageConfig {
26
- constructor(...args){
27
- super(...args), _define_property(this, "companyId", void 0);
28
- }
29
- }
30
- _ts_decorate([
31
- Column({
32
- type: 'uuid',
33
- nullable: true
34
- }),
35
- _ts_metadata("design:type", Object)
36
- ], StorageConfigWithCompany.prototype, "companyId", void 0);
37
- StorageConfigWithCompany = _ts_decorate([
38
- Entity({
39
- name: 'storage_config'
40
- })
41
- ], StorageConfigWithCompany);
@@ -1,86 +0,0 @@
1
- function _define_property(obj, key, value) {
2
- if (key in obj) {
3
- Object.defineProperty(obj, key, {
4
- value: value,
5
- enumerable: true,
6
- configurable: true,
7
- writable: true
8
- });
9
- } else {
10
- obj[key] = value;
11
- }
12
- return obj;
13
- }
14
- function _ts_decorate(decorators, target, key, desc) {
15
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
16
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
17
- else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
18
- return c > 3 && r && Object.defineProperty(target, key, r), r;
19
- }
20
- function _ts_metadata(k, v) {
21
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
22
- }
23
- import { getJsonColumnType } from '@flusys/nestjs-shared';
24
- import { Identity } from '@flusys/nestjs-shared/entities';
25
- import { Column, Entity, Index } from 'typeorm';
26
- export class StorageConfig extends Identity {
27
- constructor(...args){
28
- super(...args), _define_property(this, "name", void 0), _define_property(this, "storage", void 0), _define_property(this, "config", void 0), _define_property(this, "isActive", void 0), _define_property(this, "isDefault", void 0);
29
- }
30
- }
31
- _ts_decorate([
32
- Column({
33
- type: 'varchar',
34
- length: 255,
35
- nullable: false
36
- }),
37
- _ts_metadata("design:type", String)
38
- ], StorageConfig.prototype, "name", void 0);
39
- _ts_decorate([
40
- Column({
41
- type: 'varchar',
42
- length: 50,
43
- nullable: false
44
- }),
45
- _ts_metadata("design:type", String)
46
- ], StorageConfig.prototype, "storage", void 0);
47
- _ts_decorate([
48
- Column({
49
- type: getJsonColumnType(),
50
- nullable: false
51
- }),
52
- _ts_metadata("design:type", typeof Record === "undefined" ? Object : Record)
53
- ], StorageConfig.prototype, "config", void 0);
54
- _ts_decorate([
55
- Column({
56
- type: 'boolean',
57
- default: true,
58
- name: 'is_active'
59
- }),
60
- _ts_metadata("design:type", Boolean)
61
- ], StorageConfig.prototype, "isActive", void 0);
62
- _ts_decorate([
63
- Column({
64
- type: 'boolean',
65
- default: false,
66
- name: 'is_default'
67
- }),
68
- _ts_metadata("design:type", Boolean)
69
- ], StorageConfig.prototype, "isDefault", void 0);
70
- StorageConfig = _ts_decorate([
71
- Entity({
72
- name: 'storage_config'
73
- }),
74
- Index([
75
- 'name'
76
- ]),
77
- Index([
78
- 'storage'
79
- ]),
80
- Index([
81
- 'isActive'
82
- ]),
83
- Index([
84
- 'isDefault'
85
- ])
86
- ], StorageConfig);
@@ -1,7 +0,0 @@
1
- export var FileLocationEnum = /*#__PURE__*/ function(FileLocationEnum) {
2
- FileLocationEnum["AWS"] = "aws";
3
- FileLocationEnum["AZURE"] = "azure";
4
- FileLocationEnum["SFTP"] = "sftp";
5
- FileLocationEnum["LOCAL"] = "local";
6
- return FileLocationEnum;
7
- }({});
@@ -1 +0,0 @@
1
- export { };
@@ -1 +0,0 @@
1
- export { };
@@ -1 +0,0 @@
1
- export { };
@@ -1,3 +0,0 @@
1
- /**
2
- * Async options for StorageModule registration
3
- */ export { };
@@ -1,4 +0,0 @@
1
- /**
2
- * Storage Provider Interface
3
- * All storage providers must implement this interface
4
- */ export { };
@@ -1,190 +0,0 @@
1
- function _define_property(obj, key, value) {
2
- if (key in obj) {
3
- Object.defineProperty(obj, key, {
4
- value: value,
5
- enumerable: true,
6
- configurable: true,
7
- writable: true
8
- });
9
- } else {
10
- obj[key] = value;
11
- }
12
- return obj;
13
- }
14
- function _ts_decorate(decorators, target, key, desc) {
15
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
16
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
17
- else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
18
- return c > 3 && r && Object.defineProperty(target, key, r), r;
19
- }
20
- function _ts_metadata(k, v) {
21
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
22
- }
23
- function _ts_param(paramIndex, decorator) {
24
- return function(target, key) {
25
- decorator(target, key, paramIndex);
26
- };
27
- }
28
- import { Injectable, NotFoundException, BadRequestException, Inject } from '@nestjs/common';
29
- import { createReadStream, existsSync, statSync } from 'fs';
30
- import { join, resolve } from 'path';
31
- import * as mime from 'mime-types';
32
- import { FILE_MESSAGES, UPLOAD_MESSAGES } from '../config';
33
- import { UploadService } from '../services/upload.service';
34
- /** MIME type prefixes that can be displayed inline in browser */ const VIEWABLE_TYPE_PREFIXES = [
35
- 'image/',
36
- 'video/',
37
- 'audio/',
38
- 'text/',
39
- 'application/pdf',
40
- 'application/json',
41
- 'application/xml'
42
- ];
43
- export class FileServeMiddleware {
44
- async use(req, res, next) {
45
- try {
46
- const normalizedPath = this.extractFilePath(req);
47
- const fullPath = await this.resolveFilePath(normalizedPath);
48
- const stats = statSync(fullPath);
49
- if (!stats.isFile()) {
50
- throw new NotFoundException({
51
- message: `Not a file: ${normalizedPath}`,
52
- messageKey: FILE_MESSAGES.NOT_FOUND,
53
- messageVariables: {
54
- path: normalizedPath
55
- }
56
- });
57
- }
58
- const mimeType = this.getMimeType(fullPath);
59
- this.setResponseHeaders(res, stats.size, mimeType, normalizedPath);
60
- this.streamFile(res, fullPath, normalizedPath);
61
- } catch (error) {
62
- this.sendErrorResponse(res, error);
63
- }
64
- }
65
- /** Extract and validate file path from request URL */ extractFilePath(req) {
66
- const urlPath = req.path || req.url;
67
- const match = urlPath.match(/\/storage\/upload\/file\/(.+)/);
68
- const normalizedPath = match ? match[1] : '';
69
- if (!normalizedPath) {
70
- throw new BadRequestException({
71
- message: 'File path is required',
72
- messageKey: UPLOAD_MESSAGES.NO_FILE_PATH
73
- });
74
- }
75
- return normalizedPath;
76
- }
77
- /** Resolve file path using multiple fallback strategies */ async resolveFilePath(normalizedPath) {
78
- // Strategy 1: Path relative to CWD (new format: key includes basePath)
79
- let fullPath = join(this.uploadDir, normalizedPath);
80
- const resolvedPath = resolve(fullPath);
81
- // Prevent path traversal attacks
82
- if (!resolvedPath.startsWith(this.uploadDir)) {
83
- throw new BadRequestException({
84
- message: 'Invalid file path',
85
- messageKey: UPLOAD_MESSAGES.INVALID_FILE_PATH
86
- });
87
- }
88
- if (existsSync(fullPath)) {
89
- return fullPath;
90
- }
91
- // Try fallback strategies with basePath
92
- const basePath = await this.uploadService.getLocalStorageBasePath();
93
- if (basePath) {
94
- const fallbackPath = this.tryFallbackPaths(normalizedPath, basePath);
95
- if (fallbackPath) {
96
- return fallbackPath;
97
- }
98
- }
99
- throw new NotFoundException({
100
- message: `File not found: ${normalizedPath}`,
101
- messageKey: FILE_MESSAGES.NOT_FOUND,
102
- messageVariables: {
103
- path: normalizedPath
104
- }
105
- });
106
- }
107
- /** Try basePath-based fallback strategies */ tryFallbackPaths(normalizedPath, basePath) {
108
- const normalizedBasePath = basePath.replace(/^\.\//, '').replace(/\/$/, '');
109
- const pathStartsWithBase = normalizedPath.startsWith(normalizedBasePath + '/') || normalizedPath.startsWith(normalizedBasePath);
110
- if (pathStartsWithBase) {
111
- // Strategy 2: basePath + remaining path
112
- const remainingPath = normalizedPath.substring(normalizedBasePath.length).replace(/^\//, '');
113
- const fallbackPath = join(basePath, remainingPath);
114
- if (existsSync(fallbackPath)) return fallbackPath;
115
- } else {
116
- // Strategy 3: Old format - prepend basePath
117
- const fallbackPath = join(basePath, normalizedPath);
118
- if (existsSync(fallbackPath)) return fallbackPath;
119
- }
120
- return null;
121
- }
122
- /** Get MIME type with SVG special handling */ getMimeType(fullPath) {
123
- if (fullPath.toLowerCase().endsWith('.svg')) {
124
- return 'image/svg+xml';
125
- }
126
- return mime.lookup(fullPath) || 'application/octet-stream';
127
- }
128
- /** Set response headers for file serving */ setResponseHeaders(res, size, mimeType, normalizedPath) {
129
- const isViewable = VIEWABLE_TYPE_PREFIXES.some((type)=>mimeType.startsWith(type));
130
- const filename = normalizedPath.split('/').pop() || 'download';
131
- const contentDisposition = isViewable ? 'inline' : `attachment; filename="${encodeURIComponent(filename)}"`;
132
- res.set({
133
- 'Content-Type': mimeType,
134
- 'Content-Length': size,
135
- 'Content-Disposition': contentDisposition,
136
- 'Cache-Control': 'public, max-age=3600',
137
- 'Accept-Ranges': 'bytes',
138
- 'Cross-Origin-Resource-Policy': 'cross-origin',
139
- 'Access-Control-Allow-Origin': '*',
140
- 'X-Content-Type-Options': 'nosniff'
141
- });
142
- }
143
- /** Stream file to response with error handling */ streamFile(res, fullPath, normalizedPath) {
144
- const stream = createReadStream(fullPath);
145
- stream.pipe(res);
146
- stream.on('error', ()=>{
147
- if (!res.headersSent) {
148
- this.sendErrorResponse(res, new NotFoundException({
149
- message: `Failed to serve file: ${normalizedPath}`,
150
- messageKey: FILE_MESSAGES.NOT_FOUND,
151
- messageVariables: {
152
- path: normalizedPath
153
- }
154
- }));
155
- }
156
- });
157
- res.on('close', ()=>stream.destroy());
158
- }
159
- /** Send consistent error response */ sendErrorResponse(res, error) {
160
- if (!res.headersSent) {
161
- const isNotFound = error instanceof NotFoundException;
162
- const response = isNotFound ? error.getResponse() : null;
163
- const message = response?.message ?? 'File not found';
164
- const messageKey = response?.messageKey ?? FILE_MESSAGES.NOT_FOUND;
165
- const messageVariables = response?.messageVariables;
166
- res.status(404).json({
167
- success: false,
168
- message,
169
- messageKey,
170
- messageVariables,
171
- error: 'Not Found',
172
- statusCode: 404
173
- });
174
- }
175
- }
176
- constructor(uploadService){
177
- _define_property(this, "uploadService", void 0);
178
- _define_property(this, "uploadDir", void 0);
179
- this.uploadService = uploadService;
180
- this.uploadDir = process.cwd();
181
- }
182
- }
183
- FileServeMiddleware = _ts_decorate([
184
- Injectable(),
185
- _ts_param(0, Inject(UploadService)),
186
- _ts_metadata("design:type", Function),
187
- _ts_metadata("design:paramtypes", [
188
- typeof UploadService === "undefined" ? Object : UploadService
189
- ])
190
- ], FileServeMiddleware);
@@ -1,207 +0,0 @@
1
- function _define_property(obj, key, value) {
2
- if (key in obj) {
3
- Object.defineProperty(obj, key, {
4
- value: value,
5
- enumerable: true,
6
- configurable: true,
7
- writable: true
8
- });
9
- } else {
10
- obj[key] = value;
11
- }
12
- return obj;
13
- }
14
- import { EventEmitter } from 'events';
15
- import { join } from 'path';
16
- const mockCreateReadStream = jest.fn();
17
- const mockExistsSync = jest.fn();
18
- const mockStatSync = jest.fn();
19
- jest.mock('fs', ()=>({
20
- ...jest.requireActual('fs'),
21
- createReadStream: (...args)=>mockCreateReadStream(...args),
22
- existsSync: (...args)=>mockExistsSync(...args),
23
- statSync: (...args)=>mockStatSync(...args)
24
- }));
25
- const mockMimeLookup = jest.fn();
26
- jest.mock('mime-types', ()=>({
27
- lookup: (...args)=>mockMimeLookup(...args)
28
- }));
29
- import { FileServeMiddleware } from './file-serve.middleware';
30
- let FakeStream = class FakeStream extends EventEmitter {
31
- constructor(...args){
32
- super(...args), _define_property(this, "pipe", jest.fn()), _define_property(this, "destroy", jest.fn());
33
- }
34
- };
35
- function buildRes() {
36
- const listeners = {};
37
- return {
38
- headersSent: false,
39
- set: jest.fn(),
40
- status: jest.fn().mockReturnThis(),
41
- json: jest.fn(),
42
- on: jest.fn((event, cb)=>{
43
- listeners[event] = listeners[event] || [];
44
- listeners[event].push(cb);
45
- }),
46
- emitClose: ()=>(listeners['close'] || []).forEach((cb)=>cb())
47
- };
48
- }
49
- function buildReq(urlPath) {
50
- return {
51
- path: urlPath,
52
- url: urlPath
53
- };
54
- }
55
- describe('FileServeMiddleware', ()=>{
56
- let middleware;
57
- let mockUploadService;
58
- let next;
59
- beforeEach(()=>{
60
- jest.clearAllMocks();
61
- mockUploadService = {
62
- getLocalStorageBasePath: jest.fn().mockResolvedValue(null)
63
- };
64
- middleware = new FileServeMiddleware(mockUploadService);
65
- next = jest.fn();
66
- });
67
- it('responds 404 when the file path segment is missing from the URL', async ()=>{
68
- const res = buildRes();
69
- await middleware.use(buildReq('/storage/upload/file/'), res, next);
70
- expect(res.status).toHaveBeenCalledWith(404);
71
- expect(res.json).toHaveBeenCalledWith(expect.objectContaining({
72
- success: false,
73
- statusCode: 404
74
- }));
75
- expect(mockExistsSync).not.toHaveBeenCalled();
76
- });
77
- it('blocks path traversal attempts that resolve outside the upload directory', async ()=>{
78
- const res = buildRes();
79
- mockExistsSync.mockReturnValue(false);
80
- await middleware.use(buildReq('/storage/upload/file/../../etc/passwd'), res, next);
81
- expect(res.status).toHaveBeenCalledWith(404);
82
- expect(mockStatSync).not.toHaveBeenCalled();
83
- });
84
- it('serves a file that exists directly under the upload directory with inline disposition for viewable types', async ()=>{
85
- const res = buildRes();
86
- mockExistsSync.mockReturnValue(true);
87
- mockStatSync.mockReturnValue({
88
- isFile: ()=>true,
89
- size: 1234
90
- });
91
- mockMimeLookup.mockReturnValue('application/pdf');
92
- const stream = new FakeStream();
93
- mockCreateReadStream.mockReturnValue(stream);
94
- await middleware.use(buildReq('/storage/upload/file/report.pdf'), res, next);
95
- expect(res.set).toHaveBeenCalledWith(expect.objectContaining({
96
- 'Content-Type': 'application/pdf',
97
- 'Content-Length': 1234,
98
- 'Content-Disposition': 'inline'
99
- }));
100
- expect(stream.pipe).toHaveBeenCalledWith(res);
101
- });
102
- it('sets an attachment content-disposition with the filename for non-viewable mime types', async ()=>{
103
- const res = buildRes();
104
- mockExistsSync.mockReturnValue(true);
105
- mockStatSync.mockReturnValue({
106
- isFile: ()=>true,
107
- size: 500
108
- });
109
- mockMimeLookup.mockReturnValue('application/zip');
110
- mockCreateReadStream.mockReturnValue(new FakeStream());
111
- await middleware.use(buildReq('/storage/upload/file/archive.zip'), res, next);
112
- expect(res.set).toHaveBeenCalledWith(expect.objectContaining({
113
- 'Content-Disposition': expect.stringContaining('attachment; filename="archive.zip"')
114
- }));
115
- });
116
- it('always serves .svg files as image/svg+xml regardless of mime-types lookup', async ()=>{
117
- const res = buildRes();
118
- mockExistsSync.mockReturnValue(true);
119
- mockStatSync.mockReturnValue({
120
- isFile: ()=>true,
121
- size: 200
122
- });
123
- mockCreateReadStream.mockReturnValue(new FakeStream());
124
- await middleware.use(buildReq('/storage/upload/file/logo.svg'), res, next);
125
- expect(mockMimeLookup).not.toHaveBeenCalled();
126
- expect(res.set).toHaveBeenCalledWith(expect.objectContaining({
127
- 'Content-Type': 'image/svg+xml'
128
- }));
129
- });
130
- it('returns 404 when the resolved path exists but is not a regular file', async ()=>{
131
- const res = buildRes();
132
- mockExistsSync.mockReturnValue(true);
133
- mockStatSync.mockReturnValue({
134
- isFile: ()=>false
135
- });
136
- await middleware.use(buildReq('/storage/upload/file/some-dir'), res, next);
137
- expect(res.status).toHaveBeenCalledWith(404);
138
- expect(mockCreateReadStream).not.toHaveBeenCalled();
139
- });
140
- it('falls back to the configured local storage base path (strategy 2: path already prefixed with base)', async ()=>{
141
- const res = buildRes();
142
- const fallbackPath = join('uploads', 'report.pdf');
143
- mockUploadService.getLocalStorageBasePath.mockResolvedValue('./uploads');
144
- mockExistsSync.mockImplementation((p)=>p === fallbackPath);
145
- mockStatSync.mockReturnValue({
146
- isFile: ()=>true,
147
- size: 10
148
- });
149
- mockMimeLookup.mockReturnValue('application/pdf');
150
- mockCreateReadStream.mockReturnValue(new FakeStream());
151
- await middleware.use(buildReq('/storage/upload/file/uploads/report.pdf'), res, next);
152
- expect(mockCreateReadStream).toHaveBeenCalledWith(fallbackPath);
153
- });
154
- it('falls back to the configured local storage base path (strategy 3: legacy path without base prefix)', async ()=>{
155
- const res = buildRes();
156
- const fallbackPath = join('uploads', 'report.pdf');
157
- mockUploadService.getLocalStorageBasePath.mockResolvedValue('uploads');
158
- mockExistsSync.mockImplementation((p)=>p === fallbackPath);
159
- mockStatSync.mockReturnValue({
160
- isFile: ()=>true,
161
- size: 10
162
- });
163
- mockMimeLookup.mockReturnValue('application/pdf');
164
- mockCreateReadStream.mockReturnValue(new FakeStream());
165
- await middleware.use(buildReq('/storage/upload/file/report.pdf'), res, next);
166
- expect(mockCreateReadStream).toHaveBeenCalledWith(fallbackPath);
167
- });
168
- it('returns the underlying not-found message when no direct or fallback path resolves', async ()=>{
169
- const res = buildRes();
170
- mockExistsSync.mockReturnValue(false);
171
- mockUploadService.getLocalStorageBasePath.mockResolvedValue(null);
172
- await middleware.use(buildReq('/storage/upload/file/missing.pdf'), res, next);
173
- expect(res.status).toHaveBeenCalledWith(404);
174
- expect(res.json).toHaveBeenCalledWith(expect.objectContaining({
175
- message: expect.stringContaining('File not found'),
176
- statusCode: 404
177
- }));
178
- });
179
- it('sends a 404 if the file stream errors before headers are sent', async ()=>{
180
- const res = buildRes();
181
- mockExistsSync.mockReturnValue(true);
182
- mockStatSync.mockReturnValue({
183
- isFile: ()=>true,
184
- size: 10
185
- });
186
- mockMimeLookup.mockReturnValue('application/pdf');
187
- const stream = new FakeStream();
188
- mockCreateReadStream.mockReturnValue(stream);
189
- await middleware.use(buildReq('/storage/upload/file/report.pdf'), res, next);
190
- stream.emit('error', new Error('boom'));
191
- expect(res.status).toHaveBeenCalledWith(404);
192
- });
193
- it('destroys the stream when the response is closed early', async ()=>{
194
- const res = buildRes();
195
- mockExistsSync.mockReturnValue(true);
196
- mockStatSync.mockReturnValue({
197
- isFile: ()=>true,
198
- size: 10
199
- });
200
- mockMimeLookup.mockReturnValue('application/pdf');
201
- const stream = new FakeStream();
202
- mockCreateReadStream.mockReturnValue(stream);
203
- await middleware.use(buildReq('/storage/upload/file/report.pdf'), res, next);
204
- res.emitClose();
205
- expect(stream.destroy).toHaveBeenCalled();
206
- });
207
- });
@@ -1 +0,0 @@
1
- export * from './file-serve.middleware';