@flusys/ng-storage 1.1.0-beta → 2.0.0-rc.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 +142 -5
- package/fesm2022/{flusys-ng-storage-file-manager-form.component-CxAYC6nj.mjs → flusys-ng-storage-file-manager-form.component-Bv8ER8J0.mjs} +118 -349
- 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-SEYIBife.mjs → flusys-ng-storage-storage-config-form.component-DEMexjmg.mjs} +217 -300
- 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 +247 -204
- package/fesm2022/flusys-ng-storage.mjs.map +1 -1
- package/package.json +4 -4
- 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.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
|
+
|
|
41
160
|
class FileManagerApiService extends ApiResourceService {
|
|
42
161
|
constructor() {
|
|
43
|
-
|
|
44
|
-
super('storage/file-manager', http);
|
|
162
|
+
super('storage/file-manager', inject(HttpClient));
|
|
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
|
}
|
|
171
|
+
async createFileAsync(dto) {
|
|
172
|
+
return firstValueFrom(this.createFile(dto));
|
|
173
|
+
}
|
|
88
174
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: FileManagerApiService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
89
175
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: FileManagerApiService, providedIn: 'root' });
|
|
90
176
|
}
|
|
91
177
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", 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 }) });
|
|
188
|
-
}
|
|
189
|
-
/**
|
|
190
|
-
* Delete multiple files
|
|
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
|
-
}));
|
|
264
|
+
buildDeleteBody(data, storageConfigId) {
|
|
265
|
+
const configId = this.resolveConfigId(storageConfigId);
|
|
266
|
+
return { ...data, ...(configId && { storageConfigId: configId }) };
|
|
237
267
|
}
|
|
238
268
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: UploadService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
239
269
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: UploadService, providedIn: 'root' });
|
|
240
270
|
}
|
|
241
271
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", 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
|
/**
|
|
@@ -363,9 +391,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
363
391
|
|
|
364
392
|
/**
|
|
365
393
|
* Storage Module Routes
|
|
366
|
-
* All
|
|
367
|
-
*
|
|
368
|
-
* All routes are protected by permission guards to prevent direct URL access.
|
|
394
|
+
* All routes protected by permission guards
|
|
369
395
|
*/
|
|
370
396
|
const STORAGE_ROUTES = [
|
|
371
397
|
{
|
|
@@ -375,63 +401,68 @@ const STORAGE_ROUTES = [
|
|
|
375
401
|
// File Manager Routes
|
|
376
402
|
{
|
|
377
403
|
path: 'files',
|
|
404
|
+
canActivate: [permissionGuard(FILE_PERMISSIONS.READ)],
|
|
378
405
|
children: [
|
|
379
406
|
{
|
|
380
407
|
path: '',
|
|
381
|
-
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),
|
|
382
409
|
},
|
|
383
410
|
{
|
|
384
411
|
path: 'new',
|
|
385
|
-
|
|
412
|
+
canActivate: [permissionGuard(FILE_PERMISSIONS.CREATE)],
|
|
413
|
+
loadComponent: () => import('./flusys-ng-storage-file-manager-form.component-Bv8ER8J0.mjs').then((m) => m.FileManagerFormComponent),
|
|
386
414
|
},
|
|
387
415
|
{
|
|
388
416
|
path: ':id',
|
|
389
|
-
|
|
417
|
+
canActivate: [permissionGuard(FILE_PERMISSIONS.UPDATE)],
|
|
418
|
+
loadComponent: () => import('./flusys-ng-storage-file-manager-form.component-Bv8ER8J0.mjs').then((m) => m.FileManagerFormComponent),
|
|
390
419
|
},
|
|
391
420
|
],
|
|
392
421
|
},
|
|
393
422
|
// Folder Routes
|
|
394
423
|
{
|
|
395
424
|
path: 'folders',
|
|
425
|
+
canActivate: [permissionGuard(FOLDER_PERMISSIONS.READ)],
|
|
396
426
|
children: [
|
|
397
427
|
{
|
|
398
428
|
path: '',
|
|
399
|
-
loadComponent: () => import('./flusys-ng-storage-folder-list.component-
|
|
429
|
+
loadComponent: () => import('./flusys-ng-storage-folder-list.component-C4xHI8SO.mjs').then((m) => m.FolderListComponent),
|
|
400
430
|
},
|
|
401
431
|
{
|
|
402
432
|
path: 'new',
|
|
403
|
-
|
|
433
|
+
canActivate: [permissionGuard(FOLDER_PERMISSIONS.CREATE)],
|
|
434
|
+
loadComponent: () => import('./flusys-ng-storage-folder-form.component-fRFiMFev.mjs').then((m) => m.FolderFormComponent),
|
|
404
435
|
},
|
|
405
436
|
{
|
|
406
437
|
path: ':id',
|
|
407
|
-
|
|
438
|
+
canActivate: [permissionGuard(FOLDER_PERMISSIONS.UPDATE)],
|
|
439
|
+
loadComponent: () => import('./flusys-ng-storage-folder-form.component-fRFiMFev.mjs').then((m) => m.FolderFormComponent),
|
|
408
440
|
},
|
|
409
441
|
],
|
|
410
442
|
},
|
|
411
443
|
// Storage Config Routes
|
|
412
444
|
{
|
|
413
445
|
path: 'configs',
|
|
446
|
+
canActivate: [permissionGuard(STORAGE_CONFIG_PERMISSIONS.READ)],
|
|
414
447
|
children: [
|
|
415
448
|
{
|
|
416
449
|
path: '',
|
|
417
|
-
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),
|
|
418
451
|
},
|
|
419
452
|
{
|
|
420
453
|
path: 'new',
|
|
421
|
-
|
|
454
|
+
canActivate: [permissionGuard(STORAGE_CONFIG_PERMISSIONS.CREATE)],
|
|
455
|
+
loadComponent: () => import('./flusys-ng-storage-storage-config-form.component-DEMexjmg.mjs').then((m) => m.StorageConfigFormComponent),
|
|
422
456
|
},
|
|
423
457
|
{
|
|
424
458
|
path: ':id',
|
|
425
|
-
|
|
459
|
+
canActivate: [permissionGuard(STORAGE_CONFIG_PERMISSIONS.UPDATE)],
|
|
460
|
+
loadComponent: () => import('./flusys-ng-storage-storage-config-form.component-DEMexjmg.mjs').then((m) => m.StorageConfigFormComponent),
|
|
426
461
|
},
|
|
427
462
|
],
|
|
428
463
|
},
|
|
429
|
-
// Default redirect
|
|
430
|
-
{
|
|
431
|
-
path: '',
|
|
432
|
-
redirectTo: 'files',
|
|
433
|
-
pathMatch: 'full',
|
|
434
|
-
},
|
|
464
|
+
// Default redirect
|
|
465
|
+
{ path: '', redirectTo: 'files', pathMatch: 'full' },
|
|
435
466
|
],
|
|
436
467
|
},
|
|
437
468
|
];
|
|
@@ -441,81 +472,93 @@ const STORAGE_ROUTES = [
|
|
|
441
472
|
*
|
|
442
473
|
* Main container for Storage module with tab navigation
|
|
443
474
|
* Routes: Files | Folders | Configurations
|
|
475
|
+
* Tabs are filtered based on user permissions
|
|
444
476
|
*/
|
|
445
477
|
class StorageContainerComponent {
|
|
446
|
-
|
|
447
|
-
tabs
|
|
478
|
+
permissionValidator = inject(PermissionValidatorService);
|
|
479
|
+
/** All available tabs with required permissions */
|
|
480
|
+
allTabs = [
|
|
448
481
|
{
|
|
449
482
|
id: 'files',
|
|
450
483
|
label: 'Files',
|
|
451
484
|
icon: 'pi pi-file',
|
|
452
485
|
route: 'files',
|
|
486
|
+
permission: FILE_PERMISSIONS.READ,
|
|
453
487
|
},
|
|
454
488
|
{
|
|
455
489
|
id: 'folders',
|
|
456
490
|
label: 'Folders',
|
|
457
491
|
icon: 'pi pi-folder',
|
|
458
492
|
route: 'folders',
|
|
493
|
+
permission: FOLDER_PERMISSIONS.READ,
|
|
459
494
|
},
|
|
460
495
|
{
|
|
461
496
|
id: 'configs',
|
|
462
497
|
label: 'Configurations',
|
|
463
498
|
icon: 'pi pi-cog',
|
|
464
499
|
route: 'configs',
|
|
500
|
+
permission: STORAGE_CONFIG_PERMISSIONS.READ,
|
|
465
501
|
},
|
|
466
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" }] : []));
|
|
467
512
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: StorageContainerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
468
513
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: StorageContainerComponent, isStandalone: true, selector: "lib-storage-container", ngImport: i0, template: `
|
|
469
|
-
<div class="
|
|
470
|
-
<div class="
|
|
471
|
-
<h1 class="text-2xl font-bold m-0">Storage</h1>
|
|
472
|
-
<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>
|
|
473
518
|
</div>
|
|
474
519
|
|
|
475
|
-
<div class="
|
|
476
|
-
@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) {
|
|
477
522
|
<a
|
|
478
523
|
[routerLink]="tab.route"
|
|
479
|
-
routerLinkActive="active"
|
|
480
|
-
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
|
+
>
|
|
481
527
|
<i [class]="tab.icon"></i>
|
|
482
528
|
<span>{{ tab.label }}</span>
|
|
483
529
|
</a>
|
|
484
530
|
}
|
|
485
531
|
</div>
|
|
486
532
|
|
|
487
|
-
<
|
|
488
|
-
<router-outlet />
|
|
489
|
-
</div>
|
|
533
|
+
<router-outlet />
|
|
490
534
|
</div>
|
|
491
|
-
`, 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 });
|
|
492
536
|
}
|
|
493
537
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: StorageContainerComponent, decorators: [{
|
|
494
538
|
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-
|
|
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>
|
|
500
544
|
</div>
|
|
501
545
|
|
|
502
|
-
<div class="
|
|
503
|
-
@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) {
|
|
504
548
|
<a
|
|
505
549
|
[routerLink]="tab.route"
|
|
506
|
-
routerLinkActive="active"
|
|
507
|
-
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
|
+
>
|
|
508
553
|
<i [class]="tab.icon"></i>
|
|
509
554
|
<span>{{ tab.label }}</span>
|
|
510
555
|
</a>
|
|
511
556
|
}
|
|
512
557
|
</div>
|
|
513
558
|
|
|
514
|
-
<
|
|
515
|
-
<router-outlet />
|
|
516
|
-
</div>
|
|
559
|
+
<router-outlet />
|
|
517
560
|
</div>
|
|
518
|
-
`, styles: [".
|
|
561
|
+
`, styles: [".scrollbar-hide{-ms-overflow-style:none;scrollbar-width:none}.scrollbar-hide::-webkit-scrollbar{display:none}\n"] }]
|
|
519
562
|
}] });
|
|
520
563
|
|
|
521
564
|
var storageContainer_component = /*#__PURE__*/Object.freeze({
|
|
@@ -532,5 +575,5 @@ var storageContainer_component = /*#__PURE__*/Object.freeze({
|
|
|
532
575
|
* Generated bundle index. Do not edit.
|
|
533
576
|
*/
|
|
534
577
|
|
|
535
|
-
export { FileLocationEnum, FileManagerApiService, FolderApiService, ImageFormat, ProfileUploadProviderAdapter, STORAGE_ROUTES, StorageConfigApiService, StorageContainerComponent, UploadService, provideStorageProviders };
|
|
578
|
+
export { BaseStorageListPage, BaseStoragePage, FileLocationEnum, FileManagerApiService, FolderApiService, ImageFormat, ProfileUploadProviderAdapter, STORAGE_PROVIDERS, STORAGE_ROUTES, StorageConfigApiService, StorageContainerComponent, UploadService, formatFileSize, getFileIcon, getProviderIcon, getProviderLabel, getProviderSeverity, provideStorageProviders };
|
|
536
579
|
//# sourceMappingURL=flusys-ng-storage.mjs.map
|