#![feature(str_strip)] extern crate fern; #[macro_use] extern crate log; #[macro_use] extern crate diesel_migrations; use chrono::Utc; use diesel::connection::Connection; use diesel::pg::PgConnection; use pjournal::db; use pjournal::importer; use pjournal::models; use serde_json::to_value; use structopt::StructOpt; embed_migrations!(); /// Practice Journal #[derive(Debug, StructOpt)] struct Opt { /// URL to postgresql database #[structopt(long)] database_url: String, /// Endpoint for this web server #[structopt(long, default_value = "http://localhost:8080/")] base_url: String, /// Path to directory containing templates #[structopt(long, default_value = "./templates")] template_path: String, /// Path to directory containing static files #[structopt(long, default_value = "./static")] static_path: String, /// Port on which to server HTTP requests #[structopt(long, default_value = "8000")] port: u16, #[structopt(subcommand)] cmd: Option, } #[derive(Debug, StructOpt)] enum Command { /// Initialize config table in the database Init { /// Secret passed to rocket for encrypting cookies #[structopt(long)] rocket_secret_key: String, /// Client secret for strava authentication #[structopt(long)] strava_client_secret: String, /// Client id for strava authentication #[structopt(long)] strava_client_id: String, }, /// Add a user account AddUser { username: String, password: String }, /// Create a ProcessAllRawData task ProcessAllData, } fn setup_logger() -> Result<(), fern::InitError> { use fern::colors::{Color, ColoredLevelConfig}; let colors = ColoredLevelConfig::new(); fern::Dispatch::new() .format(move |out, message, record| { let thread = std::thread::current(); let thread_id = &format!("{:?}", thread.id())[..]; let prefix = "ThreadId("; let thread_id = if thread_id.find(prefix).is_some() { &thread_id[prefix.len()..thread_id.len() - 1] } else { thread_id }; let thread_colors = [ Color::Red, Color::Green, Color::Magenta, Color::Cyan, Color::White, Color::Yellow, Color::BrightRed, Color::BrightGreen, Color::BrightMagenta, Color::BrightCyan, Color::BrightBlue, Color::BrightWhite, ]; use std::collections::hash_map::DefaultHasher; use std::hash::{Hash, Hasher}; let mut hasher = DefaultHasher::new(); thread_id.hash(&mut hasher); let thread_color = thread_colors[hasher.finish() as usize % thread_colors.len()]; out.finish(format_args!( "[{}] \x1B[{}m{}@{}\x1B[0m {}", colors.color(record.level()), thread_color.to_fg_str(), thread.name().unwrap_or(""), thread_id, message )) }) .level(log::LevelFilter::Info) .chain(std::io::stdout()) .apply()?; Ok(()) } fn main() { setup_logger().expect("logger"); let opt = Opt::from_args(); let conn = PgConnection::establish(&opt.database_url).unwrap(); embedded_migrations::run(&conn).unwrap(); match opt.cmd { Some(Command::Init { rocket_secret_key, strava_client_secret, strava_client_id, }) => { let config = models::Config { strava_client_id, strava_client_secret, rocket_secret_key, singleton: true, }; db::create_config(&conn, &config).unwrap(); info!("config created"); } Some(Command::AddUser { username, password }) => { db::adduser(&conn, &username, &password).unwrap(); info!("added user {}", username); } Some(Command::ProcessAllData) => { let command = importer::Command::ProcessAllRawData; db::insert_task( &conn, &models::NewTask { start_at: Utc::now(), state: models::TaskState::NEW, username: "system", payload: &to_value(command).unwrap(), }, ) .expect("insert"); info!("ProcessAllRawData: task inserted"); } None => { info!("starting server with options {:?}", opt); pjournal::server::start( conn, &opt.database_url, &opt.base_url, &opt.static_path, opt.port, &opt.template_path, ); } } }