Add calendar tables

This commit is contained in:
Chris Jean-Marie 2024-11-17 04:24:39 +00:00
parent 8bc418ebb2
commit 158fef7902
5 changed files with 175 additions and 51 deletions

View File

@ -0,0 +1,5 @@
-- Add down migration script here
drop table if exists `calendar_events`;
drop table if exists `calendar_event_types`;
drop table if exists `calendar`;

View File

@ -0,0 +1,45 @@
-- Add up migration script here
-- Calendars
-- 1 - Cottage
-- 2 - Family tree
create table calendar (
id integer not null primary key autoincrement,
created_at integer not null default CURRENT_TIMESTAMP,
created_by integer not null default 0,
updated_at integer null default CURRENT_TIMESTAMP,
updated_by integer not null default 0,
name varchar(255) not null
)
-- Event types
-- 1 - Rental
-- 2 - Life event
create table calendar_event_types (
id integer not null primary key autoincrement,
created_at integer not null default CURRENT_TIMESTAMP,
created_by integer not null default 0,
updated_at integer null default CURRENT_TIMESTAMP,
updated_by integer not null default 0,
name varchar(255) not null
)
create table calendar_events (
id integer not null primary key autoincrement,
created_at integer not null default CURRENT_TIMESTAMP,
created_by integer not null default 0,
updated_at integer null default CURRENT_TIMESTAMP,
updated_by integer not null default 0,
calendar_id integer not null,
event_type_id integer not null,
title varchar(255) not null,
description varchar(255) null,
start_time integer null,
end_time integer null,
repeat_type integer not null default 0, -- 0 - None, 1 - Daily, 2 - Weekly, 3 - Monthly, 4 - Yearly, 5 - Day of week, 6 - Day of month
repeat_interval integer not null default 0,
celebrate boolean not null default 0
foreign key (calendar_id) references calendar(id),
foreign key (event_type_id) references calendar_event_types(id)
)

75
backend/src/calendar.rs Normal file
View File

@ -0,0 +1,75 @@
use askama::Template;
use askama_axum::{IntoResponse, Response};
use axum::{
extract::State,
response::{Html, Redirect},
Extension,
};
use http::StatusCode;
use sqlx::SqlitePool;
use crate::{
middlewares::is_authorized,
user::{get_user_roles_display, UserData},
};
struct HtmlTemplate<T>(T);
impl<T> IntoResponse for HtmlTemplate<T>
where
T: Template,
{
fn into_response(self) -> Response {
match self.0.render() {
Ok(html) => Html(html).into_response(),
Err(err) => (
StatusCode::INTERNAL_SERVER_ERROR,
format!("Failed to render template. Error: {}", err),
)
.into_response(),
}
}
}
#[derive(Template)]
#[template(path = "cottagecalendar.html")]
struct CottageCalendarTemplate {
logged_in: bool,
name: String,
user_roles: Vec<crate::user::UserRolesDisplay>,
}
pub async fn cottagecalendar(
Extension(user_data): Extension<Option<UserData>>,
State(db_pool): State<SqlitePool>,
) -> impl IntoResponse {
let user_name = user_data.as_ref().map(|s| s.name.clone());
let logged_in = user_name.is_some();
let name = user_name.unwrap_or_default();
let user_id = user_data.as_ref().map(|s| s.id.clone()).unwrap_or_default();
if is_authorized("/cottagecalendar", user_data, db_pool.clone()).await {
// Get user roles
let user_roles = get_user_roles_display(user_id, &db_pool.clone()).await;
let template = CottageCalendarTemplate {
logged_in,
name,
user_roles,
};
HtmlTemplate(template).into_response()
} else {
Redirect::to("/").into_response()
}
}
async fn get_next_event(db_pool: &SqlitePool) -> Option<String> {
let next_event = sqlx::query_as::<_, (String, String)>(
"SELECT date, title FROM events ORDER BY date ASC LIMIT 1",
)
.fetch_one(db_pool)
.await;
next_event.map(|(date, title)| format!("{} - {}", date, title))
}

View File

