receipts/src/main.rs

116 lines
2.8 KiB
Rust

mod config;
mod firefly;
mod receipts;
mod routes;
mod transactions;
use rocket::fairing::{self, AdHoc};
use rocket::fs::FileServer;
use rocket::response::{Redirect, Responder};
use rocket::serde::json::Json;
use rocket::Rocket;
use rocket_db_pools::Database as RocketDatabase;
use rocket_dyn_templates::Template;
use serde::Serialize;
use tracing::{debug, error, info};
use config::Config;
use firefly::Firefly;
struct Context {
#[allow(dead_code)]
config: Config,
firefly: Firefly,
}
#[derive(Debug, thiserror::Error)]
enum InitError {
#[error("Invalid config: {0}")]
Config(std::io::Error),
}
impl Context {
pub fn init(config: Config) -> Result<Self, InitError> {
let token = std::fs::read_to_string(&config.firefly.token)
.map_err(InitError::Config)?;
let firefly = Firefly::new(&config.firefly.url, &token);
Ok(Self { config, firefly })
}
}
#[derive(RocketDatabase)]
#[database("receipts")]
struct Database(rocket_db_pools::sqlx::PgPool);
#[derive(Serialize)]
struct ErrorBody {
error: String,
}
#[derive(Responder)]
#[response(status = 500)]
struct ErrorResponse {
error: Json<ErrorBody>,
}
impl ErrorResponse {
pub fn new<S: Into<String>>(error: S) -> Self {
Self {
error: Json(ErrorBody {
error: error.into(),
}),
}
}
}
#[rocket::get("/")]
async fn index() -> Redirect {
Redirect::to("/receipts")
}
async fn run_migrations(rocket: Rocket<rocket::Build>) -> fairing::Result {
if let Some(db) = Database::fetch(&rocket) {
info!("Applying database migrations");
if let Err(e) = sqlx::migrate!("./migrations").run(&db.0).await {
error!("Database migration failed: {}", e);
Err(rocket)
} else {
Ok(rocket)
}
} else {
Err(rocket)
}
}
#[rocket::launch]
async fn rocket() -> _ {
tracing_subscriber::fmt()
.with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
.with_writer(std::io::stderr)
.init();
let rocket = rocket::build();
let figment = rocket.figment();
let config: Config = figment.extract().unwrap();
let ctx = match Context::init(config) {
Ok(c) => c,
Err(e) => {
error!("Failed to initialize application context: {}", e);
std::process::exit(1);
},
};
debug!("Using Firefly III URL {}", &ctx.firefly.url());
rocket
.manage(ctx)
.mount("/", rocket::routes![index])
.mount("/transactions", routes::transactions::routes())
.mount("/receipts", routes::receipts::routes())
.mount("/static", FileServer::from("static"))
.attach(Template::fairing())
.attach(Database::init())
.attach(AdHoc::try_on_ignite("Migrate Database", run_migrations))
}