@flusys/ng-storage 0.1.0-beta.1 → 0.1.0-beta.2

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.
Files changed (2) hide show
  1. package/README.md +590 -0
  2. package/package.json +1 -1
package/README.md ADDED
@@ -0,0 +1,590 @@
1
+ # Storage Package Guide
2
+
3
+ > **Package:** `@flusys/ng-storage`
4
+ > **Type:** File storage management with upload, folder organization, and multi-provider support
5
+
6
+ ---
7
+
8
+ ## Overview
9
+
10
+ `@flusys/ng-storage` provides complete file storage management:
11
+
12
+ - **File Upload** - Single and multiple file uploads with image compression
13
+ - **File Manager** - CRUD operations for file metadata with presigned URLs
14
+ - **Folder Management** - Folder organization for files
15
+ - **Storage Configs** - Multi-provider configurations (AWS S3, Azure Blob, SFTP, Local)
16
+ - **Company Scoping** - Automatic company-scoped queries (when enabled)
17
+ - **Lazy Loading** - All page components are lazy-loaded via routes
18
+
19
+ ### Package Hierarchy
20
+
21
+ ```
22
+ @flusys/ng-core <- Foundation (BaseApiService, APP_CONFIG)
23
+ |
24
+ @flusys/ng-shared <- Shared utilities (ApiResourceService, IBaseEntity)
25
+ |
26
+ @flusys/ng-layout <- Layout (LAYOUT_AUTH_STATE for company context)
27
+ |
28
+ @flusys/ng-storage <- File storage (THIS PACKAGE)
29
+ ```
30
+
31
+ ### Company Context
32
+
33
+ Storage components use `LAYOUT_AUTH_STATE` from `@flusys/ng-layout` for company context. This follows the Provider Interface Pattern - ng-storage depends on ng-layout's abstraction, not ng-auth directly.
34
+
35
+ ```typescript
36
+ import { LAYOUT_AUTH_STATE } from '@flusys/ng-layout';
37
+
38
+ private readonly companyContext = inject(LAYOUT_AUTH_STATE);
39
+
40
+ readonly currentCompanyName = computed(
41
+ () => this.companyContext.currentCompanyInfo()?.name ?? DEFAULT_APP_NAME,
42
+ );
43
+ ```
44
+
45
+ ---
46
+
47
+ ## Package Architecture
48
+
49
+ ```
50
+ ng-storage/
51
+ ├── enums/
52
+ │ ├── file-location.enum.ts # FileLocationEnum, ImageFormat
53
+ │ └── public-api.ts
54
+ ├── interfaces/
55
+ │ ├── file-manager.interface.ts # IFileManager, ICreateFileManagerDto, IUpdateFileManagerDto
56
+ │ ├── folder.interface.ts # IFolder, ICreateFolderDto, IUpdateFolderDto
57
+ │ ├── storage-config.interface.ts # IStorageConfig, provider-specific configs
58
+ │ ├── upload.interface.ts # IUploadOptionsDto, IUploadedFileInfo
59
+ │ └── public-api.ts
60
+ ├── services/
61
+ │ ├── file-manager-api.service.ts # File metadata CRUD + presigned URLs
62
+ │ ├── folder-api.service.ts # Folder CRUD
63
+ │ ├── storage-config-api.service.ts # Storage config CRUD
64
+ │ └── upload.service.ts # File upload/delete operations
65
+ ├── pages/
66
+ │ ├── storage-container/
67
+ │ │ └── storage-container.component.ts # Tab container (Files, Folders, Configs)
68
+ │ ├── file-manager/
69
+ │ │ ├── file-manager-list.component.ts
70
+ │ │ └── file-manager-form.component.ts
71
+ │ ├── folder/
72
+ │ │ ├── folder-list.component.ts
73
+ │ │ └── folder-form.component.ts
74
+ │ └── storage-config/
75
+ │ ├── storage-config-list.component.ts
76
+ │ └── storage-config-form.component.ts
77
+ ├── routes/
78
+ │ └── storage.routes.ts
79
+ └── public-api.ts
80
+ ```
81
+
82
+ ---
83
+
84
+ ## Route Integration
85
+
86
+ ### Adding Storage Routes
87
+
88
+ ```typescript
89
+ // app.routes.ts
90
+ export const routes: Routes = [
91
+ {
92
+ path: 'storage',
93
+ loadChildren: () =>
94
+ import('@flusys/ng-storage').then((m) => m.STORAGE_ROUTES),
95
+ },
96
+ ];
97
+ ```
98
+
99
+ ### Route Structure
100
+
101
+ All routes are wrapped by `StorageContainerComponent` which provides tab navigation (Files, Folders, Configs).
102
+
103
+ | Path | Component | Description |
104
+ |------|-----------|-------------|
105
+ | `/storage` | StorageContainerComponent | Container with tab navigation |
106
+ | `/storage/files` | FileManagerListComponent | List all files (default) |
107
+ | `/storage/files/new` | FileManagerFormComponent | Upload new file |
108
+ | `/storage/files/:id` | FileManagerFormComponent | Edit file metadata |
109
+ | `/storage/folders` | FolderListComponent | List all folders |
110
+ | `/storage/folders/new` | FolderFormComponent | Create folder |
111
+ | `/storage/folders/:id` | FolderFormComponent | Edit folder |
112
+ | `/storage/configs` | StorageConfigListComponent | List storage configurations |
113
+ | `/storage/configs/new` | StorageConfigFormComponent | Create storage config |
114
+ | `/storage/configs/:id` | StorageConfigFormComponent | Edit storage config |
115
+
116
+ Default redirect: `/storage` -> `/storage/files`
117
+
118
+ ---
119
+
120
+ ## Enums
121
+
122
+ ### FileLocationEnum
123
+
124
+ Storage provider types (matches backend).
125
+
126
+ ```typescript
127
+ enum FileLocationEnum {
128
+ LOCAL = 'local',
129
+ AWS = 'aws',
130
+ AZURE = 'azure',
131
+ SFTP = 'sftp',
132
+ }
133
+ ```
134
+
135
+ ### ImageFormat
136
+
137
+ Image compression output formats.
138
+
139
+ ```typescript
140
+ enum ImageFormat {
141
+ ORIGINAL = 'original',
142
+ JPEG = 'jpeg',
143
+ PNG = 'png',
144
+ WEBP = 'webp',
145
+ }
146
+ ```
147
+
148
+ ---
149
+
150
+ ## Interfaces
151
+
152
+ ### Upload
153
+
154
+ ```typescript
155
+ interface IUploadOptionsDto {
156
+ storageConfigId?: string; // Storage config to use (uses default if not set)
157
+ folderPath?: string; // Target folder path
158
+ maxWidth?: number; // Image max width (default: 1280)
159
+ maxHeight?: number; // Image max height (default: 1280)
160
+ quality?: number; // Image quality 1-100 (default: 85)
161
+ format?: ImageFormat; // Output format
162
+ compress?: boolean; // Enable compression (default: true)
163
+ }
164
+
165
+ interface IUploadedFileInfo {
166
+ name: string; // Original filename
167
+ key: string; // Storage key/path
168
+ size: number; // File size in bytes
169
+ contentType: string; // MIME type
170
+ }
171
+ ```
172
+
173
+ ### File Manager
174
+
175
+ ```typescript
176
+ interface IFileManager {
177
+ id: string;
178
+ createdAt: Date;
179
+ updatedAt: Date;
180
+ name: string;
181
+ contentType: string;
182
+ size: string; // In KB (string)
183
+ key: string; // File key/path in storage
184
+ url: string | null; // Presigned URL (regenerated by backend)
185
+ location: FileLocationEnum;
186
+ storageConfigId: string | null;
187
+ expiresAt: Date | null; // URL expiration time
188
+ isPrivate: boolean;
189
+ companyId?: string; // Only in company mode
190
+ folderId?: string | null;
191
+ }
192
+
193
+ interface ICreateFileManagerDto {
194
+ name: string;
195
+ key: string;
196
+ size: string;
197
+ contentType: string;
198
+ isPrivate: boolean;
199
+ folderId?: string;
200
+ storageConfigId?: string;
201
+ location?: FileLocationEnum;
202
+ }
203
+
204
+ interface IUpdateFileManagerDto {
205
+ id: string;
206
+ name?: string;
207
+ isPrivate?: boolean;
208
+ folderId?: string | null;
209
+ }
210
+
211
+ // For fetching files with valid presigned URLs
212
+ interface IGetFilesRequestDto { id: string; }
213
+ interface IFilesResponseDto { id: string; name: string; contentType: string; url: string; }
214
+ ```
215
+
216
+ ### Folder
217
+
218
+ ```typescript
219
+ interface IFolder extends IBaseEntity {
220
+ name: string;
221
+ slug: string; // URL-friendly name
222
+ companyId?: string | null;
223
+ }
224
+
225
+ interface ICreateFolderDto { name: string; }
226
+ interface IUpdateFolderDto { id: string; name?: string; }
227
+ ```
228
+
229
+ ### Storage Config
230
+
231
+ ```typescript
232
+ interface IStorageConfig extends IBaseEntity {
233
+ name: string;
234
+ storage: FileLocationEnum;
235
+ config: Record<string, any>; // Provider-specific configuration
236
+ companyId?: string | null;
237
+ }
238
+
239
+ interface ICreateStorageConfigDto {
240
+ name: string;
241
+ storage: FileLocationEnum;
242
+ config: Record<string, any>;
243
+ }
244
+
245
+ interface IUpdateStorageConfigDto {
246
+ id: string;
247
+ name?: string;
248
+ storage?: FileLocationEnum;
249
+ config?: Record<string, any>;
250
+ }
251
+ ```
252
+
253
+ ### Provider-Specific Configs
254
+
255
+ These define the shape of `IStorageConfig.config` for each provider:
256
+
257
+ ```typescript
258
+ interface IAwsS3Config {
259
+ region: string;
260
+ bucket: string;
261
+ accessKeyId: string;
262
+ secretAccessKey: string;
263
+ endpoint?: string; // Custom S3-compatible endpoint
264
+ }
265
+
266
+ interface IAzureBlobConfig {
267
+ accountName: string;
268
+ accountKey: string;
269
+ containerName: string;
270
+ }
271
+
272
+ interface ISftpConfig {
273
+ host: string;
274
+ port: number;
275
+ username: string;
276
+ password?: string;
277
+ privateKey?: string;
278
+ basePath: string;
279
+ }
280
+
281
+ interface ILocalStorageConfig {
282
+ basePath: string;
283
+ }
284
+ ```
285
+
286
+ ---
287
+
288
+ ## Services
289
+
290
+ All services are `providedIn: 'root'`. Company scoping is automatic when the company feature is enabled.
291
+
292
+ ### UploadService
293
+
294
+ Handles file upload and deletion. Extends `BaseApiService` (from ng-core).
295
+
296
+ | Method | Endpoint | Description |
297
+ |--------|----------|-------------|
298
+ | `uploadSingleFile(file, options?)` | `POST /storage/upload/single-file` | Upload single file |
299
+ | `uploadMultipleFiles(files, options?)` | `POST /storage/upload/multiple-file` | Upload multiple files (max 50) |
300
+ | `deleteSingleFile(key, storageConfigId?)` | `POST /storage/upload/delete-single-file` | Delete single file from storage |
301
+ | `deleteMultipleFiles(keys, storageConfigId?)` | `POST /storage/upload/delete-multiple-file` | Delete multiple files |
302
+ | `getFileUrl(key)` | `GET /storage/upload/file/:key` | Get direct file download URL |
303
+
304
+ Upload options are sent as **query parameters**, not form data:
305
+
306
+ ```typescript
307
+ // POST /storage/upload/single-file?storageConfigId=abc&folderPath=docs&compress=true
308
+ this.uploadService.uploadSingleFile(file, {
309
+ storageConfigId: 'abc',
310
+ folderPath: 'docs',
311
+ compress: true,
312
+ quality: 85,
313
+ format: ImageFormat.WEBP,
314
+ });
315
+ ```
316
+
317
+ ### FileManagerApiService
318
+
319
+ File metadata CRUD. Extends `ApiResourceService<IUpdateFileManagerDto, IFileManager>`.
320
+
321
+ **Inherited CRUD methods** (from ApiResourceService): `insert`, `update`, `findById`, `getAll`, `delete` and their `*Async` variants.
322
+
323
+ **Additional methods:**
324
+
325
+ | Method | Endpoint | Description |
326
+ |--------|----------|-------------|
327
+ | `getFiles(fileIds)` | `POST /storage/file-manager/get-files` | Get files with valid presigned URLs |
328
+ | `getFilesByFolder(folderId, page?, limit?)` | `POST /storage/file-manager/get-all` | Filter files by folder |
329
+ | `getPrivateFiles(page?, limit?)` | `POST /storage/file-manager/get-all` | Filter by `isPrivate: true` |
330
+
331
+ **Presigned URL pattern:** Always use `getFiles()` to get valid URLs. Backend regenerates expired presigned URLs for cloud storage.
332
+
333
+ ```typescript
334
+ const response = await firstValueFrom(
335
+ this.fileService.getFiles(['file-id-1', 'file-id-2'])
336
+ );
337
+ // response.data → [{ id, name, contentType, url }]
338
+ ```
339
+
340
+ ### FolderApiService
341
+
342
+ Folder CRUD. Extends `ApiResourceService<IUpdateFolderDto, IFolder>`. Standard CRUD operations only, no custom methods.
343
+
344
+ ### StorageConfigApiService
345
+
346
+ Storage provider configuration CRUD. Extends `ApiResourceService<IUpdateStorageConfigDto, IStorageConfig>`. Standard CRUD operations only, no custom methods.
347
+
348
+ ---
349
+
350
+ ## Components
351
+
352
+ All components are standalone, use Angular 21 signal forms, and lazy-load via routes.
353
+
354
+ ### StorageContainerComponent
355
+
356
+ Main container with tab navigation between Files, Folders, and Configs sections. Uses inline CSS with theme variables (`--primary-color`, `--surface-ground`, etc.).
357
+
358
+ **Selector:** `lib-storage-container`
359
+
360
+ ### List Components
361
+
362
+ All list components share:
363
+ - **Lazy pagination** via `p-datatable`
364
+ - **Company info display** when company feature enabled (via `APP_CONFIG`)
365
+ - **Delete confirmation** via `ConfirmationService`
366
+ - **Signal-based state:** `isLoading`, `files`/`folders`/`configs`, `totalRecords`, `pageSize`, `currentPage`
367
+
368
+ **FileManagerListComponent** additionally supports:
369
+ - File content type icons (image, video, audio, PDF, Word, Excel, generic)
370
+ - View file (opens presigned URL in new tab)
371
+ - **Soft delete** - marks as deleted (recoverable)
372
+ - **Permanent delete** - removes from storage provider (irreversible)
373
+
374
+ ### Form Components
375
+
376
+ All form components share:
377
+ - **Signal Forms** using Angular 21 `form()` helper with `required()` validators
378
+ - **Create/Edit mode** auto-detection based on route param (`:id`)
379
+ - **Navigation** back to list on save/cancel
380
+
381
+ **FileManagerFormComponent** additionally supports:
382
+ - PrimeNG file upload with drag-drop
383
+ - Storage config selection dropdown
384
+ - Folder assignment
385
+ - Auto-filling filename from uploaded file
386
+
387
+ **StorageConfigFormComponent** has dynamic form fields based on selected provider:
388
+ - AWS S3: region, bucket, accessKeyId, secretAccessKey, endpoint
389
+ - Azure Blob: accountName, accountKey, containerName
390
+ - SFTP: host, port, username, password, basePath
391
+ - Local: basePath
392
+
393
+ ---
394
+
395
+ ## Usage Examples
396
+
397
+ ### Upload a File
398
+
399
+ ```typescript
400
+ import { UploadService, ImageFormat } from '@flusys/ng-storage';
401
+
402
+ private readonly uploadService = inject(UploadService);
403
+
404
+ async uploadImage(file: File): Promise<IUploadedFileInfo | null> {
405
+ const response = await firstValueFrom(
406
+ this.uploadService.uploadSingleFile(file, {
407
+ storageConfigId: 'my-config-id',
408
+ folderPath: 'images/avatars',
409
+ compress: true,
410
+ maxWidth: 800,
411
+ maxHeight: 800,
412
+ quality: 80,
413
+ format: ImageFormat.WEBP,
414
+ })
415
+ );
416
+ return response.success ? response.data : null;
417
+ }
418
+ ```
419
+
420
+ ### Get Files with Presigned URLs
421
+
422
+ ```typescript
423
+ import { FileManagerApiService } from '@flusys/ng-storage';
424
+
425
+ private readonly fileService = inject(FileManagerApiService);
426
+
427
+ async getFileUrls(ids: string[]): Promise<IFilesResponseDto[]> {
428
+ const response = await firstValueFrom(this.fileService.getFiles(ids));
429
+ return response.data ?? [];
430
+ }
431
+ ```
432
+
433
+ ### Delete with Confirmation
434
+
435
+ Two delete modes are available:
436
+
437
+ ```typescript
438
+ // Soft delete - marks as deleted, recoverable
439
+ onSoftDelete(file: IFileManager): void {
440
+ this.confirmationService.confirm({
441
+ message: `Delete "${file.name}"?`,
442
+ header: 'Delete File',
443
+ acceptButtonStyleClass: 'p-button-danger',
444
+ accept: async () => {
445
+ await this.fileService.deleteAsync({ id: file.id, type: 'delete' });
446
+ this.loadFiles();
447
+ },
448
+ });
449
+ }
450
+
451
+ // Permanent delete - removes from storage provider, irreversible
452
+ onPermanentDelete(file: IFileManager): void {
453
+ this.confirmationService.confirm({
454
+ message: `Permanently delete "${file.name}"? This cannot be undone.`,
455
+ header: 'Permanent Delete',
456
+ acceptButtonStyleClass: 'p-button-danger',
457
+ accept: async () => {
458
+ await firstValueFrom(
459
+ this.uploadService.deleteSingleFile(file.key, file.storageConfigId ?? undefined)
460
+ );
461
+ await this.fileService.deleteAsync({ id: file.id, type: 'permanent' });
462
+ this.loadFiles();
463
+ },
464
+ });
465
+ }
466
+ ```
467
+
468
+ ---
469
+
470
+ ## Backend Integration
471
+
472
+ All endpoints use **POST-only RPC** style (not REST).
473
+
474
+ | Service | Endpoint | Description |
475
+ |---------|----------|-------------|
476
+ | Upload | `POST /storage/upload/single-file` | Upload single file |
477
+ | Upload | `POST /storage/upload/multiple-file` | Upload multiple files |
478
+ | Upload | `POST /storage/upload/delete-single-file` | Delete file from storage |
479
+ | Upload | `POST /storage/upload/delete-multiple-file` | Delete multiple files |
480
+ | Upload | `GET /storage/upload/file/:filePath` | Direct file download |
481
+ | FileManager | `POST /storage/file-manager/get-files` | Get files with presigned URLs |
482
+ | FileManager | `POST /storage/file-manager/insert` | Create file metadata |
483
+ | FileManager | `POST /storage/file-manager/get/:id` | Get file by ID |
484
+ | FileManager | `POST /storage/file-manager/get-all` | List files (paginated) |
485
+ | FileManager | `POST /storage/file-manager/update` | Update file metadata |
486
+ | FileManager | `POST /storage/file-manager/delete` | Delete file |
487
+ | Folder | `POST /storage/folder/insert` | Create folder |
488
+ | Folder | `POST /storage/folder/get/:id` | Get folder by ID |
489
+ | Folder | `POST /storage/folder/get-all` | List folders |
490
+ | Folder | `POST /storage/folder/update` | Update folder |
491
+ | Folder | `POST /storage/folder/delete` | Delete folder |
492
+ | StorageConfig | `POST /storage/storage-config/insert` | Create config |
493
+ | StorageConfig | `POST /storage/storage-config/get/:id` | Get config by ID |
494
+ | StorageConfig | `POST /storage/storage-config/get-all` | List configs |
495
+ | StorageConfig | `POST /storage/storage-config/update` | Update config |
496
+ | StorageConfig | `POST /storage/storage-config/delete` | Delete config |
497
+
498
+ ---
499
+
500
+ ## Best Practices
501
+
502
+ ### 1. Always Use Presigned URLs
503
+
504
+ Never construct file URLs manually. Use `FileManagerApiService.getFiles()` or `FileUrlService` from ng-shared.
505
+
506
+ ```typescript
507
+ // Wrong: manual URL
508
+ const url = `${apiBaseUrl}/storage/${fileId}`;
509
+
510
+ // Correct: backend-generated presigned URL
511
+ const files = await firstValueFrom(this.fileService.getFiles([fileId]));
512
+ const url = files.data?.[0]?.url ?? null;
513
+ ```
514
+
515
+ ### 2. Specify Storage Config for Multi-Provider Setups
516
+
517
+ ```typescript
518
+ // Specify which storage provider to use
519
+ await firstValueFrom(
520
+ this.uploadService.uploadSingleFile(file, {
521
+ storageConfigId: 'aws-media-bucket',
522
+ folderPath: 'images',
523
+ })
524
+ );
525
+ ```
526
+
527
+ ### 3. Compress Images for Web
528
+
529
+ ```typescript
530
+ const options: IUploadOptionsDto = {
531
+ compress: true,
532
+ maxWidth: 1920,
533
+ maxHeight: 1080,
534
+ quality: 85,
535
+ format: ImageFormat.WEBP,
536
+ };
537
+ ```
538
+
539
+ ### 4. Provide ConfirmationService at App Level
540
+
541
+ Storage list components use `ConfirmationService` for delete dialogs. Provide it in `app.config.ts`:
542
+
543
+ ```typescript
544
+ export const appConfig: ApplicationConfig = {
545
+ providers: [ConfirmationService, MessageService],
546
+ };
547
+ ```
548
+
549
+ ### 5. Enable Storage in APP_CONFIG
550
+
551
+ ```typescript
552
+ // environment.ts
553
+ services: {
554
+ storage: { baseUrl: 'http://localhost:2002/storage', enabled: true },
555
+ }
556
+ ```
557
+
558
+ ---
559
+
560
+ ## Public API Exports
561
+
562
+ ```typescript
563
+ // Enums
564
+ export { FileLocationEnum, ImageFormat } from '@flusys/ng-storage';
565
+
566
+ // Interfaces (all from interfaces/)
567
+ export {
568
+ IFileManager, ICreateFileManagerDto, IUpdateFileManagerDto,
569
+ IGetFilesRequestDto, IFilesResponseDto,
570
+ IFolder, ICreateFolderDto, IUpdateFolderDto,
571
+ IStorageConfig, ICreateStorageConfigDto, IUpdateStorageConfigDto,
572
+ IAwsS3Config, IAzureBlobConfig, ISftpConfig, ILocalStorageConfig,
573
+ IUploadOptionsDto, IUploadedFileInfo,
574
+ } from '@flusys/ng-storage';
575
+
576
+ // Services
577
+ export {
578
+ FileManagerApiService, FolderApiService,
579
+ StorageConfigApiService, UploadService,
580
+ } from '@flusys/ng-storage';
581
+
582
+ // Routes & Components
583
+ export { STORAGE_ROUTES } from '@flusys/ng-storage';
584
+ export { StorageContainerComponent } from '@flusys/ng-storage';
585
+ ```
586
+
587
+ ---
588
+
589
+ **Last Updated:** 2026-02-07
590
+ **Angular Version:** 21
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flusys/ng-storage",
3
- "version": "0.1.0-beta.1",
3
+ "version": "0.1.0-beta.2",
4
4
  "description": "File storage module for FLUSYS Angular applications",
5
5
  "license": "MIT",
6
6
  "peerDependencies": {