From 08ae8e6443db55a2816571ab2438c6b27025cd64 Mon Sep 17 00:00:00 2001 From: Artemis Date: Sat, 25 Jan 2025 18:40:26 +0100 Subject: [PATCH] full account deletion done --- README.md | 1 - assets/site.css | 12 +++-- src/db/doll.rs | 31 +++++++++++-- src/db/schema.rs | 3 +- src/db/user.rs | 10 +++- src/main.rs | 2 + src/routes/account.rs | 31 +++++++++++-- templates/account/index.html.tera | 2 +- templates/account/settings.html.tera | 67 +++++++++++++++++++++++++++ templates/account/terminate.html.tera | 19 ++++++++ templates/tag/delete.html.tera | 2 +- 11 files changed, 165 insertions(+), 15 deletions(-) create mode 100644 templates/account/settings.html.tera create mode 100644 templates/account/terminate.html.tera diff --git a/README.md b/README.md index 0aee50e..2f297c4 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,5 @@ a profile is - p2: saving register form as it gets filled / re-display it with partial values - account - p2: optional email for forgotten password i guess - - ability to delete a tag (remove all data except the ID to lock the ID) - ability to delete the whole account (incl. all registered tags) - deleting a tag keeping the account allows to re-use the tag later on. no one else can use it still diff --git a/assets/site.css b/assets/site.css index 875e4c6..20af914 100644 --- a/assets/site.css +++ b/assets/site.css @@ -106,6 +106,7 @@ input:hover, select:hover, button:hover, textarea:hover, +button:hover, .btn:hover { border-color: var(--clr-primary-a0); } @@ -138,7 +139,8 @@ section { } p.form-error, -div.form-error { +div.form-error, +a.error { background-color: var(--clr-error-surface); border: 2pt solid var(--clr-error-primary-0); color: var(--clr-error-primary-40); @@ -214,12 +216,16 @@ p.heading { border-radius: 4pt; } -.fields>*:not(:last-child) { +.fields:not(:last-of-type) { + margin-bottom: 1em; +} + +.fields *:not(:last-child) { margin-bottom: 1em; /* margin-top: 1em; */ } -.fields>* * { +.fields * * { display: block; width: 100% } diff --git a/src/db/doll.rs b/src/db/doll.rs index 26e3daf..2a28470 100644 --- a/src/db/doll.rs +++ b/src/db/doll.rs @@ -1,7 +1,7 @@ use crate::db::schema::DollProfile; -use uuid::Uuid; +use uuid::{uuid, Uuid}; -use super::schema::{CreateDollProfile, DbHook}; +use super::schema::{CreateDollProfile, DbHook, TrxHook}; pub async fn list(db: &mut DbHook, from: &Uuid) -> sqlx::Result> { sqlx::query_as!( @@ -95,7 +95,7 @@ pub async fn create(db: &mut DbHook, doll: CreateDollProfile<'_>) -> sqlx::Resul /// the account holder to "re-create" one with this ID. /// /// A period of time after which deleted accounts will have their IDs freed is to be set. -pub async fn delete(db: &mut DbHook, id: i32) -> sqlx::Result<()> { +pub async fn delete(trx: &mut TrxHook<'_>, id: i32) -> sqlx::Result<()> { sqlx::query!( r#" update doll_profiles @@ -119,8 +119,31 @@ pub async fn delete(db: &mut DbHook, id: i32) -> sqlx::Result<()> { "#, id ) - .execute(&mut **db) + .execute(&mut **trx) .await?; Ok(()) } + +pub async fn delete_all_from_account(trx: &mut TrxHook<'_>, from: &Uuid) -> sqlx::Result<()> { + let null_uuid = uuid!("00000000-0000-0000-0000-000000000000"); + + // 1. archive all tags from the account + let tags = sqlx::query_as!( + DollProfile, + "select * from doll_profiles where bound_to_id = $1 and archived_at is null", + from + ) + .fetch_all(&mut **trx) + .await?; + for tag in tags { + delete(trx, tag.id).await?; + } + + // 2. unlink archived tags from the account + sqlx::query!("update doll_profiles set bound_to_id = $1 where archived_at is not null and bound_to_id = $2", null_uuid, from) + .execute(&mut **trx) + .await?; + + Ok(()) +} diff --git a/src/db/schema.rs b/src/db/schema.rs index 7301069..363ee5b 100644 --- a/src/db/schema.rs +++ b/src/db/schema.rs @@ -4,7 +4,7 @@ use rocket_db_pools::{Connection, Database}; use sqlx::{ pool::PoolConnection, types::{chrono, Uuid}, - Postgres, + Postgres, Transaction, }; #[derive(Database)] @@ -12,6 +12,7 @@ use sqlx::{ pub struct DollTags(sqlx::PgPool); pub type DollTagsDb = Connection; pub type DbHook = PoolConnection; +pub type TrxHook<'a> = Transaction<'a, Postgres>; // Doll Profiles stuff #[derive(Debug, Serialize)] diff --git a/src/db/user.rs b/src/db/user.rs index e884f70..3830646 100644 --- a/src/db/user.rs +++ b/src/db/user.rs @@ -2,7 +2,7 @@ use uuid::Uuid; use crate::db::schema::User; -use super::schema::DollTagsDb; +use super::schema::{DollTagsDb, TrxHook}; pub async fn get(mut db: DollTagsDb, username: &str) -> sqlx::Result> { sqlx::query_as!(User, "select * from users where username = $1", username) @@ -31,3 +31,11 @@ pub async fn create( .fetch_one(&mut **db) .await } + +pub async fn delete(trx: &mut TrxHook<'_>, id: &Uuid) -> sqlx::Result<()> { + sqlx::query!("delete from users where id = $1", id) + .execute(&mut **trx) + .await?; + + Ok(()) +} diff --git a/src/main.rs b/src/main.rs index 409f12a..3c4f515 100644 --- a/src/main.rs +++ b/src/main.rs @@ -36,6 +36,8 @@ fn rocket() -> _ { form::register_tag::handle_register, account::ask_delete, account::confirm_delete, + account::ask_terminate_account, + account::confirm_terminate_account, ], ) .mount( diff --git a/src/routes/account.rs b/src/routes/account.rs index 41aa69b..c51dc97 100644 --- a/src/routes/account.rs +++ b/src/routes/account.rs @@ -1,10 +1,13 @@ -use rocket::response::Redirect; +use rocket::{http::CookieJar, response::Redirect}; use rocket_dyn_templates::{context, Template}; +use sqlx::Acquire; use crate::{ + auth::session, db::{ doll, schema::{DollTagsDb, User}, + user, }, pages::CommonTemplateState, }; @@ -30,7 +33,7 @@ pub async fn index(mut db: DollTagsDb, user: User, meta: CommonTemplateState) -> #[get("/settings")] pub fn show_settings(user: User, meta: CommonTemplateState) -> Template { - todo!("woof"); + Template::render("account/settings", context! {user,meta}) } #[get("/delete/")] @@ -58,6 +61,28 @@ pub async fn ask_delete( #[get("/yes_delete_this/")] pub async fn confirm_delete(mut db: DollTagsDb, id: i32, _user: User) -> PageResult { - doll::delete(&mut *db, id).await?; + let mut trx = db.begin().await?; + doll::delete(&mut trx, id).await?; + trx.commit().await?; Ok(Redirect::to(uri!("/account", index)).into()) } + +#[get("/terminate")] +pub fn ask_terminate_account(_user: User, meta: CommonTemplateState) -> Template { + Template::render("account/terminate", context! {meta}) +} + +#[get("/termin@or")] +pub async fn confirm_terminate_account( + mut db: DollTagsDb, + user: User, + cookies: &CookieJar<'_>, +) -> PageResult { + let mut trx = db.begin().await?; + doll::delete_all_from_account(&mut trx, &user.id).await?; + user::delete(&mut trx, &user.id).await?; + session::logout(cookies); + trx.commit().await?; + + Ok(Redirect::to("/").into()) +} diff --git a/templates/account/index.html.tera b/templates/account/index.html.tera index 23d9858..714854f 100644 --- a/templates/account/index.html.tera +++ b/templates/account/index.html.tera @@ -1,5 +1,5 @@ {% extends "base" %} -{% block title %}Log in - {% endblock title %} +{% block title %}Your account - {% endblock title %} {% block main %}