@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 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
- export declare class StorageTestController {
2
+ import { FastifyRequest } from 'fastify';
3
+ import { ConfigService } from '@nestjs/config';
4
+ export declare class StorageController {
3
5
  private readonly storageFactory;
4
- constructor(storageFactory: StorageFactory);
5
- uploadTestFile(): Promise<Partial<import("..").CloudFileStorage>>;
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.StorageTestController = void 0;
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
- let StorageTestController = class StorageTestController {
52
+ const fs = __importStar(require("fs"));
53
+ const config_1 = require("@nestjs/config");
54
+ let StorageController = class StorageController {
51
55
  storageFactory;
52
- constructor(storageFactory) {
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 uploadTestFile() {
56
- const storageService = this.storageFactory.getStorageService('CLOUDFLARE');
57
- const filePath = path.join(process.cwd(), 'woman1.jpg');
58
- const fileBuffer = fs.readFileSync(filePath);
59
- const bucketName = 'polilan-bucket';
60
- if (!bucketName) {
61
- throw new Error('STORAGE_BUCKET environment variable not set.');
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 storageService.uploadWebpImage({ bucketName, fileName: 'myfolder/mywoman1-test.jpg', imageBuffer: fileBuffer });
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.StorageTestController = StorageTestController;
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
- ], StorageTestController.prototype, "uploadTestFile", null);
73
- exports.StorageTestController = StorageTestController = __decorate([
74
- (0, common_1.Controller)('storage-test'),
75
- __metadata("design:paramtypes", [storage_factory_1.StorageFactory])
76
- ], StorageTestController);
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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,2CAAkD;AAClD,iEAA6D;AAC7D,uCAAyB;AACzB,2CAA6B;AAGtB,IAAM,qBAAqB,GAA3B,MAAM,qBAAqB;IACH;IAA7B,YAA6B,cAA8B;QAA9B,mBAAc,GAAd,cAAc,CAAgB;IAAG,CAAC;IAGzD,AAAN,KAAK,CAAC,cAAc;QAClB,MAAM,cAAc,GAAG,IAAI,CAAC,cAAc,CAAC,iBAAiB,CAAC,YAAY,CAAC,CAAC;QAC3E,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,YAAY,CAAC,CAAC;QACxD,MAAM,UAAU,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;QAC7C,MAAM,UAAU,GAAG,gBAAgB,CAAC;QAEpC,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;QAClE,CAAC;QAED,OAAO,cAAc,CAAC,eAAe,CAAC,EAAE,UAAU,EAAE,QAAQ,EAAE,4BAA4B,EAAE,WAAW,EAAE,UAAU,EAAE,CAAC,CAAC;IACzH,CAAC;CACF,CAAA;AAhBY,sDAAqB;AAI1B;IADL,IAAA,aAAI,EAAC,QAAQ,CAAC;;;;2DAYd;gCAfU,qBAAqB;IADjC,IAAA,mBAAU,EAAC,cAAc,CAAC;qCAEoB,gCAAc;GADhD,qBAAqB,CAgBjC"}
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.