simplelink/src/models.rs

155 lines
3.8 KiB
Rust

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<Postgres>),
Sqlite(Pool<Sqlite>),
}
impl DatabasePool {
pub async fn begin(&self) -> Result<Box<dyn std::any::Any + Send>> {
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<T>(&self, pg_query: &str, sqlite_query: &str) -> Result<Option<T>>
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<R>
where
F: for<'c> Fn(&'c mut Transaction<'_, Postgres>) -> BoxFuture<'c, Result<R>>
+ for<'c> Fn(&'c mut Transaction<'_, Sqlite>) -> BoxFuture<'c, Result<R>>
+ 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<String>,
pub custom_code: Option<String>,
}
#[derive(Serialize, FromRow)]
pub struct Link {
pub id: i32,
pub user_id: Option<i32>,
pub original_url: String,
pub short_code: String,
pub created_at: chrono::DateTime<chrono::Utc>,
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<String>,
}
#[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,
}