added sqlx offline cache and switched to query_as!

This commit is contained in:
Butter 2026-05-04 12:10:33 -04:00
parent e0369b50dd
commit 8f2cb6eda6
9 changed files with 1682 additions and 28 deletions

2
.env
View File

@ -1,2 +0,0 @@
DATABASE_URL=sqlite:./sarmentine.db
SESSION_SECRET=gzC0+m1ol9sPHaZpEU/AwIQVA9STkmxrWK6HhCI7ovlNdVzQqR0sg8VHQCRxURQFmVNEiQvJ8cx7G7nNd3b4jw

2
.gitignore vendored
View File

@ -1,2 +1,4 @@
/target
sarmentine.db*
.env
.env.*

View File

@ -0,0 +1,62 @@
{
"db_name": "SQLite",
"query": "SELECT id AS \"id!\", username, email, password, bio, avatar_url, role, created_at\n FROM users WHERE email = ?",
"describe": {
"columns": [
{
"name": "id!",
"ordinal": 0,
"type_info": "Int64"
},
{
"name": "username",
"ordinal": 1,
"type_info": "Text"
},
{
"name": "email",
"ordinal": 2,
"type_info": "Text"
},
{
"name": "password",
"ordinal": 3,
"type_info": "Text"
},
{
"name": "bio",
"ordinal": 4,
"type_info": "Text"
},
{
"name": "avatar_url",
"ordinal": 5,
"type_info": "Text"
},
{
"name": "role",
"ordinal": 6,
"type_info": "Text"
},
{
"name": "created_at",
"ordinal": 7,
"type_info": "Datetime"
}
],
"parameters": {
"Right": 1
},
"nullable": [
true,
false,
false,
false,
true,
true,
false,
false
]
},
"hash": "59b62edeaea0297e51ae3d1d973f46cc1a0786ad5f86bf5f539d12f728136f1d"
}

View File

@ -0,0 +1,62 @@
{
"db_name": "SQLite",
"query": "SELECT id AS \"id!\", username, email, password, bio, avatar_url, role, created_at\n FROM users WHERE id = ?",
"describe": {
"columns": [
{
"name": "id!",
"ordinal": 0,
"type_info": "Int64"
},
{
"name": "username",
"ordinal": 1,
"type_info": "Text"
},
{
"name": "email",
"ordinal": 2,
"type_info": "Text"
},
{
"name": "password",
"ordinal": 3,
"type_info": "Text"
},
{
"name": "bio",
"ordinal": 4,
"type_info": "Text"
},
{
"name": "avatar_url",
"ordinal": 5,
"type_info": "Text"
},
{
"name": "role",
"ordinal": 6,
"type_info": "Text"
},
{
"name": "created_at",
"ordinal": 7,
"type_info": "Datetime"
}
],
"parameters": {
"Right": 1
},
"nullable": [
false,
false,
false,
false,
true,
true,
false,
false
]
},
"hash": "9496103101618f59b785c0c8d2cee4f6053055cb06db77dc8033c7e31d503a77"
}

View File

@ -0,0 +1,62 @@
{
"db_name": "SQLite",
"query": "SELECT id AS \"id!\", username, email, password, bio, avatar_url, role, created_at\n FROM users WHERE username = ?",
"describe": {
"columns": [
{
"name": "id!",
"ordinal": 0,
"type_info": "Int64"
},
{
"name": "username",
"ordinal": 1,
"type_info": "Text"
},
{
"name": "email",
"ordinal": 2,
"type_info": "Text"
},
{
"name": "password",
"ordinal": 3,
"type_info": "Text"
},
{
"name": "bio",
"ordinal": 4,
"type_info": "Text"
},
{
"name": "avatar_url",
"ordinal": 5,
"type_info": "Text"
},
{
"name": "role",
"ordinal": 6,
"type_info": "Text"
},
{
"name": "created_at",
"ordinal": 7,
"type_info": "Datetime"
}
],
"parameters": {
"Right": 1
},
"nullable": [
true,
false,
false,
false,
true,
true,
false,
false
]
},
"hash": "d12d0743a4bbe18465adb69102e3f7dc70ac567c7610cc9aa6f9c6bd84be6a6b"
}

1475
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -19,3 +19,6 @@ serde = { version = "1", features = ["derive"] }
chrono = { version = "0.4", features = ["serde"] }
# Error handling
anyhow = "1"
[dev-dependencies]
cargo-generate-rpm = "0.20"

View File

@ -51,7 +51,7 @@ async fn index(
async fn main() {
dotenvy::dotenv().ok();
let database_url = std::env::var("DATABASE_URL")
let database_url = std::env::var("SARMENTINE_DATABASE_URL")
.unwrap_or_else(|_| "sqlite:./sarmentine.db".into());
let pool: SqlitePool = db::connect(&database_url).await;
@ -59,7 +59,7 @@ async fn main() {
let session_store = SqliteStore::new(pool.clone());
session_store.migrate().await.expect("session store migration failed");
let session_secret = std::env::var("SESSION_SECRET")
let session_secret = std::env::var("SARMENTINE_SESSION_SECRET")
.unwrap_or_else(|_| "change-me-in-production-use-a-long-random-string!!".into());
let session_layer = SessionManagerLayer::new(session_store)
@ -69,6 +69,9 @@ async fn main() {
session_secret.as_bytes(),
));
let static_dir = std::env::var("SARMENTINE_STATIC_DIR")
.unwrap_or_else(|_| "static".into());
let app = Router::new()
.route("/", get(index))
.route("/auth/register", get(register_page))
@ -76,7 +79,7 @@ async fn main() {
.route("/auth/login", get(login_page))
.route("/auth/login", post(login_submit))
.route("/auth/logout", post(logout))
.nest_service("/static", ServeDir::new("static"))
.nest_service("/static", ServeDir::new(&static_dir))
.layer(session_layer)
.with_state(pool);

View File

@ -1,8 +1,8 @@
use chrono::NaiveDateTime;
use serde::{Deserialize, Serialize};
use sqlx::{FromRow, SqlitePool};
use sqlx::SqlitePool;
#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct User {
pub id: i64,
pub username: String,
@ -17,31 +17,34 @@ pub struct User {
impl User {
pub async fn find_by_id(pool: &SqlitePool, id: i64) -> sqlx::Result<Option<User>> {
sqlx::query_as(
"SELECT id, username, email, password, bio, avatar_url, role, created_at
FROM users WHERE id = ?"
sqlx::query_as!(
User,
r#"SELECT id AS "id!", username, email, password, bio, avatar_url, role, created_at
FROM users WHERE id = ?"#,
id
)
.bind(id)
.fetch_optional(pool)
.await
}
pub async fn find_by_username(pool: &SqlitePool, username: &str) -> sqlx::Result<Option<User>> {
sqlx::query_as(
"SELECT id, username, email, password, bio, avatar_url, role, created_at
FROM users WHERE username = ?"
sqlx::query_as!(
User,
r#"SELECT id AS "id!", username, email, password, bio, avatar_url, role, created_at
FROM users WHERE username = ?"#,
username
)
.bind(username)
.fetch_optional(pool)
.await
}
pub async fn find_by_email(pool: &SqlitePool, email: &str) -> sqlx::Result<Option<User>> {
sqlx::query_as(
"SELECT id, username, email, password, bio, avatar_url, role, created_at
FROM users WHERE email = ?"
sqlx::query_as!(
User,
r#"SELECT id AS "id!", username, email, password, bio, avatar_url, role, created_at
FROM users WHERE email = ?"#,
email
)
.bind(email)
.fetch_optional(pool)
.await
}