feat: locking and deletion of user
This commit is contained in:
parent
39a28038ca
commit
3d3e92cf38
39
Cargo.lock
generated
39
Cargo.lock
generated
@ -395,7 +395,7 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"serde",
|
"serde",
|
||||||
"syn 2.0.18",
|
"syn 2.0.52",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -532,7 +532,7 @@ checksum = "7b2d0f03b3640e3a630367e40c468cb7f309529c708ed1d88597047b0e7c6ef7"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.18",
|
"syn 2.0.52",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -650,6 +650,7 @@ dependencies = [
|
|||||||
"dotenv",
|
"dotenv",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"serde",
|
"serde",
|
||||||
|
"serde_json",
|
||||||
"sqlx",
|
"sqlx",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -1040,7 +1041,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.18",
|
"syn 2.0.52",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1670,18 +1671,18 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.60"
|
version = "1.0.78"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dec2b086b7a862cf4de201096214fa870344cf922b2b30c167badb3af3195406"
|
checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.28"
|
version = "1.0.35"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488"
|
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
@ -1860,29 +1861,29 @@ checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.164"
|
version = "1.0.197"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9e8c8cf938e98f769bc164923b06dce91cea1751522f46f8466461af04c9027d"
|
checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.164"
|
version = "1.0.197"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d9735b638ccc51c28bf6914d90a2e9725b377144fc612c49a611fddd1b631d68"
|
checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.18",
|
"syn 2.0.52",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.96"
|
version = "1.0.114"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1"
|
checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa",
|
"itoa",
|
||||||
"ryu",
|
"ryu",
|
||||||
@ -2105,9 +2106,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.18"
|
version = "2.0.52"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e"
|
checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -2131,7 +2132,7 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.18",
|
"syn 2.0.52",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2354,7 +2355,7 @@ dependencies = [
|
|||||||
"once_cell",
|
"once_cell",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.18",
|
"syn 2.0.52",
|
||||||
"wasm-bindgen-shared",
|
"wasm-bindgen-shared",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -2388,7 +2389,7 @@ checksum = "e128beba882dd1eb6200e1dc92ae6c5dbaa4311aa7bb211ca035779e5efc39f8"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.18",
|
"syn 2.0.52",
|
||||||
"wasm-bindgen-backend",
|
"wasm-bindgen-backend",
|
||||||
"wasm-bindgen-shared",
|
"wasm-bindgen-shared",
|
||||||
]
|
]
|
||||||
|
@ -19,3 +19,4 @@ chrono = { version = "0.4.33", features = ["serde"] }
|
|||||||
actix-files = "0.6.5"
|
actix-files = "0.6.5"
|
||||||
askama_actix = "0.14.0"
|
askama_actix = "0.14.0"
|
||||||
futures-util = "0.3.30"
|
futures-util = "0.3.30"
|
||||||
|
serde_json = "1.0.114"
|
||||||
|
@ -17,4 +17,6 @@ pub fn init(cfg: &mut ServiceConfig) {
|
|||||||
cfg.service(user::post_new::post_new);
|
cfg.service(user::post_new::post_new);
|
||||||
cfg.service(user::get_edit::get_edit);
|
cfg.service(user::get_edit::get_edit);
|
||||||
cfg.service(user::post_edit::post_edit);
|
cfg.service(user::post_edit::post_edit);
|
||||||
|
cfg.service(user::patch::patch);
|
||||||
|
cfg.service(user::delete::delete);
|
||||||
}
|
}
|
||||||
|
30
src/endpoints/user/delete.rs
Normal file
30
src/endpoints/user/delete.rs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
use actix_identity::Identity;
|
||||||
|
use actix_web::{web, HttpResponse, Responder};
|
||||||
|
use sqlx::PgPool;
|
||||||
|
|
||||||
|
use crate::{endpoints::IdPath, models::{Role, User}};
|
||||||
|
|
||||||
|
#[actix_web::delete("/users/delete/{id}")]
|
||||||
|
pub async fn delete(user: Identity, pool: web::Data<PgPool>, path: web::Path<IdPath>) -> 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 {
|
||||||
|
return HttpResponse::Unauthorized().finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(user_in_db) = User::read_by_id(pool.get_ref(), path.id).await {
|
||||||
|
if current_user.role == Role::AreaManager && current_user.area_id != user_in_db.area_id {
|
||||||
|
return HttpResponse::Unauthorized().finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
if user_in_db.locked {
|
||||||
|
if let Ok(_) = User::delete(pool.get_ref(), user_in_db.id).await {
|
||||||
|
return HttpResponse::NoContent().finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpResponse::BadRequest().finish()
|
||||||
|
}
|
@ -3,3 +3,5 @@ pub mod get_new;
|
|||||||
pub mod post_new;
|
pub mod post_new;
|
||||||
pub mod get_edit;
|
pub mod get_edit;
|
||||||
pub mod post_edit;
|
pub mod post_edit;
|
||||||
|
pub mod patch;
|
||||||
|
pub mod delete;
|
||||||
|
69
src/endpoints/user/patch.rs
Normal file
69
src/endpoints/user/patch.rs
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
use actix_identity::Identity;
|
||||||
|
use actix_web::{web, HttpResponse, Responder};
|
||||||
|
use serde::Deserialize;
|
||||||
|
use serde_json::value::Value;
|
||||||
|
use sqlx::PgPool;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
endpoints::IdPath,
|
||||||
|
models::{Role, User},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct JsonPatchDoc {
|
||||||
|
op: String,
|
||||||
|
path: String,
|
||||||
|
value: Value
|
||||||
|
}
|
||||||
|
|
||||||
|
#[actix_web::patch("/users/edit/{id}")]
|
||||||
|
pub async fn patch(
|
||||||
|
user: Identity,
|
||||||
|
pool: web::Data<PgPool>,
|
||||||
|
path: web::Path<IdPath>,
|
||||||
|
patch_docs: web::Json<Vec<JsonPatchDoc>>,
|
||||||
|
) -> 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 {
|
||||||
|
return HttpResponse::Unauthorized().finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(user_in_db) = User::read_by_id(pool.get_ref(), path.id).await {
|
||||||
|
if current_user.role == Role::AreaManager && current_user.area_id != user_in_db.area_id {
|
||||||
|
return HttpResponse::Unauthorized().finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut changed = false;
|
||||||
|
|
||||||
|
let mut locked: Option<bool> = None;
|
||||||
|
|
||||||
|
for doc in patch_docs.iter() {
|
||||||
|
if doc.op.as_str() != "replace" {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
match doc.path.as_str() {
|
||||||
|
"/locked" => {
|
||||||
|
changed = true;
|
||||||
|
if let Value::Bool(b) = doc.value {
|
||||||
|
locked = Some(b);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => panic!("other patch paths are not supported!"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if changed {
|
||||||
|
if let Ok(_) = User::update(pool.get_ref(), path.id, None, None, None, None, None, locked).await {
|
||||||
|
return HttpResponse::Ok().body("");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return HttpResponse::Ok().body("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpResponse::BadRequest().body("Fehler bei User PATCH")
|
||||||
|
}
|
@ -33,6 +33,10 @@ pub async fn post_edit(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(user_in_db) = User::read_by_id(pool.get_ref(), path.id).await {
|
if let Ok(user_in_db) = User::read_by_id(pool.get_ref(), path.id).await {
|
||||||
|
if current_user.role == Role::AreaManager && current_user.area_id != user_in_db.area_id {
|
||||||
|
return HttpResponse::Unauthorized().finish();
|
||||||
|
}
|
||||||
|
|
||||||
let mut changed = false;
|
let mut changed = false;
|
||||||
|
|
||||||
let email = if user_in_db.email != form.email {
|
let email = if user_in_db.email != form.email {
|
||||||
@ -79,7 +83,7 @@ pub async fn post_edit(
|
|||||||
};
|
};
|
||||||
|
|
||||||
if changed {
|
if changed {
|
||||||
match User::update(pool.get_ref(), path.id, email, name, role, function, area).await {
|
match User::update(pool.get_ref(), path.id, email, name, role, function, area, None).await {
|
||||||
Ok(_) => return HttpResponse::Found().insert_header((LOCATION, "/users")).finish(),
|
Ok(_) => return HttpResponse::Found().insert_header((LOCATION, "/users")).finish(),
|
||||||
Err(err) => println!("{}", err)
|
Err(err) => println!("{}", err)
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,7 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
.app_data(web::Data::new(pool.clone()))
|
.app_data(web::Data::new(pool.clone()))
|
||||||
.configure(auth::init)
|
.configure(auth::init)
|
||||||
.configure(calendar::init)
|
.configure(calendar::init)
|
||||||
|
.configure(endpoints::init)
|
||||||
.wrap(redirect::CheckLogin)
|
.wrap(redirect::CheckLogin)
|
||||||
.wrap(IdentityMiddleware::default())
|
.wrap(IdentityMiddleware::default())
|
||||||
.wrap(SessionMiddleware::new(
|
.wrap(SessionMiddleware::new(
|
||||||
|
@ -276,6 +276,7 @@ impl User {
|
|||||||
role: Option<Role>,
|
role: Option<Role>,
|
||||||
function: Option<Function>,
|
function: Option<Function>,
|
||||||
area_id: Option<i32>,
|
area_id: Option<i32>,
|
||||||
|
locked: Option<bool>
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
let mut query_builder = sqlx::QueryBuilder::new("UPDATE user_ SET ");
|
let mut query_builder = sqlx::QueryBuilder::new("UPDATE user_ SET ");
|
||||||
let mut separated = query_builder.separated(", ");
|
let mut separated = query_builder.separated(", ");
|
||||||
@ -305,6 +306,11 @@ impl User {
|
|||||||
separated.push_bind_unseparated(area_id);
|
separated.push_bind_unseparated(area_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(locked) = locked {
|
||||||
|
separated.push("locked = ");
|
||||||
|
separated.push_bind_unseparated(locked);
|
||||||
|
}
|
||||||
|
|
||||||
query_builder.push(" WHERE id = ");
|
query_builder.push(" WHERE id = ");
|
||||||
query_builder.push_bind(id);
|
query_builder.push_bind(id);
|
||||||
query_builder.push(";");
|
query_builder.push(";");
|
||||||
@ -316,7 +322,10 @@ impl User {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
// pub async fn delete(pool: &PgPool, id: i32) -> anyhow::Result<bool> {
|
pub async fn delete(pool: &PgPool, id: i32) -> anyhow::Result<()> {
|
||||||
// todo!()
|
sqlx::query!("DELETE FROM user_ WHERE id = $1", id)
|
||||||
// }
|
.execute(pool)
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
<section class="section">
|
<section class="section">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<form method="post" action="/users/edit/{{ id }}">
|
<form method="post" action="/users/edit/{{ id }}">
|
||||||
<h1 class="title">Nutzer {{ name }} bearbeiten</h1>
|
<h1 class="title">Nutzer '{{ name }}' bearbeiten</h1>
|
||||||
|
|
||||||
<div class="field is-horizontal">
|
<div class="field is-horizontal">
|
||||||
<div class="field-label">
|
<div class="field-label">
|
||||||
|
@ -37,7 +37,7 @@
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for u in users %}
|
{% for u in users %}
|
||||||
<tr>
|
<tr id="user-{{ u.id }}">
|
||||||
<td>
|
<td>
|
||||||
{{ u.email }}
|
{{ u.email }}
|
||||||
</td>
|
</td>
|
||||||
@ -85,8 +85,9 @@
|
|||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="buttons is-right">
|
<div class="buttons is-right">
|
||||||
|
<button class="button is-link is-light" name="toggle-lock-user">{% if u.locked %}Entsperren{% else %}Sperren{% endif %}</button>
|
||||||
<a class="button is-link" href="/users/edit/{{ u.id }}">Bearbeiten</a>
|
<a class="button is-link" href="/users/edit/{{ u.id }}">Bearbeiten</a>
|
||||||
<button class="button is-danger" name="delete-availabillity">Löschen</button>
|
<button class="button is-danger" {% if !u.locked %}disabled{% endif %} name="delete-user">Löschen</button>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@ -100,5 +101,59 @@
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
document.getElementsByName("delete-user")
|
||||||
|
.forEach(ele => ele.addEventListener("click", (event) => {
|
||||||
|
const id = event.target.closest("tr").id.split('-')[1];
|
||||||
|
event.target.classList.add("is-loading");
|
||||||
|
|
||||||
|
fetch(`/users/delete/${id}`, { method: "DELETE"})
|
||||||
|
.then(response => {
|
||||||
|
if (response.status == 204) {
|
||||||
|
document.getElementById(`user-${id}`).remove()
|
||||||
|
} else {
|
||||||
|
event.target.classList.remove("is-loading");
|
||||||
|
console.log("Fehler beim Löschen.")
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
document.getElementsByName("toggle-lock-user")
|
||||||
|
.forEach(ele => ele.addEventListener("click", (event) => {
|
||||||
|
const id = event.target.closest("tr").id.split('-')[1];
|
||||||
|
event.target.classList.add("is-loading");
|
||||||
|
|
||||||
|
const locked = event.target.innerHTML == "Sperren" ? false : true;
|
||||||
|
|
||||||
|
fetch(`/users/edit/${id}`, {
|
||||||
|
method: "PATCH",
|
||||||
|
headers: new Headers({
|
||||||
|
"Content-Type": "application/json-patch+json",
|
||||||
|
}),
|
||||||
|
body: JSON.stringify([
|
||||||
|
{
|
||||||
|
"op": "replace",
|
||||||
|
"path": "/locked",
|
||||||
|
"value": !locked
|
||||||
|
}
|
||||||
|
])
|
||||||
|
})
|
||||||
|
.then(response => {
|
||||||
|
if (response.status == 200) {
|
||||||
|
event.target.classList.remove("is-loading");
|
||||||
|
if (locked) {
|
||||||
|
event.target.innerHTML = "Sperren";
|
||||||
|
event.target.closest("tr").querySelector("td:nth-last-child(2)").innerHTML = "nein"
|
||||||
|
event.target.closest("div.buttons").querySelector("button:nth-last-child(1)").setAttribute("disabled", "");
|
||||||
|
} else {
|
||||||
|
event.target.innerHTML = "Entsperren";
|
||||||
|
event.target.closest("tr").querySelector("td:nth-last-child(2)").innerHTML = "ja"
|
||||||
|
event.target.closest("div.buttons").querySelector("button:nth-last-child(1)").removeAttribute("disabled");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
event.target.classList.remove("is-loading");
|
||||||
|
console.log("Fehler beim PATCH.")
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}));
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user