2022-04-05 09:48:29 -05:00
|
|
|
/*
|
|
|
|
* tmtd - Suckless To Do list
|
2022-04-09 10:31:10 -05:00
|
|
|
* Copyright (C) 2022 C4TG1RL5
|
2022-04-05 09:48:29 -05:00
|
|
|
*
|
|
|
|
* This program is free software: you can redistribute it and/or modify it
|
|
|
|
* under the terms of the GNU General Public License as published by the Free
|
|
|
|
* Software Foundation, either version 3 of the License, or (at your option)
|
|
|
|
* any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
|
|
* more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License along with
|
|
|
|
* this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
use std::sync::Arc;
|
2022-04-09 10:30:20 -05:00
|
|
|
|
|
|
|
use async_sqlx_session::PostgresSessionStore;
|
|
|
|
use chrono::Duration;
|
|
|
|
use sqlx::types::chrono::Utc;
|
2022-04-16 20:23:42 -05:00
|
|
|
use std::time::SystemTime;
|
2022-04-09 20:19:09 -05:00
|
|
|
use tera::{Context, Tera};
|
2022-04-05 09:48:29 -05:00
|
|
|
use tokio::sync::broadcast;
|
2022-04-08 11:54:08 -05:00
|
|
|
use tracing::info;
|
2022-04-16 05:55:32 -05:00
|
|
|
use warp::{Filter, Reply};
|
2022-04-09 10:30:20 -05:00
|
|
|
use warp_sessions::{CookieOptions, SameSiteCookieOption, SessionWithStore};
|
|
|
|
|
2022-04-10 07:53:00 -05:00
|
|
|
use crate::{task, Config, Database};
|
2022-04-09 10:30:20 -05:00
|
|
|
|
|
|
|
macro_rules! warp_try {
|
|
|
|
($expr:expr) => {
|
|
|
|
match $expr {
|
|
|
|
Ok(o) => o,
|
|
|
|
Err(e) => {
|
2022-04-16 05:55:32 -05:00
|
|
|
use warp::Reply;
|
|
|
|
tracing::warn!("Error in a request handler: {}", e);
|
|
|
|
return warp::reply::with_status(
|
2022-04-09 10:30:20 -05:00
|
|
|
format!("Error: {}", e),
|
2022-04-16 05:55:32 -05:00
|
|
|
warp::http::StatusCode::INTERNAL_SERVER_ERROR,
|
2022-04-09 10:30:20 -05:00
|
|
|
)
|
|
|
|
.into_response();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
($store:expr, $expr:expr) => {
|
|
|
|
match $expr {
|
|
|
|
Ok(o) => o,
|
|
|
|
Err(e) => {
|
2022-04-16 05:55:32 -05:00
|
|
|
use warp::Reply;
|
|
|
|
tracing::warn!("Error in a request handler: {}", e);
|
2022-04-09 10:30:20 -05:00
|
|
|
return (
|
2022-04-16 05:55:32 -05:00
|
|
|
warp::reply::with_status(
|
|
|
|
format!("Error: {}", e),
|
|
|
|
warp::http::StatusCode::INTERNAL_SERVER_ERROR,
|
|
|
|
)
|
|
|
|
.into_response(),
|
2022-04-09 10:30:20 -05:00
|
|
|
$store,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
macro_rules! warp_session {
|
|
|
|
($filter:expr, $store:expr, $then:expr) => {
|
|
|
|
$filter
|
|
|
|
.and(
|
|
|
|
warp_sessions::request::with_session(
|
|
|
|
$store.clone(),
|
|
|
|
Some(CookieOptions {
|
|
|
|
cookie_name: "tmtd_sid",
|
|
|
|
cookie_value: None,
|
2022-04-09 20:19:09 -05:00
|
|
|
max_age: Some(86400 * 7),
|
2022-04-09 10:30:20 -05:00
|
|
|
domain: None,
|
|
|
|
path: None,
|
|
|
|
secure: true,
|
|
|
|
http_only: true,
|
|
|
|
same_site: Some(SameSiteCookieOption::Strict),
|
|
|
|
}),
|
|
|
|
)
|
|
|
|
.map(|mut s: SessionWithStore<PostgresSessionStore>| {
|
|
|
|
s.session.set_expiry(Utc::now() + Duration::days(7));
|
|
|
|
s
|
|
|
|
}),
|
|
|
|
)
|
|
|
|
.then($then)
|
|
|
|
.untuple_one()
|
|
|
|
.and_then(warp_sessions::reply::with_session)
|
|
|
|
};
|
|
|
|
}
|
2022-04-05 09:48:29 -05:00
|
|
|
|
|
|
|
pub struct App {
|
|
|
|
config: Arc<Config>,
|
|
|
|
db: Arc<Database>,
|
2022-04-09 10:30:20 -05:00
|
|
|
session_store: PostgresSessionStore,
|
|
|
|
tera: Arc<Tera>,
|
2022-04-05 09:48:29 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
impl App {
|
2022-04-09 10:30:20 -05:00
|
|
|
pub fn new(
|
|
|
|
config: Arc<Config>,
|
|
|
|
db: Arc<Database>,
|
|
|
|
session_store: PostgresSessionStore,
|
|
|
|
) -> Result<Self, tera::Error> {
|
2022-04-05 09:48:29 -05:00
|
|
|
Ok(Self {
|
|
|
|
config,
|
|
|
|
db,
|
2022-04-09 10:30:20 -05:00
|
|
|
session_store,
|
2022-04-16 06:00:02 -05:00
|
|
|
tera: Arc::new(Tera::new("templates/**/*.html")?),
|
2022-04-05 09:48:29 -05:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn run(self, mut cancel: broadcast::Receiver<()>) {
|
2022-04-16 16:54:13 -05:00
|
|
|
let cloned_db = self.db.clone(); // Borrow checker moment
|
|
|
|
let db = warp::any().map(move || cloned_db.clone());
|
|
|
|
|
2022-04-09 10:30:20 -05:00
|
|
|
let route = warp_session!(
|
|
|
|
warp::path::end(),
|
|
|
|
self.session_store,
|
2022-04-16 05:55:32 -05:00
|
|
|
move |mut s: SessionWithStore<_>| async move {
|
2022-04-09 10:30:20 -05:00
|
|
|
s.session.insert_raw("test", "something".into());
|
|
|
|
(
|
|
|
|
warp::reply::html(format!("session id: {}", s.session.id())),
|
|
|
|
s,
|
|
|
|
)
|
2022-04-08 11:54:08 -05:00
|
|
|
}
|
2022-04-09 20:05:18 -05:00
|
|
|
)
|
2022-04-16 06:00:02 -05:00
|
|
|
.or(warp::path("task").map({
|
2022-04-09 20:19:09 -05:00
|
|
|
let tera = self.tera.clone();
|
2022-04-16 16:54:13 -05:00
|
|
|
let db = self.db.clone();
|
2022-04-10 07:53:00 -05:00
|
|
|
move || {
|
2022-04-09 20:19:09 -05:00
|
|
|
let mut ctx = Context::new();
|
2022-04-16 20:23:42 -05:00
|
|
|
// TODO: Replace the for loop with an SQL request to select only the tasks you can
|
|
|
|
// see after you logged in and the selected category which is stored in a cookie
|
2022-04-16 16:54:13 -05:00
|
|
|
// previously (see `post_task_sort` for more info)
|
|
|
|
// TODO: figure out what we need to pass to the get_issues function
|
|
|
|
let tasks = db.get_tasks();
|
2022-04-10 06:03:52 -05:00
|
|
|
ctx.insert("tasks", &tasks);
|
2022-04-16 06:00:02 -05:00
|
|
|
warp::reply::html(tera.render("task/task_page.html", &ctx).unwrap())
|
2022-04-09 20:05:18 -05:00
|
|
|
}
|
2022-04-10 07:53:00 -05:00
|
|
|
}))
|
2022-04-16 20:23:42 -05:00
|
|
|
.or(warp::path("create").map({
|
2022-04-15 13:00:36 -05:00
|
|
|
let tera = self.tera.clone();
|
|
|
|
move || {
|
|
|
|
let ctx = Context::new();
|
2022-04-16 06:00:02 -05:00
|
|
|
warp::reply::html(tera.render("task/create_task.html", &ctx).unwrap())
|
2022-04-15 13:00:36 -05:00
|
|
|
}
|
|
|
|
}))
|
2022-04-10 07:53:00 -05:00
|
|
|
.or(warp::post()
|
|
|
|
.and(warp::path("api"))
|
2022-04-15 13:00:36 -05:00
|
|
|
.and(warp::path("task"))
|
|
|
|
.and(warp::path("move"))
|
2022-04-16 16:54:13 -05:00
|
|
|
.and(warp::body::form::<task::MoveRequest>())
|
|
|
|
.and(db.clone())
|
2022-04-16 20:23:42 -05:00
|
|
|
.then(post_task_move))
|
2022-04-16 16:54:13 -05:00
|
|
|
.or(warp::post()
|
|
|
|
.and(warp::path("api"))
|
|
|
|
.and(warp::path("task"))
|
|
|
|
.and(warp::path("sort"))
|
2022-04-10 07:53:00 -05:00
|
|
|
.and(warp::body::form::<task::Request>())
|
2022-04-16 16:54:13 -05:00
|
|
|
.and(db.clone())
|
2022-04-16 20:23:42 -05:00
|
|
|
.then(post_task_sort))
|
2022-04-15 13:00:36 -05:00
|
|
|
.or(warp::post()
|
|
|
|
.and(warp::path("api"))
|
|
|
|
.and(warp::path("task"))
|
|
|
|
.and(warp::path("create"))
|
|
|
|
.and(warp::body::form::<task::Create>())
|
2022-04-16 16:54:13 -05:00
|
|
|
.and(db.clone())
|
2022-04-16 20:23:42 -05:00
|
|
|
.then(post_task_create));
|
2022-04-05 09:48:29 -05:00
|
|
|
|
|
|
|
let (_, server) =
|
2022-04-09 10:30:20 -05:00
|
|
|
warp::serve(route).bind_with_graceful_shutdown(self.config.listen_addr, async move {
|
2022-04-05 09:48:29 -05:00
|
|
|
cancel.recv().await.unwrap();
|
|
|
|
});
|
|
|
|
server.await;
|
2022-04-09 10:30:20 -05:00
|
|
|
info!("Web app has been shut down");
|
2022-04-05 09:48:29 -05:00
|
|
|
}
|
2022-04-08 11:54:08 -05:00
|
|
|
}
|
2022-04-15 13:00:36 -05:00
|
|
|
|
2022-04-16 16:54:13 -05:00
|
|
|
pub async fn post_task_move(form: task::MoveRequest, db: Arc<Database>) -> impl warp::Reply {
|
|
|
|
tracing::debug!("Got POST on /api/task/move: {:?}", form);
|
|
|
|
db.move_task(form).await;
|
|
|
|
warp::redirect(warp::http::Uri::from_static("/task"))
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn post_task_sort(form: task::Request, db: Arc<Database>) -> impl warp::Reply {
|
|
|
|
tracing::debug!("Got POST on /api/task/move: {}", form.request);
|
|
|
|
// TODO: Store the information of the form in a cookie for the selection of the tasks in the
|
2022-04-16 20:23:42 -05:00
|
|
|
// website building
|
2022-04-16 16:54:13 -05:00
|
|
|
warp::redirect(warp::http::Uri::from_static("/task"))
|
2022-04-15 13:00:36 -05:00
|
|
|
}
|
|
|
|
|
2022-04-16 20:23:42 -05:00
|
|
|
pub async fn post_task_create(form: task::Create, db: Arc<Database>) -> impl warp::Reply {
|
|
|
|
let date = SystemTime::now()
|
|
|
|
.duration_since(SystemTime::UNIX_EPOCH)
|
|
|
|
.unwrap();
|
2022-04-15 13:00:36 -05:00
|
|
|
tracing::debug!("Got POST on /api/task/create: {:?}, date: {:?}", form, date);
|
2022-04-16 16:58:45 -05:00
|
|
|
db.create_task(form).await;
|
2022-04-16 16:54:13 -05:00
|
|
|
warp::redirect(warp::http::Uri::from_static("/task"))
|
2022-04-15 13:00:36 -05:00
|
|
|
}
|