From 27d7c2f4b51af8d295af779626df48ccd16f95c3 Mon Sep 17 00:00:00 2001 From: trisua Date: Thu, 3 Apr 2025 15:07:57 -0400 Subject: [PATCH] fix: postgres --- crates/app/src/assets.rs | 2 + crates/app/src/routes/api/v1/auth/images.rs | 4 +- crates/core/src/config.rs | 12 +--- crates/core/src/database/audit_log.rs | 14 ++-- crates/core/src/database/auth.rs | 67 ++++++++--------- crates/core/src/database/common.rs | 71 ++++++++++++++----- crates/core/src/database/communities.rs | 24 +++---- crates/core/src/database/drivers/postgres.rs | 26 +++++-- .../database/drivers/sql/create_audit_log.sql | 6 +- .../drivers/sql/create_communities.sql | 12 ++-- .../database/drivers/sql/create_ipbans.sql | 2 +- .../drivers/sql/create_memberships.sql | 10 +-- .../drivers/sql/create_notifications.sql | 8 +-- .../src/database/drivers/sql/create_posts.sql | 16 ++--- .../database/drivers/sql/create_reactions.sql | 10 +-- .../database/drivers/sql/create_reports.sql | 8 +-- .../drivers/sql/create_userblocks.sql | 8 +-- .../drivers/sql/create_userfollows.sql | 8 +-- .../src/database/drivers/sql/create_users.sql | 16 ++--- crates/core/src/database/drivers/sqlite.rs | 10 +++ crates/core/src/database/ipbans.rs | 8 +-- crates/core/src/database/memberships.rs | 22 +++--- crates/core/src/database/notifications.rs | 26 ++++--- crates/core/src/database/posts.rs | 34 ++++----- crates/core/src/database/reactions.rs | 22 +++--- crates/core/src/database/reports.rs | 20 +++--- crates/core/src/database/userblocks.rs | 14 ++-- crates/core/src/database/userfollows.rs | 14 ++-- example/tetratto.toml | 28 ++++++++ 29 files changed, 298 insertions(+), 224 deletions(-) diff --git a/crates/app/src/assets.rs b/crates/app/src/assets.rs index 6036787..06c27fb 100644 --- a/crates/app/src/assets.rs +++ b/crates/app/src/assets.rs @@ -188,6 +188,8 @@ pub(crate) async fn write_assets(config: &Config) -> PathBufD { /// Set up extra directories. pub(crate) async fn init_dirs(config: &Config) { + create_dir_if_not_exists!(&config.dirs.templates); + // images create_dir_if_not_exists!(&config.dirs.media); let images_path = PathBufD::current().extend(&[config.dirs.media.as_str(), "images"]); diff --git a/crates/app/src/routes/api/v1/auth/images.rs b/crates/app/src/routes/api/v1/auth/images.rs index 0c8af88..c5f5441 100644 --- a/crates/app/src/routes/api/v1/auth/images.rs +++ b/crates/app/src/routes/api/v1/auth/images.rs @@ -73,7 +73,7 @@ pub async fn avatar_request( let path = PathBufD::current().extend(&[ data.0.dirs.media.as_str(), "avatars", - &format!("{}.avif", &user.id), + &format!("{}.avif", &(user.id as i64)), ]); if !exists(&path).unwrap() { @@ -118,7 +118,7 @@ pub async fn banner_request( let path = PathBufD::current().extend(&[ data.0.dirs.media.as_str(), "banners", - &format!("{}.avif", &user.id), + &format!("{}.avif", &(user.id as i64)), ]); if !exists(&path).unwrap() { diff --git a/crates/core/src/config.rs b/crates/core/src/config.rs index f2e5036..36a4917 100644 --- a/crates/core/src/config.rs +++ b/crates/core/src/config.rs @@ -9,10 +9,7 @@ pub struct SecurityConfig { /// If registrations are enabled. #[serde(default = "default_security_registration_enabled")] pub registration_enabled: bool, - /// If registrations are enabled. - #[serde(default = "default_security_admin_user")] - pub admin_user: String, - /// If registrations are enabled. + /// The name of the header which will contain the real IP of the connecting user. #[serde(default = "default_real_ip_header")] pub real_ip_header: String, } @@ -21,10 +18,6 @@ fn default_security_registration_enabled() -> bool { true } -fn default_security_admin_user() -> String { - "admin".to_string() -} - fn default_real_ip_header() -> String { "CF-Connecting-IP".to_string() } @@ -33,7 +26,6 @@ impl Default for SecurityConfig { fn default() -> Self { Self { registration_enabled: default_security_registration_enabled(), - admin_user: default_security_admin_user(), real_ip_header: default_real_ip_header(), } } @@ -202,7 +194,7 @@ fn default_name() -> String { } fn default_description() -> String { - "🐐 tetratto!".to_string() + "🐇 tetratto!".to_string() } fn default_color() -> String { diff --git a/crates/core/src/database/audit_log.rs b/crates/core/src/database/audit_log.rs index 74f32a5..df39503 100644 --- a/crates/core/src/database/audit_log.rs +++ b/crates/core/src/database/audit_log.rs @@ -1,7 +1,7 @@ use super::*; use crate::cache::Cache; use crate::model::{Error, Result, auth::User, moderation::AuditLogEntry, permissions::FinePermission}; -use crate::{auto_method, execute, get, query_row, query_rows}; +use crate::{auto_method, execute, get, params, query_row, query_rows}; #[cfg(feature = "sqlite")] use rusqlite::Row; @@ -23,7 +23,7 @@ impl DataManager { } } - auto_method!(get_audit_log_entry_by_id(usize)@get_audit_log_entry_from_row -> "SELECT * FROM audit_log WHERE id = $1" --name="audit log entry" --returns=AuditLogEntry --cache-key-tmpl="atto.audit_log:{}"); + auto_method!(get_audit_log_entry_by_id(usize as i64)@get_audit_log_entry_from_row -> "SELECT * FROM audit_log WHERE id = $1" --name="audit log entry" --returns=AuditLogEntry --cache-key-tmpl="atto.audit_log:{}"); /// Get all audit log entries (paginated). /// @@ -67,10 +67,10 @@ impl DataManager { let res = execute!( &conn, "INSERT INTO audit_log VALUES ($1, $2, $3, $4)", - &[ - &data.id.to_string().as_str(), - &data.created.to_string().as_str(), - &data.moderator.to_string().as_str(), + params![ + &(data.id as i64), + &(data.created as i64), + &(data.moderator as i64), &data.content.as_str(), ] ); @@ -96,7 +96,7 @@ impl DataManager { let res = execute!( &conn, "DELETE FROM audit_log WHERE id = $1", - &[&id.to_string()] + &[&(id as i64)] ); if let Err(e) = res { diff --git a/crates/core/src/database/auth.rs b/crates/core/src/database/auth.rs index 2dc31cd..a7be386 100644 --- a/crates/core/src/database/auth.rs +++ b/crates/core/src/database/auth.rs @@ -6,7 +6,7 @@ use crate::model::{ auth::{Token, User, UserSettings}, permissions::FinePermission, }; -use crate::{auto_method, execute, get, query_row}; +use crate::{auto_method, execute, get, query_row, params}; use pathbufd::PathBufD; use std::fs::{exists, remove_file}; use tetratto_shared::hash::{hash_salted, salt}; @@ -32,8 +32,12 @@ impl DataManager { salt: get!(x->4(String)), settings: serde_json::from_str(&get!(x->5(String)).to_string()).unwrap(), tokens: serde_json::from_str(&get!(x->6(String)).to_string()).unwrap(), - permissions: FinePermission::from_bits(get!(x->7(u32))).unwrap(), - is_verified: if get!(x->8(i8)) == 1 { true } else { false }, + permissions: FinePermission::from_bits(get!(x->7(i64)) as u32).unwrap(), + is_verified: if get!(x->8(i64)) as i8 == 1 { + true + } else { + false + }, notification_count: get!(x->9(i64)) as usize, follower_count: get!(x->10(i64)) as usize, following_count: get!(x->11(i64)) as usize, @@ -41,7 +45,7 @@ impl DataManager { } } - auto_method!(get_user_by_id(usize)@get_user_from_row -> "SELECT * FROM users WHERE id = $1" --name="user" --returns=User --cache-key-tmpl="atto.user:{}"); + auto_method!(get_user_by_id(usize as i64)@get_user_from_row -> "SELECT * FROM users WHERE id = $1" --name="user" --returns=User --cache-key-tmpl="atto.user:{}"); auto_method!(get_user_by_username(&str)@get_user_from_row -> "SELECT * FROM users WHERE username = $1" --name="user" --returns=User --cache-key-tmpl="atto.user:{}"); /// Get a user given just their auth token. @@ -106,20 +110,20 @@ impl DataManager { let res = execute!( &conn, "INSERT INTO users VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)", - &[ - &data.id.to_string().as_str(), - &data.created.to_string().as_str(), - &data.username.to_lowercase().as_str(), - &data.password.as_str(), - &data.salt.as_str(), - &serde_json::to_string(&data.settings).unwrap().as_str(), - &serde_json::to_string(&data.tokens).unwrap().as_str(), - &(FinePermission::DEFAULT.bits()).to_string().as_str(), - &(if data.is_verified { 1 } else { 0 }).to_string().as_str(), - &0.to_string().as_str(), - &0.to_string().as_str(), - &0.to_string().as_str(), - &data.last_seen.to_string().as_str(), + params![ + &(data.id as i64), + &(data.created as i64), + &data.username.to_lowercase(), + &data.password, + &data.salt, + &serde_json::to_string(&data.settings).unwrap(), + &serde_json::to_string(&data.tokens).unwrap(), + &(FinePermission::DEFAULT.bits() as i64), + &(if data.is_verified { 1 as i64 } else { 0 as i64 }), + &(0 as i64), + &(0 as i64), + &(0 as i64), + &(data.last_seen as i64), ] ); @@ -213,13 +217,13 @@ impl DataManager { let avatar = PathBufD::current().extend(&[ self.0.dirs.media.as_str(), "avatars", - &format!("{}.avif", &user.id), + &format!("{}.avif", &(user.id as i64)), ]); let banner = PathBufD::current().extend(&[ self.0.dirs.media.as_str(), "banners", - &format!("{}.avif", &user.id), + &format!("{}.avif", &(user.id as i64)), ]); if exists(&avatar).unwrap() { @@ -249,10 +253,7 @@ impl DataManager { let res = execute!( &conn, "UPDATE users SET verified = $1 WHERE id = $2", - &[ - &(if x { 1 } else { 0 }).to_string().as_str(), - &id.to_string().as_str() - ] + params![&(if x { 1 } else { 0 } as i64), &(id as i64)] ); if let Err(e) = res { @@ -299,11 +300,7 @@ impl DataManager { let res = execute!( &conn, "UPDATE users SET password = $1, salt = $2 WHERE id = $3", - &[ - &new_password.as_str(), - &new_salt.as_str(), - &id.to_string().as_str() - ] + params![&new_password.as_str(), &new_salt.as_str(), &(id as i64)] ); if let Err(e) = res { @@ -324,7 +321,7 @@ impl DataManager { let res = execute!( &conn, "UPDATE users SET username = $1 WHERE id = $3", - &[&to.as_str(), &id.to_string().as_str()] + params![&to.as_str(), &(id as i64)] ); if let Err(e) = res { @@ -370,10 +367,7 @@ impl DataManager { let res = execute!( &conn, "UPDATE users SET permissions = $1 WHERE id = $2", - &[ - &(role.bits()).to_string().as_str(), - &id.to_string().as_str() - ] + params![&(role.bits() as i64), &(id as i64)] ); if let Err(e) = res { @@ -406,10 +400,7 @@ impl DataManager { let res = execute!( &conn, "UPDATE users SET last_seen = $1 WHERE id = $2", - &[ - &unix_epoch_timestamp().to_string().as_str(), - &user.id.to_string().as_str() - ] + params![&(unix_epoch_timestamp() as i64), &(user.id as i64)] ); if let Err(e) = res { diff --git a/crates/core/src/database/common.rs b/crates/core/src/database/common.rs index 4799e82..7c7bfec 100644 --- a/crates/core/src/database/common.rs +++ b/crates/core/src/database/common.rs @@ -131,6 +131,41 @@ macro_rules! auto_method { } }; + ($name:ident($selector_t:ty as i64)@$select_fn:ident -> $query:literal --name=$name_:literal --returns=$returns_:tt --cache-key-tmpl=$cache_key_tmpl:literal) => { + pub async fn $name(&self, selector: $selector_t) -> Result<$returns_> { + if let Some(cached) = self + .2 + .get(format!($cache_key_tmpl, selector.to_string())) + .await + { + return Ok(serde_json::from_str(&cached).unwrap()); + } + + let conn = match self.connect().await { + Ok(c) => c, + Err(e) => return Err(Error::DatabaseConnection(e.to_string())), + }; + + let res = query_row!(&conn, $query, &[&(selector as i64)], |x| { + Ok(Self::$select_fn(x)) + }); + + if res.is_err() { + return Err(Error::GeneralNotFound($name_.to_string())); + } + + let x = res.unwrap(); + self.2 + .set( + format!($cache_key_tmpl, selector), + serde_json::to_string(&x).unwrap(), + ) + .await; + + Ok(x) + } + }; + ($name:ident()@$select_fn:ident:$permission:ident -> $query:literal) => { pub async fn $name(&self, id: usize, user: User) -> Result<()> { let y = self.$select_fn(id).await?; @@ -151,7 +186,7 @@ macro_rules! auto_method { Err(e) => return Err(Error::DatabaseConnection(e.to_string())), }; - let res = execute!(&conn, $query, &[&id.to_string()]); + let res = execute!(&conn, $query, &[&(id as i64)]); if let Err(e) = res { return Err(Error::DatabaseError(e.to_string())); @@ -181,7 +216,7 @@ macro_rules! auto_method { Err(e) => return Err(Error::DatabaseConnection(e.to_string())), }; - let res = execute!(&conn, $query, &[&id.to_string()]); + let res = execute!(&conn, $query, &[&(id as i64)]); if let Err(e) = res { return Err(Error::DatabaseError(e.to_string())); @@ -214,7 +249,7 @@ macro_rules! auto_method { Err(e) => return Err(Error::DatabaseConnection(e.to_string())), }; - let res = execute!(&conn, $query, &[&x, &id.to_string()]); + let res = execute!(&conn, $query, &[&x, &(id as i64)]); if let Err(e) = res { return Err(Error::DatabaseError(e.to_string())); @@ -245,7 +280,7 @@ macro_rules! auto_method { Err(e) => return Err(Error::DatabaseConnection(e.to_string())), }; - let res = execute!(&conn, $query, &[&x, &id.to_string()]); + let res = execute!(&conn, $query, params![&x, &(id as i64)]); if let Err(e) = res { return Err(Error::DatabaseError(e.to_string())); @@ -281,7 +316,7 @@ macro_rules! auto_method { let res = execute!( &conn, $query, - &[&serde_json::to_string(&x).unwrap(), &id.to_string()] + &[&serde_json::to_string(&x).unwrap(), &(id as i64)] ); if let Err(e) = res { @@ -316,7 +351,7 @@ macro_rules! auto_method { let res = execute!( &conn, $query, - &[&serde_json::to_string(&x).unwrap(), &id.to_string()] + &[&serde_json::to_string(&x).unwrap(), &(id as i64)] ); if let Err(e) = res { @@ -336,7 +371,7 @@ macro_rules! auto_method { Err(e) => return Err(Error::DatabaseConnection(e.to_string())), }; - let res = execute!(&conn, $query, &[&x, &id.to_string()]); + let res = execute!(&conn, $query, &[&x, &(id as i64)]); if let Err(e) = res { return Err(Error::DatabaseError(e.to_string())); @@ -353,7 +388,7 @@ macro_rules! auto_method { Err(e) => return Err(Error::DatabaseConnection(e.to_string())), }; - let res = execute!(&conn, $query, &[&x, &id.to_string()]); + let res = execute!(&conn, $query, &[&x, &(id as i64)]); if let Err(e) = res { return Err(Error::DatabaseError(e.to_string())); @@ -375,7 +410,7 @@ macro_rules! auto_method { let res = execute!( &conn, $query, - &[&serde_json::to_string(&x).unwrap(), &id.to_string()] + &[&serde_json::to_string(&x).unwrap(), &(id as i64)] ); if let Err(e) = res { @@ -396,7 +431,7 @@ macro_rules! auto_method { let res = execute!( &conn, $query, - &[&serde_json::to_string(&x).unwrap(), &id.to_string()] + &[&serde_json::to_string(&x).unwrap(), &(id as i64)] ); if let Err(e) = res { @@ -416,7 +451,7 @@ macro_rules! auto_method { Err(e) => return Err(Error::DatabaseConnection(e.to_string())), }; - let res = execute!(&conn, $query, &[&id.to_string()]); + let res = execute!(&conn, $query, &[&(id as i64)]); if let Err(e) = res { return Err(Error::DatabaseError(e.to_string())); @@ -435,7 +470,7 @@ macro_rules! auto_method { Err(e) => return Err(Error::DatabaseConnection(e.to_string())), }; - let res = execute!(&conn, $query, &[&id.to_string()]); + let res = execute!(&conn, $query, &[&(id as i64)]); if let Err(e) = res { return Err(Error::DatabaseError(e.to_string())); @@ -468,7 +503,7 @@ macro_rules! auto_method { Err(e) => return Err(Error::DatabaseConnection(e.to_string())), }; - let res = execute!(&conn, $query, &[&id.to_string()]); + let res = execute!(&conn, $query, &[&(id as i64)]); if let Err(e) = res { return Err(Error::DatabaseError(e.to_string())); @@ -501,7 +536,7 @@ macro_rules! auto_method { Err(e) => return Err(Error::DatabaseConnection(e.to_string())), }; - let res = execute!(&conn, $query, &[&x, &id.to_string()]); + let res = execute!(&conn, $query, params![&x, &(id as i64)]); if let Err(e) = res { return Err(Error::DatabaseError(e.to_string())); @@ -525,7 +560,7 @@ macro_rules! auto_method { let res = execute!( &conn, $query, - &[&serde_json::to_string(&x).unwrap(), &id.to_string()] + params![&serde_json::to_string(&x).unwrap(), &(id as i64)] ); if let Err(e) = res { @@ -562,7 +597,7 @@ macro_rules! auto_method { let res = execute!( &conn, $query, - &[&serde_json::to_string(&x).unwrap(), &id.to_string()] + params![&serde_json::to_string(&x).unwrap(), &(id as i64)] ); if let Err(e) = res { @@ -584,7 +619,7 @@ macro_rules! auto_method { Err(e) => return Err(Error::DatabaseConnection(e.to_string())), }; - let res = execute!(&conn, $query, &[&id.to_string()]); + let res = execute!(&conn, $query, &[&(id as i64)]); if let Err(e) = res { return Err(Error::DatabaseError(e.to_string())); @@ -605,7 +640,7 @@ macro_rules! auto_method { Err(e) => return Err(Error::DatabaseConnection(e.to_string())), }; - let res = execute!(&conn, $query, &[&id.to_string()]); + let res = execute!(&conn, $query, &[&(id as i64)]); if let Err(e) = res { return Err(Error::DatabaseError(e.to_string())); diff --git a/crates/core/src/database/communities.rs b/crates/core/src/database/communities.rs index 257c2f7..5025cd8 100644 --- a/crates/core/src/database/communities.rs +++ b/crates/core/src/database/communities.rs @@ -9,7 +9,7 @@ use crate::model::{ communities::{CommunityReadAccess, CommunityWriteAccess}, permissions::FinePermission, }; -use crate::{auto_method, execute, get, query_row, query_rows}; +use crate::{auto_method, execute, get, query_row, query_rows, params}; use pathbufd::PathBufD; use std::fs::{exists, remove_file}; @@ -96,7 +96,7 @@ impl DataManager { let res = query_row!( &conn, "SELECT * FROM communities WHERE title = $1", - &[&id], + params![&id], |x| { Ok(Self::get_community_from_row(x)) } ); @@ -202,18 +202,18 @@ impl DataManager { let res = execute!( &conn, "INSERT INTO communities VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)", - &[ - &data.id.to_string().as_str(), - &data.created.to_string().as_str(), - &data.title.to_lowercase().as_str(), + params![ + &(data.id as i64), + &(data.created as i64), + &data.title.to_lowercase(), &serde_json::to_string(&data.context).unwrap().as_str(), - &data.owner.to_string().as_str(), + &(data.owner as i64), &serde_json::to_string(&data.read_access).unwrap().as_str(), &serde_json::to_string(&data.write_access).unwrap().as_str(), &serde_json::to_string(&data.join_access).unwrap().as_str(), - &0.to_string().as_str(), - &0.to_string().as_str(), - &0.to_string().as_str() + &(0 as i64), + &(0 as i64), + &(1 as i64) ] ); @@ -266,7 +266,7 @@ impl DataManager { let res = execute!( &conn, "DELETE FROM communities WHERE id = $1", - &[&id.to_string()] + &[&(id as i64)] ); if let Err(e) = res { @@ -279,7 +279,7 @@ impl DataManager { let res = execute!( &conn, "DELETE FROM memberships WHERE community = $1", - &[&id.to_string()] + &[&(id as i64)] ); if let Err(e) = res { diff --git a/crates/core/src/database/drivers/postgres.rs b/crates/core/src/database/drivers/postgres.rs index 1949079..d97c33e 100644 --- a/crates/core/src/database/drivers/postgres.rs +++ b/crates/core/src/database/drivers/postgres.rs @@ -13,6 +13,7 @@ use bb8_postgres::{ use std::collections::HashMap; use tetratto_l10n::{LangFile, read_langs}; use tokio_postgres::{Config as PgConfig, NoTls, Row, types::ToSql}; +use std::str::FromStr; pub type Result = std::result::Result; pub type Connection<'a> = PooledConnection<'a, PostgresConnectionManager>; @@ -35,13 +36,14 @@ impl DataManager { /// Create a new [`DataManager`] (and init database). pub async fn new(config: Config) -> Result { let manager = PostgresConnectionManager::new( - { - let mut c = PgConfig::new(); - c.user(&config.database.user); - c.password(&config.database.password); - c.dbname(&config.database.name); - c - }, + PgConfig::from_str(&format!( + "postgresql://{}:{}@{}/{}?target_session_attrs=read-write", + config.database.user, + config.database.password, + config.database.url, + config.database.name + )) + .unwrap(), NoTls, ); @@ -144,3 +146,13 @@ macro_rules! execute { crate::database::execute_helper($conn, $sql, &[]).await }; } + +#[macro_export] +macro_rules! params { + () => { + &[] + }; + ($($x:expr),+ $(,)?) => { + &[$(&$x),+] + }; +} diff --git a/crates/core/src/database/drivers/sql/create_audit_log.sql b/crates/core/src/database/drivers/sql/create_audit_log.sql index bf84f08..88cec27 100644 --- a/crates/core/src/database/drivers/sql/create_audit_log.sql +++ b/crates/core/src/database/drivers/sql/create_audit_log.sql @@ -1,6 +1,6 @@ CREATE TABLE IF NOT EXISTS audit_log ( - id INTEGER NOT NULL PRIMARY KEY, - created INTEGER NOT NULL, - owner INTEGER NOT NULL, + id BIGINT NOT NULL PRIMARY KEY, + created BIGINT NOT NULL, + owner BIGINT NOT NULL, content TEXT NOT NULL ) diff --git a/crates/core/src/database/drivers/sql/create_communities.sql b/crates/core/src/database/drivers/sql/create_communities.sql index 9e004a7..c2252af 100644 --- a/crates/core/src/database/drivers/sql/create_communities.sql +++ b/crates/core/src/database/drivers/sql/create_communities.sql @@ -1,15 +1,15 @@ CREATE TABLE IF NOT EXISTS communities ( - id INTEGER NOT NULL PRIMARY KEY, - created INTEGER NOT NULL, + id BIGINT NOT NULL PRIMARY KEY, + created BIGINT NOT NULL, title TEXT NOT NULL, context TEXT NOT NULL, - owner INTEGER NOT NULL, + owner BIGINT NOT NULL, read_access TEXT NOT NULL, write_access TEXT NOT NULL, join_access TEXT NOT NULL, -- likes - likes INTEGER NOT NULL, - dislikes INTEGER NOT NULL, + likes BIGINT NOT NULL, + dislikes BIGINT NOT NULL, -- counts - member_count INTEGER NOT NULL + member_count BIGINT NOT NULL ) diff --git a/crates/core/src/database/drivers/sql/create_ipbans.sql b/crates/core/src/database/drivers/sql/create_ipbans.sql index 02a5553..bc1029b 100644 --- a/crates/core/src/database/drivers/sql/create_ipbans.sql +++ b/crates/core/src/database/drivers/sql/create_ipbans.sql @@ -1,6 +1,6 @@ CREATE TABLE IF NOT EXISTS ipbans ( ip TEXT NOT NULL, - created INTEGER NOT NULL PRIMARY KEY, + created BIGINT NOT NULL PRIMARY KEY, reason TEXT NOT NULL, moderator TEXT NOT NULL ) diff --git a/crates/core/src/database/drivers/sql/create_memberships.sql b/crates/core/src/database/drivers/sql/create_memberships.sql index a2fa3ef..7461c1c 100644 --- a/crates/core/src/database/drivers/sql/create_memberships.sql +++ b/crates/core/src/database/drivers/sql/create_memberships.sql @@ -1,7 +1,7 @@ CREATE TABLE IF NOT EXISTS memberships ( - id INTEGER NOT NULL PRIMARY KEY, - created INTEGER NOT NULL, - owner INTEGER NOT NULL, - community INTEGER NOT NULL, - role INTEGER NOT NULL + id BIGINT NOT NULL PRIMARY KEY, + created BIGINT NOT NULL, + owner BIGINT NOT NULL, + community BIGINT NOT NULL, + role BIGINT NOT NULL ) diff --git a/crates/core/src/database/drivers/sql/create_notifications.sql b/crates/core/src/database/drivers/sql/create_notifications.sql index 01fdc62..5933be7 100644 --- a/crates/core/src/database/drivers/sql/create_notifications.sql +++ b/crates/core/src/database/drivers/sql/create_notifications.sql @@ -1,8 +1,8 @@ CREATE TABLE IF NOT EXISTS notifications ( - id INTEGER NOT NULL PRIMARY KEY, - created INTEGER NOT NULL, + id BIGINT NOT NULL PRIMARY KEY, + created BIGINT NOT NULL, title TEXT NOT NULL, content TEXT NOT NULL, - owner INTEGER NOT NULL, - read INTEGER NOT NULL + owner BIGINT NOT NULL, + read BIGINT NOT NULL ) diff --git a/crates/core/src/database/drivers/sql/create_posts.sql b/crates/core/src/database/drivers/sql/create_posts.sql index 0ab073a..4d455a0 100644 --- a/crates/core/src/database/drivers/sql/create_posts.sql +++ b/crates/core/src/database/drivers/sql/create_posts.sql @@ -1,14 +1,14 @@ CREATE TABLE IF NOT EXISTS posts ( - id INTEGER NOT NULL PRIMARY KEY, - created INTEGER NOT NULL, + id BIGINT NOT NULL PRIMARY KEY, + created BIGINT NOT NULL, content TEXT NOT NULL, - owner INTEGER NOT NULL, - community INTEGER NOT NULL, + owner BIGINT NOT NULL, + community BIGINT NOT NULL, context TEXT NOT NULL, - replying_to INTEGER, -- the ID of the post this is a comment on... NULL means it isn't a reply + replying_to BIGINT, -- the ID of the post this is a comment on... NULL means it isn't a reply -- likes - likes INTEGER NOT NULL, - dislikes INTEGER NOT NULL, + likes BIGINT NOT NULL, + dislikes BIGINT NOT NULL, -- other counts - comment_count INTEGER NOT NULL + comment_count BIGINT NOT NULL ) diff --git a/crates/core/src/database/drivers/sql/create_reactions.sql b/crates/core/src/database/drivers/sql/create_reactions.sql index 34bda2e..3e60fa3 100644 --- a/crates/core/src/database/drivers/sql/create_reactions.sql +++ b/crates/core/src/database/drivers/sql/create_reactions.sql @@ -1,8 +1,8 @@ CREATE TABLE IF NOT EXISTS reactions ( - id INTEGER NOT NULL PRIMARY KEY, - created INTEGER NOT NULL, - owner INTEGER NOT NULL, - asset INTEGER NOT NULL, + id BIGINT NOT NULL PRIMARY KEY, + created BIGINT NOT NULL, + owner BIGINT NOT NULL, + asset BIGINT NOT NULL, asset_type TEXT NOT NULL, - is_like INTEGER NOT NULL + is_like BIGINT NOT NULL ) diff --git a/crates/core/src/database/drivers/sql/create_reports.sql b/crates/core/src/database/drivers/sql/create_reports.sql index 627dae6..85f30e5 100644 --- a/crates/core/src/database/drivers/sql/create_reports.sql +++ b/crates/core/src/database/drivers/sql/create_reports.sql @@ -1,8 +1,8 @@ CREATE TABLE IF NOT EXISTS reports ( - id INTEGER NOT NULL PRIMARY KEY, - created INTEGER NOT NULL, - owner INTEGER NOT NULL, + id BIGINT NOT NULL PRIMARY KEY, + created BIGINT NOT NULL, + owner BIGINT NOT NULL, content TEXT NOT NULL, - asset INTEGER NOT NULL, + asset BIGINT NOT NULL, asset_type TEXT NOT NULL ) diff --git a/crates/core/src/database/drivers/sql/create_userblocks.sql b/crates/core/src/database/drivers/sql/create_userblocks.sql index 7336baa..7754dac 100644 --- a/crates/core/src/database/drivers/sql/create_userblocks.sql +++ b/crates/core/src/database/drivers/sql/create_userblocks.sql @@ -1,6 +1,6 @@ CREATE TABLE IF NOT EXISTS userblocks ( - id INTEGER NOT NULL PRIMARY KEY, - created INTEGER NOT NULL, - initiator INTEGER NOT NULL, - receiver INTEGER NOT NULL + id BIGINT NOT NULL PRIMARY KEY, + created BIGINT NOT NULL, + initiator BIGINT NOT NULL, + receiver BIGINT NOT NULL ) diff --git a/crates/core/src/database/drivers/sql/create_userfollows.sql b/crates/core/src/database/drivers/sql/create_userfollows.sql index e6804bb..e73c4c0 100644 --- a/crates/core/src/database/drivers/sql/create_userfollows.sql +++ b/crates/core/src/database/drivers/sql/create_userfollows.sql @@ -1,6 +1,6 @@ CREATE TABLE IF NOT EXISTS userfollows ( - id INTEGER NOT NULL PRIMARY KEY, - created INTEGER NOT NULL, - initiator INTEGER NOT NULL, - receiver INTEGER NOT NULL + id BIGINT NOT NULL PRIMARY KEY, + created BIGINT NOT NULL, + initiator BIGINT NOT NULL, + receiver BIGINT NOT NULL ) diff --git a/crates/core/src/database/drivers/sql/create_users.sql b/crates/core/src/database/drivers/sql/create_users.sql index e69677a..7209117 100644 --- a/crates/core/src/database/drivers/sql/create_users.sql +++ b/crates/core/src/database/drivers/sql/create_users.sql @@ -1,15 +1,15 @@ CREATE TABLE IF NOT EXISTS users ( - id INTEGER NOT NULL PRIMARY KEY, - created INTEGER NOT NULL, + id BIGINT NOT NULL PRIMARY KEY, + created BIGINT NOT NULL, username TEXT NOT NULL, password TEXT NOT NULL, salt TEXT NOT NULL, settings TEXT NOT NULL, tokens TEXT NOT NULL, - permissions INTEGER NOT NULL, - verified INTEGER NOT NULL, - notification_count INTEGER NOT NULL, - follower_count INTEGER NOT NULL, - following_count INTEGER NOT NULL, - last_seen INTEGER NOT NULL + permissions BIGINT NOT NULL, + verified BIGINT NOT NULL, + notification_count BIGINT NOT NULL, + follower_count BIGINT NOT NULL, + following_count BIGINT NOT NULL, + last_seen BIGINT NOT NULL ) diff --git a/crates/core/src/database/drivers/sqlite.rs b/crates/core/src/database/drivers/sqlite.rs index 0b35a73..91bfe54 100644 --- a/crates/core/src/database/drivers/sqlite.rs +++ b/crates/core/src/database/drivers/sqlite.rs @@ -86,3 +86,13 @@ macro_rules! execute { $conn.prepare($sql).unwrap().execute(()) }; } + +#[macro_export] +macro_rules! params { + () => { + rusqlite::params![] + }; + ($($params:expr),+ $(,)?) => { + rusqlite::params![$($params),+] + }; +} diff --git a/crates/core/src/database/ipbans.rs b/crates/core/src/database/ipbans.rs index a9cdfd3..64a64f6 100644 --- a/crates/core/src/database/ipbans.rs +++ b/crates/core/src/database/ipbans.rs @@ -2,7 +2,7 @@ use super::*; use crate::cache::Cache; use crate::model::moderation::AuditLogEntry; use crate::model::{Error, Result, auth::IpBan, auth::User, permissions::FinePermission}; -use crate::{auto_method, execute, get, query_row, query_rows}; +use crate::{auto_method, execute, get, query_row, query_rows, params}; #[cfg(feature = "sqlite")] use rusqlite::Row; @@ -71,11 +71,11 @@ impl DataManager { let res = execute!( &conn, "INSERT INTO ipbans VALUES ($1, $2, $3, $4)", - &[ + params![ &data.ip.as_str(), - &data.created.to_string().as_str(), + &(data.created as i64), &data.reason.as_str(), - &data.moderator.to_string().as_str() + &(data.moderator as i64) ] ); diff --git a/crates/core/src/database/memberships.rs b/crates/core/src/database/memberships.rs index d92e8d1..475d05c 100644 --- a/crates/core/src/database/memberships.rs +++ b/crates/core/src/database/memberships.rs @@ -9,7 +9,7 @@ use crate::model::{ communities_permissions::CommunityPermission, permissions::FinePermission, }; -use crate::{auto_method, execute, get, query_row, query_rows}; +use crate::{auto_method, execute, get, query_row, query_rows, params}; #[cfg(feature = "sqlite")] use rusqlite::Row; @@ -28,7 +28,7 @@ impl DataManager { created: get!(x->1(i64)) as usize, owner: get!(x->2(i64)) as usize, community: get!(x->3(i64)) as usize, - role: CommunityPermission::from_bits(get!(x->4(u32))).unwrap(), + role: CommunityPermission::from_bits(get!(x->4(i64)) as u32).unwrap(), } } @@ -78,7 +78,7 @@ impl DataManager { let res = query_rows!( &conn, // 33 = banned, 65 = pending membership - "SELECT * FROM memberships WHERE owner = $1 AND role IS NOT 33 AND role IS NOT 65 ORDER BY created DESC", + "SELECT * FROM memberships WHERE owner = $1 AND NOT role = 33 AND NOT role = 65 ORDER BY created DESC", &[&(owner as i64)], |x| { Self::get_membership_from_row(x) } ); @@ -142,12 +142,12 @@ impl DataManager { let res = execute!( &conn, "INSERT INTO memberships VALUES ($1, $2, $3, $4, $5)", - &[ - &data.id.to_string().as_str(), - &data.created.to_string().as_str(), - &data.owner.to_string().as_str(), - &data.community.to_string().as_str(), - &(data.role.bits()).to_string().as_str(), + params![ + &(data.id as i64), + &(data.created as i64), + &(data.owner as i64), + &(data.community as i64), + &(data.role.bits() as i64), ] ); @@ -198,7 +198,7 @@ impl DataManager { let res = execute!( &conn, "DELETE FROM memberships WHERE id = $1", - &[&id.to_string()] + &[&(id as i64)] ); if let Err(e) = res { @@ -226,7 +226,7 @@ impl DataManager { let res = execute!( &conn, "UPDATE memberships SET role = $1 WHERE id = $2", - &[&(new_role.bits()).to_string(), &id.to_string()] + params![&(new_role.bits() as i64), &(id as i64)] ); if let Err(e) = res { diff --git a/crates/core/src/database/notifications.rs b/crates/core/src/database/notifications.rs index dc064c4..cb9e8a7 100644 --- a/crates/core/src/database/notifications.rs +++ b/crates/core/src/database/notifications.rs @@ -1,7 +1,7 @@ use super::*; use crate::cache::Cache; use crate::model::{Error, Result, auth::Notification, auth::User, permissions::FinePermission}; -use crate::{auto_method, execute, get, query_row, query_rows}; +use crate::{auto_method, execute, get, query_row, query_rows, params}; #[cfg(feature = "sqlite")] use rusqlite::Row; @@ -21,7 +21,11 @@ impl DataManager { title: get!(x->2(String)), content: get!(x->3(String)), owner: get!(x->4(i64)) as usize, - read: if get!(x->5(i8)) == 1 { true } else { false }, + read: if get!(x->5(i64)) as i8 == 1 { + true + } else { + false + }, } } @@ -61,13 +65,13 @@ impl DataManager { let res = execute!( &conn, "INSERT INTO notifications VALUES ($1, $2, $3, $4, $5, $6)", - &[ - &data.id.to_string().as_str(), - &data.created.to_string().as_str(), - &data.title.to_string().as_str(), - &data.content.to_string().as_str(), - &data.owner.to_string().as_str(), - &(if data.read { 1 } else { 0 }).to_string().as_str() + params![ + &(data.id as i64), + &(data.created as i64), + &data.title, + &data.content, + &(data.owner as i64), + &(if data.read { 1 } else { 0 } as i64) ] ); @@ -99,7 +103,7 @@ impl DataManager { let res = execute!( &conn, "DELETE FROM notifications WHERE id = $1", - &[&id.to_string()] + &[&(id as i64)] ); if let Err(e) = res { @@ -158,7 +162,7 @@ impl DataManager { let res = execute!( &conn, "UPDATE notifications SET read = $1 WHERE id = $2", - &[&(if new_read { 1 } else { 0 }).to_string(), &id.to_string()] + params![&(if new_read { 1 } else { 0 } as i64), &(id as i64)] ); if let Err(e) = res { diff --git a/crates/core/src/database/posts.rs b/crates/core/src/database/posts.rs index 387494d..64092df 100644 --- a/crates/core/src/database/posts.rs +++ b/crates/core/src/database/posts.rs @@ -11,7 +11,7 @@ use crate::model::{ communities::{Community, CommunityWriteAccess, Post, PostContext}, permissions::FinePermission, }; -use crate::{auto_method, execute, get, query_row, query_rows}; +use crate::{auto_method, execute, get, query_row, query_rows, params}; #[cfg(feature = "sqlite")] use rusqlite::Row; @@ -174,7 +174,7 @@ impl DataManager { let res = query_rows!( &conn, - "SELECT * FROM posts WHERE community = $1 AND replying_to IS NULL ORDER BY created DESC LIMIT $2 OFFSET $3", + "SELECT * FROM posts WHERE community = $1 AND replying_to = 0 AND NOT context LIKE '%\"is_pinned\":true%' ORDER BY created DESC LIMIT $2 OFFSET $3", &[&(id as i64), &(batch as i64), &((page * batch) as i64)], |x| { Self::get_post_from_row(x) } ); @@ -223,7 +223,7 @@ impl DataManager { let res = query_rows!( &conn, - "SELECT * FROM posts ORDER BY likes DESC, created ASC LIMIT $2 OFFSET $3", + "SELECT * FROM posts ORDER BY likes DESC, created ASC LIMIT $1 OFFSET $2", &[&(batch as i64), &((page * batch) as i64)], |x| { Self::get_post_from_row(x) } ); @@ -395,21 +395,21 @@ impl DataManager { let res = execute!( &conn, "INSERT INTO posts VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)", - &[ - &Some(data.id.to_string().as_str()), - &Some(data.created.to_string().as_str()), - &Some(&data.content), - &Some(data.owner.to_string().as_str()), - &Some(data.community.to_string().as_str()), - &Some(&serde_json::to_string(&data.context).unwrap()), + params![ + &(data.id as i64), + &(data.created as i64), + &data.content, + &(data.owner as i64), + &(data.community as i64), + &serde_json::to_string(&data.context).unwrap(), &if replying_to_id != "0" { - Some(replying_to_id.as_str()) + replying_to_id.parse::().unwrap() } else { - None + 0 as i64 }, - &Some(0.to_string().as_str()), - &Some(0.to_string().as_str()), - &Some(0.to_string().as_str()) + &(0 as i64), + &(0 as i64), + &(0 as i64) ] ); @@ -472,7 +472,7 @@ impl DataManager { Err(e) => return Err(Error::DatabaseConnection(e.to_string())), }; - let res = execute!(&conn, "DELETE FROM posts WHERE id = $1", &[&id.to_string()]); + let res = execute!(&conn, "DELETE FROM posts WHERE id = $1", &[&(id as i64)]); if let Err(e) = res { return Err(Error::DatabaseError(e.to_string())); @@ -538,7 +538,7 @@ impl DataManager { let res = execute!( &conn, "UPDATE posts SET context = $1 WHERE id = $2", - &[&serde_json::to_string(&x).unwrap(), &id.to_string()] + params![&serde_json::to_string(&x).unwrap(), &(id as i64)] ); if let Err(e) = res { diff --git a/crates/core/src/database/reactions.rs b/crates/core/src/database/reactions.rs index 12da79b..e376656 100644 --- a/crates/core/src/database/reactions.rs +++ b/crates/core/src/database/reactions.rs @@ -6,7 +6,7 @@ use crate::model::{ permissions::FinePermission, reactions::{AssetType, Reaction}, }; -use crate::{auto_method, execute, get, query_row}; +use crate::{auto_method, execute, get, query_row, params}; #[cfg(feature = "sqlite")] use rusqlite::Row; @@ -26,7 +26,11 @@ impl DataManager { owner: get!(x->2(i64)) as usize, asset: get!(x->3(i64)) as usize, asset_type: serde_json::from_str(&get!(x->4(String))).unwrap(), - is_like: if get!(x->5(i8)) == 1 { true } else { false }, + is_like: if get!(x->5(i64)) as i8 == 1 { + true + } else { + false + }, } } @@ -70,13 +74,13 @@ impl DataManager { let res = execute!( &conn, "INSERT INTO reactions VALUES ($1, $2, $3, $4, $5, $6)", - &[ - &data.id.to_string().as_str(), - &data.created.to_string().as_str(), - &data.owner.to_string().as_str(), - &data.asset.to_string().as_str(), + params![ + &(data.id as i64), + &(data.created as i64), + &(data.owner as i64), + &(data.asset as i64), &serde_json::to_string(&data.asset_type).unwrap().as_str(), - &(if data.is_like { 1 } else { 0 }).to_string().as_str() + &(if data.is_like { 1 } else { 0 } as i64) ] ); @@ -170,7 +174,7 @@ impl DataManager { let res = execute!( &conn, "DELETE FROM reactions WHERE id = $1", - &[&id.to_string()] + &[&(id as i64)] ); if let Err(e) = res { diff --git a/crates/core/src/database/reports.rs b/crates/core/src/database/reports.rs index 5a0db02..d657d99 100644 --- a/crates/core/src/database/reports.rs +++ b/crates/core/src/database/reports.rs @@ -2,7 +2,7 @@ use super::*; use crate::cache::Cache; use crate::model::moderation::AuditLogEntry; use crate::model::{Error, Result, auth::User, moderation::Report, permissions::FinePermission}; -use crate::{auto_method, execute, get, query_row, query_rows}; +use crate::{auto_method, execute, get, query_row, query_rows, params}; #[cfg(feature = "sqlite")] use rusqlite::Row; @@ -26,7 +26,7 @@ impl DataManager { } } - auto_method!(get_report_by_id(usize)@get_report_from_row -> "SELECT * FROM reports WHERE id = $1" --name="report" --returns=Report --cache-key-tmpl="atto.reports:{}"); + auto_method!(get_report_by_id(usize as i64)@get_report_from_row -> "SELECT * FROM reports WHERE id = $1" --name="report" --returns=Report --cache-key-tmpl="atto.reports:{}"); /// Get all reports (paginated). /// @@ -66,12 +66,12 @@ impl DataManager { let res = execute!( &conn, "INSERT INTO reports VALUES ($1, $2, $3, $4, $5, $6)", - &[ - &data.id.to_string().as_str(), - &data.created.to_string().as_str(), - &data.owner.to_string().as_str(), + params![ + &(data.id as i64), + &(data.created as i64), + &(data.owner as i64), &data.content.as_str(), - &data.asset.to_string().as_str(), + &(data.asset as i64), &serde_json::to_string(&data.asset_type).unwrap().as_str(), ] ); @@ -94,11 +94,7 @@ impl DataManager { Err(e) => return Err(Error::DatabaseConnection(e.to_string())), }; - let res = execute!( - &conn, - "DELETE FROM reports WHERE id = $1", - &[&id.to_string()] - ); + let res = execute!(&conn, "DELETE FROM reports WHERE id = $1", &[&(id as i64)]); if let Err(e) = res { return Err(Error::DatabaseError(e.to_string())); diff --git a/crates/core/src/database/userblocks.rs b/crates/core/src/database/userblocks.rs index 2bc7ef6..77edf25 100644 --- a/crates/core/src/database/userblocks.rs +++ b/crates/core/src/database/userblocks.rs @@ -1,7 +1,7 @@ use super::*; use crate::cache::Cache; use crate::model::{Error, Result, auth::User, auth::UserBlock, permissions::FinePermission}; -use crate::{auto_method, execute, get, query_row}; +use crate::{auto_method, execute, get, query_row, params}; #[cfg(feature = "sqlite")] use rusqlite::Row; @@ -88,11 +88,11 @@ impl DataManager { let res = execute!( &conn, "INSERT INTO userblocks VALUES ($1, $2, $3, $4)", - &[ - &data.id.to_string().as_str(), - &data.created.to_string().as_str(), - &data.initiator.to_string().as_str(), - &data.receiver.to_string().as_str() + params![ + &(data.id as i64), + &(data.created as i64), + &(data.initiator as i64), + &(data.receiver as i64) ] ); @@ -122,7 +122,7 @@ impl DataManager { let res = execute!( &conn, "DELETE FROM userblocks WHERE id = $1", - &[&id.to_string()] + &[&(id as i64)] ); if let Err(e) = res { diff --git a/crates/core/src/database/userfollows.rs b/crates/core/src/database/userfollows.rs index 462259f..3465d1a 100644 --- a/crates/core/src/database/userfollows.rs +++ b/crates/core/src/database/userfollows.rs @@ -1,7 +1,7 @@ use super::*; use crate::cache::Cache; use crate::model::{Error, Result, auth::User, auth::UserFollow, permissions::FinePermission}; -use crate::{auto_method, execute, get, query_row, query_rows}; +use crate::{auto_method, execute, get, query_row, query_rows, params}; #[cfg(feature = "sqlite")] use rusqlite::Row; @@ -180,11 +180,11 @@ impl DataManager { let res = execute!( &conn, "INSERT INTO userfollows VALUES ($1, $2, $3, $4)", - &[ - &data.id.to_string().as_str(), - &data.created.to_string().as_str(), - &data.initiator.to_string().as_str(), - &data.receiver.to_string().as_str() + params![ + &(data.id as i64), + &(data.created as i64), + &(data.initiator as i64), + &(data.receiver as i64) ] ); @@ -220,7 +220,7 @@ impl DataManager { let res = execute!( &conn, "DELETE FROM userfollows WHERE id = $1", - &[&id.to_string()] + &[&(id as i64)] ); if let Err(e) = res { diff --git a/example/tetratto.toml b/example/tetratto.toml index aa49893..3f74b30 100644 --- a/example/tetratto.toml +++ b/example/tetratto.toml @@ -1,12 +1,40 @@ name = "Tetratto" +description = "🐇 tetratto!" +color = "#c9b1bc" port = 4118 +banned_hosts = [] +no_track = [] +banned_usernames = [ + "admin", + "owner", + "moderator", + "api", + "communities", + "notifs", + "notification", + "post", + "void", +] [security] registration_enabled = true +real_ip_header = "CF-Connecting-IP" [dirs] templates = "html" assets = "public" +media = "media" +icons = "icons" + +[database] +name = "tetratto" +url = "localhost:5432" +user = "hkau" +password = "postgres" + +[policies] +terms_of_service = "/public/tos.html" +privacy = "/public/privacy.html" [turnstile] site_key = "1x00000000000000000000AA"