fix: postgres

This commit is contained in:
trisua 2025-04-03 15:07:57 -04:00
parent dcd5f359c6
commit 27d7c2f4b5
29 changed files with 298 additions and 224 deletions

View file

@ -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"]);

View file

@ -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() {

View file

@ -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 {

View file

@ -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 {

View file

@ -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 {

View file

@ -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()));

View file

@ -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 {

View file

@ -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<T> = std::result::Result<T, tokio_postgres::Error>;
pub type Connection<'a> = PooledConnection<'a, PostgresConnectionManager<NoTls>>;
@ -35,13 +36,14 @@ impl DataManager {
/// Create a new [`DataManager`] (and init database).
pub async fn new(config: Config) -> Result<Self> {
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),+]
};
}

View file

@ -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
)

View file

@ -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
)

View file

@ -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
)

View file

@ -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
)

View file

@ -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
)

View file

@ -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
)

View file

@ -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
)

View file

@ -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
)

View file

@ -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
)

View file

@ -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
)

View file

@ -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
)

View file

@ -86,3 +86,13 @@ macro_rules! execute {
$conn.prepare($sql).unwrap().execute(())
};
}
#[macro_export]
macro_rules! params {
() => {
rusqlite::params![]
};
($($params:expr),+ $(,)?) => {
rusqlite::params![$($params),+]
};
}

View file

@ -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)
]
);

View file

@ -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 {

View file

@ -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 {

View file

@ -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::<i64>().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 {

View file

@ -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 {

View file

@ -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()));

View file

@ -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 {

View file

@ -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 {

View file

@ -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"