damn migrating to pgsql was fast here
This commit is contained in:
parent
fe2c2a1b8d
commit
5541477da5
15 changed files with 83 additions and 91 deletions
2
.env
2
.env
|
@ -1 +1 @@
|
|||
DATABASE_URL="sqlite://dolltags.sqlite"
|
||||
DATABASE_URL="postgres://postgres:woofwoof@localhost/dolltags"
|
||||
|
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,4 +1,2 @@
|
|||
# built binary
|
||||
cap
|
||||
/target
|
||||
*.sqlite*
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
[default.databases.dolltags]
|
||||
url = "dolltags.sqlite"
|
||||
url = "postgres://postgres:woofwoof@localhost/dolltags"
|
||||
|
|
|
@ -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)
|
||||
);
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
|
|
|
@ -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<Option<DollProfile>> {
|
||||
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<chrono::NaiveDateTime>"
|
||||
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<i64>) -> sqlx::Result<Vec<i64>> {
|
||||
let idents_sql_input = idents.iter().map(|_| "?").collect::<Vec<&str>>().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<i32>) -> sqlx::Result<Vec<i32>> {
|
||||
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<Sqlite>,
|
||||
ident: i64,
|
||||
db: &mut PoolConnection<Postgres>,
|
||||
ident: i32,
|
||||
microchip_id: &str,
|
||||
) -> sqlx::Result<bool> {
|
||||
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<i64> {
|
||||
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(())
|
||||
}
|
||||
|
|
|
@ -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<DollTags>;
|
||||
|
||||
// Doll Profiles stuff
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(crate = "rocket::serde")]
|
||||
pub struct DollProfile {
|
||||
pub id: i64,
|
||||
pub id: i32,
|
||||
pub microchip_id: Option<String>,
|
||||
pub created_at: chrono::NaiveDateTime,
|
||||
pub updated_at: Option<chrono::NaiveDateTime>,
|
||||
pub created_at: chrono::DateTime<Utc>,
|
||||
pub updated_at: Option<chrono::DateTime<Utc>>,
|
||||
|
||||
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<String>,
|
||||
pub handler_link: Option<String>,
|
||||
|
||||
pub kind: Option<String>,
|
||||
pub breed: Option<String>,
|
||||
|
@ -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>,
|
||||
|
|
14
src/ids.rs
14
src/ids.rs
|
@ -3,29 +3,29 @@ use regex::Regex;
|
|||
|
||||
use crate::db::{doll, schema::DollTagsDb};
|
||||
|
||||
pub fn generate_ids() -> Vec<i64> {
|
||||
let uniform = Uniform::new_inclusive::<i64, i64>(100_000, 999_999);
|
||||
pub fn generate_ids() -> Vec<i32> {
|
||||
let uniform = Uniform::new_inclusive::<i32, i32>(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<Vec<i64>, sqlx::Error> {
|
||||
pub async fn pick_ids(db: DollTagsDb) -> Result<Vec<i32>, 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::<Vec<i64>>())
|
||||
Ok(ids_bundle.iter().take(5).map(|v| *v).collect::<Vec<i32>>())
|
||||
}
|
||||
|
||||
pub fn id_public_to_db(id: &str) -> Option<i64> {
|
||||
pub fn id_public_to_db(id: &str) -> Option<i32> {
|
||||
let id_re = Regex::new(r"^\d{6}$").unwrap();
|
||||
|
||||
if id_re.is_match(id) {
|
||||
id.parse::<i64>().ok()
|
||||
id.parse::<i32>().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)
|
||||
|
|
|
@ -23,7 +23,7 @@ fn rocket() -> _ {
|
|||
engines
|
||||
.tera
|
||||
.register_filter("pretty_id", |v: &Value, _: &HashMap<String, Value>| {
|
||||
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())
|
||||
});
|
||||
}))
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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! {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{% macro pretty_pronouns(pronouns) %}
|
||||
{% set fragments = pronouns | split(pat="/") %}
|
||||
{% for fr in fragments %}
|
||||
<span class="pronoun_fragment">{{fr}}</span>
|
||||
|
||||
{% if not loop.last %}/{% endif %}
|
||||
{% endfor %}
|
||||
{% endmacro pretty_pronouns %}
|
||||
{% endmacro pretty_pronouns %}
|
||||
|
|
|
@ -13,7 +13,11 @@
|
|||
Not microchipped
|
||||
{% endif %}
|
||||
</p>
|
||||
<p class="pronouns">{{ macros::pretty_pronouns(pronouns=profile.pronouns) }}</p>
|
||||
<p class="pronouns">
|
||||
<span class="pronoun_fragment">{{profile.pronoun_subject}}</span> /
|
||||
<span class="pronoun_fragment">{{profile.pronoun_object}}</span> /
|
||||
<span class="pronoun_fragment">{{profile.pronoun_possessive}}</span>
|
||||
</p>
|
||||
</header>
|
||||
<h2>{{profile.name}}</h2>
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue