added archival
This commit is contained in:
parent
1a152e7acb
commit
3f9ac03c92
10 changed files with 137 additions and 22 deletions
2
migrations/4_tag_archive.sql
Normal file
2
migrations/4_tag_archive.sql
Normal file
|
@ -0,0 +1,2 @@
|
|||
alter table doll_profiles
|
||||
add column archived_at timestamptz;
|
|
@ -1,13 +1,21 @@
|
|||
use crate::db::schema::DollProfile;
|
||||
use sqlx::{pool::PoolConnection, Postgres};
|
||||
use uuid::Uuid;
|
||||
|
||||
use super::schema::{CreateDollProfile, DollTagsDb};
|
||||
use super::schema::{CreateDollProfile, DbHook};
|
||||
|
||||
pub async fn list(mut db: DollTagsDb, from: &Uuid) -> sqlx::Result<Vec<DollProfile>> {
|
||||
pub async fn list(db: &mut DbHook, from: &Uuid) -> sqlx::Result<Vec<DollProfile>> {
|
||||
sqlx::query_as!(
|
||||
DollProfile,
|
||||
"select * from doll_profiles where bound_to_id = $1",
|
||||
"select * from doll_profiles where bound_to_id = $1 and archived_at is null",
|
||||
from
|
||||
)
|
||||
.fetch_all(&mut **db)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn list_archived(db: &mut DbHook, from: &Uuid) -> sqlx::Result<Vec<i32>> {
|
||||
sqlx::query_scalar!(
|
||||
"select id from doll_profiles where bound_to_id = $1 and archived_at is not null",
|
||||
from
|
||||
)
|
||||
.fetch_all(&mut **db)
|
||||
|
@ -15,14 +23,14 @@ pub async fn list(mut db: DollTagsDb, from: &Uuid) -> sqlx::Result<Vec<DollProfi
|
|||
}
|
||||
|
||||
pub async fn get(
|
||||
mut db: DollTagsDb,
|
||||
db: &mut DbHook,
|
||||
ident: i32,
|
||||
microchip_id: &str,
|
||||
) -> sqlx::Result<Option<DollProfile>> {
|
||||
sqlx::query_as!(
|
||||
DollProfile,
|
||||
r#"
|
||||
select * from doll_profiles where id = $1 or microchip_id = $2
|
||||
select * from doll_profiles where (id = $1 or microchip_id = $2) and archived_at is null
|
||||
"#,
|
||||
ident,
|
||||
microchip_id
|
||||
|
@ -31,7 +39,7 @@ pub async fn get(
|
|||
.await
|
||||
}
|
||||
|
||||
pub async fn check_ids(mut db: DollTagsDb, idents: &Vec<i32>) -> sqlx::Result<Vec<i32>> {
|
||||
pub async fn check_ids(db: &mut DbHook, idents: &Vec<i32>) -> sqlx::Result<Vec<i32>> {
|
||||
sqlx::query_scalar!(
|
||||
"select id from doll_profiles where id in (select * from unnest($1::int[]))",
|
||||
idents
|
||||
|
@ -40,11 +48,7 @@ pub async fn check_ids(mut db: DollTagsDb, idents: &Vec<i32>) -> sqlx::Result<Ve
|
|||
.await
|
||||
}
|
||||
|
||||
pub async fn id_exists(
|
||||
db: &mut PoolConnection<Postgres>,
|
||||
ident: i32,
|
||||
microchip_id: &str,
|
||||
) -> sqlx::Result<bool> {
|
||||
pub async fn id_exists(db: &mut DbHook, ident: i32, microchip_id: &str) -> sqlx::Result<bool> {
|
||||
Ok(sqlx::query!(
|
||||
"select id from doll_profiles where id = $1 or microchip_id = $2",
|
||||
ident,
|
||||
|
@ -55,7 +59,7 @@ pub async fn id_exists(
|
|||
.is_some())
|
||||
}
|
||||
|
||||
pub async fn create(mut db: DollTagsDb, doll: CreateDollProfile<'_>) -> sqlx::Result<()> {
|
||||
pub async fn create(db: &mut DbHook, doll: CreateDollProfile<'_>) -> sqlx::Result<()> {
|
||||
sqlx::query!(
|
||||
r#"
|
||||
insert into doll_profiles
|
||||
|
@ -82,3 +86,41 @@ pub async fn create(mut db: DollTagsDb, doll: CreateDollProfile<'_>) -> sqlx::Re
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// deleting a doll profile only wipes the data associated to it but retains two bits of info:
|
||||
/// - the tag's ID
|
||||
/// - the account which created this tag
|
||||
///
|
||||
/// this is to ensure that no one else will be able to re-use this tag for some time while allowing
|
||||
/// 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<()> {
|
||||
sqlx::query!(
|
||||
r#"
|
||||
update doll_profiles
|
||||
set microchip_id = null,
|
||||
name = '',
|
||||
pronoun_subject = '',
|
||||
pronoun_object = '',
|
||||
pronoun_possessive = '',
|
||||
handler_name = '',
|
||||
handler_link = null,
|
||||
kind = null,
|
||||
breed = null,
|
||||
behaviour = null,
|
||||
description = null,
|
||||
chassis_type = null,
|
||||
chassis_id = null,
|
||||
chassis_color = null,
|
||||
updated_at = current_timestamp,
|
||||
archived_at = current_timestamp
|
||||
where id = $1
|
||||
"#,
|
||||
id
|
||||
)
|
||||
.execute(&mut **db)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -1,12 +1,17 @@
|
|||
use ::chrono::Utc;
|
||||
use rocket::serde::Serialize;
|
||||
use rocket_db_pools::{Connection, Database};
|
||||
use sqlx::types::{chrono, Uuid};
|
||||
use sqlx::{
|
||||
pool::PoolConnection,
|
||||
types::{chrono, Uuid},
|
||||
Postgres,
|
||||
};
|
||||
|
||||
#[derive(Database)]
|
||||
#[database("dolltags")]
|
||||
pub struct DollTags(sqlx::PgPool);
|
||||
pub type DollTagsDb = Connection<DollTags>;
|
||||
pub type DbHook = PoolConnection<Postgres>;
|
||||
|
||||
// Doll Profiles stuff
|
||||
#[derive(Debug, Serialize)]
|
||||
|
@ -16,6 +21,7 @@ pub struct DollProfile {
|
|||
pub microchip_id: Option<String>,
|
||||
pub created_at: chrono::DateTime<Utc>,
|
||||
pub updated_at: Option<chrono::DateTime<Utc>>,
|
||||
pub archived_at: Option<chrono::DateTime<Utc>>,
|
||||
|
||||
pub bound_to_id: Uuid,
|
||||
|
||||
|
|
|
@ -9,9 +9,9 @@ pub fn generate_ids() -> Vec<i32> {
|
|||
(1..=10).map(|_| uniform.sample(&mut rng)).collect()
|
||||
}
|
||||
|
||||
pub async fn pick_ids(db: DollTagsDb) -> Result<Vec<i32>, sqlx::Error> {
|
||||
pub async fn pick_ids(mut db: DollTagsDb) -> Result<Vec<i32>, sqlx::Error> {
|
||||
let mut ids_bundle = generate_ids();
|
||||
let occupied_ids = doll::check_ids(db, &ids_bundle).await?;
|
||||
let occupied_ids = doll::check_ids(&mut *db, &ids_bundle).await?;
|
||||
ids_bundle.retain(|&id| !occupied_ids.contains(&id));
|
||||
Ok(ids_bundle.iter().take(5).map(|v| *v).collect::<Vec<i32>>())
|
||||
}
|
||||
|
|
|
@ -34,6 +34,8 @@ fn rocket() -> _ {
|
|||
account::show_settings,
|
||||
form::register_tag::show_register,
|
||||
form::register_tag::handle_register,
|
||||
account::ask_delete,
|
||||
account::confirm_delete,
|
||||
],
|
||||
)
|
||||
.mount(
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use rocket::response::Redirect;
|
||||
use rocket_dyn_templates::{context, Template};
|
||||
|
||||
use crate::{
|
||||
|
@ -11,8 +12,9 @@ use crate::{
|
|||
use super::error_handlers::PageResult;
|
||||
|
||||
#[get("/")]
|
||||
pub async fn index(db: DollTagsDb, user: User, meta: CommonTemplateState) -> PageResult {
|
||||
let tags = doll::list(db, &user.id).await?;
|
||||
pub async fn index(mut db: DollTagsDb, user: User, meta: CommonTemplateState) -> PageResult {
|
||||
let tags = doll::list(&mut *db, &user.id).await?;
|
||||
let archived_tags = doll::list_archived(&mut *db, &user.id).await?;
|
||||
|
||||
Ok(Template::render(
|
||||
"account/index",
|
||||
|
@ -20,6 +22,7 @@ pub async fn index(db: DollTagsDb, user: User, meta: CommonTemplateState) -> Pag
|
|||
meta,
|
||||
user,
|
||||
tags,
|
||||
archived_tags,
|
||||
},
|
||||
)
|
||||
.into())
|
||||
|
@ -29,3 +32,32 @@ pub async fn index(db: DollTagsDb, user: User, meta: CommonTemplateState) -> Pag
|
|||
pub fn show_settings(user: User, meta: CommonTemplateState) -> Template {
|
||||
todo!("woof");
|
||||
}
|
||||
|
||||
#[get("/delete/<id>")]
|
||||
pub async fn ask_delete(
|
||||
mut db: DollTagsDb,
|
||||
id: i32,
|
||||
_user: User,
|
||||
meta: CommonTemplateState,
|
||||
) -> PageResult {
|
||||
let db_tag = doll::get(&mut *db, id, "").await?;
|
||||
|
||||
if let Some(tag) = db_tag {
|
||||
Ok(Template::render(
|
||||
"tag/delete",
|
||||
context! {
|
||||
meta,
|
||||
tag,
|
||||
},
|
||||
)
|
||||
.into())
|
||||
} else {
|
||||
Ok(Redirect::to(uri!("/account", index)).into())
|
||||
}
|
||||
}
|
||||
|
||||
#[get("/yes_delete_this/<id>")]
|
||||
pub async fn confirm_delete(mut db: DollTagsDb, id: i32, _user: User) -> PageResult {
|
||||
doll::delete(&mut *db, id).await?;
|
||||
Ok(Redirect::to(uri!("/account", index)).into())
|
||||
}
|
||||
|
|
|
@ -136,7 +136,7 @@ pub async fn handle_register(
|
|||
}
|
||||
|
||||
doll::create(
|
||||
db,
|
||||
&mut *db,
|
||||
CreateDollProfile {
|
||||
id,
|
||||
microchip_id,
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use rocket::response::Redirect;
|
||||
use rocket_dyn_templates::{context, Template};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{
|
||||
db::{doll, schema::DollTagsDb},
|
||||
|
@ -35,7 +34,7 @@ pub fn short_url(id: &str) -> Redirect {
|
|||
|
||||
#[get("/profile?<ident>&<microchip_id>")]
|
||||
pub async fn show_profile(
|
||||
db: DollTagsDb,
|
||||
mut db: DollTagsDb,
|
||||
ident: Option<&str>,
|
||||
microchip_id: Option<&str>,
|
||||
meta: CommonTemplateState,
|
||||
|
@ -43,7 +42,7 @@ pub async fn show_profile(
|
|||
let internal_id = ident.and_then(|v| id_public_to_db(v)).unwrap_or(0);
|
||||
let microchip_id = microchip_id.unwrap_or("");
|
||||
|
||||
let profile = match doll::get(db, internal_id, microchip_id).await? {
|
||||
let profile = match doll::get(&mut *db, internal_id, microchip_id).await? {
|
||||
Some(p) => p,
|
||||
None => return Ok(Redirect::to(uri!(index(Some(true), ident, Some(microchip_id)))).into()),
|
||||
};
|
||||
|
|
|
@ -38,4 +38,18 @@
|
|||
</article>
|
||||
{% endfor %}
|
||||
</section>
|
||||
|
||||
{% if archived_tags | length > 0 %}
|
||||
<section>
|
||||
<h2>Your archived tags</h2>
|
||||
|
||||
<p>You have {{archived_tags | length}} archived tags for which you may reuse the ID.</p>
|
||||
|
||||
<ul>
|
||||
{% for id in archived_tags %}
|
||||
<li><a href="/account/edit/{{id}}">{{id}}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</section>
|
||||
{% endif %}
|
||||
{% endblock main %}
|
18
templates/tag/delete.html.tera
Normal file
18
templates/tag/delete.html.tera
Normal file
|
@ -0,0 +1,18 @@
|
|||
{% extends "base" %}
|
||||
{% block title %}Log in - {% endblock title %}
|
||||
{% block main %}
|
||||
<h2>Are you sure to delete it?</h2>
|
||||
|
||||
<p>You are about to delete the tag {{ tag.id | pretty_id }} registered for "{{tag.name}}".</p>
|
||||
<p>
|
||||
Doing so will irreversibly remove the data,
|
||||
and the ID will be kept for you in case you were to want to create a new
|
||||
tag using this ID.
|
||||
</p>
|
||||
|
||||
<p class="subnav">
|
||||
<a href="/account">No, take me back</a>
|
||||
<a href="/account/yes_delete_this/{{tag.id}}">Yes, delete this tag</a>
|
||||
</p>
|
||||
|
||||
{% endblock main %}
|
Loading…
Add table
Reference in a new issue