use actix_web::HttpRequest; use rand::{distributions::Alphanumeric, thread_rng, Rng}; use redis::AsyncCommands; use std::{ collections::HashSet, net::{IpAddr, Ipv4Addr}, }; use crate::{ auth::Claims, cfg::Cfg, ctx::Ctx, db::models::Account, error::NekrochanError, perms::PermissionWrapper, }; pub struct TemplateCtx { pub cfg: Cfg, pub boards: Vec, pub logged_in: bool, pub perms: PermissionWrapper, pub name: Option, pub password: String, pub ip: IpAddr, pub yous: HashSet, } impl TemplateCtx { pub async fn new(ctx: &Ctx, req: &HttpRequest) -> Result { let cfg = ctx.cfg.to_owned(); let boards = ctx.cache().lrange("board_ids", 0, -1).await?; let account = account_from_auth_opt(ctx, req).await?; let logged_in = account.is_some(); let perms = match &account { Some(account) => account.perms(), None => PermissionWrapper::new(0, false), }; let name = req.cookie("name").map(|cookie| cookie.value().into()); let password_cookie = req.cookie("password").map(|cookie| cookie.value().into()); let password: String = match password_cookie { Some(password) => password, None => thread_rng() .sample_iter(&Alphanumeric) .take(8) .map(char::from) .collect(), }; let (ip, _) = ip_from_req(req)?; let yous = ctx.cache().zrange(format!("yous:{ip}"), 0, -1).await?; let tcx = Self { cfg, boards, logged_in, perms, name, password, ip, yous, }; Ok(tcx) } } pub async fn account_from_auth(ctx: &Ctx, req: &HttpRequest) -> Result { let account = account_from_auth_opt(ctx, req) .await? .ok_or(NekrochanError::NotLoggedInError)?; Ok(account) } pub async fn account_from_auth_opt( ctx: &Ctx, req: &HttpRequest, ) -> Result, NekrochanError> { let account = match req.cookie("auth") { Some(auth) => { let claims = Claims::decode(ctx, auth.value())?; let account = Account::read(ctx, claims.sub) .await? .ok_or(NekrochanError::InvalidAuthError)?; Some(account) } None => None, }; Ok(account) } pub fn ip_from_req(req: &HttpRequest) -> Result<(IpAddr, String), NekrochanError> { let ip = IpAddr::V4(Ipv4Addr::UNSPECIFIED); // let ip = req // .headers() // .get("X-Real-IP") // .ok_or(NekrochanError::HeaderError("X-Real-IP"))? // .to_str() // .map_err(|_| NekrochanError::HeaderError("X-Real-IP"))? // .parse::()?; let country = req .headers() .get("X-Country-Code") .map(|hdr| hdr.to_str().unwrap_or("xx").to_ascii_lowercase()) .unwrap_or_else(|| "xx".into()); Ok((ip, country)) }