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