back: - fix broken deps - add connection check to panel - pretty-print panel errors - touch timestamps of updates - simplify u_server api - simplify u_lib api front: - add routing - use async instead of rxjs - define tables' columns in templates, not in ts - use different templates for different tablespull/1/head
parent
7b59031bfe
commit
544c07cf8d
31 changed files with 427 additions and 276 deletions
@ -1,11 +1,6 @@ |
|||||||
<mat-tab-group animationDuration="0ms" mat-align-tabs="center"> |
<nav mat-tab-nav-bar animationDuration="0ms" mat-align-tabs="center"> |
||||||
<mat-tab label="Agents"> |
<a mat-tab-link routerLink="/agents" routerLinkActive="active" ariaCurrentWhenActive="page">Agents</a> |
||||||
<agent-table></agent-table> |
<a mat-tab-link routerLink="/jobs" routerLinkActive="active" ariaCurrentWhenActive="page">Jobs</a> |
||||||
</mat-tab> |
<a mat-tab-link routerLink="/results" routerLinkActive="active" ariaCurrentWhenActive="page">Results</a> |
||||||
<mat-tab label="Jobs"> |
</nav> |
||||||
<job-table></job-table> |
<router-outlet></router-outlet> |
||||||
</mat-tab> |
|
||||||
<mat-tab label="Results"> |
|
||||||
<result-table></result-table> |
|
||||||
</mat-tab> |
|
||||||
</mat-tab-group> |
|
@ -0,0 +1,68 @@ |
|||||||
|
<div class="mat-elevation-z8"> |
||||||
|
|
||||||
|
<div class="table-container"> |
||||||
|
<div class="loading-shade" *ngIf="isLoadingResults"> |
||||||
|
<mat-spinner *ngIf="isLoadingResults"></mat-spinner> |
||||||
|
</div> |
||||||
|
<mat-form-field appearance="standard"> |
||||||
|
<mat-label>Filter</mat-label> |
||||||
|
<input matInput (keyup)="apply_filter($event)" #input> |
||||||
|
</mat-form-field> |
||||||
|
<button id="refresh_btn" mat-raised-button color="primary" (click)="fetchMany()">Refresh</button> |
||||||
|
|
||||||
|
<table mat-table [dataSource]="table_data" class="data-table" matSort matSortActive="id" matSortDisableClear |
||||||
|
matSortDirection="desc"> |
||||||
|
|
||||||
|
<ng-container matColumnDef="id"> |
||||||
|
<th mat-header-cell *matHeaderCellDef>ID</th> |
||||||
|
<td mat-cell *matCellDef="let row"> |
||||||
|
{{row.id}} |
||||||
|
</td> |
||||||
|
</ng-container> |
||||||
|
|
||||||
|
<ng-container matColumnDef="alias"> |
||||||
|
<th mat-header-cell *matHeaderCellDef>Alias</th> |
||||||
|
<td mat-cell *matCellDef="let row"> |
||||||
|
{{row.alias}} |
||||||
|
</td> |
||||||
|
</ng-container> |
||||||
|
|
||||||
|
<ng-container matColumnDef="username"> |
||||||
|
<th mat-header-cell *matHeaderCellDef>User</th> |
||||||
|
<td mat-cell *matCellDef="let row"> |
||||||
|
{{row.username}} |
||||||
|
</td> |
||||||
|
</ng-container> |
||||||
|
|
||||||
|
<ng-container matColumnDef="hostname"> |
||||||
|
<th mat-header-cell *matHeaderCellDef>Hostname</th> |
||||||
|
<td mat-cell *matCellDef="let row"> |
||||||
|
{{row.hostname}} |
||||||
|
</td> |
||||||
|
</ng-container> |
||||||
|
|
||||||
|
<ng-container matColumnDef="last_active"> |
||||||
|
<th mat-header-cell *matHeaderCellDef>Last active</th> |
||||||
|
<td mat-cell *matCellDef="let row"> |
||||||
|
{{row.last_active.secs_since_epoch * 1000 | date:'long'}} |
||||||
|
</td> |
||||||
|
</ng-container> |
||||||
|
|
||||||
|
<ng-container matColumnDef="actions"> |
||||||
|
<th mat-header-cell *matHeaderCellDef></th> |
||||||
|
<td mat-cell *matCellDef="let row"> |
||||||
|
<button mat-raised-button routerLink='' [queryParams]="{id: row.id}">Info</button> |
||||||
|
</td> |
||||||
|
</ng-container> |
||||||
|
|
||||||
|
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr> |
||||||
|
<tr mat-row class="data-table-row" *matRowDef="let row; columns: displayedColumns;"></tr> |
||||||
|
<tr class="mat-row" *matNoDataRow> |
||||||
|
<td class="mat-cell">No data</td> |
||||||
|
</tr> |
||||||
|
</table> |
||||||
|
</div> |
||||||
|
|
||||||
|
<!-- <mat-paginator [length]="resultsLength" [pageSize]="30" aria-label="Select page of GitHub search results"> |
||||||
|
</mat-paginator> --> |
||||||
|
</div> |
@ -1,57 +1,62 @@ |
|||||||
import { Component, OnInit } from '@angular/core'; |
import { Component, OnDestroy, OnInit } from '@angular/core'; |
||||||
import { TablesComponent } from './table.component'; |
import { TablesComponent } from './table.component'; |
||||||
import { AgentModel } from '../models'; |
import { AgentModel } from '../models'; |
||||||
import { AgentInfoDialogComponent } from './dialogs/agent_info.component'; |
import { AgentInfoDialogComponent } from './dialogs/agent_info.component'; |
||||||
import { HttpClient } from '@angular/common/http'; |
import { HttpClient } from '@angular/common/http'; |
||||||
import { MatDialog } from '@angular/material/dialog'; |
import { MatDialog } from '@angular/material/dialog'; |
||||||
import { epochToStr } from '../utils'; |
import { epochToStr } from '../utils'; |
||||||
|
import { ActivatedRoute, Router } from '@angular/router'; |
||||||
|
import { emitErr } from '../utils'; |
||||||
|
import { Subscription } from 'rxjs'; |
||||||
|
|
||||||
@Component({ |
@Component({ |
||||||
selector: 'agent-table', |
selector: 'agent-table', |
||||||
templateUrl: './table.component.html', |
templateUrl: './agent.component.html', |
||||||
styleUrls: ['./table.component.less'] |
styleUrls: ['./table.component.less'] |
||||||
}) |
}) |
||||||
export class AgentComponent extends TablesComponent<AgentModel> { |
export class AgentComponent extends TablesComponent<AgentModel> implements OnDestroy, OnInit { |
||||||
|
|
||||||
constructor(public override _httpClient: HttpClient, public override info_dlg: MatDialog) { |
dialogSubscr!: Subscription; |
||||||
|
area = 'agents' as const; |
||||||
|
|
||||||
|
displayedColumns = ['id', 'alias', 'username', 'hostname', 'last_active', 'actions'] |
||||||
|
|
||||||
|
constructor( |
||||||
|
public override _httpClient: HttpClient, |
||||||
|
public override info_dlg: MatDialog, |
||||||
|
public route: ActivatedRoute, |
||||||
|
public router: Router |
||||||
|
) { |
||||||
super(_httpClient, info_dlg); |
super(_httpClient, info_dlg); |
||||||
} |
} |
||||||
|
|
||||||
area = 'agents' as const; |
override ngOnInit(): void { |
||||||
|
super.ngOnInit() |
||||||
|
this.dialogSubscr = this.route.queryParams.subscribe(params => { |
||||||
|
const id = params['id'] |
||||||
|
if (id) { |
||||||
|
this.show_item_dialog(id); |
||||||
|
} |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
show_item_dialog(id: string) { |
||||||
|
this.data_source!.getOne(id).then(resp => { |
||||||
|
if (resp.status === 'ok') { |
||||||
|
const dialog = this.info_dlg.open(AgentInfoDialogComponent, { |
||||||
|
data: resp.data as AgentModel |
||||||
|
}); |
||||||
|
|
||||||
|
dialog.afterClosed().subscribe(result => { |
||||||
|
this.router.navigate(['.'], { relativeTo: this.route }) |
||||||
|
}) |
||||||
|
} else { |
||||||
|
emitErr(resp.data) |
||||||
|
} |
||||||
|
}).catch(emitErr) |
||||||
|
} |
||||||
|
|
||||||
columns = [ |
ngOnDestroy(): void { |
||||||
{ |
this.dialogSubscr.unsubscribe() |
||||||
def: "id", |
|
||||||
name: "ID", |
|
||||||
cell: (cell: AgentModel) => cell.id |
|
||||||
}, |
|
||||||
{ |
|
||||||
def: "alias", |
|
||||||
name: "Alias", |
|
||||||
cell: (cell: AgentModel) => cell.alias ?? "" |
|
||||||
}, |
|
||||||
{ |
|
||||||
def: "username", |
|
||||||
name: "User", |
|
||||||
cell: (cell: AgentModel) => cell.username |
|
||||||
}, |
|
||||||
{ |
|
||||||
def: "hostname", |
|
||||||
name: "Host", |
|
||||||
cell: (cell: AgentModel) => cell.hostname |
|
||||||
}, |
|
||||||
{ |
|
||||||
def: "last_active", |
|
||||||
name: "Last active", |
|
||||||
cell: (cell: AgentModel) => epochToStr(cell.last_active.secs_since_epoch) |
|
||||||
}, |
|
||||||
] |
|
||||||
|
|
||||||
displayedColumns = this.columns.map((c) => c.def); |
|
||||||
|
|
||||||
show_item_dialog(obj: AgentModel) { |
|
||||||
const dialog = this.info_dlg.open(AgentInfoDialogComponent, { |
|
||||||
data: obj |
|
||||||
}); |
|
||||||
} |
} |
||||||
} |
} |
||||||
|
@ -0,0 +1,68 @@ |
|||||||
|
<div class="mat-elevation-z8"> |
||||||
|
|
||||||
|
<div class="table-container"> |
||||||
|
<div class="loading-shade" *ngIf="isLoadingResults"> |
||||||
|
<mat-spinner *ngIf="isLoadingResults"></mat-spinner> |
||||||
|
</div> |
||||||
|
<mat-form-field appearance="standard"> |
||||||
|
<mat-label>Filter</mat-label> |
||||||
|
<input matInput (keyup)="apply_filter($event)" #input> |
||||||
|
</mat-form-field> |
||||||
|
<button id="refresh_btn" mat-raised-button color="primary" (click)="fetchMany()">Refresh</button> |
||||||
|
|
||||||
|
<table mat-table [dataSource]="table_data" class="data-table" matSort matSortActive="id" matSortDisableClear |
||||||
|
matSortDirection="desc"> |
||||||
|
|
||||||
|
<ng-container matColumnDef="id"> |
||||||
|
<th mat-header-cell *matHeaderCellDef>ID</th> |
||||||
|
<td mat-cell *matCellDef="let row"> |
||||||
|
{{row.id}} |
||||||
|
</td> |
||||||
|
</ng-container> |
||||||
|
|
||||||
|
<ng-container matColumnDef="alias"> |
||||||
|
<th mat-header-cell *matHeaderCellDef>Alias</th> |
||||||
|
<td mat-cell *matCellDef="let row"> |
||||||
|
{{row.alias}} |
||||||
|
</td> |
||||||
|
</ng-container> |
||||||
|
|
||||||
|
<ng-container matColumnDef="argv"> |
||||||
|
<th mat-header-cell *matHeaderCellDef>Cmd-line args</th> |
||||||
|
<td mat-cell *matCellDef="let row"> |
||||||
|
{{row.argv}} |
||||||
|
</td> |
||||||
|
</ng-container> |
||||||
|
|
||||||
|
<ng-container matColumnDef="platform"> |
||||||
|
<th mat-header-cell *matHeaderCellDef>Platform</th> |
||||||
|
<td mat-cell *matCellDef="let row"> |
||||||
|
{{row.platform}} |
||||||
|
</td> |
||||||
|
</ng-container> |
||||||
|
|
||||||
|
<ng-container matColumnDef="payload"> |
||||||
|
<th mat-header-cell *matHeaderCellDef>Payload</th> |
||||||
|
<td mat-cell *matCellDef="let row"> |
||||||
|
{{row.payload}} |
||||||
|
</td> |
||||||
|
</ng-container> |
||||||
|
|
||||||
|
<ng-container matColumnDef="exec_type"> |
||||||
|
<th mat-header-cell *matHeaderCellDef>Type</th> |
||||||
|
<td mat-cell *matCellDef="let row"> |
||||||
|
{{row.exec_type}} |
||||||
|
</td> |
||||||
|
</ng-container> |
||||||
|
|
||||||
|
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr> |
||||||
|
<tr mat-row class="data-table-row" *matRowDef="let row; columns: displayedColumns;"></tr> |
||||||
|
<tr class="mat-row" *matNoDataRow> |
||||||
|
<td class="mat-cell">No data</td> |
||||||
|
</tr> |
||||||
|
</table> |
||||||
|
</div> |
||||||
|
|
||||||
|
<!-- <mat-paginator [length]="resultsLength" [pageSize]="30" aria-label="Select page of GitHub search results"> |
||||||
|
</mat-paginator> --> |
||||||
|
</div> |
@ -0,0 +1,68 @@ |
|||||||
|
<div class="mat-elevation-z8"> |
||||||
|
|
||||||
|
<div class="table-container"> |
||||||
|
<div class="loading-shade" *ngIf="isLoadingResults"> |
||||||
|
<mat-spinner *ngIf="isLoadingResults"></mat-spinner> |
||||||
|
</div> |
||||||
|
<mat-form-field appearance="standard"> |
||||||
|
<mat-label>Filter</mat-label> |
||||||
|
<input matInput (keyup)="apply_filter($event)" #input> |
||||||
|
</mat-form-field> |
||||||
|
<button id="refresh_btn" mat-raised-button color="primary" (click)="fetchMany()">Refresh</button> |
||||||
|
|
||||||
|
<table mat-table [dataSource]="table_data" class="data-table" matSort matSortActive="id" matSortDisableClear |
||||||
|
matSortDirection="desc"> |
||||||
|
|
||||||
|
<ng-container matColumnDef="id"> |
||||||
|
<th mat-header-cell *matHeaderCellDef>ID</th> |
||||||
|
<td mat-cell *matCellDef="let row"> |
||||||
|
{{row.id}} |
||||||
|
</td> |
||||||
|
</ng-container> |
||||||
|
|
||||||
|
<ng-container matColumnDef="alias"> |
||||||
|
<th mat-header-cell *matHeaderCellDef>Alias</th> |
||||||
|
<td mat-cell *matCellDef="let row"> |
||||||
|
{{row.alias}} |
||||||
|
</td> |
||||||
|
</ng-container> |
||||||
|
|
||||||
|
<ng-container matColumnDef="agent_id"> |
||||||
|
<th mat-header-cell *matHeaderCellDef>Agent</th> |
||||||
|
<td mat-cell *matCellDef="let row"> |
||||||
|
<a routerLink='/agents' [queryParams]="{id: row.agent_id}">{{row.agent_id}}</a> |
||||||
|
</td> |
||||||
|
</ng-container> |
||||||
|
|
||||||
|
<ng-container matColumnDef="job_id"> |
||||||
|
<th mat-header-cell *matHeaderCellDef>Job</th> |
||||||
|
<td mat-cell *matCellDef="let row"> |
||||||
|
{{row.job_id}} |
||||||
|
</td> |
||||||
|
</ng-container> |
||||||
|
|
||||||
|
<ng-container matColumnDef="state"> |
||||||
|
<th mat-header-cell *matHeaderCellDef>State</th> |
||||||
|
<td mat-cell *matCellDef="let row"> |
||||||
|
{{row.state}} {{(row.state === "Finished") ? '(' + row.retcode + ')' : ''}} |
||||||
|
</td> |
||||||
|
</ng-container> |
||||||
|
|
||||||
|
<ng-container matColumnDef="last_updated"> |
||||||
|
<th mat-header-cell *matHeaderCellDef>ID</th> |
||||||
|
<td mat-cell *matCellDef="let row"> |
||||||
|
{{row.updated.secs_since_epoch * 1000| date:'long'}} |
||||||
|
</td> |
||||||
|
</ng-container> |
||||||
|
|
||||||
|
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr> |
||||||
|
<tr mat-row class="data-table-row" *matRowDef="let row; columns: displayedColumns;"></tr> |
||||||
|
<tr class="mat-row" *matNoDataRow> |
||||||
|
<td class="mat-cell">No data</td> |
||||||
|
</tr> |
||||||
|
</table> |
||||||
|
</div> |
||||||
|
|
||||||
|
<!-- <mat-paginator [length]="resultsLength" [pageSize]="30" aria-label="Select page of GitHub search results"> |
||||||
|
</mat-paginator> --> |
||||||
|
</div> |
@ -1,37 +0,0 @@ |
|||||||
<div class="mat-elevation-z8"> |
|
||||||
|
|
||||||
<div class="table-container"> |
|
||||||
<div class="loading-shade" *ngIf="isLoadingResults"> |
|
||||||
<mat-spinner *ngIf="isLoadingResults"></mat-spinner> |
|
||||||
</div> |
|
||||||
<mat-form-field appearance="standard"> |
|
||||||
<mat-label>Filter</mat-label> |
|
||||||
<input matInput (keyup)="apply_filter($event)" #input> |
|
||||||
</mat-form-field> |
|
||||||
<button id="refresh_btn" mat-raised-button color="primary" (click)="fetch_many()">Refresh</button> |
|
||||||
|
|
||||||
<table mat-table [dataSource]="table_data" class="data-table" matSort matSortActive="id" matSortDisableClear |
|
||||||
matSortDirection="desc"> |
|
||||||
|
|
||||||
<ng-container *ngFor="let column of columns" [matColumnDef]="column.def"> |
|
||||||
<th mat-header-cell *matHeaderCellDef> |
|
||||||
{{column.name}} |
|
||||||
</th> |
|
||||||
<td mat-cell *matCellDef="let row"> |
|
||||||
{{column.cell(row)}} |
|
||||||
</td> |
|
||||||
</ng-container> |
|
||||||
|
|
||||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr> |
|
||||||
<tr mat-row class="data-table-row" *matRowDef="let row; columns: displayedColumns;" |
|
||||||
(click)="show_item_dialog(row)"></tr> |
|
||||||
|
|
||||||
<tr class="mat-row" *matNoDataRow> |
|
||||||
<td class="mat-cell">No data</td> |
|
||||||
</tr> |
|
||||||
</table> |
|
||||||
</div> |
|
||||||
|
|
||||||
<!-- <mat-paginator [length]="resultsLength" [pageSize]="30" aria-label="Select page of GitHub search results"> |
|
||||||
</mat-paginator> --> |
|
||||||
</div> |
|
@ -1,3 +1,7 @@ |
|||||||
export function epochToStr(epoch: number): string { |
export function epochToStr(epoch: number): string { |
||||||
return new Date(epoch * 1000).toLocaleString('en-GB') |
return new Date(epoch * 1000).toLocaleString('en-GB') |
||||||
} |
} |
||||||
|
|
||||||
|
export function emitErr(e: any) { |
||||||
|
alert(e) |
||||||
|
} |
Loading…
Reference in new issue