106 lines
3.2 KiB
Rust
106 lines
3.2 KiB
Rust
mod db;
|
|
mod handlers;
|
|
mod models;
|
|
|
|
use askama::Template;
|
|
use axum::{
|
|
Router,
|
|
extract::State,
|
|
response::{Html, IntoResponse, Response},
|
|
routing::{get, post},
|
|
};
|
|
use sqlx::SqlitePool;
|
|
use tower_http::services::ServeDir;
|
|
use tower_sessions::{SessionManagerLayer, cookie::SameSite};
|
|
use tower_sessions_sqlx_store::SqliteStore;
|
|
|
|
use handlers::auth::{
|
|
get_current_user, login_page, login_submit, logout, register_page, register_submit,
|
|
};
|
|
use handlers::thread::{
|
|
create_post, create_thread, list_threads, new_thread_page, view_profile, view_thread,
|
|
};
|
|
|
|
pub struct CurrentUser {
|
|
pub username: String,
|
|
}
|
|
|
|
#[derive(Template)]
|
|
#[template(path = "index.html")]
|
|
struct IndexTemplate {
|
|
title: String,
|
|
current_user: Option<CurrentUser>,
|
|
}
|
|
|
|
async fn index(State(pool): State<SqlitePool>, session: tower_sessions::Session) -> Response {
|
|
let user: Option<models::user::User> = get_current_user(&session, &pool).await;
|
|
let current_user = user.map(|u| CurrentUser {
|
|
username: u.username,
|
|
});
|
|
|
|
let tmpl = IndexTemplate {
|
|
title: "home".into(),
|
|
current_user,
|
|
};
|
|
|
|
match tmpl.render() {
|
|
Ok(html) => Html(html).into_response(),
|
|
Err(_) => (
|
|
axum::http::StatusCode::INTERNAL_SERVER_ERROR,
|
|
"render error",
|
|
)
|
|
.into_response(),
|
|
}
|
|
}
|
|
|
|
#[tokio::main]
|
|
async fn main() {
|
|
dotenvy::dotenv().ok();
|
|
|
|
let database_url = std::env::var("SARMENTINE_DATABASE_URL")
|
|
.unwrap_or_else(|_| "sqlite:./sarmentine.db".into());
|
|
|
|
let pool: SqlitePool = db::connect(&database_url).await;
|
|
|
|
let session_store = SqliteStore::new(pool.clone());
|
|
session_store
|
|
.migrate()
|
|
.await
|
|
.expect("session store migration failed");
|
|
|
|
let session_secret = std::env::var("SARMENTINE_SESSION_SECRET")
|
|
.unwrap_or_else(|_| "change-me-in-production-use-a-long-random-string!!".into());
|
|
|
|
let session_layer = SessionManagerLayer::new(session_store)
|
|
.with_secure(false)
|
|
.with_same_site(SameSite::Lax)
|
|
.with_signed(tower_sessions::cookie::Key::from(session_secret.as_bytes()));
|
|
|
|
let static_dir = std::env::var("SARMENTINE_STATIC_DIR").unwrap_or_else(|_| "static".into());
|
|
|
|
let bind_addr =
|
|
std::env::var("SARMENTINE_BIND_ADDR").unwrap_or_else(|_| "127.0.0.1:3000".into());
|
|
|
|
let app = Router::new()
|
|
.route("/", get(index))
|
|
.route("/threads", get(list_threads))
|
|
.route("/threads/new", get(new_thread_page))
|
|
.route("/threads", post(create_thread))
|
|
.route("/threads/:id", get(view_thread))
|
|
.route("/threads/:id/posts", post(create_post))
|
|
.route("/profile/:username", get(view_profile))
|
|
.route("/auth/register", get(register_page))
|
|
.route("/auth/register", post(register_submit))
|
|
.route("/auth/login", get(login_page))
|
|
.route("/auth/login", post(login_submit))
|
|
.route("/auth/logout", post(logout))
|
|
.nest_service("/static", ServeDir::new(&static_dir))
|
|
.layer(session_layer)
|
|
.with_state(pool);
|
|
|
|
let listener = tokio::net::TcpListener::bind(&bind_addr).await.unwrap();
|
|
|
|
println!("listening on http://{}", bind_addr);
|
|
axum::serve(listener, app).await.unwrap();
|
|
}
|