3588AdminBackend/internal/storage/migrate.go

188 lines
5.0 KiB
Go

package storage
import (
"database/sql"
"fmt"
)
const schema001 = `
CREATE TABLE IF NOT EXISTS templates (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL UNIQUE,
description TEXT NOT NULL DEFAULT '',
body_json TEXT NOT NULL,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS profiles (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL UNIQUE,
primary_template_name TEXT NOT NULL DEFAULT '',
business_name TEXT NOT NULL DEFAULT '',
description TEXT NOT NULL DEFAULT '',
body_json TEXT NOT NULL,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS overlays (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL UNIQUE,
description TEXT NOT NULL DEFAULT '',
body_json TEXT NOT NULL,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS integration_services (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL UNIQUE,
type TEXT NOT NULL DEFAULT '',
description TEXT NOT NULL DEFAULT '',
enabled INTEGER NOT NULL DEFAULT 0,
body_json TEXT NOT NULL,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS video_sources (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL UNIQUE,
source_type TEXT NOT NULL DEFAULT '',
area TEXT NOT NULL DEFAULT '',
description TEXT NOT NULL DEFAULT '',
body_json TEXT NOT NULL,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS devices (
device_id TEXT PRIMARY KEY,
hostname TEXT NOT NULL DEFAULT '',
ip TEXT NOT NULL DEFAULT '',
agent_port INTEGER NOT NULL DEFAULT 0,
media_port INTEGER NOT NULL DEFAULT 0,
alias TEXT NOT NULL DEFAULT '',
device_name TEXT NOT NULL DEFAULT '',
version TEXT NOT NULL DEFAULT '',
git_sha TEXT NOT NULL DEFAULT '',
build_id TEXT NOT NULL DEFAULT '',
last_seen_ms INTEGER NOT NULL DEFAULT 0,
online INTEGER NOT NULL DEFAULT 0,
graphs_json TEXT NOT NULL DEFAULT '{}',
updated_at TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS device_config_state (
device_id TEXT PRIMARY KEY,
template_name TEXT NOT NULL DEFAULT '',
profile_name TEXT NOT NULL DEFAULT '',
overlays_json TEXT NOT NULL DEFAULT '[]',
config_id TEXT NOT NULL DEFAULT '',
config_version TEXT NOT NULL DEFAULT '',
last_applied_task_id TEXT NOT NULL DEFAULT '',
updated_at TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS tasks (
task_id TEXT PRIMARY KEY,
type TEXT NOT NULL,
payload_json TEXT NOT NULL,
status TEXT NOT NULL,
created_at TEXT NOT NULL,
finished_at TEXT NOT NULL DEFAULT ''
);
CREATE TABLE IF NOT EXISTS task_devices (
task_id TEXT NOT NULL,
device_id TEXT NOT NULL,
status TEXT NOT NULL,
progress REAL NOT NULL DEFAULT 0,
error_text TEXT NOT NULL DEFAULT '',
PRIMARY KEY (task_id, device_id)
);
CREATE TABLE IF NOT EXISTS audit_logs (
id INTEGER PRIMARY KEY,
actor TEXT NOT NULL DEFAULT 'system',
action TEXT NOT NULL,
target_type TEXT NOT NULL,
target_id TEXT NOT NULL,
details_json TEXT NOT NULL DEFAULT '{}',
created_at TEXT NOT NULL
);
`
func migrate(db *sql.DB) error {
if _, err := db.Exec(schema001); err != nil {
return err
}
return migrateProfilePrimaryTemplateName(db)
}
func migrateProfilePrimaryTemplateName(db *sql.DB) error {
hasPrimary, err := hasColumn(db, "profiles", "primary_template_name")
if err != nil {
return err
}
if hasPrimary {
return nil
}
hasLegacy, err := hasColumn(db, "profiles", "template_name")
if err != nil {
return err
}
if !hasLegacy {
return fmt.Errorf("profiles table is missing both primary_template_name and template_name")
}
tx, err := db.Begin()
if err != nil {
return err
}
defer func() { _ = tx.Rollback() }()
if _, err := tx.Exec(`ALTER TABLE profiles RENAME TO profiles_legacy_template_name`); err != nil {
return err
}
if _, err := tx.Exec(`
CREATE TABLE profiles (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL UNIQUE,
primary_template_name TEXT NOT NULL DEFAULT '',
business_name TEXT NOT NULL DEFAULT '',
description TEXT NOT NULL DEFAULT '',
body_json TEXT NOT NULL,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
)`); err != nil {
return err
}
if _, err := tx.Exec(`
INSERT INTO profiles(id, name, primary_template_name, business_name, description, body_json, created_at, updated_at)
SELECT id, name, template_name, business_name, description, body_json, created_at, updated_at
FROM profiles_legacy_template_name
`); err != nil {
return err
}
if _, err := tx.Exec(`DROP TABLE profiles_legacy_template_name`); err != nil {
return err
}
return tx.Commit()
}
func hasColumn(db *sql.DB, table string, column string) (bool, error) {
rows, err := db.Query(`PRAGMA table_info(` + table + `)`)
if err != nil {
return false, err
}
defer rows.Close()
for rows.Next() {
var (
cid int
name string
ctype string
notnull int
dfltValue sql.NullString
pk int
)
if err := rows.Scan(&cid, &name, &ctype, &notnull, &dfltValue, &pk); err != nil {
return false, err
}
if name == column {
return true, nil
}
}
return false, rows.Err()
}