some refactor
This commit is contained in:
parent
0043739228
commit
cc2bb42c11
6 changed files with 195 additions and 168 deletions
52
src/auth.rs
Normal file
52
src/auth.rs
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
use axum::{
|
||||||
|
http::{
|
||||||
|
header::{AUTHORIZATION, WWW_AUTHENTICATE},
|
||||||
|
Request,
|
||||||
|
},
|
||||||
|
middleware::Next,
|
||||||
|
response::Response,
|
||||||
|
};
|
||||||
|
use base64::{prelude::BASE64_STANDARD, Engine};
|
||||||
|
|
||||||
|
pub async fn http_basic_auth<B>(
|
||||||
|
request: Request<B>,
|
||||||
|
next: Next<B>,
|
||||||
|
) -> Response {
|
||||||
|
// do something with `request`...
|
||||||
|
let headers = request.headers();
|
||||||
|
let auth_fail_response = Response::builder()
|
||||||
|
.status(401)
|
||||||
|
.header(WWW_AUTHENTICATE, "Basic")
|
||||||
|
.body(axum::body::boxed(String::from("")))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let authorization = match headers.get(AUTHORIZATION) {
|
||||||
|
Some(v) => v.to_str().unwrap(),
|
||||||
|
None => return auth_fail_response,
|
||||||
|
};
|
||||||
|
let parts = authorization.split_ascii_whitespace().collect::<Vec<_>>();
|
||||||
|
let base64part = match &parts[..] {
|
||||||
|
["Basic", base64part] => *base64part,
|
||||||
|
_ => return auth_fail_response,
|
||||||
|
};
|
||||||
|
let decoded = match BASE64_STANDARD
|
||||||
|
.decode(base64part.as_bytes())
|
||||||
|
.map_err(|e| anyhow::Error::from(e))
|
||||||
|
.and_then(|e| String::from_utf8(e).map_err(|e| e.into()))
|
||||||
|
{
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(_) => return auth_fail_response,
|
||||||
|
};
|
||||||
|
let parts = decoded.split(":").collect::<Vec<_>>();
|
||||||
|
let (user, pass) = match &parts[..] {
|
||||||
|
[user, pass] => (*user, *pass),
|
||||||
|
_ => return auth_fail_response,
|
||||||
|
};
|
||||||
|
if !(user == "root" && pass == "hellosu") {
|
||||||
|
return auth_fail_response;
|
||||||
|
}
|
||||||
|
|
||||||
|
let response = next.run(request).await;
|
||||||
|
|
||||||
|
response
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
use std::{collections::HashMap, hash::Hash};
|
use std::{collections::HashMap};
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
|
|
23
src/jobs.rs
Normal file
23
src/jobs.rs
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
use futures::Future;
|
||||||
|
use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender};
|
||||||
|
|
||||||
|
pub type Job = Box<dyn Future<Output = ()>>;
|
||||||
|
pub type JobSender = UnboundedSender<Job>;
|
||||||
|
|
||||||
|
pub struct Scheduler {
|
||||||
|
rx: UnboundedReceiver<Job>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Scheduler {
|
||||||
|
pub fn new() -> (Self, JobSender) {
|
||||||
|
let (tx, rx) = mpsc::unbounded_channel();
|
||||||
|
let scheduler = Scheduler { rx };
|
||||||
|
(scheduler, tx)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn run(self) {
|
||||||
|
loop {
|
||||||
|
// Get the next job, or if a new thing comes in
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
15
src/lib.rs
Normal file
15
src/lib.rs
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
#[macro_use]
|
||||||
|
extern crate serde;
|
||||||
|
|
||||||
|
use sea_orm::DatabaseConnection;
|
||||||
|
|
||||||
|
pub mod auth;
|
||||||
|
pub mod config;
|
||||||
|
pub mod error;
|
||||||
|
pub mod jobs;
|
||||||
|
pub mod upload;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct AppState {
|
||||||
|
pub conn: DatabaseConnection,
|
||||||
|
}
|
175
src/main.rs
175
src/main.rs
|
@ -1,38 +1,15 @@
|
||||||
#[macro_use]
|
use aah::{
|
||||||
extern crate serde;
|
auth::http_basic_auth, upload::handle_upload_configuration, AppState,
|
||||||
|
};
|
||||||
mod config;
|
use anyhow::Result;
|
||||||
mod error;
|
|
||||||
|
|
||||||
use anyhow::{anyhow, Result};
|
|
||||||
use axum::{
|
use axum::{
|
||||||
extract::{Multipart, State},
|
middleware::from_fn,
|
||||||
http::{
|
response::Html,
|
||||||
header::{AUTHORIZATION, WWW_AUTHENTICATE},
|
|
||||||
HeaderName, Request,
|
|
||||||
},
|
|
||||||
middleware::{from_fn, Next},
|
|
||||||
response::{Html, Response},
|
|
||||||
routing::{get, post},
|
routing::{get, post},
|
||||||
Form, Json, Router,
|
Router,
|
||||||
};
|
};
|
||||||
use axum_auth::AuthBasic;
|
|
||||||
use base64::{prelude::BASE64_STANDARD, Engine};
|
|
||||||
use bollard::{
|
|
||||||
auth,
|
|
||||||
container::{
|
|
||||||
Config as ContainerConfig, CreateContainerOptions, StartContainerOptions,
|
|
||||||
},
|
|
||||||
image::CreateImageOptions,
|
|
||||||
Docker,
|
|
||||||
};
|
|
||||||
use entity::service;
|
|
||||||
use error::AppError;
|
|
||||||
use futures::{StreamExt, TryStreamExt};
|
|
||||||
use migration::{Migrator, MigratorTrait};
|
|
||||||
use sea_orm::{ActiveModelTrait, DatabaseConnection, Set};
|
|
||||||
|
|
||||||
use crate::config::{Config, SourceConfig};
|
use migration::{Migrator, MigratorTrait};
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<()> {
|
async fn main() -> Result<()> {
|
||||||
|
@ -59,139 +36,3 @@ async fn main() -> Result<()> {
|
||||||
webserver_handle.await??;
|
webserver_handle.await??;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn http_basic_auth<B>(request: Request<B>, next: Next<B>) -> Response {
|
|
||||||
// do something with `request`...
|
|
||||||
let headers = request.headers();
|
|
||||||
let auth_fail_response = Response::builder()
|
|
||||||
.status(401)
|
|
||||||
.header(WWW_AUTHENTICATE, "Basic")
|
|
||||||
.body(axum::body::boxed(String::from("")))
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let authorization = match headers.get(AUTHORIZATION) {
|
|
||||||
Some(v) => v.to_str().unwrap(),
|
|
||||||
None => return auth_fail_response,
|
|
||||||
};
|
|
||||||
let parts = authorization.split_ascii_whitespace().collect::<Vec<_>>();
|
|
||||||
let base64part = match &parts[..] {
|
|
||||||
["Basic", base64part] => *base64part,
|
|
||||||
_ => return auth_fail_response,
|
|
||||||
};
|
|
||||||
let decoded = match BASE64_STANDARD
|
|
||||||
.decode(base64part.as_bytes())
|
|
||||||
.map_err(|e| anyhow::Error::from(e))
|
|
||||||
.and_then(|e| String::from_utf8(e).map_err(|e| e.into()))
|
|
||||||
{
|
|
||||||
Ok(v) => v,
|
|
||||||
Err(_) => return auth_fail_response,
|
|
||||||
};
|
|
||||||
let parts = decoded.split(":").collect::<Vec<_>>();
|
|
||||||
let (user, pass) = match &parts[..] {
|
|
||||||
[user, pass] => (*user, *pass),
|
|
||||||
_ => return auth_fail_response,
|
|
||||||
};
|
|
||||||
if !(user == "root" && pass == "hellosu") {
|
|
||||||
return auth_fail_response;
|
|
||||||
}
|
|
||||||
|
|
||||||
let response = next.run(request).await;
|
|
||||||
|
|
||||||
response
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
struct AppState {
|
|
||||||
conn: DatabaseConnection,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
struct UploadForm {
|
|
||||||
name: String,
|
|
||||||
config: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn handle_upload_configuration(
|
|
||||||
AuthBasic((id, password)): AuthBasic,
|
|
||||||
state: State<AppState>,
|
|
||||||
mut form: Multipart,
|
|
||||||
// Form(upload_form): Form<UploadForm>,
|
|
||||||
) -> Result<(), AppError> {
|
|
||||||
if id != "root" || password.as_ref().unwrap() != "hellosu" {
|
|
||||||
return Err(anyhow!("error").into());
|
|
||||||
}
|
|
||||||
|
|
||||||
let (name, config) = {
|
|
||||||
let mut name = None;
|
|
||||||
let mut config = None::<Config>;
|
|
||||||
while let Some(field) = form.next_field().await.unwrap() {
|
|
||||||
let key = field.name().unwrap().to_owned();
|
|
||||||
let value =
|
|
||||||
String::from_utf8(field.bytes().await.unwrap().to_vec()).unwrap();
|
|
||||||
match key.as_ref() {
|
|
||||||
"name" => name = Some(value),
|
|
||||||
"config" => config = Some(serde_yaml::from_str(&value).unwrap()),
|
|
||||||
_ => return Err(anyhow!("error").into()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(name.unwrap(), config.unwrap())
|
|
||||||
};
|
|
||||||
|
|
||||||
println!("Loading config: {config:?}");
|
|
||||||
|
|
||||||
let docker = Docker::connect_with_local_defaults().unwrap();
|
|
||||||
|
|
||||||
for (service_name, service_config) in config.service.iter() {
|
|
||||||
let SourceConfig::Image { image: from_image } = &service_config.source;
|
|
||||||
let result_stream = docker.create_image(
|
|
||||||
Some(CreateImageOptions {
|
|
||||||
from_image: from_image.to_owned(),
|
|
||||||
..Default::default()
|
|
||||||
}),
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
|
|
||||||
result_stream
|
|
||||||
.try_for_each(|x| async move {
|
|
||||||
println!("{x:?}");
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
println!("completed pulling.");
|
|
||||||
|
|
||||||
// create a container
|
|
||||||
let result = docker
|
|
||||||
.create_container(
|
|
||||||
Some(CreateContainerOptions::<String> {
|
|
||||||
..Default::default()
|
|
||||||
}),
|
|
||||||
ContainerConfig {
|
|
||||||
image: Some("hello-world"),
|
|
||||||
cmd: Some(vec!["/hello"]),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
docker
|
|
||||||
.start_container(&result.id, None::<StartContainerOptions<String>>)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
println!("result: {result:?}");
|
|
||||||
}
|
|
||||||
|
|
||||||
service::ActiveModel {
|
|
||||||
name: Set(name),
|
|
||||||
config: Set(serde_json::to_string(&config).unwrap()),
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
.save(&state.conn)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
96
src/upload.rs
Normal file
96
src/upload.rs
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
use anyhow::anyhow;
|
||||||
|
use axum::extract::{Multipart, State};
|
||||||
|
use bollard::{
|
||||||
|
container::{
|
||||||
|
Config as ContainerConfig, CreateContainerOptions, StartContainerOptions,
|
||||||
|
},
|
||||||
|
image::CreateImageOptions,
|
||||||
|
Docker,
|
||||||
|
};
|
||||||
|
use entity::service;
|
||||||
|
use futures::TryStreamExt;
|
||||||
|
use sea_orm::{ActiveModelTrait, Set};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
config::{Config, SourceConfig},
|
||||||
|
error::AppError,
|
||||||
|
AppState,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub async fn handle_upload_configuration(
|
||||||
|
state: State<AppState>,
|
||||||
|
mut form: Multipart,
|
||||||
|
) -> Result<(), AppError> {
|
||||||
|
let (name, config) = {
|
||||||
|
let mut name = None;
|
||||||
|
let mut config = None::<Config>;
|
||||||
|
while let Some(field) = form.next_field().await.unwrap() {
|
||||||
|
let key = field.name().unwrap().to_owned();
|
||||||
|
let value =
|
||||||
|
String::from_utf8(field.bytes().await.unwrap().to_vec()).unwrap();
|
||||||
|
match key.as_ref() {
|
||||||
|
"name" => name = Some(value),
|
||||||
|
"config" => config = Some(serde_yaml::from_str(&value).unwrap()),
|
||||||
|
_ => return Err(anyhow!("error").into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(name.unwrap(), config.unwrap())
|
||||||
|
};
|
||||||
|
|
||||||
|
println!("Loading config: {config:?}");
|
||||||
|
|
||||||
|
let docker = Docker::connect_with_local_defaults().unwrap();
|
||||||
|
|
||||||
|
for (_service_name, service_config) in config.service.iter() {
|
||||||
|
let SourceConfig::Image { image: from_image } = &service_config.source;
|
||||||
|
let result_stream = docker.create_image(
|
||||||
|
Some(CreateImageOptions {
|
||||||
|
from_image: from_image.to_owned(),
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
|
result_stream
|
||||||
|
.try_for_each(|x| async move {
|
||||||
|
println!("{x:?}");
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
println!("completed pulling.");
|
||||||
|
|
||||||
|
// create a container
|
||||||
|
let result = docker
|
||||||
|
.create_container(
|
||||||
|
Some(CreateContainerOptions::<String> {
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
ContainerConfig {
|
||||||
|
image: Some(from_image.to_owned()),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
docker
|
||||||
|
.start_container(&result.id, None::<StartContainerOptions<String>>)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
println!("result: {result:?}");
|
||||||
|
}
|
||||||
|
|
||||||
|
service::ActiveModel {
|
||||||
|
name: Set(name),
|
||||||
|
config: Set(serde_json::to_string(&config).unwrap()),
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
.save(&state.conn)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
Loading…
Reference in a new issue