sqlx, db setup, users migration

This commit is contained in:
Butter 2026-04-07 21:41:39 -04:00
parent 8461e542de
commit e2a49963e5
7 changed files with 1632 additions and 14 deletions

1
.env Normal file
View File

@ -0,0 +1 @@
DATABASE_URL=sqlite:./sarmentine.db

1
.gitignore vendored
View File

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

1585
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -9,6 +9,7 @@ tokio = { version = "1", features = ["full"] }
askama = { version = "0.12", features = ["with-axum"] } askama = { version = "0.12", features = ["with-axum"] }
askama_axum = "0.4" askama_axum = "0.4"
tower-http = { version = "0.5", features = ["fs"] } tower-http = { version = "0.5", features = ["fs"] }
sqlx = { version = "0.7", features = ["runtime-tokio", "sqlite", "migrate"] }
dotenvy = "0.15"
# Error handling # Error handling
anyhow = "1" anyhow = "1"

10
migrations/0001_users.sql Normal file
View File

@ -0,0 +1,10 @@
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL UNIQUE COLLATE NOCASE,
email TEXT NOT NULL UNIQUE COLLATE NOCASE,
password TEXT NOT NULL,
bio TEXT,
avatar_url TEXT,
role TEXT NOT NULL DEFAULT 'member' CHECK(role IN ('member', 'admin')),
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);

29
src/db.rs Normal file
View File

@ -0,0 +1,29 @@
use sqlx::{sqlite::SqlitePoolOptions, SqlitePool};
pub async fn connect(database_url: &str) -> SqlitePool {
let pool = SqlitePoolOptions::new()
.max_connections(5)
.connect(database_url)
.await
.expect("failed to connect to database");
sqlx::migrate!("./migrations")
.run(&pool)
.await
.expect("migrations failed");
// WAL mode: reads and writes can happen concurrently
sqlx::query("PRAGMA journal_mode=WAL;")
.execute(&pool)
.await
.expect("failed to set WAL mode");
// SQLite ignores foreign keys by default -- turn them on
sqlx::query("PRAGMA foreign_keys=ON;")
.execute(&pool)
.await
.expect("failed to enable foreign keys");
println!("database ready");
pool
}

View File

@ -1,10 +1,11 @@
mod db;
use askama::Template; use askama::Template;
use askama_axum::IntoResponse; use askama_axum::IntoResponse;
use axum::{routing::get, Router}; use axum::{routing::get, Router};
use sqlx::SqlitePool;
use tower_http::services::ServeDir; use tower_http::services::ServeDir;
// Represents the currently logged-in user, passed into every template.
// None = logged out.
pub struct CurrentUser { pub struct CurrentUser {
pub username: String, pub username: String,
} }
@ -19,15 +20,23 @@ struct IndexTemplate {
async fn index() -> impl IntoResponse { async fn index() -> impl IntoResponse {
IndexTemplate { IndexTemplate {
title: "home".into(), title: "home".into(),
current_user: None, // no auth yet current_user: None,
} }
} }
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
dotenvy::dotenv().ok();
let database_url = std::env::var("DATABASE_URL")
.unwrap_or_else(|_| "sqlite:./sarmentine.db".into());
let pool: SqlitePool = db::connect(&database_url).await;
let app = Router::new() let app = Router::new()
.route("/", get(index)) .route("/", get(index))
.nest_service("/static", ServeDir::new("static")); .nest_service("/static", ServeDir::new("static"))
.with_state(pool);
let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") let listener = tokio::net::TcpListener::bind("127.0.0.1:3000")
.await .await