@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.
- package/config/index.d.ts +3 -3
- package/controllers/file-manager.controller.d.ts +4 -4
- package/controllers/folder.controller.d.ts +2 -2
- package/controllers/index.d.ts +4 -4
- package/controllers/storage-config.controller.d.ts +2 -2
- package/controllers/upload.controller.d.ts +3 -3
- package/docs/index.d.ts +1 -1
- package/docs/storage-swagger.config.d.ts +2 -2
- package/dtos/index.d.ts +4 -4
- package/entities/file-manager-with-company.entity.d.ts +2 -2
- package/entities/file-manager.entity.d.ts +1 -1
- package/entities/folder-with-company.entity.d.ts +1 -1
- package/entities/index.d.ts +15 -15
- package/entities/storage-config-with-company.entity.d.ts +1 -1
- package/enums/index.d.ts +1 -1
- package/fesm/493.js +2768 -0
- package/fesm/537.js +1157 -0
- package/fesm/562.js +786 -0
- package/fesm/{providers/azure-provider.optional.js → chunks/190.js} +33 -30
- package/fesm/chunks/252.js +12 -0
- package/fesm/chunks/327.js +12 -0
- package/fesm/chunks/385.js +12 -0
- package/fesm/{providers/s3-provider.optional.js → chunks/591.js} +37 -34
- package/fesm/chunks/746.js +12 -0
- package/fesm/chunks/798.js +12 -0
- package/fesm/{providers/sftp-provider.optional.js → chunks/918.js} +35 -31
- package/fesm/config/index.js +119 -3
- package/fesm/controllers/index.js +16 -4
- package/fesm/docs/index.js +206 -1
- package/fesm/dtos/index.js +879 -4
- package/fesm/entities/index.js +463 -18
- package/fesm/enums/index.js +36 -1
- package/fesm/index.js +398 -12
- package/fesm/interfaces/index.js +19 -5
- package/fesm/modules/index.js +16 -1
- package/fesm/providers/index.js +364 -3
- package/fesm/runtime.js +252 -0
- package/fesm/services/index.js +16 -6
- package/fesm/utils/index.js +149 -1
- package/index.d.ts +11 -11
- package/interfaces/file-manager.interface.d.ts +1 -1
- package/interfaces/folder.interface.d.ts +1 -1
- package/interfaces/index.d.ts +5 -5
- package/interfaces/storage-config.interface.d.ts +1 -1
- package/interfaces/storage-module-options.interface.d.ts +3 -3
- package/interfaces/storage-provider.interface.d.ts +1 -1
- package/middlewares/file-serve.middleware.d.ts +4 -3
- package/middlewares/index.d.ts +1 -1
- package/modules/index.d.ts +1 -1
- package/modules/storage.module.d.ts +2 -2
- package/package.json +30 -41
- package/providers/azure-provider.optional.d.ts +2 -2
- package/providers/index.d.ts +3 -3
- package/providers/local-provider.d.ts +2 -2
- package/providers/s3-provider.optional.d.ts +2 -2
- package/providers/sftp-provider.optional.d.ts +2 -2
- package/providers/storage-factory.service.d.ts +3 -3
- package/providers/storage-provider.registry.d.ts +2 -2
- package/services/file-manager.service.d.ts +9 -8
- package/services/folder.service.d.ts +6 -6
- package/services/index.d.ts +6 -6
- package/services/storage-config.service.d.ts +3 -3
- package/services/storage-datasource.provider.d.ts +3 -3
- package/services/storage-provider-config.service.d.ts +7 -7
- package/services/upload.service.d.ts +6 -6
- package/utils/image-compressor.util.d.ts +1 -1
- package/utils/index.d.ts +1 -1
- package/cjs/config/event-actions.js +0 -34
- package/cjs/config/index.js +0 -20
- package/cjs/config/message-keys.js +0 -46
- package/cjs/config/storage.constants.js +0 -28
- package/cjs/controllers/file-manager.controller.js +0 -152
- package/cjs/controllers/file-manager.controller.spec.js +0 -135
- package/cjs/controllers/folder.controller.js +0 -102
- package/cjs/controllers/folder.controller.spec.js +0 -86
- package/cjs/controllers/index.js +0 -21
- package/cjs/controllers/storage-config.controller.js +0 -102
- package/cjs/controllers/storage-config.controller.spec.js +0 -85
- package/cjs/controllers/upload.controller.js +0 -234
- package/cjs/controllers/upload.controller.spec.js +0 -132
- package/cjs/docs/index.js +0 -11
- package/cjs/docs/storage-swagger.config.js +0 -188
- package/cjs/docs/storage-swagger.config.spec.js +0 -72
- package/cjs/dtos/file-manager.dto.js +0 -327
- package/cjs/dtos/folder.dto.js +0 -105
- package/cjs/dtos/index.js +0 -21
- package/cjs/dtos/storage-config.dto.js +0 -170
- package/cjs/dtos/upload.dto.js +0 -272
- package/cjs/entities/file-manager-with-company.entity.js +0 -61
- package/cjs/entities/file-manager.entity.js +0 -122
- package/cjs/entities/folder-with-company.entity.js +0 -55
- package/cjs/entities/folder.entity.js +0 -64
- package/cjs/entities/index.js +0 -63
- package/cjs/entities/index.spec.js +0 -18
- package/cjs/entities/storage-config-with-company.entity.js +0 -51
- package/cjs/entities/storage-config.entity.js +0 -96
- package/cjs/enums/file-location.enum.js +0 -17
- package/cjs/enums/index.js +0 -18
- package/cjs/index.js +0 -29
- package/cjs/interfaces/file-manager.interface.js +0 -4
- package/cjs/interfaces/folder.interface.js +0 -4
- package/cjs/interfaces/index.js +0 -22
- package/cjs/interfaces/storage-config.interface.js +0 -4
- package/cjs/interfaces/storage-module-options.interface.js +0 -4
- package/cjs/interfaces/storage-provider.interface.js +0 -4
- package/cjs/middlewares/file-serve.middleware.js +0 -241
- package/cjs/middlewares/file-serve.middleware.spec.js +0 -211
- package/cjs/middlewares/index.js +0 -18
- package/cjs/modules/index.js +0 -18
- package/cjs/modules/storage.module.js +0 -165
- package/cjs/modules/storage.module.spec.js +0 -184
- package/cjs/providers/azure-provider.optional.js +0 -155
- package/cjs/providers/azure-provider.optional.spec.js +0 -228
- package/cjs/providers/index.js +0 -26
- package/cjs/providers/local-provider.js +0 -230
- package/cjs/providers/local-provider.spec.js +0 -239
- package/cjs/providers/s3-provider.optional.js +0 -175
- package/cjs/providers/s3-provider.optional.spec.js +0 -243
- package/cjs/providers/sftp-provider.optional.js +0 -203
- package/cjs/providers/sftp-provider.optional.spec.js +0 -217
- package/cjs/providers/storage-factory.service.js +0 -175
- package/cjs/providers/storage-factory.service.spec.js +0 -253
- package/cjs/providers/storage-provider.registry.js +0 -41
- package/cjs/providers/storage-provider.registry.spec.js +0 -67
- package/cjs/services/file-manager.service.js +0 -387
- package/cjs/services/file-manager.service.spec.js +0 -580
- package/cjs/services/folder.service.js +0 -101
- package/cjs/services/folder.service.spec.js +0 -167
- package/cjs/services/index.js +0 -23
- package/cjs/services/storage-config.service.js +0 -129
- package/cjs/services/storage-config.service.spec.js +0 -257
- package/cjs/services/storage-datasource.provider.js +0 -137
- package/cjs/services/storage-datasource.provider.spec.js +0 -217
- package/cjs/services/storage-provider-config.service.js +0 -171
- package/cjs/services/storage-provider-config.service.spec.js +0 -265
- package/cjs/services/upload.service.js +0 -376
- package/cjs/services/upload.service.spec.js +0 -426
- package/cjs/utils/file-validator.util.js +0 -440
- package/cjs/utils/file-validator.util.spec.js +0 -259
- package/cjs/utils/image-compressor.util.js +0 -162
- package/cjs/utils/image-compressor.util.spec.js +0 -166
- package/cjs/utils/index.js +0 -18
- package/fesm/config/event-actions.js +0 -13
- package/fesm/config/message-keys.js +0 -22
- package/fesm/config/storage.constants.js +0 -7
- package/fesm/controllers/file-manager.controller.js +0 -142
- package/fesm/controllers/file-manager.controller.spec.js +0 -131
- package/fesm/controllers/folder.controller.js +0 -92
- package/fesm/controllers/folder.controller.spec.js +0 -82
- package/fesm/controllers/storage-config.controller.js +0 -92
- package/fesm/controllers/storage-config.controller.spec.js +0 -81
- package/fesm/controllers/upload.controller.js +0 -224
- package/fesm/controllers/upload.controller.spec.js +0 -128
- package/fesm/docs/storage-swagger.config.js +0 -181
- package/fesm/docs/storage-swagger.config.spec.js +0 -68
- package/fesm/dtos/file-manager.dto.js +0 -300
- package/fesm/dtos/folder.dto.js +0 -84
- package/fesm/dtos/storage-config.dto.js +0 -149
- package/fesm/dtos/upload.dto.js +0 -245
- package/fesm/entities/file-manager-with-company.entity.js +0 -51
- package/fesm/entities/file-manager.entity.js +0 -112
- package/fesm/entities/folder-with-company.entity.js +0 -45
- package/fesm/entities/folder.entity.js +0 -54
- package/fesm/entities/index.spec.js +0 -14
- package/fesm/entities/storage-config-with-company.entity.js +0 -41
- package/fesm/entities/storage-config.entity.js +0 -86
- package/fesm/enums/file-location.enum.js +0 -7
- package/fesm/interfaces/file-manager.interface.js +0 -1
- package/fesm/interfaces/folder.interface.js +0 -1
- package/fesm/interfaces/storage-config.interface.js +0 -1
- package/fesm/interfaces/storage-module-options.interface.js +0 -3
- package/fesm/interfaces/storage-provider.interface.js +0 -4
- package/fesm/middlewares/file-serve.middleware.js +0 -190
- package/fesm/middlewares/file-serve.middleware.spec.js +0 -207
- package/fesm/middlewares/index.js +0 -1
- package/fesm/modules/storage.module.js +0 -156
- package/fesm/modules/storage.module.spec.js +0 -180
- package/fesm/providers/azure-provider.optional.spec.js +0 -224
- package/fesm/providers/local-provider.js +0 -182
- package/fesm/providers/local-provider.spec.js +0 -194
- package/fesm/providers/s3-provider.optional.spec.js +0 -239
- package/fesm/providers/sftp-provider.optional.spec.js +0 -213
- package/fesm/providers/storage-factory.service.js +0 -124
- package/fesm/providers/storage-factory.service.spec.js +0 -249
- package/fesm/providers/storage-provider.registry.js +0 -34
- package/fesm/providers/storage-provider.registry.spec.js +0 -63
- package/fesm/services/file-manager.service.js +0 -336
- package/fesm/services/file-manager.service.spec.js +0 -576
- package/fesm/services/folder.service.js +0 -91
- package/fesm/services/folder.service.spec.js +0 -163
- package/fesm/services/storage-config.service.js +0 -119
- package/fesm/services/storage-config.service.spec.js +0 -253
- package/fesm/services/storage-datasource.provider.js +0 -86
- package/fesm/services/storage-datasource.provider.spec.js +0 -172
- package/fesm/services/storage-provider-config.service.js +0 -161
- package/fesm/services/storage-provider-config.service.spec.js +0 -261
- package/fesm/services/upload.service.js +0 -366
- package/fesm/services/upload.service.spec.js +0 -422
- package/fesm/utils/file-validator.util.js +0 -433
- package/fesm/utils/file-validator.util.spec.js +0 -255
- package/fesm/utils/image-compressor.util.js +0 -114
- package/fesm/utils/image-compressor.util.spec.js +0 -162
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
import { buildMockUser } from '@test-utils/mocks/logged-user.mock';
|
|
2
|
-
import { UploadController } from './upload.controller';
|
|
3
|
-
describe('UploadController', ()=>{
|
|
4
|
-
let controller;
|
|
5
|
-
let mockUploadService;
|
|
6
|
-
beforeEach(()=>{
|
|
7
|
-
mockUploadService = {
|
|
8
|
-
bytesToKb: jest.fn((bytes)=>bytes / 1024),
|
|
9
|
-
uploadSingleFile: jest.fn(),
|
|
10
|
-
uploadMultipleFiles: jest.fn(),
|
|
11
|
-
deleteSingleFile: jest.fn(),
|
|
12
|
-
deleteMultipleFile: jest.fn()
|
|
13
|
-
};
|
|
14
|
-
controller = new UploadController(mockUploadService);
|
|
15
|
-
});
|
|
16
|
-
describe('uploadSingleFile', ()=>{
|
|
17
|
-
it('uploads a file and maps the response payload', async ()=>{
|
|
18
|
-
const user = buildMockUser();
|
|
19
|
-
const file = {
|
|
20
|
-
originalname: 'photo.png'
|
|
21
|
-
};
|
|
22
|
-
mockUploadService.uploadSingleFile.mockResolvedValue({
|
|
23
|
-
size: 2048,
|
|
24
|
-
name: 'photo.png',
|
|
25
|
-
key: 'files/photo.png',
|
|
26
|
-
contentType: 'image/png',
|
|
27
|
-
location: 's3',
|
|
28
|
-
storageConfigId: 'storage-1'
|
|
29
|
-
});
|
|
30
|
-
const result = await controller.uploadSingleFile(file, {
|
|
31
|
-
folderPath: 'photos'
|
|
32
|
-
}, user);
|
|
33
|
-
expect(mockUploadService.uploadSingleFile).toHaveBeenCalledWith(file, {
|
|
34
|
-
folderPath: 'photos'
|
|
35
|
-
}, user);
|
|
36
|
-
expect(mockUploadService.bytesToKb).toHaveBeenCalledWith(2048);
|
|
37
|
-
expect(result).toEqual({
|
|
38
|
-
success: true,
|
|
39
|
-
message: 'File uploaded successfully',
|
|
40
|
-
messageKey: expect.any(String),
|
|
41
|
-
data: {
|
|
42
|
-
size: 2,
|
|
43
|
-
name: 'photo.png',
|
|
44
|
-
key: 'files/photo.png',
|
|
45
|
-
contentType: 'image/png',
|
|
46
|
-
location: 's3',
|
|
47
|
-
storageConfigId: 'storage-1'
|
|
48
|
-
}
|
|
49
|
-
});
|
|
50
|
-
});
|
|
51
|
-
it('defaults location to "local" and storageConfigId to an empty string when absent', async ()=>{
|
|
52
|
-
const user = buildMockUser();
|
|
53
|
-
mockUploadService.uploadSingleFile.mockResolvedValue({
|
|
54
|
-
size: 100,
|
|
55
|
-
name: 'file.txt',
|
|
56
|
-
key: 'files/file.txt',
|
|
57
|
-
contentType: 'text/plain'
|
|
58
|
-
});
|
|
59
|
-
const result = await controller.uploadSingleFile({}, {}, user);
|
|
60
|
-
expect(result.data.location).toBe('local');
|
|
61
|
-
expect(result.data.storageConfigId).toBe('');
|
|
62
|
-
});
|
|
63
|
-
});
|
|
64
|
-
describe('uploadMultipleFiles', ()=>{
|
|
65
|
-
it('uploads multiple files and maps each response payload', async ()=>{
|
|
66
|
-
const user = buildMockUser();
|
|
67
|
-
const files = [
|
|
68
|
-
{
|
|
69
|
-
originalname: 'a.png'
|
|
70
|
-
},
|
|
71
|
-
{
|
|
72
|
-
originalname: 'b.png'
|
|
73
|
-
}
|
|
74
|
-
];
|
|
75
|
-
mockUploadService.uploadMultipleFiles.mockResolvedValue([
|
|
76
|
-
{
|
|
77
|
-
size: 100,
|
|
78
|
-
name: 'a.png',
|
|
79
|
-
key: 'files/a.png',
|
|
80
|
-
contentType: 'image/png'
|
|
81
|
-
},
|
|
82
|
-
{
|
|
83
|
-
size: 200,
|
|
84
|
-
name: 'b.png',
|
|
85
|
-
key: 'files/b.png',
|
|
86
|
-
contentType: 'image/png'
|
|
87
|
-
}
|
|
88
|
-
]);
|
|
89
|
-
const result = await controller.uploadMultipleFiles(files, {}, user);
|
|
90
|
-
expect(mockUploadService.uploadMultipleFiles).toHaveBeenCalledWith(files, {}, user);
|
|
91
|
-
expect(result.data).toHaveLength(2);
|
|
92
|
-
expect(result.data[0]).toEqual(expect.objectContaining({
|
|
93
|
-
name: 'a.png',
|
|
94
|
-
key: 'files/a.png'
|
|
95
|
-
}));
|
|
96
|
-
});
|
|
97
|
-
});
|
|
98
|
-
describe('deleteSingleFile', ()=>{
|
|
99
|
-
it('deletes a file by key and storage config', async ()=>{
|
|
100
|
-
const user = buildMockUser();
|
|
101
|
-
mockUploadService.deleteSingleFile.mockResolvedValue(true);
|
|
102
|
-
const result = await controller.deleteSingleFile({
|
|
103
|
-
key: 'files/a.png',
|
|
104
|
-
storageConfigId: 'storage-1'
|
|
105
|
-
}, user);
|
|
106
|
-
expect(mockUploadService.deleteSingleFile).toHaveBeenCalledWith('files/a.png', 'storage-1', user);
|
|
107
|
-
expect(result.data).toBe(true);
|
|
108
|
-
});
|
|
109
|
-
});
|
|
110
|
-
describe('deleteMultipleFile', ()=>{
|
|
111
|
-
it('deletes multiple files by keys and storage config', async ()=>{
|
|
112
|
-
const user = buildMockUser();
|
|
113
|
-
mockUploadService.deleteMultipleFile.mockResolvedValue(true);
|
|
114
|
-
const result = await controller.deleteMultipleFile({
|
|
115
|
-
keys: [
|
|
116
|
-
'files/a.png',
|
|
117
|
-
'files/b.png'
|
|
118
|
-
],
|
|
119
|
-
storageConfigId: 'storage-1'
|
|
120
|
-
}, user);
|
|
121
|
-
expect(mockUploadService.deleteMultipleFile).toHaveBeenCalledWith([
|
|
122
|
-
'files/a.png',
|
|
123
|
-
'files/b.png'
|
|
124
|
-
], 'storage-1', user);
|
|
125
|
-
expect(result.data).toBe(true);
|
|
126
|
-
});
|
|
127
|
-
});
|
|
128
|
-
});
|
|
@@ -1,181 +0,0 @@
|
|
|
1
|
-
const COMPANY_SCHEMA_EXCLUSIONS = [
|
|
2
|
-
{
|
|
3
|
-
schemaName: 'FileManagerResponseDto',
|
|
4
|
-
properties: [
|
|
5
|
-
'companyId'
|
|
6
|
-
]
|
|
7
|
-
},
|
|
8
|
-
{
|
|
9
|
-
schemaName: 'FolderResponseDto',
|
|
10
|
-
properties: [
|
|
11
|
-
'companyId'
|
|
12
|
-
]
|
|
13
|
-
},
|
|
14
|
-
{
|
|
15
|
-
schemaName: 'StorageConfigResponseDto',
|
|
16
|
-
properties: [
|
|
17
|
-
'companyId'
|
|
18
|
-
]
|
|
19
|
-
}
|
|
20
|
-
];
|
|
21
|
-
function buildDescription(enableCompanyFeature, isMultiTenant) {
|
|
22
|
-
const multiTenantNote = isMultiTenant ? `\n> **Multi-Tenant Mode**: Include \`x-tenant-id\` header to target a specific tenant database.\n` : '';
|
|
23
|
-
const companyIsolation = enableCompanyFeature ? '\n- **Company isolation**' : '';
|
|
24
|
-
const companyPermissions = enableCompanyFeature ? '\n- **Company-level permissions**' : '';
|
|
25
|
-
const multiTenantSection = enableCompanyFeature ? `
|
|
26
|
-
## Multi-Tenant Support
|
|
27
|
-
|
|
28
|
-
Files are automatically isolated by:
|
|
29
|
-
- **Company ID**: Company-level storage
|
|
30
|
-
|
|
31
|
-
All queries automatically filter by current user's company context.
|
|
32
|
-
` : '';
|
|
33
|
-
const examplesSection = enableCompanyFeature ? `
|
|
34
|
-
## Examples
|
|
35
|
-
|
|
36
|
-
### Upload a File
|
|
37
|
-
|
|
38
|
-
POST /storage/upload/single-file with multipart/form-data containing file and optional folderId
|
|
39
|
-
|
|
40
|
-
### Get All Files
|
|
41
|
-
|
|
42
|
-
POST /storage/file-manager/get-all with JSON body containing page and pageSize parameters
|
|
43
|
-
|
|
44
|
-
### Create Folder
|
|
45
|
-
|
|
46
|
-
POST /storage/folder/insert with JSON body containing folder name
|
|
47
|
-
` : '';
|
|
48
|
-
return `
|
|
49
|
-
# Storage Management API
|
|
50
|
-
${multiTenantNote}
|
|
51
|
-
Complete file storage and management system${enableCompanyFeature ? ' with multi-tenant support' : ''}.
|
|
52
|
-
|
|
53
|
-
## Features
|
|
54
|
-
|
|
55
|
-
### 📁 File Management
|
|
56
|
-
- Upload single or multiple files
|
|
57
|
-
- Download files with presigned URLs
|
|
58
|
-
- Delete files (single or batch)
|
|
59
|
-
- File metadata management
|
|
60
|
-
- Automatic compression and optimization${companyIsolation}
|
|
61
|
-
|
|
62
|
-
### 🗂️ Folder Organization
|
|
63
|
-
- Create folder hierarchies
|
|
64
|
-
- Organize files by folders${companyPermissions}
|
|
65
|
-
- Automatic context filtering
|
|
66
|
-
|
|
67
|
-
### ⚙️ Storage Configuration
|
|
68
|
-
- Multiple storage providers (AWS S3, Azure, SFTP, Local)
|
|
69
|
-
- Provider-specific configurations
|
|
70
|
-
- Default storage settings
|
|
71
|
-
- Dynamic provider switching
|
|
72
|
-
|
|
73
|
-
### 🔒 Security
|
|
74
|
-
- JWT authentication required${companyIsolation}
|
|
75
|
-
- File access control
|
|
76
|
-
- Presigned URL generation
|
|
77
|
-
|
|
78
|
-
## Storage Providers
|
|
79
|
-
|
|
80
|
-
### AWS S3
|
|
81
|
-
- Scalable cloud storage
|
|
82
|
-
- Automatic redundancy
|
|
83
|
-
- Global CDN integration
|
|
84
|
-
|
|
85
|
-
### Azure Blob Storage
|
|
86
|
-
- Microsoft Azure integration
|
|
87
|
-
- Geo-redundant storage
|
|
88
|
-
- Advanced security features
|
|
89
|
-
|
|
90
|
-
### SFTP
|
|
91
|
-
- Secure file transfer
|
|
92
|
-
- Legacy system integration
|
|
93
|
-
- Custom server support
|
|
94
|
-
|
|
95
|
-
### Local Storage
|
|
96
|
-
- Same-server storage
|
|
97
|
-
- Fast access
|
|
98
|
-
- No external dependencies
|
|
99
|
-
${multiTenantSection}
|
|
100
|
-
## File Validation
|
|
101
|
-
|
|
102
|
-
### Size Limits
|
|
103
|
-
Configure maximum file size per upload (default: 10MB)
|
|
104
|
-
|
|
105
|
-
### File Types
|
|
106
|
-
Restrict allowed file types (default: images, PDFs)
|
|
107
|
-
|
|
108
|
-
### Image Compression
|
|
109
|
-
- Automatic image optimization
|
|
110
|
-
- Format conversion (JPEG, PNG, WebP, AVIF)
|
|
111
|
-
- Resize to maximum dimensions
|
|
112
|
-
- Quality control
|
|
113
|
-
|
|
114
|
-
## Response Format
|
|
115
|
-
|
|
116
|
-
All endpoints return standardized responses with success status, data, message, and timestamp.
|
|
117
|
-
|
|
118
|
-
## Error Handling
|
|
119
|
-
|
|
120
|
-
Errors return structured responses with success false, error message, error code, and timestamp.
|
|
121
|
-
|
|
122
|
-
## Common Error Codes
|
|
123
|
-
|
|
124
|
-
- **400 Bad Request**: Invalid input data
|
|
125
|
-
- **401 Unauthorized**: Missing or invalid authentication
|
|
126
|
-
- **403 Forbidden**: Insufficient permissions
|
|
127
|
-
- **404 Not Found**: Resource not found
|
|
128
|
-
- **413 Payload Too Large**: File exceeds size limit
|
|
129
|
-
- **415 Unsupported Media Type**: File type not allowed
|
|
130
|
-
- **500 Internal Server Error**: Server error
|
|
131
|
-
|
|
132
|
-
## Getting Started
|
|
133
|
-
|
|
134
|
-
1. **Authenticate**: Obtain JWT token from /auth/login
|
|
135
|
-
2. **Configure Storage**: Create storage configuration via /storage/storage-config/insert
|
|
136
|
-
3. **Create Folders**: Organize files with /storage/folder/insert
|
|
137
|
-
4. **Upload Files**: Upload files via /storage/upload/single-file
|
|
138
|
-
5. **Manage Files**: Track uploads in /storage/file-manager
|
|
139
|
-
|
|
140
|
-
## API Endpoints
|
|
141
|
-
|
|
142
|
-
All storage endpoints are prefixed with \`/storage\`:
|
|
143
|
-
- \`/storage/file-manager/*\` - File metadata management
|
|
144
|
-
- \`/storage/folder/*\` - Folder organization
|
|
145
|
-
- \`/storage/storage-config/*\` - Storage configuration
|
|
146
|
-
- \`/storage/upload/*\` - File upload/download
|
|
147
|
-
${examplesSection}
|
|
148
|
-
## Best Practices
|
|
149
|
-
|
|
150
|
-
1. **Always authenticate**: Include JWT token in Authorization header
|
|
151
|
-
2. **Use folders**: Organize files hierarchically
|
|
152
|
-
3. **Check file size**: Validate before upload
|
|
153
|
-
4. **Handle errors**: Implement proper error handling
|
|
154
|
-
5. **Clean up**: Delete unused files regularly
|
|
155
|
-
6. **Use presigned URLs**: Generate temporary URLs for file access
|
|
156
|
-
7. **Compress images**: Enable compression for faster loading
|
|
157
|
-
|
|
158
|
-
## Rate Limiting
|
|
159
|
-
|
|
160
|
-
All endpoints are rate-limited to prevent abuse. Default limits:
|
|
161
|
-
- **Upload**: 100 requests per hour
|
|
162
|
-
- **Download**: 1000 requests per hour
|
|
163
|
-
- **Delete**: 50 requests per hour
|
|
164
|
-
`;
|
|
165
|
-
}
|
|
166
|
-
/**
|
|
167
|
-
* Storage Module Swagger Configuration
|
|
168
|
-
* Use this with setupSwaggerDocs() from @flusys/nestjs-core
|
|
169
|
-
*/ export function storageSwaggerConfig(bootstrapConfig) {
|
|
170
|
-
const enableCompanyFeature = bootstrapConfig?.enableCompanyFeature ?? true;
|
|
171
|
-
const isMultiTenant = bootstrapConfig?.databaseMode === 'multi-tenant';
|
|
172
|
-
return {
|
|
173
|
-
title: 'Storage API',
|
|
174
|
-
description: buildDescription(enableCompanyFeature, isMultiTenant),
|
|
175
|
-
version: '1.0',
|
|
176
|
-
path: 'api/docs/storage',
|
|
177
|
-
bearerAuth: true,
|
|
178
|
-
excludeTags: [],
|
|
179
|
-
excludeSchemaProperties: enableCompanyFeature ? undefined : COMPANY_SCHEMA_EXCLUSIONS
|
|
180
|
-
};
|
|
181
|
-
}
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
import { storageSwaggerConfig } from './storage-swagger.config';
|
|
2
|
-
describe('storageSwaggerConfig', ()=>{
|
|
3
|
-
it('should default to company feature enabled and single-database mode when no config is given', ()=>{
|
|
4
|
-
const config = storageSwaggerConfig();
|
|
5
|
-
expect(config.title).toBe('Storage API');
|
|
6
|
-
expect(config.path).toBe('api/docs/storage');
|
|
7
|
-
expect(config.bearerAuth).toBe(true);
|
|
8
|
-
expect(config.excludeTags).toEqual([]);
|
|
9
|
-
expect(config.excludeSchemaProperties).toBeUndefined();
|
|
10
|
-
expect(config.description).not.toContain('Multi-Tenant Mode');
|
|
11
|
-
expect(config.description).toContain('with multi-tenant support');
|
|
12
|
-
});
|
|
13
|
-
it('should exclude companyId schema properties when the company feature is disabled', ()=>{
|
|
14
|
-
const config = storageSwaggerConfig({
|
|
15
|
-
enableCompanyFeature: false
|
|
16
|
-
});
|
|
17
|
-
expect(config.excludeSchemaProperties).toEqual([
|
|
18
|
-
{
|
|
19
|
-
schemaName: 'FileManagerResponseDto',
|
|
20
|
-
properties: [
|
|
21
|
-
'companyId'
|
|
22
|
-
]
|
|
23
|
-
},
|
|
24
|
-
{
|
|
25
|
-
schemaName: 'FolderResponseDto',
|
|
26
|
-
properties: [
|
|
27
|
-
'companyId'
|
|
28
|
-
]
|
|
29
|
-
},
|
|
30
|
-
{
|
|
31
|
-
schemaName: 'StorageConfigResponseDto',
|
|
32
|
-
properties: [
|
|
33
|
-
'companyId'
|
|
34
|
-
]
|
|
35
|
-
}
|
|
36
|
-
]);
|
|
37
|
-
});
|
|
38
|
-
it('should not exclude any schema properties when the company feature is enabled', ()=>{
|
|
39
|
-
const config = storageSwaggerConfig({
|
|
40
|
-
enableCompanyFeature: true
|
|
41
|
-
});
|
|
42
|
-
expect(config.excludeSchemaProperties).toBeUndefined();
|
|
43
|
-
});
|
|
44
|
-
it('should describe company isolation and multi-tenant sections only when the company feature is enabled', ()=>{
|
|
45
|
-
const withCompany = storageSwaggerConfig({
|
|
46
|
-
enableCompanyFeature: true
|
|
47
|
-
});
|
|
48
|
-
const withoutCompany = storageSwaggerConfig({
|
|
49
|
-
enableCompanyFeature: false
|
|
50
|
-
});
|
|
51
|
-
expect(withCompany.description).toContain('Company isolation');
|
|
52
|
-
expect(withCompany.description).toContain('## Multi-Tenant Support');
|
|
53
|
-
expect(withCompany.description).toContain('## Examples');
|
|
54
|
-
expect(withoutCompany.description).not.toContain('Company isolation');
|
|
55
|
-
expect(withoutCompany.description).not.toContain('## Multi-Tenant Support');
|
|
56
|
-
expect(withoutCompany.description).not.toContain('## Examples');
|
|
57
|
-
});
|
|
58
|
-
it('should mention multi-tenant mode in the description only for multi-tenant database mode', ()=>{
|
|
59
|
-
const multiTenant = storageSwaggerConfig({
|
|
60
|
-
databaseMode: 'multi-tenant'
|
|
61
|
-
});
|
|
62
|
-
const single = storageSwaggerConfig({
|
|
63
|
-
databaseMode: 'single'
|
|
64
|
-
});
|
|
65
|
-
expect(multiTenant.description).toContain('Multi-Tenant Mode');
|
|
66
|
-
expect(single.description).not.toContain('Multi-Tenant Mode');
|
|
67
|
-
});
|
|
68
|
-
});
|
|
@@ -1,300 +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 { ApiProperty, ApiPropertyOptional, PartialType } from '@nestjs/swagger';
|
|
24
|
-
import { Expose } from 'class-transformer';
|
|
25
|
-
import { IsBoolean, IsEnum, IsNotEmpty, IsOptional, IsString, IsUUID } from 'class-validator';
|
|
26
|
-
import { FileLocationEnum } from '../enums';
|
|
27
|
-
export class CreateFileManagerDto {
|
|
28
|
-
constructor(){
|
|
29
|
-
_define_property(this, "name", void 0);
|
|
30
|
-
_define_property(this, "key", void 0);
|
|
31
|
-
_define_property(this, "size", void 0);
|
|
32
|
-
_define_property(this, "contentType", void 0);
|
|
33
|
-
_define_property(this, "isPrivate", void 0);
|
|
34
|
-
_define_property(this, "folderId", void 0);
|
|
35
|
-
_define_property(this, "storageConfigId", void 0);
|
|
36
|
-
_define_property(this, "location", void 0);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
_ts_decorate([
|
|
40
|
-
ApiProperty({
|
|
41
|
-
description: 'Original file name',
|
|
42
|
-
example: 'vacation-photo.jpg'
|
|
43
|
-
}),
|
|
44
|
-
IsNotEmpty(),
|
|
45
|
-
IsString(),
|
|
46
|
-
_ts_metadata("design:type", String)
|
|
47
|
-
], CreateFileManagerDto.prototype, "name", void 0);
|
|
48
|
-
_ts_decorate([
|
|
49
|
-
ApiProperty({
|
|
50
|
-
description: 'Storage key or path of the file',
|
|
51
|
-
example: 'uploads/abc123-vacation-photo.jpg'
|
|
52
|
-
}),
|
|
53
|
-
IsNotEmpty(),
|
|
54
|
-
IsString(),
|
|
55
|
-
_ts_metadata("design:type", String)
|
|
56
|
-
], CreateFileManagerDto.prototype, "key", void 0);
|
|
57
|
-
_ts_decorate([
|
|
58
|
-
ApiProperty({
|
|
59
|
-
description: 'Size of the file in bytes',
|
|
60
|
-
example: '1024'
|
|
61
|
-
}),
|
|
62
|
-
IsNotEmpty(),
|
|
63
|
-
IsString(),
|
|
64
|
-
_ts_metadata("design:type", String)
|
|
65
|
-
], CreateFileManagerDto.prototype, "size", void 0);
|
|
66
|
-
_ts_decorate([
|
|
67
|
-
ApiProperty({
|
|
68
|
-
description: 'MIME type of file',
|
|
69
|
-
example: 'image/jpeg'
|
|
70
|
-
}),
|
|
71
|
-
IsNotEmpty(),
|
|
72
|
-
IsString(),
|
|
73
|
-
_ts_metadata("design:type", String)
|
|
74
|
-
], CreateFileManagerDto.prototype, "contentType", void 0);
|
|
75
|
-
_ts_decorate([
|
|
76
|
-
ApiProperty({
|
|
77
|
-
description: 'Indicates if the file is private',
|
|
78
|
-
example: true
|
|
79
|
-
}),
|
|
80
|
-
IsNotEmpty(),
|
|
81
|
-
IsBoolean(),
|
|
82
|
-
_ts_metadata("design:type", Boolean)
|
|
83
|
-
], CreateFileManagerDto.prototype, "isPrivate", void 0);
|
|
84
|
-
_ts_decorate([
|
|
85
|
-
ApiPropertyOptional({
|
|
86
|
-
description: 'Folder ID (UUID)',
|
|
87
|
-
example: 'f2e9c8d0-7a2a-11eb-9439-0242ac130002'
|
|
88
|
-
}),
|
|
89
|
-
IsOptional(),
|
|
90
|
-
IsUUID(),
|
|
91
|
-
_ts_metadata("design:type", String)
|
|
92
|
-
], CreateFileManagerDto.prototype, "folderId", void 0);
|
|
93
|
-
_ts_decorate([
|
|
94
|
-
ApiPropertyOptional({
|
|
95
|
-
description: 'Storage configuration ID',
|
|
96
|
-
example: '123e4567-e89b-12d3-a456-426614174000'
|
|
97
|
-
}),
|
|
98
|
-
IsOptional(),
|
|
99
|
-
IsUUID(),
|
|
100
|
-
_ts_metadata("design:type", String)
|
|
101
|
-
], CreateFileManagerDto.prototype, "storageConfigId", void 0);
|
|
102
|
-
_ts_decorate([
|
|
103
|
-
ApiPropertyOptional({
|
|
104
|
-
description: 'Storage location type',
|
|
105
|
-
enum: FileLocationEnum,
|
|
106
|
-
example: FileLocationEnum.AWS
|
|
107
|
-
}),
|
|
108
|
-
IsOptional(),
|
|
109
|
-
IsEnum(FileLocationEnum),
|
|
110
|
-
_ts_metadata("design:type", String)
|
|
111
|
-
], CreateFileManagerDto.prototype, "location", void 0);
|
|
112
|
-
export class UpdateFileManagerDto extends PartialType(CreateFileManagerDto) {
|
|
113
|
-
constructor(...args){
|
|
114
|
-
super(...args), _define_property(this, "id", void 0);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
_ts_decorate([
|
|
118
|
-
ApiProperty({
|
|
119
|
-
description: 'Unique identifier of the file',
|
|
120
|
-
example: 'f2e9c8d0-7a2a-11eb-9439-0242ac130002'
|
|
121
|
-
}),
|
|
122
|
-
IsUUID(),
|
|
123
|
-
IsNotEmpty(),
|
|
124
|
-
_ts_metadata("design:type", String)
|
|
125
|
-
], UpdateFileManagerDto.prototype, "id", void 0);
|
|
126
|
-
export class FileManagerResponseDto {
|
|
127
|
-
constructor(){
|
|
128
|
-
_define_property(this, "id", void 0);
|
|
129
|
-
_define_property(this, "name", void 0);
|
|
130
|
-
_define_property(this, "key", void 0);
|
|
131
|
-
_define_property(this, "size", void 0);
|
|
132
|
-
_define_property(this, "contentType", void 0);
|
|
133
|
-
_define_property(this, "isPrivate", void 0);
|
|
134
|
-
_define_property(this, "folderId", void 0);
|
|
135
|
-
_define_property(this, "storageConfigId", void 0);
|
|
136
|
-
_define_property(this, "location", void 0);
|
|
137
|
-
_define_property(this, "providerName", void 0);
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
_ts_decorate([
|
|
141
|
-
Expose(),
|
|
142
|
-
ApiProperty({
|
|
143
|
-
description: 'Unique identifier of the file',
|
|
144
|
-
example: 'f2e9c8d0-7a2a-11eb-9439-0242ac130002'
|
|
145
|
-
}),
|
|
146
|
-
_ts_metadata("design:type", String)
|
|
147
|
-
], FileManagerResponseDto.prototype, "id", void 0);
|
|
148
|
-
_ts_decorate([
|
|
149
|
-
Expose(),
|
|
150
|
-
ApiProperty({
|
|
151
|
-
description: 'Original filename as uploaded',
|
|
152
|
-
example: 'profile-photo.png'
|
|
153
|
-
}),
|
|
154
|
-
_ts_metadata("design:type", String)
|
|
155
|
-
], FileManagerResponseDto.prototype, "name", void 0);
|
|
156
|
-
_ts_decorate([
|
|
157
|
-
Expose(),
|
|
158
|
-
ApiProperty({
|
|
159
|
-
description: 'Storage key / path used to retrieve the file from the provider',
|
|
160
|
-
example: 'uploads/users/abc123/profile-photo.png'
|
|
161
|
-
}),
|
|
162
|
-
_ts_metadata("design:type", String)
|
|
163
|
-
], FileManagerResponseDto.prototype, "key", void 0);
|
|
164
|
-
_ts_decorate([
|
|
165
|
-
Expose(),
|
|
166
|
-
ApiProperty({
|
|
167
|
-
description: 'File size in bytes',
|
|
168
|
-
example: '204800'
|
|
169
|
-
}),
|
|
170
|
-
_ts_metadata("design:type", String)
|
|
171
|
-
], FileManagerResponseDto.prototype, "size", void 0);
|
|
172
|
-
_ts_decorate([
|
|
173
|
-
Expose(),
|
|
174
|
-
ApiProperty({
|
|
175
|
-
description: 'MIME type of the file',
|
|
176
|
-
example: 'image/png'
|
|
177
|
-
}),
|
|
178
|
-
_ts_metadata("design:type", String)
|
|
179
|
-
], FileManagerResponseDto.prototype, "contentType", void 0);
|
|
180
|
-
_ts_decorate([
|
|
181
|
-
Expose(),
|
|
182
|
-
ApiProperty({
|
|
183
|
-
description: 'Whether the file is private and requires signed URL access',
|
|
184
|
-
example: false
|
|
185
|
-
}),
|
|
186
|
-
_ts_metadata("design:type", Boolean)
|
|
187
|
-
], FileManagerResponseDto.prototype, "isPrivate", void 0);
|
|
188
|
-
_ts_decorate([
|
|
189
|
-
Expose(),
|
|
190
|
-
ApiPropertyOptional({
|
|
191
|
-
description: 'ID of the folder this file belongs to; absent if in root',
|
|
192
|
-
example: 'a1b2c3d4-0000-0000-0000-000000000001'
|
|
193
|
-
}),
|
|
194
|
-
_ts_metadata("design:type", String)
|
|
195
|
-
], FileManagerResponseDto.prototype, "folderId", void 0);
|
|
196
|
-
_ts_decorate([
|
|
197
|
-
Expose(),
|
|
198
|
-
ApiPropertyOptional({
|
|
199
|
-
description: 'ID of the storage configuration used to store this file',
|
|
200
|
-
example: 'a1b2c3d4-0000-0000-0000-000000000002'
|
|
201
|
-
}),
|
|
202
|
-
_ts_metadata("design:type", String)
|
|
203
|
-
], FileManagerResponseDto.prototype, "storageConfigId", void 0);
|
|
204
|
-
_ts_decorate([
|
|
205
|
-
Expose(),
|
|
206
|
-
ApiPropertyOptional({
|
|
207
|
-
description: 'Storage provider type (e.g. s3, gcs, local)',
|
|
208
|
-
example: 's3'
|
|
209
|
-
}),
|
|
210
|
-
_ts_metadata("design:type", String)
|
|
211
|
-
], FileManagerResponseDto.prototype, "location", void 0);
|
|
212
|
-
_ts_decorate([
|
|
213
|
-
Expose(),
|
|
214
|
-
ApiPropertyOptional({
|
|
215
|
-
description: 'Human-readable name of the storage provider',
|
|
216
|
-
example: 'AWS S3 — Production Bucket'
|
|
217
|
-
}),
|
|
218
|
-
_ts_metadata("design:type", String)
|
|
219
|
-
], FileManagerResponseDto.prototype, "providerName", void 0);
|
|
220
|
-
export class GetFilesRequestDto {
|
|
221
|
-
constructor(){
|
|
222
|
-
_define_property(this, "id", void 0);
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
_ts_decorate([
|
|
226
|
-
ApiProperty({
|
|
227
|
-
description: 'Unique identifier of the file to retrieve',
|
|
228
|
-
example: 'f2e9c8d0-7a2a-11eb-9439-0242ac130002'
|
|
229
|
-
}),
|
|
230
|
-
IsUUID(),
|
|
231
|
-
IsNotEmpty(),
|
|
232
|
-
_ts_metadata("design:type", String)
|
|
233
|
-
], GetFilesRequestDto.prototype, "id", void 0);
|
|
234
|
-
export class FilesResponseDto {
|
|
235
|
-
constructor(){
|
|
236
|
-
_define_property(this, "id", void 0);
|
|
237
|
-
_define_property(this, "name", void 0);
|
|
238
|
-
_define_property(this, "contentType", void 0);
|
|
239
|
-
_define_property(this, "url", void 0);
|
|
240
|
-
_define_property(this, "location", void 0);
|
|
241
|
-
_define_property(this, "storageConfigId", void 0);
|
|
242
|
-
_define_property(this, "providerName", void 0);
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
_ts_decorate([
|
|
246
|
-
Expose(),
|
|
247
|
-
ApiProperty({
|
|
248
|
-
description: 'Unique identifier of the file',
|
|
249
|
-
example: 'f2e9c8d0-7a2a-11eb-9439-0242ac130002'
|
|
250
|
-
}),
|
|
251
|
-
_ts_metadata("design:type", String)
|
|
252
|
-
], FilesResponseDto.prototype, "id", void 0);
|
|
253
|
-
_ts_decorate([
|
|
254
|
-
Expose(),
|
|
255
|
-
ApiProperty({
|
|
256
|
-
description: 'Original filename as uploaded',
|
|
257
|
-
example: 'quarterly-report.pdf'
|
|
258
|
-
}),
|
|
259
|
-
_ts_metadata("design:type", String)
|
|
260
|
-
], FilesResponseDto.prototype, "name", void 0);
|
|
261
|
-
_ts_decorate([
|
|
262
|
-
Expose(),
|
|
263
|
-
ApiProperty({
|
|
264
|
-
description: 'MIME type of the file',
|
|
265
|
-
example: 'application/pdf'
|
|
266
|
-
}),
|
|
267
|
-
_ts_metadata("design:type", String)
|
|
268
|
-
], FilesResponseDto.prototype, "contentType", void 0);
|
|
269
|
-
_ts_decorate([
|
|
270
|
-
Expose(),
|
|
271
|
-
ApiProperty({
|
|
272
|
-
description: 'Publicly accessible or pre-signed URL to download the file',
|
|
273
|
-
example: 'https://cdn.example.com/uploads/quarterly-report.pdf'
|
|
274
|
-
}),
|
|
275
|
-
_ts_metadata("design:type", String)
|
|
276
|
-
], FilesResponseDto.prototype, "url", void 0);
|
|
277
|
-
_ts_decorate([
|
|
278
|
-
Expose(),
|
|
279
|
-
ApiPropertyOptional({
|
|
280
|
-
description: 'Storage provider type (e.g. s3, gcs, local)',
|
|
281
|
-
example: 'gcs'
|
|
282
|
-
}),
|
|
283
|
-
_ts_metadata("design:type", String)
|
|
284
|
-
], FilesResponseDto.prototype, "location", void 0);
|
|
285
|
-
_ts_decorate([
|
|
286
|
-
Expose(),
|
|
287
|
-
ApiPropertyOptional({
|
|
288
|
-
description: 'ID of the storage configuration used to store this file',
|
|
289
|
-
example: 'a1b2c3d4-0000-0000-0000-000000000002'
|
|
290
|
-
}),
|
|
291
|
-
_ts_metadata("design:type", String)
|
|
292
|
-
], FilesResponseDto.prototype, "storageConfigId", void 0);
|
|
293
|
-
_ts_decorate([
|
|
294
|
-
Expose(),
|
|
295
|
-
ApiPropertyOptional({
|
|
296
|
-
description: 'Human-readable name of the storage provider',
|
|
297
|
-
example: 'Google Cloud Storage — EU'
|
|
298
|
-
}),
|
|
299
|
-
_ts_metadata("design:type", String)
|
|
300
|
-
], FilesResponseDto.prototype, "providerName", void 0);
|