Updated Typst v0.14.2 and added SQLite database support

This commit is contained in:
2026-05-14 17:02:42 -04:00
parent c588867625
commit 61251b3ea7
18 changed files with 410 additions and 290 deletions
+36
View File
@@ -0,0 +1,36 @@
mod postgres;
mod sqlite;
use sqlx::any::AnyPoolOptions;
use sqlx::AnyPool;
pub async fn init_db() -> AnyPool {
sqlx::any::install_default_drivers();
let db_url = std::env::var("DATABASE_URL")
.unwrap_or_else(|_| "postgres://postgres:[email protected]:5432/typstdrive".to_string());
// DB_TYPE can override URL-based detection: "sqlite" or "postgres"
let db_type = std::env::var("DB_TYPE")
.unwrap_or_else(|_| {
if db_url.starts_with("sqlite") {
"sqlite".to_string()
} else {
"postgres".to_string()
}
});
let pool = AnyPoolOptions::new()
.max_connections(5)
.connect(&db_url)
.await
.expect("Failed to connect to database. Check DATABASE_URL.");
match db_type.as_str() {
"sqlite" => sqlite::init_schema(&pool).await,
"postgres" => postgres::init_schema(&pool).await,
other => panic!("Unknown DB_TYPE '{}'. Expected 'sqlite' or 'postgres'.", other),
}
pool
}
+94
View File
@@ -0,0 +1,94 @@
use sqlx::AnyPool;
pub async fn init_schema(pool: &AnyPool) {
let statements = [
"CREATE TABLE IF NOT EXISTS users (
id TEXT PRIMARY KEY,
username TEXT NOT NULL UNIQUE,
email TEXT UNIQUE,
password_hash TEXT NOT NULL
)",
"CREATE TABLE IF NOT EXISTS folders (
id TEXT PRIMARY KEY,
owner_id TEXT NOT NULL REFERENCES users(id),
parent_id TEXT REFERENCES folders(id),
name TEXT NOT NULL,
created_at TEXT DEFAULT to_char(NOW(), 'YYYY-MM-DD HH24:MI:SS')
)",
"CREATE TABLE IF NOT EXISTS documents (
id TEXT PRIMARY KEY,
owner_id TEXT NOT NULL REFERENCES users(id),
folder_id TEXT REFERENCES folders(id),
title TEXT NOT NULL,
content BYTEA,
thumbnail_svg TEXT,
public_role TEXT DEFAULT NULL,
created_at TEXT DEFAULT to_char(NOW(), 'YYYY-MM-DD HH24:MI:SS'),
updated_at TEXT DEFAULT to_char(NOW(), 'YYYY-MM-DD HH24:MI:SS')
)",
"CREATE TABLE IF NOT EXISTS files (
id TEXT PRIMARY KEY,
owner_id TEXT NOT NULL REFERENCES users(id),
document_id TEXT REFERENCES documents(id),
folder_id TEXT REFERENCES folders(id),
name TEXT NOT NULL,
mime_type TEXT NOT NULL,
data BYTEA NOT NULL,
created_at TEXT DEFAULT to_char(NOW(), 'YYYY-MM-DD HH24:MI:SS')
)",
"CREATE TABLE IF NOT EXISTS collaborators (
id TEXT PRIMARY KEY,
document_id TEXT NOT NULL REFERENCES documents(id) ON DELETE CASCADE,
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
role TEXT NOT NULL,
created_at TEXT DEFAULT to_char(NOW(), 'YYYY-MM-DD HH24:MI:SS'),
UNIQUE(document_id, user_id)
)",
"CREATE TABLE IF NOT EXISTS invitations (
id TEXT PRIMARY KEY,
document_id TEXT NOT NULL REFERENCES documents(id) ON DELETE CASCADE,
role TEXT NOT NULL,
token TEXT NOT NULL UNIQUE,
created_at TEXT DEFAULT to_char(NOW(), 'YYYY-MM-DD HH24:MI:SS'),
expires_at TEXT
)",
"CREATE TABLE IF NOT EXISTS comments (
id TEXT PRIMARY KEY,
document_id TEXT NOT NULL REFERENCES documents(id) ON DELETE CASCADE,
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
content TEXT NOT NULL,
resolved BOOLEAN DEFAULT FALSE,
created_at TEXT DEFAULT to_char(NOW(), 'YYYY-MM-DD HH24:MI:SS')
)",
"CREATE TABLE IF NOT EXISTS document_history (
id TEXT PRIMARY KEY,
document_id TEXT NOT NULL REFERENCES documents(id) ON DELETE CASCADE,
content BYTEA NOT NULL,
created_by TEXT NOT NULL REFERENCES users(id) ON DELETE SET NULL,
created_at TEXT DEFAULT to_char(NOW(), 'YYYY-MM-DD HH24:MI:SS')
)",
"CREATE TABLE IF NOT EXISTS document_versions (
id TEXT PRIMARY KEY,
document_id TEXT NOT NULL REFERENCES documents(id) ON DELETE CASCADE,
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
content TEXT NOT NULL,
created_at TEXT DEFAULT to_char(NOW(), 'YYYY-MM-DD HH24:MI:SS')
)",
];
for stmt in &statements {
sqlx::query(stmt)
.execute(pool)
.await
.expect("Failed to execute Postgres schema");
}
// Idempotent migration for existing databases with TIMESTAMP columns
sqlx::query("ALTER TABLE documents ADD COLUMN IF NOT EXISTS public_role TEXT")
.execute(pool)
.await
.unwrap_or_else(|e| {
eprintln!("Warning: public_role migration: {}", e);
Default::default()
});
}
+90
View File
@@ -0,0 +1,90 @@
use sqlx::AnyPool;
pub async fn init_schema(pool: &AnyPool) {
sqlx::query("PRAGMA foreign_keys = ON")
.execute(pool)
.await
.expect("Failed to enable SQLite foreign keys");
let statements = [
"CREATE TABLE IF NOT EXISTS users (
id TEXT PRIMARY KEY,
username TEXT NOT NULL UNIQUE,
email TEXT UNIQUE,
password_hash TEXT NOT NULL
)",
"CREATE TABLE IF NOT EXISTS folders (
id TEXT PRIMARY KEY,
owner_id TEXT NOT NULL REFERENCES users(id),
parent_id TEXT REFERENCES folders(id),
name TEXT NOT NULL,
created_at TEXT DEFAULT (strftime('%Y-%m-%d %H:%M:%S', 'now'))
)",
"CREATE TABLE IF NOT EXISTS documents (
id TEXT PRIMARY KEY,
owner_id TEXT NOT NULL REFERENCES users(id),
folder_id TEXT REFERENCES folders(id),
title TEXT NOT NULL,
content BLOB,
thumbnail_svg TEXT,
public_role TEXT DEFAULT NULL,
created_at TEXT DEFAULT (strftime('%Y-%m-%d %H:%M:%S', 'now')),
updated_at TEXT DEFAULT (strftime('%Y-%m-%d %H:%M:%S', 'now'))
)",
"CREATE TABLE IF NOT EXISTS files (
id TEXT PRIMARY KEY,
owner_id TEXT NOT NULL REFERENCES users(id),
document_id TEXT REFERENCES documents(id),
folder_id TEXT REFERENCES folders(id),
name TEXT NOT NULL,
mime_type TEXT NOT NULL,
data BLOB NOT NULL,
created_at TEXT DEFAULT (strftime('%Y-%m-%d %H:%M:%S', 'now'))
)",
"CREATE TABLE IF NOT EXISTS collaborators (
id TEXT PRIMARY KEY,
document_id TEXT NOT NULL REFERENCES documents(id) ON DELETE CASCADE,
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
role TEXT NOT NULL,
created_at TEXT DEFAULT (strftime('%Y-%m-%d %H:%M:%S', 'now')),
UNIQUE(document_id, user_id)
)",
"CREATE TABLE IF NOT EXISTS invitations (
id TEXT PRIMARY KEY,
document_id TEXT NOT NULL REFERENCES documents(id) ON DELETE CASCADE,
role TEXT NOT NULL,
token TEXT NOT NULL UNIQUE,
created_at TEXT DEFAULT (strftime('%Y-%m-%d %H:%M:%S', 'now')),
expires_at TEXT
)",
"CREATE TABLE IF NOT EXISTS comments (
id TEXT PRIMARY KEY,
document_id TEXT NOT NULL REFERENCES documents(id) ON DELETE CASCADE,
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
content TEXT NOT NULL,
resolved INTEGER DEFAULT 0,
created_at TEXT DEFAULT (strftime('%Y-%m-%d %H:%M:%S', 'now'))
)",
"CREATE TABLE IF NOT EXISTS document_history (
id TEXT PRIMARY KEY,
document_id TEXT NOT NULL REFERENCES documents(id) ON DELETE CASCADE,
content BLOB NOT NULL,
created_by TEXT NOT NULL REFERENCES users(id) ON DELETE SET NULL,
created_at TEXT DEFAULT (strftime('%Y-%m-%d %H:%M:%S', 'now'))
)",
"CREATE TABLE IF NOT EXISTS document_versions (
id TEXT PRIMARY KEY,
document_id TEXT NOT NULL REFERENCES documents(id) ON DELETE CASCADE,
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
content TEXT NOT NULL,
created_at TEXT DEFAULT (strftime('%Y-%m-%d %H:%M:%S', 'now'))
)",
];
for stmt in &statements {
sqlx::query(stmt)
.execute(pool)
.await
.expect("Failed to execute SQLite schema");
}
}