@flusys/nestjs-storage 4.1.0 → 5.0.0

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 (31) hide show
  1. package/README.md +71 -483
  2. package/cjs/config/message-keys.js +2 -52
  3. package/cjs/controllers/storage-config.controller.js +2 -2
  4. package/cjs/docs/storage-swagger.config.js +5 -3
  5. package/cjs/middlewares/file-serve.middleware.js +5 -5
  6. package/cjs/providers/azure-provider.optional.js +1 -1
  7. package/cjs/providers/s3-provider.optional.js +1 -1
  8. package/cjs/providers/sftp-provider.optional.js +1 -1
  9. package/cjs/providers/storage-factory.service.js +4 -4
  10. package/cjs/services/file-manager.service.js +3 -39
  11. package/cjs/services/storage-config.service.js +14 -3
  12. package/cjs/services/storage-datasource.provider.js +1 -6
  13. package/cjs/services/upload.service.js +12 -6
  14. package/config/message-keys.d.ts +0 -96
  15. package/fesm/config/message-keys.js +2 -47
  16. package/fesm/controllers/storage-config.controller.js +2 -2
  17. package/fesm/docs/storage-swagger.config.js +5 -3
  18. package/fesm/middlewares/file-serve.middleware.js +5 -5
  19. package/fesm/providers/azure-provider.optional.js +1 -1
  20. package/fesm/providers/s3-provider.optional.js +1 -1
  21. package/fesm/providers/sftp-provider.optional.js +1 -1
  22. package/fesm/providers/storage-factory.service.js +4 -4
  23. package/fesm/services/file-manager.service.js +3 -39
  24. package/fesm/services/storage-config.service.js +14 -3
  25. package/fesm/services/storage-datasource.provider.js +1 -6
  26. package/fesm/services/upload.service.js +12 -6
  27. package/package.json +3 -3
  28. package/services/file-manager.service.d.ts +1 -6
  29. package/services/storage-config.service.d.ts +2 -0
  30. package/services/storage-datasource.provider.d.ts +1 -2
  31. package/services/upload.service.d.ts +1 -1
package/README.md CHANGED
@@ -1,78 +1,9 @@
1
1
  # @flusys/nestjs-storage
2
2
 
3
- > Multi-provider file storage for NestJS — Local disk, AWS S3, Azure Blob, and SFTP with folder management, image compression, presigned URL generation, and multi-tenant support.
3
+ Multi-provider file storage for NestJS — Local disk, AWS S3, Azure Blob, and SFTP with folder management, image compression, and multi-tenant support.
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/@flusys/nestjs-storage.svg)](https://www.npmjs.com/package/@flusys/nestjs-storage)
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
- [![NestJS](https://img.shields.io/badge/NestJS-11.x-red.svg)](https://nestjs.com/)
8
- [![TypeScript](https://img.shields.io/badge/TypeScript-5.x-blue.svg)](https://www.typescriptlang.org/)
9
- [![Node.js](https://img.shields.io/badge/Node.js-%3E%3D18.x-green.svg)](https://nodejs.org/)
10
-
11
- ---
12
-
13
- ## Table of Contents
14
-
15
- - [Overview](#overview)
16
- - [Features](#features)
17
- - [Compatibility](#compatibility)
18
- - [Installation](#installation)
19
- - [Quick Start](#quick-start)
20
- - [Module Registration](#module-registration)
21
- - [forRoot (Sync)](#forroot-sync)
22
- - [forRootAsync (Factory)](#forrootasync-factory)
23
- - [Configuration Reference](#configuration-reference)
24
- - [Feature Toggles](#feature-toggles)
25
- - [Storage Providers](#storage-providers)
26
- - [Local](#local-provider)
27
- - [AWS S3](#aws-s3)
28
- - [Azure Blob Storage](#azure-blob-storage)
29
- - [SFTP](#sftp)
30
- - [Custom Provider](#custom-provider)
31
- - [API Endpoints](#api-endpoints)
32
- - [Entities](#entities)
33
- - [File Serving (Local)](#file-serving-local)
34
- - [Image Compression](#image-compression)
35
- - [File Validation](#file-validation)
36
- - [Exported Services](#exported-services)
37
- - [Programmatic Usage](#programmatic-usage)
38
- - [Troubleshooting](#troubleshooting)
39
- - [License](#license)
40
-
41
- ---
42
-
43
- ## Overview
44
-
45
- `@flusys/nestjs-storage` provides a unified API for managing files across multiple storage backends. Provider configurations are stored in the database — switch providers without code changes. Cloud providers (S3, Azure, SFTP) are dynamically imported so you only pay the install cost for what you use.
46
-
47
- ---
48
-
49
- ## Features
50
-
51
- - **4 storage backends** — Local disk, AWS S3, Azure Blob Storage, SFTP
52
- - **Pluggable providers** — Register custom providers via `StorageProviderRegistry`
53
- - **Folder hierarchy** — Organize files in folders with parent-child relationships
54
- - **Image compression** — Automatic optimization via `sharp` (JPEG, PNG, WebP, AVIF, TIFF, GIF)
55
- - **Presigned URLs** — Time-limited signed URLs for S3 and Azure with TTL caching
56
- - **File validation** — Magic-byte validation prevents file-type spoofing
57
- - **Path traversal protection** — All file paths sanitized before disk access
58
- - **Company scoping** — Optional `companyId` filtering on all queries
59
- - **Multi-tenant** — Per-tenant DataSource isolation
60
-
61
- ---
62
-
63
- ## Compatibility
64
-
65
- | Package | Version |
66
- |---------|---------|
67
- | `@flusys/nestjs-core` | `^4.0.0` |
68
- | `@flusys/nestjs-shared` | `^4.0.0` |
69
- | `sharp` | `^0.33.0` |
70
- | `uuid` | `^9.0.0` |
71
- | `mime-types` | `^2.0.0` |
72
- | `@aws-sdk/client-s3` | `^3.0.0` *(optional)* |
73
- | `@azure/storage-blob` | `^12.0.0` *(optional)* |
74
- | `ssh2-sftp-client` | `^9.0.0` *(optional)* |
75
- | Node.js | `>= 18.x` |
76
7
 
77
8
  ---
78
9
 
@@ -82,28 +13,21 @@
82
13
  npm install @flusys/nestjs-storage @flusys/nestjs-shared @flusys/nestjs-core sharp uuid mime-types
83
14
  ```
84
15
 
85
- Optional cloud provider SDKs (install only what you need):
16
+ Optional cloud SDKs install only what you need:
86
17
 
87
18
  ```bash
88
- # AWS S3
89
- npm install @aws-sdk/client-s3 @aws-sdk/lib-storage @aws-sdk/s3-request-presigner
90
-
91
- # Azure Blob Storage
92
- npm install @azure/storage-blob
93
-
94
- # SFTP
95
- npm install ssh2-sftp-client
96
- npm install -D @types/ssh2-sftp-client
19
+ npm install @aws-sdk/client-s3 @aws-sdk/lib-storage @aws-sdk/s3-request-presigner # S3
20
+ npm install @azure/storage-blob # Azure
21
+ npm install ssh2-sftp-client # SFTP
97
22
  ```
98
23
 
99
- ---
24
+ ## 1. Register the Module
100
25
 
101
- ## Quick Start
26
+ ### Synchronous
102
27
 
103
- ### Local Storage (Minimal Setup)
28
+ #### Mode 1: Single Database
104
29
 
105
30
  ```typescript
106
- import { Module } from '@nestjs/common';
107
31
  import { StorageModule } from '@flusys/nestjs-storage';
108
32
 
109
33
  @Module({
@@ -117,17 +41,17 @@ import { StorageModule } from '@flusys/nestjs-storage';
117
41
  },
118
42
  config: {
119
43
  defaultDatabaseConfig: {
120
- type: 'postgres',
44
+ type: 'mysql',
121
45
  host: process.env.DB_HOST,
122
- port: Number(process.env.DB_PORT ?? 5432),
46
+ port: Number(process.env.DB_PORT ?? 3306),
123
47
  username: process.env.DB_USER,
124
48
  password: process.env.DB_PASSWORD,
125
49
  database: process.env.DB_NAME,
126
50
  },
127
- maxFileSize: 10 * 1024 * 1024, // 10 MB
128
- allowedFileTypes: ['image/*', 'application/pdf'],
51
+ maxFileSize: 10 * 1024 * 1024, // 10 MB (default: 100 MB)
52
+ allowedFileTypes: ['image/*', 'application/pdf'], // default: ['*/*']
129
53
  localStoragePath: './uploads',
130
- appUrl: process.env.APP_URL ?? 'http://localhost:2002',
54
+ appUrl: process.env.APP_URL,
131
55
  },
132
56
  }),
133
57
  ],
@@ -135,49 +59,43 @@ import { StorageModule } from '@flusys/nestjs-storage';
135
59
  export class AppModule {}
136
60
  ```
137
61
 
138
- ### Register Local File Serving Middleware
139
-
140
- ```typescript
141
- import { FileServeMiddleware } from '@flusys/nestjs-storage';
142
-
143
- @Module({})
144
- export class AppModule implements NestModule {
145
- configure(consumer: MiddlewareConsumer) {
146
- // Serves GET /storage/upload/file/* from local disk
147
- consumer.apply(FileServeMiddleware).forRoutes('storage/upload/file');
148
- }
149
- }
150
- ```
151
-
152
- ---
153
-
154
- ## Module Registration
155
-
156
- ### forRoot (Sync)
62
+ #### Mode 2: Multi-Tenant
157
63
 
158
64
  ```typescript
159
65
  StorageModule.forRoot({
160
- global?: boolean;
161
- includeController?: boolean;
66
+ global: true,
67
+ includeController: true,
162
68
  bootstrapAppConfig: {
163
- databaseMode: 'single' | 'multi-tenant';
164
- enableCompanyFeature: boolean;
165
- };
69
+ databaseMode: 'multi-tenant',
70
+ enableCompanyFeature: true,
71
+ },
166
72
  config: {
167
- defaultDatabaseConfig: IDatabaseConfig;
168
- maxFileSize?: number; // bytes, default: 100MB
169
- allowedFileTypes?: string[]; // MIME types/patterns, default: ['*/*']
170
- localStoragePath?: string; // default: './uploads'
171
- appUrl?: string; // Base URL for local file links
172
- };
173
- })
73
+ tenantDefaultDatabaseConfig: {
74
+ type: 'mysql',
75
+ host: process.env.TENANT_DB_HOST,
76
+ port: Number(process.env.TENANT_DB_PORT ?? 3306),
77
+ username: process.env.TENANT_DB_USER,
78
+ password: process.env.TENANT_DB_PASSWORD,
79
+ database: process.env.TENANT_DB_NAME,
80
+ },
81
+ tenants: [
82
+ { id: 'tenant-a', database: 'tenant_a_db' },
83
+ { id: 'tenant-b', database: 'tenant_b_db' },
84
+ ],
85
+ maxFileSize: 10 * 1024 * 1024,
86
+ localStoragePath: './uploads',
87
+ appUrl: process.env.APP_URL,
88
+ },
89
+ });
174
90
  ```
175
91
 
176
- ### forRootAsync (Factory)
92
+ ### Asynchronous (with ConfigService)
177
93
 
178
94
  ```typescript
179
- import { ConfigService } from '@nestjs/config';
95
+ import { ConfigModule, ConfigService } from '@nestjs/config';
96
+ import { StorageModule, ITenantDatabaseConfig } from '@flusys/nestjs-storage';
180
97
 
98
+ // Single database
181
99
  StorageModule.forRootAsync({
182
100
  global: true,
183
101
  includeController: true,
@@ -188,7 +106,7 @@ StorageModule.forRootAsync({
188
106
  imports: [ConfigModule],
189
107
  useFactory: (configService: ConfigService) => ({
190
108
  defaultDatabaseConfig: {
191
- type: 'postgres',
109
+ type: 'mysql',
192
110
  host: configService.get('DB_HOST'),
193
111
  port: configService.get<number>('DB_PORT'),
194
112
  username: configService.get('DB_USER'),
@@ -200,378 +118,48 @@ StorageModule.forRootAsync({
200
118
  localStoragePath: configService.get('UPLOAD_PATH', './uploads'),
201
119
  }),
202
120
  inject: [ConfigService],
203
- })
204
- ```
205
-
206
- ---
207
-
208
- ## Configuration Reference
209
-
210
- ```typescript
211
- interface IStorageModuleConfig extends IDataSourceServiceOptions {
212
- /** Maximum file size in bytes (default: 100MB) */
213
- maxFileSize?: number;
214
-
215
- /** Allowed MIME types. Use '*/*' for all. (default: ['*/*']) */
216
- allowedFileTypes?: string[];
217
-
218
- /** Local storage base directory (default: './uploads') */
219
- localStoragePath?: string;
220
-
221
- /** Application base URL for building file URLs (local provider) */
222
- appUrl?: string;
223
- }
224
- ```
225
-
226
- ---
121
+ });
227
122
 
228
- ## Feature Toggles
229
-
230
- | Feature | Config | Default | Effect |
231
- |---------|--------|---------|--------|
232
- | Company scoping | `enableCompanyFeature: true` | `false` | Uses `*WithCompany` entity variants; filters all queries by `companyId` |
233
- | Local file serving | Built-in | Always on | Register `FileServeMiddleware` to serve uploaded files from disk |
234
- | Cloud providers | Install SDK + register | Disabled | S3/Azure/SFTP only activate after provider registration |
235
-
236
- ---
237
-
238
- ## Storage Providers
239
-
240
- ### Local Provider
241
-
242
- Built-in, no extra SDK required. Files stored on the server filesystem.
243
-
244
- Create a `StorageConfig` record:
245
-
246
- ```json
247
- POST /storage/storage-config/insert
248
- {
249
- "name": "Local Storage",
250
- "storage": "local",
251
- "config": {},
252
- "isActive": true,
253
- "isDefault": true
254
- }
255
- ```
256
-
257
- File URLs format: `{appUrl}/storage/upload/file/{path}`
258
-
259
- ### AWS S3
260
-
261
- **1. Install the SDK:**
262
- ```bash
263
- npm install @aws-sdk/client-s3 @aws-sdk/lib-storage @aws-sdk/s3-request-presigner
264
- ```
265
-
266
- **2. Register the provider at startup (before module init):**
267
- ```typescript
268
- import { StorageProviderRegistry, FileLocationEnum } from '@flusys/nestjs-storage';
269
- import { S3Provider } from '@flusys/nestjs-storage/providers/s3-provider.optional';
270
-
271
- StorageProviderRegistry.register(FileLocationEnum.AWS, S3Provider);
272
- ```
273
-
274
- **3. Create a StorageConfig:**
275
- ```json
276
- POST /storage/storage-config/insert
277
- {
278
- "name": "Production S3",
279
- "storage": "aws",
280
- "config": {
281
- "region": "us-east-1",
282
- "bucket": "my-app-files",
283
- "accessKeyId": "AKIAIOSFODNN7EXAMPLE",
284
- "secretAccessKey": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
285
- },
286
- "isDefault": true
287
- }
288
- ```
289
-
290
- ### Azure Blob Storage
291
-
292
- **1. Install the SDK:**
293
- ```bash
294
- npm install @azure/storage-blob
295
- ```
296
-
297
- **2. Register:**
298
- ```typescript
299
- import { AzureProvider } from '@flusys/nestjs-storage/providers/azure-provider.optional';
300
- StorageProviderRegistry.register(FileLocationEnum.AZURE, AzureProvider);
301
- ```
302
-
303
- **3. Create a StorageConfig:**
304
- ```json
305
- POST /storage/storage-config/insert
306
- {
307
- "name": "Azure Blob",
308
- "storage": "azure",
309
- "config": {
310
- "connectionString": "DefaultEndpointsProtocol=https;AccountName=...;AccountKey=...;EndpointSuffix=core.windows.net",
311
- "containerName": "my-files"
123
+ // Multi-tenant
124
+ StorageModule.forRootAsync({
125
+ global: true,
126
+ includeController: true,
127
+ bootstrapAppConfig: {
128
+ databaseMode: 'multi-tenant',
129
+ enableCompanyFeature: true,
312
130
  },
313
- "isDefault": true
314
- }
315
- ```
316
-
317
- ### SFTP
318
-
319
- **1. Install the SDK:**
320
- ```bash
321
- npm install ssh2-sftp-client
322
- ```
323
-
324
- **2. Register:**
325
- ```typescript
326
- import { SFTPProvider } from '@flusys/nestjs-storage/providers/sftp-provider.optional';
327
- StorageProviderRegistry.register(FileLocationEnum.SFTP, SFTPProvider);
328
- ```
329
-
330
- **3. Create a StorageConfig:**
331
- ```json
332
- POST /storage/storage-config/insert
333
- {
334
- "name": "Legacy SFTP",
335
- "storage": "sftp",
336
- "config": {
337
- "host": "sftp.example.com",
338
- "port": 22,
339
- "username": "sftpuser",
340
- "password": "secret",
341
- "remotePath": "/uploads"
342
- }
343
- }
344
- ```
345
-
346
- ### Custom Provider
347
-
348
- ```typescript
349
- import { IStorageProvider, StorageProviderRegistry } from '@flusys/nestjs-storage';
350
-
351
- class MyCloudProvider implements IStorageProvider {
352
- async upload(file: IFileUploadOptions): Promise<IUploadedFile> { /* ... */ }
353
- async delete(path: string): Promise<void> { /* ... */ }
354
- async getUrl(path: string, ttl?: number): Promise<string> { /* ... */ }
355
- }
356
-
357
- StorageProviderRegistry.register('my-cloud', MyCloudProvider);
358
- ```
359
-
360
- ---
361
-
362
- ## API Endpoints
363
-
364
- All endpoints use **POST** and require JWT authentication.
365
-
366
- ### Storage Config — `POST /storage/storage-config/*`
367
-
368
- | Endpoint | Permission | Description |
369
- |----------|-----------|-------------|
370
- | `POST /storage/storage-config/insert` | `storage-config.create` | Add provider config |
371
- | `POST /storage/storage-config/get-all` | `storage-config.read` | List configs |
372
- | `POST /storage/storage-config/get/:id` | `storage-config.read` | Get config by ID |
373
- | `POST /storage/storage-config/update` | `storage-config.update` | Update config |
374
- | `POST /storage/storage-config/delete` | `storage-config.delete` | Delete config |
375
- | `POST /storage/storage-config/set-default` | `storage-config.update` | Set as default provider |
376
-
377
- ### Folders — `POST /storage/folder/*`
378
-
379
- | Endpoint | Permission | Description |
380
- |----------|-----------|-------------|
381
- | `POST /storage/folder/insert` | `folder.create` | Create folder |
382
- | `POST /storage/folder/get-all` | `folder.read` | List folders |
383
- | `POST /storage/folder/get/:id` | `folder.read` | Get folder by ID |
384
- | `POST /storage/folder/get-tree` | `folder.read` | Get hierarchical folder tree |
385
- | `POST /storage/folder/update` | `folder.update` | Update folder |
386
- | `POST /storage/folder/delete` | `folder.delete` | Delete folder (must be empty) |
387
-
388
- ### Files — `POST /storage/file/*`
389
-
390
- | Endpoint | Permission | Description |
391
- |----------|-----------|-------------|
392
- | `POST /storage/file/upload` | `file.create` | Upload a file (multipart/form-data) |
393
- | `POST /storage/file/get-all` | `file.read` | List files with pagination |
394
- | `POST /storage/file/get/:id` | `file.read` | Get file metadata by ID |
395
- | `POST /storage/file/get-url/:id` | `file.read` | Get presigned or direct file URL |
396
- | `POST /storage/file/update` | `file.update` | Update file metadata |
397
- | `POST /storage/file/delete` | `file.delete` | Delete file (from storage + DB) |
398
- | `POST /storage/file/move` | `file.update` | Move file to a different folder |
399
-
400
- ### File Serving (Local)
401
-
402
- ```
403
- GET /storage/upload/file/{path}
131
+ imports: [ConfigModule],
132
+ useFactory: (configService: ConfigService) => ({
133
+ tenantDefaultDatabaseConfig: {
134
+ type: 'mysql',
135
+ host: configService.get('TENANT_DB_HOST'),
136
+ port: configService.get<number>('TENANT_DB_PORT'),
137
+ username: configService.get('TENANT_DB_USER'),
138
+ password: configService.get('TENANT_DB_PASSWORD'),
139
+ database: configService.get('TENANT_DB_NAME'),
140
+ },
141
+ tenants: configService.get<ITenantDatabaseConfig[]>('TENANTS'),
142
+ maxFileSize: configService.get<number>('MAX_FILE_SIZE', 10 * 1024 * 1024),
143
+ appUrl: configService.get('APP_URL'),
144
+ localStoragePath: configService.get('UPLOAD_PATH', './uploads'),
145
+ }),
146
+ inject: [ConfigService],
147
+ });
404
148
  ```
405
149
 
406
- Served directly by `FileServeMiddleware`. No authentication (public file access). Register the middleware in your `AppModule`.
407
-
408
- ---
409
-
410
- ## Entities
411
-
412
- ### Core Entities (always registered)
413
-
414
- | Entity | Table | Description |
415
- |--------|-------|-------------|
416
- | `StorageConfig` | `storage_config` | Provider configuration (credentials, bucket, etc.) |
417
- | `Folder` | `storage_folder` | Folder hierarchy with `parentId` |
418
- | `FileManager` | `storage_file` | File metadata (name, path, size, MIME type, provider) |
419
-
420
- ### Company Feature Entities (`enableCompanyFeature: true`)
421
-
422
- | Entity | Table | Description |
423
- |--------|-------|-------------|
424
- | `StorageConfigWithCompany` | `storage_config` | Same + `companyId` |
425
- | `FolderWithCompany` | `storage_folder` | Same + `companyId` |
426
- | `FileManagerWithCompany` | `storage_file` | Same + `companyId` |
150
+ ## 2. Register Entities in TypeORM
427
151
 
428
152
  ```typescript
429
- import { StorageModule } from '@flusys/nestjs-storage';
153
+ import { getStorageEntitiesByConfig } from '@flusys/nestjs-storage/entities';
430
154
 
431
155
  TypeOrmModule.forRoot({
432
156
  entities: [
433
- ...StorageModule.getEntities({ enableCompanyFeature: true }),
157
+ ...getStorageEntitiesByConfig(true), // match enableCompanyFeature in bootstrapAppConfig
434
158
  ],
435
- })
436
- ```
437
-
438
- ---
439
-
440
- ## File Serving (Local)
441
-
442
- Register `FileServeMiddleware` to serve uploaded local files over HTTP:
443
-
444
- ```typescript
445
- import { FileServeMiddleware } from '@flusys/nestjs-storage';
446
-
447
- // In AppModule
448
- configure(consumer: MiddlewareConsumer) {
449
- consumer.apply(FileServeMiddleware).forRoutes('storage/upload/file');
450
- }
451
- ```
452
-
453
- Files are served from the `localStoragePath` directory. The middleware validates paths to prevent directory traversal attacks.
454
-
455
- ---
456
-
457
- ## Image Compression
458
-
459
- Automatic compression is applied on upload for supported image types:
460
-
461
- | Format | Compression applied |
462
- |--------|---------------------|
463
- | JPEG | Quality 80%, progressive |
464
- | PNG | Compression level 6 |
465
- | WebP | Quality 80% |
466
- | AVIF | Quality 75% |
467
- | TIFF | Deflate compression |
468
- | GIF | Passthrough |
469
-
470
- Custom compression options:
471
-
472
- ```json
473
- POST /storage/file/upload
474
- Content-Type: multipart/form-data
475
-
476
- {
477
- "file": <binary>,
478
- "folderId": "uuid",
479
- "compress": true,
480
- "maxWidth": 1920,
481
- "maxHeight": 1080,
482
- "quality": 75
483
- }
484
- ```
485
-
486
- ---
487
-
488
- ## File Validation
489
-
490
- Files are validated using **magic bytes** (file header inspection), not just the file extension or MIME type header. This prevents users from uploading malicious files with forged extensions.
491
-
492
- Allowed types are configured via `allowedFileTypes`:
493
- ```typescript
494
- allowedFileTypes: ['image/*', 'application/pdf', 'text/plain']
159
+ });
495
160
  ```
496
161
 
497
- Wildcard patterns like `image/*` match any subtype (e.g., `image/jpeg`, `image/png`, `image/webp`).
498
-
499
- ---
500
-
501
- ## Exported Services
502
-
503
- | Service | Description |
504
- |---------|-------------|
505
- | `FileManagerService` | File upload, retrieval, deletion, move |
506
- | `FolderService` | Folder CRUD and tree queries |
507
- | `StorageConfigService` | Provider config CRUD |
508
- | `StorageDataSourceProvider` | Dynamic DataSource per request |
509
-
510
- ---
511
-
512
- ## Programmatic Usage
513
-
514
- ```typescript
515
- import { FileManagerService } from '@flusys/nestjs-storage';
516
-
517
- @Injectable()
518
- export class ProfileService {
519
- constructor(
520
- @Inject(FileManagerService) private readonly fileManager: FileManagerService,
521
- ) {}
522
-
523
- async uploadAvatar(userId: string, fileBuffer: Buffer, filename: string): Promise<string> {
524
- const result = await this.fileManager.uploadFile({
525
- buffer: fileBuffer,
526
- originalName: filename,
527
- mimeType: 'image/jpeg',
528
- folderId: 'avatars-folder-id',
529
- });
530
- return result.id; // Store file ID on user profile
531
- }
532
-
533
- async getAvatarUrl(fileId: string): Promise<string> {
534
- return this.fileManager.getFileUrl(fileId);
535
- }
536
- }
537
- ```
538
-
539
- ---
540
-
541
- ## Troubleshooting
542
-
543
- **`Provider not registered` error**
544
-
545
- You created a StorageConfig with `storage: 'aws'` but forgot to register the S3 provider. Call `StorageProviderRegistry.register()` before the module initializes.
546
-
547
- ---
548
-
549
- **Local files return 404**
550
-
551
- Ensure `FileServeMiddleware` is registered and that `localStoragePath` points to an existing directory. The path must be accessible by the Node.js process.
552
-
553
- ---
554
-
555
- **`File type not allowed` for a valid file**
556
-
557
- Magic-byte validation is strict. If your file is being rejected incorrectly, check `allowedFileTypes` in your config and ensure the actual file header matches the declared MIME type.
558
-
559
- ---
560
-
561
- **Presigned URL expired immediately**
562
-
563
- S3 and Azure presigned URLs have a TTL. The default TTL is cached per file. Pass a custom TTL in the get-url request body:
564
- ```json
565
- POST /storage/file/get-url/:id
566
- { "ttl": 3600 }
567
- ```
568
-
569
- ---
570
162
 
571
163
  ## License
572
164
 
573
- MIT © FLUSYS
574
-
575
- ---
576
-
577
- > Part of the **FLUSYS** framework — a full-stack monorepo powering Angular 21 + NestJS 11 applications.
165
+ MIT © FLUSYS