atagen

jelq

public

an http based roguelike rpg about jelqing

Star0
Watch0
14de3e1e8dc2a460103dbac0f56b745af96da78e
1
ssh://brewery@pls.lobotomise.me:33/atagen/jelq.git

Blame: src/http/mod.rs

782069860236atagen2026-10-10 12:44 UTC1
mod ctx;
782069860236atagen2026-10-10 12:44 UTC2
mod meta;
782069860236atagen2026-10-10 12:44 UTC3
mod middleware;
782069860236atagen2026-10-10 12:44 UTC4
mod resource;
782069860236atagen2026-10-10 12:44 UTC5
mod response;
782069860236atagen2026-10-10 12:44 UTC6
mod run;
782069860236atagen2026-10-10 12:44 UTC7
mod runs;
782069860236atagen2026-10-10 12:44 UTC8
mod ui;
782069860236atagen2026-10-10 12:44 UTC9
782069860236atagen2026-10-10 12:44 UTC10
use std::{
782069860236atagen2026-10-10 12:44 UTC11
    collections::HashMap,
782069860236atagen2026-10-10 12:44 UTC12
    sync::{Arc, Mutex, MutexGuard, PoisonError},
782069860236atagen2026-10-10 12:44 UTC13
};
782069860236atagen2026-10-10 12:44 UTC14
782069860236atagen2026-10-10 12:44 UTC15
use axum::{
782069860236atagen2026-10-10 12:44 UTC16
    Router,
782069860236atagen2026-10-10 12:44 UTC17
    extract::State,
782069860236atagen2026-10-10 12:44 UTC18
    response::Redirect,
782069860236atagen2026-10-10 12:44 UTC19
    routing::{MethodRouter, any},
782069860236atagen2026-10-10 12:44 UTC20
};
782069860236atagen2026-10-10 12:44 UTC21
7288fc269bb6atagen2026-10-10 13:36 UTC22
use tower_http::catch_panic::CatchPanicLayer;
7288fc269bb6atagen2026-10-10 13:36 UTC23
782069860236atagen2026-10-10 12:44 UTC24
use crate::{config::Config, game::Run};
782069860236atagen2026-10-10 12:44 UTC25
use ctx::Ctx;
782069860236atagen2026-10-10 12:44 UTC26
use resource::Resource;
782069860236atagen2026-10-10 12:44 UTC27
use run::RunResource;
782069860236atagen2026-10-10 12:44 UTC28
782069860236atagen2026-10-10 12:44 UTC29
pub struct AppState {
782069860236atagen2026-10-10 12:44 UTC30
    runs: Mutex<HashMap<String, Run>>,
782069860236atagen2026-10-10 12:44 UTC31
    config: Config,
782069860236atagen2026-10-10 12:44 UTC32
}
782069860236atagen2026-10-10 12:44 UTC33
782069860236atagen2026-10-10 12:44 UTC34
impl AppState {
782069860236atagen2026-10-10 12:44 UTC35
    pub fn new(config: Config) -> Self {
782069860236atagen2026-10-10 12:44 UTC36
        Self {
782069860236atagen2026-10-10 12:44 UTC37
            runs: Mutex::default(),
782069860236atagen2026-10-10 12:44 UTC38
            config,
782069860236atagen2026-10-10 12:44 UTC39
        }
782069860236atagen2026-10-10 12:44 UTC40
    }
782069860236atagen2026-10-10 12:44 UTC41
782069860236atagen2026-10-10 12:44 UTC42
    /// A panic mid-request must not take every other run down with it, so a poisoned lock is recovered.
782069860236atagen2026-10-10 12:44 UTC43
    fn runs(&self) -> MutexGuard<'_, HashMap<String, Run>> {
782069860236atagen2026-10-10 12:44 UTC44
        self.runs.lock().unwrap_or_else(PoisonError::into_inner)
782069860236atagen2026-10-10 12:44 UTC45
    }
782069860236atagen2026-10-10 12:44 UTC46
782069860236atagen2026-10-10 12:44 UTC47
    #[cfg(test)]
782069860236atagen2026-10-10 12:44 UTC48
    pub(crate) fn insert(&self, run: Run) {
782069860236atagen2026-10-10 12:44 UTC49
        self.runs().insert(run.id.clone(), run);
782069860236atagen2026-10-10 12:44 UTC50
    }
