feat!: improved CLI: separated actions from backend

This commit is contained in:
əlemi 2023-05-09 02:11:00 +02:00
parent 5c5e878cea
commit 28b32c8135
Signed by: alemi
GPG key ID: A4895B84D311642C

View file

@ -2,12 +2,14 @@ mod proto;
mod entities;
mod routes;
mod persistence;
mod maintenance;
use std::{collections::HashMap, sync::Arc};
use chrono::{DateTime, Utc, Duration};
use clap::Parser;
use clap::{Parser, Subcommand};
use axum::{Router, routing::{get, post}, response::IntoResponse, Json, http::StatusCode};
use routes::register::fill_missing_skins;
use sea_orm::{DatabaseConnection, Database};
use tokio::sync::Mutex;
use tracing_subscriber::filter::filter_fn;
@ -23,29 +25,48 @@ use crate::{routes::{
/// Reimplementation of legacy auth server for minecraft
#[derive(Parser, Debug, Clone)]
#[command(author, version, about, long_about = None)]
struct ConfigArgs {
struct CliArgs {
/// Connection string for database
database: String,
/// Address to bind web application onto
#[arg(short, long, default_value = "127.0.0.1:26656")]
bind_addr: String,
/// Action to run
#[clap(subcommand)]
action: CliAction,
}
/// How long an access token stays valid, in seconds
#[arg(long, default_value_t = 3600)]
token_duration: u32,
#[derive(Subcommand, Clone, Debug)]
enum CliAction {
/// run yggdrasil backend service
Serve {
/// Address to bind web application onto
#[arg(short, long, default_value = "127.0.0.1:26656")]
bind_addr: String,
/// How long an access token is refreshable, in hours
#[arg(long, default_value_t = 168)]
token_lifetime: u32,
/// How long an access token stays valid, in seconds
#[arg(long, default_value_t = 3600)]
token_duration: u32,
/// Valid time for join requests, in seconds
#[arg(short, long, default_value_t = 10)]
time_window: u32,
/// Valid time for join requests, in seconds
#[arg(short, long, default_value_t = 10)]
time_window: u32,
/// Enable join request fallback to Microsoft
#[arg(long)]
fallback: bool,
},
/// remove expired tokens
Clean {
/// How long an access token is refreshable, in hours
#[arg(long, default_value_t = 168)]
token_lifetime: u32,
},
/// fetch missing skins
Skins {
}
/// Enable join request fallback to Microsoft
#[arg(long)]
fallback: bool,
}
#[derive(Clone)]
@ -64,7 +85,7 @@ impl JoinAttempt {
pub struct AppState {
store: Arc<Mutex<HashMap<Uuid, JoinAttempt>>>,
db: DatabaseConnection,
cfg: ConfigArgs,
cfg: CliArgs,
secret: String,
}
@ -76,37 +97,45 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
.with(tracing_subscriber::fmt::layer())
.init();
let cfg = ConfigArgs::parse();
let cfg = CliArgs::parse();
let db = Database::connect(cfg.database.clone()).await?;
purge_expired_tokens(&db, Duration::hours(cfg.token_lifetime.into())).await?;
match cfg.action {
CliAction::Clean { token_lifetime } => {
purge_expired_tokens(&db, Duration::hours(token_lifetime.into())).await?;
},
CliAction::Skins { } => {
fill_missing_skins(&db).await?;
},
CliAction::Serve { bind_addr, token_duration, time_window, fallback } => {
let secret = load_secret(&db).await?;
let secret = load_secret(&db).await?;
let store = Arc::new(Mutex::new(HashMap::new())); // TODO do this as an Actor
let store = Arc::new(Mutex::new(HashMap::new())); // TODO do this as an Actor
let addr = bind_addr.parse()?;
let addr = cfg.bind_addr.parse()?;
let app = Router::new()
// AUTH
.route("/auth/authenticate", post(authenticate)) //in: username, pass; out: token
.route("/auth/validate", post(validate)) //in: token; out: boolean valid
.route("/auth/refresh", post(refresh)) //in: token out: refreshed token
// SESSION
.route("/session/minecraft/join", post(join))
.route("/session/minecraft/hasJoined", get(has_joined_wrapper))
.route("/session/minecraft/profile/:user_id", get(profile))
// CUSTOM
.route("/register/unmigrated", post(register_unmigrated))
.fallback(fallback_route)
.with_state(AppState { store, db, cfg, secret });
let app = Router::new()
// AUTH
.route("/auth/authenticate", post(authenticate)) //in: username, pass; out: token
.route("/auth/validate", post(validate)) //in: token; out: boolean valid
.route("/auth/refresh", post(refresh)) //in: token out: refreshed token
// SESSION
.route("/session/minecraft/join", post(join))
.route("/session/minecraft/hasJoined", get(has_joined_wrapper))
.route("/session/minecraft/profile/:user_id", get(profile))
// CUSTOM
.route("/register/unmigrated", post(register_unmigrated))
.fallback(fallback_route)
.with_state(AppState { store, db, cfg, secret });
info!(target: "MAIN", "serving Yggdrasil on {}", &addr);
info!(target: "MAIN", "serving Yggdrasil on {}", &addr);
axum::Server::bind(&addr)
.serve(app.into_make_service())
.await?;
axum::Server::bind(&addr)
.serve(app.into_make_service())
.await?;
},
}
Ok(())
}