188 lines
5.0 KiB
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, ¬null, &dfltValue, &pk); err != nil {
|
|
return false, err
|
|
}
|
|
if name == column {
|
|
return true, nil
|
|
}
|
|
}
|
|
return false, rows.Err()
|
|
}
|