@flusys/ng-storage 1.1.0 → 1.2.0-rc.1

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 (29) hide show
  1. package/README.md +36 -762
  2. package/fesm2022/flusys-ng-storage-file-manager-form.component-DhqZmpuX.mjs +671 -0
  3. package/fesm2022/flusys-ng-storage-file-manager-form.component-DhqZmpuX.mjs.map +1 -0
  4. package/fesm2022/flusys-ng-storage-file-manager-list.component-nZDj1UfS.mjs +464 -0
  5. package/fesm2022/flusys-ng-storage-file-manager-list.component-nZDj1UfS.mjs.map +1 -0
  6. package/fesm2022/flusys-ng-storage-folder-form.component-Bru9NQyr.mjs +218 -0
  7. package/fesm2022/flusys-ng-storage-folder-form.component-Bru9NQyr.mjs.map +1 -0
  8. package/fesm2022/flusys-ng-storage-folder-list.component-fuJMWYWZ.mjs +263 -0
  9. package/fesm2022/flusys-ng-storage-folder-list.component-fuJMWYWZ.mjs.map +1 -0
  10. package/fesm2022/flusys-ng-storage-storage-config-form.component-Da-pWR7d.mjs +891 -0
  11. package/fesm2022/flusys-ng-storage-storage-config-form.component-Da-pWR7d.mjs.map +1 -0
  12. package/fesm2022/flusys-ng-storage-storage-config-list.component-D59X4sae.mjs +288 -0
  13. package/fesm2022/flusys-ng-storage-storage-config-list.component-D59X4sae.mjs.map +1 -0
  14. package/fesm2022/flusys-ng-storage.mjs +533 -216
  15. package/fesm2022/flusys-ng-storage.mjs.map +1 -1
  16. package/package.json +11 -10
  17. package/types/flusys-ng-storage.d.ts +99 -48
  18. package/fesm2022/flusys-ng-storage-file-manager-form.component-CXVB9G-f.mjs +0 -484
  19. package/fesm2022/flusys-ng-storage-file-manager-form.component-CXVB9G-f.mjs.map +0 -1
  20. package/fesm2022/flusys-ng-storage-file-manager-list.component-0O_b4EjN.mjs +0 -304
  21. package/fesm2022/flusys-ng-storage-file-manager-list.component-0O_b4EjN.mjs.map +0 -1
  22. package/fesm2022/flusys-ng-storage-folder-form.component-BTQGEqNu.mjs +0 -155
  23. package/fesm2022/flusys-ng-storage-folder-form.component-BTQGEqNu.mjs.map +0 -1
  24. package/fesm2022/flusys-ng-storage-folder-list.component-B8GoIh6Z.mjs +0 -194
  25. package/fesm2022/flusys-ng-storage-folder-list.component-B8GoIh6Z.mjs.map +0 -1
  26. package/fesm2022/flusys-ng-storage-storage-config-form.component-CorNWrmF.mjs +0 -463
  27. package/fesm2022/flusys-ng-storage-storage-config-form.component-CorNWrmF.mjs.map +0 -1
  28. package/fesm2022/flusys-ng-storage-storage-config-list.component-YXzz3Tvo.mjs +0 -187
  29. package/fesm2022/flusys-ng-storage-storage-config-list.component-YXzz3Tvo.mjs.map +0 -1
package/README.md CHANGED
@@ -1,796 +1,70 @@
1
- # Storage Package Guide
1
+ # @flusys/ng-storage
2
2
 
3
- > **Package:** `@flusys/ng-storage`
4
- > **Version:** 1.1.0
5
- > **Type:** File storage management with upload, folder organization, and multi-provider support
3
+ File storage UI for FLUSYS Angular apps — upload, file manager pages, presigned URL fetching, and provider adapter registration for Local, S3, Azure, and SFTP backends.
6
4
 
