nekrochan/src/web/tcx.rs

117 řádky
3.1 KiB
Rust
Spustitelný soubor

use actix_web::HttpRequest;
use redis::{AsyncCommands, Commands, Connection};
use sqlx::query_as;
use std::{
collections::HashSet,
net::{IpAddr, Ipv4Addr},
};
use crate::{
auth::Claims, cfg::Cfg, ctx::Ctx, db::models::Account, error::NekrochanError,
perms::PermissionWrapper,
};
#[derive(Debug, Clone)]
pub struct TemplateCtx {
pub cfg: Cfg,
pub boards: Vec<String>,
pub account: Option<String>,
pub perms: PermissionWrapper,
pub ip: IpAddr,
pub yous: HashSet<String>,
pub report_count: Option<i64>,
}
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 perms = match &account {
Some(account) => account.perms(),
None => PermissionWrapper::new(0, false),
};
let (ip, _) = ip_from_req(req)?;
let yous = ctx.cache().zrange(format!("by_ip:{ip}"), 0, -1).await?;
let account = account.map(|account| account.username);
let report_count = if perms.owner() || perms.reports() {
let count: Option<Option<(i64,)>> = query_as("SELECT SUM(jsonb_array_length(reports)) FROM overboard WHERE reports != '[]'::jsonb")
.fetch_optional(ctx.db())
.await
.ok();
match count {
Some(Some((count,))) if count != 0 => Some(count),
_ => None,
}
} else {
None
};
let tcx = Self {
cfg,
boards,
perms,
ip,
yous,
account,
report_count,
};
Ok(tcx)
}
pub fn update_yous(&mut self, cache: &mut Connection) -> Result<(), NekrochanError> {
self.yous = cache.zrange(format!("by_ip:{}", self.ip), 0, -1)?;
Ok(())
}
}
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 = req
.connection_info()
.realip_remote_addr()
.map_or(IpAddr::V4(Ipv4Addr::UNSPECIFIED), |ip| {
ip.parse().unwrap_or(IpAddr::V4(Ipv4Addr::UNSPECIFIED))
});
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))
}