@flusys/ng-storage 3.0.0-rc → 3.0.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 (16) hide show
  1. package/README.md +176 -414
  2. package/fesm2022/{flusys-ng-storage-file-manager-form.component-Bv8ER8J0.mjs → flusys-ng-storage-file-manager-form.component-CXVB9G-f.mjs} +4 -4
  3. package/fesm2022/{flusys-ng-storage-file-manager-form.component-Bv8ER8J0.mjs.map → flusys-ng-storage-file-manager-form.component-CXVB9G-f.mjs.map} +1 -1
  4. package/fesm2022/{flusys-ng-storage-file-manager-list.component-McM1zSlE.mjs → flusys-ng-storage-file-manager-list.component-0O_b4EjN.mjs} +4 -4
  5. package/fesm2022/{flusys-ng-storage-file-manager-list.component-McM1zSlE.mjs.map → flusys-ng-storage-file-manager-list.component-0O_b4EjN.mjs.map} +1 -1
  6. package/fesm2022/{flusys-ng-storage-folder-form.component-fRFiMFev.mjs → flusys-ng-storage-folder-form.component-BTQGEqNu.mjs} +4 -4
  7. package/fesm2022/{flusys-ng-storage-folder-form.component-fRFiMFev.mjs.map → flusys-ng-storage-folder-form.component-BTQGEqNu.mjs.map} +1 -1
  8. package/fesm2022/{flusys-ng-storage-folder-list.component-C4xHI8SO.mjs → flusys-ng-storage-folder-list.component-B8GoIh6Z.mjs} +4 -4
  9. package/fesm2022/{flusys-ng-storage-folder-list.component-C4xHI8SO.mjs.map → flusys-ng-storage-folder-list.component-B8GoIh6Z.mjs.map} +1 -1
  10. package/fesm2022/{flusys-ng-storage-storage-config-form.component-DEMexjmg.mjs → flusys-ng-storage-storage-config-form.component-CorNWrmF.mjs} +4 -4
  11. package/fesm2022/{flusys-ng-storage-storage-config-form.component-DEMexjmg.mjs.map → flusys-ng-storage-storage-config-form.component-CorNWrmF.mjs.map} +1 -1
  12. package/fesm2022/{flusys-ng-storage-storage-config-list.component-iJez-rII.mjs → flusys-ng-storage-storage-config-list.component-YXzz3Tvo.mjs} +4 -4
  13. package/fesm2022/{flusys-ng-storage-storage-config-list.component-iJez-rII.mjs.map → flusys-ng-storage-storage-config-list.component-YXzz3Tvo.mjs.map} +1 -1
  14. package/fesm2022/flusys-ng-storage.mjs +36 -38
  15. package/fesm2022/flusys-ng-storage.mjs.map +1 -1
  16. package/package.json +10 -10
package/README.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # Storage Package Guide
2
2
 
3
3
  > **Package:** `@flusys/ng-storage`
4
+ > **Version:** 3.0.1
4
5
  > **Type:** File storage management with upload, folder organization, and multi-provider support
5
6
 
6
7
  ---
@@ -14,14 +15,15 @@
14
15
  - **Folder Management** - Folder organization for files
15
16
  - **Storage Configs** - Multi-provider configurations (AWS S3, Azure Blob, SFTP, Local)
16
17
  - **Company Scoping** - Automatic company-scoped queries (when enabled)
18
+ - **Permission Guards** - All routes protected by permission guards
17
19
  - **Lazy Loading** - All page components are lazy-loaded via routes
18
20
 
19
21
  ### Package Hierarchy
20
22
 
21
23
  ```
22
- @flusys/ng-core <- Foundation (BaseApiService, APP_CONFIG)
24
+ @flusys/ng-core <- Foundation (BaseApiService, APP_CONFIG, getDefaultStorageConfigId)
23
25
  |
24
- @flusys/ng-shared <- Shared utilities (ApiResourceService, IBaseEntity)
26
+ @flusys/ng-shared <- Shared utilities (ApiResourceService, IBaseEntity, BaseListPage)
25
27
  |
26
28
  @flusys/ng-layout <- Layout (LAYOUT_AUTH_STATE for company context)
27
29
  |
@@ -30,17 +32,7 @@
30
32
 
31
33
  ### Company Context
32
34
 
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
- ```
35
+ 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. See `BaseStoragePage` class for implementation.
44
36
 
45
37
  ---
46
38
 