@ -1,30 +1,37 @@
use std::net::SocketAddr;
use axum::{
middleware, routing::{get, get_service}, Extension, Router
middleware,
routing::{get, get_service},
Extension, Router,
};
use sqlx::{sqlite::SqlitePoolOptions, SqlitePool};
use calendar::cottagecalendar;
use sqlx::migrate::Migrator;
use sqlx::{sqlite::SqlitePoolOptions, SqlitePool};
use std::net::SocketAddr;
use tower_http::services::ServeDir;
mod calendar;
mod email;
mod error_handling;
mod google_oauth;
mod middlewares;
mod routes;
mod user;
mod wishlist;
mod email;
use error_handling::AppError;
use google_oauth::{google_auth_return, login, logout};
use middlewares::inject_user_data;
use google_oauth::{login, logout, google_auth_return};
use routes::{about, contact, cottagecalendar, dashboard, index, profile, user_profile, useradmin};
use routes::{about, contact, dashboard, index, profile, user_profile, useradmin};
use user::{add_user_role, delete_user_role, UserData};
use wishlist::{user_wishlist, user_wishlist_add, user_wishlist_add_item, user_wishlist_bought_item, user_wishlist_edit_item, user_wishlist_received_item, user_wishlist_save_item, wishlists};
use wishlist::{
user_wishlist, user_wishlist_add, user_wishlist_add_item, user_wishlist_bought_item,
user_wishlist_edit_item, user_wishlist_received_item, user_wishlist_save_item, wishlists,
};
//use email::send_emails;
#[derive(Clone)]
pub struct AppState {
pub db_pool: SqlitePool
pub db_pool: SqlitePool,
}
#[tokio::main]
@ -36,8 +43,10 @@ async fn main() {
.max_connections(5)
.connect("sqlite://db/db.sqlite3")
.await;
let app_state = AppState {db_pool:db_pool.expect("Failed to get db_pool")};
let app_state = AppState {
db_pool: db_pool.expect("Failed to get db_pool"),
};
static MIGRATOR: Migrator = sqlx::migrate!();
@ -50,31 +59,51 @@ async fn main() {
// build our application with some routes
let app = Router::new()
.route("/dashboard", get(dashboard))
.route("/cottagecalendar", get(cottagecalendar))
.route("/dashboard", get(dashboard))
.route("/profile", get(profile))
.route("/useradmin", get(useradmin))
.route("/users/:user_id", get(user_profile))
.route("/roles/:user_id/:role_id/add", get(add_user_role))
.route("/roles/:user_id/:user_role_id/delete", get(delete_user_role))
.route(
"/roles/:user_id/:user_role_id/delete",
get(delete_user_role),
)
.route("/wishlists", get(wishlists))
.route("/userwishlist/:user_id", get(user_wishlist))
.route("/userwishlist/add/:user_id", get(user_wishlist_add).post(user_wishlist_add_item))
.route("/userwishlist/edit/:item_id", get(user_wishlist_edit_item).post(user_wishlist_save_item))
.route("/userwishlist/bought/:user_id", get(user_wishlist_bought_item))
.route("/userwishlist/received/:user_id", get(user_wishlist_received_item))
.nest_service("/assets", ServeDir::new("templates/assets")
.fallback(get_service(ServeDir::new("templates/assets"))))
.route(
"/userwishlist/add/:user_id",
get(user_wishlist_add).post(user_wishlist_add_item),
)
.route(
"/userwishlist/edit/:item_id",
get(user_wishlist_edit_item).post(user_wishlist_save_item),
)
.route(
"/userwishlist/bought/:user_id",
get(user_wishlist_bought_item),
)
.route(
"/userwishlist/received/:user_id",
get(user_wishlist_received_item),
)
.nest_service(
"/assets",
ServeDir::new("templates/assets")
.fallback(get_service(ServeDir::new("templates/assets"))),
)
.route("/", get(index))
.route("/about", get(about))
.route("/contactus", get(contact))
.route("/login", get(login))
.route("/logout", get(logout))
.route("/google_auth_return", get(google_auth_return))
.route_layer(middleware::from_fn_with_state(app_state.db_pool.clone(), inject_user_data))
.route_layer(middleware::from_fn_with_state(
app_state.db_pool.clone(),
inject_user_data,
))
.with_state(app_state.db_pool.clone())
.layer(Extension(user_data))
;
.layer(Extension(user_data));
// Send email indicating server has started
//let recipients = get_useremails_by_role("admin".to_string(), &app_state.db_pool).await;
@ -87,5 +116,4 @@ async fn main() {
.serve(app.into_make_service())
.await
.unwrap();
}

View File

@ -246,32 +246,3 @@ pub async fn contact(Extension(user_data): Extension<Option<UserData>>) -> impl
let template = ContactTemplate { logged_in, name };
HtmlTemplate(template)
}
#[derive(Template)]
#[template(path = "cottagecalendar.html")]
struct CottageCalendarTemplate {
logged_in: bool,
name: String,
user_roles: Vec<crate::user::UserRolesDisplay>,
}
pub async fn cottagecalendar(
Extension(user_data): Extension<Option<UserData>>,
State(db_pool): State<SqlitePool>,
) -> impl IntoResponse {
let user_name = user_data.as_ref().map(|s| s.name.clone());
let logged_in = user_name.is_some();
let name = user_name.unwrap_or_default();
let user_id = user_data.as_ref().map(|s| s.id.clone()).unwrap_or_default();
if is_authorized("/cottagecalendar", user_data, db_pool.clone()).await {
// Get user roles
let user_roles = get_user_roles_display(user_id, &db_pool.clone()).await;
let template = CottageCalendarTemplate { logged_in, name, user_roles };
HtmlTemplate(template).into_response()
} else {
Redirect::to("/").into_response()
}
}