website/api/src/serve.rs

69 lines
2.4 KiB
Rust

use std::sync::{Arc, Mutex};
use axum::routing::{get, post};
use axum::Router;
use rusqlite::Connection;
use tower_http::cors::CorsLayer;
use crate::handlers;
use crate::rate_limit::RateLimiter;
pub struct AppState {
pub db: Mutex<Connection>,
pub notify_email: String,
pub mail_domain: String,
pub qa_reply_domain: String,
pub rate_limiter: RateLimiter,
pub webhook_secret: String,
}
pub async fn run() -> Result<(), Box<dyn std::error::Error>> {
let db_path = std::env::var("QA_DB_PATH").unwrap_or_else(|_| "qa.db".to_string());
let notify_email = std::env::var("QA_NOTIFY_EMAIL").expect("QA_NOTIFY_EMAIL must be set");
let mail_domain =
std::env::var("QA_MAIL_DOMAIN").unwrap_or_else(|_| "extremist.software".to_string());
let qa_reply_domain =
std::env::var("QA_REPLY_DOMAIN").unwrap_or_else(|_| mail_domain.clone());
let webhook_secret = std::env::var("WEBHOOK_SECRET").expect("WEBHOOK_SECRET must be set");
let listen_address =
std::env::var("QA_LISTEN_ADDRESS").unwrap_or_else(|_| "127.0.0.1".to_string());
let listen_port = std::env::var("QA_LISTEN_PORT").unwrap_or_else(|_| "3003".to_string());
let listen_target = format!("{listen_address}:{listen_port}");
let conn = Connection::open(&db_path)?;
conn.execute_batch("PRAGMA journal_mode=WAL;")?;
conn.execute_batch(
"CREATE TABLE IF NOT EXISTS questions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
question TEXT NOT NULL,
answer TEXT,
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),
answered_at TEXT
);",
)?;
let state = Arc::new(AppState {
db: Mutex::new(conn),
notify_email,
mail_domain,
qa_reply_domain,
rate_limiter: RateLimiter::new(5, 3600),
webhook_secret,
});
let app = Router::new()
.route(
"/api/questions",
get(handlers::get_questions).post(handlers::post_question),
)
.route("/api/questions/stats", get(handlers::get_question_stats))
.route("/qa/rss.xml", get(handlers::get_question_rss))
.route("/api/webhook", post(handlers::webhook))
.layer(CorsLayer::permissive())
.with_state(state);
let listener = tokio::net::TcpListener::bind(&listen_target).await?;
println!("Listening on {listen_target}");
axum::serve(listener, app).await?;
Ok(())
}