refactor(db): moved from mariadb to postgresql

This commit is contained in:
antifallobst 2023-08-17 21:09:30 +02:00
parent 9c3a3b8e26
commit 51c8a7f7fa
Signed by: antifallobst
GPG Key ID: 2B4F402172791BAF
7 changed files with 67 additions and 68 deletions

2
.env
View File

@ -1 +1 @@
DATABASE_URL="mysql://nerdcult_api:test@localhost/NC_Accounts"
DATABASE_URL="postgres://postgres:postgres@localhost/test-db"

View File

@ -17,7 +17,7 @@ env_logger = "0.10"
log = "0.4"
clap = { version = "4.3.21", features = ["derive"] }
actix-web-httpauth = "0.8.0"
sqlx = { version = "0.7.1", features = ["runtime-tokio", "mysql", "chrono"] }
sqlx = { version = "0.7.1", features = ["runtime-tokio", "postgres", "chrono"] }
uuid = { version = "1.4.1", features = ["v4"] }
chrono = "0.4"
mail-send = "0.4.0"

View File

@ -3,11 +3,11 @@ use pbkdf2::{
password_hash::{rand_core::OsRng, PasswordHash, PasswordHasher, PasswordVerifier, SaltString},
Pbkdf2,
};
use sqlx::{mysql::MySqlPool, types::chrono as sqlx_chrono};
use sqlx::{postgres::PgPool, types::chrono as sqlx_chrono};
#[derive(Debug)]
pub struct Account {
pub id: u64,
pub id: i64,
pub username: String,
pub email: String,
pub salt: String,
@ -19,7 +19,7 @@ pub struct Account {
impl Account {
/// This doesn't check if an account with that name is already existing!
pub async fn new(
pool: &MySqlPool,
pool: &PgPool,
username: &String,
email: &String,
password: &String,
@ -33,7 +33,7 @@ impl Account {
let joined = sqlx_chrono::Utc::now().naive_utc();
sqlx::query!(
r#"INSERT INTO Accounts (username, email, salt, password, joined, verified) VALUES (?, ?, ?, ?, ?, false);"#,
r#"INSERT INTO Accounts (username, email, salt, password, joined, verified) VALUES ($1, $2, $3, $4, $5, false);"#,
username,
email,
salt.to_string(),
@ -51,10 +51,10 @@ impl Account {
}
}
pub async fn from_username(pool: &MySqlPool, username: &String) -> Result<Option<Self>> {
pub async fn from_username(pool: &PgPool, username: &String) -> Result<Option<Self>> {
match sqlx::query_as!(
Self,
r#"SELECT id, username, email, salt, password, joined, verified as `verified: bool` FROM Accounts WHERE username = ?;"#,
r#"SELECT id, username, email, salt, password, joined, verified as "verified!: bool" FROM Accounts WHERE username = $1;"#,
username
)
.fetch_one(pool)
@ -66,10 +66,10 @@ impl Account {
}
}
pub async fn from_id(pool: &MySqlPool, id: u64) -> Result<Option<Self>> {
pub async fn from_id(pool: &PgPool, id: i64) -> Result<Option<Self>> {
match sqlx::query_as!(
Self,
r#"SELECT id, username, email, salt, password, joined, verified as `verified: bool` FROM Accounts WHERE id = ?;"#,
r#"SELECT id, username, email, salt, password, joined, verified as "verified!: bool" FROM Accounts WHERE id = $1;"#,
id
)
.fetch_one(pool)
@ -81,10 +81,10 @@ impl Account {
}
}
pub async fn from_email(pool: &MySqlPool, email: &String) -> Result<Option<Self>> {
pub async fn from_email(pool: &PgPool, email: &String) -> Result<Option<Self>> {
match sqlx::query_as!(
Self,
r#"SELECT id, username, email, salt, password, joined, verified as `verified: bool` FROM Accounts WHERE email = ?;"#,
r#"SELECT id, username, email, salt, password, joined, verified as "verified!: bool" FROM Accounts WHERE email = $1;"#,
email
)
.fetch_one(pool)

View File

@ -6,7 +6,7 @@ use crate::{
use anyhow::Result;
use log::info;
use mail_send::{mail_builder::MessageBuilder, SmtpClientBuilder};
use sqlx::MySqlPool;
use sqlx::PgPool;
fn is_sql_injection(string: &String) -> bool {
match libinjection::sqli(string) {
@ -26,7 +26,7 @@ impl AlphaExt for String {
}
pub async fn register(
pool: &MySqlPool,
pool: &PgPool,
request: data::RegisterRequest,
) -> Result<data::RegisterResponse> {
if is_sql_injection(&request.username) || is_sql_injection(&request.email) {
@ -84,10 +84,7 @@ pub async fn register(
Ok(data::RegisterResponse::Success)
}
pub async fn verify(
pool: &MySqlPool,
request: data::VerifyRequest,
) -> Result<data::VerifyResponse> {
pub async fn verify(pool: &PgPool, request: data::VerifyRequest) -> Result<data::VerifyResponse> {
if !request.token.is_alpha() {
return Ok(data::VerifyResponse::Blocked);
}
@ -103,7 +100,7 @@ pub async fn verify(
}
pub async fn authenticate(
pool: &MySqlPool,
pool: &PgPool,
request: data::AuthenticateRequest,
) -> Result<data::AuthenticateResponse> {
if is_sql_injection(&request.username) {
@ -130,7 +127,7 @@ pub async fn authenticate(
))
}
pub async fn delete(pool: &MySqlPool, auth: String) -> Result<data::DeleteResponse> {
pub async fn delete(pool: &PgPool, auth: String) -> Result<data::DeleteResponse> {
if !auth.is_alpha() {
return Ok(data::DeleteResponse::Blocked);
}
@ -141,13 +138,13 @@ pub async fn delete(pool: &MySqlPool, auth: String) -> Result<data::DeleteRespon
};
sqlx::query!(
r#"DELETE FROM AuthTokens WHERE account = ?;"#,
r#"DELETE FROM AuthTokens WHERE account = $1;"#,
token.account
)
.execute(pool)
.await?;
sqlx::query!(r#"DELETE FROM Accounts WHERE id = ?;"#, token.account)
sqlx::query!(r#"DELETE FROM Accounts WHERE id = $1;"#, token.account)
.execute(pool)
.await?;
@ -155,7 +152,7 @@ pub async fn delete(pool: &MySqlPool, auth: String) -> Result<data::DeleteRespon
}
pub async fn token_delete(
pool: &MySqlPool,
pool: &PgPool,
auth: String,
request: data::TokenDeleteRequest,
) -> Result<data::TokenDeleteResponse> {
@ -184,7 +181,7 @@ pub async fn token_delete(
Ok(data::TokenDeleteResponse::Success)
}
pub async fn token_list(pool: &MySqlPool, auth: String) -> Result<data::TokenListResponse> {
pub async fn token_list(pool: &PgPool, auth: String) -> Result<data::TokenListResponse> {
if !auth.is_alpha() {
return Ok(data::TokenListResponse::Blocked);
}
@ -196,7 +193,7 @@ pub async fn token_list(pool: &MySqlPool, auth: String) -> Result<data::TokenLis
let list: Vec<(String, i64)> = sqlx::query_as!(
AuthToken,
r#"SELECT * FROM AuthTokens WHERE account = ?;"#,
r#"SELECT * FROM AuthTokens WHERE account = $1;"#,
token.account
)
.fetch_all(pool)
@ -205,5 +202,7 @@ pub async fn token_list(pool: &MySqlPool, auth: String) -> Result<data::TokenLis
.map(|t| (t.token.clone(), t.expire.timestamp()))
.collect();
Ok(data::TokenListResponse::Success(data::TokenListSuccess {tokens: list}))
Ok(data::TokenListResponse::Success(data::TokenListSuccess {
tokens: list,
}))
}

View File

@ -3,25 +3,25 @@ mod account;
use actix_web::{web, App, HttpServer};
use anyhow::Result;
use log::info;
use sqlx::mysql::MySqlPool;
use sqlx::postgres::PgPool;
struct ApiState {
pool: MySqlPool,
pool: PgPool,
}
pub async fn start(port: u16, pool: MySqlPool) -> Result<()> {
pub async fn start(port: u16, pool: PgPool) -> Result<()> {
info!("HTTP server starting on port {} ...", port);
sqlx::query!(
r#"
CREATE TABLE IF NOT EXISTS Accounts (
id INT8 UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE,
username VARCHAR(32) NOT NULL,
email TEXT NOT NULL,
salt VARCHAR(22) NOT NULL,
password VARCHAR(96) NOT NULL,
joined DATETIME NOT NULL,
verified BOOLEAN NOT NULL,
id SERIAL8 NOT NULL,
username VARCHAR(32) NOT NULL,
email TEXT NOT NULL,
salt VARCHAR(22) NOT NULL,
password VARCHAR(96) NOT NULL,
joined TIMESTAMP NOT NULL,
verified BOOLEAN NOT NULL,
PRIMARY KEY(id)
);
"#
@ -32,10 +32,10 @@ pub async fn start(port: u16, pool: MySqlPool) -> Result<()> {
sqlx::query!(
r#"
CREATE TABLE IF NOT EXISTS AuthTokens (
token VARCHAR(32) NOT NULL,
account INT8 UNSIGNED NOT NULL,
expire DATETIME NOT NULL
);
token VARCHAR(32) NOT NULL,
account SERIAL8 NOT NULL,
expire TIMESTAMP NOT NULL
);
"#
)
.execute(&pool)
@ -44,10 +44,10 @@ pub async fn start(port: u16, pool: MySqlPool) -> Result<()> {
sqlx::query!(
r#"
CREATE TABLE IF NOT EXISTS VerificationTokens (
token VARCHAR(32) NOT NULL,
account INT8 UNSIGNED NOT NULL,
expire DATETIME NOT NULL
);
token VARCHAR(32) NOT NULL,
account SERIAL8 NOT NULL,
expire TIMESTAMP NOT NULL
);
"#
)
.execute(&pool)

View File

@ -5,7 +5,7 @@ mod tokens;
use anyhow::Result;
use clap::Parser;
use log::info;
use sqlx::mysql::MySqlPool;
use sqlx::postgres::PgPool;
#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
@ -28,7 +28,7 @@ async fn main() -> Result<()> {
port = p;
}
let pool = MySqlPool::connect(&std::env::var("DATABASE_URL").map_err(|_| {
let pool = PgPool::connect(&std::env::var("DATABASE_URL").map_err(|_| {
anyhow::Error::msg("Environment variable DATABASE_URL needs to be specified!")
})?)
.await?;

View File

@ -1,17 +1,17 @@
use anyhow::{Error, Result};
use sqlx::{mysql::MySqlPool, types::chrono as sqlx_chrono};
use sqlx::{postgres::PgPool, types::chrono as sqlx_chrono};
#[derive(Debug)]
pub struct AuthToken {
pub token: String,
pub account: u64,
pub account: i64,
pub expire: sqlx_chrono::NaiveDateTime,
}
impl AuthToken {
pub async fn new(
pool: &MySqlPool,
account_id: u64,
pool: &PgPool,
account_id: i64,
lifetime: chrono::Duration,
) -> Result<Self> {
let expire = match sqlx_chrono::Utc::now()
@ -28,7 +28,7 @@ impl AuthToken {
let mut token = Self {
token: uuid::Uuid::new_v4().simple().to_string(),
account: account_id as u64,
account: account_id,
expire,
};
@ -38,7 +38,7 @@ impl AuthToken {
sqlx::query!(
r#"
INSERT INTO AuthTokens (token, account, expire) VALUES (?, ?, ?);
INSERT INTO AuthTokens (token, account, expire) VALUES ($1, $2, $3);
"#,
token.token,
token.account,
@ -50,10 +50,10 @@ impl AuthToken {
Ok(token)
}
pub async fn check(pool: &MySqlPool, alphanumeric_token: &String) -> Result<Option<Self>> {
pub async fn check(pool: &PgPool, alphanumeric_token: &String) -> Result<Option<Self>> {
let query_result = sqlx::query_as!(
Self,
r#"SELECT * FROM AuthTokens WHERE token = ?;"#,
r#"SELECT * FROM AuthTokens WHERE token = $1;"#,
alphanumeric_token
)
.fetch_one(pool)
@ -73,8 +73,8 @@ impl AuthToken {
}
}
pub async fn delete(&self, pool: &MySqlPool) -> Result<()> {
sqlx::query!(r#"DELETE FROM AuthTokens WHERE token = ?;"#, self.token)
pub async fn delete(&self, pool: &PgPool) -> Result<()> {
sqlx::query!(r#"DELETE FROM AuthTokens WHERE token = $1;"#, self.token)
.execute(pool)
.await?;
Ok(())
@ -84,14 +84,14 @@ impl AuthToken {
#[derive(Debug)]
pub struct VerificationToken {
pub token: String,
pub account: u64,
pub account: i64,
pub expire: sqlx_chrono::NaiveDateTime,
}
impl VerificationToken {
pub async fn new(
pool: &MySqlPool,
account_id: u64,
pool: &PgPool,
account_id: i64,
lifetime: chrono::Duration,
) -> Result<Self> {
let expire = match sqlx_chrono::Utc::now()
@ -108,7 +108,7 @@ impl VerificationToken {
let mut token = Self {
token: uuid::Uuid::new_v4().simple().to_string(),
account: account_id as u64,
account: account_id,
expire,
};
@ -121,8 +121,8 @@ impl VerificationToken {
sqlx::query!(
r#"
INSERT INTO VerificationTokens (token, account, expire) VALUES (?, ?, ?);
"#,
INSERT INTO VerificationTokens (token, account, expire) VALUES ($1, $2, $3);
"#,
token.token,
token.account,
token.expire,
@ -133,10 +133,10 @@ impl VerificationToken {
Ok(token)
}
pub async fn check(pool: &MySqlPool, alphanumeric_token: &String) -> Result<Option<Self>> {
pub async fn check(pool: &PgPool, alphanumeric_token: &String) -> Result<Option<Self>> {
let query_result = sqlx::query_as!(
Self,
r#"SELECT * FROM VerificationTokens WHERE token = ?;"#,
r#"SELECT * FROM VerificationTokens WHERE token = $1;"#,
alphanumeric_token
)
.fetch_one(pool)
@ -156,9 +156,9 @@ impl VerificationToken {
}
}
pub async fn delete(&self, pool: &MySqlPool) -> Result<()> {
pub async fn delete(&self, pool: &PgPool) -> Result<()> {
sqlx::query!(
r#"DELETE FROM VerificationTokens WHERE token = ?;"#,
r#"DELETE FROM VerificationTokens WHERE token = $1;"#,
self.token
)
.execute(pool)
@ -166,11 +166,11 @@ impl VerificationToken {
Ok(())
}
pub async fn apply(&self, pool: &MySqlPool) -> Result<()> {
pub async fn apply(&self, pool: &PgPool) -> Result<()> {
self.delete(pool).await?;
sqlx::query!(
r#"UPDATE Accounts SET verified=true WHERE id = ?;"#,
r#"UPDATE Accounts SET verified=true WHERE id = $1;"#,
self.account
)
.execute(pool)