167 lines
4.0 KiB
Go
167 lines
4.0 KiB
Go
package db
|
|
|
|
import (
|
|
"database/sql"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
_ "modernc.org/sqlite"
|
|
)
|
|
|
|
// DB wraps a database connection
|
|
type DB struct {
|
|
*sql.DB
|
|
}
|
|
|
|
// New creates a new database connection
|
|
func New(dbPath string) (*DB, error) {
|
|
// Ensure directory exists
|
|
dir := filepath.Dir(dbPath)
|
|
if err := os.MkdirAll(dir, 0755); err != nil {
|
|
return nil, fmt.Errorf("create db directory: %w", err)
|
|
}
|
|
|
|
conn, err := sql.Open("sqlite", dbPath+"?_foreign_keys=1")
|
|
if err != nil {
|
|
return nil, fmt.Errorf("open database: %w", err)
|
|
}
|
|
|
|
db := &DB{DB: conn}
|
|
|
|
// Test connection
|
|
if err := db.Ping(); err != nil {
|
|
return nil, fmt.Errorf("ping database: %w", err)
|
|
}
|
|
|
|
// Run migrations
|
|
if err := db.migrate(); err != nil {
|
|
return nil, fmt.Errorf("migrate database: %w", err)
|
|
}
|
|
|
|
return db, nil
|
|
}
|
|
|
|
// migrate runs database migrations
|
|
func (db *DB) migrate() error {
|
|
schema := `
|
|
-- Users table
|
|
CREATE TABLE IF NOT EXISTS users (
|
|
id TEXT PRIMARY KEY,
|
|
username TEXT UNIQUE NOT NULL,
|
|
email TEXT,
|
|
password_hash TEXT NOT NULL,
|
|
role TEXT NOT NULL,
|
|
active INTEGER NOT NULL DEFAULT 1,
|
|
created_at TEXT NOT NULL,
|
|
updated_at TEXT NOT NULL
|
|
);
|
|
|
|
-- Audit logs table
|
|
CREATE TABLE IF NOT EXISTS audit_logs (
|
|
id TEXT PRIMARY KEY,
|
|
actor TEXT NOT NULL,
|
|
action TEXT NOT NULL,
|
|
resource TEXT NOT NULL,
|
|
result TEXT NOT NULL,
|
|
message TEXT,
|
|
ip TEXT,
|
|
user_agent TEXT,
|
|
timestamp TEXT NOT NULL
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_audit_actor ON audit_logs(actor);
|
|
CREATE INDEX IF NOT EXISTS idx_audit_action ON audit_logs(action);
|
|
CREATE INDEX IF NOT EXISTS idx_audit_resource ON audit_logs(resource);
|
|
CREATE INDEX IF NOT EXISTS idx_audit_timestamp ON audit_logs(timestamp);
|
|
|
|
-- SMB shares table
|
|
CREATE TABLE IF NOT EXISTS smb_shares (
|
|
id TEXT PRIMARY KEY,
|
|
name TEXT UNIQUE NOT NULL,
|
|
path TEXT NOT NULL,
|
|
dataset TEXT NOT NULL,
|
|
description TEXT,
|
|
read_only INTEGER NOT NULL DEFAULT 0,
|
|
guest_ok INTEGER NOT NULL DEFAULT 0,
|
|
enabled INTEGER NOT NULL DEFAULT 1
|
|
);
|
|
|
|
-- SMB share valid users (many-to-many)
|
|
CREATE TABLE IF NOT EXISTS smb_share_users (
|
|
share_id TEXT NOT NULL,
|
|
username TEXT NOT NULL,
|
|
PRIMARY KEY (share_id, username),
|
|
FOREIGN KEY (share_id) REFERENCES smb_shares(id) ON DELETE CASCADE
|
|
);
|
|
|
|
-- NFS exports table
|
|
CREATE TABLE IF NOT EXISTS nfs_exports (
|
|
id TEXT PRIMARY KEY,
|
|
path TEXT UNIQUE NOT NULL,
|
|
dataset TEXT NOT NULL,
|
|
read_only INTEGER NOT NULL DEFAULT 0,
|
|
root_squash INTEGER NOT NULL DEFAULT 1,
|
|
enabled INTEGER NOT NULL DEFAULT 1
|
|
);
|
|
|
|
-- NFS export clients (many-to-many)
|
|
CREATE TABLE IF NOT EXISTS nfs_export_clients (
|
|
export_id TEXT NOT NULL,
|
|
client TEXT NOT NULL,
|
|
PRIMARY KEY (export_id, client),
|
|
FOREIGN KEY (export_id) REFERENCES nfs_exports(id) ON DELETE CASCADE
|
|
);
|
|
|
|
-- iSCSI targets table
|
|
CREATE TABLE IF NOT EXISTS iscsi_targets (
|
|
id TEXT PRIMARY KEY,
|
|
iqn TEXT UNIQUE NOT NULL,
|
|
enabled INTEGER NOT NULL DEFAULT 1
|
|
);
|
|
|
|
-- iSCSI target initiators (many-to-many)
|
|
CREATE TABLE IF NOT EXISTS iscsi_target_initiators (
|
|
target_id TEXT NOT NULL,
|
|
initiator TEXT NOT NULL,
|
|
PRIMARY KEY (target_id, initiator),
|
|
FOREIGN KEY (target_id) REFERENCES iscsi_targets(id) ON DELETE CASCADE
|
|
);
|
|
|
|
-- iSCSI LUNs table
|
|
CREATE TABLE IF NOT EXISTS iscsi_luns (
|
|
target_id TEXT NOT NULL,
|
|
lun_id INTEGER NOT NULL,
|
|
zvol TEXT NOT NULL,
|
|
size INTEGER NOT NULL,
|
|
backend TEXT NOT NULL DEFAULT 'zvol',
|
|
PRIMARY KEY (target_id, lun_id),
|
|
FOREIGN KEY (target_id) REFERENCES iscsi_targets(id) ON DELETE CASCADE
|
|
);
|
|
|
|
-- Snapshot policies table
|
|
CREATE TABLE IF NOT EXISTS snapshot_policies (
|
|
id TEXT PRIMARY KEY,
|
|
dataset TEXT NOT NULL,
|
|
schedule_type TEXT NOT NULL,
|
|
schedule_value TEXT,
|
|
retention_count INTEGER,
|
|
retention_days INTEGER,
|
|
enabled INTEGER NOT NULL DEFAULT 1,
|
|
created_at TEXT NOT NULL,
|
|
updated_at TEXT NOT NULL
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_snapshot_policy_dataset ON snapshot_policies(dataset);
|
|
`
|
|
|
|
if _, err := db.Exec(schema); err != nil {
|
|
return fmt.Errorf("create schema: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Close closes the database connection
|
|
func (db *DB) Close() error {
|
|
return db.DB.Close()
|
|
}
|