Notes app :)
1use crate::{
2 route::index,
3 state::AppState,
4 task::{check_task, delete_task, new_task, pushback_task, tasks},
5 user::{login, new_user, user_info},
6};
7use axum::{
8 Router,
9 routing::{get, post},
10};
11use axum_cookie::CookieLayer;
12use deadpool_sqlite::{Config, Runtime};
13use minijinja::{Environment, path_loader};
14use serde::Serialize;
15use std::error::Error;
16use tower_http::services::ServeDir;
17
18mod route;
19pub mod session;
20pub mod state;
21mod task;
22pub mod user;
23
24type Res<T> = Result<T, Box<(dyn Error)>>;
25type SqlRes<T> = deadpool_sqlite::rusqlite::Result<T>;
26
27#[tokio::main]
28async fn main() -> Res<()> {
29 let port: u32 = std::env::var("PORT")
30 .unwrap_or("3000".to_string())
31 .parse()
32 .unwrap();
33
34 let env = jinja_env();
35
36 let cfg = Config::new("db.sqlite3");
37 let pool = cfg.create_pool(Runtime::Tokio1).unwrap();
38
39 let conn = pool.get().await.unwrap();
40 conn.interact(|conn| {
41 conn.execute(
42 "CREATE TABLE IF NOT EXISTS tasks (
43 text STRING NOT NULL,
44 date STRING NOT NULL,
45 user INTEGER NOT NULL,
46 checked BOOLEAN NOT NULL DEFAULT 0
47 )",
48 [],
49 )?;
50
51 conn.execute(
52 "CREATE TABLE IF NOT EXISTS users (
53 password STRING NOT NULL UNIQUE,
54 timezone STRING NOT NULL
55 )",
56 [],
57 )?;
58
59 conn.execute(
60 "CREATE TABLE IF NOT EXISTS sessions (
61 id STRING NOT NULL UNIQUE,
62 user INTEGER
63 )",
64 [],
65 )?;
66
67 SqlRes::Ok(())
68 })
69 .await??;
70
71 // build our application with a route
72 let app = Router::new()
73 .route("/", get(index))
74 .route("/task/new", post(new_task))
75 .route("/task/delete", post(delete_task))
76 .route("/task/list", post(tasks))
77 .route("/task/complete", post(check_task))
78 .route("/task/pushback", post(pushback_task))
79 .route("/user/new", post(new_user))
80 .route("/user/login", post(login))
81 .route("/user/info", post(user_info))
82 .nest_service("/static", ServeDir::new("static"))
83 .with_state(AppState { env, pool })
84 .layer(CookieLayer::default());
85
86 // run it
87 let listener = tokio::net::TcpListener::bind(format!("127.0.0.1:{port}")).await?;
88 println!(
89 "listening on {}. Change this with the PORT env var.",
90 listener.local_addr()?
91 );
92 axum::serve(listener, app).await?;
93
94 Ok(())
95}
96
97fn jinja_env() -> Environment<'static> {
98 let mut env = Environment::new();
99
100 env.set_loader(path_loader("templates"));
101 env
102}
103
104fn out(
105 env: &Environment<'static>,
106 file_name: &str,
107 context: impl Serialize,
108) -> Result<String, Box<dyn Error>> {
109 let template = env.get_template(file_name)?;
110 let str = template.render(context)?;
111
112 Ok(str)
113}