@devstroupe/devkit-nest 1.1.13 → 1.1.15
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/dist/storage/drivers/local.storage.d.ts +2 -1
- package/dist/storage/drivers/local.storage.js +18 -0
- package/dist/storage/drivers/local.storage.test.js +24 -0
- package/dist/storage/drivers/s3.storage.d.ts +2 -1
- package/dist/storage/drivers/s3.storage.js +7 -0
- package/dist/storage/storage.service.d.ts +2 -1
- package/dist/storage/storage.types.d.ts +5 -0
- package/package.json +2 -2
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { StorageService } from '../storage.service';
|
|
2
|
-
import { StorageFile, UploadOptions, UploadResult, LocalStorageOptions } from '../storage.types';
|
|
2
|
+
import { StorageFile, UploadOptions, UploadResult, LocalStorageOptions, StorageDownloadResult } from '../storage.types';
|
|
3
3
|
export declare class LocalStorageService implements StorageService {
|
|
4
4
|
private readonly storagePath;
|
|
5
5
|
private readonly baseUrl;
|
|
@@ -8,4 +8,5 @@ export declare class LocalStorageService implements StorageService {
|
|
|
8
8
|
delete(key: string): Promise<void>;
|
|
9
9
|
getUrl(key: string): Promise<string>;
|
|
10
10
|
getSignedUrl(key: string, expiresIn?: number): Promise<string>;
|
|
11
|
+
download(key: string): Promise<StorageDownloadResult>;
|
|
11
12
|
}
|
|
@@ -106,6 +106,24 @@ let LocalStorageService = class LocalStorageService {
|
|
|
106
106
|
async getSignedUrl(key, expiresIn) {
|
|
107
107
|
return this.getUrl(key);
|
|
108
108
|
}
|
|
109
|
+
async download(key) {
|
|
110
|
+
const targetFile = path.resolve(this.storagePath, key);
|
|
111
|
+
const rootPrefix = this.storagePath.endsWith(path.sep) ? this.storagePath : this.storagePath + path.sep;
|
|
112
|
+
// Mitigação Directory Traversal
|
|
113
|
+
if (!targetFile.startsWith(rootPrefix)) {
|
|
114
|
+
throw new Error('Directory traversal attempt detected');
|
|
115
|
+
}
|
|
116
|
+
try {
|
|
117
|
+
await fs.stat(targetFile);
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
throw new Error('File not found');
|
|
121
|
+
}
|
|
122
|
+
return {
|
|
123
|
+
type: 'file',
|
|
124
|
+
filePath: targetFile,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
109
127
|
};
|
|
110
128
|
exports.LocalStorageService = LocalStorageService;
|
|
111
129
|
exports.LocalStorageService = LocalStorageService = __decorate([
|
|
@@ -103,4 +103,28 @@ const local_storage_1 = require("./local.storage");
|
|
|
103
103
|
await service.delete('../../../etc/passwd');
|
|
104
104
|
}, /Directory traversal attempt detected/);
|
|
105
105
|
});
|
|
106
|
+
(0, node_test_1.test)('deve realizar download de arquivo com sucesso', async () => {
|
|
107
|
+
const file = {
|
|
108
|
+
originalname: 'download-test.txt',
|
|
109
|
+
mimetype: 'text/plain',
|
|
110
|
+
buffer: Buffer.from('download content'),
|
|
111
|
+
size: 16,
|
|
112
|
+
};
|
|
113
|
+
const uploadResult = await service.upload(file);
|
|
114
|
+
const downloadResult = await service.download(uploadResult.key);
|
|
115
|
+
assert.strictEqual(downloadResult.type, 'file');
|
|
116
|
+
assert.ok(downloadResult.filePath);
|
|
117
|
+
const content = await fs.readFile(downloadResult.filePath);
|
|
118
|
+
assert.strictEqual(content.toString(), 'download content');
|
|
119
|
+
});
|
|
120
|
+
(0, node_test_1.test)('deve rejeitar download com Directory Traversal em key', async () => {
|
|
121
|
+
await assert.rejects(async () => {
|
|
122
|
+
await service.download('../../../etc/passwd');
|
|
123
|
+
}, /Directory traversal attempt detected/);
|
|
124
|
+
});
|
|
125
|
+
(0, node_test_1.test)('deve lançar erro ao tentar baixar arquivo inexistente', async () => {
|
|
126
|
+
await assert.rejects(async () => {
|
|
127
|
+
await service.download('non-existent-file.txt');
|
|
128
|
+
}, /File not found/);
|
|
129
|
+
});
|
|
106
130
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { StorageService } from '../storage.service';
|
|
2
|
-
import { StorageFile, UploadOptions, UploadResult, S3StorageOptions, R2StorageOptions } from '../storage.types';
|
|
2
|
+
import { StorageFile, UploadOptions, UploadResult, S3StorageOptions, R2StorageOptions, StorageDownloadResult } from '../storage.types';
|
|
3
3
|
export declare class S3StorageService implements StorageService {
|
|
4
4
|
private readonly client;
|
|
5
5
|
private readonly bucket;
|
|
@@ -9,4 +9,5 @@ export declare class S3StorageService implements StorageService {
|
|
|
9
9
|
delete(key: string): Promise<void>;
|
|
10
10
|
getUrl(key: string): Promise<string>;
|
|
11
11
|
getSignedUrl(key: string, expiresIn?: number): Promise<string>;
|
|
12
|
+
download(key: string): Promise<StorageDownloadResult>;
|
|
12
13
|
}
|
|
@@ -130,6 +130,13 @@ let S3StorageService = class S3StorageService {
|
|
|
130
130
|
});
|
|
131
131
|
return (0, s3_request_presigner_1.getSignedUrl)(this.client, command, { expiresIn });
|
|
132
132
|
}
|
|
133
|
+
async download(key) {
|
|
134
|
+
const url = await this.getSignedUrl(key, 300);
|
|
135
|
+
return {
|
|
136
|
+
type: 'redirect',
|
|
137
|
+
url,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
133
140
|
};
|
|
134
141
|
exports.S3StorageService = S3StorageService;
|
|
135
142
|
exports.S3StorageService = S3StorageService = __decorate([
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { StorageFile, UploadOptions, UploadResult } from './storage.types';
|
|
1
|
+
import { StorageFile, UploadOptions, UploadResult, StorageDownloadResult } from './storage.types';
|
|
2
2
|
export declare abstract class StorageService {
|
|
3
3
|
abstract upload(file: StorageFile, options?: UploadOptions): Promise<UploadResult>;
|
|
4
4
|
abstract delete(key: string): Promise<void>;
|
|
5
5
|
abstract getUrl(key: string): Promise<string>;
|
|
6
6
|
abstract getSignedUrl(key: string, expiresIn?: number): Promise<string>;
|
|
7
|
+
abstract download(key: string): Promise<StorageDownloadResult>;
|
|
7
8
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@devstroupe/devkit-nest",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.15",
|
|
4
4
|
"description": "DevsTroupe Development Kit — NestJS base classes: BaseRepository, BaseCrudController and query parsers",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"keywords": [
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"@aws-sdk/client-s3": "^3.500.0",
|
|
33
33
|
"@aws-sdk/s3-request-presigner": "^3.500.0",
|
|
34
34
|
"jsonwebtoken": "^9.0.2",
|
|
35
|
-
"@devstroupe/devkit-core": "1.1.
|
|
35
|
+
"@devstroupe/devkit-core": "1.1.15"
|
|
36
36
|
},
|
|
37
37
|
"peerDependencies": {
|
|
38
38
|
"@nestjs/common": "^10.0.0 || ^11.0.0",
|