* temporarily disabled u_run * clean unused deps a bit * disable daemonizing in release mode because wtf * use one async runtime in u_panel * fix cors issues * fix passing cmd from frontend * initial pretty web interface * remove storing errors in db * check target and payload platform * prepare for cross compile to windows binarypull/1/head
parent
a50e6d242f
commit
c25fa780bf
46 changed files with 550 additions and 308 deletions
@ -1,49 +1,11 @@ |
|||||||
<mat-tab-group animationDuration="0ms" mat-align-tabs="center"> |
<mat-tab-group animationDuration="0ms" mat-align-tabs="center"> |
||||||
<mat-tab label="Agents"> |
<mat-tab label="Agents"> |
||||||
<div class="example-container mat-elevation-z8"> |
<agent-table></agent-table> |
||||||
<div class="example-loading-shade" *ngIf="isLoadingResults"> |
</mat-tab> |
||||||
<mat-spinner *ngIf="isLoadingResults"></mat-spinner> |
<mat-tab label="Jobs"> |
||||||
</div> |
<job-table></job-table> |
||||||
|
</mat-tab> |
||||||
<div class="example-table-container"> |
<mat-tab label="Results"> |
||||||
|
<result-table></result-table> |
||||||
<table mat-table [dataSource]="table_data" class="example-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@hostname</th> |
|
||||||
<td mat-cell *matCellDef="let row">{{row.username}}@{{row.hostname}}</td> |
|
||||||
</ng-container> |
|
||||||
|
|
||||||
<ng-container matColumnDef="last_active"> |
|
||||||
<th mat-header-cell *matHeaderCellDef mat-sort-header disableClear> |
|
||||||
Last active |
|
||||||
</th> |
|
||||||
<td mat-cell *matCellDef="let row">{{row.last_active}}</td> |
|
||||||
</ng-container> |
|
||||||
|
|
||||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr> |
|
||||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr> |
|
||||||
</table> |
|
||||||
<button mat-raised-button (click)="fetch_agents()">Refresh</button> |
|
||||||
</div> |
|
||||||
|
|
||||||
<!-- <mat-paginator [length]="resultsLength" [pageSize]="30" aria-label="Select page of GitHub search results"> |
|
||||||
</mat-paginator> --> |
|
||||||
</div> |
|
||||||
|
|
||||||
</mat-tab> |
</mat-tab> |
||||||
<mat-tab label="Jobs"></mat-tab> |
|
||||||
<mat-tab label="Results"></mat-tab> |
|
||||||
</mat-tab-group> |
</mat-tab-group> |
@ -1,82 +1,9 @@ |
|||||||
import { HttpClient } from '@angular/common/http'; |
|
||||||
import { Component, ViewChild, AfterViewInit } from '@angular/core'; |
import { Component, ViewChild, AfterViewInit } from '@angular/core'; |
||||||
import { timer, Observable, of as observableOf } from 'rxjs'; |
|
||||||
import { catchError, map, startWith, switchMap } from 'rxjs/operators'; |
|
||||||
|
|
||||||
interface Agent { |
|
||||||
alias: string | null, |
|
||||||
hostname: string, |
|
||||||
id: string, |
|
||||||
is_root: boolean, |
|
||||||
is_root_allowed: boolean, |
|
||||||
last_active: Date, |
|
||||||
platform: string, |
|
||||||
regtime: Date, |
|
||||||
state: "new" | "active" | "banned", |
|
||||||
token: string | null, |
|
||||||
username: string, |
|
||||||
} |
|
||||||
|
|
||||||
@Component({ |
@Component({ |
||||||
selector: 'app-root', |
selector: 'app-root', |
||||||
templateUrl: './app.component.html', |
templateUrl: './app.component.html', |
||||||
styleUrls: ['./app.component.less'] |
styleUrls: ['./app.component.less'] |
||||||
}) |
}) |
||||||
export class AppComponent implements AfterViewInit { |
export class AppComponent { |
||||||
displayedColumns: string[] = ['id', 'alias', 'username', 'last_active']; |
|
||||||
exampleDatabase!: ExampleHttpDatabase | null; |
|
||||||
|
|
||||||
table_data: Agent[] = []; |
|
||||||
isLoadingResults = true; |
|
||||||
|
|
||||||
constructor(private _httpClient: HttpClient) { } |
|
||||||
|
|
||||||
ngAfterViewInit() { |
|
||||||
this.exampleDatabase = new ExampleHttpDatabase(this._httpClient); |
|
||||||
this.fetch_agents(); |
|
||||||
// If the user changes the sort order, reset back to the first page.
|
|
||||||
//this.sort.sortChange.subscribe(() => (this.paginator.pageIndex = 0));
|
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
fetch_agents() { |
|
||||||
timer(0) |
|
||||||
.pipe( |
|
||||||
startWith({}), |
|
||||||
switchMap(() => { |
|
||||||
this.isLoadingResults = true; |
|
||||||
return this.exampleDatabase!.getAgents().pipe(catchError(() => observableOf(null))); |
|
||||||
}), |
|
||||||
map(data => { |
|
||||||
// Flip flag to show that loading has finished.
|
|
||||||
this.isLoadingResults = false; |
|
||||||
|
|
||||||
if (data === null) { |
|
||||||
return []; |
|
||||||
} |
|
||||||
|
|
||||||
// Only refresh the result length if there is new data. In case of rate
|
|
||||||
// limit errors, we do not want to reset the paginator to zero, as that
|
|
||||||
// would prevent users from re-triggering requests.
|
|
||||||
return data.data; |
|
||||||
}), |
|
||||||
) |
|
||||||
.subscribe(data => { if (typeof data !== 'string') { this.table_data = data } else { alert(`Error: ${data}`) } }); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
interface ServerResponse<T> { |
|
||||||
status: "ok" | "err", |
|
||||||
data: T | string |
|
||||||
} |
|
||||||
|
|
||||||
class ExampleHttpDatabase { |
|
||||||
constructor(private _httpClient: HttpClient) { } |
|
||||||
|
|
||||||
getAgents(): Observable<ServerResponse<Agent[]>> { |
|
||||||
const requestUrl = "/cmd/"; |
|
||||||
const cmd = "agents list"; |
|
||||||
|
|
||||||
return this._httpClient.post<ServerResponse<Agent[]>>(requestUrl, cmd); |
|
||||||
} |
|
||||||
} |
} |
||||||
|
@ -0,0 +1 @@ |
|||||||
|
export * from './services';
|
@ -0,0 +1,15 @@ |
|||||||
|
import { UTCDate } from "."; |
||||||
|
|
||||||
|
export interface AgentModel { |
||||||
|
alias: string | null, |
||||||
|
hostname: string, |
||||||
|
id: string, |
||||||
|
is_root: boolean, |
||||||
|
is_root_allowed: boolean, |
||||||
|
last_active: UTCDate, |
||||||
|
platform: string, |
||||||
|
regtime: UTCDate, |
||||||
|
state: "new" | "active" | "banned", |
||||||
|
token: string | null, |
||||||
|
username: string, |
||||||
|
} |
@ -0,0 +1,8 @@ |
|||||||
|
export * from './agent.model'; |
||||||
|
export * from './result.model'; |
||||||
|
export * from './job.model'; |
||||||
|
|
||||||
|
export interface UTCDate { |
||||||
|
secs_since_epoch: number, |
||||||
|
nanos_since_epoch: number |
||||||
|
} |
@ -0,0 +1,8 @@ |
|||||||
|
export interface JobModel { |
||||||
|
alias: string, |
||||||
|
argv: string, |
||||||
|
id: string, |
||||||
|
exec_type: string, |
||||||
|
platform: string, |
||||||
|
payload: Uint8Array | null, |
||||||
|
} |
@ -0,0 +1,13 @@ |
|||||||
|
import { UTCDate } from "."; |
||||||
|
|
||||||
|
export interface ResultModel { |
||||||
|
agent_id: string, |
||||||
|
alias: string, |
||||||
|
created: UTCDate, |
||||||
|
id: string, |
||||||
|
job_id: string, |
||||||
|
result: Uint8Array, |
||||||
|
state: "Queued" | "Running" | "Finished", |
||||||
|
retcode: number | null, |
||||||
|
updated: UTCDate, |
||||||
|
} |
@ -0,0 +1,43 @@ |
|||||||
|
import { Injectable } from '@angular/core'; |
||||||
|
import { environment } from 'src/environments/environment'; |
||||||
|
import { HttpClient } from '@angular/common/http'; |
||||||
|
import { Observable } from 'rxjs'; |
||||||
|
|
||||||
|
interface ServerResponse<T> { |
||||||
|
status: "ok" | "err", |
||||||
|
data: T | string |
||||||
|
} |
||||||
|
|
||||||
|
export class ApiTableService<T> { |
||||||
|
area: string; |
||||||
|
|
||||||
|
constructor(private http: HttpClient, area: string) { |
||||||
|
this.area = area; |
||||||
|
} |
||||||
|
|
||||||
|
requestUrl = `${environment.server}/cmd/`; |
||||||
|
|
||||||
|
req<R>(cmd: string): Observable<ServerResponse<R>> { |
||||||
|
return this.http.post<ServerResponse<R>>(this.requestUrl, cmd); |
||||||
|
} |
||||||
|
|
||||||
|
getOne(id: string): Observable<ServerResponse<T>> { |
||||||
|
return this.req(`${this.area} read ${id}`) |
||||||
|
} |
||||||
|
|
||||||
|
getMany(): Observable<ServerResponse<T[]>> { |
||||||
|
return this.req(`${this.area} read`) |
||||||
|
} |
||||||
|
|
||||||
|
update(item: T): Observable<ServerResponse<void>> { |
||||||
|
return this.req(`${this.area} update '${JSON.stringify(item)}'`) |
||||||
|
} |
||||||
|
|
||||||
|
delete(id: string): Observable<ServerResponse<void>> { |
||||||
|
return this.req(`${this.area} delete ${id}`) |
||||||
|
} |
||||||
|
|
||||||
|
create(item: string): Observable<ServerResponse<void>> { |
||||||
|
return this.req(`${this.area} create ${item}`) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1 @@ |
|||||||
|
export * from './api.service' |
@ -0,0 +1,41 @@ |
|||||||
|
import { Component, OnInit } from '@angular/core'; |
||||||
|
import { TablesComponent } from './table.component'; |
||||||
|
import { AgentModel } from '../models'; |
||||||
|
|
||||||
|
@Component({ |
||||||
|
selector: 'agent-table', |
||||||
|
templateUrl: './table.component.html', |
||||||
|
styleUrls: ['./table.component.less'] |
||||||
|
}) |
||||||
|
export class AgentComponent extends TablesComponent<AgentModel> { |
||||||
|
area = 'agents' as const; |
||||||
|
|
||||||
|
columns = [ |
||||||
|
{ |
||||||
|
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) => `${cell.last_active.secs_since_epoch}` |
||||||
|
}, |
||||||
|
] |
||||||
|
displayedColumns = this.columns.map((c) => c.def); |
||||||
|
} |
@ -0,0 +1,3 @@ |
|||||||
|
export * from './agent.component'; |
||||||
|
export * from './job.component'; |
||||||
|
export * from './result.component'; |
@ -0,0 +1,46 @@ |
|||||||
|
import { Component, OnInit } from '@angular/core'; |
||||||
|
import { TablesComponent } from './table.component'; |
||||||
|
import { JobModel } from '../models'; |
||||||
|
|
||||||
|
@Component({ |
||||||
|
selector: 'job-table', |
||||||
|
templateUrl: './table.component.html', |
||||||
|
styleUrls: ['./table.component.less'] |
||||||
|
}) |
||||||
|
export class JobComponent extends TablesComponent<JobModel> { |
||||||
|
area = 'jobs' as const; |
||||||
|
|
||||||
|
columns = [ |
||||||
|
{ |
||||||
|
def: "id", |
||||||
|
name: "ID", |
||||||
|
cell: (cell: JobModel) => `${cell.id}` |
||||||
|
}, |
||||||
|
{ |
||||||
|
def: "alias", |
||||||
|
name: "Alias", |
||||||
|
cell: (cell: JobModel) => `${cell.alias}` |
||||||
|
}, |
||||||
|
{ |
||||||
|
def: "argv", |
||||||
|
name: "Cmd-line args", |
||||||
|
cell: (cell: JobModel) => `${cell.argv}` |
||||||
|
}, |
||||||
|
{ |
||||||
|
def: "platform", |
||||||
|
name: "Platform", |
||||||
|
cell: (cell: JobModel) => `${cell.platform}` |
||||||
|
}, |
||||||
|
{ |
||||||
|
def: "payload", |
||||||
|
name: "Payload", |
||||||
|
cell: (cell: JobModel) => `${cell.payload}` |
||||||
|
}, |
||||||
|
{ |
||||||
|
def: "etype", |
||||||
|
name: "Type", |
||||||
|
cell: (cell: JobModel) => `${cell.exec_type}` |
||||||
|
}, |
||||||
|
] |
||||||
|
displayedColumns = this.columns.map((c) => c.def); |
||||||
|
} |
@ -0,0 +1,46 @@ |
|||||||
|
import { Component, OnInit } from '@angular/core'; |
||||||
|
import { TablesComponent } from './table.component'; |
||||||
|
import { ResultModel } from '../models'; |
||||||
|
|
||||||
|
@Component({ |
||||||
|
selector: 'result-table', |
||||||
|
templateUrl: './table.component.html', |
||||||
|
styleUrls: ['./table.component.less'] |
||||||
|
}) |
||||||
|
export class ResultComponent extends TablesComponent<ResultModel> { |
||||||
|
area = 'map' as const; |
||||||
|
|
||||||
|
columns = [ |
||||||
|
{ |
||||||
|
def: "id", |
||||||
|
name: "ID", |
||||||
|
cell: (cell: ResultModel) => `${cell.id}` |
||||||
|
}, |
||||||
|
{ |
||||||
|
def: "alias", |
||||||
|
name: "Alias", |
||||||
|
cell: (cell: ResultModel) => `${cell.alias}` |
||||||
|
}, |
||||||
|
{ |
||||||
|
def: "agent_id", |
||||||
|
name: "Agent ID", |
||||||
|
cell: (cell: ResultModel) => `${cell.agent_id}` |
||||||
|
}, |
||||||
|
{ |
||||||
|
def: "job_id", |
||||||
|
name: "Job ID", |
||||||
|
cell: (cell: ResultModel) => `${cell.job_id}` |
||||||
|
}, |
||||||
|
{ |
||||||
|
def: "state", |
||||||
|
name: "State", |
||||||
|
cell: (cell: ResultModel) => `${cell.state} `.concat((cell.state === "Finished") ? `(${cell.retcode})` : '') |
||||||
|
}, |
||||||
|
{ |
||||||
|
def: "last_updated", |
||||||
|
name: "Last updated", |
||||||
|
cell: (cell: ResultModel) => `${cell.updated.secs_since_epoch}` |
||||||
|
}, |
||||||
|
] |
||||||
|
displayedColumns = this.columns.map((c) => c.def); |
||||||
|
} |
@ -0,0 +1,36 @@ |
|||||||
|
<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 *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,24 @@ |
|||||||
|
.data-table { |
||||||
|
width: 100%; |
||||||
|
} |
||||||
|
|
||||||
|
.table-container { |
||||||
|
margin: 50px; |
||||||
|
} |
||||||
|
|
||||||
|
.loading-shade { |
||||||
|
position: absolute; |
||||||
|
top: 0; |
||||||
|
left: 0; |
||||||
|
bottom: 56px; |
||||||
|
right: 0; |
||||||
|
background: rgba(0, 0, 0, 0.15); |
||||||
|
z-index: 1; |
||||||
|
display: flex; |
||||||
|
align-items: center; |
||||||
|
justify-content: center; |
||||||
|
} |
||||||
|
|
||||||
|
#refresh_btn { |
||||||
|
margin-left: 10px; |
||||||
|
} |
@ -0,0 +1,66 @@ |
|||||||
|
import { Component, OnInit, Directive } from '@angular/core'; |
||||||
|
import { timer, of as observableOf } from 'rxjs'; |
||||||
|
import { catchError, map, startWith, switchMap } from 'rxjs/operators'; |
||||||
|
import { HttpClient } from '@angular/common/http'; |
||||||
|
import { ApiTableService } from '../'; |
||||||
|
import { MatTableDataSource } from '@angular/material/table'; |
||||||
|
|
||||||
|
@Directive() |
||||||
|
export abstract class TablesComponent<T> implements OnInit { |
||||||
|
abstract area: "agents" | "jobs" | "map"; |
||||||
|
data_source!: ApiTableService<T> | null; |
||||||
|
table_data!: MatTableDataSource<T>; |
||||||
|
|
||||||
|
isLoadingResults = true; |
||||||
|
|
||||||
|
constructor(private _httpClient: HttpClient) { |
||||||
|
this.table_data = new MatTableDataSource; |
||||||
|
} |
||||||
|
|
||||||
|
ngOnInit() { |
||||||
|
this.data_source = new ApiTableService(this._httpClient, this.area); |
||||||
|
this.fetch_many(); |
||||||
|
// If the user changes the sort order, reset back to the first page.
|
||||||
|
//this.sort.sortChange.subscribe(() => (this.paginator.pageIndex = 0));
|
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
fetch_many() { |
||||||
|
timer(0) |
||||||
|
.pipe( |
||||||
|
startWith({}), |
||||||
|
switchMap(() => { |
||||||
|
this.isLoadingResults = true; |
||||||
|
return this.data_source!.getMany().pipe(catchError(() => observableOf(null))); |
||||||
|
}), |
||||||
|
map(data => { |
||||||
|
this.isLoadingResults = false; |
||||||
|
|
||||||
|
if (data === null) { |
||||||
|
return []; |
||||||
|
} |
||||||
|
|
||||||
|
// Only refresh the result length if there is new data. In case of rate
|
||||||
|
// limit errors, we do not want to reset the paginator to zero, as that
|
||||||
|
// would prevent users from re-triggering requests.
|
||||||
|
return data.data; |
||||||
|
}), |
||||||
|
) |
||||||
|
.subscribe(data => { if (typeof data !== 'string') { this.table_data.data = data } else { alert(`Error: ${data}`) } }); |
||||||
|
} |
||||||
|
|
||||||
|
apply_filter(event: Event) { |
||||||
|
const filterValue = (event.target as HTMLInputElement).value; |
||||||
|
this.table_data.filter = filterValue.trim().toLowerCase(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
abstract columns: ColumnDef<T>[]; |
||||||
|
abstract displayedColumns: string[]; |
||||||
|
} |
||||||
|
|
||||||
|
type ColumnDef<C> = { |
||||||
|
def: string, |
||||||
|
name: string, |
||||||
|
cell: (cell: C) => string |
||||||
|
} |
@ -1,3 +1,4 @@ |
|||||||
export const environment = { |
export const environment = { |
||||||
production: true |
production: true, |
||||||
|
server: "" |
||||||
}; |
}; |
||||||
|
@ -0,0 +1,22 @@ |
|||||||
|
use crate::helpers::ENV; |
||||||
|
use u_lib::config::MASTER_PORT; |
||||||
|
|
||||||
|
#[tokio::test] |
||||||
|
async fn test_non_auth_connection_dropped() { |
||||||
|
let client = reqwest::ClientBuilder::new() |
||||||
|
.danger_accept_invalid_certs(true) |
||||||
|
.build() |
||||||
|
.unwrap(); |
||||||
|
match client |
||||||
|
.get(format!("https://{}:{}", &ENV.u_server, MASTER_PORT)) |
||||||
|
.send() |
||||||
|
.await |
||||||
|
{ |
||||||
|
Err(e) => { |
||||||
|
let err = e.to_string(); |
||||||
|
println!("captured err: {err}"); |
||||||
|
assert!(err.contains("certificate required")); |
||||||
|
} |
||||||
|
_ => panic!("no error occured on foreign client connection"), |
||||||
|
} |
||||||
|
} |
@ -1 +1,2 @@ |
|||||||
mod behaviour; |
mod behaviour; |
||||||
|
mod connection; |
||||||
|
@ -1,35 +0,0 @@ |
|||||||
use crate::models::schema::*; |
|
||||||
use diesel::{Insertable, Queryable}; |
|
||||||
use serde::{Deserialize, Serialize}; |
|
||||||
use std::time::SystemTime; |
|
||||||
use uuid::Uuid; |
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, Queryable, Insertable, PartialEq)] |
|
||||||
#[table_name = "errors"] |
|
||||||
pub struct AgentError { |
|
||||||
pub agent_id: Uuid, |
|
||||||
pub created: SystemTime, |
|
||||||
pub id: Uuid, |
|
||||||
pub msg: String, |
|
||||||
} |
|
||||||
|
|
||||||
impl AgentError { |
|
||||||
pub fn from_msg(msg: impl Into<String>, agent_id: Uuid) -> Self { |
|
||||||
AgentError { |
|
||||||
agent_id, |
|
||||||
msg: msg.into(), |
|
||||||
..Default::default() |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
impl Default for AgentError { |
|
||||||
fn default() -> Self { |
|
||||||
Self { |
|
||||||
agent_id: Uuid::new_v4(), |
|
||||||
created: SystemTime::now(), |
|
||||||
id: Uuid::new_v4(), |
|
||||||
msg: String::new(), |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -1,6 +1,5 @@ |
|||||||
mod agent; |
mod agent; |
||||||
mod error; |
|
||||||
mod jobs; |
mod jobs; |
||||||
pub mod schema; |
pub mod schema; |
||||||
|
|
||||||
pub use crate::models::{agent::*, error::*, jobs::*}; |
pub use crate::models::{agent::*, jobs::*}; |
||||||
|
@ -0,0 +1,38 @@ |
|||||||
|
use guess_host_triple::guess_host_triple; |
||||||
|
use platforms::{Platform as _Platform, PlatformReq}; |
||||||
|
use serde::Deserialize; |
||||||
|
use std::str::FromStr; |
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)] |
||||||
|
pub struct Platform(String); |
||||||
|
|
||||||
|
impl Platform { |
||||||
|
pub fn current() -> Platform { |
||||||
|
Self(guess_host_triple().unwrap_or("unknown").to_string()) |
||||||
|
} |
||||||
|
|
||||||
|
pub fn matches(&self, pf: impl AsRef<str>) -> bool { |
||||||
|
match PlatformReq::from_str(pf.as_ref()) { |
||||||
|
Ok(p) => p.matches(&_Platform::find(&self.0).unwrap()), |
||||||
|
Err(_) => false, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
pub fn check(&self) -> bool { |
||||||
|
PlatformReq::from_str(&self.0).is_ok() |
||||||
|
} |
||||||
|
|
||||||
|
pub fn into_string(self) -> String { |
||||||
|
self.0 |
||||||
|
} |
||||||
|
|
||||||
|
pub fn any() -> Platform { |
||||||
|
Self(String::from("*")) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl Default for Platform { |
||||||
|
fn default() -> Self { |
||||||
|
Self::any() |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue