@dataclouder/nest-storage 0.0.8 → 0.0.10
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/README.md +66 -0
- package/controllers/storage.controller.d.ts +9 -3
- package/controllers/storage.controller.js +107 -19
- package/controllers/storage.controller.js.map +1 -1
- package/docs/frontend-integration.md +98 -0
- package/docs/index.md +45 -0
- package/docs/storage-diagram.excalidraw +856 -0
- package/docs/storage-providers.md +80 -0
- package/docs/universal-storage.md +48 -0
- package/docs/usage.md +145 -0
- package/index.d.ts +1 -1
- package/index.js +0 -1
- package/index.js.map +1 -1
- package/models/cloud.model.d.ts +10 -1
- package/nest-storage.module.js +4 -2
- package/nest-storage.module.js.map +1 -1
- package/package.json +6 -2
- package/services/cloud-storage.service.d.ts +1 -11
- package/services/cloud-storage.service.js.map +1 -1
- package/services/cloudflare-r2.service.d.ts +1 -2
- package/services/cloudflare-r2.service.js +1 -1
- package/services/cloudflare-r2.service.js.map +1 -1
- package/services/local-storage.service.d.ts +27 -0
- package/services/local-storage.service.js +201 -0
- package/services/local-storage.service.js.map +1 -0
- package/services/storage.factory.d.ts +3 -1
- package/services/storage.factory.js +9 -2
- package/services/storage.factory.js.map +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# @dataclouder/nest-storage
|
|
2
|
+
|
|
3
|
+
NestJS library for storage services, providing a unified interface for Google Cloud Storage, Cloudflare R2, Minio, and Local storage.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @dataclouder/nest-storage
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
- **Multi-Provider Support**: Integrated support for Google Cloud Storage, Cloudflare R2, and Minio.
|
|
14
|
+
- **Unified Interface**: Use the same methods regardless of the underlying storage service.
|
|
15
|
+
- **Image Processing**: Automatic conversion of images to WebP format for optimized web delivery.
|
|
16
|
+
- **Batch Operations**: Easily remove all storage files referenced within complex data objects or by URLs.
|
|
17
|
+
- **Security**: Generate signed URLs for temporary access to private files.
|
|
18
|
+
- **Auditable**: Built-in support for tracking uploads with auditable metadata.
|
|
19
|
+
|
|
20
|
+
## Documentation
|
|
21
|
+
|
|
22
|
+
For detailed guides and API references, please check the documentation files:
|
|
23
|
+
|
|
24
|
+
- [Getting Started](./docs/index.md)
|
|
25
|
+
- [Usage Guide](./docs/usage.md)
|
|
26
|
+
- [Storage Providers](./docs/storage-providers.md)
|
|
27
|
+
- [Frontend Integration](./docs/frontend-integration.md)
|
|
28
|
+
- [Universal Storage](./docs/universal-storage.md)
|
|
29
|
+
|
|
30
|
+
## Basic Usage
|
|
31
|
+
|
|
32
|
+
1. **Register the module:**
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
import { StorageModule } from '@dataclouder/nest-storage';
|
|
36
|
+
|
|
37
|
+
@Module({
|
|
38
|
+
imports: [
|
|
39
|
+
StorageModule.register({
|
|
40
|
+
provider: 'GOOGLE', // or 'CLOUDFLARE', 'MINIO', 'LOCAL'
|
|
41
|
+
bucket: 'my-bucket',
|
|
42
|
+
// ... provider specific config
|
|
43
|
+
}),
|
|
44
|
+
],
|
|
45
|
+
})
|
|
46
|
+
export class AppModule {}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
2. **Inject the service:**
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
import { StorageService } from '@dataclouder/nest-storage';
|
|
53
|
+
|
|
54
|
+
@Injectable()
|
|
55
|
+
export class MyService {
|
|
56
|
+
constructor(private storageService: StorageService) {}
|
|
57
|
+
|
|
58
|
+
async uploadFile(file: Buffer, filename: string) {
|
|
59
|
+
return await this.storageService.upload(file, filename);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
Developed by [dataclouder](https://github.com/dataclouder).
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import { StorageFactory } from '../services/storage.factory';
|
|
2
|
-
|
|
2
|
+
import { FastifyRequest } from 'fastify';
|
|
3
|
+
import { ConfigService } from '@nestjs/config';
|
|
4
|
+
export declare class StorageController {
|
|
3
5
|
private readonly storageFactory;
|
|
4
|
-
|
|
5
|
-
|
|
6
|
+
private readonly configService;
|
|
7
|
+
constructor(storageFactory: StorageFactory, configService: ConfigService);
|
|
8
|
+
uploadFile(req: FastifyRequest, queryPath?: string, queryProvider?: string, queryKeepName?: any): Promise<Partial<import("..").CloudFileStorage>>;
|
|
9
|
+
uploadMultiple(req: FastifyRequest, queryPath?: string, queryProvider?: string, queryKeepName?: any): Promise<any[]>;
|
|
10
|
+
private generateFileName;
|
|
11
|
+
serveFile(filename: string, res: any): Promise<any>;
|
|
6
12
|
}
|
|
@@ -41,37 +41,125 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
41
41
|
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
42
42
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
43
43
|
};
|
|
44
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
45
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
46
|
+
};
|
|
44
47
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
|
-
exports.
|
|
48
|
+
exports.StorageController = void 0;
|
|
46
49
|
const common_1 = require("@nestjs/common");
|
|
47
50
|
const storage_factory_1 = require("../services/storage.factory");
|
|
48
|
-
const fs = __importStar(require("fs"));
|
|
49
51
|
const path = __importStar(require("path"));
|
|
50
|
-
|
|
52
|
+
const fs = __importStar(require("fs"));
|
|
53
|
+
const config_1 = require("@nestjs/config");
|
|
54
|
+
let StorageController = class StorageController {
|
|
51
55
|
storageFactory;
|
|
52
|
-
|
|
56
|
+
configService;
|
|
57
|
+
constructor(storageFactory, configService) {
|
|
53
58
|
this.storageFactory = storageFactory;
|
|
59
|
+
this.configService = configService;
|
|
60
|
+
}
|
|
61
|
+
async uploadFile(req, queryPath, queryProvider, queryKeepName) {
|
|
62
|
+
const data = await req.file();
|
|
63
|
+
if (!data) {
|
|
64
|
+
throw new Error('No file uploaded');
|
|
65
|
+
}
|
|
66
|
+
const effectiveProvider = queryProvider || data.fields?.provider?.value;
|
|
67
|
+
const effectivePath = queryPath || data.fields?.path?.value;
|
|
68
|
+
const queryKeepNameBool = queryKeepName === 'true' || queryKeepName === true;
|
|
69
|
+
const fieldKeepName = data.fields?.keepOriginalName?.value;
|
|
70
|
+
const fieldKeepNameBool = fieldKeepName === 'true' || fieldKeepName === true;
|
|
71
|
+
const effectiveKeepName = queryKeepNameBool || fieldKeepNameBool;
|
|
72
|
+
const fileBuffer = await data.toBuffer();
|
|
73
|
+
const storageService = this.storageFactory.getStorageService(effectiveProvider);
|
|
74
|
+
const auditable = { createdBy: 'system', updatedBy: 'system' };
|
|
75
|
+
const fileName = this.generateFileName(effectivePath, data.filename, effectiveKeepName);
|
|
76
|
+
if (data.mimetype.startsWith('image/')) {
|
|
77
|
+
return storageService.uploadWebpImage({
|
|
78
|
+
fileName,
|
|
79
|
+
imageBuffer: fileBuffer,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
return storageService.uploadFile(fileName, fileBuffer, auditable, data.mimetype);
|
|
54
83
|
}
|
|
55
|
-
async
|
|
56
|
-
const
|
|
57
|
-
const
|
|
58
|
-
const
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
84
|
+
async uploadMultiple(req, queryPath, queryProvider, queryKeepName) {
|
|
85
|
+
const parts = req.files();
|
|
86
|
+
const uploadResults = [];
|
|
87
|
+
const currentPath = queryPath;
|
|
88
|
+
const currentProvider = queryProvider;
|
|
89
|
+
const queryKeepNameBool = queryKeepName === 'true' || queryKeepName === true;
|
|
90
|
+
const currentKeepName = queryKeepNameBool;
|
|
91
|
+
for await (const part of parts) {
|
|
92
|
+
if (part.file) {
|
|
93
|
+
const effectiveProvider = currentProvider || part.fields?.provider?.value;
|
|
94
|
+
const effectivePath = currentPath || part.fields?.path?.value;
|
|
95
|
+
const partKeepField = part.fields?.keepOriginalName?.value;
|
|
96
|
+
const partKeepNameBool = partKeepField === 'true' || partKeepField === true;
|
|
97
|
+
const effectiveKeepName = currentKeepName || partKeepNameBool;
|
|
98
|
+
const fileBuffer = await part.toBuffer();
|
|
99
|
+
const storageService = this.storageFactory.getStorageService(effectiveProvider);
|
|
100
|
+
const auditable = { createdBy: 'system', updatedBy: 'system' };
|
|
101
|
+
const fileName = this.generateFileName(effectivePath, part.filename, effectiveKeepName);
|
|
102
|
+
let result;
|
|
103
|
+
if (part.mimetype.startsWith('image/')) {
|
|
104
|
+
result = await storageService.uploadWebpImage({
|
|
105
|
+
fileName,
|
|
106
|
+
imageBuffer: fileBuffer,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
result = await storageService.uploadFile(fileName, fileBuffer, auditable, part.mimetype);
|
|
111
|
+
}
|
|
112
|
+
uploadResults.push(result);
|
|
113
|
+
}
|
|
62
114
|
}
|
|
63
|
-
return
|
|
115
|
+
return uploadResults;
|
|
116
|
+
}
|
|
117
|
+
generateFileName(storagePath, originalName, keepOriginalName) {
|
|
118
|
+
const sanitizedPath = storagePath ? storagePath.replace(/\/$/, '') : '';
|
|
119
|
+
const name = keepOriginalName ? originalName : `${Date.now()}-${originalName}`;
|
|
120
|
+
return sanitizedPath ? `${sanitizedPath}/${name}` : name;
|
|
121
|
+
}
|
|
122
|
+
async serveFile(filename, res) {
|
|
123
|
+
const uploadDir = this.configService.get('LOCAL_STORAGE_PATH') || path.join(process.cwd(), 'uploads');
|
|
124
|
+
const filePath = path.join(uploadDir, filename);
|
|
125
|
+
if (!fs.existsSync(filePath)) {
|
|
126
|
+
throw new common_1.NotFoundException('File not found');
|
|
127
|
+
}
|
|
128
|
+
return res.sendFile(filename, uploadDir);
|
|
64
129
|
}
|
|
65
130
|
};
|
|
66
|
-
exports.
|
|
131
|
+
exports.StorageController = StorageController;
|
|
67
132
|
__decorate([
|
|
68
133
|
(0, common_1.Post)('upload'),
|
|
134
|
+
__param(0, (0, common_1.Req)()),
|
|
135
|
+
__param(1, (0, common_1.Query)('path')),
|
|
136
|
+
__param(2, (0, common_1.Query)('provider')),
|
|
137
|
+
__param(3, (0, common_1.Query)('keepOriginalName')),
|
|
138
|
+
__metadata("design:type", Function),
|
|
139
|
+
__metadata("design:paramtypes", [Object, String, String, Object]),
|
|
140
|
+
__metadata("design:returntype", Promise)
|
|
141
|
+
], StorageController.prototype, "uploadFile", null);
|
|
142
|
+
__decorate([
|
|
143
|
+
(0, common_1.Post)('upload-multiple'),
|
|
144
|
+
__param(0, (0, common_1.Req)()),
|
|
145
|
+
__param(1, (0, common_1.Query)('path')),
|
|
146
|
+
__param(2, (0, common_1.Query)('provider')),
|
|
147
|
+
__param(3, (0, common_1.Query)('keepOriginalName')),
|
|
148
|
+
__metadata("design:type", Function),
|
|
149
|
+
__metadata("design:paramtypes", [Object, String, String, Object]),
|
|
150
|
+
__metadata("design:returntype", Promise)
|
|
151
|
+
], StorageController.prototype, "uploadMultiple", null);
|
|
152
|
+
__decorate([
|
|
153
|
+
(0, common_1.Get)('files/filename'),
|
|
154
|
+
__param(0, (0, common_1.Param)('filename')),
|
|
155
|
+
__param(1, (0, common_1.Res)()),
|
|
69
156
|
__metadata("design:type", Function),
|
|
70
|
-
__metadata("design:paramtypes", []),
|
|
157
|
+
__metadata("design:paramtypes", [String, Object]),
|
|
71
158
|
__metadata("design:returntype", Promise)
|
|
72
|
-
],
|
|
73
|
-
exports.
|
|
74
|
-
(0, common_1.Controller)('storage
|
|
75
|
-
__metadata("design:paramtypes", [storage_factory_1.StorageFactory
|
|
76
|
-
]
|
|
159
|
+
], StorageController.prototype, "serveFile", null);
|
|
160
|
+
exports.StorageController = StorageController = __decorate([
|
|
161
|
+
(0, common_1.Controller)('storage'),
|
|
162
|
+
__metadata("design:paramtypes", [storage_factory_1.StorageFactory,
|
|
163
|
+
config_1.ConfigService])
|
|
164
|
+
], StorageController);
|
|
77
165
|
//# sourceMappingURL=storage.controller.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storage.controller.js","sourceRoot":"","sources":["../../../../../libs/nest-storage/src/controllers/storage.controller.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"storage.controller.js","sourceRoot":"","sources":["../../../../../libs/nest-storage/src/controllers/storage.controller.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# Frontend Integration Guide
|
|
2
|
+
|
|
3
|
+
This guide explains how to connect your frontend application (e.g., Angular) to the Nest Storage service.
|
|
4
|
+
|
|
5
|
+
## API Endpoint
|
|
6
|
+
|
|
7
|
+
- **URL**: `YOUR_BASE_URL/storage/upload`
|
|
8
|
+
- **Method**: `POST`
|
|
9
|
+
- **Content-Type**: `multipart/form-data`
|
|
10
|
+
|
|
11
|
+
## Request Parameters
|
|
12
|
+
|
|
13
|
+
Metadata is passed via **Query Parameters** in the URL, while the file itself is sent in the `multipart/form-data` body.
|
|
14
|
+
|
|
15
|
+
### Query Parameters
|
|
16
|
+
|
|
17
|
+
| Parameter | Type | Required | Description |
|
|
18
|
+
| :--- | :--- | :--- | :--- |
|
|
19
|
+
| `path` | `string` | No | The target directory/path in storage (e.g., `avatars`, `docs/2024`). |
|
|
20
|
+
| `provider` | `string` | No | The storage provider to use (`google`, `cloudflare`, `local`). |
|
|
21
|
+
| `keepOriginalName` | `boolean` | No | If `true`, the file will keep its original name. If `false` (default), a timestamp prefix will be added. |
|
|
22
|
+
|
|
23
|
+
### Multipart Body
|
|
24
|
+
|
|
25
|
+
| Field | Type | Required | Description |
|
|
26
|
+
| :--- | :--- | :--- | :--- |
|
|
27
|
+
| `file` | `File` | **Yes** | The file object to be uploaded. |
|
|
28
|
+
|
|
29
|
+
> [!TIP]
|
|
30
|
+
> Using Query Parameters for metadata is the most reliable way to ensure the backend receives these values before processing the file stream.
|
|
31
|
+
|
|
32
|
+
## Example Implementation (Angular)
|
|
33
|
+
|
|
34
|
+
### 1. Storage Service
|
|
35
|
+
|
|
36
|
+
Create a service to handle the upload logic:
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
import { Injectable } from '@angular/core';
|
|
40
|
+
import { HttpClient, HttpParams } from '@angular/common/http';
|
|
41
|
+
import { Observable } from 'rxjs';
|
|
42
|
+
|
|
43
|
+
@Injectable({
|
|
44
|
+
providedIn: 'root'
|
|
45
|
+
})
|
|
46
|
+
export class FileUploadService {
|
|
47
|
+
private apiUrl = 'http://localhost:8091/storage/upload';
|
|
48
|
+
|
|
49
|
+
constructor(private http: HttpClient) {}
|
|
50
|
+
|
|
51
|
+
uploadFile(file: File, path?: string, provider?: string, keepOriginalName?: boolean): Observable<any> {
|
|
52
|
+
const formData = new FormData();
|
|
53
|
+
formData.append('file', file);
|
|
54
|
+
|
|
55
|
+
let params = new HttpParams();
|
|
56
|
+
if (path) params = params.set('path', path);
|
|
57
|
+
if (provider) params = params.set('provider', provider);
|
|
58
|
+
if (keepOriginalName) params = params.set('keepOriginalName', 'true');
|
|
59
|
+
|
|
60
|
+
return this.http.post(this.apiUrl, formData, { params });
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### 2. Component Usage
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
onFileSelected(event: any) {
|
|
69
|
+
const file: File = event.target.files[0];
|
|
70
|
+
if (file) {
|
|
71
|
+
this.uploadService.uploadFile(file, 'user-uploads', 'cloudflare').subscribe({
|
|
72
|
+
next: (response) => {
|
|
73
|
+
console.log('Upload successful', response.url);
|
|
74
|
+
},
|
|
75
|
+
error: (err) => {
|
|
76
|
+
console.error('Upload failed', err);
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Handling Response
|
|
84
|
+
|
|
85
|
+
The service returns a `CloudFileStorage` object:
|
|
86
|
+
|
|
87
|
+
```json
|
|
88
|
+
{
|
|
89
|
+
"url": "https://storage.googleapis.com/bucket/path/to/file.png",
|
|
90
|
+
"path": "path/to/file.png",
|
|
91
|
+
"bucket": "my-bucket",
|
|
92
|
+
"provider": "google",
|
|
93
|
+
"auditable": {
|
|
94
|
+
"createdBy": "system",
|
|
95
|
+
"updatedBy": "system"
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
```
|
package/docs/index.md
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Nest Storage Library
|
|
2
|
+
|
|
3
|
+
The `@polilan/nest-storage` library provides a robust and flexible storage abstraction layer for NestJS applications. It simplifies file management by supporting multiple storage providers through a unified interface, allowing developers to switch between providers with minimal configuration changes.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Multi-Provider Support**: Integrated support for Google Cloud Storage, Cloudflare R2, and Minio.
|
|
8
|
+
- **Unified Interface**: Use the same methods regardless of the underlying storage service.
|
|
9
|
+
- **Image Processing**: Automatic conversion of images to WebP format for optimized web delivery.
|
|
10
|
+
- **Batch Operations**: Easily remove all storage files referenced within complex data objects or by URLs.
|
|
11
|
+
- **Security**: Generate signed URLs for temporary access to private files.
|
|
12
|
+
- **Auditable**: Built-in support for tracking uploads with auditable metadata.
|
|
13
|
+
- **Simplified Integration**: Support for metadata via URL Query Parameters to avoid multipart ordering issues.
|
|
14
|
+
|
|
15
|
+
## File Size Limits
|
|
16
|
+
|
|
17
|
+
By default, the application is configured to support file uploads up to **100MB**. This limit is defined at the application level in `src/main.ts` using two configurations:
|
|
18
|
+
|
|
19
|
+
1. **Fastify Body Limit**: Controls the maximum total request size.
|
|
20
|
+
2. **Multipart File Size**: Controls the maximum size for individual files.
|
|
21
|
+
|
|
22
|
+
To increase these limits, modify the following in `src/main.ts`:
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
// src/main.ts
|
|
26
|
+
const app = await NestFactory.create<NestFastifyApplication>(
|
|
27
|
+
AppModule,
|
|
28
|
+
new FastifyAdapter({ bodyLimit: 104857600 }), // 100MB
|
|
29
|
+
{ rawBody: true },
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
app.register(multipart as any, {
|
|
33
|
+
limits: {
|
|
34
|
+
fileSize: 104857600, // 100MB
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## References
|
|
40
|
+
|
|
41
|
+
- [Frontend Integration](./frontend-integration.md): How to connect your frontend app to this service.
|
|
42
|
+
- [Storage Providers](./storage-providers.md): Detailed configuration for GOOGLE, CLOUDFLARE, and LOCAL.
|
|
43
|
+
- [Universal Storage](./universal-storage.md): Guide on the universal multi-provider integration.
|
|
44
|
+
- [Usage Guide](./usage.md): Instructions on how to upload, download, and delete files.
|
|
45
|
+
- [Cloud Models](../src/models/cloud.model.ts): Type definitions for storage objects and interfaces.
|