0c1047d390
- Go backend with SQLite, JWT auth, file CRUD - Vue 3 frontend with split/raw/WYSIWYG editor modes - Markdown preview (marked, GFM) - Formatting toolbar + keyboard shortcuts - File tree with search, create, delete - Light/dark theme toggle - Admin panel (user management) - Preferences (timezone, theme, default mode) - Shared documents section (placeholder) - Export: PDF, HTML, MD - Build daemon (Python, stdlib only) - Build job queue API - Docker deployment
140 lines
3.5 KiB
Go
140 lines
3.5 KiB
Go
package db
|
|
|
|
import (
|
|
"database/sql"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
_ "modernc.org/sqlite"
|
|
)
|
|
|
|
type DB = sql.DB
|
|
|
|
func Open(path string) (*DB, error) {
|
|
dir := filepath.Dir(path)
|
|
if err := os.MkdirAll(dir, 0755); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
database, err := sql.Open("sqlite", path+"?_journal_mode=WAL&_busy_timeout=5000")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := database.Ping(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return database, nil
|
|
}
|
|
|
|
func Migrate(database *DB) error {
|
|
_, err := database.Exec(schema)
|
|
return err
|
|
}
|
|
|
|
var schema = `
|
|
CREATE TABLE IF NOT EXISTS users (
|
|
id TEXT PRIMARY KEY,
|
|
username TEXT UNIQUE NOT NULL,
|
|
email TEXT UNIQUE NOT NULL,
|
|
password_hash TEXT NOT NULL,
|
|
totp_secret TEXT,
|
|
encryption_enabled INTEGER DEFAULT 0,
|
|
encryption_salt BLOB,
|
|
recovery_key_hash TEXT,
|
|
is_admin INTEGER DEFAULT 0,
|
|
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS files (
|
|
id TEXT PRIMARY KEY,
|
|
owner_id TEXT NOT NULL REFERENCES users(id),
|
|
path TEXT NOT NULL,
|
|
title TEXT,
|
|
encrypted INTEGER DEFAULT 0,
|
|
encrypted_content BLOB,
|
|
encrypted_file_key BLOB,
|
|
sync_flagged INTEGER DEFAULT 0,
|
|
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
UNIQUE(owner_id, path)
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS virtual_tree (
|
|
id TEXT PRIMARY KEY,
|
|
user_id TEXT NOT NULL REFERENCES users(id),
|
|
parent_id TEXT REFERENCES virtual_tree(id),
|
|
name TEXT NOT NULL,
|
|
type TEXT NOT NULL,
|
|
target_file_id TEXT REFERENCES files(id),
|
|
sort_order INTEGER DEFAULT 0,
|
|
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS permissions (
|
|
id TEXT PRIMARY KEY,
|
|
file_id TEXT NOT NULL REFERENCES files(id),
|
|
user_id TEXT NOT NULL REFERENCES users(id),
|
|
level TEXT NOT NULL,
|
|
granted_by TEXT NOT NULL REFERENCES users(id),
|
|
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
UNIQUE(file_id, user_id)
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS git_remotes (
|
|
id TEXT PRIMARY KEY,
|
|
user_id TEXT NOT NULL REFERENCES users(id),
|
|
name TEXT NOT NULL,
|
|
url TEXT NOT NULL,
|
|
auth_token TEXT,
|
|
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
UNIQUE(user_id, name)
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS collab_state (
|
|
file_id TEXT PRIMARY KEY REFERENCES files(id),
|
|
yjs_state BLOB,
|
|
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS sessions (
|
|
id TEXT PRIMARY KEY,
|
|
user_id TEXT NOT NULL REFERENCES users(id),
|
|
token_hash TEXT NOT NULL,
|
|
expires_at TEXT NOT NULL,
|
|
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS api_tokens (
|
|
id TEXT PRIMARY KEY,
|
|
user_id TEXT NOT NULL REFERENCES users(id),
|
|
name TEXT NOT NULL,
|
|
token_hash TEXT NOT NULL,
|
|
last_used_at TEXT,
|
|
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS build_jobs (
|
|
id TEXT PRIMARY KEY,
|
|
user_id TEXT NOT NULL REFERENCES users(id),
|
|
status TEXT NOT NULL DEFAULT 'pending',
|
|
spec_content TEXT NOT NULL,
|
|
gitea_url TEXT,
|
|
gitea_token TEXT,
|
|
gitea_org TEXT,
|
|
repo_name TEXT NOT NULL,
|
|
model TEXT,
|
|
log TEXT DEFAULT '',
|
|
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS daemon_state (
|
|
id TEXT PRIMARY KEY DEFAULT 'singleton',
|
|
last_heartbeat TEXT,
|
|
status TEXT DEFAULT 'offline'
|
|
);
|
|
`
|