116 lines
2.8 KiB
Rust
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))
|
|
}
|