/*
* Copyright (C) 2023 Jonni Liljamo <jonni@liljamo.com>
*
* This file is licensed under AGPL-3.0-or-later, see NOTICE and LICENSE for
* more information.
*/
package db
import (
"fmt"
"log/slog"
"os"
)
var migrationsTable string = "schema_migrations"
func runMigrations() {
slog.Info("Running migrations")
var schemaVersion int = 0
schemaVersionQuery := fmt.Sprintf(
`SELECT schema_version
FROM %s
ORDER BY schema_version
DESC LIMIT 1`,
migrationsTable)
err := DBConn.QueryRow(schemaVersionQuery).Scan(&schemaVersion)
if err != nil {
slog.Info("No schema version found, running all migrations")
} else {
slog.Info("Current schema", slog.Int("version", schemaVersion))
}
migrations := migrations()
if schemaVersion != len(migrations) {
for i := 0; i < len(migrations); i++ {
if i >= schemaVersion {
slog.Info("Running migration", slog.Int("version", i + 1)) // + 1 is just a visual thing
_, err := DBConn.Exec(migrations[i])
if err != nil {
slog.Error("Migration failed to run!", slog.Int("version", i))
os.Exit(1)
}
}
}
// We are now up to date
schemaVersion = len(migrations)
// Create a new entry in the migrations table
schemaMigrationInsertQuery := fmt.Sprintf(
`INSERT INTO %s(schema_version) VALUES(%d)`,
migrationsTable, schemaVersion)
_, err = DBConn.Exec(schemaMigrationInsertQuery)
if err != nil {
slog.Error("Migrations ran, but was not able to create migration entry")
os.Exit(1)
} else {
slog.Info("Migrations ran successfully")
}
} else {
slog.Info("No migrations to run")
}
}
func migrations() []string {
return []string{
fmt.Sprintf(`CREATE TABLE %s (
schema_version INTEGER
)`, migrationsTable),
fmt.Sprintf(`CREATE TABLE users (
id TEXT NOT NULL PRIMARY KEY UNIQUE,
email TEXT NOT NULL UNIQUE,
pwd TEXT NOT NULL,
is_admin INTEGER DEFAULT FALSE NOT NULL
)`),
fmt.Sprintf(`CREATE TABLE domains (
id TEXT NOT NULL PRIMARY KEY UNIQUE,
apikey TEXT NOT NULL,
owner TEXT NOT NULL,
ddns_domain TEXT NOT NULL UNIQUE,
a_record TEXT,
FOREIGN KEY(owner) REFERENCES users(id)
)`),
}
}