7
- ---
8
-
9
- ## Overview
10
-
11
- `@flusys/ng-storage` provides complete file storage management:
5
+ [![npm version](https://img.shields.io/npm/v/@flusys/ng-storage.svg)](https://www.npmjs.com/package/@flusys/ng-storage)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
12
7
 
13
- - **File Upload** - Single and multiple file uploads with image compression
14
- - **File Manager** - CRUD operations for file metadata with presigned URLs
15
- - **Folder Management** - Folder organization for files
16
- - **Storage Configs** - Multi-provider configurations (AWS S3, Azure Blob, SFTP, Local)
17
- - **Company Scoping** - Automatic company-scoped queries (when enabled)
18
- - **Lazy Loading** - All page components are lazy-loaded via routes
8
+ ---
19
9
 
20
- ### Package Hierarchy
10
+ ## Installation
21
11
 
22
- ```
23
- @flusys/ng-core <- Foundation (BaseApiService, APP_CONFIG)
24
- |
25
- @flusys/ng-shared <- Shared utilities (ApiResourceService, IBaseEntity)
26
- |
27
- @flusys/ng-layout <- Layout (LAYOUT_AUTH_STATE for company context)
28
- |
29
- @flusys/ng-storage <- File storage (THIS PACKAGE)
12
+ ```bash
13
+ npm install @flusys/ng-storage @flusys/ng-core @flusys/ng-shared
30
14
  ```
31
15
 
32
- ### Company Context
33
-
34
- 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.
16
+ ## 1. Register Providers
35
17
 
36
18
  ```typescript
37
- import { LAYOUT_AUTH_STATE } from '@flusys/ng-layout';
38
-
39
- private readonly companyContext = inject(LAYOUT_AUTH_STATE);
40
-
41
- readonly currentCompanyName = computed(
42
- () => this.companyContext.currentCompanyInfo()?.name ?? DEFAULT_APP_NAME,
43
- );
44
- ```
45
-
46
- ---
47
-
48
- ## Package Architecture
19
+ // app.config.ts
20
+ import { provideStorageProviders } from '@flusys/ng-storage';
49
21
 
22
+ export const appConfig: ApplicationConfig = {
23
+ providers: [
24
+ ...provideStorageProviders(), // registers FILE_PROVIDER for ng-shared's file picker and FileUrlService
25
+ ],
26
+ };
50
27
  ```
51
- ng-storage/
52
- ├── adapters/
53
- │ └── profile-upload-provider.adapter.ts # Profile upload adapter for ng-auth
54
- ├── enums/
55
- │ ├── file-location.enum.ts # FileLocationEnum, ImageFormat
56
- │ └── public-api.ts
57
- ├── interfaces/
58
- │ ├── file-manager.interface.ts # IFileManager, ICreateFileManagerDto, IUpdateFileManagerDto
59
- │ ├── folder.interface.ts # IFolder, ICreateFolderDto, IUpdateFolderDto
60
- │ ├── storage-config.interface.ts # IStorageConfig, provider-specific configs
61
- │ ├── upload.interface.ts # IUploadOptionsDto, IUploadedFileInfo
62
- │ └── public-api.ts
63
- ├── providers/
64
- │ └── storage-providers.ts # provideStorageProviders()
65
- ├── services/
66
- │ ├── file-manager-api.service.ts # File metadata CRUD + presigned URLs
67
- │ ├── folder-api.service.ts # Folder CRUD
68
- │ ├── storage-config-api.service.ts # Storage config CRUD
69
- │ └── upload.service.ts # File upload/delete operations
70
- ├── utils/
71
- │ ├── base-storage-page.ts # BaseStoragePage, BaseStorageListPage base classes
72
- │ ├── storage-utils.ts # Utility functions (icons, labels, formatters)
73
- │ └── public-api.ts
74
- ├── pages/
75
- │ ├── storage-container/
76
- │ │ └── storage-container.component.ts # Tab container (Files, Folders, Configs)
77
- │ ├── file-manager/
78
- │ │ ├── file-manager-list.component.ts
79
- │ │ └── file-manager-form.component.ts
80
- │ ├── folder/
81
- │ │ ├── folder-list.component.ts
82
- │ │ └── folder-form.component.ts
83
- │ └── storage-config/
84
- │ ├── storage-config-list.component.ts
85
- │ └── storage-config-form.component.ts
86
- ├── routes/
87
- │ └── storage.routes.ts
88
- └── public-api.ts
89
- ```
90
-
91
- ---
92
28
 
93
- ## Route Integration
29
+ `FILE_PROVIDER` is consumed by `FileSelectorComponent` and `FileUrlService` in `ng-shared`. Without this, file pickers in other packages (e.g., ng-form-builder, ng-email) will not work.
94
30
 
95
- ### Adding Storage Routes
31
+ ## 2. Add Storage Routes (Optional)
96
32
 
97
33
  ```typescript
98
34
  // app.routes.ts
35
+ import { STORAGE_ROUTES } from '@flusys/ng-storage';
36
+
99
37
  export const routes: Routes = [
100
38
  {
101
39
  path: 'storage',
102
- loadChildren: () =>
103
- import('@flusys/ng-storage').then((m) => m.STORAGE_ROUTES),
40
+ loadChildren: () => STORAGE_ROUTES,
104
41
  },
105
42
  ];
106
43
  ```
107
44
 
108
- ### Route Structure
109
-
110
- All routes are wrapped by `StorageContainerComponent` which provides tab navigation (Files, Folders, Configs).
111
-
112
- | Path | Component | Description |
113
- |------|-----------|-------------|
114
- | `/storage` | StorageContainerComponent | Container with tab navigation |
115
- | `/storage/files` | FileManagerListComponent | List all files (default) |
116
- | `/storage/files/new` | FileManagerFormComponent | Upload new file |
117
- | `/storage/files/:id` | FileManagerFormComponent | Edit file metadata |
118
- | `/storage/folders` | FolderListComponent | List all folders |
119
- | `/storage/folders/new` | FolderFormComponent | Create folder |
120
- | `/storage/folders/:id` | FolderFormComponent | Edit folder |
121
- | `/storage/configs` | StorageConfigListComponent | List storage configurations |
122
- | `/storage/configs/new` | StorageConfigFormComponent | Create storage config |
123
- | `/storage/configs/:id` | StorageConfigFormComponent | Edit storage config |
124
-
125
- Default redirect: `/storage` -> `/storage/files`
126
-
127
- ---
128
-
129
- ## Enums
130
-
131
- ### FileLocationEnum
132
-
133
- Storage provider types (matches backend).
134
-
135
- ```typescript
136
- enum FileLocationEnum {
137
- LOCAL = 'local',
138
- AWS = 'aws',
139
- AZURE = 'azure',
140
- SFTP = 'sftp',
141
- }
142
- ```
143
-
144
- ### ImageFormat
145
-
146
- Image compression output formats.
147
-
148
- ```typescript
149
- enum ImageFormat {
150
- ORIGINAL = 'original',
151
- JPEG = 'jpeg',
152
- PNG = 'png',
153
- WEBP = 'webp',
154
- }
155
- ```
156
-
157
- ---
158
-
159
- ## Interfaces
160
-
161
- ### Upload
162
-
163
- ```typescript
164
- interface IUploadOptionsDto {
165
- storageConfigId?: string; // Storage config to use (uses default if not set)
166
- folderPath?: string; // Target folder path
167
- maxWidth?: number; // Image max width (default: 1280)
168
- maxHeight?: number; // Image max height (default: 1280)
169
- quality?: number; // Image quality 1-100 (default: 85)
170
- format?: ImageFormat; // Output format
171
- compress?: boolean; // Enable compression (default: true)
172
- }
173
-
174
- interface IUploadedFileInfo {
175
- name: string; // Original filename
176
- key: string; // Storage key/path
177
- size: number; // File size in bytes
178
- contentType: string; // MIME type
179
- }
180
- ```
181
-
182
- ### File Manager
183
-
184
- ```typescript
185
- interface IFileManager {
186
- id: string;
187
- createdAt: Date;
188
- updatedAt: Date;
189
- name: string;
190
- contentType: string;
191
- size: string; // In KB (string)
192
- key: string; // File key/path in storage
193
- url: string | null; // Presigned URL (regenerated by backend)
194
- location: FileLocationEnum;
195
- storageConfigId: string | null;
196
- providerName?: string; // Storage config name (enriched from storage_config table)
197
- expiresAt: Date | null; // URL expiration time
198
- isPrivate: boolean;
199
- companyId?: string; // Only in company mode
200
- folderId?: string | null;
201
- }
202
-
203
- interface ICreateFileManagerDto {
204
- name: string;
205
- key: string;
206
- size: string;
207
- contentType: string;
208
- isPrivate: boolean;
209
- folderId?: string;
210
- storageConfigId?: string;
211
- location?: FileLocationEnum;
212
- }
213
-
214
- interface IUpdateFileManagerDto {
215
- id: string;
216
- name?: string;
217
- isPrivate?: boolean;
218
- folderId?: string | null;
219
- }
220
-
221
- // For fetching files with valid presigned URLs
222
- interface IGetFilesRequestDto { id: string; }
223
- interface IFilesResponseDto { id: string; name: string; contentType: string; url: string; }
224
- ```
225
-
226
- ### Folder
227
-
228
- ```typescript
229
- interface IFolder extends IBaseEntity {
230
- name: string;
231
- slug: string; // URL-friendly name
232
- companyId?: string | null;
233
- }
234
-
235
- interface ICreateFolderDto { name: string; }
236
- interface IUpdateFolderDto { id: string; name?: string; }
237
- ```
238
-
239
- ### Storage Config
240
-
241
- ```typescript
242
- interface IStorageConfig extends IBaseEntity {
243
- name: string;
244
- storage: FileLocationEnum;
245
- config: Record<string, any>; // Provider-specific configuration
246
- isActive: boolean; // Whether config is active and usable
247
- isDefault: boolean; // Set as default configuration
248
- companyId?: string | null;
249
- }
250
-
251
- interface ICreateStorageConfigDto {
252
- name: string;
253
- storage: FileLocationEnum;
254
- config: Record<string, any>;
255
- isActive?: boolean;
256
- isDefault?: boolean;
257
- }
258
-
259
- interface IUpdateStorageConfigDto {
260
- id: string;
261
- name?: string;
262
- storage?: FileLocationEnum;
263
- config?: Record<string, any>;
264
- isActive?: boolean;
265
- isDefault?: boolean;
266
- }
267
- ```
268
-
269
- ### Provider-Specific Configs
270
-
271
- These define the shape of `IStorageConfig.config` for each provider:
272
-
273
- ```typescript
274
- interface IAwsS3Config {
275
- region: string;
276
- bucket: string;
277
- accessKeyId: string;
278
- secretAccessKey: string;
279
- endpoint?: string; // Custom S3-compatible endpoint
280
- }
281
-
282
- interface IAzureBlobConfig {
283
- accountName: string;
284
- accountKey: string;
285
- containerName: string;
286
- }
287
-
288
- interface ISftpConfig {
289
- host: string;
290
- port: number;
291
- username: string;
292
- password?: string;
293
- privateKey?: string;
294
- basePath: string;
295
- }
296
-
297
- interface ILocalStorageConfig {
298
- basePath: string;
299
- }
300
- ```
301
-
302
- ---
303
-
304
- ## Services
305
-
306
- All services are `providedIn: 'root'`. Company scoping is automatic when the company feature is enabled.
307
-
308
- ### UploadService
309
-
310
- Handles file upload and deletion. Extends `BaseApiService` (from ng-core).
311
-
312
- | Method | Endpoint | Description |
313
- |--------|----------|-------------|
314
- | `uploadSingleFile(file, options?)` | `POST /storage/upload/single-file` | Upload single file |
315
- | `uploadMultipleFiles(files, options?)` | `POST /storage/upload/multiple-file` | Upload multiple files (max 50) |
316
- | `uploadAndRegister(file, options?)` | Combined | Upload file and create file manager record |
317
- | `deleteSingleFile(key, storageConfigId?)` | `POST /storage/upload/delete-single-file` | Delete single file from storage |
318
- | `deleteMultipleFiles(keys, storageConfigId?)` | `POST /storage/upload/delete-multiple-file` | Delete multiple files |
319
- | `getFileUrl(key)` | `GET /storage/upload/file/:key` | Get direct file download URL |
320
- | `getDefaultConfigId()` | - | Get default storage config ID from APP_CONFIG |
321
-
322
- Upload options are sent as **query parameters**, not form data:
323
-
324
- ```typescript
325
- // POST /storage/upload/single-file?storageConfigId=abc&folderPath=docs&compress=true
326
- this.uploadService.uploadSingleFile(file, {
327
- storageConfigId: 'abc',
328
- folderPath: 'docs',
329
- compress: true,
330
- quality: 85,
331
- format: ImageFormat.WEBP,
332
- });
333
- ```
334
-
335
- ### FileManagerApiService
336
-
337
- File metadata CRUD. Extends `ApiResourceService<IUpdateFileManagerDto, IFileManager>`.
338
-
339
- **Inherited CRUD methods** (from ApiResourceService): `insert`, `update`, `findById`, `getAll`, `delete` and their `*Async` variants.
340
-
341
- **Additional methods:**
342
-
343
- | Method | Endpoint | Description |
344
- |--------|----------|-------------|
345
- | `getFiles(fileIds)` | `POST /storage/file-manager/get-files` | Get files with valid presigned URLs |
346
- | `getFilesByFolder(folderId, page?, limit?)` | `POST /storage/file-manager/get-all` | Filter files by folder |
347
- | `getPrivateFiles(page?, limit?)` | `POST /storage/file-manager/get-all` | Filter by `isPrivate: true` |
348
-
349
- **Presigned URL pattern:** Always use `getFiles()` to get valid URLs. Backend regenerates expired presigned URLs for cloud storage.
350
-
351
- ```typescript
352
- const response = await firstValueFrom(
353
- this.fileService.getFiles(['file-id-1', 'file-id-2'])
354
- );
355
- // response.data → [{ id, name, contentType, url }]
356
- ```
357
-
358
- ### FolderApiService
359
-
360
- Folder CRUD. Extends `ApiResourceService<IUpdateFolderDto, IFolder>`. Standard CRUD operations only, no custom methods.
361
-
362
- ### StorageConfigApiService
363
-
364
- Storage provider configuration CRUD. Extends `ApiResourceService<IUpdateStorageConfigDto, IStorageConfig>`. Standard CRUD operations only, no custom methods.
365
-
366
- ---
367
-
368
- ## Components
369
-
370
- All components are standalone, use Angular 21 signal forms, and lazy-load via routes.
371
-
372
- ### Base Classes
373
-
374
- Components extend shared base classes from `utils/base-storage-page.ts`:
375
-
376
- ```typescript
377
- // BaseStoragePage - Common functionality for all storage pages
378
- abstract class BaseStoragePage {
379
- readonly isLoading = signal(false);
380
- readonly showCompanyInfo = computed(() => this.appConfig.enableCompanyFeature);
381
- readonly currentCompanyName = computed(() =>
382
- this.companyContext.currentCompanyInfo()?.name ?? DEFAULT_APP_NAME
383
- );
384
-
385
- protected showSuccess(detail: string): void;
386
- protected showError(detail: string): void;
387
- protected navigateTo(path: string[]): void;
388
- }
389
-
390
- // BaseStorageListPage - Adds pagination support
391
- abstract class BaseStorageListPage extends BaseStoragePage {
392
- readonly totalRecords = signal(0);
393
- readonly pageSize = signal(50);
394
- readonly currentPage = signal(1);
395
-
396
- onPageChange(event: { first?: number; rows?: number }): void;
397
- protected getPaginationParams(): { currentPage: number; pageSize: number };
398
- abstract loadData(): Promise<void>;
399
- }
400
- ```
401
-
402
- ### StorageContainerComponent
403
-
404
- Main container with tab navigation between Files, Folders, and Configs sections.
405
-
406
- **Selector:** `lib-storage-container`
407
-
408
- **Features:**
409
- - Tab navigation with `scrollbar-hide` CSS (horizontal scroll without visible scrollbar on mobile)
410
- - Responsive padding: `p-2 sm:p-4`
411
- - Dark mode compatible using Tailwind theme classes
412
-
413
- ### List Components
414
-
415
- All list components share:
416
- - **Extends `BaseStorageListPage`** for common functionality
417
- - **Lazy pagination** via `p-table` with `[paginator]="totalRecords() > 0"` (hidden when no data)
418
- - **Responsive columns** with `hidden sm:table-cell`, `hidden md:table-cell`, etc.
419
- - **Company info display** when company feature enabled
420
- - **Delete confirmation** via `ConfirmationService`
421
- - **Signal-based state:** `isLoading`, data signal, `totalRecords`, `pageSize`, `currentPage`
422
- - **Responsive title:** `text-lg sm:text-xl`
423
- - **Responsive buttons:** `w-full sm:w-auto`
424
-
425
- **FileManagerListComponent** additionally supports:
426
- - File content type icons (image, video, audio, PDF, Word, Excel, generic)
427
- - View file (opens presigned URL in new tab)
428
- - **Soft delete** - marks as deleted (recoverable)
429
- - **Permanent delete** - removes from storage provider (irreversible)
430
-
431
- ### Form Components
432
-
433
- All form components share:
434
- - **Signal Forms** using Angular 21 `form()` helper with `required()` validators
435
- - **Create/Edit mode** auto-detection based on route param (`:id`)
436
- - **Navigation** back to list on save/cancel
437
- - **Button styling pattern:**
438
- - Cancel: `severity="secondary" [outlined]="true"`
439
- - Submit: Primary style (no severity attribute)
440
- - Order: Cancel first, then Submit (right-aligned)
441
- - Responsive: `flex justify-end gap-4 pt-4`
442
-
443
- **FileManagerFormComponent** additionally supports:
444
- - PrimeNG file upload with drag-drop
445
- - Storage config selection dropdown
446
- - Folder assignment
447
- - Auto-filling filename from uploaded file
448
-
449
- **StorageConfigFormComponent** has dynamic form fields based on selected provider:
450
- - AWS S3: region, bucket, accessKeyId, secretAccessKey, endpoint
451
- - Azure Blob: accountName, accountKey, containerName
452
- - SFTP: host, port, username, password, basePath
453
- - Local: basePath
454
- - **Active toggle** - enables/disables the configuration
455
- - **Set as Default toggle** - marks configuration as the default for uploads
456
-
457
- **Default Configuration:**
458
- When `isDefault: true`, this configuration will be used automatically when no `storageConfigId` is provided in upload requests.
459
-
460
- ---
461
-
462
- ## Utility Functions
463
-
464
- The `utils/storage-utils.ts` file provides helper functions used across components:
465
-
466
- ```typescript
467
- // Get display label for storage provider
468
- getProviderLabel(storage: FileLocationEnum): string;
469
- // Returns: 'AWS S3', 'Azure Blob', 'SFTP', 'Local'
470
-
471
- // Get icon class for storage provider
472
- getProviderIcon(storage: FileLocationEnum): string;
473
- // Returns: 'pi pi-cloud', 'pi pi-server', 'pi pi-folder'
474
-
475
- // Get severity for p-tag based on provider
476
- getProviderSeverity(storage: FileLocationEnum): 'success' | 'info' | 'warn' | 'danger' | 'secondary' | 'contrast';
477
-
478
- // Get file icon based on content type
479
- getFileIcon(contentType: string): string;
480
- // Returns: 'pi pi-image', 'pi pi-video', 'pi pi-file-pdf', etc.
481
-
482
- // Format file size to human readable
483
- formatFileSize(bytes: number): string;
484
- // Returns: '1.5 MB', '256 KB', etc.
485
-
486
- // Storage provider options for dropdowns
487
- const STORAGE_PROVIDERS = [
488
- { label: 'AWS S3', value: FileLocationEnum.AWS },
489
- { label: 'Azure Blob Storage', value: FileLocationEnum.AZURE },
490
- { label: 'SFTP', value: FileLocationEnum.SFTP },
491
- { label: 'Local Storage', value: FileLocationEnum.LOCAL },
492
- ];
493
- ```
494
-
495
- ---
496
-
497
- ## Usage Examples
498
-
499
- ### Upload a File
45
+ Routes included (all protected by `permissionGuard`):
500
46
 
501
- ```typescript
502
- import { UploadService, ImageFormat } from '@flusys/ng-storage';
503
-
504
- private readonly uploadService = inject(UploadService);
505
-
506
- async uploadImage(file: File): Promise<IUploadedFileInfo | null> {
507
- const response = await firstValueFrom(
508
- this.uploadService.uploadSingleFile(file, {
509
- storageConfigId: 'my-config-id',
510
- folderPath: 'images/avatars',
511
- compress: true,
512
- maxWidth: 800,
513
- maxHeight: 800,
514
- quality: 80,
515
- format: ImageFormat.WEBP,
516
- })
517
- );
518
- return response.success ? response.data : null;
519
- }
520
- ```
521
-
522
- ### Upload and Register (Combined)
47
+ ## 4. Fetch File URLs (Critical Rule)
523
48
 
524
- Use `uploadAndRegister` for a single call that uploads and creates the file manager record:
49
+ Never construct file URLs manually. Always use `FileUrlService` from `ng-shared` — the backend generates presigned, access-controlled URLs for all storage providers.
525
50
 
526
51
  ```typescript
527
- import { UploadService, IFileManager } from '@flusys/ng-storage';
52
+ import { FileUrlService } from '@flusys/ng-shared';
528
53
 
529
- private readonly uploadService = inject(UploadService);
54
+ @Component({ ... })
55
+ export class MyComponent {
56
+ private readonly fileUrlService = inject(FileUrlService);
530
57
 
531
- async uploadProfilePicture(file: File): Promise<IFileManager | null> {
532
- const response = await firstValueFrom(
533
- this.uploadService.uploadAndRegister(file, {
534
- compress: true,
535
- quality: 85,
536
- isPrivate: false,
537
- folderId: 'avatars-folder-id',
538
- })
539
- );
540
- return response.success ? response.data : null;
58
+ async loadAvatar(fileId: string) {
59
+ // POST /file-manager/get-files → returns presigned URL
60
+ const files = await firstValueFrom(this.fileUrlService.getFileUrls([fileId]));
61
+ const url = files?.data?.[0]?.url ?? null;
62
+ }
541
63
  }
542
64
  ```
543
65
 
544
- ### Get Files with Presigned URLs
545
-
546
- ```typescript
547
- import { FileManagerApiService } from '@flusys/ng-storage';
66
+ Manual URL construction breaks with S3/Azure (presigned URLs expire) and does not enforce access control.
548
67
 
549
- private readonly fileService = inject(FileManagerApiService);
550
-
551
- async getFileUrls(ids: string[]): Promise<IFilesResponseDto[]> {
552
- const response = await firstValueFrom(this.fileService.getFiles(ids));
553
- return response.data ?? [];
554
- }
555
- ```
556
-
557
- ### Delete with Confirmation
558
-
559
- Two delete modes are available:
560
-
561
- ```typescript
562
- // Soft delete - marks as deleted, recoverable
563
- onSoftDelete(file: IFileManager): void {
564
- this.confirmationService.confirm({
565
- message: `Delete "${file.name}"?`,
566
- header: 'Delete File',
567
- acceptButtonStyleClass: 'p-button-danger',
568
- accept: async () => {
569
- await this.fileService.deleteAsync({ id: file.id, type: 'delete' });
570
- this.loadFiles();
571
- },
572
- });
573
- }
574
-
575
- // Permanent delete - removes from storage provider, irreversible
576
- onPermanentDelete(file: IFileManager): void {
577
- this.confirmationService.confirm({
578
- message: `Permanently delete "${file.name}"? This cannot be undone.`,
579
- header: 'Permanent Delete',
580
- acceptButtonStyleClass: 'p-button-danger',
581
- accept: async () => {
582
- await firstValueFrom(
583
- this.uploadService.deleteSingleFile(file.key, file.storageConfigId ?? undefined)
584
- );
585
- await this.fileService.deleteAsync({ id: file.id, type: 'permanent' });
586
- this.loadFiles();
587
- },
588
- });
589
- }
590
- ```
591
-
592
- ---
593
-
594
- ## Backend Integration
595
-
596
- All endpoints use **POST-only RPC** style (not REST).
597
-
598
- | Service | Endpoint | Description |
599
- |---------|----------|-------------|
600
- | Upload | `POST /storage/upload/single-file` | Upload single file |
601
- | Upload | `POST /storage/upload/multiple-file` | Upload multiple files |
602
- | Upload | `POST /storage/upload/delete-single-file` | Delete file from storage |
603
- | Upload | `POST /storage/upload/delete-multiple-file` | Delete multiple files |
604
- | Upload | `GET /storage/upload/file/:filePath` | Direct file download |
605
- | FileManager | `POST /storage/file-manager/get-files` | Get files with presigned URLs |
606
- | FileManager | `POST /storage/file-manager/insert` | Create file metadata |
607
- | FileManager | `POST /storage/file-manager/get/:id` | Get file by ID |
608
- | FileManager | `POST /storage/file-manager/get-all` | List files (paginated) |
609
- | FileManager | `POST /storage/file-manager/update` | Update file metadata |
610
- | FileManager | `POST /storage/file-manager/delete` | Delete file |
611
- | Folder | `POST /storage/folder/insert` | Create folder |
612
- | Folder | `POST /storage/folder/get/:id` | Get folder by ID |
613
- | Folder | `POST /storage/folder/get-all` | List folders |
614
- | Folder | `POST /storage/folder/update` | Update folder |
615
- | Folder | `POST /storage/folder/delete` | Delete folder |
616
- | StorageConfig | `POST /storage/storage-config/insert` | Create config |
617
- | StorageConfig | `POST /storage/storage-config/get/:id` | Get config by ID |
618
- | StorageConfig | `POST /storage/storage-config/get-all` | List configs |
619
- | StorageConfig | `POST /storage/storage-config/update` | Update config |
620
- | StorageConfig | `POST /storage/storage-config/delete` | Delete config |
621
-
622
- ---
623
-
624
- ## Best Practices
625
-
626
- ### 1. Always Use Presigned URLs
627
-
628
- Never construct file URLs manually. Use `FileManagerApiService.getFiles()` or `FileUrlService` from ng-shared.
629
-
630
- ```typescript
631
- // Wrong: manual URL
632
- const url = `${apiBaseUrl}/storage/${fileId}`;
633
-
634
- // Correct: backend-generated presigned URL
635
- const files = await firstValueFrom(this.fileService.getFiles([fileId]));
636
- const url = files.data?.[0]?.url ?? null;
637
- ```
638
-
639
- ### 2. Specify Storage Config for Multi-Provider Setups
640
-
641
- ```typescript
642
- // Specify which storage provider to use
643
- await firstValueFrom(
644
- this.uploadService.uploadSingleFile(file, {
645
- storageConfigId: 'aws-media-bucket',
646
- folderPath: 'images',
647
- })
648
- );
649
- ```
650
-
651
- ### 3. Compress Images for Web
652
-
653
- ```typescript
654
- const options: IUploadOptionsDto = {
655
- compress: true,
656
- maxWidth: 1920,
657
- maxHeight: 1080,
658
- quality: 85,
659
- format: ImageFormat.WEBP,
660
- };
661
- ```
662
-
663
- ### 4. Provide ConfirmationService at App Level
664
-
665
- Storage list components use `ConfirmationService` for delete dialogs. Provide it in `app.config.ts`:
666
-
667
- ```typescript
668
- export const appConfig: ApplicationConfig = {
669
- providers: [ConfirmationService, MessageService],
670
- };
671
- ```
672
-
673
- ### 5. Enable Storage in APP_CONFIG
674
-
675
- ```typescript
676
- // environment.ts
677
- services: {
678
- storage: { baseUrl: 'http://localhost:2002/storage', enabled: true },
679
- }
680
- ```
681
-
682
- ### 6. Responsive Design Patterns
683
-
684
- All components follow these responsive patterns:
685
-
686
- ```html
687
- <!-- Titles: smaller on mobile -->
688
- <h3 class="text-lg sm:text-xl font-semibold">Title</h3>
689
-
690
- <!-- Buttons: full width on mobile, auto on larger -->
691
- <p-button label="New" styleClass="w-full sm:w-auto" />
692
-
693
- <!-- Form grids: stack on mobile, 2 columns on md+ -->
694
- <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
695
-
696
- <!-- Labels: smaller text on mobile -->
697
- <label class="text-sm sm:text-base">Label</label>
698
-
699
- <!-- Table columns: hide on smaller screens -->
700
- <th class="hidden sm:table-cell">Column</th>
701
- <th class="hidden md:table-cell">Column</th>
702
-
703
- <!-- Tab navigation: horizontal scroll without visible scrollbar -->
704
- <div class="scrollbar-hide flex overflow-x-auto">
705
- ```
706
-
707
- ---
708
-
709
- ## Provider Functions
710
-
711
- ### provideStorageProviders()
712
-
713
- Registers provider adapters that enable other packages (ng-auth) to access storage functionality without direct dependencies.
714
-
715
- ```typescript
716
- import { provideStorageProviders } from '@flusys/ng-storage';
717
-
718
- // app.config.ts
719
- export const appConfig: ApplicationConfig = {
720
- providers: [
721
- ...provideStorageProviders(), // Register storage providers
722
- ],
723
- };
724
- ```
725
-
726
- **Registers:**
727
-
728
- | Token | Adapter | Purpose |
729
- |-------|---------|---------|
730
- | `PROFILE_UPLOAD_PROVIDER` | `ProfileUploadProviderAdapter` | Profile picture upload for ng-auth |
731
-
732
- ### ProfileUploadProviderAdapter
733
-
734
- Implements `IProfileUploadProvider` from ng-shared. Enables ng-auth's profile page to upload profile pictures.
735
-
736
- ```typescript
737
- interface IProfileUploadProvider {
738
- uploadProfilePicture(
739
- file: File,
740
- options?: IProfileUploadOptions
741
- ): Observable<ISingleResponse<IProfileUploadResult>>;
742
- }
743
- ```
744
-
745
- **Usage in ng-auth profile page:**
746
-
747
- ```typescript
748
- // Optional injection - graceful degradation when storage disabled
749
- private readonly uploadProvider = inject(PROFILE_UPLOAD_PROVIDER, { optional: true });
750
-
751
- // Upload profile picture
752
- if (this.uploadProvider) {
753
- this.uploadProvider.uploadProfilePicture(file, { compress: true }).subscribe(response => {
754
- // response.data.id contains the file manager ID
755
- this.updateUserProfile({ profilePictureId: response.data.id });
756
- });
757
- }
758
- ```
759
-
760
- ---
761
-
762
- ## Public API Exports
763
-
764
- ```typescript
765
- // Enums
766
- export { FileLocationEnum, ImageFormat } from '@flusys/ng-storage';
767
-
768
- // Interfaces (all from interfaces/)
769
- export {
770
- IFileManager, ICreateFileManagerDto, IUpdateFileManagerDto,
771
- IGetFilesRequestDto, IFilesResponseDto,
772
- IFolder, ICreateFolderDto, IUpdateFolderDto,
773
- IStorageConfig, ICreateStorageConfigDto, IUpdateStorageConfigDto,
774
- IAwsS3Config, IAzureBlobConfig, ISftpConfig, ILocalStorageConfig,
775
- IUploadOptionsDto, IUploadedFileInfo,
776
- } from '@flusys/ng-storage';
777
-
778
- // Services
779
- export {
780
- FileManagerApiService, FolderApiService,
781
- StorageConfigApiService, UploadService,
782
- } from '@flusys/ng-storage';
783
-
784
- // Routes & Components
785
- export { STORAGE_ROUTES } from '@flusys/ng-storage';
786
- export { StorageContainerComponent } from '@flusys/ng-storage';
787
-
788
- // Provider Functions
789
- export { provideStorageProviders } from '@flusys/ng-storage';
790
- ```
791
-
792
- ---
68
+ ## License
793
69
 
794
- **Last Updated:** 2026-02-23
795
- **Version:** 1.1.0
796
- **Angular Version:** 21
70
+ MIT © FLUSYS