Something went wrong. Try again.
Paper Mario mod hosting site - AT Protocol appview starhaven.dev
appview
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125//! Shared application state.
use std::collections::HashMap;use std::ops::Deref;use std::sync::{Arc, Mutex};use std::time::Instant;
use atproto_identity::resolve::{ HickoryDnsResolver, InnerIdentityResolver, SharedIdentityResolver,};use sqlx::sqlite::{SqliteConnectOptions, SqlitePoolOptions};use sqlx::SqlitePool;
use crate::config::Config;use crate::mail::Mailer;use crate::oauth::login::PersistedOAuthRequest;use crate::s3::Store;use crate::secrets::Secrets;use crate::signup;use crate::stats;
/// Shared, cheaply-cloneable application state passed to axum handlers.#[derive(Clone)]pub struct AppState(pub Arc<Inner>);
/// The actual state; accessed through `AppState`'s `Deref`.pub struct Inner { /// Application configuration. pub config: Config, /// OAuth signing keys and the cookie secret. pub secrets: Secrets, /// Shared HTTP client for calling out to PDSes/authorization servers. pub http_client: reqwest::Client, /// DID/handle resolver. pub identity_resolver: SharedIdentityResolver, /// In-flight OAuth requests (PKCE verifier, CSRF state, per-flow DPoP /// key), keyed by the CSRF `state` value, between `/login` and `/callback`. pub oauth_requests: Mutex<HashMap<String, PersistedOAuthRequest>>, /// Handles and DIDs that recently failed to resolve, so a stream of made-up /// ones cannot turn every request into a network lookup. In-memory and /// bounded: negative results are cheap to rediscover and must not fill /// the disk. pub resolve_misses: Mutex<HashMap<String, Instant>>, /// SQLite connection pool. pub db: SqlitePool, /// Object store holding mod deliverables. pub s3: Store, /// Outgoing email. pub mailer: Mailer, /// What sign-up authenticates to its captcha and the PDS with. pub signup: signup::Credentials, /// Who downloaded what today, so a repeat counts once. pub downloads_seen: stats::Seen, /// Who viewed what today, likewise. pub views_seen: stats::Seen, /// Sessions being renewed, or just renewed, so requests that arrive /// together share one renewal. pub renewals: crate::oauth::refresh::Renewals,}
impl AppState { /// Build application state from configuration. pub async fn new(config: Config) -> anyhow::Result<Self> { // Holds both the secrets file and the database. std::fs::create_dir_all(&config.data_dir)?;
let secrets = Secrets::load_or_generate(&config.data_dir)?;
let http_client = reqwest::Client::new(); let dns_resolver = Arc::new(HickoryDnsResolver::create_resolver(&[])); let identity_resolver = SharedIdentityResolver(Arc::new(InnerIdentityResolver { dns_resolver, http_client: http_client.clone(), plc_hostname: "plc.directory".to_string(), }));
// Create and migrate db if necessary. let connect_options = SqliteConnectOptions::new() .filename(config.database_path()) .create_if_missing(true); let db = SqlitePoolOptions::new() .connect_with(connect_options) .await?; sqlx::migrate!().run(&db).await?;
// Derived from mod_listing, so it is rebuilt rather than migrated. crate::search::reindex_all(&db).await?;
let s3 = Store::new(&config)?; // Best effort: a mod browser that cannot reach its object store can // still show every mod, so this is a warning rather than a refusal to // start. Publishing a version retries it. if let Err(err) = s3.ensure_bucket().await { eprintln!("object store not ready, versions cannot be published yet: {err:#}"); }
let mailer = Mailer::new(&config)?; let signup = signup::Credentials::load(&config)?;
Ok(AppState(Arc::new(Inner { config, secrets, http_client, identity_resolver, oauth_requests: Mutex::new(HashMap::new()), resolve_misses: Mutex::new(HashMap::new()), db, s3, mailer, signup, downloads_seen: stats::Seen::default(), views_seen: stats::Seen::default(), renewals: Default::default(), }))) }}
impl Deref for AppState { type Target = Inner;
fn deref(&self) -> &Inner { &self.0 }}