1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
/*
* Copyright (C) 2024 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 = "schema_migrations"
func runMigrations() {
slog.Info("Running migrations")
var schemaVersion int
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), slog.String("error", err.Error()))
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)
}
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)
)`),
}
}