-use std::io::prelude::*;
-use std::{fs::File, env::args};
+use std::path::Path;
use actix_files as fs;
-use actix_web::{get, web, Responder, middleware, App, HttpServer, HttpResponse, HttpRequest, web::Query};
+use actix_web::{middleware, web, App, HttpServer};
+use chrono::prelude::*;
+use clap::Parser;
-use askama_actix::Template;
-use ron::de::from_reader;
-use serde::Deserialize;
-
-use itertools::Itertools;
+use data::db;
+mod config;
mod consts;
+mod data;
+mod email;
+mod hash;
mod model;
-mod db;
-
-#[derive(Template)]
-#[template(path = "home.html")]
-struct HomeTemplate {
- recipes: Vec<db::Recipe>
-}
-
-#[derive(Template)]
-#[template(path = "view_recipe.html")]
-struct ViewRecipeTemplate {
- recipes: Vec<db::Recipe>,
- current_recipe: db::Recipe
-}
-
-#[derive(Deserialize)]
-pub struct Request {
- m: Option<String>
-}
-
-#[get("/")]
-async fn home_page(req: HttpRequest) -> impl Responder {
- HomeTemplate { recipes: vec![ db::Recipe { title: String::from("Saumon en croûte feuilletée"), id: 1 }, db::Recipe { title: String::from("Croissant au jambon"), id: 2 } ] }
-}
-
-#[get("/recipe/view/{id}")]
-async fn view_page(req: HttpRequest, path: web::Path<(i32,)>) -> impl Responder {
- ViewRecipeTemplate { recipes: vec![ db::Recipe { title: String::from("Saumon en croûte feuilletée"), id: 1 }, db::Recipe { title: String::from("Croissant au jambon"), id: 2 } ], current_recipe: db::Recipe { title: String::from("Saumon en croûte feuilletée"), id: 1 } }
-}
-
-#[derive(Debug, Deserialize)]
-struct Config {
- port: u16
-}
-
-fn get_exe_name() -> String {
- let first_arg = std::env::args().nth(0).unwrap();
- let sep: &[_] = &['\\', '/'];
- first_arg[first_arg.rfind(sep).unwrap()+1..].to_string()
-}
+mod services;
+mod utils;
#[actix_web::main]
async fn main() -> std::io::Result<()> {
- if process_args() { return Ok(()) }
+ if process_args() {
+ return Ok(());
+ }
- std::env::set_var("RUST_LOG", "actix_web=debug");
+ std::env::set_var("RUST_LOG", "info,actix_web=info");
env_logger::init();
println!("Starting Recipes as web server...");
- let config: Config = {
- let f = File::open(consts::FILE_CONF).unwrap_or_else(|_| panic!("Failed to open configuration file {}", consts::FILE_CONF));
- match from_reader(f) {
- Ok(c) => c,
- Err(e) => panic!("Failed to load config: {}", e)
- }
- };
+ let config = web::Data::new(config::load());
+ let port = config.as_ref().port;
println!("Configuration: {:?}", config);
- // let database_connection = db::create_or_update();
-
- std::env::set_var("RUST_LOG", "actix_web=info");
-
- let mut server =
- HttpServer::new(
- || {
- App::new()
- .wrap(middleware::Logger::default())
- .wrap(middleware::Compress::default())
- .service(home_page)
- .service(view_page)
- .service(fs::Files::new("/static", "static").show_files_listing())
- }
- );
-
- server = server.bind(&format!("0.0.0.0:{}", config.port)).unwrap();
+ let db_connection = web::Data::new(db::Connection::new().unwrap());
+
+ let server = HttpServer::new(move || {
+ App::new()
+ .wrap(middleware::Logger::default())
+ .wrap(middleware::Compress::default())
+ .app_data(db_connection.clone())
+ .app_data(config.clone())
+ .service(services::home_page)
+ .service(services::sign_up_get)
+ .service(services::sign_up_post)
+ .service(services::sign_up_check_email)
+ .service(services::sign_up_validation)
+ .service(services::sign_in_get)
+ .service(services::sign_in_post)
+ .service(services::sign_out)
+ .service(services::view_recipe)
+ .service(services::edit_recipe)
+ .service(fs::Files::new("/static", "static"))
+ .default_service(web::to(services::not_found))
+ });
+ //.workers(1);
+
+ server.bind(&format!("0.0.0.0:{}", port))?.run().await
+}
- server.run().await
+#[derive(Parser, Debug)]
+struct Args {
+ /// Will clear the database and insert some test data. (A backup is made first).
+ #[arg(long)]
+ dbtest: bool,
}
fn process_args() -> bool {
- fn print_usage() {
- println!("Usage:");
- println!(" {} [--help] [--test]", get_exe_name());
- }
-
- let args: Vec<String> = args().collect();
+ let args = Args::parse();
+
+ if args.dbtest {
+ // Make a backup of the database.
+ let db_path = Path::new(consts::DB_DIRECTORY).join(consts::DB_FILENAME);
+ if db_path.exists() {
+ let db_path_bckup = (1..)
+ .find_map(|n| {
+ let p = db_path.with_extension(format!("sqlite.bckup{:03}", n));
+ if p.exists() {
+ None
+ } else {
+ Some(p)
+ }
+ })
+ .unwrap();
+ std::fs::copy(&db_path, &db_path_bckup).expect(&format!(
+ "Unable to make backup of {:?} to {:?}",
+ &db_path, &db_path_bckup
+ ));
+ std::fs::remove_file(&db_path)
+ .expect(&format!("Unable to remove db file: {:?}", &db_path));
+ }
- if args.iter().any(|arg| arg == "--help") {
- print_usage();
- return true
- } else if args.iter().any(|arg| arg == "--test") {
match db::Connection::new() {
- Ok(_) => (),
- Err(error) => println!("Error: {:?}", error)
+ Ok(con) => {
+ if let Err(error) = con.execute_file("sql/data_test.sql") {
+ eprintln!("{}", error);
+ }
+ // Set the creation datetime to 'now'.
+ con.execute_sql(
+ "UPDATE [User] SET [creation_datetime] = ?1 WHERE [email] = 'paul@test.org'",
+ [Utc::now()],
+ )
+ .unwrap();
+ }
+ Err(error) => {
+ eprintln!("{}", error);
+ }
}
- return true
+
+ return true;
}
false