Files
anders 8a7b0e18ed LDAP admin GUI + group filter
- LDAP settings configurable from Admin panel (no restart needed)
- Required group filter: only users in specified group can login
- Supports both memberOf attribute and groupOfNames search
- Settings stored in DB (settings table), env vars as fallback
- SLDAP supported via ldaps:// URL
- Bind password masked in UI
2026-05-27 00:08:00 +02:00

157 lines
4.0 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)
if err != nil {
return err
}
// Add columns that may not exist yet (idempotent)
database.Exec("ALTER TABLE users ADD COLUMN totp_pending TEXT")
database.Exec("ALTER TABLE users ADD COLUMN audit_log_enabled INTEGER DEFAULT 0")
database.Exec(`CREATE TABLE IF NOT EXISTS audit_log (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id TEXT NOT NULL,
action TEXT NOT NULL,
detail TEXT,
created_at TEXT NOT NULL DEFAULT (datetime('now'))
)`)
database.Exec(`CREATE TABLE IF NOT EXISTS settings (
key TEXT PRIMARY KEY,
value TEXT NOT NULL
)`)
return nil
}
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'
);
`