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