@@ -106,20 +98,20 @@ export const routes: Routes = [
106
98
 
107
99
  ### Route Structure
108
100
 
109
- All routes are wrapped by `StorageContainerComponent` which provides tab navigation (Files, Folders, Configs).
110
-
111
- | Path | Component | Description |
112
- |------|-----------|-------------|
113
- | `/storage` | StorageContainerComponent | Container with tab navigation |
114
- | `/storage/files` | FileManagerListComponent | List all files (default) |
115
- | `/storage/files/new` | FileManagerFormComponent | Upload new file |
116
- | `/storage/files/:id` | FileManagerFormComponent | Edit file metadata |
117
- | `/storage/folders` | FolderListComponent | List all folders |
118
- | `/storage/folders/new` | FolderFormComponent | Create folder |
119
- | `/storage/folders/:id` | FolderFormComponent | Edit folder |
120
- | `/storage/configs` | StorageConfigListComponent | List storage configurations |
121
- | `/storage/configs/new` | StorageConfigFormComponent | Create storage config |
122
- | `/storage/configs/:id` | StorageConfigFormComponent | Edit storage config |
101
+ All routes are wrapped by `StorageContainerComponent` which provides tab navigation (Files, Folders, Configs). Routes are protected by permission guards.
102
+
103
+ | Path | Component | Permission |
104
+ |------|-----------|------------|
105
+ | `/storage` | StorageContainerComponent | - |
106
+ | `/storage/files` | FileManagerListComponent | `FILE_PERMISSIONS.READ` |
107
+ | `/storage/files/new` | FileManagerFormComponent | `FILE_PERMISSIONS.CREATE` |
108
+ | `/storage/files/:id` | FileManagerFormComponent | `FILE_PERMISSIONS.UPDATE` |
109
+ | `/storage/folders` | FolderListComponent | `FOLDER_PERMISSIONS.READ` |
110
+ | `/storage/folders/new` | FolderFormComponent | `FOLDER_PERMISSIONS.CREATE` |
111
+ | `/storage/folders/:id` | FolderFormComponent | `FOLDER_PERMISSIONS.UPDATE` |
112
+ | `/storage/configs` | StorageConfigListComponent | `STORAGE_CONFIG_PERMISSIONS.READ` |
113
+ | `/storage/configs/new` | StorageConfigFormComponent | `STORAGE_CONFIG_PERMISSIONS.CREATE` |
114
+ | `/storage/configs/:id` | StorageConfigFormComponent | `STORAGE_CONFIG_PERMISSIONS.UPDATE` |
123
115
 
124
116
  Default redirect: `/storage` -> `/storage/files`
125
117
 
@@ -171,20 +163,19 @@ interface IUploadOptionsDto {
171
163
  }
172
164
 
173
165
  interface IUploadedFileInfo {
174
- name: string; // Original filename
175
- key: string; // Storage key/path
176
- size: number; // File size in bytes
177
- contentType: string; // MIME type
166
+ name: string; // Original filename
167
+ key: string; // Storage key/path
168
+ size: number; // File size in bytes
169
+ contentType: string; // MIME type
170
+ location?: string; // Storage location (optional)
171
+ storageConfigId?: string; // Config used for upload (optional)
178
172
  }
179
173
  ```
180
174
 
181
175
  ### File Manager
182
176
 
183
177
  ```typescript
184
- interface IFileManager {
185
- id: string;
186
- createdAt: Date;
187
- updatedAt: Date;
178
+ interface IFileManager extends IBaseEntity {
188
179
  name: string;
189
180
  contentType: string;
190
181
  size: string; // In KB (string)
@@ -232,7 +223,7 @@ interface IFolder extends IBaseEntity {
232
223
  }
233
224
 
234
225
  interface ICreateFolderDto { name: string; }
235
- interface IUpdateFolderDto { id: string; name?: string; }
226
+ interface IUpdateFolderDto extends Partial<ICreateFolderDto> { id: string; }
236
227
  ```
237
228
 
238
229
  ### Storage Config
@@ -241,27 +232,22 @@ interface IUpdateFolderDto { id: string; name?: string; }
241
232
  interface IStorageConfig extends IBaseEntity {
242
233
  name: string;
243
234
  storage: FileLocationEnum;
244
- config: Record<string, any>; // Provider-specific configuration
245
- isActive: boolean; // Whether config is active and usable
246
- isDefault: boolean; // Set as default configuration
235
+ config: Record<string, unknown>; // Provider-specific configuration
236
+ isActive: boolean; // Whether config is active and usable
237
+ isDefault: boolean; // Set as default configuration
247
238
  companyId?: string | null;
248
239
  }
249
240
 
250
241
  interface ICreateStorageConfigDto {
251
242
  name: string;
252
243
  storage: FileLocationEnum;
253
- config: Record<string, any>;
244
+ config: Record<string, unknown>;
254
245
  isActive?: boolean;
255
246
  isDefault?: boolean;
256
247
  }
257
248
 
258
- interface IUpdateStorageConfigDto {
249
+ interface IUpdateStorageConfigDto extends Partial<ICreateStorageConfigDto> {
259
250
  id: string;
260
- name?: string;
261
- storage?: FileLocationEnum;
262
- config?: Record<string, any>;
263
- isActive?: boolean;
264
- isDefault?: boolean;
265
251
  }
266
252
  ```
267
253
 
@@ -269,34 +255,12 @@ interface IUpdateStorageConfigDto {
269
255
 
270
256
  These define the shape of `IStorageConfig.config` for each provider:
271
257
 
272
- ```typescript
273
- interface IAwsS3Config {
274
- region: string;
275
- bucket: string;
276
- accessKeyId: string;
277
- secretAccessKey: string;
278
- endpoint?: string; // Custom S3-compatible endpoint
279
- }
280
-
281
- interface IAzureBlobConfig {
282
- accountName: string;
283
- accountKey: string;
284
- containerName: string;
285
- }
286
-
287
- interface ISftpConfig {
288
- host: string;
289
- port: number;
290
- username: string;
291
- password?: string;
292
- privateKey?: string;
293
- basePath: string;
294
- }
295
-
296
- interface ILocalStorageConfig {
297
- basePath: string;
298
- }
299
- ```
258
+ | Interface | Required Fields | Optional Fields |
259
+ |-----------|-----------------|-----------------|
260
+ | `IAwsS3Config` | `region`, `bucket`, `accessKeyId`, `secretAccessKey` | `endpoint` (custom S3-compatible) |
261
+ | `IAzureBlobConfig` | `accountName`, `accountKey`, `containerName` | - |
262
+ | `ISftpConfig` | `host`, `port`, `username`, `basePath` | `password`, `privateKey` |
263
+ | `ILocalStorageConfig` | `basePath` | - |
300
264
 
301
265
  ---
302
266
 
@@ -306,7 +270,9 @@ All services are `providedIn: 'root'`. Company scoping is automatic when the com
306
270
 
307
271
  ### UploadService
308
272
 
309
- Handles file upload and deletion. Extends `BaseApiService` (from ng-core).
273
+ Handles file upload and deletion. Extends `BaseApiService` (from ng-core) with base path `/storage`.
274
+
275
+ **Methods:**
310
276
 
311
277
  | Method | Endpoint | Description |
312
278
  |--------|----------|-------------|
@@ -318,85 +284,52 @@ Handles file upload and deletion. Extends `BaseApiService` (from ng-core).
318
284
  | `getFileUrl(key)` | `GET /storage/upload/file/:key` | Get direct file download URL |
319
285
  | `getDefaultConfigId()` | - | Get default storage config ID from APP_CONFIG |
320
286
 
321
- Upload options are sent as **query parameters**, not form data:
322
-
323
- ```typescript
324
- // POST /storage/upload/single-file?storageConfigId=abc&folderPath=docs&compress=true
325
- this.uploadService.uploadSingleFile(file, {
326
- storageConfigId: 'abc',
327
- folderPath: 'docs',
328
- compress: true,
329
- quality: 85,
330
- format: ImageFormat.WEBP,
331
- });
332
- ```
287
+ **Upload options** are sent as query parameters. **Storage Config Resolution:** Uses `storageConfigId` from options if provided, otherwise falls back to `getDefaultConfigId()` from APP_CONFIG. See Usage Examples for code samples.
333
288
 
334
289
  ### FileManagerApiService
335
290
 
336
- File metadata CRUD. Extends `ApiResourceService<IUpdateFileManagerDto, IFileManager>`.
291
+ File metadata CRUD. Extends `ApiResourceService<IUpdateFileManagerDto, IFileManager>` with endpoint `/storage/file-manager`.
337
292
 
338
- **Inherited CRUD methods** (from ApiResourceService): `insert`, `update`, `findById`, `getAll`, `delete` and their `*Async` variants.
293
+ **Inherited CRUD:** `insert`, `update`, `findById`, `getAll`, `delete` (and `*Async` variants)
339
294
 
340
295
  **Additional methods:**
341
296
 
342
297
  | Method | Endpoint | Description |
343
298
  |--------|----------|-------------|
344
- | `getFiles(fileIds)` | `POST /storage/file-manager/get-files` | Get files with valid presigned URLs |
345
- | `getFilesByFolder(folderId, page?, limit?)` | `POST /storage/file-manager/get-all` | Filter files by folder |
346
- | `getPrivateFiles(page?, limit?)` | `POST /storage/file-manager/get-all` | Filter by `isPrivate: true` |
347
-
348
- **Presigned URL pattern:** Always use `getFiles()` to get valid URLs. Backend regenerates expired presigned URLs for cloud storage.
299
+ | `getFiles(fileIds: string[])` | `POST /storage/file-manager/get-files` | Get files with valid presigned URLs |
300
+ | `createFile(dto: ICreateFileManagerDto)` | `POST /storage/file-manager/insert` | Create file record (Observable) |
301
+ | `createFileAsync(dto: ICreateFileManagerDto)` | `POST /storage/file-manager/insert` | Create file record (Promise) |
349
302
 
350
- ```typescript
351
- const response = await firstValueFrom(
352
- this.fileService.getFiles(['file-id-1', 'file-id-2'])
353
- );
354
- // response.data → [{ id, name, contentType, url }]
355
- ```
303
+ **Presigned URL pattern:** Always use `getFiles()` to get valid URLs. Backend regenerates expired presigned URLs for cloud storage. See Usage Examples.
356
304
 
357
305
  ### FolderApiService
358
306
 
359
- Folder CRUD. Extends `ApiResourceService<IUpdateFolderDto, IFolder>`. Standard CRUD operations only, no custom methods.
307
+ Folder CRUD. Extends `ApiResourceService<ICreateFolderDto | IUpdateFolderDto, IFolder>` with endpoint `/storage/folder`. Standard CRUD operations only.
360
308
 
361
309
  ### StorageConfigApiService
362
310
 
363
- Storage provider configuration CRUD. Extends `ApiResourceService<IUpdateStorageConfigDto, IStorageConfig>`. Standard CRUD operations only, no custom methods.
311
+ Storage config CRUD. Extends `ApiResourceService<ICreateStorageConfigDto | IUpdateStorageConfigDto, IStorageConfig>` with endpoint `/storage/storage-config`. Standard CRUD operations only.
364
312
 
365
313
  ---
366
314
 
367
- ## Components
315
+ ## Base Classes
368
316
 
369
- All components are standalone, use Angular 21 signal forms, and lazy-load via routes.
317
+ Components extend shared base classes from `utils/base-storage-page.ts`:
370
318
 
371
- ### Base Classes
319
+ | Class | Extends | Purpose | Key Members |
320
+ |-------|---------|---------|-------------|
321
+ | `BaseStoragePage` | - | Form pages | `isLoading`, `showCompanyInfo`, `currentCompanyName`, `showSuccess()`, `showError()`, `navigateTo()` |
322
+ | `BaseStorageListPage<T>` | `BaseListPage<T>` | List pages | `pageSize=50`, `totalRecords`, `currentCompanyName`, `getDeleteConfirmMessage()` |
372
323
 
373
- Components extend shared base classes from `utils/base-storage-page.ts`:
324
+ Both inject: `Router`, `MessageService`, `APP_CONFIG`, `LAYOUT_AUTH_STATE`
374
325
 
375
- ```typescript
376
- // BaseStoragePage - Common functionality for all storage pages
377
- abstract class BaseStoragePage {
378
- readonly isLoading = signal(false);
379
- readonly showCompanyInfo = computed(() => this.appConfig.enableCompanyFeature);
380
- readonly currentCompanyName = computed(() =>
381
- this.companyContext.currentCompanyInfo()?.name ?? DEFAULT_APP_NAME
382
- );
383
-
384
- protected showSuccess(detail: string): void;
385
- protected showError(detail: string): void;
386
- protected navigateTo(path: string[]): void;
387
- }
326
+ **BaseStorageListPage inherits from BaseListPage:** `items`, `total`, `isLoading`, `currentPage`, `onPageChange()`, `getPaginationParams()`, `onCreate()`, `onEdit()`, `onDeleteAsync()`, `loadData()` (abstract), `getResourceRoute()` (abstract)
388
327
 
389
- // BaseStorageListPage - Adds pagination support
390
- abstract class BaseStorageListPage extends BaseStoragePage {
391
- readonly totalRecords = signal(0);
392
- readonly pageSize = signal(50);
393
- readonly currentPage = signal(1);
328
+ ---
394
329
 
395
- onPageChange(event: { first?: number; rows?: number }): void;
396
- protected getPaginationParams(): { currentPage: number; pageSize: number };
397
- abstract loadData(): Promise<void>;
398
- }
399
- ```
330
+ ## Components
331
+
332
+ All components are standalone, use Angular 21 signal forms, and lazy-load via routes.
400
333
 
401
334
  ### StorageContainerComponent
402
335
 
@@ -405,91 +338,65 @@ Main container with tab navigation between Files, Folders, and Configs sections.
405
338
  **Selector:** `lib-storage-container`
406
339
 
407
340
  **Features:**
408
- - Tab navigation with `scrollbar-hide` CSS (horizontal scroll without visible scrollbar on mobile)
341
+ - Tab navigation filtered by user permissions (via `PermissionValidatorService`)
342
+ - Horizontal scroll without visible scrollbar (`scrollbar-hide`)
343
+ - Dynamic subtitle based on visible tabs
409
344
  - Responsive padding: `p-2 sm:p-4`
410
- - Dark mode compatible using Tailwind theme classes
345
+ - Dark mode compatible (Tailwind theme classes)
411
346
 
412
347
  ### List Components
413
348
 
414
- All list components share:
415
- - **Extends `BaseStorageListPage`** for common functionality
416
- - **Lazy pagination** via `p-table` with `[paginator]="totalRecords() > 0"` (hidden when no data)
417
- - **Responsive columns** with `hidden sm:table-cell`, `hidden md:table-cell`, etc.
418
- - **Company info display** when company feature enabled
419
- - **Delete confirmation** via `ConfirmationService`
420
- - **Signal-based state:** `isLoading`, data signal, `totalRecords`, `pageSize`, `currentPage`
421
- - **Responsive title:** `text-lg sm:text-xl`
422
- - **Responsive buttons:** `w-full sm:w-auto`
423
-
424
- **FileManagerListComponent** additionally supports:
349
+ All list components extend `BaseStorageListPage<T>` with: lazy pagination (`p-table`), responsive columns (`hidden sm:table-cell`), company info display, delete confirmation (`ConfirmationService`), signal-based state (`isLoading`, `totalRecords`, `pageSize`, `currentPage`), and permission-based actions (`*hasPermission`). Responsive styling: titles `text-lg sm:text-xl`, buttons `w-full sm:w-auto`.
350
+
351
+ #### FileManagerListComponent
352
+
353
+ **Selector:** `lib-file-manager-list`
354
+
355
+ **Features:**
425
356
  - File content type icons (image, video, audio, PDF, Word, Excel, generic)
426
- - View file (opens presigned URL in new tab)
427
- - **Soft delete** - marks as deleted (recoverable)
428
- - **Permanent delete** - removes from storage provider (irreversible)
357
+ - Provider location tags with severity colors
358
+ - View file: fetches presigned URL via `getFiles()`, opens in new tab (tracks loading with `viewingFileId` signal)
359
+ - **Soft delete** (`type: 'delete'`) - recoverable | **Permanent delete** (`type: 'permanent'`) - irreversible
360
+
361
+ #### FolderListComponent
362
+
363
+ **Selector:** `lib-folder-list`
364
+
365
+ Simple CRUD list with folder icon, name, slug, and created date columns.
366
+
367
+ #### StorageConfigListComponent
368
+
369
+ **Selector:** `lib-storage-config-list`
370
+
371
+ Lists storage configurations with provider icons and tags.
429
372
 
430
373
  ### Form Components
431
374
 
432
- All form components share:
433
- - **Signal Forms** using Angular 21 `form()` helper with `required()` validators
434
- - **Create/Edit mode** auto-detection based on route param (`:id`)
435
- - **Navigation** back to list on save/cancel
436
- - **Button styling pattern:**
437
- - Cancel: `severity="secondary" [outlined]="true"`
438
- - Submit: Primary style (no severity attribute)
439
- - Order: Cancel first, then Submit (right-aligned)
440
- - Responsive: `flex justify-end gap-4 pt-4`
441
-
442
- **FileManagerFormComponent** additionally supports:
443
- - PrimeNG file upload with drag-drop
444
- - Storage config selection dropdown
445
- - Folder assignment
446
- - Auto-filling filename from uploaded file
447
-
448
- **StorageConfigFormComponent** has dynamic form fields based on selected provider:
449
- - AWS S3: region, bucket, accessKeyId, secretAccessKey, endpoint
450
- - Azure Blob: accountName, accountKey, containerName
451
- - SFTP: host, port, username, password, basePath
452
- - Local: basePath
453
- - **Active toggle** - enables/disables the configuration
454
- - **Set as Default toggle** - marks configuration as the default for uploads
455
-
456
- **Default Configuration:**
457
- When `isDefault: true`, this configuration will be used automatically when no `storageConfigId` is provided in upload requests.
375
+ All form components share: Signal Forms with `form()` + `required()` validators, create/edit mode detection via `:id` route param, `effect()` + `toSignal(route.paramMap)` initialization, navigation back on save/cancel, computed validation, and consistent button styling (`Cancel: secondary outlined`, `Submit: primary`, order: Cancel → Submit, container: `flex justify-end gap-2 mt-4`).
376
+
377
+ | Component | Selector | Form Fields | Notes |
378
+ |-----------|----------|-------------|-------|
379
+ | FileManagerForm | `lib-file-manager-form` | `id`, `name`, `isPrivate`, `folderId`, `storageConfigId` | PrimeNG file upload (50MB max), storage config required for upload, edit mode shows metadata only |
380
+ | FolderForm | `lib-folder-form` | `id`, `name` | Simple name-only form |
381
+ | StorageConfigForm | `lib-storage-config-form` | `id`, `name`, `storage`, `isActive`, `isDefault`, provider-specific fields | Dynamic fields based on provider; defaults: `storage: AWS`, `isActive: true`, `sftpPort: '22'` |
382
+
383
+ **FileManagerForm Upload Flow:** 1) Upload file via `uploadSingleFile()` 2) Create metadata via `createFileAsync()`. Edit mode only calls `updateAsync()`.
458
384
 
459
385
  ---
460
386
 
461
387
  ## Utility Functions
462
388
 
463
- The `utils/storage-utils.ts` file provides helper functions used across components:
389
+ The `utils/storage-utils.ts` file provides helper functions:
464
390
 
465
- ```typescript
466
- // Get display label for storage provider
467
- getProviderLabel(storage: FileLocationEnum): string;
468
- // Returns: 'AWS S3', 'Azure Blob', 'SFTP', 'Local'
469
-
470
- // Get icon class for storage provider
471
- getProviderIcon(storage: FileLocationEnum): string;
472
- // Returns: 'pi pi-cloud', 'pi pi-server', 'pi pi-folder'
473
-
474
- // Get severity for p-tag based on provider
475
- getProviderSeverity(storage: FileLocationEnum): 'success' | 'info' | 'warn' | 'danger' | 'secondary' | 'contrast';
476
-
477
- // Get file icon based on content type
478
- getFileIcon(contentType: string): string;
479
- // Returns: 'pi pi-image', 'pi pi-video', 'pi pi-file-pdf', etc.
480
-
481
- // Format file size to human readable
482
- formatFileSize(bytes: number): string;
483
- // Returns: '1.5 MB', '256 KB', etc.
484
-
485
- // Storage provider options for dropdowns
486
- const STORAGE_PROVIDERS = [
487
- { label: 'AWS S3', value: FileLocationEnum.AWS },
488
- { label: 'Azure Blob Storage', value: FileLocationEnum.AZURE },
489
- { label: 'SFTP', value: FileLocationEnum.SFTP },
490
- { label: 'Local Storage', value: FileLocationEnum.LOCAL },
491
- ];
492
- ```
391
+ | Function | Returns | Description |
392
+ |----------|---------|-------------|
393
+ | `getProviderLabel(storage)` | `string` | Display label: 'AWS S3', 'Azure Blob', 'SFTP', 'Local' |
394
+ | `getProviderIcon(storage)` | `string` | Icon class: `pi pi-cloud`, `pi pi-server`, `pi pi-folder` |
395
+ | `getProviderSeverity(storage)` | severity | Tag severity: `warn` (AWS), `info` (Azure), `contrast` (SFTP), `secondary` (Local) |
396
+ | `getFileIcon(contentType)` | `string` | File icon based on MIME type (image, video, audio, pdf, word, excel, generic) |
397
+ | `formatFileSize(bytes)` | `string` | Human readable: '256 KB', '1.5 MB', '2.3 GB' |
398
+
399
+ **Constants:** `STORAGE_PROVIDERS` - Dropdown options array with `{ label, value }` for AWS S3, Azure Blob, SFTP, Local Storage.
493
400
 
494
401
  ---
495
402
 
@@ -498,24 +405,13 @@ const STORAGE_PROVIDERS = [
498
405
  ### Upload a File
499
406
 
500
407
  ```typescript
501
- import { UploadService, ImageFormat } from '@flusys/ng-storage';
502
-
503
- private readonly uploadService = inject(UploadService);
504
-
505
- async uploadImage(file: File): Promise<IUploadedFileInfo | null> {
506
- const response = await firstValueFrom(
507
- this.uploadService.uploadSingleFile(file, {
508
- storageConfigId: 'my-config-id',
509
- folderPath: 'images/avatars',
510
- compress: true,
511
- maxWidth: 800,
512
- maxHeight: 800,
513
- quality: 80,
514
- format: ImageFormat.WEBP,
515
- })
516
- );
517
- return response.success ? response.data : null;
518
- }
408
+ const response = await firstValueFrom(
409
+ this.uploadService.uploadSingleFile(file, {
410
+ storageConfigId: 'my-config-id', folderPath: 'images/avatars',
411
+ compress: true, maxWidth: 800, maxHeight: 800, quality: 80, format: ImageFormat.WEBP,
412
+ })
413
+ );
414
+ return response.success ? response.data : null;
519
415
  ```
520
416
 
521
417
  ### Upload and Register (Combined)
@@ -523,100 +419,55 @@ async uploadImage(file: File): Promise<IUploadedFileInfo | null> {
523
419
  Use `uploadAndRegister` for a single call that uploads and creates the file manager record:
524
420
 
525
421
  ```typescript
526
- import { UploadService, IFileManager } from '@flusys/ng-storage';
527
-
528
- private readonly uploadService = inject(UploadService);
529
-
530
- async uploadProfilePicture(file: File): Promise<IFileManager | null> {
531
- const response = await firstValueFrom(
532
- this.uploadService.uploadAndRegister(file, {
533
- compress: true,
534
- quality: 85,
535
- isPrivate: false,
536
- folderId: 'avatars-folder-id',
537
- })
538
- );
539
- return response.success ? response.data : null;
540
- }
422
+ const response = await firstValueFrom(
423
+ this.uploadService.uploadAndRegister(file, { compress: true, quality: 85, isPrivate: false, folderId: 'avatars-folder-id' })
424
+ );
425
+ return response.success ? response.data : null;
541
426
  ```
542
427
 
543
428
  ### Get Files with Presigned URLs
544
429
 
545
430
  ```typescript
546
- import { FileManagerApiService } from '@flusys/ng-storage';
547
-
548
- private readonly fileService = inject(FileManagerApiService);
549
-
550
- async getFileUrls(ids: string[]): Promise<IFilesResponseDto[]> {
551
- const response = await firstValueFrom(this.fileService.getFiles(ids));
552
- return response.data ?? [];
553
- }
431
+ const response = await firstValueFrom(this.fileService.getFiles(['file-id-1', 'file-id-2']));
432
+ // response.data → [{ id, name, contentType, url }]
554
433
  ```
555
434
 
556
- ### Delete with Confirmation
435
+ ### Delete Files
557
436
 
558
- Two delete modes are available:
437
+ | Mode | Type | Description |
438
+ |------|------|-------------|
439
+ | Soft delete | `type: 'delete'` | Marks as deleted (recoverable) |
440
+ | Permanent delete | `type: 'permanent'` | Removes from storage provider (irreversible) |
559
441
 
560
442
  ```typescript
561
- // Soft delete - marks as deleted, recoverable
562
- onSoftDelete(file: IFileManager): void {
563
- this.confirmationService.confirm({
564
- message: `Delete "${file.name}"?`,
565
- header: 'Delete File',
566
- acceptButtonStyleClass: 'p-button-danger',
567
- accept: async () => {
568
- await this.fileService.deleteAsync({ id: file.id, type: 'delete' });
569
- this.loadFiles();
570
- },
571
- });
572
- }
443
+ await this.fileService.deleteAsync({ id: file.id, type: 'delete' }); // Soft
444
+ await this.fileService.deleteAsync({ id: file.id, type: 'permanent' }); // Permanent
445
+ ```
573
446
 
574
- // Permanent delete - removes from storage provider, irreversible
575
- onPermanentDelete(file: IFileManager): void {
576
- this.confirmationService.confirm({
577
- message: `Permanently delete "${file.name}"? This cannot be undone.`,
578
- header: 'Permanent Delete',
579
- acceptButtonStyleClass: 'p-button-danger',
580
- accept: async () => {
581
- await firstValueFrom(
582
- this.uploadService.deleteSingleFile(file.key, file.storageConfigId ?? undefined)
583
- );
584
- await this.fileService.deleteAsync({ id: file.id, type: 'permanent' });
585
- this.loadFiles();
586
- },
587
- });
588
- }
447
+ ### Create Storage Configuration
448
+
449
+ ```typescript
450
+ await this.configService.insertAsync({
451
+ name: 'Production AWS S3',
452
+ storage: FileLocationEnum.AWS,
453
+ config: { region: 'us-east-1', bucket: 'my-bucket', accessKeyId: 'AKIA...', secretAccessKey: '...' },
454
+ isActive: true,
455
+ isDefault: true,
456
+ });
589
457
  ```
590
458
 
591
459
  ---
592
460
 
593
461
  ## Backend Integration
594
462
 
595
- All endpoints use **POST-only RPC** style (not REST).
596
-
597
- | Service | Endpoint | Description |
598
- |---------|----------|-------------|
599
- | Upload | `POST /storage/upload/single-file` | Upload single file |
600
- | Upload | `POST /storage/upload/multiple-file` | Upload multiple files |
601
- | Upload | `POST /storage/upload/delete-single-file` | Delete file from storage |
602
- | Upload | `POST /storage/upload/delete-multiple-file` | Delete multiple files |
603
- | Upload | `GET /storage/upload/file/:filePath` | Direct file download |
604
- | FileManager | `POST /storage/file-manager/get-files` | Get files with presigned URLs |
605
- | FileManager | `POST /storage/file-manager/insert` | Create file metadata |
606
- | FileManager | `POST /storage/file-manager/get/:id` | Get file by ID |
607
- | FileManager | `POST /storage/file-manager/get-all` | List files (paginated) |
608
- | FileManager | `POST /storage/file-manager/update` | Update file metadata |
609
- | FileManager | `POST /storage/file-manager/delete` | Delete file |
610
- | Folder | `POST /storage/folder/insert` | Create folder |
611
- | Folder | `POST /storage/folder/get/:id` | Get folder by ID |
612
- | Folder | `POST /storage/folder/get-all` | List folders |
613
- | Folder | `POST /storage/folder/update` | Update folder |
614
- | Folder | `POST /storage/folder/delete` | Delete folder |
615
- | StorageConfig | `POST /storage/storage-config/insert` | Create config |
616
- | StorageConfig | `POST /storage/storage-config/get/:id` | Get config by ID |
617
- | StorageConfig | `POST /storage/storage-config/get-all` | List configs |
618
- | StorageConfig | `POST /storage/storage-config/update` | Update config |
619
- | StorageConfig | `POST /storage/storage-config/delete` | Delete config |
463
+ All endpoints use **POST-only RPC** style (not REST). Base paths:
464
+
465
+ | Service | Base Path | Standard CRUD |
466
+ |---------|-----------|---------------|
467
+ | Upload | `/storage/upload` | Custom methods (see Services section) |
468
+ | FileManager | `/storage/file-manager` | `insert`, `get/:id`, `get-all`, `update`, `delete` + `get-files` |
469
+ | Folder | `/storage/folder` | `insert`, `get/:id`, `get-all`, `update`, `delete` |
470
+ | StorageConfig | `/storage/storage-config` | `insert`, `get/:id`, `get-all`, `update`, `delete` |
620
471
 
621
472
  ---
622
473
 
@@ -627,10 +478,8 @@ All endpoints use **POST-only RPC** style (not REST).
627
478
  Never construct file URLs manually. Use `FileManagerApiService.getFiles()` or `FileUrlService` from ng-shared.
628
479
 
629
480
  ```typescript
630
- // Wrong: manual URL
631
- const url = `${apiBaseUrl}/storage/${fileId}`;
632
-
633
- // Correct: backend-generated presigned URL
481
+ // Wrong: const url = `${apiBaseUrl}/storage/${fileId}`;
482
+ // Correct:
634
483
  const files = await firstValueFrom(this.fileService.getFiles([fileId]));
635
484
  const url = files.data?.[0]?.url ?? null;
636
485
  ```
@@ -638,70 +487,34 @@ const url = files.data?.[0]?.url ?? null;
638
487
  ### 2. Specify Storage Config for Multi-Provider Setups
639
488
 
640
489
  ```typescript
641
- // Specify which storage provider to use
642
- await firstValueFrom(
643
- this.uploadService.uploadSingleFile(file, {
644
- storageConfigId: 'aws-media-bucket',
645
- folderPath: 'images',
646
- })
647
- );
490
+ this.uploadService.uploadSingleFile(file, { storageConfigId: 'aws-media-bucket', folderPath: 'images' });
648
491
  ```
649
492
 
650
493
  ### 3. Compress Images for Web
651
494
 
652
495
  ```typescript
653
- const options: IUploadOptionsDto = {
654
- compress: true,
655
- maxWidth: 1920,
656
- maxHeight: 1080,
657
- quality: 85,
658
- format: ImageFormat.WEBP,
659
- };
496
+ { compress: true, maxWidth: 1920, maxHeight: 1080, quality: 85, format: ImageFormat.WEBP }
660
497
  ```
661
498
 
662
- ### 4. Provide ConfirmationService at App Level
499
+ ### 4. Provide Required Services at App Level
663
500
 
664
- Storage list components use `ConfirmationService` for delete dialogs. Provide it in `app.config.ts`:
665
-
666
- ```typescript
667
- export const appConfig: ApplicationConfig = {
668
- providers: [ConfirmationService, MessageService],
669
- };
670
- ```
501
+ Provide `ConfirmationService` and `MessageService` in `app.config.ts` for dialogs and toasts.
671
502
 
672
503
  ### 5. Enable Storage in APP_CONFIG
673
504
 
674
505
  ```typescript
675
- // environment.ts
676
- services: {
677
- storage: { baseUrl: 'http://localhost:2002/storage', enabled: true },
678
- }
506
+ services: { storage: { baseUrl: 'http://localhost:2002/storage', enabled: true } }
679
507
  ```
680
508
 
681
509
  ### 6. Responsive Design Patterns
682
510
 
683
- All components follow these responsive patterns:
684
-
685
- ```html
686
- <!-- Titles: smaller on mobile -->
687
- <h3 class="text-lg sm:text-xl font-semibold">Title</h3>
688
-
689
- <!-- Buttons: full width on mobile, auto on larger -->
690
- <p-button label="New" styleClass="w-full sm:w-auto" />
691
-
692
- <!-- Form grids: stack on mobile, 2 columns on md+ -->
693
- <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
694
-
695
- <!-- Labels: smaller text on mobile -->
696
- <label class="text-sm sm:text-base">Label</label>
697
-
698
- <!-- Table columns: hide on smaller screens -->
699
- <th class="hidden sm:table-cell">Column</th>
700
- <th class="hidden md:table-cell">Column</th>
701
-
702
- <!-- Tab navigation: horizontal scroll without visible scrollbar -->
703
- <div class="scrollbar-hide flex overflow-x-auto">
704
- ```
511
+ | Pattern | Classes |
512
+ |---------|---------|
513
+ | Titles | `text-lg sm:text-xl` |
514
+ | Buttons | `w-full sm:w-auto` |
515
+ | Form grids | `grid grid-cols-1 md:grid-cols-2 gap-4` |
516
+ | Table columns | `hidden sm:table-cell`, `hidden md:table-cell`, etc. |
517
+ | Tab navigation | `scrollbar-hide flex overflow-x-auto` |
705
518
 
706
519
  ---
707
520
 
@@ -712,83 +525,32 @@ All components follow these responsive patterns:
712
525
  Registers provider adapters that enable other packages (ng-auth) to access storage functionality without direct dependencies.
713
526
 
714
527
  ```typescript
715
- import { provideStorageProviders } from '@flusys/ng-storage';
716
-
717
- // app.config.ts
718
- export const appConfig: ApplicationConfig = {
719
- providers: [
720
- ...provideStorageProviders(), // Register storage providers
721
- ],
722
- };
528
+ // app.config.ts - add to providers array
529
+ ...provideStorageProviders()
723
530
  ```
724
531
 
725
- **Registers:**
726
-
727
- | Token | Adapter | Purpose |
728
- |-------|---------|---------|
729
- | `PROFILE_UPLOAD_PROVIDER` | `ProfileUploadProviderAdapter` | Profile picture upload for ng-auth |
730
-
731
- ### ProfileUploadProviderAdapter
732
-
733
- Implements `IProfileUploadProvider` from ng-shared. Enables ng-auth's profile page to upload profile pictures.
532
+ **Registers:** `PROFILE_UPLOAD_PROVIDER` → `ProfileUploadProviderAdapter` (implements `IProfileUploadProvider` from ng-shared)
734
533
 
735
- ```typescript
736
- interface IProfileUploadProvider {
737
- uploadProfilePicture(
738
- file: File,
739
- options?: IProfileUploadOptions
740
- ): Observable<ISingleResponse<IProfileUploadResult>>;
741
- }
742
- ```
534
+ **ProfileUploadProviderAdapter defaults:** `folderPath: 'profile-pictures'`, `compress: true`, `maxWidth: 400`, `maxHeight: 400`
743
535
 
744
- **Usage in ng-auth profile page:**
745
-
746
- ```typescript
747
- // Optional injection - graceful degradation when storage disabled
748
- private readonly uploadProvider = inject(PROFILE_UPLOAD_PROVIDER, { optional: true });
749
-
750
- // Upload profile picture
751
- if (this.uploadProvider) {
752
- this.uploadProvider.uploadProfilePicture(file, { compress: true }).subscribe(response => {
753
- // response.data.id contains the file manager ID
754
- this.updateUserProfile({ profilePictureId: response.data.id });
755
- });
756
- }
757
- ```
536
+ **Usage:** `inject(PROFILE_UPLOAD_PROVIDER, { optional: true })?.uploadProfilePicture(file)`
758
537
 
759
538
  ---
760
539
 
761
540
  ## Public API Exports
762
541
 
763
- ```typescript
764
- // Enums
765
- export { FileLocationEnum, ImageFormat } from '@flusys/ng-storage';
766
-
767
- // Interfaces (all from interfaces/)
768
- export {
769
- IFileManager, ICreateFileManagerDto, IUpdateFileManagerDto,
770
- IGetFilesRequestDto, IFilesResponseDto,
771
- IFolder, ICreateFolderDto, IUpdateFolderDto,
772
- IStorageConfig, ICreateStorageConfigDto, IUpdateStorageConfigDto,
773
- IAwsS3Config, IAzureBlobConfig, ISftpConfig, ILocalStorageConfig,
774
- IUploadOptionsDto, IUploadedFileInfo,
775
- } from '@flusys/ng-storage';
776
-
777
- // Services
778
- export {
779
- FileManagerApiService, FolderApiService,
780
- StorageConfigApiService, UploadService,
781
- } from '@flusys/ng-storage';
782
-
783
- // Routes & Components
784
- export { STORAGE_ROUTES } from '@flusys/ng-storage';
785
- export { StorageContainerComponent } from '@flusys/ng-storage';
786
-
787
- // Provider Functions
788
- export { provideStorageProviders } from '@flusys/ng-storage';
789
- ```
542
+ | Category | Exports |
543
+ |----------|---------|
544
+ | Enums | `FileLocationEnum`, `ImageFormat` |
545
+ | Interfaces | `IFileManager`, `ICreateFileManagerDto`, `IUpdateFileManagerDto`, `IGetFilesRequestDto`, `IFilesResponseDto`, `IFolder`, `ICreateFolderDto`, `IUpdateFolderDto`, `IStorageConfig`, `ICreateStorageConfigDto`, `IUpdateStorageConfigDto`, `IAwsS3Config`, `IAzureBlobConfig`, `ISftpConfig`, `ILocalStorageConfig`, `IUploadOptionsDto`, `IUploadedFileInfo` |
546
+ | Utils | `getProviderLabel`, `getProviderIcon`, `getProviderSeverity`, `getFileIcon`, `formatFileSize`, `STORAGE_PROVIDERS`, `BaseStoragePage`, `BaseStorageListPage` |
547
+ | Adapters | `ProfileUploadProviderAdapter` |
548
+ | Services | `FileManagerApiService`, `FolderApiService`, `StorageConfigApiService`, `UploadService` |
549
+ | Routes | `STORAGE_ROUTES`, `StorageContainerComponent` |
550
+ | Providers | `provideStorageProviders` |
790
551
 
791
552
  ---
792
553
 
793
- **Last Updated:** 2026-02-21
554
+ **Last Updated:** 2026-02-25
555
+ **Version:** 3.0.1
794
556
  **Angular Version:** 21