Files
Maintainarr/internal/db/db.go

161 lines
5.0 KiB
Go

package db
import (
"context"
"database/sql"
"fmt"
"os"
"path/filepath"
"time"
_ "modernc.org/sqlite"
)
func Open(path string) (*sql.DB, error) {
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
return nil, err
}
database, err := sql.Open("sqlite", path)
if err != nil {
return nil, err
}
database.SetMaxOpenConns(1)
database.SetConnMaxLifetime(30 * time.Minute)
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
if err := database.PingContext(ctx); err != nil {
return nil, err
}
if err := migrate(ctx, database); err != nil {
return nil, err
}
return database, nil
}
func migrate(ctx context.Context, database *sql.DB) error {
statements := []string{
`PRAGMA foreign_keys = ON;`,
`CREATE TABLE IF NOT EXISTS organizations (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
theme TEXT NOT NULL DEFAULT 'emerald',
theme_mode TEXT NOT NULL DEFAULT 'dark',
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);`,
`CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
organization_id INTEGER NOT NULL,
name TEXT NOT NULL,
email TEXT NOT NULL UNIQUE,
password_hash TEXT NOT NULL,
role TEXT NOT NULL,
otp_secret TEXT NOT NULL,
otp_enabled BOOLEAN NOT NULL DEFAULT 0,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (organization_id) REFERENCES organizations(id)
);`,
`CREATE TABLE IF NOT EXISTS vm_groups (
id INTEGER PRIMARY KEY AUTOINCREMENT,
organization_id INTEGER NOT NULL,
name TEXT NOT NULL,
description TEXT NOT NULL DEFAULT '',
color_token TEXT NOT NULL DEFAULT 'primary',
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (organization_id) REFERENCES organizations(id)
);`,
`CREATE TABLE IF NOT EXISTS nodes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
organization_id INTEGER NOT NULL,
group_id INTEGER,
tag TEXT NOT NULL DEFAULT '',
name TEXT NOT NULL,
distro TEXT NOT NULL,
hostname TEXT NOT NULL,
ip_address TEXT NOT NULL,
mac_address TEXT NOT NULL DEFAULT '',
ssh_port INTEGER NOT NULL DEFAULT 22,
ssh_username TEXT NOT NULL DEFAULT '',
ssh_password TEXT NOT NULL DEFAULT '',
package_manager TEXT NOT NULL DEFAULT '',
architecture TEXT NOT NULL DEFAULT '',
kernel_version TEXT NOT NULL DEFAULT '',
cpu_model TEXT NOT NULL DEFAULT '',
memory_total_mb INTEGER NOT NULL DEFAULT 0,
disk_total_gb INTEGER NOT NULL DEFAULT 0,
cpu_usage REAL NOT NULL DEFAULT 0,
ram_usage REAL NOT NULL DEFAULT 0,
disk_usage REAL NOT NULL DEFAULT 0,
uptime_seconds INTEGER NOT NULL DEFAULT 0,
last_seen_at DATETIME,
auto_updates_enabled BOOLEAN NOT NULL DEFAULT 0,
notes TEXT NOT NULL DEFAULT '',
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (organization_id) REFERENCES organizations(id),
FOREIGN KEY (group_id) REFERENCES vm_groups(id)
);`,
`CREATE TABLE IF NOT EXISTS automation_jobs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
organization_id INTEGER NOT NULL,
node_id INTEGER,
group_id INTEGER,
tag TEXT NOT NULL DEFAULT '',
name TEXT NOT NULL,
trigger_type TEXT NOT NULL,
schedule TEXT NOT NULL DEFAULT '',
command TEXT NOT NULL,
enabled BOOLEAN NOT NULL DEFAULT 1,
last_run_at DATETIME,
next_run_at DATETIME,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (organization_id) REFERENCES organizations(id),
FOREIGN KEY (node_id) REFERENCES nodes(id),
FOREIGN KEY (group_id) REFERENCES vm_groups(id)
);`,
`CREATE TABLE IF NOT EXISTS command_runs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
job_id INTEGER,
node_id INTEGER NOT NULL,
action TEXT NOT NULL,
status TEXT NOT NULL,
output TEXT NOT NULL DEFAULT '',
triggered_by INTEGER,
started_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
finished_at DATETIME,
FOREIGN KEY (job_id) REFERENCES automation_jobs(id),
FOREIGN KEY (node_id) REFERENCES nodes(id),
FOREIGN KEY (triggered_by) REFERENCES users(id)
);`,
}
for _, statement := range statements {
if _, err := database.ExecContext(ctx, statement); err != nil {
return fmt.Errorf("migrate: %w", err)
}
}
alterStatements := []string{
`ALTER TABLE organizations ADD COLUMN theme_mode TEXT NOT NULL DEFAULT 'dark';`,
`ALTER TABLE nodes ADD COLUMN tag TEXT NOT NULL DEFAULT '';`,
`ALTER TABLE nodes ADD COLUMN package_manager TEXT NOT NULL DEFAULT '';`,
`ALTER TABLE nodes ADD COLUMN architecture TEXT NOT NULL DEFAULT '';`,
`ALTER TABLE nodes ADD COLUMN kernel_version TEXT NOT NULL DEFAULT '';`,
`ALTER TABLE nodes ADD COLUMN cpu_model TEXT NOT NULL DEFAULT '';`,
`ALTER TABLE nodes ADD COLUMN memory_total_mb INTEGER NOT NULL DEFAULT 0;`,
`ALTER TABLE nodes ADD COLUMN disk_total_gb INTEGER NOT NULL DEFAULT 0;`,
`ALTER TABLE automation_jobs ADD COLUMN tag TEXT NOT NULL DEFAULT '';`,
}
for _, statement := range alterStatements {
_, _ = database.ExecContext(ctx, statement)
}
return nil
}