2023-12-11 15:18:43 +00:00
|
|
|
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> {
|
2023-12-11 15:59:32 +00:00
|
|
|
let cfg = ctx.cfg.clone();
|
2023-12-11 15:18:43 +00:00
|
|
|
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>()?;
|
|
|
|
|
2023-12-11 15:59:32 +00:00
|
|
|
let country = req.headers().get("X-Country-Code").map_or_else(
|
|
|
|
|| "xx".into(),
|
|
|
|
|hdr| hdr.to_str().unwrap_or("xx").to_ascii_lowercase(),
|
|
|
|
);
|
2023-12-11 15:18:43 +00:00
|
|
|
|
|
|
|
Ok((ip, country))
|
|
|
|
}
|