Something went wrong. Try again.
Web frontend and supporting services for lance.blue
Something went wrong. Try again.
4.9 kB · 109 lines
Rust
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110//! Seeds a throwaway sqlite database with what `/api/session` needs to//! accept a session for a test DID, and prints a matching signed cookie.//!//! Not for production: this exists so a local dev instance (started by//! `scripts/dev-instance.sh`) can be driven as a signed-in player without a//! real ATProto OAuth login, which `VITE_LOCAL_AUTH=1`'s `devauth.ts` does//! not help with — it fakes the frontend's sign-in and never touches//! headquarters-api at all, so `session.rs`'s cookie check still sees no//! session. This mints a real one instead, with the real signing code//! (`session::SessionSigner`) rather than a hand-reimplemented copy of it —//! see `lib.rs` for why that distinction matters.//!//! `/api/session` checks more than the cookie: `has_oauth_session` must//! also find a row (`routes.rs`, "the cookie alone is not a session: the//! stored OAuth session is"). `value` there is never read for its content by//! anything this tool needs to satisfy, only its presence — see//! `Db::has_oauth_session` — so an empty JSON object is enough.//!//! Reads its configuration from the environment, matching `Config::from_env`'s//! own convention rather than introducing a CLI-flag style this crate does//! not otherwise use://!//! DEV_SESSION_DB required - path to the target sqlite file. Created//! (with the real migrations, via Db::open) if it//! does not exist yet.//! SESSION_SECRET optional - must match what the target server was//! started with, or the cookie this prints will not//! verify. Defaults to this codebase's own test//! fixture secret (session.rs's test module), a//! fixed, published, non-sensitive value that only//! ever signs throwaway local sessions.//! DEV_SESSION_DID optional - defaults to a fixed, obviously-fake DID.//! DEV_SESSION_HANDLE optional - if set, also seeds an `account` row//! with this handle, checked-in-full so nothing//! tries to refresh it from the network.//!//! Prints exactly one line to stdout: the cookie value, nothing else, so a//! caller can capture it with `$(...)`. Everything else goes to stderr.
const DEV_TEST_SECRET: &str = "0123456789abcdef0123456789abcdef";const DEV_DEFAULT_DID: &str = "did:plc:devsession0000000000000000";
#[tokio::main]async fn main() { let db_path = std::env::var("DEV_SESSION_DB").unwrap_or_else(|_| { eprintln!("dev_session: DEV_SESSION_DB is required (path to a sqlite file)"); std::process::exit(1); }); let secret = std::env::var("SESSION_SECRET").unwrap_or_else(|_| DEV_TEST_SECRET.to_owned()); let did = std::env::var("DEV_SESSION_DID").unwrap_or_else(|_| DEV_DEFAULT_DID.to_owned()); let handle = std::env::var("DEV_SESSION_HANDLE").ok();
let db = match headquarters_api::db::Db::open(std::path::Path::new(&db_path)) { Ok(db) => db, Err(e) => { eprintln!("dev_session: cannot open {db_path}: {e}"); std::process::exit(1); } };
let write_did = did.clone(); let result = db .call(move |conn| { conn.execute( "INSERT INTO oauth_session (did, value) VALUES (?1, '{}') ON CONFLICT (did) DO UPDATE SET updated_at = unixepoch()", [&write_did], )?; Ok(()) }) .await; if let Err(e) = result { eprintln!("dev_session: could not seed oauth_session: {e}"); std::process::exit(1); }
if let Some(handle) = &handle { let write_did = did.clone(); let write_handle = handle.clone(); let now = std::time::SystemTime::now() .duration_since(std::time::UNIX_EPOCH) .expect("clock before 1970") .as_secs() as i64; let result = db .call(move |conn| { conn.execute( "INSERT INTO account (did, handle, verified_at, checked_at) VALUES (?1, ?2, ?3, ?3) ON CONFLICT (did) DO UPDATE SET handle = ?2, checked_at = ?3", rusqlite::params![write_did, write_handle, now], )?; Ok(()) }) .await; if let Err(e) = result { eprintln!("dev_session: could not seed account: {e}"); std::process::exit(1); } }
let signer = headquarters_api::session::SessionSigner::new(secret.into_bytes(), false, None); let cookie = signer.issue(&did);
eprintln!( "dev_session: seeded {did}{} in {db_path}", handle.map(|h| format!(" (handle {h})")).unwrap_or_default() ); println!("{}", cookie.value());}