Initial commit: Phase 1+2 prototype

- 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
This commit is contained in:
2026-05-22 19:48:48 +02:00
commit 0c1047d390
26 changed files with 6206 additions and 0 deletions
+139
View File
@@ -0,0 +1,139 @@
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'
);
`