forked from github-starred/komodo
implement auth extension
This commit is contained in:
10
.gitignore
vendored
10
.gitignore
vendored
@@ -1,4 +1,6 @@
|
||||
target
|
||||
node_modules
|
||||
dist
|
||||
.env
|
||||
target
|
||||
node_modules
|
||||
dist
|
||||
.env
|
||||
|
||||
config.json
|
||||
110
.vscode/tasks.json
vendored
110
.vscode/tasks.json
vendored
@@ -1,56 +1,56 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"type": "cargo",
|
||||
"command": "build",
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
},
|
||||
"label": "rust: cargo build"
|
||||
},
|
||||
{
|
||||
"type": "cargo",
|
||||
"command": "fmt",
|
||||
"label": "rust: cargo fmt"
|
||||
},
|
||||
{
|
||||
"type": "cargo",
|
||||
"command": "check",
|
||||
"label": "rust: cargo check"
|
||||
},
|
||||
{
|
||||
"type": "cargo",
|
||||
"command": "run",
|
||||
"label": "rust: cargo run",
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/core"
|
||||
},
|
||||
},
|
||||
{
|
||||
"type": "cargo",
|
||||
"command": "run",
|
||||
"label": "run periphery",
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/periphery"
|
||||
},
|
||||
},
|
||||
{
|
||||
"type": "cargo",
|
||||
"command": "run",
|
||||
"label": "run core",
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/core"
|
||||
},
|
||||
},
|
||||
{
|
||||
"type": "cargo",
|
||||
"command": "run",
|
||||
"label": "run cli",
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/cli"
|
||||
},
|
||||
}
|
||||
]
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"type": "cargo",
|
||||
"command": "build",
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
},
|
||||
"label": "rust: cargo build"
|
||||
},
|
||||
{
|
||||
"type": "cargo",
|
||||
"command": "fmt",
|
||||
"label": "rust: cargo fmt"
|
||||
},
|
||||
{
|
||||
"type": "cargo",
|
||||
"command": "check",
|
||||
"label": "rust: cargo check"
|
||||
},
|
||||
{
|
||||
"type": "cargo",
|
||||
"command": "run",
|
||||
"label": "rust: cargo run",
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/core"
|
||||
},
|
||||
},
|
||||
{
|
||||
"type": "cargo",
|
||||
"command": "run",
|
||||
"label": "run periphery",
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/periphery"
|
||||
},
|
||||
},
|
||||
{
|
||||
"type": "cargo",
|
||||
"command": "run",
|
||||
"label": "run core",
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/core"
|
||||
},
|
||||
},
|
||||
{
|
||||
"type": "cargo",
|
||||
"command": "run",
|
||||
"label": "run cli",
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/cli"
|
||||
},
|
||||
}
|
||||
]
|
||||
}
|
||||
5308
Cargo.lock
generated
5308
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
14
Cargo.toml
14
Cargo.toml
@@ -1,8 +1,8 @@
|
||||
[workspace]
|
||||
|
||||
members = [
|
||||
"cli",
|
||||
"core",
|
||||
"periphery",
|
||||
"builder"
|
||||
[workspace]
|
||||
|
||||
members = [
|
||||
"cli",
|
||||
"core",
|
||||
"periphery",
|
||||
"builder"
|
||||
]
|
||||
@@ -1,20 +1,20 @@
|
||||
[package]
|
||||
name = "builder"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
docker = { package = "docker_client", path = "../lib/docker_client" }
|
||||
git = { package = "git_client", path = "../lib/git_client" }
|
||||
types = { path = "../lib/types" }
|
||||
tokio = { version = "1.21", features = ["full"] }
|
||||
axum = { version = "0.5", features = ["ws"] }
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
mungos = "0.2.24"
|
||||
anyhow = "1.0"
|
||||
thiserror = "1.0"
|
||||
envy = "0.4"
|
||||
[package]
|
||||
name = "builder"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
docker = { package = "docker_client", path = "../lib/docker_client" }
|
||||
git = { package = "git_client", path = "../lib/git_client" }
|
||||
types = { path = "../lib/types" }
|
||||
tokio = { version = "1.21", features = ["full"] }
|
||||
axum = { version = "0.5", features = ["ws"] }
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
mungos = "0.2.24"
|
||||
anyhow = "1.0"
|
||||
thiserror = "1.0"
|
||||
envy = "0.4"
|
||||
dotenv = "0.15"
|
||||
@@ -1,22 +1,22 @@
|
||||
use std::{net::SocketAddr, str::FromStr};
|
||||
|
||||
use dotenv::dotenv;
|
||||
use mungos::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct Env {
|
||||
port: u16,
|
||||
}
|
||||
|
||||
pub fn load() {
|
||||
pub fn load() -> (SocketAddr) {
|
||||
dotenv().ok();
|
||||
|
||||
let env = envy::from_env::<Env>().unwrap();
|
||||
|
||||
let socket_addr = SocketAddr::from_str(&format!("0.0.0.0:{}", env.port))
|
||||
.expect("failed to parse socket addr");
|
||||
|
||||
(socket_addr)
|
||||
}
|
||||
}
|
||||
use std::{net::SocketAddr, str::FromStr};
|
||||
|
||||
use dotenv::dotenv;
|
||||
use mungos::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct Env {
|
||||
port: u16,
|
||||
}
|
||||
|
||||
pub fn load() {
|
||||
pub fn load() -> (SocketAddr) {
|
||||
dotenv().ok();
|
||||
|
||||
let env = envy::from_env::<Env>().unwrap();
|
||||
|
||||
let socket_addr = SocketAddr::from_str(&format!("0.0.0.0:{}", env.port))
|
||||
.expect("failed to parse socket addr");
|
||||
|
||||
(socket_addr)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
mod config;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let (socket_addr) = config::load();
|
||||
}
|
||||
mod config;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let (socket_addr) = config::load();
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
[package]
|
||||
name = "cli"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
clap = { version="4.0", features=["derive"] }
|
||||
[package]
|
||||
name = "cli"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
clap = { version="4.0", features=["derive"] }
|
||||
bollard = "0.13"
|
||||
@@ -1,3 +1,3 @@
|
||||
fn main() {
|
||||
println!("Hello, world!");
|
||||
}
|
||||
fn main() {
|
||||
println!("Hello, world!");
|
||||
}
|
||||
|
||||
@@ -1,31 +1,31 @@
|
||||
[package]
|
||||
name = "monitor-core"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
docker = { package = "docker_client", path = "../lib/docker_client" }
|
||||
git = { package = "git_client", path = "../lib/git_client" }
|
||||
db = { package = "db_client", path = "../lib/db_client" }
|
||||
types = { path = "../lib/types" }
|
||||
tokio = { version = "1.21", features = ["full"] }
|
||||
axum = { version = "0.5", features = ["ws", "json"] }
|
||||
axum-extra = { version = "0.3", features = ["spa"] }
|
||||
tower = { version = "0.4", features = ["full"] }
|
||||
tower-http = { version = "0.3", features = ["cors"] }
|
||||
slack = { package = "slack_client_rs", version = "0.0.7" }
|
||||
mungos = "0.2.24"
|
||||
dotenv = "0.15"
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
serde_json = "1.0"
|
||||
envy = "0.4"
|
||||
oauth2 = "4.2.3"
|
||||
anyhow = "1.0"
|
||||
bcrypt = "0.13"
|
||||
jwt = "0.16"
|
||||
hmac = "0.12"
|
||||
sha2 = "0.10"
|
||||
[package]
|
||||
name = "monitor-core"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
docker = { package = "docker_client", path = "../lib/docker_client" }
|
||||
git = { package = "git_client", path = "../lib/git_client" }
|
||||
db = { package = "db_client", path = "../lib/db_client" }
|
||||
types = { path = "../lib/types" }
|
||||
tokio = { version = "1.21", features = ["full"] }
|
||||
axum = { version = "0.5", features = ["ws", "json"] }
|
||||
axum-extra = { version = "0.3", features = ["spa"] }
|
||||
tower = { version = "0.4", features = ["full"] }
|
||||
tower-http = { version = "0.3", features = ["cors"] }
|
||||
slack = { package = "slack_client_rs", version = "0.0.7" }
|
||||
mungos = "0.2.24"
|
||||
dotenv = "0.15"
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
serde_json = "1.0"
|
||||
envy = "0.4"
|
||||
oauth2 = "4.2.3"
|
||||
anyhow = "1.0"
|
||||
bcrypt = "0.13"
|
||||
jwt = "0.16"
|
||||
hmac = "0.12"
|
||||
sha2 = "0.10"
|
||||
async_timing_util = "0.1.11"
|
||||
17
core/config.json.example
Normal file
17
core/config.json.example
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"PORT": 9000,
|
||||
"PASSKEY": "your-default-passkey",
|
||||
"DOCKER_ACCOUNTS": {},
|
||||
"GITHUB_ACCOUNTS": {},
|
||||
"GITHUB_OAUTH": {
|
||||
"ID": "your_client_id",
|
||||
"SECRET": "your_client_secret"
|
||||
},
|
||||
"GITHUB_WEBHOOK_SECRET": "your_random_webhook_secret",
|
||||
"JWT_SECRET": "your_jwt_secret",
|
||||
"JWT_VALID_FOR": "1-wk",
|
||||
"SLACK_URL": "your_slack_app_webhook_url",
|
||||
"MONGO_URI": "your_mongo_uri",
|
||||
"MONGO_APP_NAME": "monitor_core",
|
||||
"MONGO_DB_NAME": "monitor"
|
||||
}
|
||||
@@ -1,5 +1,28 @@
|
||||
use axum::Router;
|
||||
use anyhow::anyhow;
|
||||
use axum::{http::StatusCode, middleware, routing::get, Extension, Json, Router};
|
||||
use db::DbExtension;
|
||||
use types::{User, UserId};
|
||||
|
||||
use crate::{auth::auth_request, helpers::handle_anyhow_error, ResponseResult};
|
||||
|
||||
pub fn router() -> Router {
|
||||
Router::new()
|
||||
.route(
|
||||
"/user",
|
||||
get(|user_id, db| async { get_user(user_id, db).await.map_err(handle_anyhow_error) }),
|
||||
)
|
||||
.layer(middleware::from_fn(auth_request))
|
||||
}
|
||||
|
||||
async fn get_user(
|
||||
Extension(user_id): Extension<UserId>,
|
||||
Extension(db): DbExtension,
|
||||
) -> anyhow::Result<Json<User>> {
|
||||
let mut user = db
|
||||
.users
|
||||
.find_one_by_id(&user_id)
|
||||
.await?
|
||||
.ok_or(anyhow!("did not find user"))?;
|
||||
user.password = None;
|
||||
Ok(Json(user))
|
||||
}
|
||||
|
||||
@@ -7,7 +7,10 @@ use types::CoreConfig;
|
||||
pub type GithubOauthExtension = Extension<Arc<BasicClient>>;
|
||||
|
||||
pub fn router(config: &CoreConfig) -> Router {
|
||||
Router::new().layer(github_oauth_extension(config, format!("")))
|
||||
Router::new().layer(github_oauth_extension(
|
||||
config,
|
||||
format!("http://localhost:9000/auth/github/callback"),
|
||||
))
|
||||
}
|
||||
|
||||
fn github_oauth_extension(config: &CoreConfig, redirect_url: String) -> GithubOauthExtension {
|
||||
|
||||
@@ -2,22 +2,24 @@ use std::sync::Arc;
|
||||
|
||||
use anyhow::{anyhow, Context};
|
||||
use async_timing_util::{get_timelength_in_ms, unix_timestamp_ms, Timelength};
|
||||
use axum::Extension;
|
||||
use axum::{body::Body, http::Request, Extension};
|
||||
use db::DbClient;
|
||||
use hmac::{Hmac, Mac};
|
||||
use jwt::{SignWithKey, VerifyWithKey};
|
||||
use mungos::{Deserialize, Serialize};
|
||||
use sha2::Sha256;
|
||||
use types::CoreConfig;
|
||||
use types::{CoreConfig, User, UserId};
|
||||
|
||||
pub type JwtExtension = Extension<Arc<JwtClient>>;
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct JwtClaims {
|
||||
pub id: String,
|
||||
pub id: UserId,
|
||||
pub iat: u128,
|
||||
pub exp: u128,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct JwtClient {
|
||||
key: Hmac<Sha256>,
|
||||
valid_for_ms: u128,
|
||||
@@ -47,12 +49,34 @@ impl JwtClient {
|
||||
Ok(jwt)
|
||||
}
|
||||
|
||||
pub fn validate(&self, jwt: &str) -> anyhow::Result<JwtClaims> {
|
||||
pub async fn authenticate(&self, req: &Request<Body>) -> anyhow::Result<UserId> {
|
||||
let jwt = req
|
||||
.headers()
|
||||
.get("authorization")
|
||||
.ok_or(anyhow!(
|
||||
"no authorization header provided. must be Bearer <jwt_token>"
|
||||
))?
|
||||
.to_str()?
|
||||
.replace("Bearer ", "")
|
||||
.replace("bearer ", "");
|
||||
let claims: JwtClaims = jwt
|
||||
.verify_with_key(&self.key)
|
||||
.context("failed to verify claims")?;
|
||||
if claims.exp < unix_timestamp_ms() {
|
||||
Ok(claims)
|
||||
if claims.exp > unix_timestamp_ms() {
|
||||
let users_collection = &req
|
||||
.extensions()
|
||||
.get::<Arc<DbClient>>()
|
||||
.ok_or(anyhow!("failed at getting db handle"))?
|
||||
.users;
|
||||
let user = users_collection
|
||||
.find_one_by_id(&claims.id)
|
||||
.await?
|
||||
.ok_or(anyhow!("did not find user with id {}", claims.id))?;
|
||||
if user.enabled {
|
||||
Ok(claims.id)
|
||||
} else {
|
||||
Err(anyhow!("user not enabled"))
|
||||
}
|
||||
} else {
|
||||
Err(anyhow!("token has expired"))
|
||||
}
|
||||
|
||||
@@ -1,88 +1,88 @@
|
||||
use anyhow::{anyhow, Context};
|
||||
use axum::{extract::Json, routing::post, Extension, Router};
|
||||
use db::{DbClient, DbExtension};
|
||||
use mungos::{doc, Deserialize};
|
||||
use types::{User, UserCredentials};
|
||||
|
||||
use crate::helpers::handle_anyhow_error;
|
||||
|
||||
use super::jwt::JwtExtension;
|
||||
|
||||
const BCRYPT_COST: u32 = 10;
|
||||
|
||||
pub fn router() -> Router {
|
||||
Router::new()
|
||||
.route(
|
||||
"/create_user",
|
||||
post(|db, jwt, body| async {
|
||||
create_user_handler(db, jwt, body)
|
||||
.await
|
||||
.map_err(handle_anyhow_error)
|
||||
}),
|
||||
)
|
||||
.route(
|
||||
"/login",
|
||||
post(|db, jwt, body| async {
|
||||
login_handler(db, jwt, body)
|
||||
.await
|
||||
.map_err(handle_anyhow_error)
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
async fn create_user_handler(
|
||||
Extension(db): DbExtension,
|
||||
Extension(jwt): JwtExtension,
|
||||
Json(UserCredentials { username, password }): Json<UserCredentials>,
|
||||
) -> anyhow::Result<String> {
|
||||
let password = bcrypt::hash(password, BCRYPT_COST).context("failed to hash password")?;
|
||||
|
||||
let user = User {
|
||||
username,
|
||||
password: Some(password),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let user_id = db
|
||||
.users
|
||||
.create_one(user)
|
||||
.await
|
||||
.context("failed to create user")?;
|
||||
|
||||
let jwt = jwt
|
||||
.generate(user_id)
|
||||
.context("failed to generate jwt for user")?;
|
||||
|
||||
Ok(jwt)
|
||||
}
|
||||
|
||||
async fn login_handler(
|
||||
Extension(db): DbExtension,
|
||||
Extension(jwt): JwtExtension,
|
||||
Json(UserCredentials { username, password }): Json<UserCredentials>,
|
||||
) -> anyhow::Result<String> {
|
||||
let user = db
|
||||
.users
|
||||
.find_one(doc! { "username": &username }, None)
|
||||
.await
|
||||
.context("failed at mongo query")?
|
||||
.ok_or(anyhow!("did not find user with username {username}"))?;
|
||||
|
||||
let user_pw_hash = user
|
||||
.password
|
||||
.ok_or(anyhow!("invalid login, user does not have password login"))?;
|
||||
|
||||
let verified = bcrypt::verify(password, &user_pw_hash).context("failed at verify password")?;
|
||||
|
||||
if !verified {
|
||||
return Err(anyhow!("invalid credentials"));
|
||||
}
|
||||
|
||||
let user_id = user.id.ok_or(anyhow!("user does not have id"))?.to_string();
|
||||
|
||||
let jwt = jwt
|
||||
.generate(user_id)
|
||||
.context("failed at generating jwt for user")?;
|
||||
|
||||
Ok(jwt)
|
||||
}
|
||||
use anyhow::{anyhow, Context};
|
||||
use axum::{extract::Json, routing::post, Extension, Router};
|
||||
use db::{DbClient, DbExtension};
|
||||
use mungos::{doc, Deserialize};
|
||||
use types::{User, UserCredentials};
|
||||
|
||||
use crate::helpers::handle_anyhow_error;
|
||||
|
||||
use super::jwt::JwtExtension;
|
||||
|
||||
const BCRYPT_COST: u32 = 10;
|
||||
|
||||
pub fn router() -> Router {
|
||||
Router::new()
|
||||
.route(
|
||||
"/create_user",
|
||||
post(|db, jwt, body| async {
|
||||
create_user_handler(db, jwt, body)
|
||||
.await
|
||||
.map_err(handle_anyhow_error)
|
||||
}),
|
||||
)
|
||||
.route(
|
||||
"/login",
|
||||
post(|db, jwt, body| async {
|
||||
login_handler(db, jwt, body)
|
||||
.await
|
||||
.map_err(handle_anyhow_error)
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
async fn create_user_handler(
|
||||
Extension(db): DbExtension,
|
||||
Extension(jwt): JwtExtension,
|
||||
Json(UserCredentials { username, password }): Json<UserCredentials>,
|
||||
) -> anyhow::Result<String> {
|
||||
let password = bcrypt::hash(password, BCRYPT_COST).context("failed to hash password")?;
|
||||
|
||||
let user = User {
|
||||
username,
|
||||
password: Some(password),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let user_id = db
|
||||
.users
|
||||
.create_one(user)
|
||||
.await
|
||||
.context("failed to create user")?;
|
||||
|
||||
let jwt = jwt
|
||||
.generate(user_id)
|
||||
.context("failed to generate jwt for user")?;
|
||||
|
||||
Ok(jwt)
|
||||
}
|
||||
|
||||
async fn login_handler(
|
||||
Extension(db): DbExtension,
|
||||
Extension(jwt): JwtExtension,
|
||||
Json(UserCredentials { username, password }): Json<UserCredentials>,
|
||||
) -> anyhow::Result<String> {
|
||||
let user = db
|
||||
.users
|
||||
.find_one(doc! { "username": &username }, None)
|
||||
.await
|
||||
.context("failed at mongo query")?
|
||||
.ok_or(anyhow!("did not find user with username {username}"))?;
|
||||
|
||||
let user_pw_hash = user
|
||||
.password
|
||||
.ok_or(anyhow!("invalid login, user does not have password login"))?;
|
||||
|
||||
let verified = bcrypt::verify(password, &user_pw_hash).context("failed at verify password")?;
|
||||
|
||||
if !verified {
|
||||
return Err(anyhow!("invalid credentials"));
|
||||
}
|
||||
|
||||
let user_id = user.id.ok_or(anyhow!("user does not have id"))?.to_string();
|
||||
|
||||
let jwt = jwt
|
||||
.generate(user_id)
|
||||
.context("failed at generating jwt for user")?;
|
||||
|
||||
Ok(jwt)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
use axum::Router;
|
||||
use types::CoreConfig;
|
||||
use std::sync::Arc;
|
||||
|
||||
use anyhow::Context;
|
||||
use axum::{
|
||||
body::Body,
|
||||
http::{header, Request, StatusCode},
|
||||
middleware::Next,
|
||||
response::{IntoResponse, Response},
|
||||
Router,
|
||||
};
|
||||
use types::{CoreConfig, UserId};
|
||||
|
||||
mod github;
|
||||
mod jwt;
|
||||
@@ -12,3 +21,19 @@ pub fn router(config: &CoreConfig) -> Router {
|
||||
.nest("/local", local::router())
|
||||
.nest("/github", github::router(config))
|
||||
}
|
||||
|
||||
pub async fn auth_request(
|
||||
mut req: Request<Body>,
|
||||
next: Next<Body>,
|
||||
) -> Result<Response, (StatusCode, String)> {
|
||||
let jwt_client = req.extensions().get::<Arc<JwtClient>>().ok_or((
|
||||
StatusCode::UNAUTHORIZED,
|
||||
"failed to get jwt client extension".to_string(),
|
||||
))?;
|
||||
let user_id: UserId = jwt_client
|
||||
.authenticate(&req)
|
||||
.await
|
||||
.map_err(|e| (StatusCode::UNAUTHORIZED, format!("error: {e:#?}")))?;
|
||||
req.extensions_mut().insert(user_id);
|
||||
Ok(next.run(req).await)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use std::{
|
||||
env,
|
||||
fs::File,
|
||||
net::{IpAddr, SocketAddr},
|
||||
str::FromStr,
|
||||
@@ -10,13 +11,8 @@ use dotenv::dotenv;
|
||||
use mungos::{Deserialize, Mungos};
|
||||
use types::CoreConfig;
|
||||
|
||||
pub async fn load() -> CoreConfig {
|
||||
let config = load_config();
|
||||
|
||||
config
|
||||
}
|
||||
|
||||
fn load_config() -> CoreConfig {
|
||||
let file = File::open("/secrets/secrets.json");
|
||||
todo!()
|
||||
pub fn load() -> CoreConfig {
|
||||
let config_path = env::var("CONFIG_PATH").unwrap_or("./config.json".to_string());
|
||||
let file = File::open(config_path).expect("failed to open config file");
|
||||
serde_json::from_reader(file).unwrap()
|
||||
}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
use std::{net::SocketAddr, str::FromStr};
|
||||
|
||||
use axum::http::StatusCode;
|
||||
use types::CoreConfig;
|
||||
|
||||
pub fn handle_anyhow_error(err: anyhow::Error) -> (StatusCode, String) {
|
||||
(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("Internal Error: {err:#?}"),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_socket_addr(config: &CoreConfig) -> SocketAddr {
|
||||
SocketAddr::from_str(&format!("0.0.0.0:{}", config.port)).expect("failed to parse socket addr")
|
||||
}
|
||||
use std::{net::SocketAddr, str::FromStr};
|
||||
|
||||
use axum::http::StatusCode;
|
||||
use types::CoreConfig;
|
||||
|
||||
pub fn handle_anyhow_error(err: anyhow::Error) -> (StatusCode, String) {
|
||||
(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("Internal Error: {err:#?}"),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_socket_addr(config: &CoreConfig) -> SocketAddr {
|
||||
SocketAddr::from_str(&format!("0.0.0.0:{}", config.port)).expect("failed to parse socket addr")
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#![allow(unused)]
|
||||
|
||||
use auth::JwtClient;
|
||||
use axum::Router;
|
||||
use axum::{http::StatusCode, Router};
|
||||
use db::DbClient;
|
||||
use docker::DockerClient;
|
||||
use helpers::get_socket_addr;
|
||||
@@ -11,15 +11,19 @@ mod auth;
|
||||
mod config;
|
||||
mod helpers;
|
||||
|
||||
type ResponseResult<T> = Result<T, (StatusCode, String)>;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let config = config::load().await;
|
||||
let config = config::load();
|
||||
|
||||
let app = Router::new()
|
||||
.nest("/api", api::router())
|
||||
.nest("/auth", auth::router(&config))
|
||||
.layer(DbClient::extension((&config).into()).await)
|
||||
.layer(JwtClient::extension(&config));
|
||||
.layer(JwtClient::extension(&config))
|
||||
.layer(DbClient::extension((&config).into()).await);
|
||||
|
||||
println!("starting monitor_core on localhost:{}", config.port);
|
||||
|
||||
axum::Server::bind(&get_socket_addr(&config))
|
||||
.serve(app.into_make_service())
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<link rel="shortcut icon" type="image/ico" href="/src/assets/favicon.ico" />
|
||||
<title>Solid App</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
|
||||
<script src="/src/index.tsx" type="module"></script>
|
||||
</body>
|
||||
</html>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<link rel="shortcut icon" type="image/ico" href="/src/assets/favicon.ico" />
|
||||
<title>Solid App</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
|
||||
<script src="/src/index.tsx" type="module"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
{
|
||||
"name": "vite-template-solid",
|
||||
"version": "0.0.0",
|
||||
"description": "",
|
||||
"scripts": {
|
||||
"start": "vite",
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"serve": "vite preview"
|
||||
},
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"typescript": "^4.8.2",
|
||||
"vite": "^3.0.9",
|
||||
"vite-plugin-solid": "^2.3.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"solid-js": "^1.5.1"
|
||||
}
|
||||
}
|
||||
{
|
||||
"name": "vite-template-solid",
|
||||
"version": "0.0.0",
|
||||
"description": "",
|
||||
"scripts": {
|
||||
"start": "vite",
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"serve": "vite preview"
|
||||
},
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"typescript": "^4.8.2",
|
||||
"vite": "^3.0.9",
|
||||
"vite-plugin-solid": "^2.3.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"solid-js": "^1.5.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,33 +1,33 @@
|
||||
.App {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.logo {
|
||||
animation: logo-spin infinite 20s linear;
|
||||
height: 40vmin;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.header {
|
||||
background-color: #282c34;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: calc(10px + 2vmin);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.link {
|
||||
color: #b318f0;
|
||||
}
|
||||
|
||||
@keyframes logo-spin {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
.App {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.logo {
|
||||
animation: logo-spin infinite 20s linear;
|
||||
height: 40vmin;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.header {
|
||||
background-color: #282c34;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: calc(10px + 2vmin);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.link {
|
||||
color: #b318f0;
|
||||
}
|
||||
|
||||
@keyframes logo-spin {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,27 +1,27 @@
|
||||
import type { Component } from 'solid-js';
|
||||
|
||||
import logo from './logo.svg';
|
||||
import styles from './App.module.css';
|
||||
|
||||
const App: Component = () => {
|
||||
return (
|
||||
<div class={styles.App}>
|
||||
<header class={styles.header}>
|
||||
<img src={logo} class={styles.logo} alt="logo" />
|
||||
<p>
|
||||
Edit <code>src/App.tsx</code> and save to reload.
|
||||
</p>
|
||||
<a
|
||||
class={styles.link}
|
||||
href="https://github.com/solidjs/solid"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Learn Solid
|
||||
</a>
|
||||
</header>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
||||
import type { Component } from 'solid-js';
|
||||
|
||||
import logo from './logo.svg';
|
||||
import styles from './App.module.css';
|
||||
|
||||
const App: Component = () => {
|
||||
return (
|
||||
<div class={styles.App}>
|
||||
<header class={styles.header}>
|
||||
<img src={logo} class={styles.logo} alt="logo" />
|
||||
<p>
|
||||
Edit <code>src/App.tsx</code> and save to reload.
|
||||
</p>
|
||||
<a
|
||||
class={styles.link}
|
||||
href="https://github.com/solidjs/solid"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Learn Solid
|
||||
</a>
|
||||
</header>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||
sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
||||
monospace;
|
||||
}
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||
sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
||||
monospace;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* @refresh reload */
|
||||
import { render } from 'solid-js/web';
|
||||
|
||||
import './index.css';
|
||||
import App from './App';
|
||||
|
||||
render(() => <App />, document.getElementById('root') as HTMLElement);
|
||||
/* @refresh reload */
|
||||
import { render } from 'solid-js/web';
|
||||
|
||||
import './index.css';
|
||||
import App from './App';
|
||||
|
||||
render(() => <App />, document.getElementById('root') as HTMLElement);
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"strict": true,
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "node",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"esModuleInterop": true,
|
||||
"jsx": "preserve",
|
||||
"jsxImportSource": "solid-js",
|
||||
"types": ["vite/client"],
|
||||
"noEmit": true,
|
||||
"isolatedModules": true
|
||||
}
|
||||
}
|
||||
{
|
||||
"compilerOptions": {
|
||||
"strict": true,
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "node",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"esModuleInterop": true,
|
||||
"jsx": "preserve",
|
||||
"jsxImportSource": "solid-js",
|
||||
"types": ["vite/client"],
|
||||
"noEmit": true,
|
||||
"isolatedModules": true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { defineConfig } from 'vite';
|
||||
import solidPlugin from 'vite-plugin-solid';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [solidPlugin()],
|
||||
server: {
|
||||
port: 3000,
|
||||
},
|
||||
build: {
|
||||
target: 'esnext',
|
||||
},
|
||||
});
|
||||
import { defineConfig } from 'vite';
|
||||
import solidPlugin from 'vite-plugin-solid';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [solidPlugin()],
|
||||
server: {
|
||||
port: 3000,
|
||||
},
|
||||
build: {
|
||||
target: 'esnext',
|
||||
},
|
||||
});
|
||||
|
||||
1524
frontend/yarn.lock
1524
frontend/yarn.lock
File diff suppressed because it is too large
Load Diff
@@ -1,12 +1,12 @@
|
||||
[package]
|
||||
name = "db_client"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
types = { path = "../types" }
|
||||
axum = "0.5"
|
||||
mungos = "0.2.24"
|
||||
[package]
|
||||
name = "db_client"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
types = { path = "../types" }
|
||||
axum = "0.5"
|
||||
mungos = "0.2.24"
|
||||
anyhow = "1.0"
|
||||
@@ -1,84 +1,84 @@
|
||||
use anyhow::Context;
|
||||
use mungos::{Collection, Mungos};
|
||||
use types::{Build, Deployment, Procedure, Server, Update, User};
|
||||
|
||||
pub async fn users_collection(mungos: &Mungos, db_name: &str) -> anyhow::Result<Collection<User>> {
|
||||
let coll = mungos.collection(db_name, "users");
|
||||
coll.create_unique_index("username")
|
||||
.await
|
||||
.context("failed at creating username index")?;
|
||||
Ok(coll)
|
||||
}
|
||||
|
||||
pub async fn servers_collection(
|
||||
mungos: &Mungos,
|
||||
db_name: &str,
|
||||
) -> anyhow::Result<Collection<Server>> {
|
||||
let coll = mungos.collection(db_name, "servers");
|
||||
coll.create_unique_index("name")
|
||||
.await
|
||||
.context("failed at creating name index")?;
|
||||
coll.create_index("permissions")
|
||||
.await
|
||||
.context("failed at creating permissions index")?;
|
||||
Ok(coll)
|
||||
}
|
||||
|
||||
pub async fn deployments_collection(
|
||||
mungos: &Mungos,
|
||||
db_name: &str,
|
||||
) -> anyhow::Result<Collection<Deployment>> {
|
||||
let coll = mungos.collection(db_name, "deployments");
|
||||
coll.create_unique_index("name")
|
||||
.await
|
||||
.context("failed at creating name index")?;
|
||||
coll.create_index("permissions")
|
||||
.await
|
||||
.context("failed at creating permissions index")?;
|
||||
Ok(coll)
|
||||
}
|
||||
|
||||
pub async fn builds_collection(
|
||||
mungos: &Mungos,
|
||||
db_name: &str,
|
||||
) -> anyhow::Result<Collection<Build>> {
|
||||
let coll = mungos.collection(db_name, "builds");
|
||||
coll.create_unique_index("name")
|
||||
.await
|
||||
.context("failed at creating name index")?;
|
||||
coll.create_index("permissions")
|
||||
.await
|
||||
.context("failed at creating permissions index")?;
|
||||
Ok(coll)
|
||||
}
|
||||
|
||||
pub async fn updates_collection(
|
||||
mungos: &Mungos,
|
||||
db_name: &str,
|
||||
) -> anyhow::Result<Collection<Update>> {
|
||||
let coll = mungos.collection(db_name, "updates");
|
||||
coll.create_index("entity_id")
|
||||
.await
|
||||
.context("failed at creating entity_id index")?;
|
||||
coll.create_index("ts")
|
||||
.await
|
||||
.context("failed at creating ts index")?;
|
||||
coll.create_index("operator")
|
||||
.await
|
||||
.context("failed at creating operator index")?;
|
||||
Ok(coll)
|
||||
}
|
||||
|
||||
pub async fn procedures_collection(
|
||||
mungos: &Mungos,
|
||||
db_name: &str,
|
||||
) -> anyhow::Result<Collection<Procedure>> {
|
||||
let coll = mungos.collection(db_name, "procedures");
|
||||
coll.create_index("name")
|
||||
.await
|
||||
.context("failed at creating entity_id index")?;
|
||||
coll.create_index("permissions")
|
||||
.await
|
||||
.context("failed at creating permissions index")?;
|
||||
Ok(coll)
|
||||
}
|
||||
use anyhow::Context;
|
||||
use mungos::{Collection, Mungos};
|
||||
use types::{Build, Deployment, Procedure, Server, Update, User};
|
||||
|
||||
pub async fn users_collection(mungos: &Mungos, db_name: &str) -> anyhow::Result<Collection<User>> {
|
||||
let coll = mungos.collection(db_name, "users");
|
||||
coll.create_unique_index("username")
|
||||
.await
|
||||
.context("failed at creating username index")?;
|
||||
Ok(coll)
|
||||
}
|
||||
|
||||
pub async fn servers_collection(
|
||||
mungos: &Mungos,
|
||||
db_name: &str,
|
||||
) -> anyhow::Result<Collection<Server>> {
|
||||
let coll = mungos.collection(db_name, "servers");
|
||||
coll.create_unique_index("name")
|
||||
.await
|
||||
.context("failed at creating name index")?;
|
||||
coll.create_index("permissions")
|
||||
.await
|
||||
.context("failed at creating permissions index")?;
|
||||
Ok(coll)
|
||||
}
|
||||
|
||||
pub async fn deployments_collection(
|
||||
mungos: &Mungos,
|
||||
db_name: &str,
|
||||
) -> anyhow::Result<Collection<Deployment>> {
|
||||
let coll = mungos.collection(db_name, "deployments");
|
||||
coll.create_unique_index("name")
|
||||
.await
|
||||
.context("failed at creating name index")?;
|
||||
coll.create_index("permissions")
|
||||
.await
|
||||
.context("failed at creating permissions index")?;
|
||||
Ok(coll)
|
||||
}
|
||||
|
||||
pub async fn builds_collection(
|
||||
mungos: &Mungos,
|
||||
db_name: &str,
|
||||
) -> anyhow::Result<Collection<Build>> {
|
||||
let coll = mungos.collection(db_name, "builds");
|
||||
coll.create_unique_index("name")
|
||||
.await
|
||||
.context("failed at creating name index")?;
|
||||
coll.create_index("permissions")
|
||||
.await
|
||||
.context("failed at creating permissions index")?;
|
||||
Ok(coll)
|
||||
}
|
||||
|
||||
pub async fn updates_collection(
|
||||
mungos: &Mungos,
|
||||
db_name: &str,
|
||||
) -> anyhow::Result<Collection<Update>> {
|
||||
let coll = mungos.collection(db_name, "updates");
|
||||
coll.create_index("entity_id")
|
||||
.await
|
||||
.context("failed at creating entity_id index")?;
|
||||
coll.create_index("ts")
|
||||
.await
|
||||
.context("failed at creating ts index")?;
|
||||
coll.create_index("operator")
|
||||
.await
|
||||
.context("failed at creating operator index")?;
|
||||
Ok(coll)
|
||||
}
|
||||
|
||||
pub async fn procedures_collection(
|
||||
mungos: &Mungos,
|
||||
db_name: &str,
|
||||
) -> anyhow::Result<Collection<Procedure>> {
|
||||
let coll = mungos.collection(db_name, "procedures");
|
||||
coll.create_index("name")
|
||||
.await
|
||||
.context("failed at creating entity_id index")?;
|
||||
coll.create_index("permissions")
|
||||
.await
|
||||
.context("failed at creating permissions index")?;
|
||||
Ok(coll)
|
||||
}
|
||||
|
||||
@@ -1,73 +1,73 @@
|
||||
use std::{sync::Arc, time::Duration};
|
||||
|
||||
use axum::Extension;
|
||||
use collections::{
|
||||
builds_collection, deployments_collection, procedures_collection, servers_collection,
|
||||
updates_collection, users_collection,
|
||||
};
|
||||
use mungos::{Collection, Mungos};
|
||||
use types::{Build, CoreConfig, Deployment, Procedure, Server, Update, User};
|
||||
|
||||
mod collections;
|
||||
|
||||
pub type DbExtension = Extension<Arc<DbClient>>;
|
||||
|
||||
pub struct DbClient {
|
||||
pub users: Collection<User>,
|
||||
pub servers: Collection<Server>,
|
||||
pub deployments: Collection<Deployment>,
|
||||
pub builds: Collection<Build>,
|
||||
pub procedures: Collection<Procedure>,
|
||||
pub updates: Collection<Update>,
|
||||
}
|
||||
|
||||
pub struct DbConfig {
|
||||
mongo_uri: String,
|
||||
mongo_app_name: String,
|
||||
mongo_db_name: String,
|
||||
}
|
||||
|
||||
impl From<&CoreConfig> for DbConfig {
|
||||
fn from(config: &CoreConfig) -> DbConfig {
|
||||
DbConfig {
|
||||
mongo_uri: config.mongo_uri.clone(),
|
||||
mongo_app_name: config.mongo_app_name.clone(),
|
||||
mongo_db_name: config.mongo_db_name.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DbClient {
|
||||
pub async fn extension(config: DbConfig) -> DbExtension {
|
||||
let db_name = &config.mongo_db_name;
|
||||
let mungos = Mungos::new(
|
||||
&config.mongo_uri,
|
||||
&config.mongo_app_name,
|
||||
Duration::from_secs(3),
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.expect("failed to initialize mungos");
|
||||
let client = DbClient {
|
||||
users: users_collection(&mungos, db_name)
|
||||
.await
|
||||
.expect("failed to make users collection"),
|
||||
servers: servers_collection(&mungos, db_name)
|
||||
.await
|
||||
.expect("failed to make servers collection"),
|
||||
deployments: deployments_collection(&mungos, db_name)
|
||||
.await
|
||||
.expect("failed to make deployments collection"),
|
||||
builds: builds_collection(&mungos, db_name)
|
||||
.await
|
||||
.expect("failed to make builds collection"),
|
||||
updates: updates_collection(&mungos, db_name)
|
||||
.await
|
||||
.expect("failed to make updates collection"),
|
||||
procedures: procedures_collection(&mungos, db_name)
|
||||
.await
|
||||
.expect("failed to make procedures collection"),
|
||||
};
|
||||
Extension(Arc::new(client))
|
||||
}
|
||||
}
|
||||
use std::{sync::Arc, time::Duration};
|
||||
|
||||
use axum::Extension;
|
||||
use collections::{
|
||||
builds_collection, deployments_collection, procedures_collection, servers_collection,
|
||||
updates_collection, users_collection,
|
||||
};
|
||||
use mungos::{Collection, Mungos};
|
||||
use types::{Build, CoreConfig, Deployment, Procedure, Server, Update, User};
|
||||
|
||||
mod collections;
|
||||
|
||||
pub type DbExtension = Extension<Arc<DbClient>>;
|
||||
|
||||
pub struct DbClient {
|
||||
pub users: Collection<User>,
|
||||
pub servers: Collection<Server>,
|
||||
pub deployments: Collection<Deployment>,
|
||||
pub builds: Collection<Build>,
|
||||
pub procedures: Collection<Procedure>,
|
||||
pub updates: Collection<Update>,
|
||||
}
|
||||
|
||||
pub struct DbConfig {
|
||||
mongo_uri: String,
|
||||
mongo_app_name: String,
|
||||
mongo_db_name: String,
|
||||
}
|
||||
|
||||
impl From<&CoreConfig> for DbConfig {
|
||||
fn from(config: &CoreConfig) -> DbConfig {
|
||||
DbConfig {
|
||||
mongo_uri: config.mongo_uri.clone(),
|
||||
mongo_app_name: config.mongo_app_name.clone(),
|
||||
mongo_db_name: config.mongo_db_name.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DbClient {
|
||||
pub async fn extension(config: DbConfig) -> DbExtension {
|
||||
let db_name = &config.mongo_db_name;
|
||||
let mungos = Mungos::new(
|
||||
&config.mongo_uri,
|
||||
&config.mongo_app_name,
|
||||
Duration::from_secs(3),
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.expect("failed to initialize mungos");
|
||||
let client = DbClient {
|
||||
users: users_collection(&mungos, db_name)
|
||||
.await
|
||||
.expect("failed to make users collection"),
|
||||
servers: servers_collection(&mungos, db_name)
|
||||
.await
|
||||
.expect("failed to make servers collection"),
|
||||
deployments: deployments_collection(&mungos, db_name)
|
||||
.await
|
||||
.expect("failed to make deployments collection"),
|
||||
builds: builds_collection(&mungos, db_name)
|
||||
.await
|
||||
.expect("failed to make builds collection"),
|
||||
updates: updates_collection(&mungos, db_name)
|
||||
.await
|
||||
.expect("failed to make updates collection"),
|
||||
procedures: procedures_collection(&mungos, db_name)
|
||||
.await
|
||||
.expect("failed to make procedures collection"),
|
||||
};
|
||||
Extension(Arc::new(client))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
[package]
|
||||
name = "docker_client"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
types = { path = "../types" }
|
||||
run_command = { version = "0.0.5", features = ["async_tokio"] }
|
||||
bollard = "0.13"
|
||||
anyhow = "1.0"
|
||||
[package]
|
||||
name = "docker_client"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
types = { path = "../types" }
|
||||
run_command = { version = "0.0.5", features = ["async_tokio"] }
|
||||
bollard = "0.13"
|
||||
anyhow = "1.0"
|
||||
thiserror = "1.0"
|
||||
@@ -1,9 +1,9 @@
|
||||
use types::Build;
|
||||
|
||||
use crate::DockerClient;
|
||||
|
||||
impl DockerClient {
|
||||
pub async fn build(&self, build: Build) -> anyhow::Result<()> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
use types::Build;
|
||||
|
||||
use crate::DockerClient;
|
||||
|
||||
impl DockerClient {
|
||||
pub async fn build(&self, build: Build) -> anyhow::Result<()> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,45 +1,45 @@
|
||||
use run_command::{async_run_command, CommandOutput};
|
||||
use types::{Deployment, Log};
|
||||
|
||||
use crate::DockerClient;
|
||||
|
||||
impl DockerClient {
|
||||
pub async fn deploy(&self, deployment: &Deployment) -> (bool, Log) {
|
||||
let docker_run = docker_run_command(deployment);
|
||||
let output = async_run_command(&docker_run).await;
|
||||
output_into_log("docker run", output)
|
||||
}
|
||||
|
||||
pub async fn docker_start_command(&self, container_name: &str) -> (bool, Log) {
|
||||
let command = format!("start stop {container_name}");
|
||||
let output = async_run_command(&command).await;
|
||||
output_into_log("docker stop", output)
|
||||
}
|
||||
|
||||
pub async fn docker_stop_command(&self, container_name: &str) -> (bool, Log) {
|
||||
let command = format!("docker stop {container_name}");
|
||||
let output = async_run_command(&command).await;
|
||||
output_into_log("docker stop", output)
|
||||
}
|
||||
|
||||
pub async fn docker_stop_and_remove(&self, container_name: &str) -> (bool, Log) {
|
||||
let command =
|
||||
format!("docker stop {container_name} && docker container rm {container_name}");
|
||||
let output = async_run_command(&command).await;
|
||||
output_into_log("docker stop and remove", output)
|
||||
}
|
||||
}
|
||||
|
||||
fn docker_run_command(deployment: &Deployment) -> String {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn output_into_log(stage: &str, output: CommandOutput) -> (bool, Log) {
|
||||
let success = output.success();
|
||||
let log = Log {
|
||||
stage: stage.to_string(),
|
||||
stdout: output.stdout,
|
||||
stderr: output.stderr,
|
||||
};
|
||||
(success, log)
|
||||
}
|
||||
use run_command::{async_run_command, CommandOutput};
|
||||
use types::{Deployment, Log};
|
||||
|
||||
use crate::DockerClient;
|
||||
|
||||
impl DockerClient {
|
||||
pub async fn deploy(&self, deployment: &Deployment) -> (bool, Log) {
|
||||
let docker_run = docker_run_command(deployment);
|
||||
let output = async_run_command(&docker_run).await;
|
||||
output_into_log("docker run", output)
|
||||
}
|
||||
|
||||
pub async fn docker_start_command(&self, container_name: &str) -> (bool, Log) {
|
||||
let command = format!("start stop {container_name}");
|
||||
let output = async_run_command(&command).await;
|
||||
output_into_log("docker stop", output)
|
||||
}
|
||||
|
||||
pub async fn docker_stop_command(&self, container_name: &str) -> (bool, Log) {
|
||||
let command = format!("docker stop {container_name}");
|
||||
let output = async_run_command(&command).await;
|
||||
output_into_log("docker stop", output)
|
||||
}
|
||||
|
||||
pub async fn docker_stop_and_remove(&self, container_name: &str) -> (bool, Log) {
|
||||
let command =
|
||||
format!("docker stop {container_name} && docker container rm {container_name}");
|
||||
let output = async_run_command(&command).await;
|
||||
output_into_log("docker stop and remove", output)
|
||||
}
|
||||
}
|
||||
|
||||
fn docker_run_command(deployment: &Deployment) -> String {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn output_into_log(stage: &str, output: CommandOutput) -> (bool, Log) {
|
||||
let success = output.success();
|
||||
let log = Log {
|
||||
stage: stage.to_string(),
|
||||
stdout: output.stdout,
|
||||
stderr: output.stderr,
|
||||
};
|
||||
(success, log)
|
||||
}
|
||||
|
||||
@@ -1,44 +1,44 @@
|
||||
use anyhow::anyhow;
|
||||
use bollard::{container::ListContainersOptions, Docker};
|
||||
use types::BasicContainerInfo;
|
||||
|
||||
mod build;
|
||||
mod deploy;
|
||||
|
||||
pub struct DockerClient {
|
||||
client: Docker,
|
||||
}
|
||||
|
||||
impl DockerClient {
|
||||
pub fn new() -> anyhow::Result<DockerClient> {
|
||||
Ok(DockerClient {
|
||||
client: Docker::connect_with_local_defaults()?,
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn list_containers(&self) -> anyhow::Result<Vec<BasicContainerInfo>> {
|
||||
let res = self
|
||||
.client
|
||||
.list_containers(Some(ListContainersOptions::<String> {
|
||||
all: true,
|
||||
..Default::default()
|
||||
}))
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|s| {
|
||||
let info = BasicContainerInfo {
|
||||
name: s
|
||||
.names
|
||||
.ok_or(anyhow!("no names on container"))?
|
||||
.pop()
|
||||
.ok_or(anyhow!("no names on container (empty vec)"))?
|
||||
.replace("/", ""),
|
||||
state: s.state.unwrap().parse().unwrap(),
|
||||
status: s.status,
|
||||
};
|
||||
Ok::<_, anyhow::Error>(info)
|
||||
})
|
||||
.collect::<anyhow::Result<Vec<BasicContainerInfo>>>()?;
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
use anyhow::anyhow;
|
||||
use bollard::{container::ListContainersOptions, Docker};
|
||||
use types::BasicContainerInfo;
|
||||
|
||||
mod build;
|
||||
mod deploy;
|
||||
|
||||
pub struct DockerClient {
|
||||
client: Docker,
|
||||
}
|
||||
|
||||
impl DockerClient {
|
||||
pub fn new() -> anyhow::Result<DockerClient> {
|
||||
Ok(DockerClient {
|
||||
client: Docker::connect_with_local_defaults()?,
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn list_containers(&self) -> anyhow::Result<Vec<BasicContainerInfo>> {
|
||||
let res = self
|
||||
.client
|
||||
.list_containers(Some(ListContainersOptions::<String> {
|
||||
all: true,
|
||||
..Default::default()
|
||||
}))
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|s| {
|
||||
let info = BasicContainerInfo {
|
||||
name: s
|
||||
.names
|
||||
.ok_or(anyhow!("no names on container"))?
|
||||
.pop()
|
||||
.ok_or(anyhow!("no names on container (empty vec)"))?
|
||||
.replace("/", ""),
|
||||
state: s.state.unwrap().parse().unwrap(),
|
||||
status: s.status,
|
||||
};
|
||||
Ok::<_, anyhow::Error>(info)
|
||||
})
|
||||
.collect::<anyhow::Result<Vec<BasicContainerInfo>>>()?;
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
[package]
|
||||
name = "git_client"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
[package]
|
||||
name = "git_client"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
pub struct GitClient;
|
||||
|
||||
impl GitClient {}
|
||||
pub struct GitClient;
|
||||
|
||||
impl GitClient {}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
[package]
|
||||
name = "types"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
mungos = "0.2.24"
|
||||
strum = "0.24"
|
||||
strum_macros = "0.24"
|
||||
[package]
|
||||
name = "types"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
mungos = "0.2.24"
|
||||
strum = "0.24"
|
||||
strum_macros = "0.24"
|
||||
async_timing_util = "0.1.11"
|
||||
@@ -7,6 +7,7 @@ use strum_macros::{Display, EnumString};
|
||||
|
||||
pub type PermissionsMap = HashMap<String, PermissionLevel>;
|
||||
|
||||
pub type UserId = String;
|
||||
pub type ServerId = String;
|
||||
pub type DeploymentId = String;
|
||||
pub type BuildId = String;
|
||||
@@ -223,15 +224,28 @@ pub struct OauthCredentials {
|
||||
#[derive(Deserialize, Debug, Clone)]
|
||||
#[serde(rename_all = "UPPERCASE")]
|
||||
pub struct CoreConfig {
|
||||
// port the core web server runs on
|
||||
pub port: u16,
|
||||
|
||||
// default periphery passkey
|
||||
pub passkey: String,
|
||||
|
||||
// docker integration
|
||||
pub docker_accounts: DockerAccounts,
|
||||
|
||||
// github integration
|
||||
pub github_accounts: GithubAccounts,
|
||||
pub github_oauth: OauthCredentials,
|
||||
pub jwt_secret: String,
|
||||
pub slack_token: Option<String>,
|
||||
pub github_webhook_secret: String,
|
||||
|
||||
// jwt config
|
||||
pub jwt_secret: String,
|
||||
pub jwt_valid_for: Timelength,
|
||||
|
||||
// integration with slack app
|
||||
pub slack_url: Option<String>,
|
||||
|
||||
//mongo config
|
||||
pub mongo_uri: String,
|
||||
#[serde(default = "default_core_mongo_app_name")]
|
||||
pub mongo_app_name: String,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"folders": [
|
||||
{
|
||||
"path": "."
|
||||
}
|
||||
],
|
||||
"settings": {}
|
||||
{
|
||||
"folders": [
|
||||
{
|
||||
"path": "."
|
||||
}
|
||||
],
|
||||
"settings": {}
|
||||
}
|
||||
@@ -1,16 +1,16 @@
|
||||
[package]
|
||||
name = "monitor-periphery"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
tokio = { version = "1.21", features = ["full"] }
|
||||
axum = { version = "0.5" }
|
||||
tower = { version = "0.4", features = ["full"] }
|
||||
dotenv = "0.15"
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
serde_json = "1.0"
|
||||
[package]
|
||||
name = "monitor-periphery"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
tokio = { version = "1.21", features = ["full"] }
|
||||
axum = { version = "0.5" }
|
||||
tower = { version = "0.4", features = ["full"] }
|
||||
dotenv = "0.15"
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
serde_json = "1.0"
|
||||
bollard = "0.13"
|
||||
@@ -1,2 +1,2 @@
|
||||
#[tokio::main]
|
||||
async fn main() {}
|
||||
#[tokio::main]
|
||||
async fn main() {}
|
||||
|
||||
Reference in New Issue
Block a user