@flusys/ng-storage 4.0.2 → 4.1.0

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 +305 -432
  2. package/package.json +4 -4
package/README.md CHANGED
@@ -1,562 +1,435 @@
1
- # Storage Package Guide
1
+ # @flusys/ng-storage
2
2
 
3
- > **Package:** `@flusys/ng-storage`
4
- > **Version:** 4.0.2
5
- > **Type:** File storage management with upload, folder organization, and multi-provider support
3
+ > File storage UI library for the FLUSYS Angular platform — file upload, folder management, file browser, presigned URL fetching, and provider adapter registration.
6
4
 
7
- ---
8
-
9
- ## Overview
5
+ [![npm version](https://img.shields.io/npm/v/@flusys/ng-storage.svg)](https://www.npmjs.com/package/@flusys/ng-storage)
6
+ [![Angular](https://img.shields.io/badge/Angular-21-red.svg)](https://angular.io)
7
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.x-blue.svg)](https://www.typescriptlang.org)
8
+ [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)
10
9
 
11
- `@flusys/ng-storage` provides complete file storage management:
10
+ ---
12
11
 
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
- - **Permission Guards** - All routes protected by permission guards
19
- - **Lazy Loading** - All page components are lazy-loaded via routes
12
+ ## Table of Contents
13
+
14
+ - [Overview](#overview)
15
+ - [Features](#features)
16
+ - [Compatibility](#compatibility)
17
+ - [Installation](#installation)
18
+ - [Quick Start](#quick-start)
19
+ - [Module Registration](#module-registration)
20
+ - [File URL Rule](#file-url-rule)
21
+ - [Services](#services)
22
+ - [FileManagerApiService](#filemanagerapiservice)
23
+ - [FolderApiService](#folderapiservice)
24
+ - [UploadService](#uploadservice)
25
+ - [Components](#components)
26
+ - [FileManagerComponent](#filemanagercomponent)
27
+ - [FileSelectorDialogComponent](#fileselectordialogcomponent)
28
+ - [FileUploaderComponent](#fileuploadercomponent)
29
+ - [Storage Routes](#storage-routes)
30
+ - [File Location Enum](#file-location-enum)
31
+ - [API Endpoints](#api-endpoints)
32
+ - [Configuration Reference](#configuration-reference)
33
+ - [Troubleshooting](#troubleshooting)
34
+ - [License](#license)
20
35
 
21
- ### Package Hierarchy
36
+ ---
22
37
 
23
- ```
24
- @flusys/ng-core <- Foundation (BaseApiService, APP_CONFIG, getDefaultStorageConfigId)
25
- |
26
- @flusys/ng-shared <- Shared utilities (ApiResourceService, IBaseEntity, BaseListPage)
27
- |
28
- @flusys/ng-layout <- Layout (LAYOUT_AUTH_STATE for company context)
29
- |
30
- @flusys/ng-storage <- File storage (THIS PACKAGE)
31
- ```
38
+ ## Overview
32
39
 
33
- ### Company Context
40
+ `@flusys/ng-storage` provides the complete file management UI for FLUSYS applications. It supports all four storage backends (Local, S3, Azure Blob, SFTP) transparently — the backend generates the correct presigned URLs for each provider.
34
41
 
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.
42
+ **Critical rule:** Never construct file URLs manually. Always use `FileUrlService` (from `ng-shared`) or the `FILE_PROVIDER` token. The backend generates presigned URLs that are provider-agnostic and access-controlled.
36
43
 
37
44
  ---
38
45
 
39
- ## Package Architecture
46
+ ## Features
40
47
 
41
- ```
42
- ng-storage/
43
- ├── adapters/
44
- │ └── profile-upload-provider.adapter.ts # Profile upload adapter for ng-auth
45
- ├── enums/
46
- │ ├── file-location.enum.ts # FileLocationEnum, ImageFormat
47
- │ └── public-api.ts
48
- ├── interfaces/
49
- │ ├── file-manager.interface.ts # IFileManager, ICreateFileManagerDto, IUpdateFileManagerDto
50
- │ ├── folder.interface.ts # IFolder, ICreateFolderDto, IUpdateFolderDto
51
- │ ├── storage-config.interface.ts # IStorageConfig, provider-specific configs
52
- │ ├── upload.interface.ts # IUploadOptionsDto, IUploadedFileInfo
53
- │ └── public-api.ts
54
- ├── providers/
55
- │ └── storage-providers.ts # provideStorageProviders()
56
- ├── services/
57
- │ ├── file-manager-api.service.ts # File metadata CRUD + presigned URLs
58
- │ ├── folder-api.service.ts # Folder CRUD
59
- │ ├── storage-config-api.service.ts # Storage config CRUD
60
- │ └── upload.service.ts # File upload/delete operations
61
- ├── utils/
62
- │ ├── base-storage-page.ts # BaseStoragePage, BaseStorageListPage base classes
63
- │ ├── storage-utils.ts # Utility functions (icons, labels, formatters)
64
- │ └── public-api.ts
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
- ```
48
+ - ✅ File browser with folder navigation
49
+ - ✅ Drag-and-drop upload with progress
50
+ - ✅ Multi-file selection dialog (`FileSelectorDialogComponent`)
51
+ - ✅ Image preview thumbnails
52
+ - ✅ Download and delete actions
53
+ - ✅ `UploadService` — typed multi-part file upload
54
+ - ✅ `provideStorageProviders()` — registers `FILE_PROVIDER` for shared components
55
+ - ✅ Presigned URL fetching via `FileUrlService`
56
+ - ✅ Support for `LOCAL`, `S3`, `AZURE`, `SFTP` backends
81
57
 
82
58
  ---
83
59
 
84
- ## Route Integration
85
-
86
- ### Adding Storage Routes
60
+ ## Compatibility
87
61
 
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
62
+ | Package | Version |
63
+ |---------|---------|
64
+ | Angular | 21+ |
65
+ | @flusys/ng-core | 4.x |
66
+ | @flusys/ng-shared | 4.x |
100
67
 
101
- All routes are wrapped by `StorageContainerComponent` which provides tab navigation (Files, Folders, Configs). Routes are protected by permission guards.
68
+ ---
102
69
 
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` |
70
+ ## Installation
115
71
 
116
- Default redirect: `/storage` -> `/storage/files`
72
+ ```bash
73
+ npm install @flusys/ng-storage @flusys/ng-core @flusys/ng-shared
74
+ ```
117
75
 
118
76
  ---
119
77
 
120
- ## Enums
78
+ ## Quick Start
121
79
 
122
- ### FileLocationEnum
123
-
124
- Storage provider types (matches backend).
80
+ ### 1. Enable Storage in Config
125
81
 
126
82
  ```typescript
127
- enum FileLocationEnum {
128
- LOCAL = 'local',
129
- AWS = 'aws',
130
- AZURE = 'azure',
131
- SFTP = 'sftp',
132
- }
83
+ // environments/environment.ts
84
+ export const environment = {
85
+ services: {
86
+ storage: { enabled: true },
87
+ },
88
+ };
133
89
  ```
134
90
 
135
- ### ImageFormat
136
-
137
- Image compression output formats.
91
+ ### 2. Register Storage Providers
138
92
 
139
93
  ```typescript
140
- enum ImageFormat {
141
- ORIGINAL = 'original',
142
- JPEG = 'jpeg',
143
- PNG = 'png',
144
- WEBP = 'webp',
145
- }
94
+ // app.config.ts
95
+ import { provideStorageProviders } from '@flusys/ng-storage';
96
+
97
+ export const appConfig: ApplicationConfig = {
98
+ providers: [
99
+ ...provideStorageProviders(),
100
+ // Provides: FILE_PROVIDER (IFileProvider) — used by FileSelectorDialogComponent, FileUrlService
101
+ ],
102
+ };
146
103
  ```
147
104
 
148
- ---
149
-
150
- ## Interfaces
151
-
152
- ### Upload
105
+ ### 3. Add Storage Routes (Optional)
153
106
 
154
107
  ```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
- }
108
+ // app.routes.ts
109
+ import { STORAGE_ROUTES } from '@flusys/ng-storage';
164
110
 
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
- location?: string; // Storage location (optional)
171
- storageConfigId?: string; // Config used for upload (optional)
172
- }
111
+ export const routes: Routes = [
112
+ {
113
+ path: 'storage',
114
+ loadChildren: () => STORAGE_ROUTES,
115
+ },
116
+ ];
173
117
  ```
174
118
 
175
- ### File Manager
119
+ ---
176
120
 
177
- ```typescript
178
- interface IFileManager extends IBaseEntity {
179
- name: string;
180
- contentType: string;
181
- size: string; // In KB (string)
182
- key: string; // File key/path in storage
183
- url: string | null; // Presigned URL (regenerated by backend)
184
- location: FileLocationEnum;
185
- storageConfigId: string | null;
186
- providerName?: string; // Storage config name (enriched from storage_config table)
187
- expiresAt: Date | null; // URL expiration time
188
- isPrivate: boolean;
189
- companyId?: string; // Only in company mode
190
- folderId?: string | null;
191
- }
121
+ ## Module Registration
192
122
 
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
- }
123
+ ### `provideStorageProviders()`
203
124
 
204
- interface IUpdateFileManagerDto {
205
- id: string;
206
- name?: string;
207
- isPrivate?: boolean;
208
- folderId?: string | null;
209
- }
125
+ Registers the storage adapter against the `FILE_PROVIDER` token from `ng-shared`:
126
+
127
+ ```typescript
128
+ import { provideStorageProviders } from '@flusys/ng-storage';
210
129
 
211
- // For fetching files with valid presigned URLs
212
- interface IGetFilesRequestDto { id: string; }
213
- interface IFilesResponseDto { id: string; name: string; contentType: string; url: string; }
130
+ providers: [
131
+ ...provideStorageProviders(),
132
+ ]
214
133
  ```
215
134
 
216
- ### Folder
135
+ This enables:
136
+ - `FileSelectorDialogComponent` (from `ng-shared`) to list/browse files
137
+ - `FileUrlService.fetchSingleFileUrl()` to work correctly
138
+ - `HasPermissionDirective` to show storage-gated content
217
139
 
218
- ```typescript
219
- interface IFolder extends IBaseEntity {
220
- name: string;
221
- slug: string; // URL-friendly name
222
- companyId?: string | null;
223
- }
140
+ ---
224
141
 
225
- interface ICreateFolderDto { name: string; }
226
- interface IUpdateFolderDto extends Partial<ICreateFolderDto> { id: string; }
227
- ```
142
+ ## File URL Rule
228
143
 
229
- ### Storage Config
144
+ **CRITICAL:** Never construct file URLs manually. The backend handles URL generation for all storage providers (Local, S3, Azure, SFTP) including presigned URL rotation.
230
145
 
231
146
  ```typescript
232
- interface IStorageConfig extends IBaseEntity {
233
- name: string;
234
- storage: FileLocationEnum;
235
- config: Record<string, unknown>; // Provider-specific configuration
236
- isActive: boolean; // Whether config is active and usable
237
- isDefault: boolean; // Set as default configuration
238
- companyId?: string | null;
239
- }
147
+ // ❌ WRONG — breaks with S3, Azure, SFTP
148
+ const url = `${apiBaseUrl}/uploads/${fileId}`;
149
+ const url = `https://my-bucket.s3.amazonaws.com/${path}`;
240
150
 
241
- interface ICreateStorageConfigDto {
242
- name: string;
243
- storage: FileLocationEnum;
244
- config: Record<string, unknown>;
245
- isActive?: boolean;
246
- isDefault?: boolean;
247
- }
151
+ // ✅ CORRECT — always use FileUrlService
152
+ import { FileUrlService } from '@flusys/ng-shared';
248
153
 
249
- interface IUpdateStorageConfigDto extends Partial<ICreateStorageConfigDto> {
250
- id: string;
251
- }
154
+ const file = await this.fileUrlService.fetchSingleFileUrl(fileId);
155
+ const url = file?.url ?? null; // Presigned, access-controlled, provider-agnostic
252
156
  ```
253
157
 
254
- ### Provider-Specific Configs
255
-
256
- These define the shape of `IStorageConfig.config` for each provider:
257
-
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` | - |
158
+ **Why backend URLs:**
159
+ - S3/Azure require presigned URLs that expire
160
+ - URLs are access-controlled (user must have permission)
161
+ - Works transparently across all 4 storage providers
162
+ - Automatic URL refresh on expiry
264
163
 
265
164
  ---
266
165
 
267
166
  ## Services
268
167
 
269
- All services are `providedIn: 'root'`. Company scoping is automatic when the company feature is enabled.
270
-
271
- ### UploadService
272
-
273
- Handles file upload and deletion. Extends `BaseApiService` (from ng-core) with base path `/storage`.
168
+ ### FileManagerApiService
274
169
 
275
- **Methods:**
170
+ Typed API client for file management operations:
276
171
 
277
- | Method | Endpoint | Description |
278
- |--------|----------|-------------|
279
- | `uploadSingleFile(file, options?)` | `POST /storage/upload/single-file` | Upload single file |
280
- | `uploadMultipleFiles(files, options?)` | `POST /storage/upload/multiple-file` | Upload multiple files (max 50) |
281
- | `uploadAndRegister(file, options?)` | Combined | Upload file and create file manager record |
282
- | `deleteSingleFile(key, storageConfigId?)` | `POST /storage/upload/delete-single-file` | Delete single file from storage |
283
- | `deleteMultipleFiles(keys, storageConfigId?)` | `POST /storage/upload/delete-multiple-file` | Delete multiple files |
284
- | `getFileUrl(key)` | `GET /storage/upload/file/:key` | Get direct file download URL |
285
- | `getDefaultConfigId()` | - | Get default storage config ID from APP_CONFIG |
172
+ ```typescript
173
+ import { FileManagerApiService } from '@flusys/ng-storage';
286
174
 
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.
175
+ @Injectable({ ... })
176
+ export class MyService {
177
+ private fileApi = inject(FileManagerApiService);
288
178
 
289
- ### FileManagerApiService
179
+ getFiles(folderId?: string): Observable<IListResponse<IFile>> {
180
+ return this.fileApi.getAll({ filters: { folderId } });
181
+ }
290
182
 
291
- File metadata CRUD. Extends `ApiResourceService<IUpdateFileManagerDto, IFileManager>` with endpoint `/storage/file-manager`.
183
+ getFileUrls(fileIds: string[]): Observable<ISingleResponse<IFileWithUrl[]>> {
184
+ return this.fileApi.getFiles(fileIds);
185
+ }
292
186
 
293
- **Inherited CRUD:** `insert`, `update`, `findById`, `getAll`, `delete` (and `*Async` variants)
187
+ deleteFile(id: string): Observable<IMessageResponse> {
188
+ return this.fileApi.delete(id);
189
+ }
190
+ }
191
+ ```
294
192
 
295
- **Additional methods:**
193
+ **FileManagerApiService Methods:**
296
194
 
297
195
  | Method | Endpoint | Description |
298
196
  |--------|----------|-------------|
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) |
302
-
303
- **Presigned URL pattern:** Always use `getFiles()` to get valid URLs. Backend regenerates expired presigned URLs for cloud storage. See Usage Examples.
197
+ | `getAll(params)` | `POST /file-manager/get-all` | List files with pagination |
198
+ | `getById(id)` | `POST /file-manager/get/:id` | Get file metadata |
199
+ | `getFiles(ids[])` | `POST /file-manager/get-files` | Batch fetch with presigned URLs |
200
+ | `delete(id)` | `POST /file-manager/delete` | Delete file |
201
+ | `bulkDelete(ids[])` | `POST /file-manager/bulk-delete` | Delete multiple files |
202
+ | `move(id, folderId)` | `POST /file-manager/move` | Move file to folder |
304
203
 
305
204
  ### FolderApiService
306
205
 
307
- Folder CRUD. Extends `ApiResourceService<ICreateFolderDto | IUpdateFolderDto, IFolder>` with endpoint `/storage/folder`. Standard CRUD operations only.
308
-
309
- ### StorageConfigApiService
206
+ Typed API client for folder management:
310
207
 
311
- Storage config CRUD. Extends `ApiResourceService<ICreateStorageConfigDto | IUpdateStorageConfigDto, IStorageConfig>` with endpoint `/storage/storage-config`. Standard CRUD operations only.
312
-
313
- ---
314
-
315
- ## Base Classes
316
-
317
- Components extend shared base classes from `utils/base-storage-page.ts`:
318
-
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()` |
323
-
324
- Both inject: `Router`, `MessageService`, `APP_CONFIG`, `LAYOUT_AUTH_STATE`
325
-
326
- **BaseStorageListPage inherits from BaseListPage:** `items`, `total`, `isLoading`, `currentPage`, `onPageChange()`, `getPaginationParams()`, `onCreate()`, `onEdit()`, `onDeleteAsync()`, `loadData()` (abstract), `getResourceRoute()` (abstract)
327
-
328
- ---
329
-
330
- ## Components
331
-
332
- All components are standalone, use Angular 21 signal forms, and lazy-load via routes.
333
-
334
- ### StorageContainerComponent
335
-
336
- Main container with tab navigation between Files, Folders, and Configs sections.
337
-
338
- **Selector:** `lib-storage-container`
339
-
340
- **Features:**
341
- - Tab navigation filtered by user permissions (via `PermissionValidatorService`)
342
- - Horizontal scroll without visible scrollbar (`scrollbar-hide`)
343
- - Dynamic subtitle based on visible tabs
344
- - Responsive padding: `p-2 sm:p-4`
345
- - Dark mode compatible (Tailwind theme classes)
346
-
347
- ### List Components
348
-
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:**
356
- - File content type icons (image, video, audio, PDF, Word, Excel, generic)
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
208
+ ```typescript
209
+ import { FolderApiService } from '@flusys/ng-storage';
360
210
 
361
- #### FolderListComponent
211
+ @Injectable({ ... })
212
+ export class MyService {
213
+ private folderApi = inject(FolderApiService);
362
214
 
363
- **Selector:** `lib-folder-list`
215
+ getFolders(parentId?: string): Observable<IListResponse<IFolder>> {
216
+ return this.folderApi.getAll({ filters: { parentId } });
217
+ }
364
218
 
365
- Simple CRUD list with folder icon, name, slug, and created date columns.
219
+ createFolder(name: string, parentId?: string): Observable<ISingleResponse<IFolder>> {
220
+ return this.folderApi.insert({ name, parentId });
221
+ }
222
+ }
223
+ ```
366
224
 
367
- #### StorageConfigListComponent
225
+ **FolderApiService Methods:**
368
226
 
369
- **Selector:** `lib-storage-config-list`
227
+ | Method | Endpoint | Description |
228
+ |--------|----------|-------------|
229
+ | `getAll(params)` | `POST /folder/get-all` | List folders |
230
+ | `getById(id)` | `POST /folder/get/:id` | Get folder |
231
+ | `insert(dto)` | `POST /folder/insert` | Create folder |
232
+ | `update(dto)` | `POST /folder/update` | Rename folder |
233
+ | `delete(id)` | `POST /folder/delete` | Delete folder |
370
234
 
371
- Lists storage configurations with provider icons and tags.
235
+ ### UploadService
372
236
 
373
- ### Form Components
237
+ Handles multi-part file uploads with progress tracking:
374
238
 
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`).
239
+ ```typescript
240
+ import { UploadService } from '@flusys/ng-storage';
241
+
242
+ @Component({ ... })
243
+ export class UploadComponent {
244
+ private uploadService = inject(UploadService);
245
+
246
+ uploadFile(file: File, folderId?: string): void {
247
+ this.uploadService.upload(file, { folderId }).subscribe({
248
+ next: (event) => {
249
+ if (event.type === 'progress') {
250
+ console.log(`Progress: ${event.percent}%`);
251
+ }
252
+ if (event.type === 'complete') {
253
+ console.log('Uploaded:', event.file);
254
+ }
255
+ },
256
+ error: (err) => console.error('Upload failed:', err),
257
+ });
258
+ }
259
+
260
+ uploadMultiple(files: File[]): void {
261
+ this.uploadService.uploadBatch(files).subscribe(results => {
262
+ console.log(`Uploaded ${results.length} files`);
263
+ });
264
+ }
265
+ }
266
+ ```
376
267
 
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'` |
268
+ **UploadService API:**
382
269
 
383
- **FileManagerForm Upload Flow:** 1) Upload file via `uploadSingleFile()` 2) Create metadata via `createFileAsync()`. Edit mode only calls `updateAsync()`.
270
+ | Method | Description |
271
+ |--------|-------------|
272
+ | `upload(file, options?)` | Single file upload with progress events |
273
+ | `uploadBatch(files, options?)` | Multiple file upload (sequential) |
274
+ | `cancel(uploadId)` | Cancel in-progress upload |
384
275
 
385
276
  ---
386
277
 
387
- ## Utility Functions
388
-
389
- The `utils/storage-utils.ts` file provides helper functions:
390
-
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.
278
+ ## Components
400
279
 
401
- ---
280
+ ### FileManagerComponent
402
281
 
403
- ## Usage Examples
282
+ Full-page file browser with folder tree, grid/list view, upload, and actions.
404
283
 
405
- ### Upload a File
284
+ ```html
285
+ <!-- Full file manager -->
286
+ <flusys-file-manager />
406
287
 
407
- ```typescript
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;
288
+ <!-- With pre-selected folder -->
289
+ <flusys-file-manager [initialFolderId]="rootFolderId" />
415
290
  ```
416
291
 
417
- ### Upload and Register (Combined)
418
-
419
- Use `uploadAndRegister` for a single call that uploads and creates the file manager record:
420
-
421
- ```typescript
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;
292
+ Features:
293
+ - Folder tree (left panel)
294
+ - File grid with thumbnails (right panel)
295
+ - Drag-and-drop upload zone
296
+ - Right-click context menu (rename, move, delete)
297
+ - Breadcrumb navigation
298
+ - Search files
299
+
300
+ ### FileSelectorDialogComponent
301
+
302
+ Dialog for selecting one or multiple files. Used by other packages (`ng-form-builder`, `ng-email`) to pick files.
303
+
304
+ ```html
305
+ <flusys-file-selector-dialog
306
+ [visible]="showPicker"
307
+ [multiple]="true"
308
+ [accept]="'image/*'"
309
+ (fileSelected)="onFilesSelected($event)"
310
+ (cancel)="showPicker = false"
311
+ />
426
312
  ```
427
313
 
428
- ### Get Files with Presigned URLs
429
-
430
- ```typescript
431
- const response = await firstValueFrom(this.fileService.getFiles(['file-id-1', 'file-id-2']));
432
- // response.data → [{ id, name, contentType, url }]
314
+ | Input | Type | Description |
315
+ |-------|------|-------------|
316
+ | `visible` | `boolean` | Show/hide dialog |
317
+ | `multiple` | `boolean` | Allow multi-select (default: false) |
318
+ | `accept` | `string` | MIME type filter |
319
+ | `folderId` | `string` | Pre-open specific folder |
320
+
321
+ | Output | Type | Description |
322
+ |--------|------|-------------|
323
+ | `fileSelected` | `IFile \| IFile[]` | Selected file(s) |
324
+ | `cancel` | `void` | Dialog dismissed |
325
+
326
+ ### FileUploaderComponent
327
+
328
+ Lightweight standalone upload component (without the full browser):
329
+
330
+ ```html
331
+ <flusys-file-uploader
332
+ [multiple]="false"
333
+ [accept]="'image/jpeg,image/png'"
334
+ [maxSizeMb]="5"
335
+ [folderId]="profilePicturesFolderId"
336
+ (uploaded)="onAvatarUploaded($event)"
337
+ />
433
338
  ```
434
339
 
435
- ### Delete Files
436
-
437
- | Mode | Type | Description |
438
- |------|------|-------------|
439
- | Soft delete | `type: 'delete'` | Marks as deleted (recoverable) |
440
- | Permanent delete | `type: 'permanent'` | Removes from storage provider (irreversible) |
340
+ ---
441
341
 
442
- ```typescript
443
- await this.fileService.deleteAsync({ id: file.id, type: 'delete' }); // Soft
444
- await this.fileService.deleteAsync({ id: file.id, type: 'permanent' }); // Permanent
445
- ```
342
+ ## Storage Routes
446
343
 
447
- ### Create Storage Configuration
344
+ Lazy-loaded pages for the full file manager:
448
345
 
449
346
  ```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
- });
457
- ```
347
+ import { STORAGE_ROUTES } from '@flusys/ng-storage';
458
348
 
459
- ---
460
-
461
- ## Backend Integration
462
-
463
- All endpoints use **POST-only RPC** style (not REST). Base paths:
349
+ { path: 'storage', loadChildren: () => STORAGE_ROUTES }
350
+ ```
464
351
 
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` |
352
+ | Route | Component | Description |
353
+ |-------|-----------|-------------|
354
+ | `/storage` | `FileManagerPageComponent` | Full file manager page |
355
+ | `/storage/folder/:id` | `FileManagerPageComponent` | Open specific folder |
471
356
 
472
357
  ---
473
358
 
474
- ## Best Practices
475
-
476
- ### 1. Always Use Presigned URLs
477
-
478
- Never construct file URLs manually. Use `FileManagerApiService.getFiles()` or `FileUrlService` from ng-shared.
359
+ ## File Location Enum
479
360
 
480
361
  ```typescript
481
- // Wrong: const url = `${apiBaseUrl}/storage/${fileId}`;
482
- // Correct:
483
- const files = await firstValueFrom(this.fileService.getFiles([fileId]));
484
- const url = files.data?.[0]?.url ?? null;
485
- ```
486
-
487
- ### 2. Specify Storage Config for Multi-Provider Setups
362
+ import { FileLocationEnum } from '@flusys/ng-storage';
488
363
 
489
- ```typescript
490
- this.uploadService.uploadSingleFile(file, { storageConfigId: 'aws-media-bucket', folderPath: 'images' });
364
+ enum FileLocationEnum {
365
+ LOCAL = 'LOCAL',
366
+ S3 = 'S3',
367
+ AZURE = 'AZURE',
368
+ SFTP = 'SFTP',
369
+ }
491
370
  ```
492
371
 
493
- ### 3. Compress Images for Web
372
+ Use `FileLocationEnum` when filtering or categorizing files by their storage backend.
494
373
 
495
- ```typescript
496
- { compress: true, maxWidth: 1920, maxHeight: 1080, quality: 85, format: ImageFormat.WEBP }
497
- ```
374
+ ---
498
375
 
499
- ### 4. Provide Required Services at App Level
376
+ ## API Endpoints
500
377
 
501
- Provide `ConfirmationService` and `MessageService` in `app.config.ts` for dialogs and toasts.
378
+ | Method | Endpoint | Description |
379
+ |--------|----------|-------------|
380
+ | POST | `/file-manager/get-all` | List files with pagination |
381
+ | POST | `/file-manager/get/:id` | Get file metadata |
382
+ | POST | `/file-manager/get-files` | Batch fetch with presigned URLs |
383
+ | POST | `/file-manager/upload` | Upload file (multipart) |
384
+ | POST | `/file-manager/delete` | Delete file |
385
+ | POST | `/file-manager/bulk-delete` | Delete multiple files |
386
+ | POST | `/file-manager/move` | Move file to folder |
387
+ | POST | `/folder/get-all` | List folders |
388
+ | POST | `/folder/get/:id` | Get folder metadata |
389
+ | POST | `/folder/insert` | Create folder |
390
+ | POST | `/folder/update` | Rename folder |
391
+ | POST | `/folder/delete` | Delete folder |
502
392
 
503
- ### 5. Enable Storage in APP_CONFIG
393
+ ---
504
394
 
505
- ```typescript
506
- services: { storage: { baseUrl: 'http://localhost:2002/storage', enabled: true } }
507
- ```
395
+ ## Configuration Reference
508
396
 
509
- ### 6. Responsive Design Patterns
397
+ | Config Key | Type | Default | Description |
398
+ |------------|------|---------|-------------|
399
+ | `services.storage.enabled` | `boolean` | `false` | Enable storage module |
510
400
 
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` |
401
+ The storage backend (LOCAL/S3/AZURE/SFTP) is configured server-side in `nestjs-storage`. The Angular client doesn't need to know which provider is active.
518
402
 
519
403
  ---
520
404
 
521
- ## Provider Functions
405
+ ## Troubleshooting
522
406
 
523
- ### provideStorageProviders()
407
+ **File thumbnails don't load**
524
408
 
525
- Registers provider adapters that enable other packages (ng-auth) to access storage functionality without direct dependencies.
409
+ Images are loaded via presigned URLs. Ensure `FileUrlService.fetchSingleFileUrl()` is used (not manual URL construction). Check the browser network tab — are the URLs returning 403?
526
410
 
527
- ```typescript
528
- // app.config.ts - add to providers array
529
- ...provideStorageProviders()
411
+ **Upload fails with 413 Payload Too Large**
412
+
413
+ The backend has a file size limit. Configure `maxBodySize` in `nestjs-storage` on the server. On the Angular side, validate client-side before upload:
414
+
415
+ ```html
416
+ <flusys-file-uploader [maxSizeMb]="10" />
530
417
  ```
531
418
 
532
- **Registers:** `FILE_PROVIDER` → `FileProviderAdapter` (implements `IFileProvider` from ng-shared)
419
+ **`No provider for FILE_PROVIDER`**
533
420
 
534
- **FileProviderAdapter features:**
535
- - `loadFiles(filter)` - Load files with filtering and pagination
536
- - `uploadFile(file, options)` - Upload a single file and register in file manager
537
- - `uploadMultipleFiles(files, options)` - Upload multiple files at once
538
- - `getFileUrls(fileIds)` - Get file URLs by IDs (for refreshing presigned URLs)
539
- - `loadFolders(filter)` - Load folders for folder selector
540
- - `loadStorageConfigs(filter)` - Load storage configs for config selector
421
+ Add `provideStorageProviders()` to `app.config.ts` providers.
541
422
 
542
- **Usage:** `inject(FILE_PROVIDER, { optional: true })?.uploadFile(file, options)`
423
+ **FileSelectorDialog is empty (no files)**
543
424
 
544
- ---
425
+ Ensure the user has permission to list files (`file:read`) and the backend storage is configured. Check the network tab for errors on `POST /file-manager/get-all`.
545
426
 
546
- ## Public API Exports
427
+ **Files visible but download fails**
547
428
 
548
- | Category | Exports |
549
- |----------|---------|
550
- | Enums | `FileLocationEnum`, `ImageFormat` |
551
- | Interfaces | `IFileManager`, `ICreateFileManagerDto`, `IUpdateFileManagerDto`, `IGetFilesRequestDto`, `IFilesResponseDto`, `IFolder`, `ICreateFolderDto`, `IUpdateFolderDto`, `IStorageConfig`, `ICreateStorageConfigDto`, `IUpdateStorageConfigDto`, `IAwsS3Config`, `IAzureBlobConfig`, `ISftpConfig`, `ILocalStorageConfig`, `IUploadOptionsDto`, `IUploadedFileInfo` |
552
- | Utils | `getProviderLabel`, `getProviderIcon`, `getProviderSeverity`, `getFileIcon`, `formatFileSize`, `STORAGE_PROVIDERS`, `BaseStoragePage`, `BaseStorageListPage` |
553
- | Adapters | `FileProviderAdapter` |
554
- | Services | `FileManagerApiService`, `FolderApiService`, `StorageConfigApiService`, `UploadService` |
555
- | Routes | `STORAGE_ROUTES`, `StorageContainerComponent` |
556
- | Providers | `provideStorageProviders` |
429
+ The presigned URL may have expired. Call `FileUrlService.clearCache(fileId)` to force a fresh URL fetch.
557
430
 
558
431
  ---
559
432
 
560
- **Last Updated:** 2026-03-01
561
- **Version:** 3.0.1
562
- **Angular Version:** 21
433
+ ## License
434
+
435
+ MIT © FLUSYS
package/package.json CHANGED
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "@flusys/ng-storage",
3
- "version": "4.0.2",
3
+ "version": "4.1.0",
4
4
  "description": "File storage module for FLUSYS Angular applications",
5
5
  "license": "MIT",
6
6
  "peerDependencies": {
7
7
  "@angular/common": ">=21.0.0",
8
8
  "@angular/core": ">=21.0.0",
9
9
  "@angular/router": ">=21.0.0",
10
- "@flusys/ng-core": ">=4.0.2",
11
- "@flusys/ng-layout": ">=4.0.2",
12
- "@flusys/ng-shared": ">=4.0.2",
10
+ "@flusys/ng-core": ">=4.1.0",
11
+ "@flusys/ng-layout": ">=4.1.0",
12
+ "@flusys/ng-shared": ">=4.1.0",
13
13
  "@primeuix/themes": ">=1.0.0",
14
14
  "primeicons": ">=7.0.0",
15
15
  "primeng": ">=21.0.0",