use anyhow::Result; use futures::future::BoxFuture; use serde::{Deserialize, Serialize}; use sqlx::postgres::PgRow; use sqlx::sqlite::SqliteRow; use sqlx::FromRow; use sqlx::Pool; use sqlx::Postgres; use sqlx::Sqlite; use sqlx::Transaction; use std::time::{SystemTime, UNIX_EPOCH}; #[derive(Clone)] pub enum DatabasePool { Postgres(Pool), Sqlite(Pool), } impl DatabasePool { pub async fn begin(&self) -> Result> { match self { DatabasePool::Postgres(pool) => Ok(Box::new(pool.begin().await?)), DatabasePool::Sqlite(pool) => Ok(Box::new(pool.begin().await?)), } } pub async fn fetch_optional(&self, pg_query: &str, sqlite_query: &str) -> Result> where T: for<'r> FromRow<'r, PgRow> + for<'r> FromRow<'r, SqliteRow> + Send + Sync + Unpin, { match self { DatabasePool::Postgres(pool) => { Ok(sqlx::query_as(pg_query).fetch_optional(pool).await?) } DatabasePool::Sqlite(pool) => { Ok(sqlx::query_as(sqlite_query).fetch_optional(pool).await?) } } } pub async fn execute(&self, pg_query: &str, sqlite_query: &str) -> Result<()> { match self { DatabasePool::Postgres(pool) => { sqlx::query(pg_query).execute(pool).await?; Ok(()) } DatabasePool::Sqlite(pool) => { sqlx::query(sqlite_query).execute(pool).await?; Ok(()) } } } pub async fn transaction<'a, F, R>(&'a self, f: F) -> Result where F: for<'c> Fn(&'c mut Transaction<'_, Postgres>) -> BoxFuture<'c, Result> + for<'c> Fn(&'c mut Transaction<'_, Sqlite>) -> BoxFuture<'c, Result> + Copy, R: Send + 'static, { match self { DatabasePool::Postgres(pool) => { let mut tx = pool.begin().await?; let result = f(&mut tx).await?; tx.commit().await?; Ok(result) } DatabasePool::Sqlite(pool) => { let mut tx = pool.begin().await?; let result = f(&mut tx).await?; tx.commit().await?; Ok(result) } } } } #[derive(Debug, Serialize, Deserialize)] pub struct Claims { pub sub: i32, // user id pub exp: usize, } impl Claims { pub fn new(user_id: i32) -> Self { let exp = SystemTime::now() .duration_since(UNIX_EPOCH) .unwrap() .as_secs() as usize + 24 * 60 * 60; // 24 hours from now Self { sub: user_id, exp } } } #[derive(Deserialize)] pub struct CreateLink { pub url: String, pub source: Option, pub custom_code: Option, } #[derive(Serialize, FromRow)] pub struct Link { pub id: i32, pub user_id: Option, pub original_url: String, pub short_code: String, pub created_at: chrono::DateTime, pub clicks: i64, } #[derive(Deserialize)] pub struct LoginRequest { pub email: String, pub password: String, } #[derive(Deserialize)] pub struct RegisterRequest { pub email: String, pub password: String, pub admin_token: Option, } #[derive(Serialize)] pub struct AuthResponse { pub token: String, pub user: UserResponse, } #[derive(Serialize)] pub struct UserResponse { pub id: i32, pub email: String, } #[derive(FromRow)] pub struct User { pub id: i32, pub email: String, pub password_hash: String, } #[derive(sqlx::FromRow, Serialize)] pub struct ClickStats { pub date: String, pub clicks: i64, } #[derive(sqlx::FromRow, Serialize)] pub struct SourceStats { pub source: String, pub count: i64, }