committed by
GitHub
14 changed files with 635 additions and 21 deletions
@ -0,0 +1,81 @@ |
|||
///
|
|||
/// Copyright © 2016-2021 The Thingsboard Authors
|
|||
///
|
|||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
|||
/// you may not use this file except in compliance with the License.
|
|||
/// You may obtain a copy of the License at
|
|||
///
|
|||
/// http://www.apache.org/licenses/LICENSE-2.0
|
|||
///
|
|||
/// Unless required by applicable law or agreed to in writing, software
|
|||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
|||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||
/// See the License for the specific language governing permissions and
|
|||
/// limitations under the License.
|
|||
///
|
|||
|
|||
import { Injectable } from '@angular/core'; |
|||
import { HttpClient } from '@angular/common/http'; |
|||
import { PageLink } from '@shared/models/page/page-link'; |
|||
import { defaultHttpOptionsFromConfig, RequestConfig } from '@core/http/http-utils'; |
|||
import { Observable } from 'rxjs'; |
|||
import { PageData } from '@shared/models/page/page-data'; |
|||
import { Resource, ResourceInfo } from '@shared/models/resource.models'; |
|||
import { map } from 'rxjs/operators'; |
|||
|
|||
@Injectable({ |
|||
providedIn: 'root' |
|||
}) |
|||
export class ResourceService { |
|||
constructor( |
|||
private http: HttpClient |
|||
) { |
|||
|
|||
} |
|||
|
|||
public getResources(pageLink: PageLink, config?: RequestConfig): Observable<PageData<ResourceInfo>> { |
|||
return this.http.get<PageData<ResourceInfo>>(`/api/resource${pageLink.toQuery()}`, |
|||
defaultHttpOptionsFromConfig(config)); |
|||
} |
|||
|
|||
public getResource(resourceId: string, config?: RequestConfig): Observable<Resource> { |
|||
return this.http.get<Resource>(`/api/resource/${resourceId}`, defaultHttpOptionsFromConfig(config)); |
|||
} |
|||
|
|||
public downloadResource(resourceId: string): Observable<any> { |
|||
return this.http.get(`/api/resource/${resourceId}/download`, { responseType: 'arraybuffer', observe: 'response' }).pipe( |
|||
map((response) => { |
|||
const headers = response.headers; |
|||
const filename = headers.get('x-filename'); |
|||
const contentType = headers.get('content-type'); |
|||
const linkElement = document.createElement('a'); |
|||
try { |
|||
const blob = new Blob([response.body], { type: contentType }); |
|||
const url = URL.createObjectURL(blob); |
|||
linkElement.setAttribute('href', url); |
|||
linkElement.setAttribute('download', filename); |
|||
const clickEvent = new MouseEvent('click', |
|||
{ |
|||
view: window, |
|||
bubbles: true, |
|||
cancelable: false |
|||
} |
|||
); |
|||
linkElement.dispatchEvent(clickEvent); |
|||
return null; |
|||
} catch (e) { |
|||
throw e; |
|||
} |
|||
}) |
|||
); |
|||
} |
|||
|
|||
public saveResource(resource: Resource, config?: RequestConfig): Observable<Resource> { |
|||
return this.http.post<Resource>('/api/resource', resource, defaultHttpOptionsFromConfig(config)); |
|||
} |
|||
|
|||
public deleteResource(resourceId: string, config?: RequestConfig) { |
|||
return this.http.delete(`/api/resource/${resourceId}`, defaultHttpOptionsFromConfig(config)); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,48 @@ |
|||
///
|
|||
/// Copyright © 2016-2021 The Thingsboard Authors
|
|||
///
|
|||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
|||
/// you may not use this file except in compliance with the License.
|
|||
/// You may obtain a copy of the License at
|
|||
///
|
|||
/// http://www.apache.org/licenses/LICENSE-2.0
|
|||
///
|
|||
/// Unless required by applicable law or agreed to in writing, software
|
|||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
|||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||
/// See the License for the specific language governing permissions and
|
|||
/// limitations under the License.
|
|||
///
|
|||
|
|||
import { RouterModule, Routes } from '@angular/router'; |
|||
import { EntitiesTableComponent } from '@home/components/entity/entities-table.component'; |
|||
import { Authority } from '@shared/models/authority.enum'; |
|||
import { NgModule } from '@angular/core'; |
|||
import { ResourcesLibraryTableConfigResolver } from './resources-library-table-config.resolve'; |
|||
|
|||
const routes: Routes = [ |
|||
{ |
|||
path: 'resources-library', |
|||
component: EntitiesTableComponent, |
|||
data: { |
|||
auth: [Authority.TENANT_ADMIN, Authority.SYS_ADMIN], |
|||
title: 'resource.resources-library', |
|||
breadcrumb: { |
|||
label: 'resource.resources-library', |
|||
icon: 'folder' |
|||
} |
|||
}, |
|||
resolve: { |
|||
entitiesTableConfig: ResourcesLibraryTableConfigResolver |
|||
} |
|||
} |
|||
]; |
|||
|
|||
@NgModule({ |
|||
imports: [RouterModule.forChild(routes)], |
|||
exports: [RouterModule], |
|||
providers: [ |
|||
ResourcesLibraryTableConfigResolver |
|||
] |
|||
}) |
|||
export class ResourcesLibraryRoutingModule{ } |
|||
@ -0,0 +1,33 @@ |
|||
///
|
|||
/// Copyright © 2016-2021 The Thingsboard Authors
|
|||
///
|
|||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
|||
/// you may not use this file except in compliance with the License.
|
|||
/// You may obtain a copy of the License at
|
|||
///
|
|||
/// http://www.apache.org/licenses/LICENSE-2.0
|
|||
///
|
|||
/// Unless required by applicable law or agreed to in writing, software
|
|||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
|||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||
/// See the License for the specific language governing permissions and
|
|||
/// limitations under the License.
|
|||
///
|
|||
|
|||
import { NgModule } from '@angular/core'; |
|||
import { CommonModule } from '@angular/common'; |
|||
import { ResourcesLibraryRoutingModule } from '@home/pages/resource/resource-routing.module'; |
|||
import { SharedModule } from '@shared/shared.module'; |
|||
import { HomeComponentsModule } from '@home/components/home-components.module'; |
|||
import { ResourcesLibraryComponent } from './resources-library.component'; |
|||
|
|||
@NgModule({ |
|||
declarations: [ResourcesLibraryComponent], |
|||
imports: [ |
|||
CommonModule, |
|||
SharedModule, |
|||
HomeComponentsModule, |
|||
ResourcesLibraryRoutingModule |
|||
] |
|||
}) |
|||
export class ResourceModule { } |
|||
@ -0,0 +1,115 @@ |
|||
///
|
|||
/// Copyright © 2016-2021 The Thingsboard Authors
|
|||
///
|
|||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
|||
/// you may not use this file except in compliance with the License.
|
|||
/// You may obtain a copy of the License at
|
|||
///
|
|||
/// http://www.apache.org/licenses/LICENSE-2.0
|
|||
///
|
|||
/// Unless required by applicable law or agreed to in writing, software
|
|||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
|||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||
/// See the License for the specific language governing permissions and
|
|||
/// limitations under the License.
|
|||
///
|
|||
|
|||
import { Injectable } from '@angular/core'; |
|||
import { |
|||
checkBoxCell, |
|||
DateEntityTableColumn, |
|||
EntityTableColumn, |
|||
EntityTableConfig |
|||
} from '@home/models/entity/entities-table-config.models'; |
|||
import { Resolve } from '@angular/router'; |
|||
import { Resource, ResourceInfo, ResourceTypeTranslationMap } from '@shared/models/resource.models'; |
|||
import { EntityType, entityTypeResources, entityTypeTranslations } from '@shared/models/entity-type.models'; |
|||
import { Direction } from '@shared/models/page/sort-order'; |
|||
import { NULL_UUID } from '@shared/models/id/has-uuid'; |
|||
import { DatePipe } from '@angular/common'; |
|||
import { TranslateService } from '@ngx-translate/core'; |
|||
import { ResourceService } from '@core/http/resource.service'; |
|||
import { getCurrentAuthUser } from '@core/auth/auth.selectors'; |
|||
import { Store } from '@ngrx/store'; |
|||
import { AppState } from '@core/core.state'; |
|||
import { Authority } from '@shared/models/authority.enum'; |
|||
import { ResourcesLibraryComponent } from '@home/pages/resource/resources-library.component'; |
|||
import { Observable } from 'rxjs/internal/Observable'; |
|||
import { PageData } from '@shared/models/page/page-data'; |
|||
|
|||
@Injectable() |
|||
export class ResourcesLibraryTableConfigResolver implements Resolve<EntityTableConfig<Resource>> { |
|||
|
|||
private readonly config: EntityTableConfig<Resource> = new EntityTableConfig<Resource>(); |
|||
private readonly resourceTypesTranslationMap = ResourceTypeTranslationMap; |
|||
|
|||
constructor(private store: Store<AppState>, |
|||
private resourceService: ResourceService, |
|||
private translate: TranslateService, |
|||
private datePipe: DatePipe) { |
|||
|
|||
this.config.entityType = EntityType.TB_RESOURCE; |
|||
this.config.entityComponent = ResourcesLibraryComponent; |
|||
this.config.entityTranslations = entityTypeTranslations.get(EntityType.TB_RESOURCE); |
|||
this.config.entityResources = entityTypeResources.get(EntityType.TB_RESOURCE); |
|||
this.config.defaultSortOrder = {property: 'title', direction: Direction.ASC}; |
|||
|
|||
this.config.entityTitle = (resource) => resource ? |
|||
resource.title : ''; |
|||
|
|||
this.config.columns.push( |
|||
new DateEntityTableColumn<ResourceInfo>('createdTime', 'common.created-time', this.datePipe, '150px'), |
|||
new EntityTableColumn<ResourceInfo>('title', 'widgets-bundle.title', '60%'), |
|||
new EntityTableColumn<ResourceInfo>('resourceType', 'resource.resource-type', '40%', |
|||
entity => this.resourceTypesTranslationMap.get(entity.resourceType)), |
|||
new EntityTableColumn<ResourceInfo>('tenantId', 'widgets-bundle.system', '60px', |
|||
entity => { |
|||
return checkBoxCell(entity.tenantId.id === NULL_UUID); |
|||
}), |
|||
); |
|||
|
|||
this.config.cellActionDescriptors.push( |
|||
{ |
|||
name: this.translate.instant('resource.export'), |
|||
icon: 'file_download', |
|||
isEnabled: () => true, |
|||
onAction: ($event, entity) => this.exportResource($event, entity) |
|||
} |
|||
); |
|||
|
|||
this.config.deleteEntityTitle = resource => this.translate.instant('resource.delete-resource-title', |
|||
{ resourceTitle: resource.title }); |
|||
this.config.deleteEntityContent = () => this.translate.instant('resource.delete-resource-text'); |
|||
this.config.deleteEntitiesTitle = count => this.translate.instant('resource.delete-resources-title', {count}); |
|||
this.config.deleteEntitiesContent = () => this.translate.instant('resource.delete-resources-text'); |
|||
|
|||
this.config.entitiesFetchFunction = pageLink => this.resourceService.getResources(pageLink) as Observable<PageData<Resource>>; |
|||
this.config.loadEntity = id => this.resourceService.getResource(id.id); |
|||
this.config.saveEntity = resource => this.resourceService.saveResource(resource); |
|||
this.config.deleteEntity = id => this.resourceService.deleteResource(id.id); |
|||
} |
|||
|
|||
resolve(): EntityTableConfig<Resource> { |
|||
this.config.tableTitle = this.translate.instant('resource.resources-library'); |
|||
const authUser = getCurrentAuthUser(this.store); |
|||
this.config.deleteEnabled = (resource) => this.isResourceEditable(resource, authUser.authority); |
|||
this.config.entitySelectionEnabled = (resource) => this.isResourceEditable(resource, authUser.authority); |
|||
this.config.detailsReadonly = (resource) => !this.isResourceEditable(resource, authUser.authority); |
|||
return this.config; |
|||
} |
|||
|
|||
exportResource($event: Event, resource: ResourceInfo) { |
|||
if ($event) { |
|||
$event.stopPropagation(); |
|||
} |
|||
this.resourceService.downloadResource(resource.id.id).subscribe(); |
|||
} |
|||
|
|||
private isResourceEditable(resource: Resource, authority: Authority): boolean { |
|||
if (authority === Authority.TENANT_ADMIN) { |
|||
return resource && resource.tenantId && resource.tenantId.id !== NULL_UUID; |
|||
} else { |
|||
return authority === Authority.SYS_ADMIN; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,56 @@ |
|||
<!-- |
|||
|
|||
Copyright © 2016-2021 The Thingsboard Authors |
|||
|
|||
Licensed under the Apache License, Version 2.0 (the "License"); |
|||
you may not use this file except in compliance with the License. |
|||
You may obtain a copy of the License at |
|||
|
|||
http://www.apache.org/licenses/LICENSE-2.0 |
|||
|
|||
Unless required by applicable law or agreed to in writing, software |
|||
distributed under the License is distributed on an "AS IS" BASIS, |
|||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
See the License for the specific language governing permissions and |
|||
limitations under the License. |
|||
|
|||
--> |
|||
<div class="tb-details-buttons" fxLayout.xs="column"> |
|||
<button mat-raised-button color="primary" fxFlex.xs |
|||
[disabled]="(isLoading$ | async)" |
|||
(click)="onEntityAction($event, 'delete')" |
|||
[fxShow]="!hideDelete() && !isEdit"> |
|||
{{'resource.delete' | translate }} |
|||
</button> |
|||
</div> |
|||
<div class="mat-padding" fxLayout="column"> |
|||
<form [formGroup]="entityForm"> |
|||
<fieldset [disabled]="(isLoading$ | async) || !isEdit"> |
|||
<mat-form-field class="mat-block"> |
|||
<mat-label translate>resource.resource-type</mat-label> |
|||
<mat-select formControlName="resourceType" required> |
|||
<mat-option *ngFor="let resourceType of resourceTypes" [value]="resourceType"> |
|||
{{ resourceTypesTranslationMap.get(resourceType) }} |
|||
</mat-option> |
|||
</mat-select> |
|||
</mat-form-field> |
|||
<mat-form-field class="mat-block" *ngIf="entityForm.get('resourceType').value !== resourceType.LWM2M_MODEL || !isAdd"> |
|||
<mat-label translate>resource.title</mat-label> |
|||
<input matInput formControlName="title" required [readonly]="entityForm.get('resourceType').value === resourceType.LWM2M_MODEL"> |
|||
<mat-error *ngIf="entityForm.get('title').hasError('required')"> |
|||
{{ 'resource.title-required' | translate }} |
|||
</mat-error> |
|||
</mat-form-field> |
|||
<tb-file-input |
|||
formControlName="data" |
|||
required |
|||
[convertToBase64]="true" |
|||
[allowedExtensions]="getAllowedExtensions()" |
|||
[accept]="getAcceptType()" |
|||
dropLabel="{{'resource.drop-file' | translate}}" |
|||
[existingFileName]="entityForm.get('fileName')?.value" |
|||
(fileNameChanged)="entityForm?.get('fileName').patchValue($event)"> |
|||
</tb-file-input> |
|||
</fieldset> |
|||
</form> |
|||
</div> |
|||
@ -0,0 +1,122 @@ |
|||
///
|
|||
/// Copyright © 2016-2021 The Thingsboard Authors
|
|||
///
|
|||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
|||
/// you may not use this file except in compliance with the License.
|
|||
/// You may obtain a copy of the License at
|
|||
///
|
|||
/// http://www.apache.org/licenses/LICENSE-2.0
|
|||
///
|
|||
/// Unless required by applicable law or agreed to in writing, software
|
|||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
|||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||
/// See the License for the specific language governing permissions and
|
|||
/// limitations under the License.
|
|||
///
|
|||
|
|||
import { Component, Inject, OnDestroy, OnInit } from '@angular/core'; |
|||
import { Subject } from 'rxjs'; |
|||
import { Store } from '@ngrx/store'; |
|||
import { AppState } from '@core/core.state'; |
|||
import { TranslateService } from '@ngx-translate/core'; |
|||
import { EntityTableConfig } from '@home/models/entity/entities-table-config.models'; |
|||
import { FormBuilder, FormGroup, Validators } from '@angular/forms'; |
|||
import { EntityComponent } from '@home/components/entity/entity.component'; |
|||
import { |
|||
Resource, |
|||
ResourceType, |
|||
ResourceTypeExtension, |
|||
ResourceTypeMIMETypes, |
|||
ResourceTypeTranslationMap |
|||
} from '@shared/models/resource.models'; |
|||
import { distinctUntilChanged, takeUntil } from 'rxjs/operators'; |
|||
|
|||
@Component({ |
|||
selector: 'tb-resources-library', |
|||
templateUrl: './resources-library.component.html' |
|||
}) |
|||
export class ResourcesLibraryComponent extends EntityComponent<Resource> implements OnInit, OnDestroy { |
|||
|
|||
readonly resourceType = ResourceType; |
|||
readonly resourceTypes = Object.values(this.resourceType); |
|||
readonly resourceTypesTranslationMap = ResourceTypeTranslationMap; |
|||
|
|||
private destroy$ = new Subject(); |
|||
|
|||
constructor(protected store: Store<AppState>, |
|||
protected translate: TranslateService, |
|||
@Inject('entity') protected entityValue: Resource, |
|||
@Inject('entitiesTableConfig') protected entitiesTableConfigValue: EntityTableConfig<Resource>, |
|||
public fb: FormBuilder) { |
|||
super(store, fb, entityValue, entitiesTableConfigValue); |
|||
} |
|||
|
|||
ngOnInit() { |
|||
super.ngOnInit(); |
|||
this.entityForm.get('resourceType').valueChanges.pipe( |
|||
distinctUntilChanged((oldValue, newValue) => [oldValue, newValue].includes(this.resourceType.LWM2M_MODEL)), |
|||
takeUntil(this.destroy$) |
|||
).subscribe((type) => { |
|||
if (type === this.resourceType.LWM2M_MODEL) { |
|||
this.entityForm.get('title').clearValidators(); |
|||
} else { |
|||
this.entityForm.get('title').setValidators(Validators.required); |
|||
} |
|||
this.entityForm.get('title').updateValueAndValidity({emitEvent: false}); |
|||
}); |
|||
} |
|||
|
|||
ngOnDestroy() { |
|||
super.ngOnDestroy(); |
|||
this.destroy$.next(); |
|||
this.destroy$.complete(); |
|||
} |
|||
|
|||
hideDelete() { |
|||
if (this.entitiesTableConfig) { |
|||
return !this.entitiesTableConfig.deleteEnabled(this.entity); |
|||
} else { |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
buildForm(entity: Resource): FormGroup { |
|||
return this.fb.group( |
|||
{ |
|||
resourceType: [{value: entity?.resourceType ? entity.resourceType : ResourceType.LWM2M_MODEL, |
|||
disabled: this.isEdit }, [Validators.required]], |
|||
data: [entity ? entity.data : null, [Validators.required]], |
|||
fileName: [entity ? entity.fileName : null, [Validators.required]], |
|||
title: [entity ? entity.title : '', []] |
|||
} |
|||
); |
|||
} |
|||
|
|||
updateForm(entity: Resource) { |
|||
this.entityForm.patchValue({resourceType: entity.resourceType}); |
|||
if (this.isEdit) { |
|||
this.entityForm.get('resourceType').disable({emitEvent: false}); |
|||
} |
|||
this.entityForm.patchValue({ |
|||
data: entity.data, |
|||
fileName: entity.fileName, |
|||
title: entity.title |
|||
}); |
|||
} |
|||
|
|||
getAllowedExtensions() { |
|||
try { |
|||
return ResourceTypeExtension.get(this.entityForm.get('resourceType').value); |
|||
} catch (e) { |
|||
return ''; |
|||
} |
|||
} |
|||
|
|||
getAcceptType() { |
|||
try { |
|||
return ResourceTypeMIMETypes.get(this.entityForm.get('resourceType').value); |
|||
} catch (e) { |
|||
return '*/*'; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
///
|
|||
/// Copyright © 2016-2021 The Thingsboard Authors
|
|||
///
|
|||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
|||
/// you may not use this file except in compliance with the License.
|
|||
/// You may obtain a copy of the License at
|
|||
///
|
|||
/// http://www.apache.org/licenses/LICENSE-2.0
|
|||
///
|
|||
/// Unless required by applicable law or agreed to in writing, software
|
|||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
|||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||
/// See the License for the specific language governing permissions and
|
|||
/// limitations under the License.
|
|||
///
|
|||
|
|||
import { EntityId } from '@shared/models/id/entity-id'; |
|||
import { EntityType } from '@shared/models/entity-type.models'; |
|||
|
|||
export class TbResourceId implements EntityId { |
|||
entityType = EntityType.TB_RESOURCE; |
|||
id: string; |
|||
constructor(id: string) { |
|||
this.id = id; |
|||
} |
|||
} |
|||
@ -0,0 +1,61 @@ |
|||
///
|
|||
/// Copyright © 2016-2021 The Thingsboard Authors
|
|||
///
|
|||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
|||
/// you may not use this file except in compliance with the License.
|
|||
/// You may obtain a copy of the License at
|
|||
///
|
|||
/// http://www.apache.org/licenses/LICENSE-2.0
|
|||
///
|
|||
/// Unless required by applicable law or agreed to in writing, software
|
|||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
|||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||
/// See the License for the specific language governing permissions and
|
|||
/// limitations under the License.
|
|||
///
|
|||
|
|||
import { BaseData } from '@shared/models/base-data'; |
|||
import { TenantId } from '@shared/models/id/tenant-id'; |
|||
import { TbResourceId } from '@shared/models/id/tb-resource-id'; |
|||
|
|||
export enum ResourceType { |
|||
LWM2M_MODEL = 'LWM2M_MODEL', |
|||
PKCS_12 = 'PKCS_12', |
|||
JKS = 'JKS' |
|||
} |
|||
|
|||
export const ResourceTypeMIMETypes = new Map<ResourceType, string>( |
|||
[ |
|||
[ResourceType.LWM2M_MODEL, 'application/xml,text/xml'], |
|||
[ResourceType.PKCS_12, 'application/x-pkcs12'], |
|||
[ResourceType.JKS, 'application/x-java-keystore'] |
|||
] |
|||
); |
|||
|
|||
export const ResourceTypeExtension = new Map<ResourceType, string>( |
|||
[ |
|||
[ResourceType.LWM2M_MODEL, 'xml'], |
|||
[ResourceType.PKCS_12, 'p12,pfx'], |
|||
[ResourceType.JKS, 'jks'] |
|||
] |
|||
); |
|||
|
|||
export const ResourceTypeTranslationMap = new Map<ResourceType, string>( |
|||
[ |
|||
[ResourceType.LWM2M_MODEL, 'LWM2M model'], |
|||
[ResourceType.PKCS_12, 'PKCS #12'], |
|||
[ResourceType.JKS, 'JKS'] |
|||
] |
|||
); |
|||
|
|||
export interface ResourceInfo extends BaseData<TbResourceId> { |
|||
tenantId?: TenantId; |
|||
resourceKey?: string; |
|||
title?: string; |
|||
resourceType: ResourceType; |
|||
} |
|||
|
|||
export interface Resource extends ResourceInfo { |
|||
data: string; |
|||
fileName: string; |
|||
} |
|||
Loading…
Reference in new issue