@flusys/ng-storage 1.0.0-beta → 1.0.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 +209 -5
- package/fesm2022/flusys-ng-storage-file-manager-form.component-Bv8ER8J0.mjs +484 -0
- package/fesm2022/flusys-ng-storage-file-manager-form.component-Bv8ER8J0.mjs.map +1 -0
- package/fesm2022/flusys-ng-storage-file-manager-list.component-McM1zSlE.mjs +304 -0
- package/fesm2022/flusys-ng-storage-file-manager-list.component-McM1zSlE.mjs.map +1 -0
- package/fesm2022/flusys-ng-storage-folder-form.component-fRFiMFev.mjs +155 -0
- package/fesm2022/flusys-ng-storage-folder-form.component-fRFiMFev.mjs.map +1 -0
- package/fesm2022/flusys-ng-storage-folder-list.component-C4xHI8SO.mjs +194 -0
- package/fesm2022/flusys-ng-storage-folder-list.component-C4xHI8SO.mjs.map +1 -0
- package/fesm2022/{flusys-ng-storage-storage-config-form.component-Bc95TVD0.mjs → flusys-ng-storage-storage-config-form.component-DEMexjmg.mjs} +242 -292
- package/fesm2022/flusys-ng-storage-storage-config-form.component-DEMexjmg.mjs.map +1 -0
- package/fesm2022/flusys-ng-storage-storage-config-list.component-iJez-rII.mjs +187 -0
- package/fesm2022/flusys-ng-storage-storage-config-list.component-iJez-rII.mjs.map +1 -0
- package/fesm2022/flusys-ng-storage.mjs +363 -199
- package/fesm2022/flusys-ng-storage.mjs.map +1 -1
- package/package.json +9 -9
- package/types/flusys-ng-storage.d.ts +150 -127
- package/fesm2022/flusys-ng-storage-file-manager-form.component-DVZsFYrj.mjs +0 -715
- package/fesm2022/flusys-ng-storage-file-manager-form.component-DVZsFYrj.mjs.map +0 -1
- package/fesm2022/flusys-ng-storage-file-manager-list.component-CwnyNoYb.mjs +0 -475
- package/fesm2022/flusys-ng-storage-file-manager-list.component-CwnyNoYb.mjs.map +0 -1
- package/fesm2022/flusys-ng-storage-folder-form.component-DH-KCz21.mjs +0 -253
- package/fesm2022/flusys-ng-storage-folder-form.component-DH-KCz21.mjs.map +0 -1
- package/fesm2022/flusys-ng-storage-folder-list.component-iIys4NvL.mjs +0 -267
- package/fesm2022/flusys-ng-storage-folder-list.component-iIys4NvL.mjs.map +0 -1
- package/fesm2022/flusys-ng-storage-storage-config-form.component-Bc95TVD0.mjs.map +0 -1
- package/fesm2022/flusys-ng-storage-storage-config-list.component-Bfomqyr3.mjs +0 -328
- package/fesm2022/flusys-ng-storage-storage-config-list.component-Bfomqyr3.mjs.map +0 -1
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { inject, Injectable, Component } from '@angular/core';
|
|
2
|
+
import { inject, signal, computed, Directive, Injectable, ChangeDetectionStrategy, Component } from '@angular/core';
|
|
3
|
+
import { Router, RouterOutlet, RouterLink, RouterLinkActive } from '@angular/router';
|
|
4
|
+
import { APP_CONFIG, DEFAULT_APP_NAME, BaseApiService, getDefaultStorageConfigId } from '@flusys/ng-core';
|
|
5
|
+
import { LAYOUT_AUTH_STATE } from '@flusys/ng-layout';
|
|
6
|
+
import { BaseListPage, ApiResourceService, PROFILE_UPLOAD_PROVIDER, permissionGuard, FILE_PERMISSIONS, FOLDER_PERMISSIONS, STORAGE_CONFIG_PERMISSIONS, PermissionValidatorService } from '@flusys/ng-shared';
|
|
7
|
+
import { MessageService } from 'primeng/api';
|
|
8
|
+
import { map } from 'rxjs/operators';
|
|
9
|
+
import { firstValueFrom, switchMap } from 'rxjs';
|
|
3
10
|
import { HttpClient } from '@angular/common/http';
|
|
4
|
-
import { ApiResourceService } from '@flusys/ng-shared';
|
|
5
|
-
import { BaseApiService } from '@flusys/ng-core';
|
|
6
11
|
import { CommonModule } from '@angular/common';
|
|
7
|
-
import { RouterOutlet, RouterLink, RouterLinkActive } from '@angular/router';
|
|
8
12
|
|
|
9
13
|
/**
|
|
10
14
|
* File Location Enum
|
|
@@ -30,62 +34,317 @@ var ImageFormat;
|
|
|
30
34
|
})(ImageFormat || (ImageFormat = {}));
|
|
31
35
|
|
|
32
36
|
/**
|
|
33
|
-
*
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
37
|
+
* Provider configurations for all storage types
|
|
38
|
+
*/
|
|
39
|
+
const PROVIDER_CONFIGS = {
|
|
40
|
+
[FileLocationEnum.LOCAL]: {
|
|
41
|
+
label: 'Local',
|
|
42
|
+
icon: 'pi pi-folder',
|
|
43
|
+
severity: 'secondary',
|
|
44
|
+
},
|
|
45
|
+
[FileLocationEnum.AWS]: {
|
|
46
|
+
label: 'AWS S3',
|
|
47
|
+
icon: 'pi pi-cloud',
|
|
48
|
+
severity: 'warn',
|
|
49
|
+
},
|
|
50
|
+
[FileLocationEnum.AZURE]: {
|
|
51
|
+
label: 'Azure Blob',
|
|
52
|
+
icon: 'pi pi-cloud',
|
|
53
|
+
severity: 'info',
|
|
54
|
+
},
|
|
55
|
+
[FileLocationEnum.SFTP]: {
|
|
56
|
+
label: 'SFTP',
|
|
57
|
+
icon: 'pi pi-server',
|
|
58
|
+
severity: 'contrast',
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* Get provider label for display
|
|
63
|
+
*/
|
|
64
|
+
function getProviderLabel(storage) {
|
|
65
|
+
const enumValue = storage;
|
|
66
|
+
return PROVIDER_CONFIGS[enumValue]?.label ?? storage;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Get provider icon class
|
|
70
|
+
*/
|
|
71
|
+
function getProviderIcon(storage) {
|
|
72
|
+
const enumValue = storage;
|
|
73
|
+
return PROVIDER_CONFIGS[enumValue]?.icon ?? 'pi pi-database';
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Get provider severity for p-tag
|
|
77
|
+
*/
|
|
78
|
+
function getProviderSeverity(storage) {
|
|
79
|
+
const enumValue = storage;
|
|
80
|
+
return PROVIDER_CONFIGS[enumValue]?.severity ?? 'secondary';
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Get file icon based on content type
|
|
84
|
+
*/
|
|
85
|
+
function getFileIcon(contentType) {
|
|
86
|
+
if (contentType.startsWith('image/'))
|
|
87
|
+
return 'pi pi-image';
|
|
88
|
+
if (contentType.startsWith('video/'))
|
|
89
|
+
return 'pi pi-video';
|
|
90
|
+
if (contentType.startsWith('audio/'))
|
|
91
|
+
return 'pi pi-volume-up';
|
|
92
|
+
if (contentType.includes('pdf'))
|
|
93
|
+
return 'pi pi-file-pdf';
|
|
94
|
+
if (contentType.includes('word') || contentType.includes('document'))
|
|
95
|
+
return 'pi pi-file-word';
|
|
96
|
+
if (contentType.includes('excel') || contentType.includes('spreadsheet'))
|
|
97
|
+
return 'pi pi-file-excel';
|
|
98
|
+
return 'pi pi-file';
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Format file size from bytes to human readable string
|
|
102
|
+
*/
|
|
103
|
+
function formatFileSize(bytes) {
|
|
104
|
+
if (bytes <= 0)
|
|
105
|
+
return '0 Bytes';
|
|
106
|
+
const k = 1024;
|
|
107
|
+
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
|
|
108
|
+
const i = Math.min(Math.floor(Math.log(bytes) / Math.log(k)), sizes.length - 1);
|
|
109
|
+
return Math.round((bytes / Math.pow(k, i)) * 100) / 100 + ' ' + sizes[i];
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Storage provider options for dropdown selects
|
|
38
113
|
*/
|
|
114
|
+
const STORAGE_PROVIDERS = [
|
|
115
|
+
{ label: 'AWS S3', value: FileLocationEnum.AWS },
|
|
116
|
+
{ label: 'Azure Blob Storage', value: FileLocationEnum.AZURE },
|
|
117
|
+
{ label: 'SFTP', value: FileLocationEnum.SFTP },
|
|
118
|
+
{ label: 'Local Storage', value: FileLocationEnum.LOCAL },
|
|
119
|
+
];
|
|
120
|
+
|
|
121
|
+
class BaseStoragePage {
|
|
122
|
+
router = inject(Router);
|
|
123
|
+
messageService = inject(MessageService);
|
|
124
|
+
appConfig = inject(APP_CONFIG);
|
|
125
|
+
companyContext = inject(LAYOUT_AUTH_STATE);
|
|
126
|
+
isLoading = signal(false, ...(ngDevMode ? [{ debugName: "isLoading" }] : []));
|
|
127
|
+
showCompanyInfo = computed(() => this.appConfig.enableCompanyFeature, ...(ngDevMode ? [{ debugName: "showCompanyInfo" }] : []));
|
|
128
|
+
currentCompanyName = computed(() => this.companyContext.currentCompanyInfo()?.name ?? DEFAULT_APP_NAME, ...(ngDevMode ? [{ debugName: "currentCompanyName" }] : []));
|
|
129
|
+
showSuccess(detail, summary = 'Success') {
|
|
130
|
+
this.messageService.add({ severity: 'success', summary, detail });
|
|
131
|
+
}
|
|
132
|
+
showError(detail, summary = 'Error') {
|
|
133
|
+
this.messageService.add({ severity: 'error', summary, detail });
|
|
134
|
+
}
|
|
135
|
+
navigateTo(path) {
|
|
136
|
+
this.router.navigate(path);
|
|
137
|
+
}
|
|
138
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: BaseStoragePage, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
139
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.3", type: BaseStoragePage, isStandalone: true, ngImport: i0 });
|
|
140
|
+
}
|
|
141
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: BaseStoragePage, decorators: [{
|
|
142
|
+
type: Directive
|
|
143
|
+
}] });
|
|
144
|
+
class BaseStorageListPage extends BaseListPage {
|
|
145
|
+
companyContext = inject(LAYOUT_AUTH_STATE);
|
|
146
|
+
pageSize = signal(50, ...(ngDevMode ? [{ debugName: "pageSize" }] : []));
|
|
147
|
+
totalRecords = this.total;
|
|
148
|
+
currentCompanyName = computed(() => this.companyContext.currentCompanyInfo()?.name ?? DEFAULT_APP_NAME, ...(ngDevMode ? [{ debugName: "currentCompanyName" }] : []));
|
|
149
|
+
getDeleteConfirmMessage(item) {
|
|
150
|
+
const name = item.name ?? 'this item';
|
|
151
|
+
return `Are you sure you want to delete "${name}"?`;
|
|
152
|
+
}
|
|
153
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: BaseStorageListPage, deps: null, target: i0.ɵɵFactoryTarget.Directive });
|
|
154
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.3", type: BaseStorageListPage, isStandalone: true, usesInheritance: true, ngImport: i0 });
|
|
155
|
+
}
|
|
156
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: BaseStorageListPage, decorators: [{
|
|
157
|
+
type: Directive
|
|
158
|
+
}] });
|
|
159
|
+
|
|
39
160
|
class FileManagerApiService extends ApiResourceService {
|
|
40
161
|
constructor() {
|
|
41
|
-
|
|
42
|
-
super('storage/file-manager', http);
|
|
162
|
+
super('storage/file-manager', inject(HttpClient));
|
|
43
163
|
}
|
|
44
|
-
/**
|
|
45
|
-
* Get multiple files with valid URLs
|
|
46
|
-
* POST /storage/file-manager/get-files
|
|
47
|
-
* Regenerates presigned URLs if expired
|
|
48
|
-
*/
|
|
49
164
|
getFiles(fileIds) {
|
|
50
165
|
const requests = fileIds.map((id) => ({ id }));
|
|
51
166
|
return this.http.post(`${this.baseUrl}/get-files`, requests);
|
|
52
167
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
* POST /storage/file-manager/get-all
|
|
56
|
-
* Filtered by folder ID and company (if enabled)
|
|
57
|
-
*/
|
|
58
|
-
getFilesByFolder(folderId, page = 1, limit = 50) {
|
|
59
|
-
return this.getAll('', {
|
|
60
|
-
filter: folderId ? { folderId } : {},
|
|
61
|
-
pagination: { currentPage: page - 1, pageSize: limit },
|
|
62
|
-
select: [],
|
|
63
|
-
sort: {},
|
|
64
|
-
});
|
|
168
|
+
createFile(dto) {
|
|
169
|
+
return this.http.post(`${this.baseUrl}/insert`, dto);
|
|
65
170
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
* POST /storage/file-manager/get-all
|
|
69
|
-
* Filtered by isPrivate and user permission 'storage.file.viewPrivate'
|
|
70
|
-
*/
|
|
71
|
-
getPrivateFiles(page = 1, limit = 50) {
|
|
72
|
-
return this.getAll('', {
|
|
73
|
-
filter: { isPrivate: true },
|
|
74
|
-
pagination: { currentPage: page - 1, pageSize: limit },
|
|
75
|
-
select: [],
|
|
76
|
-
sort: {},
|
|
77
|
-
});
|
|
171
|
+
async createFileAsync(dto) {
|
|
172
|
+
return firstValueFrom(this.createFile(dto));
|
|
78
173
|
}
|
|
79
174
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: FileManagerApiService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
80
175
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: FileManagerApiService, providedIn: 'root' });
|
|
81
176
|
}
|
|
82
177
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: FileManagerApiService, decorators: [{
|
|
83
178
|
type: Injectable,
|
|
84
|
-
args: [{
|
|
85
|
-
|
|
86
|
-
|
|
179
|
+
args: [{ providedIn: 'root' }]
|
|
180
|
+
}], ctorParameters: () => [] });
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Upload Service - File upload/delete operations
|
|
184
|
+
* Endpoint: POST /storage/upload/*
|
|
185
|
+
*/
|
|
186
|
+
class UploadService extends BaseApiService {
|
|
187
|
+
fileManagerApi = inject(FileManagerApiService);
|
|
188
|
+
constructor() {
|
|
189
|
+
super('storage');
|
|
190
|
+
}
|
|
191
|
+
/** Get default storage config ID from app config */
|
|
192
|
+
getDefaultConfigId() {
|
|
193
|
+
return getDefaultStorageConfigId(this.appConfig);
|
|
194
|
+
}
|
|
195
|
+
/** Upload single file - POST /storage/upload/single-file */
|
|
196
|
+
uploadSingleFile(file, options) {
|
|
197
|
+
const formData = new FormData();
|
|
198
|
+
formData.append('file', file);
|
|
199
|
+
return this.http.post(this.buildUploadUrl('single-file', options), formData);
|
|
200
|
+
}
|
|
201
|
+
/** Upload multiple files (max 50) - POST /storage/upload/multiple-file */
|
|
202
|
+
uploadMultipleFiles(files, options) {
|
|
203
|
+
const formData = new FormData();
|
|
204
|
+
files.forEach((file) => formData.append('files', file));
|
|
205
|
+
return this.http.post(this.buildUploadUrl('multiple-file', options), formData);
|
|
206
|
+
}
|
|
207
|
+
/** Delete single file - POST /storage/upload/delete-single-file */
|
|
208
|
+
deleteSingleFile(key, storageConfigId) {
|
|
209
|
+
return this.http.post(this.getUrl('upload/delete-single-file'), this.buildDeleteBody({ key }, storageConfigId));
|
|
210
|
+
}
|
|
211
|
+
/** Delete multiple files - POST /storage/upload/delete-multiple-file */
|
|
212
|
+
deleteMultipleFiles(keys, storageConfigId) {
|
|
213
|
+
return this.http.post(this.getUrl('upload/delete-multiple-file'), this.buildDeleteBody({ keys }, storageConfigId));
|
|
214
|
+
}
|
|
215
|
+
/** Get file download URL - GET /storage/upload/file/:filePath */
|
|
216
|
+
getFileUrl(key) {
|
|
217
|
+
return this.getUrl(`upload/file/${key}`);
|
|
218
|
+
}
|
|
219
|
+
/** Upload file and register in file manager - returns file manager ID (UUID) */
|
|
220
|
+
uploadAndRegister(file, options) {
|
|
221
|
+
return this.uploadSingleFile(file, options).pipe(switchMap((response) => {
|
|
222
|
+
if (!response.success || !response.data) {
|
|
223
|
+
throw new Error(response.message || 'Upload failed');
|
|
224
|
+
}
|
|
225
|
+
return this.fileManagerApi.createFile({
|
|
226
|
+
name: response.data.name,
|
|
227
|
+
key: response.data.key,
|
|
228
|
+
size: String(response.data.size),
|
|
229
|
+
contentType: response.data.contentType,
|
|
230
|
+
isPrivate: options?.isPrivate ?? false,
|
|
231
|
+
folderId: options?.folderId,
|
|
232
|
+
storageConfigId: this.resolveConfigId(options?.storageConfigId),
|
|
233
|
+
});
|
|
234
|
+
}));
|
|
235
|
+
}
|
|
236
|
+
// ─── Private Helpers ───────────────────────────────────────────────────────
|
|
237
|
+
resolveConfigId(storageConfigId) {
|
|
238
|
+
return storageConfigId ?? this.getDefaultConfigId();
|
|
239
|
+
}
|
|
240
|
+
buildUploadUrl(endpoint, options) {
|
|
241
|
+
const params = this.buildQueryParams(options);
|
|
242
|
+
const baseUrl = this.getUrl(`upload/${endpoint}`);
|
|
243
|
+
return params ? `${baseUrl}?${params}` : baseUrl;
|
|
244
|
+
}
|
|
245
|
+
buildQueryParams(options) {
|
|
246
|
+
const params = new URLSearchParams();
|
|
247
|
+
const configId = this.resolveConfigId(options?.storageConfigId);
|
|
248
|
+
if (configId)
|
|
249
|
+
params.append('storageConfigId', configId);
|
|
250
|
+
if (options?.folderPath)
|
|
251
|
+
params.append('folderPath', options.folderPath);
|
|
252
|
+
if (options?.maxWidth)
|
|
253
|
+
params.append('maxWidth', options.maxWidth.toString());
|
|
254
|
+
if (options?.maxHeight)
|
|
255
|
+
params.append('maxHeight', options.maxHeight.toString());
|
|
256
|
+
if (options?.quality)
|
|
257
|
+
params.append('quality', options.quality.toString());
|
|
258
|
+
if (options?.format)
|
|
259
|
+
params.append('format', options.format);
|
|
260
|
+
if (options?.compress !== undefined)
|
|
261
|
+
params.append('compress', options.compress.toString());
|
|
262
|
+
return params.toString();
|
|
263
|
+
}
|
|
264
|
+
buildDeleteBody(data, storageConfigId) {
|
|
265
|
+
const configId = this.resolveConfigId(storageConfigId);
|
|
266
|
+
return { ...data, ...(configId && { storageConfigId: configId }) };
|
|
267
|
+
}
|
|
268
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: UploadService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
269
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: UploadService, providedIn: 'root' });
|
|
270
|
+
}
|
|
271
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: UploadService, decorators: [{
|
|
272
|
+
type: Injectable,
|
|
273
|
+
args: [{ providedIn: 'root' }]
|
|
87
274
|
}], ctorParameters: () => [] });
|
|
88
275
|
|
|
276
|
+
/**
|
|
277
|
+
* Profile Upload Provider Adapter
|
|
278
|
+
*
|
|
279
|
+
* Implements IProfileUploadProvider interface for ng-auth profile page.
|
|
280
|
+
* Uses UploadService.uploadAndRegister() to upload files and register in file manager.
|
|
281
|
+
*
|
|
282
|
+
* @example
|
|
283
|
+
* // In app.config.ts
|
|
284
|
+
* providers: [
|
|
285
|
+
* ...provideStorageProviders(),
|
|
286
|
+
* ]
|
|
287
|
+
*/
|
|
288
|
+
class ProfileUploadProviderAdapter {
|
|
289
|
+
uploadService = inject(UploadService);
|
|
290
|
+
/**
|
|
291
|
+
* Upload profile picture and register in file manager
|
|
292
|
+
* Returns file manager ID (UUID) for storing in user.profilePictureId
|
|
293
|
+
*/
|
|
294
|
+
uploadProfilePicture(file, options) {
|
|
295
|
+
return this.uploadService
|
|
296
|
+
.uploadAndRegister(file, {
|
|
297
|
+
folderPath: options?.folderPath ?? 'profile-pictures',
|
|
298
|
+
compress: options?.compress ?? true,
|
|
299
|
+
maxWidth: options?.maxWidth ?? 400,
|
|
300
|
+
maxHeight: options?.maxHeight ?? 400,
|
|
301
|
+
})
|
|
302
|
+
.pipe(map((response) => ({
|
|
303
|
+
success: response.success,
|
|
304
|
+
message: response.message,
|
|
305
|
+
data: response.data
|
|
306
|
+
? {
|
|
307
|
+
id: response.data.id,
|
|
308
|
+
name: response.data.name,
|
|
309
|
+
key: response.data.key,
|
|
310
|
+
size: parseFloat(response.data.size) * 1024, // Convert KB to bytes
|
|
311
|
+
contentType: response.data.contentType,
|
|
312
|
+
}
|
|
313
|
+
: undefined,
|
|
314
|
+
})));
|
|
315
|
+
}
|
|
316
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ProfileUploadProviderAdapter, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
317
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ProfileUploadProviderAdapter });
|
|
318
|
+
}
|
|
319
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ProfileUploadProviderAdapter, decorators: [{
|
|
320
|
+
type: Injectable
|
|
321
|
+
}] });
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Provide Storage Providers
|
|
325
|
+
*
|
|
326
|
+
* Registers storage-related provider implementations.
|
|
327
|
+
* Call this in app.config.ts to enable storage features in other packages.
|
|
328
|
+
*
|
|
329
|
+
* @example
|
|
330
|
+
* // In app.config.ts
|
|
331
|
+
* export const appConfig: ApplicationConfig = {
|
|
332
|
+
* providers: [
|
|
333
|
+
* ...provideStorageProviders(),
|
|
334
|
+
* ],
|
|
335
|
+
* };
|
|
336
|
+
*
|
|
337
|
+
* @returns Array of providers for storage functionality
|
|
338
|
+
*/
|
|
339
|
+
function provideStorageProviders() {
|
|
340
|
+
return [
|
|
341
|
+
{
|
|
342
|
+
provide: PROFILE_UPLOAD_PROVIDER,
|
|
343
|
+
useClass: ProfileUploadProviderAdapter,
|
|
344
|
+
},
|
|
345
|
+
];
|
|
346
|
+
}
|
|
347
|
+
|
|
89
348
|
/**
|
|
90
349
|
* Folder API Service
|
|
91
350
|
* Handles folder CRUD operations
|
|
@@ -130,121 +389,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
130
389
|
}]
|
|
131
390
|
}], ctorParameters: () => [] });
|
|
132
391
|
|
|
133
|
-
/**
|
|
134
|
-
* Upload Service
|
|
135
|
-
* Handles file upload/delete operations
|
|
136
|
-
* Endpoint: POST /storage/upload/*
|
|
137
|
-
*
|
|
138
|
-
* Features:
|
|
139
|
-
* - Company-scoped uploads (if company feature enabled)
|
|
140
|
-
* - Image compression
|
|
141
|
-
* - Multiple storage providers
|
|
142
|
-
* - File validation (size, type)
|
|
143
|
-
*/
|
|
144
|
-
class UploadService extends BaseApiService {
|
|
145
|
-
constructor() {
|
|
146
|
-
super('storage');
|
|
147
|
-
}
|
|
148
|
-
/**
|
|
149
|
-
* Upload single file
|
|
150
|
-
* POST /storage/upload/single-file?storageConfigId=x&folderPath=y
|
|
151
|
-
*
|
|
152
|
-
* @param file - File to upload
|
|
153
|
-
* @param options - Upload options (storage config, compression, etc.)
|
|
154
|
-
*/
|
|
155
|
-
uploadSingleFile(file, options) {
|
|
156
|
-
const formData = new FormData();
|
|
157
|
-
formData.append('file', file);
|
|
158
|
-
const queryParams = this.buildQueryParams(options);
|
|
159
|
-
const url = queryParams
|
|
160
|
-
? `${this.getUrl('upload/single-file')}?${queryParams}`
|
|
161
|
-
: this.getUrl('upload/single-file');
|
|
162
|
-
return this.http.post(url, formData);
|
|
163
|
-
}
|
|
164
|
-
/**
|
|
165
|
-
* Upload multiple files (max 50)
|
|
166
|
-
* POST /storage/upload/multiple-file?storageConfigId=x&folderPath=y
|
|
167
|
-
*
|
|
168
|
-
* @param files - Array of files to upload
|
|
169
|
-
* @param options - Upload options
|
|
170
|
-
*/
|
|
171
|
-
uploadMultipleFiles(files, options) {
|
|
172
|
-
const formData = new FormData();
|
|
173
|
-
files.forEach((file) => formData.append('files', file));
|
|
174
|
-
const queryParams = this.buildQueryParams(options);
|
|
175
|
-
const url = queryParams
|
|
176
|
-
? `${this.getUrl('upload/multiple-file')}?${queryParams}`
|
|
177
|
-
: this.getUrl('upload/multiple-file');
|
|
178
|
-
return this.http.post(url, formData);
|
|
179
|
-
}
|
|
180
|
-
/**
|
|
181
|
-
* Build query string from upload options
|
|
182
|
-
*/
|
|
183
|
-
buildQueryParams(options) {
|
|
184
|
-
if (!options)
|
|
185
|
-
return '';
|
|
186
|
-
const params = new URLSearchParams();
|
|
187
|
-
if (options.storageConfigId)
|
|
188
|
-
params.append('storageConfigId', options.storageConfigId);
|
|
189
|
-
if (options.folderPath)
|
|
190
|
-
params.append('folderPath', options.folderPath);
|
|
191
|
-
if (options.maxWidth)
|
|
192
|
-
params.append('maxWidth', options.maxWidth.toString());
|
|
193
|
-
if (options.maxHeight)
|
|
194
|
-
params.append('maxHeight', options.maxHeight.toString());
|
|
195
|
-
if (options.quality)
|
|
196
|
-
params.append('quality', options.quality.toString());
|
|
197
|
-
if (options.format)
|
|
198
|
-
params.append('format', options.format);
|
|
199
|
-
if (options.compress !== undefined)
|
|
200
|
-
params.append('compress', options.compress.toString());
|
|
201
|
-
return params.toString();
|
|
202
|
-
}
|
|
203
|
-
/**
|
|
204
|
-
* Delete single file
|
|
205
|
-
* POST /storage/upload/delete-single-file
|
|
206
|
-
*
|
|
207
|
-
* @param key - File key/path to delete
|
|
208
|
-
* @param storageConfigId - Optional storage config ID (uses default if not provided)
|
|
209
|
-
*/
|
|
210
|
-
deleteSingleFile(key, storageConfigId) {
|
|
211
|
-
return this.http.post(this.getUrl('upload/delete-single-file'), { key, ...(storageConfigId && { storageConfigId }) });
|
|
212
|
-
}
|
|
213
|
-
/**
|
|
214
|
-
* Delete multiple files
|
|
215
|
-
* POST /storage/upload/delete-multiple-file
|
|
216
|
-
*
|
|
217
|
-
* @param keys - Array of file keys/paths to delete
|
|
218
|
-
* @param storageConfigId - Optional storage config ID (uses default if not provided)
|
|
219
|
-
*/
|
|
220
|
-
deleteMultipleFiles(keys, storageConfigId) {
|
|
221
|
-
return this.http.post(this.getUrl('upload/delete-multiple-file'), { keys, ...(storageConfigId && { storageConfigId }) });
|
|
222
|
-
}
|
|
223
|
-
/**
|
|
224
|
-
* Get file download URL
|
|
225
|
-
* GET /storage/upload/file/:filePath
|
|
226
|
-
*
|
|
227
|
-
* @param key - File key/path
|
|
228
|
-
* @returns URL for direct file access
|
|
229
|
-
*/
|
|
230
|
-
getFileUrl(key) {
|
|
231
|
-
return this.getUrl(`upload/file/${key}`);
|
|
232
|
-
}
|
|
233
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: UploadService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
234
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: UploadService, providedIn: 'root' });
|
|
235
|
-
}
|
|
236
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: UploadService, decorators: [{
|
|
237
|
-
type: Injectable,
|
|
238
|
-
args: [{
|
|
239
|
-
providedIn: 'root',
|
|
240
|
-
}]
|
|
241
|
-
}], ctorParameters: () => [] });
|
|
242
|
-
|
|
243
392
|
/**
|
|
244
393
|
* Storage Module Routes
|
|
245
|
-
* All
|
|
246
|
-
*
|
|
247
|
-
* All routes are protected by permission guards to prevent direct URL access.
|
|
394
|
+
* All routes protected by permission guards
|
|
248
395
|
*/
|
|
249
396
|
const STORAGE_ROUTES = [
|
|
250
397
|
{
|
|
@@ -254,63 +401,68 @@ const STORAGE_ROUTES = [
|
|
|
254
401
|
// File Manager Routes
|
|
255
402
|
{
|
|
256
403
|
path: 'files',
|
|
404
|
+
canActivate: [permissionGuard(FILE_PERMISSIONS.READ)],
|
|
257
405
|
children: [
|
|
258
406
|
{
|
|
259
407
|
path: '',
|
|
260
|
-
loadComponent: () => import('./flusys-ng-storage-file-manager-list.component-
|
|
408
|
+
loadComponent: () => import('./flusys-ng-storage-file-manager-list.component-McM1zSlE.mjs').then((m) => m.FileManagerListComponent),
|
|
261
409
|
},
|
|
262
410
|
{
|
|
263
411
|
path: 'new',
|
|
264
|
-
|
|
412
|
+
canActivate: [permissionGuard(FILE_PERMISSIONS.CREATE)],
|
|
413
|
+
loadComponent: () => import('./flusys-ng-storage-file-manager-form.component-Bv8ER8J0.mjs').then((m) => m.FileManagerFormComponent),
|
|
265
414
|
},
|
|
266
415
|
{
|
|
267
416
|
path: ':id',
|
|
268
|
-
|
|
417
|
+
canActivate: [permissionGuard(FILE_PERMISSIONS.UPDATE)],
|
|
418
|
+
loadComponent: () => import('./flusys-ng-storage-file-manager-form.component-Bv8ER8J0.mjs').then((m) => m.FileManagerFormComponent),
|
|
269
419
|
},
|
|
270
420
|
],
|
|
271
421
|
},
|
|
272
422
|
// Folder Routes
|
|
273
423
|
{
|
|
274
424
|
path: 'folders',
|
|
425
|
+
canActivate: [permissionGuard(FOLDER_PERMISSIONS.READ)],
|
|
275
426
|
children: [
|
|
276
427
|
{
|
|
277
428
|
path: '',
|
|
278
|
-
loadComponent: () => import('./flusys-ng-storage-folder-list.component-
|
|
429
|
+
loadComponent: () => import('./flusys-ng-storage-folder-list.component-C4xHI8SO.mjs').then((m) => m.FolderListComponent),
|
|
279
430
|
},
|
|
280
431
|
{
|
|
281
432
|
path: 'new',
|
|
282
|
-
|
|
433
|
+
canActivate: [permissionGuard(FOLDER_PERMISSIONS.CREATE)],
|
|
434
|
+
loadComponent: () => import('./flusys-ng-storage-folder-form.component-fRFiMFev.mjs').then((m) => m.FolderFormComponent),
|
|
283
435
|
},
|
|
284
436
|
{
|
|
285
437
|
path: ':id',
|
|
286
|
-
|
|
438
|
+
canActivate: [permissionGuard(FOLDER_PERMISSIONS.UPDATE)],
|
|
439
|
+
loadComponent: () => import('./flusys-ng-storage-folder-form.component-fRFiMFev.mjs').then((m) => m.FolderFormComponent),
|
|
287
440
|
},
|
|
288
441
|
],
|
|
289
442
|
},
|
|
290
443
|
// Storage Config Routes
|
|
291
444
|
{
|
|
292
445
|
path: 'configs',
|
|
446
|
+
canActivate: [permissionGuard(STORAGE_CONFIG_PERMISSIONS.READ)],
|
|
293
447
|
children: [
|
|
294
448
|
{
|
|
295
449
|
path: '',
|
|
296
|
-
loadComponent: () => import('./flusys-ng-storage-storage-config-list.component-
|
|
450
|
+
loadComponent: () => import('./flusys-ng-storage-storage-config-list.component-iJez-rII.mjs').then((m) => m.StorageConfigListComponent),
|
|
297
451
|
},
|
|
298
452
|
{
|
|
299
453
|
path: 'new',
|
|
300
|
-
|
|
454
|
+
canActivate: [permissionGuard(STORAGE_CONFIG_PERMISSIONS.CREATE)],
|
|
455
|
+
loadComponent: () => import('./flusys-ng-storage-storage-config-form.component-DEMexjmg.mjs').then((m) => m.StorageConfigFormComponent),
|
|
301
456
|
},
|
|
302
457
|
{
|
|
303
458
|
path: ':id',
|
|
304
|
-
|
|
459
|
+
canActivate: [permissionGuard(STORAGE_CONFIG_PERMISSIONS.UPDATE)],
|
|
460
|
+
loadComponent: () => import('./flusys-ng-storage-storage-config-form.component-DEMexjmg.mjs').then((m) => m.StorageConfigFormComponent),
|
|
305
461
|
},
|
|
306
462
|
],
|
|
307
463
|
},
|
|
308
|
-
// Default redirect
|
|
309
|
-
{
|
|
310
|
-
path: '',
|
|
311
|
-
redirectTo: 'files',
|
|
312
|
-
pathMatch: 'full',
|
|
313
|
-
},
|
|
464
|
+
// Default redirect
|
|
465
|
+
{ path: '', redirectTo: 'files', pathMatch: 'full' },
|
|
314
466
|
],
|
|
315
467
|
},
|
|
316
468
|
];
|
|
@@ -320,81 +472,93 @@ const STORAGE_ROUTES = [
|
|
|
320
472
|
*
|
|
321
473
|
* Main container for Storage module with tab navigation
|
|
322
474
|
* Routes: Files | Folders | Configurations
|
|
475
|
+
* Tabs are filtered based on user permissions
|
|
323
476
|
*/
|
|
324
477
|
class StorageContainerComponent {
|
|
325
|
-
|
|
326
|
-
tabs
|
|
478
|
+
permissionValidator = inject(PermissionValidatorService);
|
|
479
|
+
/** All available tabs with required permissions */
|
|
480
|
+
allTabs = [
|
|
327
481
|
{
|
|
328
482
|
id: 'files',
|
|
329
483
|
label: 'Files',
|
|
330
484
|
icon: 'pi pi-file',
|
|
331
485
|
route: 'files',
|
|
486
|
+
permission: FILE_PERMISSIONS.READ,
|
|
332
487
|
},
|
|
333
488
|
{
|
|
334
489
|
id: 'folders',
|
|
335
490
|
label: 'Folders',
|
|
336
491
|
icon: 'pi pi-folder',
|
|
337
492
|
route: 'folders',
|
|
493
|
+
permission: FOLDER_PERMISSIONS.READ,
|
|
338
494
|
},
|
|
339
495
|
{
|
|
340
496
|
id: 'configs',
|
|
341
497
|
label: 'Configurations',
|
|
342
498
|
icon: 'pi pi-cog',
|
|
343
499
|
route: 'configs',
|
|
500
|
+
permission: STORAGE_CONFIG_PERMISSIONS.READ,
|
|
344
501
|
},
|
|
345
502
|
];
|
|
503
|
+
/** Visible tabs based on user permissions */
|
|
504
|
+
visibleTabs = computed(() => this.allTabs.filter((tab) => this.permissionValidator.hasPermission(tab.permission)), ...(ngDevMode ? [{ debugName: "visibleTabs" }] : []));
|
|
505
|
+
/** Dynamic subtitle based on visible tabs */
|
|
506
|
+
subtitle = computed(() => {
|
|
507
|
+
const tabs = this.visibleTabs();
|
|
508
|
+
if (tabs.length === 0)
|
|
509
|
+
return 'No permissions available';
|
|
510
|
+
return `Manage ${tabs.map((t) => t.label.toLowerCase()).join(', ')}`;
|
|
511
|
+
}, ...(ngDevMode ? [{ debugName: "subtitle" }] : []));
|
|
346
512
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: StorageContainerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
347
513
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: StorageContainerComponent, isStandalone: true, selector: "lib-storage-container", ngImport: i0, template: `
|
|
348
|
-
<div class="
|
|
349
|
-
<div class="
|
|
350
|
-
<h1 class="text-2xl font-bold m-0">Storage</h1>
|
|
351
|
-
<p class="text-
|
|
514
|
+
<div class="p-2 sm:p-4">
|
|
515
|
+
<div class="mb-4">
|
|
516
|
+
<h1 class="text-xl sm:text-2xl font-bold m-0">Storage</h1>
|
|
517
|
+
<p class="text-muted-color mt-1 text-sm sm:text-base">{{ subtitle() }}</p>
|
|
352
518
|
</div>
|
|
353
519
|
|
|
354
|
-
<div class="
|
|
355
|
-
@for (tab of
|
|
520
|
+
<div class="scrollbar-hide flex gap-1 mb-4 border-b border-surface overflow-x-auto flex-nowrap">
|
|
521
|
+
@for (tab of visibleTabs(); track tab.id) {
|
|
356
522
|
<a
|
|
357
523
|
[routerLink]="tab.route"
|
|
358
|
-
routerLinkActive="active"
|
|
359
|
-
class="
|
|
524
|
+
routerLinkActive="tab-active"
|
|
525
|
+
class="flex items-center gap-2 px-4 py-2 rounded-t-lg cursor-pointer transition-colors text-muted-color no-underline border-b-2 border-transparent -mb-px hover:bg-surface-hover [&.tab-active]:text-primary [&.tab-active]:border-primary [&.tab-active]:bg-surface-ground whitespace-nowrap shrink-0"
|
|
526
|
+
>
|
|
360
527
|
<i [class]="tab.icon"></i>
|
|
361
528
|
<span>{{ tab.label }}</span>
|
|
362
529
|
</a>
|
|
363
530
|
}
|
|
364
531
|
</div>
|
|
365
532
|
|
|
366
|
-
<
|
|
367
|
-
<router-outlet />
|
|
368
|
-
</div>
|
|
533
|
+
<router-outlet />
|
|
369
534
|
</div>
|
|
370
|
-
`, isInline: true, styles: [".
|
|
535
|
+
`, isInline: true, styles: [".scrollbar-hide{-ms-overflow-style:none;scrollbar-width:none}.scrollbar-hide::-webkit-scrollbar{display:none}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
371
536
|
}
|
|
372
537
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: StorageContainerComponent, decorators: [{
|
|
373
538
|
type: Component,
|
|
374
|
-
args: [{ selector: 'lib-storage-container',
|
|
375
|
-
<div class="
|
|
376
|
-
<div class="
|
|
377
|
-
<h1 class="text-2xl font-bold m-0">Storage</h1>
|
|
378
|
-
<p class="text-
|
|
539
|
+
args: [{ selector: 'lib-storage-container', changeDetection: ChangeDetectionStrategy.OnPush, imports: [CommonModule, RouterOutlet, RouterLink, RouterLinkActive], template: `
|
|
540
|
+
<div class="p-2 sm:p-4">
|
|
541
|
+
<div class="mb-4">
|
|
542
|
+
<h1 class="text-xl sm:text-2xl font-bold m-0">Storage</h1>
|
|
543
|
+
<p class="text-muted-color mt-1 text-sm sm:text-base">{{ subtitle() }}</p>
|
|
379
544
|
</div>
|
|
380
545
|
|
|
381
|
-
<div class="
|
|
382
|
-
@for (tab of
|
|
546
|
+
<div class="scrollbar-hide flex gap-1 mb-4 border-b border-surface overflow-x-auto flex-nowrap">
|
|
547
|
+
@for (tab of visibleTabs(); track tab.id) {
|
|
383
548
|
<a
|
|
384
549
|
[routerLink]="tab.route"
|
|
385
|
-
routerLinkActive="active"
|
|
386
|
-
class="
|
|
550
|
+
routerLinkActive="tab-active"
|
|
551
|
+
class="flex items-center gap-2 px-4 py-2 rounded-t-lg cursor-pointer transition-colors text-muted-color no-underline border-b-2 border-transparent -mb-px hover:bg-surface-hover [&.tab-active]:text-primary [&.tab-active]:border-primary [&.tab-active]:bg-surface-ground whitespace-nowrap shrink-0"
|
|
552
|
+
>
|
|
387
553
|
<i [class]="tab.icon"></i>
|
|
388
554
|
<span>{{ tab.label }}</span>
|
|
389
555
|
</a>
|
|
390
556
|
}
|
|
391
557
|
</div>
|
|
392
558
|
|
|
393
|
-
<
|
|
394
|
-
<router-outlet />
|
|
395
|
-
</div>
|
|
559
|
+
<router-outlet />
|
|
396
560
|
</div>
|
|
397
|
-
`, styles: [".
|
|
561
|
+
`, styles: [".scrollbar-hide{-ms-overflow-style:none;scrollbar-width:none}.scrollbar-hide::-webkit-scrollbar{display:none}\n"] }]
|
|
398
562
|
}] });
|
|
399
563
|
|
|
400
564
|
var storageContainer_component = /*#__PURE__*/Object.freeze({
|
|
@@ -411,5 +575,5 @@ var storageContainer_component = /*#__PURE__*/Object.freeze({
|
|
|
411
575
|
* Generated bundle index. Do not edit.
|
|
412
576
|
*/
|
|
413
577
|
|
|
414
|
-
export { FileLocationEnum, FileManagerApiService, FolderApiService, ImageFormat, STORAGE_ROUTES, StorageConfigApiService, StorageContainerComponent, UploadService };
|
|
578
|
+
export { BaseStorageListPage, BaseStoragePage, FileLocationEnum, FileManagerApiService, FolderApiService, ImageFormat, ProfileUploadProviderAdapter, STORAGE_PROVIDERS, STORAGE_ROUTES, StorageConfigApiService, StorageContainerComponent, UploadService, formatFileSize, getFileIcon, getProviderIcon, getProviderLabel, getProviderSeverity, provideStorageProviders };
|
|
415
579
|
//# sourceMappingURL=flusys-ng-storage.mjs.map
|