@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,156 +0,0 @@
1
- function _ts_decorate(decorators, target, key, desc) {
2
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
- 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;
5
- return c > 3 && r && Object.defineProperty(target, key, r), r;
6
- }
7
- import { CacheModule, UtilsModule, createModuleEventsProvider } from '@flusys/nestjs-shared/modules';
8
- import { Logger, Module, RequestMethod } from '@nestjs/common';
9
- import { FileServeMiddleware } from '../middlewares';
10
- import { StorageConfigService } from '../services';
11
- import { STORAGE_MODULE_OPTIONS } from '../config/storage.constants';
12
- import { FileManagerController, FolderController, StorageConfigController, UploadController } from '../controllers';
13
- import { FileLocationEnum } from '../enums/file-location.enum';
14
- import { StorageFactoryService, StorageProviderRegistry } from '../providers';
15
- import { LocalProvider } from '../providers/local-provider';
16
- import { FileManagerService, FolderService, StorageDataSourceProvider, UploadService } from '../services';
17
- import { StorageProviderConfigService } from '../services/storage-provider-config.service';
18
- const logger = new Logger('StorageModule');
19
- const CONTROLLERS = [
20
- FileManagerController,
21
- FolderController,
22
- StorageConfigController,
23
- UploadController
24
- ];
25
- const EXPORTED_SERVICES = [
26
- StorageConfigService,
27
- StorageDataSourceProvider,
28
- FileManagerService,
29
- FolderService,
30
- StorageProviderConfigService,
31
- UploadService,
32
- StorageFactoryService
33
- ];
34
- // Register built-in provider
35
- StorageProviderRegistry.register(FileLocationEnum.LOCAL, LocalProvider);
36
- logger.log('Registered LocalProvider');
37
- // Register optional providers (skip if dependencies not installed)
38
- const OPTIONAL_PROVIDERS = [
39
- {
40
- location: FileLocationEnum.AWS,
41
- path: '../providers/s3-provider.optional',
42
- name: 'S3Provider',
43
- dep: '@aws-sdk/client-s3'
44
- },
45
- {
46
- location: FileLocationEnum.AZURE,
47
- path: '../providers/azure-provider.optional',
48
- name: 'AzureProvider',
49
- dep: '@azure/storage-blob'
50
- },
51
- {
52
- location: FileLocationEnum.SFTP,
53
- path: '../providers/sftp-provider.optional',
54
- name: 'SftpProvider',
55
- dep: 'ssh2-sftp-client'
56
- }
57
- ];
58
- for (const { location, path, name, dep } of OPTIONAL_PROVIDERS){
59
- try {
60
- StorageProviderRegistry.register(location, require(path)[name]);
61
- logger.log(`Registered ${name}`);
62
- } catch {
63
- logger.debug(`${name} not available (install ${dep} to enable)`);
64
- }
65
- }
66
- export class StorageModule {
67
- configure(consumer) {
68
- consumer.apply(FileServeMiddleware).forRoutes({
69
- path: 'storage/upload/file/*',
70
- method: RequestMethod.GET
71
- });
72
- }
73
- static forRoot(options) {
74
- return {
75
- module: StorageModule,
76
- global: options.global ?? false,
77
- imports: [
78
- CacheModule,
79
- UtilsModule
80
- ],
81
- controllers: options.includeController !== false ? CONTROLLERS : [],
82
- providers: this.buildProviders(options),
83
- exports: EXPORTED_SERVICES
84
- };
85
- }
86
- static forRootAsync(options) {
87
- return {
88
- module: StorageModule,
89
- global: options.global ?? false,
90
- imports: [
91
- ...options.imports || [],
92
- CacheModule,
93
- UtilsModule
94
- ],
95
- controllers: options.includeController !== false ? CONTROLLERS : [],
96
- providers: [
97
- ...this.createAsyncProviders(options),
98
- ...this.buildProviders()
99
- ],
100
- exports: EXPORTED_SERVICES
101
- };
102
- }
103
- // ─── Private Helpers ──────────────────────────────────────────────────────────
104
- static buildProviders(options) {
105
- const providers = [
106
- ...EXPORTED_SERVICES,
107
- FileServeMiddleware
108
- ];
109
- if (options) {
110
- providers.unshift({
111
- provide: STORAGE_MODULE_OPTIONS,
112
- useValue: options
113
- });
114
- }
115
- providers.push(createModuleEventsProvider('storage', STORAGE_MODULE_OPTIONS));
116
- return providers;
117
- }
118
- static createAsyncProviders(options) {
119
- if (options.useFactory) {
120
- return [
121
- {
122
- provide: STORAGE_MODULE_OPTIONS,
123
- useFactory: async (...args)=>({
124
- ...options,
125
- config: await options.useFactory(...args)
126
- }),
127
- inject: options.inject || []
128
- }
129
- ];
130
- }
131
- const factoryClass = options.useClass || options.useExisting;
132
- if (!factoryClass) return [];
133
- const providers = [
134
- {
135
- provide: STORAGE_MODULE_OPTIONS,
136
- useFactory: async (factory)=>({
137
- ...options,
138
- config: await factory.createStorageOptions()
139
- }),
140
- inject: [
141
- factoryClass
142
- ]
143
- }
144
- ];
145
- if (options.useClass) {
146
- providers.push({
147
- provide: options.useClass,
148
- useClass: options.useClass
149
- });
150
- }
151
- return providers;
152
- }
153
- }
154
- StorageModule = _ts_decorate([
155
- Module({})
156
- ], StorageModule);
@@ -1,180 +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 { StorageModule } from './storage.module';
15
- import { STORAGE_MODULE_OPTIONS } from '../config/storage.constants';
16
- import { FileManagerController, FolderController, StorageConfigController, UploadController } from '../controllers';
17
- import { FileServeMiddleware } from '../middlewares';
18
- function providerTokens(providers) {
19
- return providers.map((p)=>typeof p === 'function' ? p : p.provide);
20
- }
21
- describe('StorageModule', ()=>{
22
- describe('forRoot', ()=>{
23
- it('should default to a non-global module with all controllers registered', ()=>{
24
- const dynamicModule = StorageModule.forRoot({});
25
- expect(dynamicModule.module).toBe(StorageModule);
26
- expect(dynamicModule.global).toBe(false);
27
- expect(dynamicModule.controllers).toEqual([
28
- FileManagerController,
29
- FolderController,
30
- StorageConfigController,
31
- UploadController
32
- ]);
33
- });
34
- it('should respect global: true', ()=>{
35
- expect(StorageModule.forRoot({
36
- global: true
37
- }).global).toBe(true);
38
- });
39
- it('should register no controllers when includeController is false', ()=>{
40
- expect(StorageModule.forRoot({
41
- includeController: false
42
- }).controllers).toEqual([]);
43
- });
44
- it('should register controllers when includeController is anything other than exactly false', ()=>{
45
- expect(StorageModule.forRoot({
46
- includeController: true
47
- }).controllers).toHaveLength(4);
48
- expect(StorageModule.forRoot({}).controllers).toHaveLength(4);
49
- });
50
- it('should register a STORAGE_MODULE_OPTIONS provider carrying the given options', ()=>{
51
- const options = {
52
- bootstrapAppConfig: {
53
- enableCompanyFeature: true
54
- }
55
- };
56
- const dynamicModule = StorageModule.forRoot(options);
57
- const provider = dynamicModule.providers.find((p)=>p.provide === STORAGE_MODULE_OPTIONS);
58
- expect(provider.useValue).toBe(options);
59
- });
60
- it('should always export the same fixed set of services', ()=>{
61
- const dynamicModule = StorageModule.forRoot({});
62
- expect(dynamicModule.exports).toHaveLength(7);
63
- });
64
- });
65
- describe('forRootAsync', ()=>{
66
- it('should forward external imports alongside CacheModule/UtilsModule', ()=>{
67
- let FakeImportedModule = class FakeImportedModule {
68
- };
69
- const dynamicModule = StorageModule.forRootAsync({
70
- bootstrapAppConfig: {},
71
- imports: [
72
- FakeImportedModule
73
- ]
74
- });
75
- expect(dynamicModule.imports).toEqual(expect.arrayContaining([
76
- FakeImportedModule
77
- ]));
78
- });
79
- it('should register no controllers when includeController is false', ()=>{
80
- const dynamicModule = StorageModule.forRootAsync({
81
- bootstrapAppConfig: {},
82
- includeController: false
83
- });
84
- expect(dynamicModule.controllers).toEqual([]);
85
- });
86
- it('useFactory: builds a STORAGE_MODULE_OPTIONS provider that merges resolved config into the static options', async ()=>{
87
- const useFactory = jest.fn().mockResolvedValue({
88
- maxFileSize: 1024
89
- });
90
- const dynamicModule = StorageModule.forRootAsync({
91
- bootstrapAppConfig: {},
92
- useFactory,
93
- inject: [
94
- 'SOME_TOKEN'
95
- ]
96
- });
97
- const provider = dynamicModule.providers.find((p)=>p.provide === STORAGE_MODULE_OPTIONS);
98
- expect(provider.inject).toEqual([
99
- 'SOME_TOKEN'
100
- ]);
101
- const resolved = await provider.useFactory('injected');
102
- expect(useFactory).toHaveBeenCalledWith('injected');
103
- expect(resolved.config).toEqual({
104
- maxFileSize: 1024
105
- });
106
- });
107
- it('useClass: registers both the class provider and an options provider calling createStorageOptions()', async ()=>{
108
- let MyOptionsFactory = class MyOptionsFactory {
109
- constructor(){
110
- _define_property(this, "createStorageOptions", jest.fn().mockResolvedValue({
111
- maxFileSize: 2048
112
- }));
113
- }
114
- };
115
- const dynamicModule = StorageModule.forRootAsync({
116
- bootstrapAppConfig: {},
117
- useClass: MyOptionsFactory
118
- });
119
- const classProvider = dynamicModule.providers.find((p)=>p.provide === MyOptionsFactory);
120
- expect(classProvider.useClass).toBe(MyOptionsFactory);
121
- const optionsProvider = dynamicModule.providers.find((p)=>p.provide === STORAGE_MODULE_OPTIONS);
122
- const instance = new MyOptionsFactory();
123
- const resolved = await optionsProvider.useFactory(instance);
124
- expect(instance.createStorageOptions).toHaveBeenCalled();
125
- expect(resolved.config).toEqual({
126
- maxFileSize: 2048
127
- });
128
- });
129
- it('useExisting: registers a single options provider without a duplicate class registration', async ()=>{
130
- let MyOptionsFactory = class MyOptionsFactory {
131
- constructor(){
132
- _define_property(this, "createStorageOptions", jest.fn().mockResolvedValue({
133
- maxFileSize: 4096
134
- }));
135
- }
136
- };
137
- const dynamicModule = StorageModule.forRootAsync({
138
- bootstrapAppConfig: {},
139
- useExisting: MyOptionsFactory
140
- });
141
- expect(dynamicModule.providers.filter((p)=>p.provide === MyOptionsFactory)).toHaveLength(0);
142
- const optionsProvider = dynamicModule.providers.find((p)=>p.provide === STORAGE_MODULE_OPTIONS);
143
- expect(optionsProvider.inject).toEqual([
144
- MyOptionsFactory
145
- ]);
146
- });
147
- it('should register no STORAGE_MODULE_OPTIONS provider when neither useFactory, useClass, nor useExisting is given', ()=>{
148
- const dynamicModule = StorageModule.forRootAsync({
149
- bootstrapAppConfig: {}
150
- });
151
- expect(providerTokens(dynamicModule.providers)).not.toContain(STORAGE_MODULE_OPTIONS);
152
- });
153
- it('should still register the fixed exported services as providers alongside async options', ()=>{
154
- const dynamicModule = StorageModule.forRootAsync({
155
- bootstrapAppConfig: {},
156
- useFactory: jest.fn()
157
- });
158
- expect(dynamicModule.providers).toEqual(expect.arrayContaining([
159
- ...dynamicModule.exports
160
- ]));
161
- });
162
- });
163
- describe('configure (NestModule)', ()=>{
164
- it('should wire FileServeMiddleware to the file GET route', ()=>{
165
- const forRoutes = jest.fn();
166
- const apply = jest.fn().mockReturnValue({
167
- forRoutes
168
- });
169
- const consumer = {
170
- apply
171
- };
172
- new StorageModule().configure(consumer);
173
- expect(apply).toHaveBeenCalledWith(FileServeMiddleware);
174
- expect(forRoutes).toHaveBeenCalledWith({
175
- path: 'storage/upload/file/*',
176
- method: expect.anything()
177
- });
178
- });
179
- });
180
- });
@@ -1,224 +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
- const mockCreateIfNotExists = jest.fn();
15
- const mockBlockBlobUpload = jest.fn();
16
- const mockDeleteIfExists = jest.fn();
17
- const mockContainerExists = jest.fn();
18
- function buildBlockBlobClient() {
19
- return {
20
- upload: mockBlockBlobUpload,
21
- deleteIfExists: mockDeleteIfExists,
22
- url: 'https://account.blob.core.windows.net/container/blob-key.jpg',
23
- credential: 'shared-key-credential'
24
- };
25
- }
26
- const mockGetBlockBlobClient = jest.fn().mockImplementation(()=>buildBlockBlobClient());
27
- const mockGetContainerClient = jest.fn().mockImplementation(()=>({
28
- createIfNotExists: mockCreateIfNotExists,
29
- getBlockBlobClient: mockGetBlockBlobClient,
30
- exists: mockContainerExists
31
- }));
32
- const mockFromConnectionString = jest.fn().mockImplementation(()=>({
33
- getContainerClient: mockGetContainerClient
34
- }));
35
- const mockBlobServiceClientCtor = jest.fn().mockImplementation(()=>({
36
- getContainerClient: mockGetContainerClient
37
- }));
38
- mockBlobServiceClientCtor.fromConnectionString = mockFromConnectionString;
39
- const mockGenerateBlobSASQueryParameters = jest.fn().mockReturnValue({
40
- toString: ()=>'sv=2022&sig=abc123'
41
- });
42
- let MockStorageSharedKeyCredential = class MockStorageSharedKeyCredential {
43
- constructor(accountName, accountKey){
44
- _define_property(this, "accountName", void 0);
45
- _define_property(this, "accountKey", void 0);
46
- this.accountName = accountName;
47
- this.accountKey = accountKey;
48
- }
49
- };
50
- let MockBlobSASPermissions = class MockBlobSASPermissions {
51
- static parse(input) {
52
- return {
53
- permissions: input
54
- };
55
- }
56
- };
57
- jest.mock('@azure/storage-blob', ()=>({
58
- BlobServiceClient: mockBlobServiceClientCtor,
59
- StorageSharedKeyCredential: MockStorageSharedKeyCredential,
60
- BlobSASPermissions: MockBlobSASPermissions,
61
- generateBlobSASQueryParameters: (...args)=>mockGenerateBlobSASQueryParameters(...args)
62
- }));
63
- jest.mock('../utils/image-compressor.util', ()=>({
64
- ImageCompressor: {
65
- compress: jest.fn()
66
- }
67
- }));
68
- import { AzureProvider } from './azure-provider.optional';
69
- import { ImageCompressor } from '../utils/image-compressor.util';
70
- function buildMulterFile(overrides = {}) {
71
- return {
72
- fieldname: 'file',
73
- originalname: 'photo.jpg',
74
- encoding: '7bit',
75
- mimetype: 'image/jpeg',
76
- buffer: Buffer.from('file-bytes'),
77
- size: 10,
78
- stream: undefined,
79
- destination: '',
80
- filename: '',
81
- path: '',
82
- ...overrides
83
- };
84
- }
85
- describe('AzureProvider', ()=>{
86
- let provider;
87
- beforeEach(async ()=>{
88
- jest.clearAllMocks();
89
- mockCreateIfNotExists.mockResolvedValue(undefined);
90
- mockBlockBlobUpload.mockResolvedValue(undefined);
91
- mockDeleteIfExists.mockResolvedValue(undefined);
92
- mockContainerExists.mockResolvedValue(true);
93
- mockGetBlockBlobClient.mockImplementation(()=>buildBlockBlobClient());
94
- mockGetContainerClient.mockImplementation(()=>({
95
- createIfNotExists: mockCreateIfNotExists,
96
- getBlockBlobClient: mockGetBlockBlobClient,
97
- exists: mockContainerExists
98
- }));
99
- provider = new AzureProvider();
100
- await provider.initialize({
101
- accountName: 'myaccount',
102
- accountKey: 'key123',
103
- containerName: 'my-container'
104
- });
105
- });
106
- describe('initialize', ()=>{
107
- it('builds a BlobServiceClient using an account key credential when no connection string is given', ()=>{
108
- expect(mockBlobServiceClientCtor).toHaveBeenCalledWith('https://myaccount.blob.core.windows.net', expect.any(MockStorageSharedKeyCredential));
109
- expect(mockCreateIfNotExists).toHaveBeenCalled();
110
- });
111
- it('builds a BlobServiceClient from a connection string when provided', async ()=>{
112
- jest.clearAllMocks();
113
- mockCreateIfNotExists.mockResolvedValue(undefined);
114
- const p = new AzureProvider();
115
- await p.initialize({
116
- accountName: 'acct2',
117
- accountKey: 'key2',
118
- containerName: 'container2',
119
- connectionString: 'DefaultEndpointsProtocol=https;AccountName=acct2;AccountKey=key2'
120
- });
121
- expect(mockFromConnectionString).toHaveBeenCalledWith('DefaultEndpointsProtocol=https;AccountName=acct2;AccountKey=key2');
122
- });
123
- it('throws InternalServerErrorException when the Azure SDK cannot be initialized', async ()=>{
124
- mockBlobServiceClientCtor.mockImplementationOnce(()=>{
125
- throw new Error('boom');
126
- });
127
- const brokenProvider = new AzureProvider();
128
- await expect(brokenProvider.initialize({
129
- accountName: 'a',
130
- accountKey: 'b',
131
- containerName: 'c'
132
- })).rejects.toThrow('Azure Storage SDK not found');
133
- });
134
- });
135
- describe('uploadFile', ()=>{
136
- it('uploads the blob and returns file metadata', async ()=>{
137
- const file = buildMulterFile();
138
- const result = await provider.uploadFile(file, {});
139
- expect(mockBlockBlobUpload).toHaveBeenCalledWith(file.buffer, file.buffer.length, expect.objectContaining({
140
- blobHTTPHeaders: {
141
- blobContentType: 'image/jpeg'
142
- }
143
- }));
144
- expect(result.name).toMatch(/photo\.jpg$/);
145
- expect(result.key).toBe(result.name);
146
- });
147
- it('prefixes blob name with folderPath when provided', async ()=>{
148
- const file = buildMulterFile();
149
- const result = await provider.uploadFile(file, {
150
- folderPath: 'docs'
151
- });
152
- expect(result.key).toMatch(/^docs\//);
153
- });
154
- it('compresses image files when options.compress is true', async ()=>{
155
- ImageCompressor.compress.mockResolvedValue({
156
- buffer: Buffer.from('small'),
157
- format: 'image/webp'
158
- });
159
- const file = buildMulterFile();
160
- const result = await provider.uploadFile(file, {
161
- compress: true
162
- });
163
- expect(ImageCompressor.compress).toHaveBeenCalled();
164
- expect(result.contentType).toBe('image/webp');
165
- });
166
- });
167
- describe('uploadMultipleFiles', ()=>{
168
- it('uploads all files', async ()=>{
169
- const files = [
170
- buildMulterFile({
171
- originalname: 'a.jpg'
172
- }),
173
- buildMulterFile({
174
- originalname: 'b.jpg'
175
- })
176
- ];
177
- const results = await provider.uploadMultipleFiles(files, {});
178
- expect(results).toHaveLength(2);
179
- expect(mockBlockBlobUpload).toHaveBeenCalledTimes(2);
180
- });
181
- });
182
- describe('deleteFile', ()=>{
183
- it('calls deleteIfExists on the block blob client for the given key', async ()=>{
184
- await provider.deleteFile('blob-key.jpg');
185
- expect(mockGetBlockBlobClient).toHaveBeenCalledWith('blob-key.jpg');
186
- expect(mockDeleteIfExists).toHaveBeenCalled();
187
- });
188
- });
189
- describe('deleteMultipleFiles', ()=>{
190
- it('deletes every provided key', async ()=>{
191
- await provider.deleteMultipleFiles([
192
- 'a.jpg',
193
- 'b.jpg'
194
- ]);
195
- expect(mockDeleteIfExists).toHaveBeenCalledTimes(2);
196
- });
197
- });
198
- describe('generatePresignedUrl', ()=>{
199
- it('generates a SAS URL appended with the query string', async ()=>{
200
- const url = await provider.generatePresignedUrl('blob-key.jpg', 1800);
201
- expect(mockGenerateBlobSASQueryParameters).toHaveBeenCalledWith(expect.objectContaining({
202
- containerName: 'my-container',
203
- blobName: 'blob-key.jpg'
204
- }), 'shared-key-credential');
205
- expect(url).toBe('https://account.blob.core.windows.net/container/blob-key.jpg?sv=2022&sig=abc123');
206
- });
207
- it('defaults the expiry window to 3600 seconds', async ()=>{
208
- await provider.generatePresignedUrl('blob-key.jpg');
209
- const call = mockGenerateBlobSASQueryParameters.mock.calls[0][0];
210
- const diffSeconds = (call.expiresOn.getTime() - call.startsOn.getTime()) / 1000;
211
- expect(diffSeconds).toBeCloseTo(3600, 0);
212
- });
213
- });
214
- describe('healthCheck', ()=>{
215
- it('returns true when the container exists', async ()=>{
216
- mockContainerExists.mockResolvedValue(true);
217
- await expect(provider.healthCheck()).resolves.toBe(true);
218
- });
219
- it('returns false when checking existence throws', async ()=>{
220
- mockContainerExists.mockRejectedValue(new Error('network error'));
221
- await expect(provider.healthCheck()).resolves.toBe(false);
222
- });
223
- });
224
- });