use axum::{
routing::{get, post},
Router,
};
use clap::Parser;
use include_dir::{include_dir, Dir};
use minijinja::Environment;
use std::sync::Arc;
use tower_http::trace::{self, TraceLayer};
use tracing::Level;
mod errors;
mod git;
mod md2html;
mod page;
mod routes;
pub static STATIC_ASSETS: Dir = include_dir!("static");
pub static TEMPLATES: Dir = include_dir!("templates");
pub static CSS: &str = concat!(
include_str!("../css/reset.css"),
include_str!("../css/icons.css"),
include_str!("../css/wiki.css"),
include_str!("../css/content.css"),
);
#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
struct Args {
#[arg(short, long, env = "WIKIMARK_PORT", default_value = "3000")]
port: u16,
#[arg(short, long, env = "WIKIMARK_ADDR", default_value = "127.0.0.1")]
address: String,
#[arg(short, long, env = "WIKIMARK_REPO", default_value = "repo")]
repo: String,
#[arg(short, long, env = "WIKIMARK_COMMIT_URL_PREFIX", default_value = "")]
commit_url_prefix: String,
}
pub struct WikiState {
pub repo: git::ThreadSafeRepo,
pub commit_url_prefix: String,
pub env: Environment<'static>,
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
dotenv::dotenv().ok();
let args = Args::parse();
tracing_subscriber::fmt()
.compact()
.with_target(false)
.with_env_filter(tracing_subscriber::EnvFilter::from_env("WIKIMARK_LOG"))
.init();
let repo = git::ThreadSafeRepo::open(&args.repo)?;
let mut env = Environment::new();
let env_repo = repo.clone();
env.set_loader(move |name| {
if let Ok(c) = env_repo.local().get_file(&format!("templates/{name}")) {
Ok(Some(c))
} else {
Ok(TEMPLATES.get_file(name).map(|f| f.contents_utf8().unwrap().to_owned()))
}
});
let state = WikiState {
repo,
commit_url_prefix: args.commit_url_prefix,
env,
};
use routes::*;
let app = Router::new()
.route("/", get(index))
.route("/static/wiki.css", get(css))
.route("/static/{*path}", get(assets))
.route("/page/", get(page))
.route("/page/{*page}", get(page))
.route("/all", get(pages))
.route("/edit", get(edit))
.route("/commit", post(commit))
.route("/changelog", get(changelog))
.layer(
TraceLayer::new_for_http()
.make_span_with(trace::DefaultMakeSpan::new().level(Level::INFO))
.on_response(
trace::DefaultOnResponse::new()
.level(Level::INFO)
.latency_unit(tower_http::LatencyUnit::Micros),
),
)
.with_state(Arc::new(state));
#[cfg(debug_assertions)]
let app = app.layer(tower_livereload::LiveReloadLayer::new().request_predicate(
|r: &axum::http::Request<axum::body::Body>| r.headers().get("HX-Request").is_none(),
));
let listener = tokio::net::TcpListener::bind(format!("{}:{}", args.address, args.port)).await?;
axum::serve(listener, app).await?;
Ok(())
}