* removed api client macro * fixed passing --release mode in cargo make * optimized integration tests * added logging module (tracing) * allow u_panel to alter entries * reworked u_panel args (CRUD) * improved db hooks * started implementing web-interface ** incapsulated all frontend in binary ** setup workflow ** make u_panel accept commands from interfacepull/1/head
parent
83252b6f95
commit
c70cdbd262
47 changed files with 808 additions and 651 deletions
@ -0,0 +1,17 @@ |
||||
use actix_web::http::StatusCode; |
||||
use actix_web::ResponseError; |
||||
|
||||
#[derive(thiserror::Error, Debug)] |
||||
pub enum Error { |
||||
#[error("Arg parse error: {0}")] |
||||
ArgparseError(#[from] structopt::clap::Error), |
||||
|
||||
#[error("Just an error: {0}")] |
||||
JustError(String), |
||||
} |
||||
|
||||
impl ResponseError for Error { |
||||
fn status_code(&self) -> actix_web::http::StatusCode { |
||||
StatusCode::BAD_REQUEST |
||||
} |
||||
} |
@ -0,0 +1,48 @@ |
||||
# See http://help.github.com/ignore-files/ for more about ignoring files. |
||||
|
||||
# compiled output |
||||
/dist |
||||
/tmp |
||||
/out-tsc |
||||
# Only exists if Bazel was run |
||||
/bazel-out |
||||
|
||||
# dependencies |
||||
/node_modules |
||||
|
||||
# profiling files |
||||
chrome-profiler-events*.json |
||||
|
||||
# IDEs and editors |
||||
/.idea |
||||
.project |
||||
.classpath |
||||
.c9/ |
||||
*.launch |
||||
.settings/ |
||||
*.sublime-workspace |
||||
|
||||
# IDE - VSCode |
||||
.vscode/* |
||||
!.vscode/settings.json |
||||
!.vscode/tasks.json |
||||
!.vscode/launch.json |
||||
!.vscode/extensions.json |
||||
.history/* |
||||
|
||||
# misc |
||||
/.angular/cache |
||||
/.sass-cache |
||||
/connect.lock |
||||
/coverage |
||||
/libpeerconnection.log |
||||
npm-debug.log |
||||
yarn-error.log |
||||
testem.log |
||||
/typings |
||||
|
||||
# System Files |
||||
.DS_Store |
||||
Thumbs.db |
||||
|
||||
package-lock.json |
@ -1 +1,49 @@ |
||||
<span>{{ title }}</span> |
||||
<mat-tab-group animationDuration="0ms" mat-align-tabs="center"> |
||||
<mat-tab label="Agents"> |
||||
<div class="example-container mat-elevation-z8"> |
||||
<div class="example-loading-shade" *ngIf="isLoadingResults"> |
||||
<mat-spinner *ngIf="isLoadingResults"></mat-spinner> |
||||
</div> |
||||
|
||||
<div class="example-table-container"> |
||||
|
||||
<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 label="Jobs"></mat-tab> |
||||
<mat-tab label="Results"></mat-tab> |
||||
</mat-tab-group> |
@ -1,10 +1,82 @@ |
||||
import { Component } from '@angular/core'; |
||||
import { HttpClient } from '@angular/common/http'; |
||||
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({ |
||||
selector: 'app-root', |
||||
templateUrl: './app.component.html', |
||||
styleUrls: ['./app.component.less'] |
||||
}) |
||||
export class AppComponent { |
||||
title = 'ты лох'; |
||||
export class AppComponent implements AfterViewInit { |
||||
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); |
||||
} |
||||
} |
||||
|
@ -1 +1,4 @@ |
||||
/* You can add global styles to this file, and also import other style files */ |
||||
|
||||
html, body { height: 100%; } |
||||
body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; } |
||||
|
@ -1,136 +0,0 @@ |
||||
use crate::handlers::Endpoints; |
||||
use crate::{db::UDB, errors::SResult}; |
||||
use serde::de::DeserializeOwned; |
||||
use std::path::PathBuf; |
||||
use u_lib::{ |
||||
messaging::{AsMsg, BaseMessage, Reportable}, |
||||
models::*, |
||||
}; |
||||
use uuid::Uuid; |
||||
use warp::{ |
||||
body, |
||||
reply::{json, reply, Json}, |
||||
Filter, Rejection, Reply, |
||||
}; |
||||
|
||||
fn get_content<M>() -> impl Filter<Extract = (BaseMessage<'static, M>,), Error = Rejection> + Clone |
||||
where |
||||
M: AsMsg + Sync + Send + DeserializeOwned + 'static, |
||||
{ |
||||
body::content_length_limit(1024 * 64).and(body::json::<BaseMessage<M>>()) |
||||
} |
||||
|
||||
fn into_message<M: AsMsg>(msg: M) -> Json { |
||||
json(&msg.as_message()) |
||||
} |
||||
|
||||
pub fn init_filters( |
||||
auth_token: &str, |
||||
) -> impl Filter<Extract = (impl Reply,), Error = Rejection> + Clone { |
||||
let infallible_none = |_| async { Ok::<(Option<Uuid>,), std::convert::Infallible>((None,)) }; |
||||
|
||||
let get_agents = warp::get() |
||||
.and(warp::path("get_agents")) |
||||
.and( |
||||
warp::path::param::<Uuid>() |
||||
.map(Some) |
||||
.or_else(infallible_none), |
||||
) |
||||
.and_then(Endpoints::get_agents) |
||||
.map(into_message); |
||||
|
||||
let upload_jobs = warp::post() |
||||
.and(warp::path("upload_jobs")) |
||||
.and(get_content::<Vec<JobMeta>>()) |
||||
.and_then(Endpoints::upload_jobs) |
||||
.map(|_| reply()); |
||||
|
||||
let get_jobs = warp::get() |
||||
.and(warp::path("get_jobs")) |
||||
.and( |
||||
warp::path::param::<Uuid>() |
||||
.map(Some) |
||||
.or_else(infallible_none), |
||||
) |
||||
.and_then(Endpoints::get_jobs) |
||||
.map(into_message); |
||||
|
||||
let get_agent_jobs = warp::get() |
||||
.and(warp::path("get_agent_jobs")) |
||||
.and( |
||||
warp::path::param::<Uuid>() |
||||
.map(Some) |
||||
.or_else(infallible_none), |
||||
) |
||||
.and_then(Endpoints::get_agent_jobs) |
||||
.map(into_message); |
||||
|
||||
let get_personal_jobs = warp::get() |
||||
.and(warp::path("get_personal_jobs")) |
||||
.and(warp::path::param::<Uuid>().map(Some)) |
||||
.and_then(Endpoints::get_personal_jobs) |
||||
.map(into_message); |
||||
|
||||
let del = warp::get() |
||||
.and(warp::path("del")) |
||||
.and(warp::path::param::<Uuid>()) |
||||
.and_then(Endpoints::del) |
||||
.map(|_| reply()); |
||||
|
||||
let set_jobs = warp::post() |
||||
.and(warp::path("set_jobs")) |
||||
.and(warp::path::param::<Uuid>()) |
||||
.and(get_content::<Vec<String>>()) |
||||
.and_then(Endpoints::set_jobs) |
||||
.map(into_message); |
||||
|
||||
let report = warp::post() |
||||
.and(warp::path("report")) |
||||
.and(get_content::<Vec<Reportable>>()) |
||||
.and_then(Endpoints::report) |
||||
.map(|_| reply()); |
||||
|
||||
let auth_token = format!("Bearer {auth_token}",).into_boxed_str(); |
||||
let auth_header = warp::header::exact("authorization", Box::leak(auth_token)); |
||||
|
||||
let auth_zone = (get_agents |
||||
.or(get_jobs) |
||||
.or(upload_jobs) |
||||
.or(del) |
||||
.or(set_jobs) |
||||
.or(get_agent_jobs)) |
||||
.and(auth_header); |
||||
|
||||
let agent_zone = get_jobs.clone().or(get_personal_jobs).or(report); |
||||
|
||||
auth_zone.or(agent_zone) |
||||
} |
||||
|
||||
pub fn prefill_jobs() -> SResult<()> { |
||||
let agent_hello = JobMeta::builder() |
||||
.with_type(misc::JobType::Manage) |
||||
.with_alias("agent_hello") |
||||
.build() |
||||
.unwrap(); |
||||
UDB::lock_db().insert_jobs(&[agent_hello]) |
||||
} |
||||
|
||||
pub fn init_logger() { |
||||
use simplelog::*; |
||||
use std::fs::OpenOptions; |
||||
let log_cfg = ConfigBuilder::new() |
||||
.set_time_format_str("%x %X") |
||||
.set_time_to_local(true) |
||||
.build(); |
||||
let logfile = OpenOptions::new() |
||||
.append(true) |
||||
.create(true) |
||||
.open(PathBuf::from("logs").join("u_server.log")) |
||||
.unwrap(); |
||||
let level = LevelFilter::Info; |
||||
let loggers = vec![ |
||||
WriteLogger::new(level, log_cfg.clone(), logfile) as Box<dyn SharedLogger>, |
||||
TermLogger::new(level, log_cfg, TerminalMode::Stderr, ColorChoice::Auto), |
||||
]; |
||||
CombinedLogger::init(loggers).unwrap(); |
||||
} |
@ -1,4 +1,4 @@ |
||||
FROM rust:1.60 |
||||
FROM rust:1.62 |
||||
|
||||
RUN rustup target add x86_64-unknown-linux-musl |
||||
CMD ["sleep", "3600"] |
@ -1,16 +0,0 @@ |
||||
[package] |
||||
name = "u_api_proc_macro" |
||||
version = "0.1.0" |
||||
authors = ["plazmoid <kronos44@mail.ru>"] |
||||
edition = "2018" |
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html |
||||
|
||||
[lib] |
||||
proc-macro = true |
||||
|
||||
[dependencies] |
||||
syn = { version = "1.0", features = ["full", "extra-traits"] } |
||||
quote = "1.0" |
||||
strum = { version = "0.20", features = ["derive"] } |
||||
proc-macro2 = "1.0" |
@ -1,181 +0,0 @@ |
||||
use proc_macro::TokenStream; |
||||
use proc_macro2::{Ident, TokenStream as TokenStream2}; |
||||
use quote::quote; |
||||
use std::{collections::HashMap, str::FromStr}; |
||||
use strum::EnumString; |
||||
use syn::{ |
||||
parse_macro_input, punctuated::Punctuated, AttributeArgs, FnArg, ItemFn, Lit, NestedMeta, |
||||
ReturnType, Signature, Token, Type, |
||||
}; |
||||
|
||||
#[derive(EnumString, Debug)] |
||||
enum ReqMethod { |
||||
GET, |
||||
POST, |
||||
} |
||||
|
||||
#[derive(Debug)] |
||||
struct Endpoint { |
||||
method: ReqMethod, |
||||
} |
||||
|
||||
#[derive(Debug)] |
||||
struct FnArgs { |
||||
url_param: Option<Type>, |
||||
payload: Option<Type>, |
||||
} |
||||
|
||||
#[proc_macro_attribute] |
||||
pub fn api_route(args: TokenStream, item: TokenStream) -> TokenStream { |
||||
let args: AttributeArgs = parse_macro_input!(args); |
||||
let input: ItemFn = parse_macro_input!(item); |
||||
let Signature { |
||||
ident, |
||||
inputs, |
||||
generics, |
||||
output, |
||||
.. |
||||
} = input.sig; |
||||
let (impl_generics, _, _) = generics.split_for_impl(); |
||||
let FnArgs { url_param, payload } = parse_fn_args(inputs); |
||||
let Endpoint { method } = parse_attr_args(args); |
||||
let url_path = build_url_path(&ident, &url_param); |
||||
let return_ty = match output { |
||||
ReturnType::Type(_, ty) => quote!(#ty), |
||||
ReturnType::Default => quote!(()), |
||||
}; |
||||
let request = match method { |
||||
ReqMethod::GET => build_get(url_path), |
||||
ReqMethod::POST => build_post(url_path, &payload), |
||||
}; |
||||
let url_param = match url_param { |
||||
Some(p) => quote!(, param: #p), |
||||
None => TokenStream2::new(), |
||||
}; |
||||
let payload = match payload { |
||||
Some(p) => quote!(, payload: #p), |
||||
None => TokenStream2::new(), |
||||
}; |
||||
let q = quote! { |
||||
pub async fn #ident #impl_generics( |
||||
&self #url_param #payload |
||||
) -> UResult<#return_ty> { |
||||
let request = { |
||||
#request |
||||
}; |
||||
let response = request.send().await?; |
||||
let content_len = response.content_length(); |
||||
let is_success = match response.error_for_status_ref() { |
||||
Ok(_) => Ok(()), |
||||
Err(e) => Err(UError::from(e)) |
||||
}; |
||||
let resp = response.text().await?; |
||||
let result = match is_success { |
||||
Ok(_) => { |
||||
serde_json::from_str::<BaseMessage<#return_ty>>(&resp) |
||||
.map(|msg| msg.into_inner()) |
||||
.or_else(|e| { |
||||
match content_len { |
||||
Some(0) => Ok(Default::default()), |
||||
_ => Err(UError::NetError(e.to_string(), resp.clone())) |
||||
} |
||||
}) |
||||
}, |
||||
Err(UError::NetError(err_src, _)) => Err( |
||||
UError::NetError( |
||||
err_src, |
||||
resp |
||||
) |
||||
), |
||||
_ => unreachable!() |
||||
}; |
||||
Ok(result?) |
||||
} |
||||
}; |
||||
q.into() |
||||
} |
||||
|
||||
fn parse_fn_args(raw: Punctuated<FnArg, Token![,]>) -> FnArgs { |
||||
let mut arg: HashMap<String, Type> = raw |
||||
.into_iter() |
||||
.filter_map(|arg| { |
||||
if let FnArg::Typed(argt) = arg { |
||||
let mut arg_name = String::new(); |
||||
// did you think I won't overplay you? won't destroy?
|
||||
|arg_ident| -> TokenStream { |
||||
let q: TokenStream = quote!(#arg_ident).into(); |
||||
arg_name = parse_macro_input!(q as Ident).to_string(); |
||||
TokenStream::new() |
||||
}(argt.pat); |
||||
if &arg_name != "url_param" && &arg_name != "payload" { |
||||
panic!("Wrong arg name: {}", &arg_name) |
||||
} |
||||
let arg_type = *argt.ty; |
||||
Some((arg_name, arg_type)) |
||||
} else { |
||||
None |
||||
} |
||||
}) |
||||
.collect(); |
||||
FnArgs { |
||||
url_param: arg.remove("url_param"), |
||||
payload: arg.remove("payload"), |
||||
} |
||||
} |
||||
|
||||
fn build_get(url: TokenStream2) -> TokenStream2 { |
||||
quote! { |
||||
let request = self.build_get(#url); |
||||
request |
||||
} |
||||
} |
||||
|
||||
fn build_post(url: TokenStream2, payload: &Option<Type>) -> TokenStream2 { |
||||
let pld = match payload { |
||||
Some(_) => quote! { |
||||
.json(&payload.as_message()) |
||||
}, |
||||
None => TokenStream2::new(), |
||||
}; |
||||
quote! { |
||||
let request = self.build_post(#url); |
||||
request #pld |
||||
} |
||||
} |
||||
|
||||
fn build_url_path(path: &Ident, url_param: &Option<Type>) -> TokenStream2 { |
||||
let url_param = match url_param { |
||||
Some(_) => quote! { |
||||
+ &opt_to_string(param) |
||||
}, |
||||
None => TokenStream2::new(), |
||||
}; |
||||
quote! { |
||||
&format!( |
||||
"{}/{}", |
||||
stringify!(#path), |
||||
String::new() #url_param |
||||
) |
||||
} |
||||
} |
||||
|
||||
fn parse_attr_args(args: AttributeArgs) -> Endpoint { |
||||
let mut args = args.into_iter(); |
||||
let method = match args.next() { |
||||
Some(method) => match method { |
||||
NestedMeta::Lit(l) => { |
||||
if let Lit::Str(s) = l { |
||||
match ReqMethod::from_str(&s.value()) { |
||||
Ok(v) => v, |
||||
Err(_) => panic!("Unknown method"), |
||||
} |
||||
} else { |
||||
panic!("Method must be a str") |
||||
} |
||||
} |
||||
_ => panic!("Method must be on the first place"), |
||||
}, |
||||
None => panic!("Method required"), |
||||
}; |
||||
Endpoint { method } |
||||
} |
@ -1,15 +0,0 @@ |
||||
/* |
||||
use std::fmt::Display; |
||||
use u_api_proc_macro::api_route; |
||||
|
||||
type UResult<T, E> = Result<T, E>; |
||||
|
||||
struct ClientHandler; |
||||
struct Paths; |
||||
|
||||
#[test] |
||||
fn test1() { |
||||
#[api_route("GET", Uuid)] |
||||
fn list<T: Display>(url_param: T) {} |
||||
} |
||||
*/ |
@ -1,98 +1,134 @@ |
||||
use std::collections::HashMap; |
||||
|
||||
use crate::{ |
||||
config::MASTER_PORT, |
||||
messaging::{self, AsMsg, BaseMessage}, |
||||
models, |
||||
utils::{opt_to_string, VecDisplay}, |
||||
messaging::{self, AsMsg, BaseMessage, Empty}, |
||||
models::{self, Agent}, |
||||
utils::opt_to_string, |
||||
UError, UResult, |
||||
}; |
||||
use reqwest::{Certificate, Client, Identity, RequestBuilder, Url}; |
||||
use u_api_proc_macro::api_route; |
||||
use reqwest::{header::HeaderMap, Certificate, Client, Identity, Url}; |
||||
use serde::de::DeserializeOwned; |
||||
use uuid::Uuid; |
||||
|
||||
const AGENT_IDENTITY: &[u8] = include_bytes!("../../../certs/alice.p12"); |
||||
const ROOT_CA_CERT: &[u8] = include_bytes!("../../../certs/ca.crt"); |
||||
|
||||
#[derive(Clone)] |
||||
pub struct ClientHandler { |
||||
base_url: Url, |
||||
client: Client, |
||||
password: Option<String>, |
||||
} |
||||
|
||||
impl ClientHandler { |
||||
pub fn new(server: &str) -> Self { |
||||
pub fn new(server: &str, password: Option<String>) -> Self { |
||||
let identity = Identity::from_pkcs12_der(AGENT_IDENTITY, "").unwrap(); |
||||
let client = Client::builder() |
||||
.identity(identity) |
||||
let mut client = Client::builder().identity(identity); |
||||
if let Some(pwd) = password { |
||||
client = client.default_headers( |
||||
HeaderMap::try_from(&HashMap::from([( |
||||
"Authorization".to_string(), |
||||
format!("Bearer {pwd}"), |
||||
)])) |
||||
.unwrap(), |
||||
) |
||||
} |
||||
let client = client |
||||
.add_root_certificate(Certificate::from_pem(ROOT_CA_CERT).unwrap()) |
||||
.build() |
||||
.unwrap(); |
||||
Self { |
||||
client, |
||||
base_url: Url::parse(&format!("https://{}:{}", server, MASTER_PORT)).unwrap(), |
||||
password: None, |
||||
} |
||||
} |
||||
|
||||
pub fn password(mut self, password: String) -> ClientHandler { |
||||
self.password = Some(password); |
||||
self |
||||
} |
||||
async fn _req<P: AsMsg, M: AsMsg + DeserializeOwned>( |
||||
&self, |
||||
url: impl AsRef<str>, |
||||
payload: P, |
||||
) -> UResult<M> { |
||||
let request = self |
||||
.client |
||||
.post(self.base_url.join(url.as_ref()).unwrap()) |
||||
.json(&payload.as_message()); |
||||
|
||||
fn set_pwd(&self, rb: RequestBuilder) -> RequestBuilder { |
||||
match &self.password { |
||||
Some(p) => rb.bearer_auth(p), |
||||
None => rb, |
||||
let response = request.send().await?; |
||||
let is_success = match response.error_for_status_ref() { |
||||
Ok(_) => Ok(()), |
||||
Err(e) => Err(UError::from(e)), |
||||
}; |
||||
let resp = response.text().await?; |
||||
match is_success { |
||||
Ok(_) => serde_json::from_str::<BaseMessage<M>>(&resp) |
||||
.map(|msg| msg.into_inner()) |
||||
.or_else(|e| Err(UError::NetError(e.to_string(), resp.clone()))), |
||||
Err(UError::NetError(err, _)) => Err(UError::NetError(err, resp)), |
||||
_ => unreachable!(), |
||||
} |
||||
} |
||||
|
||||
fn build_get(&self, url: &str) -> RequestBuilder { |
||||
let rb = self.client.get(self.base_url.join(url).unwrap()); |
||||
self.set_pwd(rb) |
||||
// get jobs for client
|
||||
pub async fn get_personal_jobs( |
||||
&self, |
||||
url_param: Option<Uuid>, |
||||
) -> UResult<Vec<models::AssignedJob>> { |
||||
self._req( |
||||
format!("get_personal_jobs/{}", opt_to_string(url_param)), |
||||
Empty, |
||||
) |
||||
.await |
||||
} |
||||
|
||||
fn build_post(&self, url: &str) -> RequestBuilder { |
||||
let rb = self.client.post(self.base_url.join(url).unwrap()); |
||||
self.set_pwd(rb) |
||||
} |
||||
//
|
||||
// get jobs for client
|
||||
#[api_route("GET")] |
||||
async fn get_personal_jobs(&self, url_param: Option<Uuid>) -> VecDisplay<models::AssignedJob> {} |
||||
//
|
||||
// send something to server
|
||||
#[api_route("POST")] |
||||
async fn report(&self, payload: &[messaging::Reportable]) -> messaging::Empty {} |
||||
//
|
||||
pub async fn report(&self, payload: &[messaging::Reportable]) -> UResult<Empty> { |
||||
self._req("report", payload).await |
||||
} |
||||
|
||||
// download file
|
||||
#[api_route("GET")] |
||||
async fn dl(&self, url_param: Option<Uuid>) -> Vec<u8> {} |
||||
//
|
||||
// request download
|
||||
#[api_route("POST")] |
||||
async fn dlr(&self, url_param: Option<String>) -> messaging::DownloadInfo {} |
||||
pub async fn dl(&self, file: String) -> UResult<Vec<u8>> { |
||||
self._req(format!("dl/{file}"), Empty).await |
||||
} |
||||
} |
||||
|
||||
//##########// Admin area //##########//
|
||||
/// client listing
|
||||
#[api_route("GET")] |
||||
async fn get_agents(&self, url_param: Option<Uuid>) -> VecDisplay<models::Agent> {} |
||||
//
|
||||
// get all available jobs
|
||||
#[api_route("GET")] |
||||
async fn get_jobs(&self, url_param: Option<Uuid>) -> VecDisplay<models::JobMeta> {} |
||||
//
|
||||
// create and upload job
|
||||
#[api_route("POST")] |
||||
async fn upload_jobs(&self, payload: &[models::JobMeta]) -> messaging::Empty {} |
||||
//
|
||||
// delete something
|
||||
#[api_route("GET")] |
||||
async fn del(&self, url_param: Option<Uuid>) -> i32 {} |
||||
//
|
||||
// set jobs for any client
|
||||
#[api_route("POST")] |
||||
async fn set_jobs(&self, url_param: Option<Uuid>, payload: &[String]) -> VecDisplay<Uuid> {} |
||||
//
|
||||
// get jobs for any client
|
||||
#[api_route("GET")] |
||||
async fn get_agent_jobs(&self, url_param: Option<Uuid>) -> VecDisplay<models::AssignedJob> {} |
||||
#[cfg(feature = "panel")] |
||||
impl ClientHandler { |
||||
/// agent listing
|
||||
pub async fn get_agents(&self, agent: Option<Uuid>) -> UResult<Vec<models::Agent>> { |
||||
self._req(format!("get_agents/{}", opt_to_string(agent)), Empty) |
||||
.await |
||||
} |
||||
|
||||
/// update something
|
||||
pub async fn update_item(&self, item: impl AsMsg) -> UResult<Empty> { |
||||
self._req("update_item", item).await |
||||
} |
||||
|
||||
/// get all available jobs
|
||||
pub async fn get_jobs(&self, job: Option<Uuid>) -> UResult<Vec<models::JobMeta>> { |
||||
self._req(format!("get_jobs/{}", opt_to_string(job)), Empty) |
||||
.await |
||||
} |
||||
|
||||
/// create and upload job
|
||||
pub async fn upload_jobs(&self, payload: &[models::JobMeta]) -> UResult<Empty> { |
||||
self._req("upload_jobs", payload).await |
||||
} |
||||
|
||||
/// delete something
|
||||
pub async fn del(&self, item: Uuid) -> UResult<i32> { |
||||
self._req(format!("del/{item}"), Empty).await |
||||
} |
||||
|
||||
/// set jobs for any agent
|
||||
pub async fn set_jobs(&self, agent: Uuid, job_idents: &[String]) -> UResult<Vec<Uuid>> { |
||||
self._req(format!("set_jobs/{agent}"), job_idents).await |
||||
} |
||||
|
||||
/// get jobs for any agent
|
||||
pub async fn get_agent_jobs(&self, agent: Option<Uuid>) -> UResult<Vec<models::AssignedJob>> { |
||||
self._req(format!("set_jobs/{}", opt_to_string(agent)), Empty) |
||||
.await |
||||
} |
||||
} |
||||
|
@ -0,0 +1,16 @@ |
||||
use std::env; |
||||
use std::path::Path; |
||||
|
||||
use tracing_appender::rolling; |
||||
use tracing_subscriber::EnvFilter; |
||||
|
||||
pub fn init_logger(logfile: impl AsRef<Path> + Send + Sync + 'static) { |
||||
if env::var("RUST_LOG").is_err() { |
||||
env::set_var("RUST_LOG", "info") |
||||
} |
||||
|
||||
tracing_subscriber::fmt::fmt() |
||||
.with_env_filter(EnvFilter::from_default_env()) |
||||
.with_writer(move || rolling::never(".", logfile.as_ref().with_extension("log"))) |
||||
.init(); |
||||
} |
Loading…
Reference in new issue