map in progress
This commit is contained in:
parent
5d52f8e0f2
commit
d11770821e
|
@ -86,6 +86,7 @@
|
||||||
|
|
||||||
<div class="ui-g-12 ui-g-nopad">
|
<div class="ui-g-12 ui-g-nopad">
|
||||||
<of-service-selector [disabled]="!serviceChecked" (change)="includeServices = $event"></of-service-selector>
|
<of-service-selector [disabled]="!serviceChecked" (change)="includeServices = $event"></of-service-selector>
|
||||||
|
<!-- <of-meta-service-type-selector [disabled]="!serviceChecked" (change)="includeServices = $event"></of-meta-service-type-selector> -->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="ui-g-12" dir="rtl">
|
<div class="ui-g-12" dir="rtl">
|
||||||
|
|
|
@ -5,14 +5,24 @@
|
||||||
<!-- ZONE node template -->
|
<!-- ZONE node template -->
|
||||||
<ng-template let-node pTemplate="ZONE">
|
<ng-template let-node pTemplate="ZONE">
|
||||||
<div (contextmenu)="showContextMenu($event, node)">
|
<div (contextmenu)="showContextMenu($event, node)">
|
||||||
{{node.label}}
|
<div>
|
||||||
|
{{node.label}}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{{node.data.subLabel}}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
<!-- HOST node template -->
|
<!-- HOST node template -->
|
||||||
<ng-template let-node pTemplate="HOST">
|
<ng-template let-node pTemplate="HOST">
|
||||||
<div (contextmenu)="showContextMenu($event, node)">
|
<div (contextmenu)="showContextMenu($event, node)">
|
||||||
{{node.label}}
|
<div>
|
||||||
|
{{node.label}}
|
||||||
|
</div>
|
||||||
|
<div *ngIf="node.data.subLabel">
|
||||||
|
{{node.data.subLabel}}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,13 @@
|
||||||
import {
|
import {
|
||||||
Component, Input, OnChanges, SimpleChanges, OnInit, ViewChild
|
Component, Input, OnChanges, SimpleChanges, OnInit, ViewChild
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { Infra } from '@overflow/commons-typescript/model/infra';
|
import { InfraService, InfraHost, InfraZone, Infra } from '@overflow/commons-typescript/model/infra';
|
||||||
import { InfraHost as InfraTypeHost } from '@overflow/commons-typescript/model/infra';
|
|
||||||
import { InfraService as InfraTypeService } from '@overflow/commons-typescript/model/infra';
|
|
||||||
import { Observable, of } from 'rxjs';
|
import { Observable, of } from 'rxjs';
|
||||||
import { catchError, exhaustMap, map, tap, take } from 'rxjs/operators';
|
import { catchError, exhaustMap, map, tap, take } from 'rxjs/operators';
|
||||||
import { TreeNode, MenuItem, ContextMenu } from 'primeng/primeng';
|
import { TreeNode, MenuItem, ContextMenu } from 'primeng/primeng';
|
||||||
import { PageParams, Page } from '@overflow/commons-typescript/core/model';
|
import { PageParams, Page } from '@overflow/commons-typescript/core/model';
|
||||||
import { ProbeHost } from '@overflow/commons-typescript';
|
import { ProbeHost, MetaInfraTypeEnum } from '@overflow/commons-typescript';
|
||||||
import { InfraService } from '../service/infra.service';
|
import { InfraService as InfraCRUDService } from '../service/infra.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'of-infra-tree',
|
selector: 'of-infra-tree',
|
||||||
|
@ -36,19 +34,11 @@ export class InfraTreeComponent implements OnInit, OnChanges {
|
||||||
@ViewChild('cmService') cmService: ContextMenu;
|
@ViewChild('cmService') cmService: ContextMenu;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private infraService: InfraService,
|
private infraService: InfraCRUDService,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.zoneNode.push({
|
|
||||||
label: this.probeHost.probe.cidr,
|
|
||||||
type: 'ZONE',
|
|
||||||
data: {
|
|
||||||
},
|
|
||||||
children: this.hostNode,
|
|
||||||
expanded: true
|
|
||||||
});
|
|
||||||
this.initContextMenu();
|
this.initContextMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,8 +63,7 @@ export class InfraTreeComponent implements OnInit, OnChanges {
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnChanges(changes: SimpleChanges): void {
|
ngOnChanges(changes: SimpleChanges): void {
|
||||||
console.log(changes);
|
if (changes['probeHost'].isFirstChange) {
|
||||||
if (changes['probeHost'].isFirstChange ) {
|
|
||||||
this.zoneNode = [];
|
this.zoneNode = [];
|
||||||
this.hostNode = [];
|
this.hostNode = [];
|
||||||
this.getInfras();
|
this.getInfras();
|
||||||
|
@ -111,11 +100,14 @@ export class InfraTreeComponent implements OnInit, OnChanges {
|
||||||
|
|
||||||
generateTreeData(infras: Infra[]) {
|
generateTreeData(infras: Infra[]) {
|
||||||
infras.forEach(infra => {
|
infras.forEach(infra => {
|
||||||
switch (infra.metaInfraType.id) { // TODO: fix to enum
|
switch (infra.metaInfraType.key) {
|
||||||
case 2: // InfraHost
|
case MetaInfraTypeEnum.ZONE:
|
||||||
|
this.addZone(infra);
|
||||||
|
break;
|
||||||
|
case MetaInfraTypeEnum.HOST:
|
||||||
this.addHost(infra);
|
this.addHost(infra);
|
||||||
break;
|
break;
|
||||||
case 7: // InfraService
|
case MetaInfraTypeEnum.HOST:
|
||||||
this.addService(infra);
|
this.addService(infra);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -124,19 +116,36 @@ export class InfraTreeComponent implements OnInit, OnChanges {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
addHost(infraHost: InfraTypeHost) {
|
addZone(infraZone: InfraZone) {
|
||||||
|
this.zoneNode.push({
|
||||||
|
label: infraZone.network + '(' + infraZone.iface + ')',
|
||||||
|
type: 'ZONE',
|
||||||
|
data: {
|
||||||
|
target: infraZone,
|
||||||
|
subLabel: 'Something to display'
|
||||||
|
},
|
||||||
|
children: this.hostNode,
|
||||||
|
expanded: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
addHost(infraHost: InfraHost) {
|
||||||
|
let ipAddr = infraHost.infraHostIPs[0].address.split('/')[0];
|
||||||
|
if (infraHost.infraHostOS && infraHost.infraHostOS.name) {
|
||||||
|
ipAddr += '(' + infraHost.infraHostOS.name + ')';
|
||||||
|
}
|
||||||
this.hostNode.push({
|
this.hostNode.push({
|
||||||
type: 'HOST',
|
type: 'HOST',
|
||||||
label: infraHost.infraHostIPs[0].address,
|
label: ipAddr,
|
||||||
data: {
|
data: {
|
||||||
target: infraHost
|
target: infraHost,
|
||||||
},
|
},
|
||||||
expanded: true,
|
expanded: true,
|
||||||
children: []
|
children: []
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
addService(infraService: InfraTypeService) {
|
addService(infraService: InfraService) {
|
||||||
const idx = this.findHostIndex(infraService);
|
const idx = this.findHostIndex(infraService);
|
||||||
if (idx === -1) {
|
if (idx === -1) {
|
||||||
// this.addHost(infraService.infraHost);
|
// this.addHost(infraService.infraHost);
|
||||||
|
@ -152,13 +161,16 @@ export class InfraTreeComponent implements OnInit, OnChanges {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
findHostIndex(infraService: InfraTypeService): number {
|
findHostIndex(infraService: InfraService): number {
|
||||||
const idx = -1;
|
let idx = -1;
|
||||||
this.hostNode.forEach((node, index) => {
|
this.hostNode.forEach((node, index) => {
|
||||||
// if (node.data.target.id === infraService.infraHost.id) {
|
const infraHost: InfraHost = node.data.target;
|
||||||
// idx = index;
|
for (const infraHostIP of infraHost.infraHostIPs) {
|
||||||
// return;
|
if (infraHostIP.id === infraService.infraHostPort.infraHostIP.id) {
|
||||||
// }
|
idx = index;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
return idx;
|
return idx;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,27 @@
|
||||||
<of-error-message [error]="error$ | async" [closable]="false"></of-error-message>
|
<div *ngIf="!disabled">
|
||||||
<of-block-progressbar [target]="content" [pending]="pending$ | async"></of-block-progressbar>
|
<of-error-message [error]="error$ | async" [closable]="false"></of-error-message>
|
||||||
|
<of-block-progressbar [target]="content" [pending]="pending$ | async"></of-block-progressbar>
|
||||||
|
<p-panel #content [showHeader]="false" class="block-panel">
|
||||||
|
<div class="ui-g" dir="rtl">
|
||||||
|
<button pButton type="button" class="ui-button-secondary ui-button-width-fit ui-s-button" label="Unselect All" style="margin-bottom: 3px;"
|
||||||
|
(click)="onUnselectAll()"></button>
|
||||||
|
<button pButton type="button" class="ui-button-secondary ui-button-width-fit ui-s-button" label="Select All" style="margin-bottom: 3px;"
|
||||||
|
(click)="onSelectAll()"></button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<p-panel *ngIf="probeHost" #content [showHeader]="false" class="block-panel">
|
<p-table selectionMode="multiple" [scrollable]="true" scrollHeight="200px" [value]="metaCrawlers" [(selection)]="includeServices"
|
||||||
<div *ngIf="metaTargetServiceTypes" class="ui-width-100-">
|
dataKey="id" (onRowSelect)="onSelect($event.data)" (onRowUnselect)="onUnselect($event.data)">
|
||||||
<p-orderList [value]="metaTargetServiceTypes" metaKeySelection="false" [listStyle]="{'height':'200px'}" [responsive]="true"
|
<ng-template pTemplate="body" let-rowData let-columns="columns">
|
||||||
filterBy="name" (onSelectionChange)="selected.emit($event.value[0])" class="ui_orderlist_controls_none">
|
<tr [pSelectableRow]="rowData">
|
||||||
<ng-template let-service pTemplate="item">
|
<!-- <td>
|
||||||
<div class="ui-helper-clearfix">
|
<p-tableCheckbox [value]="rowData"></p-tableCheckbox>
|
||||||
<div *ngIf="service.isSupported" style="font-size:14px;margin:0">{{service.name}}</div>
|
</td> -->
|
||||||
</div>
|
<td>
|
||||||
|
<p-tableCheckbox [value]="rowData"></p-tableCheckbox>
|
||||||
|
{{rowData.name}}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</p-orderList>
|
</p-table>
|
||||||
</div>
|
</p-panel>
|
||||||
</p-panel>
|
</div>
|
|
@ -3,7 +3,6 @@ import { Store, select } from '@ngrx/store';
|
||||||
import { Observable, of, Subscription } from 'rxjs';
|
import { Observable, of, Subscription } from 'rxjs';
|
||||||
import { catchError, exhaustMap, map, tap, take } from 'rxjs/operators';
|
import { catchError, exhaustMap, map, tap, take } from 'rxjs/operators';
|
||||||
|
|
||||||
|
|
||||||
import { MetaCrawler, MetaTargetServiceType, MetaInfraTypeEnum, toMetaInfraTypeEnum, toMetaInfraType, MetaTargetType } from '@overflow/commons-typescript/model/meta';
|
import { MetaCrawler, MetaTargetServiceType, MetaInfraTypeEnum, toMetaInfraTypeEnum, toMetaInfraType, MetaTargetType } from '@overflow/commons-typescript/model/meta';
|
||||||
import { MetaCrawlerService } from '../service/meta-crawler.service';
|
import { MetaCrawlerService } from '../service/meta-crawler.service';
|
||||||
import { MetaTargetTypeService } from '../service/meta-target-type.service';
|
import { MetaTargetTypeService } from '../service/meta-target-type.service';
|
||||||
|
@ -15,9 +14,12 @@ import { MetaTargetTypeService } from '../service/meta-target-type.service';
|
||||||
})
|
})
|
||||||
export class MetaServiceTypeSelectorComponent implements OnInit {
|
export class MetaServiceTypeSelectorComponent implements OnInit {
|
||||||
|
|
||||||
@Output() selected = new EventEmitter<MetaCrawler>();
|
includeServices: MetaTargetType[];
|
||||||
|
|
||||||
metaTargetServiceTypes: MetaTargetServiceType[];
|
@Output() change = new EventEmitter<MetaCrawler[]>();
|
||||||
|
@Input() disabled: boolean;
|
||||||
|
|
||||||
|
metaTargetTypes: MetaTargetType[];
|
||||||
pending$: Observable<boolean>;
|
pending$: Observable<boolean>;
|
||||||
error$: Observable<any>;
|
error$: Observable<any>;
|
||||||
|
|
||||||
|
@ -26,25 +28,47 @@ export class MetaServiceTypeSelectorComponent implements OnInit {
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.metaTargetTypeService.readAllByMetaInfraType
|
this.metaTargetTypeService.readAllByMetaInfraTypeKey("SERVICE")
|
||||||
(toMetaInfraType(MetaInfraTypeEnum.SERVICE))
|
|
||||||
.pipe(
|
.pipe(
|
||||||
tap(() => {
|
tap(() => {
|
||||||
this.pending$ = of(true);
|
this.pending$ = of(true);
|
||||||
}),
|
}),
|
||||||
map((metaTargetTypes: MetaTargetType[]) => {
|
map((metaTargetTypes: MetaTargetType[]) => {
|
||||||
this.metaTargetServiceTypes = metaTargetTypes;
|
console.log(metaTargetTypes);
|
||||||
console.log(this.metaTargetServiceTypes);
|
this.metaTargetTypes = metaTargetTypes;
|
||||||
}),
|
}),
|
||||||
catchError(error => {
|
catchError(error => {
|
||||||
this.error$ = of(error);
|
this.error$ = of(error);
|
||||||
return of();
|
return of(null);
|
||||||
}),
|
}),
|
||||||
tap(() => {
|
tap(() => {
|
||||||
this.pending$ = of(false);
|
this.pending$ = of(false);
|
||||||
|
this.includeServices = this.metaTargetTypes.slice();
|
||||||
|
this.change.emit(this.includeServices);
|
||||||
}),
|
}),
|
||||||
take(1),
|
take(1),
|
||||||
).subscribe();
|
).subscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
onSelectAll() {
|
||||||
|
this.includeServices = [];
|
||||||
|
this.metaTargetTypes.forEach((value) => {
|
||||||
|
this.includeServices.push(value);
|
||||||
|
});
|
||||||
|
this.change.emit(this.includeServices);
|
||||||
|
}
|
||||||
|
|
||||||
|
onUnselectAll() {
|
||||||
|
this.includeServices = [];
|
||||||
|
this.change.emit(this.includeServices);
|
||||||
|
}
|
||||||
|
|
||||||
|
onSelect(service: MetaTargetType) {
|
||||||
|
this.change.emit(this.includeServices);
|
||||||
|
}
|
||||||
|
onUnselect(service: MetaTargetType) {
|
||||||
|
this.change.emit(this.includeServices);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ export class MetaTargetTypeService {
|
||||||
return this.rpcService.call<MetaTargetType[]>('MetaTargetTypeService.readAll');
|
return this.rpcService.call<MetaTargetType[]>('MetaTargetTypeService.readAll');
|
||||||
}
|
}
|
||||||
|
|
||||||
public readAllByMetaInfraType(metaInfraType: MetaInfraType): Observable<MetaTargetType[]> {
|
public readAllByMetaInfraTypeKey(key: string): Observable<MetaTargetType[]> {
|
||||||
return this.rpcService.call<MetaTargetType[]>('MetaTargetTypeService.readAllByMetaInfraType', metaInfraType);
|
return this.rpcService.call<MetaTargetType[]>('MetaTargetTypeService.readAllByMetaInfraTypeKey', key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
|
|
||||||
<div class="ui-g-6 nopad" dir="rtl" style="padding-top: 15px">
|
<div class="ui-g-6 nopad" dir="rtl" style="padding-top: 15px">
|
||||||
<button class="ui-button-width-fit" *ngIf="!editMode" pButton type="button" label="Edit" (click)="editMode = true"></button>
|
<button class="ui-button-width-fit" *ngIf="!editMode" pButton type="button" label="Edit" (click)="editMode = true"></button>
|
||||||
<button class="ui-button-width-fit" *ngIf="editMode" pButton type="button" label="Save" (click)="onEditSave()" [disabled]="displayNamedisplayNameErrMsg || descriptionErrMsg"></button>
|
<button class="ui-button-width-fit" *ngIf="editMode" pButton type="button" label="Save" (click)="onEditSave()" [disabled]="namenameErrMsg || descriptionErrMsg"></button>
|
||||||
<button class="ui-button-width-fit" *ngIf="editMode" pButton type="button" label="Cancel" (click)="editMode = false"></button>
|
<button class="ui-button-width-fit" *ngIf="editMode" pButton type="button" label="Cancel" (click)="editMode = false"></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -25,9 +25,8 @@
|
||||||
<div *ngIf="editMode" class="of-key-value-div">
|
<div *ngIf="editMode" class="of-key-value-div">
|
||||||
<span>Name</span>
|
<span>Name</span>
|
||||||
<span class="ng-star-inserted">
|
<span class="ng-star-inserted">
|
||||||
<input #displayName type="text" pInputText value="{{probe.displayName}}" (keyup)="onDisplayNameEditing(displayName.value)"
|
<input #name type="text" pInputText value="{{probe.name}}" (keyup)="onNameEditing(name.value)" />
|
||||||
/>
|
<div *ngIf="nameErrMsg" class="ui-message ui-messages-error ui-corner-all">{{nameErrMsg}}</div>
|
||||||
<div *ngIf="displayNameErrMsg" class="ui-message ui-messages-error ui-corner-all">{{displayNameErrMsg}}</div>
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<of-key-value *ngIf="!editMode" [key]="'Name'" [value]="probe.name"></of-key-value>
|
<of-key-value *ngIf="!editMode" [key]="'Name'" [value]="probe.name"></of-key-value>
|
||||||
|
|
|
@ -22,9 +22,9 @@ export class ProbeGeneralComponent implements OnInit {
|
||||||
pending$: Observable<boolean>;
|
pending$: Observable<boolean>;
|
||||||
|
|
||||||
editMode = false;
|
editMode = false;
|
||||||
displayNameErrMsg: string;
|
nameErrMsg: string;
|
||||||
descriptionErrMsg: string;
|
descriptionErrMsg: string;
|
||||||
displayName: string;
|
name: string;
|
||||||
description: string;
|
description: string;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -61,13 +61,13 @@ export class ProbeGeneralComponent implements OnInit {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onDisplayNameEditing(value: string) {
|
onNameEditing(value: string) {
|
||||||
const msg: string = this.checkValidDisplayName(value);
|
const msg: string = this.checkValidName(value);
|
||||||
if (msg !== null) {
|
if (msg !== null) {
|
||||||
this.displayNameErrMsg = msg;
|
this.nameErrMsg = msg;
|
||||||
} else {
|
} else {
|
||||||
this.displayNameErrMsg = null;
|
this.nameErrMsg = null;
|
||||||
this.displayName = value.trim();
|
this.name = value.trim();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,7 +82,7 @@ export class ProbeGeneralComponent implements OnInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
onEditSave() {
|
onEditSave() {
|
||||||
this.probe.name = this.displayName ? this.displayName : this.probe.name;
|
this.probe.name = this.name ? this.name : this.probe.name;
|
||||||
this.probe.description = this.description ? this.description : this.probe.description;
|
this.probe.description = this.description ? this.description : this.probe.description;
|
||||||
|
|
||||||
this.probeService.modify(this.probe)
|
this.probeService.modify(this.probe)
|
||||||
|
@ -106,7 +106,7 @@ export class ProbeGeneralComponent implements OnInit {
|
||||||
).subscribe();
|
).subscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
checkValidDisplayName(value: string): string | null {
|
checkValidName(value: string): string | null {
|
||||||
if (value.length <= 2 || value.length > 20) {
|
if (value.length <= 2 || value.length > 20) {
|
||||||
return 'displayname length : 3 ~ 20';
|
return 'displayname length : 3 ~ 20';
|
||||||
}
|
}
|
||||||
|
@ -128,4 +128,3 @@ export class ProbeGeneralComponent implements OnInit {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
<div *ngIf="infraHost">
|
<div *ngIf="infraHost">
|
||||||
<div class="ui-g">
|
<div class="ui-g">
|
||||||
<div class="ui-g-12 ui-md-6 ui-key-value">
|
<div class="ui-g-12 ui-md-6 ui-key-value">
|
||||||
<of-key-value [key]="'IP ver'" [value]="infraHost.infraZone.metaIPType.value"></of-key-value>
|
<of-key-value [key]="'IP ver'" [value]="infraHost.infraZone.metaIPType.name"></of-key-value>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="ui-g-12 ui-md-6 ui-key-value">
|
<div class="ui-g-12 ui-md-6 ui-key-value">
|
||||||
|
@ -31,7 +31,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="ui-g-12 ui-md-6 ui-key-value">
|
<div class="ui-g-12 ui-md-6 ui-key-value">
|
||||||
<of-key-value [key]="'Host Type'" [value]="infraHost.metaTargetHostType.value"></of-key-value>
|
<of-key-value [key]="'Host Type'" [value]="infraHost.metaTargetHostType.name"></of-key-value>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="ui-g-12 ui-md-6 ui-key-value">
|
<div class="ui-g-12 ui-md-6 ui-key-value">
|
||||||
|
|
Loading…
Reference in New Issue
Block a user