a whole lot of preem User/Session/Auth work
This commit is contained in:
@@ -6,13 +6,17 @@ CREATE TABLE users (
|
||||
);
|
||||
CREATE TABLE sessions (
|
||||
id BLOB NOT NULL UNIQUE PRIMARY KEY, -- UUIDv7 as bytes
|
||||
token TEXT NOT NULL UNIQUE,
|
||||
token BLOB NOT NULL UNIQUE,
|
||||
user_id BLOB NOT NULL REFERENCES users(id), -- UUIDv7 bytes (userID)
|
||||
issued TEXT NOT NULL, -- RFC3339 into DateTime<Utc>
|
||||
expiry TEXT NOT NULL, -- RFC3339 into DateTime<Utc>
|
||||
revoked INTEGER NOT NULL DEFAULT 0, -- bool (int 0 or int 1)
|
||||
revoked_at TEXT DEFAULT NULL, -- RFC3339 into DateTime<Utc>
|
||||
revoked_by BLOB DEFAULT NULL REFERENCES users(id) -- UUIDv7 bytes (userID)
|
||||
|
||||
CHECK(
|
||||
(revoked = 0 AND revoked_at IS NULL AND revoked_by IS NULL) OR
|
||||
(revoked = 1 AND revoked_at IS NOT NULL AND revoked_by IS NOT NULL)
|
||||
)
|
||||
);
|
||||
CREATE INDEX sessions_by_userid ON sessions(user_id);
|
||||
|
||||
|
||||
@@ -1 +1,57 @@
|
||||
use std::{env, error::Error, sync::LazyLock};
|
||||
|
||||
use rusqlite::{Connection, OptionalExtension};
|
||||
|
||||
macro_rules! migration {
|
||||
($name:literal) => {
|
||||
($name, include_str!(concat!("./migrations/", $name, ".sql")))
|
||||
};
|
||||
}
|
||||
const MIGRATIONS: &[(&str, &str)] = &[migration!("2026-02-20--01")];
|
||||
|
||||
pub static DB_URL: LazyLock<String> =
|
||||
LazyLock::new(|| env::var("DATABASE_URL").expect("DATABASE_URL is not set"));
|
||||
|
||||
const PERSISTENT_PRAGMAS: &[&str] = &["PRAGMA journal_mode = WAL"];
|
||||
const CONNECTION_PRAGMAS: &[&str] = &["PRAGMA foreign_keys = ON", "PRAGMA busy_timeout = 5000"];
|
||||
const TABLE_MIGRATIONS: &str = r#"
|
||||
CREATE TABLE IF NOT EXISTS migrations (
|
||||
id TEXT PRIMARY KEY,
|
||||
time INTEGER DEFAULT (unixepoch())
|
||||
);
|
||||
"#;
|
||||
|
||||
pub fn conn() -> Result<Connection, rusqlite::Error> {
|
||||
let conn = Connection::open(&*DB_URL)?;
|
||||
for pragma in CONNECTION_PRAGMAS {
|
||||
conn.query_row(*pragma, (), |_| Ok(())).optional()?;
|
||||
}
|
||||
Ok(conn)
|
||||
}
|
||||
|
||||
pub fn migrations() -> Result<(), Box<dyn Error>> {
|
||||
let conn = Connection::open(&*DB_URL)?;
|
||||
for pragma in PERSISTENT_PRAGMAS {
|
||||
conn.query_row(*pragma, (), |_| Ok(()))?;
|
||||
}
|
||||
conn.execute(TABLE_MIGRATIONS, ())?;
|
||||
|
||||
let mut changes = false;
|
||||
for (key, sql) in MIGRATIONS {
|
||||
let mut statement = conn.prepare("SELECT id, time FROM migrations WHERE id = ?1")?;
|
||||
let query = statement.query_one([key], |_| Ok(())).optional()?;
|
||||
if query.is_some() {
|
||||
continue;
|
||||
}
|
||||
changes = true;
|
||||
println!("Applying migration {key}...");
|
||||
|
||||
conn.execute_batch(sql)?;
|
||||
conn.execute("INSERT INTO migrations(id) VALUES (?1)", &[key])?;
|
||||
}
|
||||
|
||||
if changes {
|
||||
println!("Migrations applied.")
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user