nekrochan/src/web/tcx.rs
2023-12-11 16:59:32 +01:00

113 řádky
3.0 KiB
Rust
Spustitelný soubor

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<String>,
pub logged_in: bool,
pub perms: PermissionWrapper,
pub name: Option<String>,
pub password: String,
pub ip: IpAddr,
pub yous: HashSet<String>,
}
impl TemplateCtx {
pub async fn new(ctx: &Ctx, req: &HttpRequest) -> Result<TemplateCtx, NekrochanError> {
let cfg = ctx.cfg.clone();
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<Account, NekrochanError> {
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<Option<Account>, 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::<IpAddr>()?;
let country = req.headers().get("X-Country-Code").map_or_else(
|| "xx".into(),
|hdr| hdr.to_str().unwrap_or("xx").to_ascii_lowercase(),
);
Ok((ip, country))
}