Added proper db migrations
This commit is contained in:
parent
54497370ce
commit
d381528db1
|
|
@ -1,44 +0,0 @@
|
||||||
package app
|
|
||||||
|
|
||||||
import "database/sql"
|
|
||||||
|
|
||||||
func initializeDb(databaseUrl string) *sql.DB {
|
|
||||||
db, err := sql.Open("sqlite3", databaseUrl)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = db.Exec(`CREATE TABLE IF NOT EXISTS sensor_values (
|
|
||||||
timestamp INTEGER NOT NULL,
|
|
||||||
sensor TEXT NOT NULL,
|
|
||||||
value REAL NOT NULL
|
|
||||||
);`)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = db.Exec(`CREATE TABLE IF NOT EXISTS sensor_config (
|
|
||||||
sensor TEXT NOT NULL,
|
|
||||||
key TEXT NOT NULL,
|
|
||||||
value REAL NOT NULL,
|
|
||||||
PRIMARY KEY (sensor, key)
|
|
||||||
);`)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = db.Exec(`CREATE TABLE IF NOT EXISTS sessions (
|
|
||||||
id TEXT NOT NULL,
|
|
||||||
expires_at INTEGER NOT NULL,
|
|
||||||
PRIMARY KEY (id)
|
|
||||||
);`)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return db
|
|
||||||
}
|
|
||||||
|
|
@ -2,6 +2,7 @@ package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"basic-sensor-receiver/config"
|
"basic-sensor-receiver/config"
|
||||||
|
"basic-sensor-receiver/database"
|
||||||
"basic-sensor-receiver/services"
|
"basic-sensor-receiver/services"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
)
|
)
|
||||||
|
|
@ -15,7 +16,14 @@ type Server struct {
|
||||||
func InitializeServer() *Server {
|
func InitializeServer() *Server {
|
||||||
server := Server{}
|
server := Server{}
|
||||||
server.Config = config.LoadConfig()
|
server.Config = config.LoadConfig()
|
||||||
server.DB = initializeDb(server.Config.DatabaseUrl)
|
|
||||||
|
db, err := database.Initialize(server.Config.DatabaseUrl)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
server.DB = db
|
||||||
|
|
||||||
ctx := services.Context{DB: server.DB, Config: server.Config}
|
ctx := services.Context{DB: server.DB, Config: server.Config}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
package database
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Initialize(databaseUrl string) (*sql.DB, error) {
|
||||||
|
db, err := sql.Open("sqlite3", databaseUrl)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to open database connection: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = db.Exec(`CREATE TABLE IF NOT EXISTS migrations (
|
||||||
|
id TEXT NOT NULL,
|
||||||
|
run_at INTEGER NOT NULL
|
||||||
|
);`)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to initialize migrations table: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pendingMigrations, err := getPendingMigrations(db)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get pending migrations: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, migrationId := range pendingMigrations {
|
||||||
|
migrationSource, err := getMigrationContent(migrationId)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = db.Exec(migrationSource)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to run migration %s: %w", migrationId, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = db.Exec("INSERT INTO migrations (id, run_at) VALUES (?, ?)", migrationId, time.Now().Unix())
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to store run migration: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return db, nil
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,100 @@
|
||||||
|
package database
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"embed"
|
||||||
|
"fmt"
|
||||||
|
"io/fs"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed migrations/*.sql
|
||||||
|
var migrationStorage embed.FS
|
||||||
|
|
||||||
|
func getAppliedMigrationsSet(db *sql.DB) (map[string]bool, error) {
|
||||||
|
set := make(map[string]bool)
|
||||||
|
|
||||||
|
rows, err := db.Query("SELECT id FROM migrations")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
for rows.Next() {
|
||||||
|
var id string
|
||||||
|
|
||||||
|
err := rows.Scan(&id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
set[id] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
err = rows.Err()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return set, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getMigrations() ([]string, error) {
|
||||||
|
var items []string
|
||||||
|
|
||||||
|
entries, err := migrationStorage.ReadDir("migrations")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, entry := range entries {
|
||||||
|
if !entry.IsDir() && strings.HasSuffix(entry.Name(), ".sql") {
|
||||||
|
items = append(items, strings.TrimSuffix(entry.Name(), ".sql"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Strings(items)
|
||||||
|
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getMigrationContent(id string) (string, error) {
|
||||||
|
content, err := fs.ReadFile(migrationStorage, "migrations/"+id+".sql")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(content), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPendingMigrations(db *sql.DB) ([]string, error) {
|
||||||
|
var migrationsToRun []string
|
||||||
|
|
||||||
|
migrationSet, err := getAppliedMigrationsSet(db)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
storedMigrations, err := getMigrations()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to load stored migrations: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, storedMigration := range storedMigrations {
|
||||||
|
if !migrationSet[storedMigration] {
|
||||||
|
migrationsToRun = append(migrationsToRun, storedMigration)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return migrationsToRun, nil
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
CREATE TABLE IF NOT EXISTS sensor_values (
|
||||||
|
timestamp INTEGER NOT NULL,
|
||||||
|
sensor TEXT NOT NULL,
|
||||||
|
value REAL NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS sensor_config (
|
||||||
|
sensor TEXT NOT NULL,
|
||||||
|
key TEXT NOT NULL,
|
||||||
|
value REAL NOT NULL,
|
||||||
|
PRIMARY KEY (sensor, key)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS sessions (
|
||||||
|
id TEXT NOT NULL,
|
||||||
|
expires_at INTEGER NOT NULL,
|
||||||
|
PRIMARY KEY (id)
|
||||||
|
);
|
||||||
Loading…
Reference in New Issue