@flusys/ng-storage 4.0.1 → 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.
- package/README.md +305 -432
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -1,562 +1,435 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @flusys/ng-storage
|
|
2
2
|
|
|
3
|
-
>
|
|
4
|
-
> **Version:** 4.0.1
|
|
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
|
-
|
|
5
|
+
[](https://www.npmjs.com/package/@flusys/ng-storage)
|
|
6
|
+
[](https://angular.io)
|
|
7
|
+
[](https://www.typescriptlang.org)
|
|
8
|
+
[](LICENSE)
|
|
10
9
|
|
|
11
|
-
|
|
10
|
+
---
|
|
12
11
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
46
|
+
## Features
|
|
40
47
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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
|
-
##
|
|
85
|
-
|
|
86
|
-
### Adding Storage Routes
|
|
60
|
+
## Compatibility
|
|
87
61
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
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
|
-
|
|
68
|
+
---
|
|
102
69
|
|
|
103
|
-
|
|
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
|
-
|
|
72
|
+
```bash
|
|
73
|
+
npm install @flusys/ng-storage @flusys/ng-core @flusys/ng-shared
|
|
74
|
+
```
|
|
117
75
|
|
|
118
76
|
---
|
|
119
77
|
|
|
120
|
-
##
|
|
78
|
+
## Quick Start
|
|
121
79
|
|
|
122
|
-
###
|
|
123
|
-
|
|
124
|
-
Storage provider types (matches backend).
|
|
80
|
+
### 1. Enable Storage in Config
|
|
125
81
|
|
|
126
82
|
```typescript
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
}
|
|
83
|
+
// environments/environment.ts
|
|
84
|
+
export const environment = {
|
|
85
|
+
services: {
|
|
86
|
+
storage: { enabled: true },
|
|
87
|
+
},
|
|
88
|
+
};
|
|
133
89
|
```
|
|
134
90
|
|
|
135
|
-
###
|
|
136
|
-
|
|
137
|
-
Image compression output formats.
|
|
91
|
+
### 2. Register Storage Providers
|
|
138
92
|
|
|
139
93
|
```typescript
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
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
|
-
|
|
156
|
-
|
|
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
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
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
|
-
|
|
119
|
+
---
|
|
176
120
|
|
|
177
|
-
|
|
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
|
-
|
|
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
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
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
|
-
|
|
212
|
-
|
|
213
|
-
|
|
130
|
+
providers: [
|
|
131
|
+
...provideStorageProviders(),
|
|
132
|
+
]
|
|
214
133
|
```
|
|
215
134
|
|
|
216
|
-
|
|
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
|
-
|
|
219
|
-
interface IFolder extends IBaseEntity {
|
|
220
|
-
name: string;
|
|
221
|
-
slug: string; // URL-friendly name
|
|
222
|
-
companyId?: string | null;
|
|
223
|
-
}
|
|
140
|
+
---
|
|
224
141
|
|
|
225
|
-
|
|
226
|
-
interface IUpdateFolderDto extends Partial<ICreateFolderDto> { id: string; }
|
|
227
|
-
```
|
|
142
|
+
## File URL Rule
|
|
228
143
|
|
|
229
|
-
|
|
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
|
-
|
|
233
|
-
|
|
234
|
-
|
|
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
|
-
|
|
242
|
-
|
|
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
|
-
|
|
250
|
-
|
|
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
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
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
|
-
|
|
270
|
-
|
|
271
|
-
### UploadService
|
|
272
|
-
|
|
273
|
-
Handles file upload and deletion. Extends `BaseApiService` (from ng-core) with base path `/storage`.
|
|
168
|
+
### FileManagerApiService
|
|
274
169
|
|
|
275
|
-
|
|
170
|
+
Typed API client for file management operations:
|
|
276
171
|
|
|
277
|
-
|
|
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
|
-
|
|
175
|
+
@Injectable({ ... })
|
|
176
|
+
export class MyService {
|
|
177
|
+
private fileApi = inject(FileManagerApiService);
|
|
288
178
|
|
|
289
|
-
|
|
179
|
+
getFiles(folderId?: string): Observable<IListResponse<IFile>> {
|
|
180
|
+
return this.fileApi.getAll({ filters: { folderId } });
|
|
181
|
+
}
|
|
290
182
|
|
|
291
|
-
|
|
183
|
+
getFileUrls(fileIds: string[]): Observable<ISingleResponse<IFileWithUrl[]>> {
|
|
184
|
+
return this.fileApi.getFiles(fileIds);
|
|
185
|
+
}
|
|
292
186
|
|
|
293
|
-
|
|
187
|
+
deleteFile(id: string): Observable<IMessageResponse> {
|
|
188
|
+
return this.fileApi.delete(id);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
```
|
|
294
192
|
|
|
295
|
-
**
|
|
193
|
+
**FileManagerApiService Methods:**
|
|
296
194
|
|
|
297
195
|
| Method | Endpoint | Description |
|
|
298
196
|
|--------|----------|-------------|
|
|
299
|
-
| `
|
|
300
|
-
| `
|
|
301
|
-
| `
|
|
302
|
-
|
|
303
|
-
|
|
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
|
-
|
|
308
|
-
|
|
309
|
-
### StorageConfigApiService
|
|
206
|
+
Typed API client for folder management:
|
|
310
207
|
|
|
311
|
-
|
|
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
|
-
|
|
211
|
+
@Injectable({ ... })
|
|
212
|
+
export class MyService {
|
|
213
|
+
private folderApi = inject(FolderApiService);
|
|
362
214
|
|
|
363
|
-
|
|
215
|
+
getFolders(parentId?: string): Observable<IListResponse<IFolder>> {
|
|
216
|
+
return this.folderApi.getAll({ filters: { parentId } });
|
|
217
|
+
}
|
|
364
218
|
|
|
365
|
-
|
|
219
|
+
createFolder(name: string, parentId?: string): Observable<ISingleResponse<IFolder>> {
|
|
220
|
+
return this.folderApi.insert({ name, parentId });
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
```
|
|
366
224
|
|
|
367
|
-
|
|
225
|
+
**FolderApiService Methods:**
|
|
368
226
|
|
|
369
|
-
|
|
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
|
-
|
|
235
|
+
### UploadService
|
|
372
236
|
|
|
373
|
-
|
|
237
|
+
Handles multi-part file uploads with progress tracking:
|
|
374
238
|
|
|
375
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
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
|
-
|
|
282
|
+
Full-page file browser with folder tree, grid/list view, upload, and actions.
|
|
404
283
|
|
|
405
|
-
|
|
284
|
+
```html
|
|
285
|
+
<!-- Full file manager -->
|
|
286
|
+
<flusys-file-manager />
|
|
406
287
|
|
|
407
|
-
|
|
408
|
-
|
|
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
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
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
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
344
|
+
Lazy-loaded pages for the full file manager:
|
|
448
345
|
|
|
449
346
|
```typescript
|
|
450
|
-
|
|
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
|
-
|
|
|
466
|
-
|
|
467
|
-
|
|
|
468
|
-
|
|
|
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
|
-
##
|
|
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
|
-
|
|
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
|
-
|
|
490
|
-
|
|
364
|
+
enum FileLocationEnum {
|
|
365
|
+
LOCAL = 'LOCAL',
|
|
366
|
+
S3 = 'S3',
|
|
367
|
+
AZURE = 'AZURE',
|
|
368
|
+
SFTP = 'SFTP',
|
|
369
|
+
}
|
|
491
370
|
```
|
|
492
371
|
|
|
493
|
-
|
|
372
|
+
Use `FileLocationEnum` when filtering or categorizing files by their storage backend.
|
|
494
373
|
|
|
495
|
-
|
|
496
|
-
{ compress: true, maxWidth: 1920, maxHeight: 1080, quality: 85, format: ImageFormat.WEBP }
|
|
497
|
-
```
|
|
374
|
+
---
|
|
498
375
|
|
|
499
|
-
|
|
376
|
+
## API Endpoints
|
|
500
377
|
|
|
501
|
-
|
|
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
|
-
|
|
393
|
+
---
|
|
504
394
|
|
|
505
|
-
|
|
506
|
-
services: { storage: { baseUrl: 'http://localhost:2002/storage', enabled: true } }
|
|
507
|
-
```
|
|
395
|
+
## Configuration Reference
|
|
508
396
|
|
|
509
|
-
|
|
397
|
+
| Config Key | Type | Default | Description |
|
|
398
|
+
|------------|------|---------|-------------|
|
|
399
|
+
| `services.storage.enabled` | `boolean` | `false` | Enable storage module |
|
|
510
400
|
|
|
511
|
-
|
|
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
|
-
##
|
|
405
|
+
## Troubleshooting
|
|
522
406
|
|
|
523
|
-
|
|
407
|
+
**File thumbnails don't load**
|
|
524
408
|
|
|
525
|
-
|
|
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
|
-
|
|
528
|
-
|
|
529
|
-
|
|
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
|
-
|
|
419
|
+
**`No provider for FILE_PROVIDER`**
|
|
533
420
|
|
|
534
|
-
|
|
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
|
-
**
|
|
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
|
-
|
|
427
|
+
**Files visible but download fails**
|
|
547
428
|
|
|
548
|
-
|
|
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
|
-
|
|
561
|
-
|
|
562
|
-
|
|
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
|
|
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
|
|
11
|
-
"@flusys/ng-layout": ">=4.0
|
|
12
|
-
"@flusys/ng-shared": ">=4.0
|
|
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",
|