forked from github-starred/komodo
add api secret create / delete methods
This commit is contained in:
Generated
+5
-4
@@ -1924,9 +1924,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "resolver_api"
|
||||
version = "0.1.4"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4ec3e46f747f873a3aac9612d4599cae1d7600df5a5db106b5bca28ab734369"
|
||||
checksum = "2a4c612fa902830ac9b906bacbe800e0cb34968ff913a1aef29b16b65a8c6781"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -1937,9 +1937,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "resolver_api_derive"
|
||||
version = "0.1.4"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce9be59c8b5b836bc5d2e87a1bd3309b9fb6b4b4e1c414cf8039a7fb110496df"
|
||||
checksum = "c9be9792e0b0154c7764feb2ee95dd2427d4db012715bb8320a5f0e53368c558"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -2494,6 +2494,7 @@ name = "tests"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"dotenv",
|
||||
"make_option",
|
||||
"monitor_client",
|
||||
"monitor_types",
|
||||
|
||||
+1
-1
@@ -52,7 +52,7 @@ termination_signal = "0.1.2"
|
||||
async_timing_util = "0.1.14"
|
||||
partial_derive2 = "0.1.4"
|
||||
make_option = "0.1.3"
|
||||
resolver_api = "0.1.4"
|
||||
resolver_api = "0.1.5"
|
||||
parse_csl = "0.1.0"
|
||||
mungos = "0.4.2"
|
||||
svi = "0.1.4"
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
use anyhow::{anyhow, Context};
|
||||
use async_timing_util::unix_timestamp_ms;
|
||||
use axum::{extract::Query, response::Redirect, routing::get, Router, http::StatusCode};
|
||||
use axum::{extract::Query, http::StatusCode, response::Redirect, routing::get, Router};
|
||||
use monitor_types::entities::user::User;
|
||||
use mungos::mongodb::bson::doc;
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::{state::StateExtension};
|
||||
use crate::state::StateExtension;
|
||||
|
||||
pub mod client;
|
||||
|
||||
|
||||
@@ -18,11 +18,12 @@ pub struct GoogleOauthClient {
|
||||
}
|
||||
|
||||
impl GoogleOauthClient {
|
||||
pub fn new(CoreConfig { google_oauth, host, .. }: &CoreConfig) -> Option<GoogleOauthClient> {
|
||||
if google_oauth.enabled
|
||||
&& !google_oauth.id.is_empty()
|
||||
&& !google_oauth.secret.is_empty()
|
||||
{
|
||||
pub fn new(
|
||||
CoreConfig {
|
||||
google_oauth, host, ..
|
||||
}: &CoreConfig,
|
||||
) -> Option<GoogleOauthClient> {
|
||||
if google_oauth.enabled && !google_oauth.id.is_empty() && !google_oauth.secret.is_empty() {
|
||||
GoogleOauthClient {
|
||||
http: Default::default(),
|
||||
client_id: google_oauth.id.clone(),
|
||||
|
||||
@@ -2,7 +2,7 @@ use std::{collections::HashMap, sync::Arc};
|
||||
|
||||
use anyhow::{anyhow, Context};
|
||||
use async_timing_util::{get_timelength_in_ms, unix_timestamp_ms, Timelength};
|
||||
use axum::{body::Body, http::Request};
|
||||
use axum::{body::Body, http::Request, Extension};
|
||||
use hmac::{Hmac, Mac};
|
||||
use jwt::{SignWithKey, VerifyWithKey};
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -15,8 +15,11 @@ use super::random_string;
|
||||
|
||||
type ExchangeTokenMap = Mutex<HashMap<String, (String, u128)>>;
|
||||
|
||||
pub type RequestUser = Arc<InnerRequestUser>;
|
||||
pub type RequestUserExtension = Extension<RequestUser>;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct RequestUser {
|
||||
pub struct InnerRequestUser {
|
||||
pub id: String,
|
||||
pub is_admin: bool,
|
||||
pub create_server_permissions: bool,
|
||||
@@ -92,7 +95,7 @@ impl State {
|
||||
pub async fn authenticate_check_enabled(
|
||||
&self,
|
||||
req: &Request<Body>,
|
||||
) -> anyhow::Result<Arc<RequestUser>> {
|
||||
) -> anyhow::Result<RequestUser> {
|
||||
let jwt = req
|
||||
.headers()
|
||||
.get("authorization")
|
||||
@@ -106,7 +109,7 @@ impl State {
|
||||
.auth_jwt_check_enabled(&jwt)
|
||||
.await
|
||||
.context("failed to authenticate jwt")?;
|
||||
Ok(Arc::new(user))
|
||||
Ok(user.into())
|
||||
}
|
||||
|
||||
pub async fn auth_jwt_check_enabled(&self, jwt: &str) -> anyhow::Result<RequestUser> {
|
||||
@@ -121,13 +124,13 @@ impl State {
|
||||
.await?
|
||||
.ok_or(anyhow!("did not find user with id {}", claims.id))?;
|
||||
if user.enabled {
|
||||
let user = RequestUser {
|
||||
let user = InnerRequestUser {
|
||||
id: claims.id,
|
||||
is_admin: user.admin,
|
||||
create_server_permissions: user.create_server_permissions,
|
||||
create_build_permissions: user.create_build_permissions,
|
||||
};
|
||||
Ok(user)
|
||||
Ok(user.into())
|
||||
} else {
|
||||
Err(anyhow!("user not enabled"))
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ impl Resolve<CreateLocalUser> for State {
|
||||
async fn resolve(
|
||||
&self,
|
||||
CreateLocalUser { username, password }: CreateLocalUser,
|
||||
_: (),
|
||||
) -> anyhow::Result<CreateLocalUserResponse> {
|
||||
if !self.config.local_auth {
|
||||
return Err(anyhow!("local auth is not enabled"));
|
||||
@@ -63,6 +64,7 @@ impl Resolve<LoginLocalUser> for State {
|
||||
async fn resolve(
|
||||
&self,
|
||||
LoginLocalUser { username, password }: LoginLocalUser,
|
||||
_: (),
|
||||
) -> anyhow::Result<LoginLocalUserResponse> {
|
||||
if !self.config.local_auth {
|
||||
return Err(anyhow!("local auth is not enabled"));
|
||||
|
||||
@@ -20,7 +20,7 @@ use crate::{
|
||||
state::{State, StateExtension},
|
||||
};
|
||||
|
||||
pub use self::jwt::JwtClient;
|
||||
pub use self::jwt::{JwtClient, RequestUser, RequestUserExtension};
|
||||
pub use github::client::GithubOauthClient;
|
||||
pub use google::client::GoogleOauthClient;
|
||||
|
||||
@@ -49,7 +49,7 @@ pub fn router(state: &State) -> Router {
|
||||
let req_id = Uuid::new_v4();
|
||||
info!("/auth request {req_id} | {request:?}");
|
||||
let res = state
|
||||
.resolve_request(request)
|
||||
.resolve_request(request, ())
|
||||
.await
|
||||
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("{e:?}")));
|
||||
if let Err(e) = &res {
|
||||
@@ -75,7 +75,7 @@ pub fn router(state: &State) -> Router {
|
||||
router
|
||||
}
|
||||
|
||||
fn random_string(length: usize) -> String {
|
||||
pub fn random_string(length: usize) -> String {
|
||||
thread_rng()
|
||||
.sample_iter(&Alphanumeric)
|
||||
.take(length)
|
||||
|
||||
@@ -15,6 +15,7 @@ impl Resolve<LoginWithSecret> for State {
|
||||
async fn resolve(
|
||||
&self,
|
||||
LoginWithSecret { username, secret }: LoginWithSecret,
|
||||
_: (),
|
||||
) -> anyhow::Result<LoginWithSecretResponse> {
|
||||
let user = self
|
||||
.db
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
use anyhow::Context;
|
||||
use monitor_types::entities::user::User;
|
||||
|
||||
use crate::state::State;
|
||||
|
||||
impl State {
|
||||
pub async fn get_user(&self, user_id: &str) -> anyhow::Result<User> {
|
||||
self.db
|
||||
.users
|
||||
.find_one_by_id(user_id)
|
||||
.await?
|
||||
.context(format!("no user exists with id {user_id}"))
|
||||
}
|
||||
}
|
||||
+6
-3
@@ -3,7 +3,7 @@ extern crate log;
|
||||
|
||||
use std::time::Instant;
|
||||
|
||||
use auth::auth_request;
|
||||
use auth::{auth_request, RequestUserExtension};
|
||||
use axum::{
|
||||
headers::ContentType, http::StatusCode, middleware, routing::post, Extension, Json, Router,
|
||||
TypedHeader,
|
||||
@@ -18,6 +18,7 @@ use crate::requests::api::ApiRequest;
|
||||
mod auth;
|
||||
mod config;
|
||||
mod db;
|
||||
mod helpers;
|
||||
mod requests;
|
||||
mod state;
|
||||
|
||||
@@ -47,12 +48,14 @@ fn api() -> Router {
|
||||
.route(
|
||||
"/",
|
||||
post(
|
||||
|state: StateExtension, Json(request): Json<ApiRequest>| async move {
|
||||
|state: StateExtension,
|
||||
Extension(user): RequestUserExtension,
|
||||
Json(request): Json<ApiRequest>| async move {
|
||||
let timer = Instant::now();
|
||||
let req_id = Uuid::new_v4();
|
||||
info!("/auth request {req_id} | {request:?}");
|
||||
let res = state
|
||||
.resolve_request(request)
|
||||
.resolve_request(request, user)
|
||||
.await
|
||||
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("{e:?}")));
|
||||
if let Err(e) = &res {
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
use resolver_api::derive::Resolver;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::state::State;
|
||||
use crate::{auth::RequestUser, state::State};
|
||||
|
||||
mod secret;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Resolver)]
|
||||
#[resolver_target(State)]
|
||||
#[resolver_args(RequestUser)]
|
||||
#[serde(tag = "type", content = "params")]
|
||||
#[allow(clippy::enum_variant_names, clippy::large_enum_variant)]
|
||||
pub enum ApiRequest {}
|
||||
@@ -0,0 +1,82 @@
|
||||
use anyhow::{anyhow, Context};
|
||||
use async_timing_util::unix_timestamp_ms;
|
||||
use async_trait::async_trait;
|
||||
use monitor_types::{
|
||||
entities::user::ApiSecret,
|
||||
requests::api::{CreateLoginSecret, CreateLoginSecretResponse, DeleteLoginSecret},
|
||||
};
|
||||
use mungos::{
|
||||
mongodb::bson::{doc, to_bson, Document},
|
||||
Update,
|
||||
};
|
||||
use resolver_api::Resolve;
|
||||
|
||||
use crate::{
|
||||
auth::{random_string, RequestUser},
|
||||
state::State,
|
||||
};
|
||||
|
||||
const SECRET_LENGTH: usize = 40;
|
||||
const BCRYPT_COST: u32 = 10;
|
||||
|
||||
#[async_trait]
|
||||
impl Resolve<CreateLoginSecret, RequestUser> for State {
|
||||
async fn resolve(
|
||||
&self,
|
||||
secret: CreateLoginSecret,
|
||||
user: RequestUser,
|
||||
) -> anyhow::Result<CreateLoginSecretResponse> {
|
||||
let user = self.get_user(&user.id).await?;
|
||||
for s in &user.secrets {
|
||||
if s.name == secret.name {
|
||||
return Err(anyhow!("secret with name {} already exists", secret.name));
|
||||
}
|
||||
}
|
||||
let secret_str = random_string(SECRET_LENGTH);
|
||||
let api_secret = ApiSecret {
|
||||
name: secret.name,
|
||||
created_at: unix_timestamp_ms() as i64,
|
||||
expires: secret.expires,
|
||||
hash: bcrypt::hash(&secret_str, BCRYPT_COST)
|
||||
.context("failed at hashing secret string")?,
|
||||
};
|
||||
self.db
|
||||
.users
|
||||
.update_one::<Document>(
|
||||
&user.id,
|
||||
Update::Custom(doc! {
|
||||
"$push": {
|
||||
"secrets": to_bson(&api_secret).context("failed at converting secret to bson")?
|
||||
}
|
||||
}),
|
||||
)
|
||||
.await
|
||||
.context("failed at mongo update query")?;
|
||||
Ok(CreateLoginSecretResponse { secret: secret_str })
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Resolve<DeleteLoginSecret, RequestUser> for State {
|
||||
async fn resolve(
|
||||
&self,
|
||||
DeleteLoginSecret { name }: DeleteLoginSecret,
|
||||
user: RequestUser,
|
||||
) -> anyhow::Result<()> {
|
||||
self.db
|
||||
.users
|
||||
.update_one::<Document>(
|
||||
&user.id,
|
||||
Update::Custom(doc! {
|
||||
"$pull": {
|
||||
"secrets": {
|
||||
"name": name
|
||||
}
|
||||
}
|
||||
}),
|
||||
)
|
||||
.await
|
||||
.context("failed at mongo update query")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
use async_trait::async_trait;
|
||||
use monitor_types::requests::auth::{
|
||||
ExchangeForJwt, ExchangeForJwtResponse, GetLoginOptions, LoginWithSecret, CreateLocalUser, LoginLocalUser,
|
||||
CreateLocalUser, ExchangeForJwt, ExchangeForJwtResponse, GetLoginOptions, LoginLocalUser,
|
||||
LoginWithSecret,
|
||||
};
|
||||
use resolver_api::{derive::Resolver, Resolve, ResolveToString};
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -22,7 +23,7 @@ pub enum AuthRequest {
|
||||
|
||||
#[async_trait]
|
||||
impl ResolveToString<GetLoginOptions> for State {
|
||||
async fn resolve_to_string(&self, _: GetLoginOptions) -> anyhow::Result<String> {
|
||||
async fn resolve_to_string(&self, _: GetLoginOptions, _: ()) -> anyhow::Result<String> {
|
||||
Ok(self.login_options_response.clone())
|
||||
}
|
||||
}
|
||||
@@ -32,6 +33,7 @@ impl Resolve<ExchangeForJwt> for State {
|
||||
async fn resolve(
|
||||
&self,
|
||||
ExchangeForJwt { token }: ExchangeForJwt,
|
||||
_: (),
|
||||
) -> anyhow::Result<ExchangeForJwtResponse> {
|
||||
let jwt = self.jwt.redeem_exchange_token(&token).await?;
|
||||
let res = ExchangeForJwtResponse { jwt };
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
use anyhow::{anyhow, Context};
|
||||
use monitor_types::requests::auth::{self, LoginLocalUserResponse, LoginWithSecretResponse};
|
||||
use monitor_types::requests::auth::{
|
||||
self, CreateLocalUserResponse, LoginLocalUserResponse, LoginWithSecretResponse,
|
||||
};
|
||||
use reqwest::StatusCode;
|
||||
use resolver_api::HasResponse;
|
||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||
@@ -7,7 +9,7 @@ use serde_json::json;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct MonitorEnv {
|
||||
monitor_url: String,
|
||||
monitor_address: String,
|
||||
monitor_token: Option<String>,
|
||||
monitor_username: Option<String>,
|
||||
monitor_password: Option<String>,
|
||||
@@ -52,6 +54,29 @@ impl MonitorClient {
|
||||
Ok(client)
|
||||
}
|
||||
|
||||
pub async fn new_with_new_account(
|
||||
address: impl Into<String>,
|
||||
username: impl Into<String>,
|
||||
password: impl Into<String>,
|
||||
) -> anyhow::Result<MonitorClient> {
|
||||
let mut client = MonitorClient {
|
||||
reqwest: Default::default(),
|
||||
address: address.into(),
|
||||
jwt: Default::default(),
|
||||
};
|
||||
|
||||
let CreateLocalUserResponse { jwt } = client
|
||||
.auth(auth::CreateLocalUser {
|
||||
username: username.into(),
|
||||
password: password.into(),
|
||||
})
|
||||
.await?;
|
||||
|
||||
client.jwt = jwt;
|
||||
|
||||
Ok(client)
|
||||
}
|
||||
|
||||
pub async fn new_with_secret(
|
||||
address: impl Into<String>,
|
||||
username: impl Into<String>,
|
||||
@@ -79,17 +104,17 @@ impl MonitorClient {
|
||||
let env = envy::from_env::<MonitorEnv>()
|
||||
.context("failed to parse environment for monitor client")?;
|
||||
if let Some(token) = env.monitor_token {
|
||||
Ok(MonitorClient::new_with_token(&env.monitor_url, token))
|
||||
Ok(MonitorClient::new_with_token(&env.monitor_address, token))
|
||||
} else if let Some(password) = env.monitor_password {
|
||||
let username = env.monitor_username.ok_or(anyhow!(
|
||||
"must provide MONITOR_USERNAME to authenticate with MONITOR_PASSWORD"
|
||||
))?;
|
||||
MonitorClient::new_with_password(&env.monitor_url, username, password).await
|
||||
MonitorClient::new_with_password(&env.monitor_address, username, password).await
|
||||
} else if let Some(secret) = env.monitor_secret {
|
||||
let username = env.monitor_username.ok_or(anyhow!(
|
||||
"must provide MONITOR_USERNAME to authenticate with MONITOR_SECRET"
|
||||
))?;
|
||||
MonitorClient::new_with_secret(&env.monitor_url, username, secret).await
|
||||
MonitorClient::new_with_secret(&env.monitor_address, username, secret).await
|
||||
} else {
|
||||
Err(anyhow!("failed to initialize monitor client from env | must provide one of: (MONITOR_TOKEN), (MONITOR_USERNAME and MONITOR_PASSWORD), (MONITOR_USERNAME and MONITOR_SECRET)"))
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ use std::collections::HashMap;
|
||||
use serde::{de::DeserializeOwned, Deserialize, Deserializer, Serialize};
|
||||
use typeshare::typeshare;
|
||||
|
||||
use crate::I64;
|
||||
|
||||
#[typeshare]
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||
pub struct ImageSummary {
|
||||
@@ -26,19 +28,19 @@ pub struct ImageSummary {
|
||||
|
||||
/// Date and time at which the image was created as a Unix timestamp (number of seconds sinds EPOCH).
|
||||
#[serde(rename = "Created")]
|
||||
pub created: i64,
|
||||
pub created: I64,
|
||||
|
||||
/// Total size of the image including all layers it is composed of.
|
||||
#[serde(rename = "Size")]
|
||||
pub size: i64,
|
||||
pub size: I64,
|
||||
|
||||
/// Total size of image layers that are shared between this image and other images. This size is not calculated by default. `-1` indicates that the value has not been set / calculated.
|
||||
#[serde(rename = "SharedSize")]
|
||||
pub shared_size: i64,
|
||||
pub shared_size: I64,
|
||||
|
||||
/// Total size of the image including all layers it is composed of. In versions of Docker before v1.10, this field was calculated from the image itself and all of its parent images. Docker v1.10 and up store images self-contained, and no longer use a parent-chain, making this field an equivalent of the Size field. This field is kept for backward compatibility, but may be removed in a future version of the API.
|
||||
#[serde(rename = "VirtualSize")]
|
||||
pub virtual_size: i64,
|
||||
pub virtual_size: I64,
|
||||
|
||||
/// User-defined key/value metadata.
|
||||
#[serde(rename = "Labels")]
|
||||
@@ -47,7 +49,7 @@ pub struct ImageSummary {
|
||||
|
||||
/// Number of containers using this image. Includes both stopped and running containers. This size is not calculated by default, and depends on which API endpoint is used. `-1` indicates that the value has not been set / calculated.
|
||||
#[serde(rename = "Containers")]
|
||||
pub containers: i64,
|
||||
pub containers: I64,
|
||||
}
|
||||
|
||||
fn deserialize_nonoptional_vec<'de, D: Deserializer<'de>, T: DeserializeOwned>(
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
use bson::serde_helpers::hex_string_as_object_id;
|
||||
use mungos::MungosIndexed;
|
||||
use serde::{Serialize, Deserialize};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use typeshare::typeshare;
|
||||
|
||||
use crate::I64;
|
||||
|
||||
#[typeshare]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Default, MungosIndexed)]
|
||||
pub struct User {
|
||||
@@ -14,11 +16,11 @@ pub struct User {
|
||||
)]
|
||||
pub id: String,
|
||||
|
||||
#[unique_index]
|
||||
#[unique_index]
|
||||
pub username: String,
|
||||
|
||||
#[serde(default)]
|
||||
#[index]
|
||||
#[index]
|
||||
pub enabled: bool,
|
||||
|
||||
#[serde(default)]
|
||||
@@ -42,10 +44,10 @@ pub struct User {
|
||||
pub google_id: Option<String>,
|
||||
|
||||
#[serde(default, skip_serializing_if = "i64_is_zero")]
|
||||
pub created_at: i64,
|
||||
pub created_at: I64,
|
||||
|
||||
#[serde(default)]
|
||||
pub updated_at: i64,
|
||||
pub updated_at: I64,
|
||||
}
|
||||
|
||||
#[typeshare]
|
||||
@@ -54,10 +56,10 @@ pub struct ApiSecret {
|
||||
pub name: String,
|
||||
#[serde(default, skip_serializing_if = "String::is_empty")]
|
||||
pub hash: String,
|
||||
pub created_at: String,
|
||||
pub expires: Option<i64>,
|
||||
pub created_at: I64,
|
||||
pub expires: Option<I64>,
|
||||
}
|
||||
|
||||
fn i64_is_zero(n: &i64) -> bool {
|
||||
fn i64_is_zero(n: &I64) -> bool {
|
||||
*n == 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use typeshare::typeshare;
|
||||
|
||||
pub mod requests;
|
||||
pub mod entities;
|
||||
pub mod requests;
|
||||
|
||||
#[typeshare(serialized_as = "number")]
|
||||
pub type I64 = i64;
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
use resolver_api::derive::Request;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use typeshare::typeshare;
|
||||
|
||||
use crate::I64;
|
||||
|
||||
//
|
||||
|
||||
#[typeshare]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Request)]
|
||||
#[response(CreateLoginSecretResponse)]
|
||||
pub struct CreateLoginSecret {
|
||||
pub name: String,
|
||||
pub expires: Option<I64>,
|
||||
}
|
||||
|
||||
#[typeshare]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct CreateLoginSecretResponse {
|
||||
pub secret: String,
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
#[typeshare]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Request)]
|
||||
#[response(())]
|
||||
pub struct DeleteLoginSecret {
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
@@ -81,5 +81,3 @@ pub struct LoginWithSecretResponse {
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
pub mod api;
|
||||
pub mod auth;
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ async fn app() -> anyhow::Result<()> {
|
||||
let req_id = Uuid::new_v4();
|
||||
info!("request {req_id} | {request:?}");
|
||||
let res = state
|
||||
.resolve_request(request)
|
||||
.resolve_request(request, ())
|
||||
.await
|
||||
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("{e:?}")));
|
||||
if let Err(e) = &res {
|
||||
|
||||
@@ -14,7 +14,7 @@ pub struct Build {
|
||||
|
||||
#[async_trait]
|
||||
impl Resolve<Build> for State {
|
||||
async fn resolve(&self, Build { build }: Build) -> anyhow::Result<Vec<Log>> {
|
||||
async fn resolve(&self, Build { build }: Build, _: ()) -> anyhow::Result<Vec<Log>> {
|
||||
let secrets = self.secrets.clone();
|
||||
let repo_dir = self.config.repo_dir.clone();
|
||||
let log = match self.get_docker_token(&optional_string(&build.config.docker_account)) {
|
||||
@@ -38,7 +38,7 @@ pub struct PruneImages {}
|
||||
|
||||
#[async_trait]
|
||||
impl Resolve<PruneImages> for State {
|
||||
async fn resolve(&self, _: PruneImages) -> anyhow::Result<Log> {
|
||||
async fn resolve(&self, _: PruneImages, _: ()) -> anyhow::Result<Log> {
|
||||
Ok(docker::prune_images().await)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ pub struct GetContainerList {}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl Resolve<GetContainerList> for State {
|
||||
async fn resolve(&self, _: GetContainerList) -> anyhow::Result<Vec<BasicContainerInfo>> {
|
||||
async fn resolve(&self, _: GetContainerList, _: ()) -> anyhow::Result<Vec<BasicContainerInfo>> {
|
||||
self.docker.list_containers().await
|
||||
}
|
||||
}
|
||||
@@ -39,7 +39,7 @@ fn default_tail() -> u64 {
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl Resolve<GetContainerLog> for State {
|
||||
async fn resolve(&self, req: GetContainerLog) -> anyhow::Result<Log> {
|
||||
async fn resolve(&self, req: GetContainerLog, _: ()) -> anyhow::Result<Log> {
|
||||
Ok(docker::container_log(&req.name, req.tail).await)
|
||||
}
|
||||
}
|
||||
@@ -54,7 +54,7 @@ pub struct GetContainerStats {
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl Resolve<GetContainerStats> for State {
|
||||
async fn resolve(&self, req: GetContainerStats) -> anyhow::Result<DockerContainerStats> {
|
||||
async fn resolve(&self, req: GetContainerStats, _: ()) -> anyhow::Result<DockerContainerStats> {
|
||||
let error = anyhow!("no stats matching {}", req.name);
|
||||
let mut stats = docker::container_stats(Some(req.name)).await?;
|
||||
let stats = stats.pop().ok_or(error)?;
|
||||
@@ -70,7 +70,11 @@ pub struct GetContainerStatsList {}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl Resolve<GetContainerStatsList> for State {
|
||||
async fn resolve(&self, _: GetContainerStatsList) -> anyhow::Result<Vec<DockerContainerStats>> {
|
||||
async fn resolve(
|
||||
&self,
|
||||
_: GetContainerStatsList,
|
||||
_: (),
|
||||
) -> anyhow::Result<Vec<DockerContainerStats>> {
|
||||
docker::container_stats(None).await
|
||||
}
|
||||
}
|
||||
@@ -83,7 +87,7 @@ pub struct GetNetworkList {}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl Resolve<GetNetworkList> for State {
|
||||
async fn resolve(&self, _: GetNetworkList) -> anyhow::Result<Vec<DockerNetwork>> {
|
||||
async fn resolve(&self, _: GetNetworkList, _: ()) -> anyhow::Result<Vec<DockerNetwork>> {
|
||||
self.docker.list_networks().await
|
||||
}
|
||||
}
|
||||
@@ -96,7 +100,7 @@ pub struct GetImageList {}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl Resolve<GetImageList> for State {
|
||||
async fn resolve(&self, _: GetImageList) -> anyhow::Result<Vec<ImageSummary>> {
|
||||
async fn resolve(&self, _: GetImageList, _: ()) -> anyhow::Result<Vec<ImageSummary>> {
|
||||
self.docker.list_images().await
|
||||
}
|
||||
}
|
||||
@@ -111,7 +115,7 @@ pub struct StartContainer {
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl Resolve<StartContainer> for State {
|
||||
async fn resolve(&self, req: StartContainer) -> anyhow::Result<Log> {
|
||||
async fn resolve(&self, req: StartContainer, _: ()) -> anyhow::Result<Log> {
|
||||
Ok(docker::start_container(&req.name).await)
|
||||
}
|
||||
}
|
||||
@@ -128,7 +132,7 @@ pub struct StopContainer {
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl Resolve<StopContainer> for State {
|
||||
async fn resolve(&self, req: StopContainer) -> anyhow::Result<Log> {
|
||||
async fn resolve(&self, req: StopContainer, _: ()) -> anyhow::Result<Log> {
|
||||
Ok(docker::stop_container(&req.name, req.signal, req.time).await)
|
||||
}
|
||||
}
|
||||
@@ -145,7 +149,7 @@ pub struct RemoveContainer {
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl Resolve<RemoveContainer> for State {
|
||||
async fn resolve(&self, req: RemoveContainer) -> anyhow::Result<Log> {
|
||||
async fn resolve(&self, req: RemoveContainer, _: ()) -> anyhow::Result<Log> {
|
||||
Ok(docker::stop_and_remove_container(&req.name, req.signal, req.time).await)
|
||||
}
|
||||
}
|
||||
@@ -161,7 +165,7 @@ pub struct RenameContainer {
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl Resolve<RenameContainer> for State {
|
||||
async fn resolve(&self, req: RenameContainer) -> anyhow::Result<Log> {
|
||||
async fn resolve(&self, req: RenameContainer, _: ()) -> anyhow::Result<Log> {
|
||||
Ok(docker::rename_container(&req.curr_name, &req.new_name).await)
|
||||
}
|
||||
}
|
||||
@@ -174,7 +178,7 @@ pub struct PruneContainers {}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl Resolve<PruneContainers> for State {
|
||||
async fn resolve(&self, _: PruneContainers) -> anyhow::Result<Log> {
|
||||
async fn resolve(&self, _: PruneContainers, _: ()) -> anyhow::Result<Log> {
|
||||
Ok(docker::prune_containers().await)
|
||||
}
|
||||
}
|
||||
@@ -198,6 +202,7 @@ impl Resolve<Deploy> for State {
|
||||
stop_signal,
|
||||
stop_time,
|
||||
}: Deploy,
|
||||
_: (),
|
||||
) -> anyhow::Result<Log> {
|
||||
let secrets = self.secrets.clone();
|
||||
let log = match self.get_docker_token(&optional_string(&deployment.config.docker_account)) {
|
||||
|
||||
@@ -13,7 +13,7 @@ pub struct CloneRepo {
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl Resolve<CloneRepo> for State {
|
||||
async fn resolve(&self, CloneRepo { args }: CloneRepo) -> anyhow::Result<Vec<Log>> {
|
||||
async fn resolve(&self, CloneRepo { args }: CloneRepo, _: ()) -> anyhow::Result<Vec<Log>> {
|
||||
let access_token = self.get_github_token(&args.github_account)?;
|
||||
git::clone_repo(args, self.config.repo_dir.clone(), access_token).await
|
||||
}
|
||||
@@ -38,6 +38,7 @@ impl Resolve<PullRepo> for State {
|
||||
branch,
|
||||
on_pull,
|
||||
}: PullRepo,
|
||||
_: (),
|
||||
) -> anyhow::Result<Vec<Log>> {
|
||||
let name = to_monitor_name(&name);
|
||||
Ok(git::pull(self.config.repo_dir.join(name), &branch, &on_pull).await)
|
||||
@@ -54,7 +55,7 @@ pub struct DeleteRepo {
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl Resolve<DeleteRepo> for State {
|
||||
async fn resolve(&self, DeleteRepo { name }: DeleteRepo) -> anyhow::Result<Log> {
|
||||
async fn resolve(&self, DeleteRepo { name }: DeleteRepo, _: ()) -> anyhow::Result<Log> {
|
||||
let name = to_monitor_name(&name);
|
||||
let deleted = std::fs::remove_dir_all(self.config.repo_dir.join(&name));
|
||||
let msg = match deleted {
|
||||
|
||||
@@ -87,7 +87,7 @@ pub struct GetHealthResponse {}
|
||||
|
||||
#[async_trait]
|
||||
impl ResolveToString<GetHealth> for State {
|
||||
async fn resolve_to_string(&self, _: GetHealth) -> anyhow::Result<String> {
|
||||
async fn resolve_to_string(&self, _: GetHealth, _: ()) -> anyhow::Result<String> {
|
||||
Ok(String::from("{}"))
|
||||
}
|
||||
}
|
||||
@@ -105,7 +105,7 @@ pub struct GetVersionResponse {
|
||||
|
||||
#[async_trait]
|
||||
impl Resolve<GetVersion> for State {
|
||||
async fn resolve(&self, _: GetVersion) -> anyhow::Result<GetVersionResponse> {
|
||||
async fn resolve(&self, _: GetVersion, _: ()) -> anyhow::Result<GetVersionResponse> {
|
||||
Ok(GetVersionResponse {
|
||||
version: env!("CARGO_PKG_VERSION").to_string(),
|
||||
})
|
||||
@@ -126,7 +126,7 @@ pub struct GetAccountsResponse {
|
||||
|
||||
#[async_trait]
|
||||
impl ResolveToString<GetAccounts> for State {
|
||||
async fn resolve_to_string(&self, _: GetAccounts) -> anyhow::Result<String> {
|
||||
async fn resolve_to_string(&self, _: GetAccounts, _: ()) -> anyhow::Result<String> {
|
||||
Ok(self.accounts_response.clone())
|
||||
}
|
||||
}
|
||||
@@ -139,7 +139,7 @@ pub struct GetSecrets {}
|
||||
|
||||
#[async_trait]
|
||||
impl ResolveToString<GetSecrets> for State {
|
||||
async fn resolve_to_string(&self, _: GetSecrets) -> anyhow::Result<String> {
|
||||
async fn resolve_to_string(&self, _: GetSecrets, _: ()) -> anyhow::Result<String> {
|
||||
Ok(self.secrets_response.clone())
|
||||
}
|
||||
}
|
||||
@@ -157,6 +157,7 @@ impl Resolve<RunCommand> for State {
|
||||
RunCommand {
|
||||
command: SystemCommand { path, command },
|
||||
}: RunCommand,
|
||||
_: (),
|
||||
) -> anyhow::Result<Log> {
|
||||
tokio::spawn(async move {
|
||||
let command = if path.is_empty() {
|
||||
|
||||
@@ -16,7 +16,11 @@ pub struct CreateNetwork {
|
||||
|
||||
#[async_trait]
|
||||
impl Resolve<CreateNetwork> for State {
|
||||
async fn resolve(&self, CreateNetwork { name, driver }: CreateNetwork) -> anyhow::Result<Log> {
|
||||
async fn resolve(
|
||||
&self,
|
||||
CreateNetwork { name, driver }: CreateNetwork,
|
||||
_: (),
|
||||
) -> anyhow::Result<Log> {
|
||||
Ok(docker::create_network(&name, driver).await)
|
||||
}
|
||||
}
|
||||
@@ -31,7 +35,7 @@ pub struct DeleteNetwork {
|
||||
|
||||
#[async_trait]
|
||||
impl Resolve<DeleteNetwork> for State {
|
||||
async fn resolve(&self, DeleteNetwork { name }: DeleteNetwork) -> anyhow::Result<Log> {
|
||||
async fn resolve(&self, DeleteNetwork { name }: DeleteNetwork, _: ()) -> anyhow::Result<Log> {
|
||||
Ok(docker::delete_network(&name).await)
|
||||
}
|
||||
}
|
||||
@@ -44,7 +48,7 @@ pub struct PruneNetworks {}
|
||||
|
||||
#[async_trait]
|
||||
impl Resolve<PruneNetworks> for State {
|
||||
async fn resolve(&self, _: PruneNetworks) -> anyhow::Result<Log> {
|
||||
async fn resolve(&self, _: PruneNetworks, _: ()) -> anyhow::Result<Log> {
|
||||
Ok(docker::prune_networks().await)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ pub struct GetSystemInformation {}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl ResolveToString<GetSystemInformation> for State {
|
||||
async fn resolve_to_string(&self, _: GetSystemInformation) -> anyhow::Result<String> {
|
||||
async fn resolve_to_string(&self, _: GetSystemInformation, _: ()) -> anyhow::Result<String> {
|
||||
let info = &self.stats.read().await.info;
|
||||
serde_json::to_string(info).context("failed to serialize response to string")
|
||||
}
|
||||
@@ -28,7 +28,7 @@ pub struct GetAllSystemStats {}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl ResolveToString<GetAllSystemStats> for State {
|
||||
async fn resolve_to_string(&self, _: GetAllSystemStats) -> anyhow::Result<String> {
|
||||
async fn resolve_to_string(&self, _: GetAllSystemStats, _: ()) -> anyhow::Result<String> {
|
||||
let stats = &self.stats.read().await.stats;
|
||||
serde_json::to_string(stats).context("failed to serialize response to string")
|
||||
}
|
||||
@@ -42,7 +42,7 @@ pub struct GetBasicSystemStats {}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl ResolveToString<GetBasicSystemStats> for State {
|
||||
async fn resolve_to_string(&self, _: GetBasicSystemStats) -> anyhow::Result<String> {
|
||||
async fn resolve_to_string(&self, _: GetBasicSystemStats, _: ()) -> anyhow::Result<String> {
|
||||
let stats = &self.stats.read().await.stats.basic;
|
||||
serde_json::to_string(stats).context("failed to serialize response to string")
|
||||
}
|
||||
@@ -56,7 +56,7 @@ pub struct GetCpuUsage {}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl ResolveToString<GetCpuUsage> for State {
|
||||
async fn resolve_to_string(&self, _: GetCpuUsage) -> anyhow::Result<String> {
|
||||
async fn resolve_to_string(&self, _: GetCpuUsage, _: ()) -> anyhow::Result<String> {
|
||||
let stats = &self.stats.read().await.stats.cpu;
|
||||
serde_json::to_string(stats).context("failed to serialize response to string")
|
||||
}
|
||||
@@ -70,7 +70,7 @@ pub struct GetDiskUsage {}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl ResolveToString<GetDiskUsage> for State {
|
||||
async fn resolve_to_string(&self, _: GetDiskUsage) -> anyhow::Result<String> {
|
||||
async fn resolve_to_string(&self, _: GetDiskUsage, _: ()) -> anyhow::Result<String> {
|
||||
let stats = &self.stats.read().await.stats.disk;
|
||||
serde_json::to_string(stats).context("failed to serialize response to string")
|
||||
}
|
||||
@@ -84,7 +84,7 @@ pub struct GetNetworkUsage {}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl ResolveToString<GetNetworkUsage> for State {
|
||||
async fn resolve_to_string(&self, _: GetNetworkUsage) -> anyhow::Result<String> {
|
||||
async fn resolve_to_string(&self, _: GetNetworkUsage, _: ()) -> anyhow::Result<String> {
|
||||
let stats = &self.stats.read().await.stats.network;
|
||||
serde_json::to_string(&stats).context("failed to serialize response to string")
|
||||
}
|
||||
@@ -98,7 +98,7 @@ pub struct GetSystemProcesses {}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl ResolveToString<GetSystemProcesses> for State {
|
||||
async fn resolve_to_string(&self, _: GetSystemProcesses) -> anyhow::Result<String> {
|
||||
async fn resolve_to_string(&self, _: GetSystemProcesses, _: ()) -> anyhow::Result<String> {
|
||||
let stats = &self.stats.read().await.stats.processes;
|
||||
serde_json::to_string(&stats).context("failed to serialize response to string")
|
||||
}
|
||||
@@ -112,7 +112,7 @@ pub struct GetSystemComponents {}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl ResolveToString<GetSystemComponents> for State {
|
||||
async fn resolve_to_string(&self, _: GetSystemComponents) -> anyhow::Result<String> {
|
||||
async fn resolve_to_string(&self, _: GetSystemComponents, _: ()) -> anyhow::Result<String> {
|
||||
let stats = &self.stats.read().await.stats.componenets;
|
||||
serde_json::to_string(&stats).context("failed to serialize response to string")
|
||||
}
|
||||
|
||||
+2
-1
@@ -15,4 +15,5 @@ serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
partial_derive2.workspace = true
|
||||
make_option.workspace = true
|
||||
mungos.workspace = true
|
||||
mungos.workspace = true
|
||||
dotenv.workspace = true
|
||||
@@ -0,0 +1,11 @@
|
||||
use monitor_client::MonitorClient;
|
||||
|
||||
#[allow(unused)]
|
||||
pub async fn tests() -> anyhow::Result<()> {
|
||||
dotenv::dotenv().ok();
|
||||
let monitor =
|
||||
MonitorClient::new_with_new_account("http://localhost:9001", "defi moses", "jah guide")
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
+4
-43
@@ -1,49 +1,10 @@
|
||||
use periphery_client::{requests, PeripheryClient};
|
||||
mod core;
|
||||
mod periphery;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
let periphery = PeripheryClient::new("http://localhost:9001", "monitor_passkey");
|
||||
|
||||
let version = periphery.request(requests::GetVersion {}).await?;
|
||||
println!("{version:?}");
|
||||
|
||||
let system_info = periphery.request(requests::GetSystemInformation {}).await?;
|
||||
println!("{system_info:#?}");
|
||||
|
||||
let processes = periphery.request(requests::GetSystemProcesses {}).await?;
|
||||
// println!("{system_stats:#?}");
|
||||
|
||||
let periphery_process = processes.into_iter().find(|p| p.name.contains("periphery"));
|
||||
println!("{periphery_process:#?}");
|
||||
|
||||
let accounts = periphery.request(requests::GetAccounts {}).await?;
|
||||
println!("{accounts:#?}");
|
||||
|
||||
let secrets = periphery.request(requests::GetSecrets {}).await?;
|
||||
println!("{secrets:#?}");
|
||||
|
||||
let container_stats = periphery
|
||||
.request(requests::GetContainerStatsList {})
|
||||
.await?;
|
||||
println!("{container_stats:#?}");
|
||||
|
||||
let res = periphery.request(requests::GetNetworkList {}).await?;
|
||||
println!("{res:#?}");
|
||||
|
||||
let res = periphery
|
||||
.request(requests::GetContainerStats {
|
||||
name: "monitor-mongo".into(),
|
||||
})
|
||||
.await?;
|
||||
println!("{res:#?}");
|
||||
|
||||
let res = periphery
|
||||
.request(requests::GetContainerLog {
|
||||
name: "monitor-mongo".into(),
|
||||
tail: 50,
|
||||
})
|
||||
.await?;
|
||||
println!("{res:#?}");
|
||||
// periphery::tests().await?;
|
||||
core::tests().await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
use periphery_client::{requests, PeripheryClient};
|
||||
|
||||
#[allow(unused)]
|
||||
pub async fn tests() -> anyhow::Result<()> {
|
||||
let periphery = PeripheryClient::new("http://localhost:9001", "monitor_passkey");
|
||||
|
||||
let version = periphery.request(requests::GetVersion {}).await?;
|
||||
println!("{version:?}");
|
||||
|
||||
let system_info = periphery.request(requests::GetSystemInformation {}).await?;
|
||||
println!("{system_info:#?}");
|
||||
|
||||
let processes = periphery.request(requests::GetSystemProcesses {}).await?;
|
||||
// println!("{system_stats:#?}");
|
||||
|
||||
let periphery_process = processes.into_iter().find(|p| p.name.contains("periphery"));
|
||||
println!("{periphery_process:#?}");
|
||||
|
||||
let accounts = periphery.request(requests::GetAccounts {}).await?;
|
||||
println!("{accounts:#?}");
|
||||
|
||||
let secrets = periphery.request(requests::GetSecrets {}).await?;
|
||||
println!("{secrets:#?}");
|
||||
|
||||
let container_stats = periphery
|
||||
.request(requests::GetContainerStatsList {})
|
||||
.await?;
|
||||
println!("{container_stats:#?}");
|
||||
|
||||
let res = periphery.request(requests::GetNetworkList {}).await?;
|
||||
println!("{res:#?}");
|
||||
|
||||
let res = periphery
|
||||
.request(requests::GetContainerStats {
|
||||
name: "monitor-mongo".into(),
|
||||
})
|
||||
.await?;
|
||||
println!("{res:#?}");
|
||||
|
||||
let res = periphery
|
||||
.request(requests::GetContainerLog {
|
||||
name: "monitor-mongo".into(),
|
||||
tail: 50,
|
||||
})
|
||||
.await?;
|
||||
println!("{res:#?}");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user