189 lines
6.4 KiB
Rust
189 lines
6.4 KiB
Rust
use actix_files::{Files, NamedFile};
|
|
use actix_web::{
|
|
body::MessageBody,
|
|
dev::ServiceResponse,
|
|
get,
|
|
http::header::{HeaderValue, CACHE_CONTROL, PRAGMA},
|
|
middleware::{ErrorHandlerResponse, ErrorHandlers},
|
|
web::Data,
|
|
App, HttpRequest, HttpResponse, HttpServer, ResponseError,
|
|
};
|
|
use anyhow::Error;
|
|
use askama::Template;
|
|
use log::{error, info};
|
|
use nekrochan::{
|
|
cfg::Cfg,
|
|
ctx::Ctx,
|
|
db::{cache::init_cache, models::Banner},
|
|
error::NekrochanError,
|
|
schedule::s_cleanup_files,
|
|
web::{self, template_response},
|
|
};
|
|
use sqlx::migrate;
|
|
use std::{env::var, time::Duration};
|
|
use tokio::time::sleep;
|
|
|
|
#[actix_web::main]
|
|
async fn main() {
|
|
dotenv::dotenv().ok();
|
|
env_logger::init();
|
|
|
|
if let Err(err) = run().await {
|
|
error!("{err:?}");
|
|
}
|
|
}
|
|
|
|
async fn run() -> Result<(), Error> {
|
|
let cfg_path = var("NEKROCHAN_CONFIG").unwrap_or_else(|_| "Nekrochan.toml".into());
|
|
|
|
let cfg = Cfg::load(&cfg_path).await?;
|
|
let ctx = Ctx::new(cfg).await?;
|
|
|
|
migrate!().run(ctx.db()).await?;
|
|
init_cache(&ctx).await?;
|
|
|
|
let ctx_ = ctx.clone();
|
|
|
|
tokio::spawn(async move {
|
|
loop {
|
|
match s_cleanup_files(&ctx_).await {
|
|
Ok(()) => info!("Routine file cleanup successful."),
|
|
Err(err) => error!("Routine file cleanup failed: {err:?}"),
|
|
};
|
|
|
|
sleep(Duration::from_secs(ctx_.cfg.files.cleanup_interval)).await;
|
|
}
|
|
});
|
|
|
|
let bind_addr = ctx.bind_addr();
|
|
|
|
HttpServer::new(move || {
|
|
App::new()
|
|
.app_data(Data::new(ctx.clone()))
|
|
.service(web::board::board)
|
|
.service(web::board_catalog::board_catalog)
|
|
.service(web::index::index)
|
|
.service(web::captcha::captcha)
|
|
.service(web::edit_posts::edit_posts)
|
|
.service(web::ip_posts::ip_posts)
|
|
.service(web::live::live)
|
|
.service(web::login::login_get)
|
|
.service(web::login::login_post)
|
|
.service(web::logout::logout)
|
|
.service(web::news::news)
|
|
.service(web::overboard::overboard)
|
|
.service(web::overboard_catalog::overboard_catalog)
|
|
.service(web::page::page)
|
|
.service(web::search::search)
|
|
.service(web::thread::thread)
|
|
.service(web::thread_json::thread_json)
|
|
.service(web::actions::appeal_ban::appeal_ban)
|
|
.service(web::actions::create_post::create_post)
|
|
.service(web::actions::edit_posts::edit_posts)
|
|
.service(web::actions::report_posts::report_posts)
|
|
.service(web::actions::staff_post_actions::staff_post_actions)
|
|
.service(web::actions::user_post_actions::user_post_actions)
|
|
.service(web::staff::account::account)
|
|
.service(web::staff::accounts::accounts)
|
|
.service(web::staff::bans::bans)
|
|
.service(web::staff::banners::banners)
|
|
.service(web::staff::board_config::board_config)
|
|
.service(web::staff::boards::boards)
|
|
.service(web::staff::edit_news::edit_news)
|
|
.service(web::staff::news::news)
|
|
.service(web::staff::permissions::permissions)
|
|
.service(web::staff::reports::reports)
|
|
.service(web::staff::actions::add_banners::add_banners)
|
|
.service(web::staff::actions::change_password::change_password)
|
|
.service(web::staff::actions::create_account::create_account)
|
|
.service(web::staff::actions::create_board::create_board)
|
|
.service(web::staff::actions::create_news::create_news)
|
|
.service(web::staff::actions::delete_account::delete_account)
|
|
.service(web::staff::actions::edit_news::edit_news)
|
|
.service(web::staff::actions::remove_accounts::remove_accounts)
|
|
.service(web::staff::actions::remove_banners::remove_banners)
|
|
.service(web::staff::actions::remove_bans::remove_bans)
|
|
.service(web::staff::actions::remove_boards::remove_boards)
|
|
.service(web::staff::actions::remove_news::remove_news)
|
|
.service(web::staff::actions::transfer_ownership::transfer_ownership)
|
|
.service(web::staff::actions::update_board_config::update_board_config)
|
|
.service(web::staff::actions::update_boards::update_boards)
|
|
.service(web::staff::actions::update_permissions::update_permissions)
|
|
.service(favicon)
|
|
.service(random_banner)
|
|
.service(Files::new("/static", "./static"))
|
|
.service(Files::new("/uploads", "./uploads").disable_content_disposition())
|
|
.wrap(ErrorHandlers::new().default_handler(error_handler))
|
|
})
|
|
.bind(bind_addr)?
|
|
.run()
|
|
.await?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[get("/favicon.ico")]
|
|
async fn favicon() -> Result<NamedFile, NekrochanError> {
|
|
let favicon = NamedFile::open("./static/favicon.ico")?;
|
|
|
|
Ok(favicon)
|
|
}
|
|
|
|
#[get("/random-banner")]
|
|
async fn random_banner(ctx: Data<Ctx>, req: HttpRequest) -> Result<HttpResponse, NekrochanError> {
|
|
let file = if let Some(banner) = Banner::read_random(&ctx).await? {
|
|
let timestamp = banner.banner.timestamp;
|
|
let format = &banner.banner.format;
|
|
|
|
NamedFile::open(format!("./uploads/{timestamp}.{format}"))?
|
|
} else {
|
|
NamedFile::open("./static/default-banner.png")?
|
|
};
|
|
|
|
let mut res = file.into_response(&req);
|
|
|
|
res.headers_mut().append(
|
|
CACHE_CONTROL,
|
|
HeaderValue::from_static("no-cache, no-store, must-revalidate"),
|
|
);
|
|
|
|
res.headers_mut()
|
|
.append(PRAGMA, HeaderValue::from_static("no-cache"));
|
|
|
|
Ok(res)
|
|
}
|
|
|
|
#[derive(Template)]
|
|
#[template(path = "error.html")]
|
|
struct ErrorTempalate {
|
|
error_code: u16,
|
|
error_message: String,
|
|
}
|
|
|
|
fn error_handler<B>(res: ServiceResponse<B>) -> actix_web::Result<ErrorHandlerResponse<B>>
|
|
where
|
|
B: MessageBody,
|
|
<B as MessageBody>::Error: ResponseError + 'static,
|
|
{
|
|
let (req, res) = res.into_parts();
|
|
let status = res.status();
|
|
|
|
let error_code = status.as_u16();
|
|
let error_message = match res.into_body().try_into_bytes().ok() {
|
|
Some(bytes) => String::from_utf8(bytes.to_vec()).unwrap_or_default(),
|
|
None => String::default(),
|
|
};
|
|
|
|
let template = ErrorTempalate {
|
|
error_code,
|
|
error_message,
|
|
};
|
|
|
|
let mut res = template_response(&template)?;
|
|
*(res.status_mut()) = status;
|
|
|
|
let res = ServiceResponse::new(req, res).map_into_right_body();
|
|
|
|
Ok(ErrorHandlerResponse::Response(res))
|
|
}
|