69 lines
2.4 KiB
Rust
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(())
|
|
}
|