@flusys/ng-storage 1.1.0-beta → 1.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 +144 -5
- package/fesm2022/{flusys-ng-storage-file-manager-form.component-CxAYC6nj.mjs → flusys-ng-storage-file-manager-form.component-CXVB9G-f.mjs} +120 -351
- package/fesm2022/flusys-ng-storage-file-manager-form.component-CXVB9G-f.mjs.map +1 -0
- package/fesm2022/flusys-ng-storage-file-manager-list.component-0O_b4EjN.mjs +304 -0
- package/fesm2022/flusys-ng-storage-file-manager-list.component-0O_b4EjN.mjs.map +1 -0
- package/fesm2022/flusys-ng-storage-folder-form.component-BTQGEqNu.mjs +155 -0
- package/fesm2022/flusys-ng-storage-folder-form.component-BTQGEqNu.mjs.map +1 -0
- package/fesm2022/flusys-ng-storage-folder-list.component-B8GoIh6Z.mjs +194 -0
- package/fesm2022/flusys-ng-storage-folder-list.component-B8GoIh6Z.mjs.map +1 -0
- package/fesm2022/{flusys-ng-storage-storage-config-form.component-SEYIBife.mjs → flusys-ng-storage-storage-config-form.component-CorNWrmF.mjs} +219 -302
- package/fesm2022/flusys-ng-storage-storage-config-form.component-CorNWrmF.mjs.map +1 -0
- package/fesm2022/flusys-ng-storage-storage-config-list.component-YXzz3Tvo.mjs +187 -0
- package/fesm2022/flusys-ng-storage-storage-config-list.component-YXzz3Tvo.mjs.map +1 -0
- package/fesm2022/flusys-ng-storage.mjs +267 -226
- package/fesm2022/flusys-ng-storage.mjs.map +1 -1
- package/package.json +9 -9
- package/types/flusys-ng-storage.d.ts +101 -151
- package/fesm2022/flusys-ng-storage-file-manager-form.component-CxAYC6nj.mjs.map +0 -1
- package/fesm2022/flusys-ng-storage-file-manager-list.component-BevfDZO0.mjs +0 -475
- package/fesm2022/flusys-ng-storage-file-manager-list.component-BevfDZO0.mjs.map +0 -1
- package/fesm2022/flusys-ng-storage-folder-form.component-BhurG3dl.mjs +0 -253
- package/fesm2022/flusys-ng-storage-folder-form.component-BhurG3dl.mjs.map +0 -1
- package/fesm2022/flusys-ng-storage-folder-list.component-Clrjcw0B.mjs +0 -267
- package/fesm2022/flusys-ng-storage-folder-list.component-Clrjcw0B.mjs.map +0 -1
- package/fesm2022/flusys-ng-storage-storage-config-form.component-SEYIBife.mjs.map +0 -1
- package/fesm2022/flusys-ng-storage-storage-config-list.component-CofVBysj.mjs +0 -328
- package/fesm2022/flusys-ng-storage-storage-config-list.component-CofVBysj.mjs.map +0 -1
|
@@ -1,12 +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';
|
|
3
8
|
import { map } from 'rxjs/operators';
|
|
4
|
-
import { switchMap } from 'rxjs';
|
|
5
|
-
import { BaseApiService, getDefaultStorageConfigId } from '@flusys/ng-core';
|
|
9
|
+
import { firstValueFrom, switchMap } from 'rxjs';
|
|
6
10
|
import { HttpClient } from '@angular/common/http';
|
|
7
|
-
import { ApiResourceService, PROFILE_UPLOAD_PROVIDER } from '@flusys/ng-shared';
|
|
8
11
|
import { CommonModule } from '@angular/common';
|
|
9
|
-
import { RouterOutlet, RouterLink, RouterLinkActive } from '@angular/router';
|
|
10
12
|
|
|
11
13
|
/**
|
|
12
14
|
* File Location Enum
|
|
@@ -32,135 +34,219 @@ var ImageFormat;
|
|
|
32
34
|
})(ImageFormat || (ImageFormat = {}));
|
|
33
35
|
|
|
34
36
|
/**
|
|
35
|
-
*
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
|
40
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
|
|
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.5", ngImport: i0, type: BaseStoragePage, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
139
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.5", type: BaseStoragePage, isStandalone: true, ngImport: i0 });
|
|
140
|
+
}
|
|
141
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", 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.5", ngImport: i0, type: BaseStorageListPage, deps: null, target: i0.ɵɵFactoryTarget.Directive });
|
|
154
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.5", type: BaseStorageListPage, isStandalone: true, usesInheritance: true, ngImport: i0 });
|
|
155
|
+
}
|
|
156
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: BaseStorageListPage, decorators: [{
|
|
157
|
+
type: Directive
|
|
158
|
+
}] });
|
|
159
|
+
|
|
41
160
|
class FileManagerApiService extends ApiResourceService {
|
|
42
161
|
constructor() {
|
|
43
|
-
|
|
44
|
-
super('storage/file-manager', http);
|
|
162
|
+
super('file-manager', inject(HttpClient), 'storage');
|
|
45
163
|
}
|
|
46
|
-
/**
|
|
47
|
-
* Get multiple files with valid URLs
|
|
48
|
-
* POST /storage/file-manager/get-files
|
|
49
|
-
* Regenerates presigned URLs if expired
|
|
50
|
-
*/
|
|
51
164
|
getFiles(fileIds) {
|
|
52
165
|
const requests = fileIds.map((id) => ({ id }));
|
|
53
166
|
return this.http.post(`${this.baseUrl}/get-files`, requests);
|
|
54
167
|
}
|
|
55
|
-
|
|
56
|
-
* Get files by folder
|
|
57
|
-
* POST /storage/file-manager/get-all
|
|
58
|
-
* Filtered by folder ID and company (if enabled)
|
|
59
|
-
*/
|
|
60
|
-
getFilesByFolder(folderId, page = 1, limit = 50) {
|
|
61
|
-
return this.getAll('', {
|
|
62
|
-
filter: folderId ? { folderId } : {},
|
|
63
|
-
pagination: { currentPage: page - 1, pageSize: limit },
|
|
64
|
-
select: [],
|
|
65
|
-
sort: {},
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
/**
|
|
69
|
-
* Get private files (requires permission)
|
|
70
|
-
* POST /storage/file-manager/get-all
|
|
71
|
-
* Filtered by isPrivate and user permission 'storage.file.viewPrivate'
|
|
72
|
-
*/
|
|
73
|
-
getPrivateFiles(page = 1, limit = 50) {
|
|
74
|
-
return this.getAll('', {
|
|
75
|
-
filter: { isPrivate: true },
|
|
76
|
-
pagination: { currentPage: page - 1, pageSize: limit },
|
|
77
|
-
select: [],
|
|
78
|
-
sort: {},
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
/**
|
|
82
|
-
* Create new file manager record
|
|
83
|
-
* POST /storage/file-manager/insert
|
|
84
|
-
*/
|
|
85
|
-
create(dto) {
|
|
168
|
+
createFile(dto) {
|
|
86
169
|
return this.http.post(`${this.baseUrl}/insert`, dto);
|
|
87
170
|
}
|
|
88
|
-
|
|
89
|
-
|
|
171
|
+
async createFileAsync(dto) {
|
|
172
|
+
return firstValueFrom(this.createFile(dto));
|
|
173
|
+
}
|
|
174
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: FileManagerApiService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
175
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: FileManagerApiService, providedIn: 'root' });
|
|
90
176
|
}
|
|
91
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.
|
|
177
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: FileManagerApiService, decorators: [{
|
|
92
178
|
type: Injectable,
|
|
93
|
-
args: [{
|
|
94
|
-
providedIn: 'root',
|
|
95
|
-
}]
|
|
179
|
+
args: [{ providedIn: 'root' }]
|
|
96
180
|
}], ctorParameters: () => [] });
|
|
97
181
|
|
|
98
182
|
/**
|
|
99
|
-
* Upload Service
|
|
100
|
-
* Handles file upload/delete operations
|
|
183
|
+
* Upload Service - File upload/delete operations
|
|
101
184
|
* Endpoint: POST /storage/upload/*
|
|
102
|
-
*
|
|
103
|
-
* Features:
|
|
104
|
-
* - Company-scoped uploads (if company feature enabled)
|
|
105
|
-
* - Image compression
|
|
106
|
-
* - Multiple storage providers
|
|
107
|
-
* - File validation (size, type)
|
|
108
|
-
* - Default storage config from environment (services.storage.defaultStorageConfigId)
|
|
109
185
|
*/
|
|
110
186
|
class UploadService extends BaseApiService {
|
|
111
187
|
fileManagerApi = inject(FileManagerApiService);
|
|
112
188
|
constructor() {
|
|
113
189
|
super('storage');
|
|
114
190
|
}
|
|
115
|
-
/**
|
|
116
|
-
* Get default storage config ID from app config
|
|
117
|
-
* Used when no storageConfigId is provided in upload options
|
|
118
|
-
*/
|
|
191
|
+
/** Get default storage config ID from app config */
|
|
119
192
|
getDefaultConfigId() {
|
|
120
193
|
return getDefaultStorageConfigId(this.appConfig);
|
|
121
194
|
}
|
|
122
|
-
/**
|
|
123
|
-
* Upload single file
|
|
124
|
-
* POST /storage/upload/single-file?storageConfigId=x&folderPath=y
|
|
125
|
-
*
|
|
126
|
-
* @param file - File to upload
|
|
127
|
-
* @param options - Upload options (storage config, compression, etc.)
|
|
128
|
-
*/
|
|
195
|
+
/** Upload single file - POST /storage/upload/single-file */
|
|
129
196
|
uploadSingleFile(file, options) {
|
|
130
197
|
const formData = new FormData();
|
|
131
198
|
formData.append('file', file);
|
|
132
|
-
|
|
133
|
-
const url = queryParams
|
|
134
|
-
? `${this.getUrl('upload/single-file')}?${queryParams}`
|
|
135
|
-
: this.getUrl('upload/single-file');
|
|
136
|
-
return this.http.post(url, formData);
|
|
199
|
+
return this.http.post(this.buildUploadUrl('single-file', options), formData);
|
|
137
200
|
}
|
|
138
|
-
/**
|
|
139
|
-
* Upload multiple files (max 50)
|
|
140
|
-
* POST /storage/upload/multiple-file?storageConfigId=x&folderPath=y
|
|
141
|
-
*
|
|
142
|
-
* @param files - Array of files to upload
|
|
143
|
-
* @param options - Upload options
|
|
144
|
-
*/
|
|
201
|
+
/** Upload multiple files (max 50) - POST /storage/upload/multiple-file */
|
|
145
202
|
uploadMultipleFiles(files, options) {
|
|
146
203
|
const formData = new FormData();
|
|
147
204
|
files.forEach((file) => formData.append('files', file));
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
return this.http.post(
|
|
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;
|
|
153
244
|
}
|
|
154
|
-
/**
|
|
155
|
-
* Build query string from upload options
|
|
156
|
-
* Uses default storageConfigId from app config if not provided in options
|
|
157
|
-
*/
|
|
158
245
|
buildQueryParams(options) {
|
|
159
246
|
const params = new URLSearchParams();
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
params.append('storageConfigId', storageConfigId);
|
|
247
|
+
const configId = this.resolveConfigId(options?.storageConfigId);
|
|
248
|
+
if (configId)
|
|
249
|
+
params.append('storageConfigId', configId);
|
|
164
250
|
if (options?.folderPath)
|
|
165
251
|
params.append('folderPath', options.folderPath);
|
|
166
252
|
if (options?.maxWidth)
|
|
@@ -175,74 +261,16 @@ class UploadService extends BaseApiService {
|
|
|
175
261
|
params.append('compress', options.compress.toString());
|
|
176
262
|
return params.toString();
|
|
177
263
|
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
*
|
|
182
|
-
* @param key - File key/path to delete
|
|
183
|
-
* @param storageConfigId - Optional storage config ID (uses default from config if not provided)
|
|
184
|
-
*/
|
|
185
|
-
deleteSingleFile(key, storageConfigId) {
|
|
186
|
-
const configId = storageConfigId ?? this.getDefaultConfigId();
|
|
187
|
-
return this.http.post(this.getUrl('upload/delete-single-file'), { key, ...(configId && { storageConfigId: configId }) });
|
|
264
|
+
buildDeleteBody(data, storageConfigId) {
|
|
265
|
+
const configId = this.resolveConfigId(storageConfigId);
|
|
266
|
+
return { ...data, ...(configId && { storageConfigId: configId }) };
|
|
188
267
|
}
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
* POST /storage/upload/delete-multiple-file
|
|
192
|
-
*
|
|
193
|
-
* @param keys - Array of file keys/paths to delete
|
|
194
|
-
* @param storageConfigId - Optional storage config ID (uses default from config if not provided)
|
|
195
|
-
*/
|
|
196
|
-
deleteMultipleFiles(keys, storageConfigId) {
|
|
197
|
-
const configId = storageConfigId ?? this.getDefaultConfigId();
|
|
198
|
-
return this.http.post(this.getUrl('upload/delete-multiple-file'), { keys, ...(configId && { storageConfigId: configId }) });
|
|
199
|
-
}
|
|
200
|
-
/**
|
|
201
|
-
* Get file download URL
|
|
202
|
-
* GET /storage/upload/file/:filePath
|
|
203
|
-
*
|
|
204
|
-
* @param key - File key/path
|
|
205
|
-
* @returns URL for direct file access
|
|
206
|
-
*/
|
|
207
|
-
getFileUrl(key) {
|
|
208
|
-
return this.getUrl(`upload/file/${key}`);
|
|
209
|
-
}
|
|
210
|
-
/**
|
|
211
|
-
* Upload file and register in file manager
|
|
212
|
-
* Returns file manager ID (UUID) for storing in references like profilePictureId
|
|
213
|
-
*
|
|
214
|
-
* @param file - File to upload
|
|
215
|
-
* @param options - Upload options (storage config, compression, etc.)
|
|
216
|
-
* @returns Observable with file manager record (id is the UUID to store)
|
|
217
|
-
*/
|
|
218
|
-
uploadAndRegister(file, options) {
|
|
219
|
-
return this.uploadSingleFile(file, options).pipe(switchMap((uploadResponse) => {
|
|
220
|
-
if (!uploadResponse.success || !uploadResponse.data) {
|
|
221
|
-
throw new Error(uploadResponse.message || 'Upload failed');
|
|
222
|
-
}
|
|
223
|
-
const uploadedFile = uploadResponse.data;
|
|
224
|
-
const storageConfigId = options?.storageConfigId ?? this.getDefaultConfigId();
|
|
225
|
-
// Create file manager record
|
|
226
|
-
return this.fileManagerApi.create({
|
|
227
|
-
name: uploadedFile.name,
|
|
228
|
-
key: uploadedFile.key,
|
|
229
|
-
size: String(uploadedFile.size),
|
|
230
|
-
contentType: uploadedFile.contentType,
|
|
231
|
-
isPrivate: options?.isPrivate ?? false,
|
|
232
|
-
folderId: options?.folderId,
|
|
233
|
-
storageConfigId,
|
|
234
|
-
location: FileLocationEnum.LOCAL,
|
|
235
|
-
});
|
|
236
|
-
}));
|
|
237
|
-
}
|
|
238
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: UploadService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
239
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: UploadService, providedIn: 'root' });
|
|
268
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: UploadService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
269
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: UploadService, providedIn: 'root' });
|
|
240
270
|
}
|
|
241
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.
|
|
271
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: UploadService, decorators: [{
|
|
242
272
|
type: Injectable,
|
|
243
|
-
args: [{
|
|
244
|
-
providedIn: 'root',
|
|
245
|
-
}]
|
|
273
|
+
args: [{ providedIn: 'root' }]
|
|
246
274
|
}], ctorParameters: () => [] });
|
|
247
275
|
|
|
248
276
|
/**
|
|
@@ -285,10 +313,10 @@ class ProfileUploadProviderAdapter {
|
|
|
285
313
|
: undefined,
|
|
286
314
|
})));
|
|
287
315
|
}
|
|
288
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.
|
|
289
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.
|
|
316
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: ProfileUploadProviderAdapter, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
317
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: ProfileUploadProviderAdapter });
|
|
290
318
|
}
|
|
291
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.
|
|
319
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: ProfileUploadProviderAdapter, decorators: [{
|
|
292
320
|
type: Injectable
|
|
293
321
|
}] });
|
|
294
322
|
|
|
@@ -326,13 +354,12 @@ function provideStorageProviders() {
|
|
|
326
354
|
*/
|
|
327
355
|
class FolderApiService extends ApiResourceService {
|
|
328
356
|
constructor() {
|
|
329
|
-
|
|
330
|
-
super('storage/folder', http);
|
|
357
|
+
super('folder', inject(HttpClient), 'storage');
|
|
331
358
|
}
|
|
332
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.
|
|
333
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.
|
|
359
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: FolderApiService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
360
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: FolderApiService, providedIn: 'root' });
|
|
334
361
|
}
|
|
335
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.
|
|
362
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: FolderApiService, decorators: [{
|
|
336
363
|
type: Injectable,
|
|
337
364
|
args: [{
|
|
338
365
|
providedIn: 'root',
|
|
@@ -348,13 +375,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
348
375
|
*/
|
|
349
376
|
class StorageConfigApiService extends ApiResourceService {
|
|
350
377
|
constructor() {
|
|
351
|
-
|
|
352
|
-
super('storage/storage-config', http);
|
|
378
|
+
super('storage-config', inject(HttpClient), 'storage');
|
|
353
379
|
}
|
|
354
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.
|
|
355
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.
|
|
380
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: StorageConfigApiService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
381
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: StorageConfigApiService, providedIn: 'root' });
|
|
356
382
|
}
|
|
357
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.
|
|
383
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: StorageConfigApiService, decorators: [{
|
|
358
384
|
type: Injectable,
|
|
359
385
|
args: [{
|
|
360
386
|
providedIn: 'root',
|
|
@@ -363,9 +389,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
363
389
|
|
|
364
390
|
/**
|
|
365
391
|
* Storage Module Routes
|
|
366
|
-
* All
|
|
367
|
-
*
|
|
368
|
-
* All routes are protected by permission guards to prevent direct URL access.
|
|
392
|
+
* All routes protected by permission guards
|
|
369
393
|
*/
|
|
370
394
|
const STORAGE_ROUTES = [
|
|
371
395
|
{
|
|
@@ -375,63 +399,68 @@ const STORAGE_ROUTES = [
|
|
|
375
399
|
// File Manager Routes
|
|
376
400
|
{
|
|
377
401
|
path: 'files',
|
|
402
|
+
canActivate: [permissionGuard(FILE_PERMISSIONS.READ)],
|
|
378
403
|
children: [
|
|
379
404
|
{
|
|
380
405
|
path: '',
|
|
381
|
-
loadComponent: () => import('./flusys-ng-storage-file-manager-list.component-
|
|
406
|
+
loadComponent: () => import('./flusys-ng-storage-file-manager-list.component-0O_b4EjN.mjs').then((m) => m.FileManagerListComponent),
|
|
382
407
|
},
|
|
383
408
|
{
|
|
384
409
|
path: 'new',
|
|
385
|
-
|
|
410
|
+
canActivate: [permissionGuard(FILE_PERMISSIONS.CREATE)],
|
|
411
|
+
loadComponent: () => import('./flusys-ng-storage-file-manager-form.component-CXVB9G-f.mjs').then((m) => m.FileManagerFormComponent),
|
|
386
412
|
},
|
|
387
413
|
{
|
|
388
414
|
path: ':id',
|
|
389
|
-
|
|
415
|
+
canActivate: [permissionGuard(FILE_PERMISSIONS.UPDATE)],
|
|
416
|
+
loadComponent: () => import('./flusys-ng-storage-file-manager-form.component-CXVB9G-f.mjs').then((m) => m.FileManagerFormComponent),
|
|
390
417
|
},
|
|
391
418
|
],
|
|
392
419
|
},
|
|
393
420
|
// Folder Routes
|
|
394
421
|
{
|
|
395
422
|
path: 'folders',
|
|
423
|
+
canActivate: [permissionGuard(FOLDER_PERMISSIONS.READ)],
|
|
396
424
|
children: [
|
|
397
425
|
{
|
|
398
426
|
path: '',
|
|
399
|
-
loadComponent: () => import('./flusys-ng-storage-folder-list.component-
|
|
427
|
+
loadComponent: () => import('./flusys-ng-storage-folder-list.component-B8GoIh6Z.mjs').then((m) => m.FolderListComponent),
|
|
400
428
|
},
|
|
401
429
|
{
|
|
402
430
|
path: 'new',
|
|
403
|
-
|
|
431
|
+
canActivate: [permissionGuard(FOLDER_PERMISSIONS.CREATE)],
|
|
432
|
+
loadComponent: () => import('./flusys-ng-storage-folder-form.component-BTQGEqNu.mjs').then((m) => m.FolderFormComponent),
|
|
404
433
|
},
|
|
405
434
|
{
|
|
406
435
|
path: ':id',
|
|
407
|
-
|
|
436
|
+
canActivate: [permissionGuard(FOLDER_PERMISSIONS.UPDATE)],
|
|
437
|
+
loadComponent: () => import('./flusys-ng-storage-folder-form.component-BTQGEqNu.mjs').then((m) => m.FolderFormComponent),
|
|
408
438
|
},
|
|
409
439
|
],
|
|
410
440
|
},
|
|
411
441
|
// Storage Config Routes
|
|
412
442
|
{
|
|
413
443
|
path: 'configs',
|
|
444
|
+
canActivate: [permissionGuard(STORAGE_CONFIG_PERMISSIONS.READ)],
|
|
414
445
|
children: [
|
|
415
446
|
{
|
|
416
447
|
path: '',
|
|
417
|
-
loadComponent: () => import('./flusys-ng-storage-storage-config-list.component-
|
|
448
|
+
loadComponent: () => import('./flusys-ng-storage-storage-config-list.component-YXzz3Tvo.mjs').then((m) => m.StorageConfigListComponent),
|
|
418
449
|
},
|
|
419
450
|
{
|
|
420
451
|
path: 'new',
|
|
421
|
-
|
|
452
|
+
canActivate: [permissionGuard(STORAGE_CONFIG_PERMISSIONS.CREATE)],
|
|
453
|
+
loadComponent: () => import('./flusys-ng-storage-storage-config-form.component-CorNWrmF.mjs').then((m) => m.StorageConfigFormComponent),
|
|
422
454
|
},
|
|
423
455
|
{
|
|
424
456
|
path: ':id',
|
|
425
|
-
|
|
457
|
+
canActivate: [permissionGuard(STORAGE_CONFIG_PERMISSIONS.UPDATE)],
|
|
458
|
+
loadComponent: () => import('./flusys-ng-storage-storage-config-form.component-CorNWrmF.mjs').then((m) => m.StorageConfigFormComponent),
|
|
426
459
|
},
|
|
427
460
|
],
|
|
428
461
|
},
|
|
429
|
-
// Default redirect
|
|
430
|
-
{
|
|
431
|
-
path: '',
|
|
432
|
-
redirectTo: 'files',
|
|
433
|
-
pathMatch: 'full',
|
|
434
|
-
},
|
|
462
|
+
// Default redirect
|
|
463
|
+
{ path: '', redirectTo: 'files', pathMatch: 'full' },
|
|
435
464
|
],
|
|
436
465
|
},
|
|
437
466
|
];
|
|
@@ -441,81 +470,93 @@ const STORAGE_ROUTES = [
|
|
|
441
470
|
*
|
|
442
471
|
* Main container for Storage module with tab navigation
|
|
443
472
|
* Routes: Files | Folders | Configurations
|
|
473
|
+
* Tabs are filtered based on user permissions
|
|
444
474
|
*/
|
|
445
475
|
class StorageContainerComponent {
|
|
446
|
-
|
|
447
|
-
tabs
|
|
476
|
+
permissionValidator = inject(PermissionValidatorService);
|
|
477
|
+
/** All available tabs with required permissions */
|
|
478
|
+
allTabs = [
|
|
448
479
|
{
|
|
449
480
|
id: 'files',
|
|
450
481
|
label: 'Files',
|
|
451
482
|
icon: 'pi pi-file',
|
|
452
483
|
route: 'files',
|
|
484
|
+
permission: FILE_PERMISSIONS.READ,
|
|
453
485
|
},
|
|
454
486
|
{
|
|
455
487
|
id: 'folders',
|
|
456
488
|
label: 'Folders',
|
|
457
489
|
icon: 'pi pi-folder',
|
|
458
490
|
route: 'folders',
|
|
491
|
+
permission: FOLDER_PERMISSIONS.READ,
|
|
459
492
|
},
|
|
460
493
|
{
|
|
461
494
|
id: 'configs',
|
|
462
495
|
label: 'Configurations',
|
|
463
496
|
icon: 'pi pi-cog',
|
|
464
497
|
route: 'configs',
|
|
498
|
+
permission: STORAGE_CONFIG_PERMISSIONS.READ,
|
|
465
499
|
},
|
|
466
500
|
];
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
501
|
+
/** Visible tabs based on user permissions */
|
|
502
|
+
visibleTabs = computed(() => this.allTabs.filter((tab) => this.permissionValidator.hasPermission(tab.permission)), ...(ngDevMode ? [{ debugName: "visibleTabs" }] : []));
|
|
503
|
+
/** Dynamic subtitle based on visible tabs */
|
|
504
|
+
subtitle = computed(() => {
|
|
505
|
+
const tabs = this.visibleTabs();
|
|
506
|
+
if (tabs.length === 0)
|
|
507
|
+
return 'No permissions available';
|
|
508
|
+
return `Manage ${tabs.map((t) => t.label.toLowerCase()).join(', ')}`;
|
|
509
|
+
}, ...(ngDevMode ? [{ debugName: "subtitle" }] : []));
|
|
510
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: StorageContainerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
511
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.5", type: StorageContainerComponent, isStandalone: true, selector: "lib-storage-container", ngImport: i0, template: `
|
|
512
|
+
<div class="p-2 sm:p-4">
|
|
513
|
+
<div class="mb-4">
|
|
514
|
+
<h1 class="text-xl sm:text-2xl font-bold m-0">Storage</h1>
|
|
515
|
+
<p class="text-muted-color mt-1 text-sm sm:text-base">{{ subtitle() }}</p>
|
|
473
516
|
</div>
|
|
474
517
|
|
|
475
|
-
<div class="
|
|
476
|
-
@for (tab of
|
|
518
|
+
<div class="scrollbar-hide flex gap-1 mb-4 border-b border-surface overflow-x-auto flex-nowrap">
|
|
519
|
+
@for (tab of visibleTabs(); track tab.id) {
|
|
477
520
|
<a
|
|
478
521
|
[routerLink]="tab.route"
|
|
479
|
-
routerLinkActive="active"
|
|
480
|
-
class="
|
|
522
|
+
routerLinkActive="tab-active"
|
|
523
|
+
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"
|
|
524
|
+
>
|
|
481
525
|
<i [class]="tab.icon"></i>
|
|
482
526
|
<span>{{ tab.label }}</span>
|
|
483
527
|
</a>
|
|
484
528
|
}
|
|
485
529
|
</div>
|
|
486
530
|
|
|
487
|
-
<
|
|
488
|
-
<router-outlet />
|
|
489
|
-
</div>
|
|
531
|
+
<router-outlet />
|
|
490
532
|
</div>
|
|
491
|
-
`, isInline: true, styles: [".
|
|
533
|
+
`, 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 });
|
|
492
534
|
}
|
|
493
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.
|
|
535
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: StorageContainerComponent, decorators: [{
|
|
494
536
|
type: Component,
|
|
495
|
-
args: [{ selector: 'lib-storage-container',
|
|
496
|
-
<div class="
|
|
497
|
-
<div class="
|
|
498
|
-
<h1 class="text-2xl font-bold m-0">Storage</h1>
|
|
499
|
-
<p class="text-
|
|
537
|
+
args: [{ selector: 'lib-storage-container', changeDetection: ChangeDetectionStrategy.OnPush, imports: [CommonModule, RouterOutlet, RouterLink, RouterLinkActive], template: `
|
|
538
|
+
<div class="p-2 sm:p-4">
|
|
539
|
+
<div class="mb-4">
|
|
540
|
+
<h1 class="text-xl sm:text-2xl font-bold m-0">Storage</h1>
|
|
541
|
+
<p class="text-muted-color mt-1 text-sm sm:text-base">{{ subtitle() }}</p>
|
|
500
542
|
</div>
|
|
501
543
|
|
|
502
|
-
<div class="
|
|
503
|
-
@for (tab of
|
|
544
|
+
<div class="scrollbar-hide flex gap-1 mb-4 border-b border-surface overflow-x-auto flex-nowrap">
|
|
545
|
+
@for (tab of visibleTabs(); track tab.id) {
|
|
504
546
|
<a
|
|
505
547
|
[routerLink]="tab.route"
|
|
506
|
-
routerLinkActive="active"
|
|
507
|
-
class="
|
|
548
|
+
routerLinkActive="tab-active"
|
|
549
|
+
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"
|
|
550
|
+
>
|
|
508
551
|
<i [class]="tab.icon"></i>
|
|
509
552
|
<span>{{ tab.label }}</span>
|
|
510
553
|
</a>
|
|
511
554
|
}
|
|
512
555
|
</div>
|
|
513
556
|
|
|
514
|
-
<
|
|
515
|
-
<router-outlet />
|
|
516
|
-
</div>
|
|
557
|
+
<router-outlet />
|
|
517
558
|
</div>
|
|
518
|
-
`, styles: [".
|
|
559
|
+
`, styles: [".scrollbar-hide{-ms-overflow-style:none;scrollbar-width:none}.scrollbar-hide::-webkit-scrollbar{display:none}\n"] }]
|
|
519
560
|
}] });
|
|
520
561
|
|
|
521
562
|
var storageContainer_component = /*#__PURE__*/Object.freeze({
|
|
@@ -532,5 +573,5 @@ var storageContainer_component = /*#__PURE__*/Object.freeze({
|
|
|
532
573
|
* Generated bundle index. Do not edit.
|
|
533
574
|
*/
|
|
534
575
|
|
|
535
|
-
export { FileLocationEnum, FileManagerApiService, FolderApiService, ImageFormat, ProfileUploadProviderAdapter, STORAGE_ROUTES, StorageConfigApiService, StorageContainerComponent, UploadService, provideStorageProviders };
|
|
576
|
+
export { BaseStorageListPage, BaseStoragePage, FileLocationEnum, FileManagerApiService, FolderApiService, ImageFormat, ProfileUploadProviderAdapter, STORAGE_PROVIDERS, STORAGE_ROUTES, StorageConfigApiService, StorageContainerComponent, UploadService, formatFileSize, getFileIcon, getProviderIcon, getProviderLabel, getProviderSeverity, provideStorageProviders };
|
|
536
577
|
//# sourceMappingURL=flusys-ng-storage.mjs.map
|