782069860236atagen2026-10-10 12:44 UTC51
}
782069860236atagen2026-10-10 12:44 UTC52
782069860236atagen2026-10-10 12:44 UTC53
impl Default for AppState {
782069860236atagen2026-10-10 12:44 UTC54
    fn default() -> Self {
782069860236atagen2026-10-10 12:44 UTC55
        Self::new(Config::default())
782069860236atagen2026-10-10 12:44 UTC56
    }
782069860236atagen2026-10-10 12:44 UTC57
}
782069860236atagen2026-10-10 12:44 UTC58
782069860236atagen2026-10-10 12:44 UTC59
type Shared = Arc<AppState>;
782069860236atagen2026-10-10 12:44 UTC60
782069860236atagen2026-10-10 12:44 UTC61
pub fn app(config: Config) -> Router {
782069860236atagen2026-10-10 12:44 UTC62
    router(Arc::new(AppState::new(config)))
782069860236atagen2026-10-10 12:44 UTC63
}
782069860236atagen2026-10-10 12:44 UTC64
782069860236atagen2026-10-10 12:44 UTC65
pub fn router(state: Shared) -> Router {
782069860236atagen2026-10-10 12:44 UTC66
    let api = api(state);
782069860236atagen2026-10-10 12:44 UTC67
    let ui = Router::new()
782069860236atagen2026-10-10 12:44 UTC68
        .route("/ui", any(|| async { Redirect::permanent("/ui/") }))
782069860236atagen2026-10-10 12:44 UTC69
        .route("/ui/", any(ui::page))
782069860236atagen2026-10-10 12:44 UTC70
        .route("/ui/send", any(ui::send))
782069860236atagen2026-10-10 12:44 UTC71
        .with_state(api.clone());
782069860236atagen2026-10-10 12:44 UTC72
    api.merge(ui)
7288fc269bb6atagen2026-10-10 13:36 UTC73
        .layer(CatchPanicLayer::custom(middleware::panicked))
782069860236atagen2026-10-10 12:44 UTC74
}
782069860236atagen2026-10-10 12:44 UTC75
782069860236atagen2026-10-10 12:44 UTC76
fn global(resource: &'static Resource<Shared>) -> MethodRouter<Shared> {
782069860236atagen2026-10-10 12:44 UTC77
    any(
782069860236atagen2026-10-10 12:44 UTC78
        move |State(mut state): State<Shared>, ctx: Ctx| async move {
782069860236atagen2026-10-10 12:44 UTC79
            resource.dispatch(&mut state, &ctx)
782069860236atagen2026-10-10 12:44 UTC80
        },
782069860236atagen2026-10-10 12:44 UTC81
    )
782069860236atagen2026-10-10 12:44 UTC82
}
782069860236atagen2026-10-10 12:44 UTC83
782069860236atagen2026-10-10 12:44 UTC84
fn scoped(resource: &'static RunResource) -> MethodRouter<Shared> {
782069860236atagen2026-10-10 12:44 UTC85
    any(
782069860236atagen2026-10-10 12:44 UTC86
        move |State(state): State<Shared>, ctx: Ctx| async move { run::serve(&state, ctx, resource) },
782069860236atagen2026-10-10 12:44 UTC87
    )
782069860236atagen2026-10-10 12:44 UTC88
}
782069860236atagen2026-10-10 12:44 UTC89
782069860236atagen2026-10-10 12:44 UTC90
fn api(state: Shared) -> Router {
782069860236atagen2026-10-10 12:44 UTC91
    Router::new()
782069860236atagen2026-10-10 12:44 UTC92
        .route("/", global(&meta::INDEX))
782069860236atagen2026-10-10 12:44 UTC93
        .route("/runs", global(&meta::RUNS))
782069860236atagen2026-10-10 12:44 UTC94
        .route("/runs/{id}", scoped(&runs::RUN))
782069860236atagen2026-10-10 12:44 UTC95
        .route("/runs/{id}/length", scoped(&runs::LENGTH))
782069860236atagen2026-10-10 12:44 UTC96
        .route("/runs/{id}/foreskin", scoped(&runs::FORESKIN))
782069860236atagen2026-10-10 12:44 UTC97
        .route("/runs/{id}/inventory", scoped(&runs::INVENTORY))
782069860236atagen2026-10-10 12:44 UTC98
        .route("/runs/{id}/inventory/{item}", scoped(&runs::ITEM))
782069860236atagen2026-10-10 12:44 UTC99
        .route("/runs/{id}/techniques", scoped(&runs::TECHNIQUES))
782069860236atagen2026-10-10 12:44 UTC100
        .route("/runs/{id}/drawer", scoped(&runs::DRAWER))
782069860236atagen2026-10-10 12:44 UTC101
        .route("/runs/{id}/branches/{choice}", scoped(&runs::BRANCH))
0a0bfee8329eatagen2026-10-11 05:11 UTC102
        .route("/runs/{id}/kitchen", scoped(&runs::KITCHEN))
0a0bfee8329eatagen2026-10-11 05:11 UTC103
        .route("/runs/{id}/kitchen/watermelon", scoped(&runs::MELON))
782069860236atagen2026-10-10 12:44 UTC104
        .route("/runs/{id}/floor", scoped(&runs::FLOOR))
782069860236atagen2026-10-10 12:44 UTC105
        .route("/runs/{id}/shop", scoped(&runs::SHOP))
782069860236atagen2026-10-10 12:44 UTC106
        .route("/runs/{id}/shop/{item}", scoped(&runs::BUY))
782069860236atagen2026-10-10 12:44 UTC107
        .route("/runs/{id}/portal", scoped(&runs::PORTAL))
782069860236atagen2026-10-10 12:44 UTC108
        .route("/runs/{id}/elder", scoped(&runs::ELDER))
782069860236atagen2026-10-10 12:44 UTC109
        .route("/runs/{id}/grip", scoped(&runs::GRIP))
782069860236atagen2026-10-10 12:44 UTC110
        .route("/runs/{id}/diagnosis", scoped(&runs::DIAGNOSIS))
782069860236atagen2026-10-10 12:44 UTC111
        .route("/runs/{id}/footage", scoped(&runs::FOOTAGE))
782069860236atagen2026-10-10 12:44 UTC112
        .route("/forum/threads/{year}", global(&meta::THREAD))
782069860236atagen2026-10-10 12:44 UTC113
        .route("/graveyard", global(&meta::GRAVEYARD))
782069860236atagen2026-10-10 12:44 UTC114
        .route("/leaderboard", global(&meta::LEADERBOARD))
782069860236atagen2026-10-10 12:44 UTC115
        .fallback(meta::not_found)
782069860236atagen2026-10-10 12:44 UTC116
        .layer(axum::middleware::from_fn(middleware::chrome))
7288fc269bb6atagen2026-10-10 13:36 UTC117
        .layer(CatchPanicLayer::custom(middleware::panicked))
782069860236atagen2026-10-10 12:44 UTC118
        .with_state(state)
782069860236atagen2026-10-10 12:44 UTC119
}