117 řádky
3.1 KiB
Rust
Spustitelný soubor
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))
|
|
}
|