From 5541477da5bedf03878f6244b4c0c1e75fcfa300 Mon Sep 17 00:00:00 2001 From: Artemis Date: Fri, 24 Jan 2025 16:24:49 +0100 Subject: [PATCH] damn migrating to pgsql was fast here --- .env | 2 +- .gitignore | 2 -- Cargo.toml | 4 +-- README.md | 1 - Rocket.toml | 2 +- migrations/1_base.sql | 41 +++++++++++------------ shell.nix | 3 +- src/db/doll.rs | 54 +++++++++++++----------------- src/db/schema.rs | 23 ++++++++----- src/ids.rs | 14 ++++---- src/main.rs | 2 +- src/routes/form/register_tag.rs | 14 ++++---- src/routes/public.rs | 2 +- templates/macros/display.html.tera | 4 +-- templates/show_profile.html.tera | 6 +++- 15 files changed, 83 insertions(+), 91 deletions(-) diff --git a/.env b/.env index f54a4c2..07b8778 100644 --- a/.env +++ b/.env @@ -1 +1 @@ -DATABASE_URL="sqlite://dolltags.sqlite" +DATABASE_URL="postgres://postgres:woofwoof@localhost/dolltags" diff --git a/.gitignore b/.gitignore index 51b0e40..be8553b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,2 @@ # built binary -cap /target -*.sqlite* diff --git a/Cargo.toml b/Cargo.toml index af42855..e253010 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,9 +6,9 @@ edition = "2021" [dependencies] rocket = "0.5.1" rocket_dyn_templates = { version = "0.2.0", features = ["tera"] } -rocket_db_pools = { version = "0.2.0", features = ["sqlx_sqlite"] } +rocket_db_pools = { version = "0.2.0", features = ["sqlx_postgres"] } sqlx = { version = "0.7", default-features = false, features = [ - "sqlite", + "postgres", "macros", "chrono", "migrate", diff --git a/README.md b/README.md index a9bd536..c9f4705 100644 --- a/README.md +++ b/README.md @@ -29,4 +29,3 @@ a profile is - 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 -- pgsql migration? may make hosting easier for me as well as cleaner migrations and types diff --git a/Rocket.toml b/Rocket.toml index d115462..686ac52 100644 --- a/Rocket.toml +++ b/Rocket.toml @@ -1,2 +1,2 @@ [default.databases.dolltags] -url = "dolltags.sqlite" +url = "postgres://postgres:woofwoof@localhost/dolltags" diff --git a/migrations/1_base.sql b/migrations/1_base.sql index 835baae..39623f1 100644 --- a/migrations/1_base.sql +++ b/migrations/1_base.sql @@ -1,26 +1,23 @@ --- base schema +create table doll_profiles ( + id integer not null primary key, + microchip_id varchar(32) unique, + created_at timestamptz not null default current_timestamp, + updated_at timestamptz, --- notnulls are tmp till i deal with proper validation -create table if not exists doll_profiles ( - id integer not null primary key, -- 000000 format - created_at text not null default current_timestamp, - updated_at text, + name varchar(256) not null, + pronoun_subject varchar(32) not null, + pronoun_object varchar(32) not null, + pronoun_possessive varchar(32) not null, - -- base info - name text not null, - pronouns text not null, -- format as `{subject}/{object}/{possessive}` eg `she/her/hers` - handler_name text not null, - handler_url text, + handler_name varchar(256) not null, + handler_link varchar(2048), - -- customisation options for various entities - description text, - kind text, - breed text, - chassis_type text, - chassis_id text, - chassis_color text, + kind varchar(256), + breed varchar(256), + behaviour varchar(256), + description varchar(2048), - -- ID'ing - behaviour text, - microchip_id text unique -) strict; + chassis_type varchar(256), + chassis_id varchar(256), + chassis_color varchar(256) +); diff --git a/shell.nix b/shell.nix index 90b8777..e472d0e 100644 --- a/shell.nix +++ b/shell.nix @@ -9,7 +9,7 @@ pkgs.mkShell { # native dependencies # pkg-config # Dev env - sqlite sqlitebrowser sqlx-cli + sqlx-cli ] ); @@ -19,5 +19,4 @@ pkgs.mkShell { # Rust # See https://discourse.nixos.org/t/rust-src-not-found-and-other-misadventures-of-developing-rust-on-nixos/11570/3?u=samuela. for more details. RUST_SRC_PATH = pkgs.rust.packages.stable.rustPlatform.rustLibSrc; - DATABASE_URL = "sqlite://dolltags.sqlite"; } diff --git a/src/db/doll.rs b/src/db/doll.rs index ccbe3fa..29c5918 100644 --- a/src/db/doll.rs +++ b/src/db/doll.rs @@ -1,51 +1,41 @@ use crate::db::schema::DollProfile; -use sqlx::{pool::PoolConnection, types::chrono, Sqlite}; +use sqlx::{pool::PoolConnection, Postgres}; use super::schema::{CreateDollProfile, DollTagsDb}; pub async fn get( mut db: DollTagsDb, - ident: i64, + ident: i32, microchip_id: &str, ) -> sqlx::Result> { sqlx::query_as!( DollProfile, r#" - select - id, microchip_id, - name, pronouns, handler_name, handler_url, kind, breed, behaviour, description, chassis_type, chassis_id, chassis_color, - created_at as "created_at!: chrono::NaiveDateTime", updated_at as "updated_at!: Option" - from doll_profiles where id = ? or microchip_id = ? + select * from doll_profiles where id = $1 or microchip_id = $2 "#, ident, microchip_id ) - .fetch_optional(&mut **db).await + .fetch_optional(&mut **db) + .await } -pub async fn check_ids(mut db: DollTagsDb, idents: &Vec) -> sqlx::Result> { - let idents_sql_input = idents.iter().map(|_| "?").collect::>().join(", "); - - let sql = format!( - r#"select id from doll_profiles where id in ({})"#, - idents_sql_input - ); - let mut query = sqlx::query_scalar(&sql); - - for ident in idents { - query = query.bind(ident); - } - - query.fetch_all(&mut **db).await +pub async fn check_ids(mut db: DollTagsDb, idents: &Vec) -> sqlx::Result> { + sqlx::query_scalar!( + "select id from doll_profiles where id in (select * from unnest($1::int[]))", + idents + ) + .fetch_all(&mut **db) + .await } pub async fn id_exists( - db: &mut PoolConnection, - ident: i64, + db: &mut PoolConnection, + ident: i32, microchip_id: &str, ) -> sqlx::Result { Ok(sqlx::query!( - "select id from doll_profiles where id = ? or microchip_id = ?", + "select id from doll_profiles where id = $1 or microchip_id = $2", ident, microchip_id ) @@ -54,19 +44,21 @@ pub async fn id_exists( .is_some()) } -pub async fn create(mut db: DollTagsDb, doll: CreateDollProfile<'_>) -> sqlx::Result { +pub async fn create(mut db: DollTagsDb, doll: CreateDollProfile<'_>) -> sqlx::Result<()> { sqlx::query!( r#" insert into doll_profiles - (id, microchip_id, name, pronouns, handler_name, handler_url, kind, breed, behaviour, description, chassis_type, chassis_id, chassis_color) - values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + (id, microchip_id, name, pronoun_subject, pronoun_object, pronoun_possessive, handler_name, handler_link, kind, breed, behaviour, description, chassis_type, chassis_id, chassis_color) + values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15) "#, doll.id, doll.microchip_id, doll.name, - doll.pronouns, + doll.pronoun_subject, + doll.pronoun_object, + doll.pronoun_possessive, doll.handler_name, - doll.handler_url, + doll.handler_link, doll.kind, doll.breed, doll.behaviour, @@ -76,5 +68,5 @@ pub async fn create(mut db: DollTagsDb, doll: CreateDollProfile<'_>) -> sqlx::Re doll.chassis_color, ).execute(&mut **db).await?; - Ok(doll.id) + Ok(()) } diff --git a/src/db/schema.rs b/src/db/schema.rs index ba9d194..3482582 100644 --- a/src/db/schema.rs +++ b/src/db/schema.rs @@ -1,25 +1,28 @@ +use ::chrono::Utc; use rocket::serde::Serialize; use rocket_db_pools::{Connection, Database}; use sqlx::types::chrono; #[derive(Database)] #[database("dolltags")] -pub struct DollTags(sqlx::SqlitePool); +pub struct DollTags(sqlx::PgPool); pub type DollTagsDb = Connection; // Doll Profiles stuff #[derive(Debug, Serialize)] #[serde(crate = "rocket::serde")] pub struct DollProfile { - pub id: i64, + pub id: i32, pub microchip_id: Option, - pub created_at: chrono::NaiveDateTime, - pub updated_at: Option, + pub created_at: chrono::DateTime, + pub updated_at: Option>, pub name: String, - pub pronouns: String, + pub pronoun_subject: String, + pub pronoun_object: String, + pub pronoun_possessive: String, pub handler_name: String, - pub handler_url: Option, + pub handler_link: Option, pub kind: Option, pub breed: Option, @@ -43,13 +46,15 @@ impl DollProfile { #[derive(Debug)] pub struct CreateDollProfile<'a> { - pub id: i64, + pub id: i32, pub microchip_id: Option<&'a str>, pub name: &'a str, - pub pronouns: &'a str, + pub pronoun_subject: &'a str, + pub pronoun_object: &'a str, + pub pronoun_possessive: &'a str, pub handler_name: &'a str, - pub handler_url: Option<&'a str>, + pub handler_link: Option<&'a str>, pub kind: Option<&'a str>, pub breed: Option<&'a str>, diff --git a/src/ids.rs b/src/ids.rs index 7dc195c..ff0a8d3 100644 --- a/src/ids.rs +++ b/src/ids.rs @@ -3,29 +3,29 @@ use regex::Regex; use crate::db::{doll, schema::DollTagsDb}; -pub fn generate_ids() -> Vec { - let uniform = Uniform::new_inclusive::(100_000, 999_999); +pub fn generate_ids() -> Vec { + let uniform = Uniform::new_inclusive::(100_000, 999_999); let mut rng = thread_rng(); (1..=10).map(|_| uniform.sample(&mut rng)).collect() } -pub async fn pick_ids(db: DollTagsDb) -> Result, sqlx::Error> { +pub async fn pick_ids(db: DollTagsDb) -> Result, sqlx::Error> { let mut ids_bundle = generate_ids(); let occupied_ids = doll::check_ids(db, &ids_bundle).await?; ids_bundle.retain(|&id| !occupied_ids.contains(&id)); - Ok(ids_bundle.iter().take(5).map(|v| *v).collect::>()) + Ok(ids_bundle.iter().take(5).map(|v| *v).collect::>()) } -pub fn id_public_to_db(id: &str) -> Option { +pub fn id_public_to_db(id: &str) -> Option { let id_re = Regex::new(r"^\d{6}$").unwrap(); if id_re.is_match(id) { - id.parse::().ok() + id.parse::().ok() } else { None } } -pub fn id_db_to_public(id: i64) -> String { +pub fn id_db_to_public(id: i32) -> String { let first = id / 1000; let second = id % 1000; format!("{:0>3}-{:0>3}", first, second) diff --git a/src/main.rs b/src/main.rs index d0dda16..26b8f7a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -23,7 +23,7 @@ fn rocket() -> _ { engines .tera .register_filter("pretty_id", |v: &Value, _: &HashMap| { - let value = try_get_value!("pretty_id", "value", i64, v); + let value = try_get_value!("pretty_id", "value", i32, v); Ok(to_value(ids::id_db_to_public(value)).unwrap()) }); })) diff --git a/src/routes/form/register_tag.rs b/src/routes/form/register_tag.rs index a492740..669e110 100644 --- a/src/routes/form/register_tag.rs +++ b/src/routes/form/register_tag.rs @@ -100,7 +100,7 @@ pub async fn handle_register( // in case the form validation fails, this will be tasked with rendering the page again with submitted values and display errors let ids = pick_ids(db).await?; - println!("{:?}", &tag.context); + debug!("registration form invalid, context: {:?}", &tag.context); return Ok(Template::render( "register", @@ -113,7 +113,7 @@ pub async fn handle_register( } }; - println!("register: {:?}", tag); + debug!("registering tag: {:?}", tag); fn normalize_opt(opt: &str) -> Option<&str> { if opt.len() != 0 { Some(opt) @@ -123,10 +123,6 @@ pub async fn handle_register( } let id = id_public_to_db(&tag.ident).expect("id format was wrong but is now right??"); - let pronouns = format!( - "{}/{}/{}", - tag.pronoun_subject, tag.pronoun_object, tag.pronoun_possessive - ); let normalized_microchip_id = tag.microchip_id.to_lowercase(); let microchip_id = normalize_opt(&normalized_microchip_id); @@ -140,9 +136,11 @@ pub async fn handle_register( id, microchip_id, name: tag.name, - pronouns: pronouns.as_str(), + pronoun_subject: tag.pronoun_subject, + pronoun_object: tag.pronoun_object, + pronoun_possessive: tag.pronoun_possessive, handler_name: tag.handler_name, - handler_url: normalize_opt(tag.handler_link), + handler_link: normalize_opt(tag.handler_link), kind: normalize_opt(tag.kind), breed: normalize_opt(tag.breed), behaviour: normalize_opt(tag.behaviour), diff --git a/src/routes/public.rs b/src/routes/public.rs index bc21cf0..45aae1d 100644 --- a/src/routes/public.rs +++ b/src/routes/public.rs @@ -39,7 +39,7 @@ pub async fn show_profile( && profile.chassis_type.is_some() && profile.chassis_color.is_some(); - println!("{:?}", profile); + debug!("showing profile: {:?}", profile); Ok(Template::render( "show_profile", context! { diff --git a/templates/macros/display.html.tera b/templates/macros/display.html.tera index 40b7bf5..a592600 100644 --- a/templates/macros/display.html.tera +++ b/templates/macros/display.html.tera @@ -1,7 +1,7 @@ {% macro pretty_pronouns(pronouns) %} {% set fragments = pronouns | split(pat="/") %} {% for fr in fragments %} -{{fr}} + {% if not loop.last %}/{% endif %} {% endfor %} -{% endmacro pretty_pronouns %} \ No newline at end of file +{% endmacro pretty_pronouns %} diff --git a/templates/show_profile.html.tera b/templates/show_profile.html.tera index d78cbcd..52eebf5 100644 --- a/templates/show_profile.html.tera +++ b/templates/show_profile.html.tera @@ -13,7 +13,11 @@ Not microchipped {% endif %}

-

{{ macros::pretty_pronouns(pronouns=profile.pronouns) }}

+

+ {{profile.pronoun_subject}} / + {{profile.pronoun_object}} / + {{profile.pronoun_possessive}} +

{{profile.name}}