feat!: improved CLI: separated actions from backend
This commit is contained in:
parent
5c5e878cea
commit
28b32c8135
1 changed files with 70 additions and 41 deletions
111
src/main.rs
111
src/main.rs
|
@ -2,12 +2,14 @@ mod proto;
|
||||||
mod entities;
|
mod entities;
|
||||||
mod routes;
|
mod routes;
|
||||||
mod persistence;
|
mod persistence;
|
||||||
|
mod maintenance;
|
||||||
|
|
||||||
use std::{collections::HashMap, sync::Arc};
|
use std::{collections::HashMap, sync::Arc};
|
||||||
|
|
||||||
use chrono::{DateTime, Utc, Duration};
|
use chrono::{DateTime, Utc, Duration};
|
||||||
use clap::Parser;
|
use clap::{Parser, Subcommand};
|
||||||
use axum::{Router, routing::{get, post}, response::IntoResponse, Json, http::StatusCode};
|
use axum::{Router, routing::{get, post}, response::IntoResponse, Json, http::StatusCode};
|
||||||
|
use routes::register::fill_missing_skins;
|
||||||
use sea_orm::{DatabaseConnection, Database};
|
use sea_orm::{DatabaseConnection, Database};
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
use tracing_subscriber::filter::filter_fn;
|
use tracing_subscriber::filter::filter_fn;
|
||||||
|
@ -23,29 +25,48 @@ use crate::{routes::{
|
||||||
/// Reimplementation of legacy auth server for minecraft
|
/// Reimplementation of legacy auth server for minecraft
|
||||||
#[derive(Parser, Debug, Clone)]
|
#[derive(Parser, Debug, Clone)]
|
||||||
#[command(author, version, about, long_about = None)]
|
#[command(author, version, about, long_about = None)]
|
||||||
struct ConfigArgs {
|
struct CliArgs {
|
||||||
/// Connection string for database
|
/// Connection string for database
|
||||||
database: String,
|
database: String,
|
||||||
|
|
||||||
/// Address to bind web application onto
|
/// Action to run
|
||||||
#[arg(short, long, default_value = "127.0.0.1:26656")]
|
#[clap(subcommand)]
|
||||||
bind_addr: String,
|
action: CliAction,
|
||||||
|
}
|
||||||
|
|
||||||
/// How long an access token stays valid, in seconds
|
#[derive(Subcommand, Clone, Debug)]
|
||||||
#[arg(long, default_value_t = 3600)]
|
enum CliAction {
|
||||||
token_duration: u32,
|
/// 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
|
/// How long an access token stays valid, in seconds
|
||||||
#[arg(long, default_value_t = 168)]
|
#[arg(long, default_value_t = 3600)]
|
||||||
token_lifetime: u32,
|
token_duration: u32,
|
||||||
|
|
||||||
/// Valid time for join requests, in seconds
|
/// Valid time for join requests, in seconds
|
||||||
#[arg(short, long, default_value_t = 10)]
|
#[arg(short, long, default_value_t = 10)]
|
||||||
time_window: u32,
|
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)]
|
#[derive(Clone)]
|
||||||
|
@ -64,7 +85,7 @@ impl JoinAttempt {
|
||||||
pub struct AppState {
|
pub struct AppState {
|
||||||
store: Arc<Mutex<HashMap<Uuid, JoinAttempt>>>,
|
store: Arc<Mutex<HashMap<Uuid, JoinAttempt>>>,
|
||||||
db: DatabaseConnection,
|
db: DatabaseConnection,
|
||||||
cfg: ConfigArgs,
|
cfg: CliArgs,
|
||||||
secret: String,
|
secret: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,37 +97,45 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
.with(tracing_subscriber::fmt::layer())
|
.with(tracing_subscriber::fmt::layer())
|
||||||
.init();
|
.init();
|
||||||
|
|
||||||
let cfg = ConfigArgs::parse();
|
let cfg = CliArgs::parse();
|
||||||
|
|
||||||
let db = Database::connect(cfg.database.clone()).await?;
|
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()
|
info!(target: "MAIN", "serving Yggdrasil on {}", &addr);
|
||||||
// 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);
|
axum::Server::bind(&addr)
|
||||||
|
.serve(app.into_make_service())
|
||||||
axum::Server::bind(&addr)
|
.await?;
|
||||||
.serve(app.into_make_service())
|
},
|
||||||
.await?;
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue