full account deletion done
This commit is contained in:
parent
3f9ac03c92
commit
08ae8e6443
11 changed files with 165 additions and 15 deletions
|
@ -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
|
||||
|
|
|
@ -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%
|
||||
}
|
||||
|
|
|
@ -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<Vec<DollProfile>> {
|
||||
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(())
|
||||
}
|
||||
|
|
|
@ -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<DollTags>;
|
||||
pub type DbHook = PoolConnection<Postgres>;
|
||||
pub type TrxHook<'a> = Transaction<'a, Postgres>;
|
||||
|
||||
// Doll Profiles stuff
|
||||
#[derive(Debug, Serialize)]
|
||||
|
|
|
@ -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<Option<User>> {
|
||||
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(())
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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/<id>")]
|
||||
|
@ -58,6 +61,28 @@ pub async fn ask_delete(
|
|||
|
||||
#[get("/yes_delete_this/<id>")]
|
||||
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())
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{% extends "base" %}
|
||||
{% block title %}Log in - {% endblock title %}
|
||||
{% block title %}Your account - {% endblock title %}
|
||||
{% block main %}
|
||||
<aside>
|
||||
<p class="subnav">
|
||||
|
|
67
templates/account/settings.html.tera
Normal file
67
templates/account/settings.html.tera
Normal file
|
@ -0,0 +1,67 @@
|
|||
{% extends "base" %}
|
||||
{% block title %}Settings - {% endblock title %}
|
||||
{% block main %}
|
||||
<h2>Your settings</h2>
|
||||
|
||||
<div class="fields raised">
|
||||
<form method="post" class="">
|
||||
<input type="hidden" name="form" value="info">
|
||||
<div>
|
||||
<p class="heading"><label for="username">Username</label></p>
|
||||
<input type="text" id="username" name="username" maxlength="256" placeholder="{{user.username}}">
|
||||
<p class="note">
|
||||
Reminder: your username is your login ID.
|
||||
If you change it, don't forget to use the new one to log in.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p class="heading"><label for="email">Recovery email</label></p>
|
||||
<input type="email" id="email" name="email" maxlength="254" placeholder="{{user.email}}">
|
||||
</div>
|
||||
|
||||
<button type="submit">Save those changes</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="fields raised">
|
||||
<form method="post" class="">
|
||||
<input type="hidden" name="form" value="password">
|
||||
<div>
|
||||
<p class="heading"><label for="old_password">Current password</label></p>
|
||||
<input type="password" id="old_password" name="old_password" minlength="8">
|
||||
</div>
|
||||
<div>
|
||||
<p class="heading"><label for="password">New password</label></p>
|
||||
<input type="password" id="password" name="password" minlength="8">
|
||||
</div>
|
||||
<div>
|
||||
<p class="heading"><label for="confirm_password">Confirm the password</label></p>
|
||||
<input type="password" id="confirm_password" name="confirm_password" minlength="8">
|
||||
</div>
|
||||
|
||||
<button type="submit">Change your password</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<section>
|
||||
<h3>Exporting your data</h3>
|
||||
|
||||
<p>You can export all your account's data using this button.</p>
|
||||
<p>It will be provided in a JSON file whose structure is documented.</p>
|
||||
<!-- TODO: Link structure documentation. -->
|
||||
<a href="/account/export" class="btn">Export all my data</a>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3>Deleting your account</h3>
|
||||
|
||||
<p>You can delete your account by clicking on the button below.</p>
|
||||
<p>
|
||||
Deleting your account will also delete all tags registered to you
|
||||
while locking them out of registration for anyone else for a certain time period.
|
||||
</p>
|
||||
<p>This is not a recoverable operation.</p>
|
||||
<a href="/account/terminate" class="error btn">Delete my account</a>
|
||||
</section>
|
||||
{% endblock main %}
|
19
templates/account/terminate.html.tera
Normal file
19
templates/account/terminate.html.tera
Normal file
|
@ -0,0 +1,19 @@
|
|||
{% extends "base" %}
|
||||
{% block title %}Terminate your account - {% endblock title %}
|
||||
{% block main %}
|
||||
<h2>Are you sure to delete your entire account?</h2>
|
||||
|
||||
<p>You are about to delete your entire account.</p>
|
||||
<p>
|
||||
Doing so will irreversibly remove all data,
|
||||
and the IDs of all the tags you had linked to your account will be locked
|
||||
for a certain time to avoid issues.
|
||||
</p>
|
||||
<p>This is not reversible.</p>
|
||||
|
||||
<p class="subnav">
|
||||
<a href="/account" class="btn">No, take me back</a>
|
||||
<a href="/account/termin@or" class="btn error">Yes, delete my account and associated tags</a>
|
||||
</p>
|
||||
|
||||
{% endblock main %}
|
|
@ -1,5 +1,5 @@
|
|||
{% extends "base" %}
|
||||
{% block title %}Log in - {% endblock title %}
|
||||
{% block title %}Delete {{tag.id|pretty_id}} - {% endblock title %}
|
||||
{% block main %}
|
||||
<h2>Are you sure to delete it?</h2>
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue