diff options
| author | Fuwn <[email protected]> | 2026-01-23 20:54:50 -0800 |
|---|---|---|
| committer | Fuwn <[email protected]> | 2026-01-23 20:54:50 -0800 |
| commit | 4f8955fb5b9e05a5d0e2f4811c13926a6cbbe4b3 (patch) | |
| tree | 4d1bcd871bb90e55f1aa11b16bc8763ac692af73 /internal/storage | |
| parent | feat: Add database maintenance with backup/reset modes and triggers (diff) | |
| download | kaze-4f8955fb5b9e05a5d0e2f4811c13926a6cbbe4b3.tar.xz kaze-4f8955fb5b9e05a5d0e2f4811c13926a6cbbe4b3.zip | |
feat: Switch to libsql driver for Turso compatibility
Diffstat (limited to 'internal/storage')
| -rw-r--r-- | internal/storage/sqlite.go | 123 |
1 files changed, 63 insertions, 60 deletions
diff --git a/internal/storage/sqlite.go b/internal/storage/sqlite.go index db0ed52..5fcc9b3 100644 --- a/internal/storage/sqlite.go +++ b/internal/storage/sqlite.go @@ -8,7 +8,7 @@ import ( "time" "github.com/Fuwn/kaze/internal/config" - _ "modernc.org/sqlite" + _ "github.com/tursodatabase/go-libsql" ) // Storage handles all database operations @@ -107,22 +107,27 @@ func NewWithMaintenance(dbPath string, historyDays int, maintenanceCfg config.Ma return s, nil } -func openDB(dbPath string) (*sql.DB, error) { - dsn := fmt.Sprintf("%s?_txlock=immediate&_busy_timeout=5000&_journal_mode=WAL&_synchronous=NORMAL", dbPath) +func openDB(dbURL string) (*sql.DB, error) { + isRemote := strings.HasPrefix(dbURL, "libsql://") || strings.HasPrefix(dbURL, "https://") - db, err := sql.Open("sqlite", dsn) + dsn := dbURL + if !isRemote && !strings.HasPrefix(dbURL, "file:") { + dsn = "file:" + dbURL + } + + if !isRemote { + dsn = dsn + "?_txlock=immediate&_busy_timeout=5000&_journal_mode=WAL&_synchronous=NORMAL" + } + + db, err := sql.Open("libsql", dsn) if err != nil { return nil, fmt.Errorf("failed to open database: %w", err) } - db.SetMaxOpenConns(1) - db.SetMaxIdleConns(1) - db.SetConnMaxLifetime(0) - - var journalMode string - if err := db.QueryRow("PRAGMA journal_mode").Scan(&journalMode); err != nil { - db.Close() - return nil, fmt.Errorf("failed to check journal mode: %w", err) + if !isRemote { + db.SetMaxOpenConns(1) + db.SetMaxIdleConns(1) + db.SetConnMaxLifetime(0) } if _, err := db.Exec("PRAGMA foreign_keys=ON"); err != nil { @@ -133,55 +138,53 @@ func openDB(dbPath string) (*sql.DB, error) { return db, nil } -// migrate creates the database schema func (s *Storage) migrate() error { - schema := ` - CREATE TABLE IF NOT EXISTS check_results ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - monitor_name TEXT NOT NULL, - timestamp DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - status TEXT NOT NULL CHECK(status IN ('up', 'down', 'degraded')), - response_time_ms INTEGER NOT NULL DEFAULT 0, - status_code INTEGER DEFAULT 0, - error_message TEXT, - ssl_expiry DATETIME, - ssl_days_left INTEGER DEFAULT 0 - ); - - CREATE INDEX IF NOT EXISTS idx_check_results_monitor_time - ON check_results(monitor_name, timestamp DESC); - - CREATE INDEX IF NOT EXISTS idx_check_results_timestamp - ON check_results(timestamp); - - CREATE TABLE IF NOT EXISTS daily_stats ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - monitor_name TEXT NOT NULL, - date DATE NOT NULL, - success_count INTEGER NOT NULL DEFAULT 0, - failure_count INTEGER NOT NULL DEFAULT 0, - total_checks INTEGER NOT NULL DEFAULT 0, - avg_response_ms REAL DEFAULT 0, - uptime_percent REAL DEFAULT 0, - UNIQUE(monitor_name, date) - ); - - CREATE INDEX IF NOT EXISTS idx_daily_stats_monitor_date - ON daily_stats(monitor_name, date DESC); - - CREATE TABLE IF NOT EXISTS monitor_state ( - monitor_name TEXT PRIMARY KEY, - current_status TEXT NOT NULL DEFAULT 'unknown', - last_check DATETIME, - last_response_time_ms INTEGER DEFAULT 0, - last_error TEXT, - ssl_expiry DATETIME, - ssl_days_left INTEGER DEFAULT 0 - ); - ` - - _, err := s.db.Exec(schema) - return err + statements := []string{ + `CREATE TABLE IF NOT EXISTS check_results ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + monitor_name TEXT NOT NULL, + timestamp DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + status TEXT NOT NULL CHECK(status IN ('up', 'down', 'degraded')), + response_time_ms INTEGER NOT NULL DEFAULT 0, + status_code INTEGER DEFAULT 0, + error_message TEXT, + ssl_expiry DATETIME, + ssl_days_left INTEGER DEFAULT 0 + )`, + `CREATE INDEX IF NOT EXISTS idx_check_results_monitor_time + ON check_results(monitor_name, timestamp DESC)`, + `CREATE INDEX IF NOT EXISTS idx_check_results_timestamp + ON check_results(timestamp)`, + `CREATE TABLE IF NOT EXISTS daily_stats ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + monitor_name TEXT NOT NULL, + date DATE NOT NULL, + success_count INTEGER NOT NULL DEFAULT 0, + failure_count INTEGER NOT NULL DEFAULT 0, + total_checks INTEGER NOT NULL DEFAULT 0, + avg_response_ms REAL DEFAULT 0, + uptime_percent REAL DEFAULT 0, + UNIQUE(monitor_name, date) + )`, + `CREATE INDEX IF NOT EXISTS idx_daily_stats_monitor_date + ON daily_stats(monitor_name, date DESC)`, + `CREATE TABLE IF NOT EXISTS monitor_state ( + monitor_name TEXT PRIMARY KEY, + current_status TEXT NOT NULL DEFAULT 'unknown', + last_check DATETIME, + last_response_time_ms INTEGER DEFAULT 0, + last_error TEXT, + ssl_expiry DATETIME, + ssl_days_left INTEGER DEFAULT 0 + )`, + } + + for _, stmt := range statements { + if _, err := s.db.Exec(stmt); err != nil { + return fmt.Errorf("migration failed: %w", err) + } + } + return nil } // SaveCheckResult saves a check result and updates monitor state |