diff --git a/migrations/20230609121618_initial.sql b/migrations/20230609121618_initial.sql index e312ba72..254d120e 100644 --- a/migrations/20230609121618_initial.sql +++ b/migrations/20230609121618_initial.sql @@ -1,4 +1,4 @@ -CREATE TYPE role AS ENUM ('staff', 'area manager', 'admin'); +CREATE TYPE role AS ENUM ('staff', 'areamanager', 'admin'); CREATE TYPE function AS ENUM ('posten', 'wachhabender'); CREATE TABLE area diff --git a/src/auth/routes/post_register.rs b/src/auth/routes/post_register.rs index 0fc96653..5d3e9977 100644 --- a/src/auth/routes/post_register.rs +++ b/src/auth/routes/post_register.rs @@ -33,10 +33,10 @@ async fn route( let result = User::create( pool.get_ref(), - form.name, - form.email, - hash, - salt, + &form.name, + &form.email, + &hash, + &salt, role, function, form.area_id, diff --git a/src/endpoints/mod.rs b/src/endpoints/mod.rs index 2d69ba30..addc7b46 100644 --- a/src/endpoints/mod.rs +++ b/src/endpoints/mod.rs @@ -1,10 +1,19 @@ use actix_web::web::ServiceConfig; +use serde::Deserialize; mod location; mod user; +#[derive(Deserialize)] +pub struct IdPath { + pub id: i32 +} + pub fn init(cfg: &mut ServiceConfig) { cfg.service(location::get); - cfg.service(user::get); + cfg.service(user::get_overview::get_overview); + cfg.service(user::get_new::get_new); + cfg.service(user::post_new::post_new); + cfg.service(user::get_edit::get_edit); } diff --git a/src/endpoints/user/get_edit.rs b/src/endpoints/user/get_edit.rs new file mode 100644 index 00000000..10d2b62c --- /dev/null +++ b/src/endpoints/user/get_edit.rs @@ -0,0 +1,37 @@ +use actix_identity::Identity; +use actix_web::{web, HttpResponse, Responder}; +use askama::Template; +use askama_actix::TemplateToResponse; +use sqlx::PgPool; + +use crate::{endpoints::IdPath, models::{Area, Role, User}}; + +#[derive(Template)] +#[template(path = "user/edit.html")] +pub struct EditUserTemplate { + user: User, + areas: Option>, + email: String, + name: String, + role: u8, + function: u8, + area_id: i32 +} + +#[actix_web::get("/users/edit/{id}")] +pub async fn get_edit(user: Identity, pool: web::Data, path: web::Path) -> impl Responder { + let current_user = User::read_by_id(pool.get_ref(), user.id().unwrap().parse().unwrap()).await.unwrap(); + let mut areas = None; + + if current_user.role == Role::Admin { + areas = Some(Area::read_all(pool.get_ref()).await.unwrap()); + } + + if let Ok(user_in_db) = User::read_by_id(pool.get_ref(), path.id).await { + let template = EditUserTemplate { user: current_user, areas, email: user_in_db.email, name: user_in_db.name, role: user_in_db.role as u8, function: user_in_db.function as u8, area_id: user_in_db.area_id }; + + return template.to_response(); + } + + HttpResponse::BadRequest().body("Fehler beim Bearbeiten des Nutzers") +} diff --git a/src/endpoints/user/get_new.rs b/src/endpoints/user/get_new.rs new file mode 100644 index 00000000..86274839 --- /dev/null +++ b/src/endpoints/user/get_new.rs @@ -0,0 +1,28 @@ +use actix_identity::Identity; +use actix_web::{web, Responder}; +use askama::Template; +use askama_actix::TemplateToResponse; +use sqlx::PgPool; + +use crate::models::{Area, Role, User}; + +#[derive(Template)] +#[template(path = "user/new.html")] +pub struct NewUserTemplate { + user: User, + areas: Option> +} + +#[actix_web::get("/users/new")] +pub async fn get_new(user: Identity, pool: web::Data) -> impl Responder { + let current_user = User::read_by_id(pool.get_ref(), user.id().unwrap().parse().unwrap()).await.unwrap(); + let mut areas: Option> = None; + + if current_user.role == Role::Admin { + areas = Some(Area::read_all(pool.get_ref()).await.unwrap()) + } + + let template = NewUserTemplate { user: current_user, areas }; + + template.to_response() +} diff --git a/src/endpoints/user/get_overview.rs b/src/endpoints/user/get_overview.rs new file mode 100644 index 00000000..e5eb6ee2 --- /dev/null +++ b/src/endpoints/user/get_overview.rs @@ -0,0 +1,49 @@ +use crate::models::{Area, Role, User, Function}; +use actix_identity::Identity; +use actix_web::{web, HttpResponse, Responder}; +use askama::Template; +use askama_actix::TemplateToResponse; +use sqlx::PgPool; + +#[derive(Template)] +#[template(path = "user/overview.html")] +pub struct UsersTemplate { + user: User, + area: Option, + users: Vec, +} + +#[actix_web::get("/users")] +pub async fn get_overview(user: Identity, pool: web::Data) -> impl Responder { + let current_user = User::read_by_id(pool.get_ref(), user.id().unwrap().parse().unwrap()) + .await + .unwrap(); + + if current_user.role == Role::AreaManager || current_user.role == Role::Admin { + let mut area = None; + let users; + + if current_user.role == Role::AreaManager { + area = Some( + Area::read_by_id(pool.get_ref(), current_user.area_id) + .await + .unwrap(), + ); + users = User::read_all_by_area(pool.get_ref(), current_user.area_id) + .await + .unwrap(); + } else { + users = User::read_all_including_area(pool.get_ref()).await.unwrap(); + } + + let template = UsersTemplate { + user: current_user, + area, + users, + }; + + return template.to_response(); + } + + return HttpResponse::BadRequest().body("Fehler beim Zugriff auf die Nutzerverwaltung!"); +} diff --git a/src/endpoints/user/mod.rs b/src/endpoints/user/mod.rs index 96217ec8..7b332c34 100644 --- a/src/endpoints/user/mod.rs +++ b/src/endpoints/user/mod.rs @@ -1,38 +1,5 @@ -use actix_identity::Identity; -use actix_web::{web, HttpResponse, Responder}; -use askama::Template; -use askama_actix::TemplateToResponse; -use sqlx::PgPool; - -use crate::models::{Area, Role, User}; - -#[derive(Template)] -#[template(path = "user/overview.html")] -pub struct UsersTemplate { - user: User, - area: Option, - users: Vec -} - -#[actix_web::get("/users")] -pub async fn get(user: Identity, pool: web::Data) -> impl Responder { - let current_user = User::read_by_id(pool.get_ref(), user.id().unwrap().parse().unwrap()).await.unwrap(); - - if current_user.role == Role::AreaManager || current_user.role == Role::Admin { - let mut area = None; - let users; - - if current_user.role == Role::AreaManager { - area = Some(Area::read_by_id(pool.get_ref(), current_user.area_id).await.unwrap()); - users = User::read_all_by_area(pool.get_ref(), current_user.area_id).await.unwrap(); - } else { - users = User::read_all(pool.get_ref()).await.unwrap(); - } - - let template = UsersTemplate { user: current_user, area, users}; - - return template.to_response() - } - - return HttpResponse::BadRequest().body("Fehler beim Zugriff auf die Nutzerverwaltung!"); -} +pub mod get_overview; +pub mod get_new; +pub mod post_new; +pub mod get_edit; +pub mod put_edit; diff --git a/src/endpoints/user/post_new.rs b/src/endpoints/user/post_new.rs new file mode 100644 index 00000000..5a0cba0d --- /dev/null +++ b/src/endpoints/user/post_new.rs @@ -0,0 +1,41 @@ +use actix_identity::Identity; +use actix_web::{http::header::LOCATION, web, HttpResponse, Responder}; +use serde::Deserialize; +use sqlx::PgPool; + +use crate::{auth::utils::generate_salt_and_hash_plain_password, models::{Function, Role, User}}; + +#[derive(Deserialize)] +pub struct NewUserForm { + email: String, + name: String, + password: String, + role: u8, + function: u8, + area: Option +} + +#[actix_web::post("/users/new")] +pub async fn post_new(user: Identity, pool: web::Data, form: web::Form) -> impl Responder { + let current_user = User::read_by_id(pool.get_ref(), user.id().unwrap().parse().unwrap()).await.unwrap(); + let mut area_id = current_user.area_id; + + if current_user.role == Role::Admin { + if let Some(id) = form.area { + area_id = id; + } + } + + if let Ok((hash, salt)) = generate_salt_and_hash_plain_password(&form.password) { + if let Ok(role) = Role::try_from(form.role) { + if let Ok(function) = Function::try_from(form.function) { + match User::create(pool.get_ref(), &form.name, &form.email, &hash, &salt, role, function, area_id).await { + Ok(_) => return HttpResponse::Found().insert_header((LOCATION, "/users")).finish(), + Err(err) => println!("{}", err) + } + } + } + } + + return HttpResponse::BadRequest().body("Fehler beim Erstellen des Nutzers"); +} diff --git a/src/endpoints/user/put_edit.rs b/src/endpoints/user/put_edit.rs new file mode 100644 index 00000000..e69de29b diff --git a/src/models/area.rs b/src/models/area.rs index 928f1eb1..b4bb06d3 100644 --- a/src/models/area.rs +++ b/src/models/area.rs @@ -1,5 +1,6 @@ use sqlx::{query, query_as, PgPool}; +#[derive(Clone)] pub struct Area { pub id: i32, pub name: String diff --git a/src/models/availabillity.rs b/src/models/availabillity.rs index cb73b915..abefc85b 100644 --- a/src/models/availabillity.rs +++ b/src/models/availabillity.rs @@ -102,6 +102,7 @@ impl Availabillity { role: r.role.clone(), function: r.function.clone(), area_id: r.areaid, + area: None, locked: r.locked, last_login: r.lastlogin, receive_notifications: r.receivenotifications, diff --git a/src/models/user.rs b/src/models/user.rs index bfad72c6..96c2f605 100644 --- a/src/models/user.rs +++ b/src/models/user.rs @@ -1,7 +1,7 @@ use chrono::{DateTime, Utc}; use sqlx::PgPool; -use super::{Function, Role}; +use super::{Area, Function, Role}; #[derive(Clone)] pub struct User { @@ -13,6 +13,7 @@ pub struct User { pub role: Role, pub function: Function, pub area_id: i32, + pub area: Option, pub locked: bool, pub last_login: Option>, pub receive_notifications: bool, @@ -21,10 +22,10 @@ pub struct User { impl User { pub async fn create( pool: &PgPool, - name: String, - email: String, - password: String, - salt: String, + name: &str, + email: &str, + password: &str, + salt: &str, role: Role, function: Function, area_id: i32, @@ -83,6 +84,7 @@ impl User { role: record.role, function: record.function, area_id: record.areaid, + area: None, locked: record.locked, last_login: record.lastlogin, receive_notifications: record.receivenotifications, @@ -123,6 +125,7 @@ impl User { role: record.role, function: record.function, area_id: record.areaid, + area: None, locked: record.locked, last_login: record.lastlogin, receive_notifications: record.receivenotifications, @@ -164,6 +167,7 @@ impl User { role: record.role.clone(), function: record.function.clone(), area_id: record.areaid, + area: None, locked: record.locked, last_login: record.lastlogin, receive_notifications: record.receivenotifications, @@ -173,6 +177,54 @@ impl User { Ok(result) } + pub async fn read_all_including_area(pool: &PgPool) -> anyhow::Result> { + let records = sqlx::query!( + r#" + SELECT + user_.id AS userId, + user_.name, + user_.email, + user_.password, + user_.salt, + user_.role AS "role: Role", + user_.function AS "function: Function", + user_.areaId, + user_.locked, + user_.lastLogin, + user_.receiveNotifications, + area.id, + area.name AS areaName + FROM user_ + JOIN area ON user_.areaId = area.id + "# + ) + .fetch_all(pool) + .await?; + + let results = records + .iter() + .map(|record| User { + id: record.userid, + name: record.name.clone(), + email: record.email.clone(), + password: record.password.clone(), + salt: record.salt.clone(), + role: record.role.clone(), + function: record.function.clone(), + area_id: record.areaid, + area: Some(Area { + id: record.areaid, + name: record.areaname.clone(), + }), + locked: record.locked, + last_login: record.lastlogin, + receive_notifications: record.receivenotifications, + }) + .collect(); + + Ok(results) + } + pub async fn read_all_by_area(pool: &PgPool, area_id: i32) -> anyhow::Result> { let records = sqlx::query!( r#" @@ -206,6 +258,7 @@ impl User { role: record.role.clone(), function: record.function.clone(), area_id: record.areaid, + area: None, locked: record.locked, last_login: record.lastlogin, receive_notifications: record.receivenotifications, @@ -215,11 +268,11 @@ impl User { Ok(result) } - pub async fn update(pool: &PgPool, id: i32, updated_user: User) -> Option { - todo!() - } + // pub async fn update(pool: &PgPool, id: i32, updated_user: User) -> Option { + // todo!() + // } - pub async fn delete(pool: &PgPool, id: i32) -> anyhow::Result { - todo!() - } + // pub async fn delete(pool: &PgPool, id: i32) -> anyhow::Result { + // todo!() + // } } diff --git a/templates/user/edit.html b/templates/user/edit.html new file mode 100644 index 00000000..99845d24 --- /dev/null +++ b/templates/user/edit.html @@ -0,0 +1,126 @@ +{% extends "nav.html" %} + +{% block content %} +
+
+
+

Nutzer {{ name }} bearbeiten

+ +
+
+ +
+
+
+
+ +
+
+
+
+ +
+
+ +
+
+
+
+ +
+
+
+
+ +
+
+ +
+
+
+
+ +
+
+
+
+ +
+
+ +
+
+
+
+
+ +
+
+
+
+
+ +
+
+ +
+
+
+
+
+ +
+
+
+
+
+ + {% if user.role == Role::Admin %} +
+
+ +
+
+
+
+
+ +
+
+
+
+
+ {% endif %} + +
+
+
+
+
+ +
+
+ Zurück +
+
+
+
+ +
+
+
+ + +{% endblock %} diff --git a/templates/user/new.html b/templates/user/new.html new file mode 100644 index 00000000..5dd6b5b7 --- /dev/null +++ b/templates/user/new.html @@ -0,0 +1,126 @@ +{% extends "nav.html" %} + +{% block content %} +
+
+
+

Neuen Nutzer anlegen

+ +
+
+ +
+
+
+
+ +
+
+
+
+ +
+
+ +
+
+
+
+ +
+
+
+
+ +
+
+ +
+
+
+
+ +
+
+
+
+ +
+
+ +
+
+
+
+
+ +
+
+
+
+
+ +
+
+ +
+
+
+
+
+ +
+
+
+
+
+ + {% if user.role == Role::Admin %} +
+
+ +
+
+
+
+
+ +
+
+
+
+
+ {% endif %} + +
+
+
+
+
+ +
+
+ Zurück +
+
+
+
+ +
+
+
+ + +{% endblock %} diff --git a/templates/user/overview.html b/templates/user/overview.html index a2c1d9ec..43eb7c24 100644 --- a/templates/user/overview.html +++ b/templates/user/overview.html @@ -6,7 +6,7 @@

- Nutzer + Nutzer {% if user.role == Role::AreaManager %}für Bereich {{ area.as_ref().unwrap().name }}{% endif %}

@@ -16,14 +16,68 @@ {% if users.len() == 0 %}
-
keine Orte vorhanden
+
keine Nutzer vorhanden
{% else %} - {% for u in users %}
-
{{ u.email }}
+ + + + + + + + {% if user.role == Role::Admin %} + + {% endif %} + + + + + {% for u in users %} + + + + + + {% if user.role == Role::Admin %} + + {% endif %} + + + {% endfor %} + +
E-MailNameRolleFunktionBereich
+ {{ u.email }} + + {{ u.name }} + + {% match u.role %} + {% when Role::Staff %} + Nutzer + {% when Role::AreaManager %} + Bereichsleiter + {% when Role::Admin %} + Admin + {% else %} + {% endmatch %} + + {% match u.function %} + {% when Function::Posten %} + Posten + {% when Function::Wachhabender %} + Wachhabender + {% else %} + {% endmatch %} + + {{ u.area.as_ref().unwrap().name }} + +
+ Bearbeiten + +
+
- {% endfor %} {% endif %}