Refactor to use disk-based SQLite database instead of in-memory
- Modified main.go to initialize the disk-based SQLite database and removed handling of in-memory database. - Removed sqlite_test.go since we no longer have the in-memory database. - Simplified sqliteclose.go to only close the disk-based SQLite database. - Refactored sqliteinit.go to support disk-based SQLite database initialization.
- commit
- d276230715f81de82a84f40ff905ade426cc4ae8
- author
- bnema · · 2023-10-16 15:26 UTC
- parent
- f2713b0
- signature
- unsigned
5 files · +51 -377
M cmd/srv/main.go +9 -9
| @@ -1,7 +1,6 @@ | |||
| 1 | 1 | package main | |
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | - | "database/sql" | |
| 5 | 4 | "fmt" | |
| 6 | 5 | "log" | |
| 7 | 6 | "os" | |
| @@ -15,23 +14,24 @@ import ( | |||
| 15 | 14 | "github.com/bnema/gordon/pkg/docker" | |
| 16 | 15 | ) | |
| 17 | 16 | ||
| 18 | - | func cleanup(a *app.App, memDb *sql.DB) { | |
| 19 | - | if err := app.CloseAndBackupDB(a, memDb); err != nil { | |
| 17 | + | func cleanup(a *app.App) { | |
| 18 | + | if err := app.CloseDB(a); err != nil { | |
| 20 | 19 | log.Fatal("Failed to close and backup database:", err) | |
| 21 | 20 | } | |
| 22 | 21 | } | |
| 23 | 22 | ||
| 24 | 23 | func main() { | |
| 25 | 24 | a := app.NewApp() | |
| 26 | - | // Initialize database | |
| 27 | - | memDb, err := app.InitializeDB(a) | |
| 25 | + | ||
| 26 | + | _, err := app.InitializeDB(a) | |
| 28 | 27 | if err != nil { | |
| 29 | 28 | log.Fatal("Failed to initialize database:", err) | |
| 30 | 29 | } | |
| 31 | - | // Pass memDb to the app with the rest of the configs | |
| 32 | - | a.DB = memDb | |
| 33 | 30 | ||
| 34 | - | a.HandleNewTokenInitialization() | |
| 31 | + | _, err = app.HandleNewTokenInitialization(a) | |
| 32 | + | if err != nil { | |
| 33 | + | log.Fatal("Failed to initialize new token:", err) | |
| 34 | + | } | |
| 35 | 35 | ||
| 36 | 36 | dockerClient := &docker.DockerClient{} | |
| 37 | 37 | err = dockerClient.InitializeClient(a.Config.NewDockerConfig()) | |
| @@ -46,7 +46,7 @@ func main() { | |||
| 46 | 46 | go func() { | |
| 47 | 47 | sig := <-sigs | |
| 48 | 48 | log.Println("Received signal:", sig) | |
| 49 | - | cleanup(a, memDb) // <- Call the cleanup function | |
| 49 | + | cleanup(a) // <- Call the cleanup function | |
| 50 | 50 | os.Exit(0) | |
| 51 | 51 | }() | |
| 52 | 52 | ||
| @@ -1,7 +1,6 @@ | |||||
| 1 | package main | 1 | package main | ||
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | - | "database/sql" | |||
| 5 | "fmt" | 4 | "fmt" | ||
| 6 | "log" | 5 | "log" | ||
| 7 | "os" | 6 | "os" | ||
| @@ -15,23 +14,24 @@ import ( | |||||
| 15 | "github.com/bnema/gordon/pkg/docker" | 14 | "github.com/bnema/gordon/pkg/docker" | ||
| 16 | ) | 15 | ) | ||
| 17 | 16 | ||||
| 18 | - | func cleanup(a *app.App, memDb *sql.DB) { | 17 | + | func cleanup(a *app.App) { |
| 19 | - | if err := app.CloseAndBackupDB(a, memDb); err != nil { | 18 | + | if err := app.CloseDB(a); err != nil { |
| 20 | log.Fatal("Failed to close and backup database:", err) | 19 | log.Fatal("Failed to close and backup database:", err) | ||
| 21 | } | 20 | } | ||
| 22 | } | 21 | } | ||
| 23 | 22 | ||||
| 24 | func main() { | 23 | func main() { | ||
| 25 | a := app.NewApp() | 24 | a := app.NewApp() | ||
| 26 | - | // Initialize database | 25 | + | |
| 27 | - | memDb, err := app.InitializeDB(a) | 26 | + | _, err := app.InitializeDB(a) |
| 28 | if err != nil { | 27 | if err != nil { | ||
| 29 | log.Fatal("Failed to initialize database:", err) | 28 | log.Fatal("Failed to initialize database:", err) | ||
| 30 | } | 29 | } | ||
| 31 | - | // Pass memDb to the app with the rest of the configs | |||
| 32 | - | a.DB = memDb | |||
| 33 | 30 | ||||
| 34 | - | a.HandleNewTokenInitialization() | 31 | + | _, err = app.HandleNewTokenInitialization(a) |
| 32 | + | if err != nil { | |||
| 33 | + | log.Fatal("Failed to initialize new token:", err) | |||
| 34 | + | } | |||
| 35 | 35 | ||||
| 36 | dockerClient := &docker.DockerClient{} | 36 | dockerClient := &docker.DockerClient{} | ||
| 37 | err = dockerClient.InitializeClient(a.Config.NewDockerConfig()) | 37 | err = dockerClient.InitializeClient(a.Config.NewDockerConfig()) | ||
| @@ -46,7 +46,7 @@ func main() { | |||||
| 46 | go func() { | 46 | go func() { | ||
| 47 | sig := <-sigs | 47 | sig := <-sigs | ||
| 48 | log.Println("Received signal:", sig) | 48 | log.Println("Received signal:", sig) | ||
| 49 | - | cleanup(a, memDb) // <- Call the cleanup function | 49 | + | cleanup(a) // <- Call the cleanup function |
| 50 | os.Exit(0) | 50 | os.Exit(0) | ||
| 51 | }() | 51 | }() | ||
| 52 | 52 | ||||
D internal/app/sqlite_test.go +0 -146
| @@ -1,146 +0,0 @@ | |||
| 1 | - | package app_test | |
| 2 | - | ||
| 3 | - | import ( | |
| 4 | - | "database/sql" | |
| 5 | - | "os" | |
| 6 | - | "testing" | |
| 7 | - | ||
| 8 | - | "github.com/bnema/gordon/internal/app" | |
| 9 | - | ) | |
| 10 | - | ||
| 11 | - | // test for CloseAndBackupDB with database modifications (checksum should be different and backup file should be created) | |
| 12 | - | func TestCloseAndBackupDBWithModifications(t *testing.T) { | |
| 13 | - | // Ensure the directory exists | |
| 14 | - | err := os.MkdirAll("./tmp/data", 0755) | |
| 15 | - | if err != nil { | |
| 16 | - | t.Fatalf("Failed to create directory: %v", err) | |
| 17 | - | } | |
| 18 | - | ||
| 19 | - | // Create a temporary file for testing | |
| 20 | - | tempFile, err := os.CreateTemp("./tmp/data", "sqlite.db") | |
| 21 | - | if err != nil { | |
| 22 | - | t.Fatalf("Failed to create temp file: %v", err) | |
| 23 | - | } | |
| 24 | - | tempFile.Close() | |
| 25 | - | ||
| 26 | - | app := &app.App{ | |
| 27 | - | DBFilename: tempFile.Name(), | |
| 28 | - | DBTables: app.DBTables{}, | |
| 29 | - | } | |
| 30 | - | ||
| 31 | - | // Initialize in-memory database | |
| 32 | - | memDb, err := sql.Open("sqlite3", ":memory:") | |
| 33 | - | if err != nil { | |
| 34 | - | t.Fatalf("Failed to open in-memory database: %v", err) | |
| 35 | - | } | |
| 36 | - | ||
| 37 | - | // Initialize the DB field in the App struct | |
| 38 | - | app.DB = memDb | |
| 39 | - | defer memDb.Close() | |
| 40 | - | ||
| 41 | - | // Create table and insert some data | |
| 42 | - | _, err = memDb.Exec("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)") | |
| 43 | - | if err != nil { | |
| 44 | - | t.Fatalf("Failed to create table: %v", err) | |
| 45 | - | } | |
| 46 | - | _, err = memDb.Exec("INSERT INTO users (name) VALUES ('Alice'), ('Bob')") | |
| 47 | - | if err != nil { | |
| 48 | - | t.Fatalf("Failed to insert data: %v", err) | |
| 49 | - | } | |
| 50 | - | ||
| 51 | - | // Generate initial checksum | |
| 52 | - | initialChecksum, err := app.GenerateDBChecksum(memDb, app) | |
| 53 | - | if err != nil { | |
| 54 | - | t.Fatalf("Failed to generate initial checksum: %v", err) | |
| 55 | - | } | |
| 56 | - | ||
| 57 | - | // Validate: Checksum should remain the same | |
| 58 | - | finalChecksum, err := GenerateDBChecksum(memDb, app) | |
| 59 | - | if err != nil { | |
| 60 | - | t.Fatalf("Failed to generate final checksum: %v", err) | |
| 61 | - | } | |
| 62 | - | if initialChecksum != finalChecksum { | |
| 63 | - | t.Fatalf("Checksum mismatch. Expected: %s, Got: %s", initialChecksum, finalChecksum) | |
| 64 | - | } | |
| 65 | - | ||
| 66 | - | // Modify the database | |
| 67 | - | _, err = memDb.Exec("INSERT INTO users (name) VALUES ('Charlie')") | |
| 68 | - | if err != nil { | |
| 69 | - | t.Fatalf("Failed to insert data: %v", err) | |
| 70 | - | } | |
| 71 | - | ||
| 72 | - | // Validate: Checksum should be different | |
| 73 | - | finalChecksum, err = GenerateDBChecksum(memDb, app) | |
| 74 | - | if err != nil { | |
| 75 | - | t.Fatalf("Failed to generate final checksum: %v", err) | |
| 76 | - | } | |
| 77 | - | if initialChecksum == finalChecksum { | |
| 78 | - | t.Fatalf("Checksum mismatch. Expected: %s, Got: %s", initialChecksum, finalChecksum) | |
| 79 | - | } | |
| 80 | - | ||
| 81 | - | // Test: CloseAndBackupDB with modification | |
| 82 | - | err = CloseAndBackupDB(app, memDb) | |
| 83 | - | if err != nil { | |
| 84 | - | t.Fatalf("Failed in CloseAndBackupDB: %v", err) | |
| 85 | - | } | |
| 86 | - | ||
| 87 | - | } | |
| 88 | - | ||
| 89 | - | // test for CloseAndBackupDB with NO database modifications (checksum should remain the same and no backup file should be created) | |
| 90 | - | func TestCloseAndBackupDBWithNoModifications(t *testing.T) { | |
| 91 | - | // Ensure the directory exists | |
| 92 | - | err := os.MkdirAll("./tmp/data", 0755) | |
| 93 | - | if err != nil { | |
| 94 | - | t.Fatalf("Failed to create directory: %v", err) | |
| 95 | - | } | |
| 96 | - | ||
| 97 | - | // Create a temporary file for testing | |
| 98 | - | tempFile, err := os.CreateTemp("./tmp/data", "sqlite.db") | |
| 99 | - | if err != nil { | |
| 100 | - | t.Fatalf("Failed to create temp file: %v", err) | |
| 101 | - | } | |
| 102 | - | tempFile.Close() | |
| 103 | - | ||
| 104 | - | app := &App{ | |
| 105 | - | DBFilename: tempFile.Name(), | |
| 106 | - | } | |
| 107 | - | ||
| 108 | - | // Initialize in-memory database | |
| 109 | - | memDb, err := sql.Open("sqlite3", ":memory:") | |
| 110 | - | if err != nil { | |
| 111 | - | t.Fatalf("Failed to open in-memory database: %v", err) | |
| 112 | - | } | |
| 113 | - | defer memDb.Close() | |
| 114 | - | ||
| 115 | - | // Create table and insert some data | |
| 116 | - | _, err = memDb.Exec("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)") | |
| 117 | - | if err != nil { | |
| 118 | - | t.Fatalf("Failed to create table: %v", err) | |
| 119 | - | } | |
| 120 | - | _, err = memDb.Exec("INSERT INTO users (name) VALUES ('Alice'), ('Bob')") | |
| 121 | - | if err != nil { | |
| 122 | - | t.Fatalf("Failed to insert data: %v", err) | |
| 123 | - | } | |
| 124 | - | ||
| 125 | - | // Generate initial checksum | |
| 126 | - | initialChecksum, err := GenerateDBChecksum(memDb, app) | |
| 127 | - | if err != nil { | |
| 128 | - | t.Fatalf("Failed to generate initial checksum: %v", err) | |
| 129 | - | } | |
| 130 | - | ||
| 131 | - | // Validate: Checksum should remain the same | |
| 132 | - | finalChecksum, err := GenerateDBChecksum(memDb, app) | |
| 133 | - | if err != nil { | |
| 134 | - | t.Fatalf("Failed to generate final checksum: %v", err) | |
| 135 | - | } | |
| 136 | - | if initialChecksum != finalChecksum { | |
| 137 | - | t.Fatalf("Checksum mismatch. Expected: %s, Got: %s", initialChecksum, finalChecksum) | |
| 138 | - | } | |
| 139 | - | ||
| 140 | - | // Test: CloseAndBackupDB with modification | |
| 141 | - | err = CloseAndBackupDB(app, memDb) | |
| 142 | - | if err != nil { | |
| 143 | - | t.Fatalf("Failed in CloseAndBackupDB: %v", err) | |
| 144 | - | } | |
| 145 | - | ||
| 146 | - | } | |
| @@ -1,146 +0,0 @@ | |||||
| 1 | - | package app_test | |||
| 2 | - | ||||
| 3 | - | import ( | |||
| 4 | - | "database/sql" | |||
| 5 | - | "os" | |||
| 6 | - | "testing" | |||
| 7 | - | ||||
| 8 | - | "github.com/bnema/gordon/internal/app" | |||
| 9 | - | ) | |||
| 10 | - | ||||
| 11 | - | // test for CloseAndBackupDB with database modifications (checksum should be different and backup file should be created) | |||
| 12 | - | func TestCloseAndBackupDBWithModifications(t *testing.T) { | |||
| 13 | - | // Ensure the directory exists | |||
| 14 | - | err := os.MkdirAll("./tmp/data", 0755) | |||
| 15 | - | if err != nil { | |||
| 16 | - | t.Fatalf("Failed to create directory: %v", err) | |||
| 17 | - | } | |||
| 18 | - | ||||
| 19 | - | // Create a temporary file for testing | |||
| 20 | - | tempFile, err := os.CreateTemp("./tmp/data", "sqlite.db") | |||
| 21 | - | if err != nil { | |||
| 22 | - | t.Fatalf("Failed to create temp file: %v", err) | |||
| 23 | - | } | |||
| 24 | - | tempFile.Close() | |||
| 25 | - | ||||
| 26 | - | app := &app.App{ | |||
| 27 | - | DBFilename: tempFile.Name(), | |||
| 28 | - | DBTables: app.DBTables{}, | |||
| 29 | - | } | |||
| 30 | - | ||||
| 31 | - | // Initialize in-memory database | |||
| 32 | - | memDb, err := sql.Open("sqlite3", ":memory:") | |||
| 33 | - | if err != nil { | |||
| 34 | - | t.Fatalf("Failed to open in-memory database: %v", err) | |||
| 35 | - | } | |||
| 36 | - | ||||
| 37 | - | // Initialize the DB field in the App struct | |||
| 38 | - | app.DB = memDb | |||
| 39 | - | defer memDb.Close() | |||
| 40 | - | ||||
| 41 | - | // Create table and insert some data | |||
| 42 | - | _, err = memDb.Exec("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)") | |||
| 43 | - | if err != nil { | |||
| 44 | - | t.Fatalf("Failed to create table: %v", err) | |||
| 45 | - | } | |||
| 46 | - | _, err = memDb.Exec("INSERT INTO users (name) VALUES ('Alice'), ('Bob')") | |||
| 47 | - | if err != nil { | |||
| 48 | - | t.Fatalf("Failed to insert data: %v", err) | |||
| 49 | - | } | |||
| 50 | - | ||||
| 51 | - | // Generate initial checksum | |||
| 52 | - | initialChecksum, err := app.GenerateDBChecksum(memDb, app) | |||
| 53 | - | if err != nil { | |||
| 54 | - | t.Fatalf("Failed to generate initial checksum: %v", err) | |||
| 55 | - | } | |||
| 56 | - | ||||
| 57 | - | // Validate: Checksum should remain the same | |||
| 58 | - | finalChecksum, err := GenerateDBChecksum(memDb, app) | |||
| 59 | - | if err != nil { | |||
| 60 | - | t.Fatalf("Failed to generate final checksum: %v", err) | |||
| 61 | - | } | |||
| 62 | - | if initialChecksum != finalChecksum { | |||
| 63 | - | t.Fatalf("Checksum mismatch. Expected: %s, Got: %s", initialChecksum, finalChecksum) | |||
| 64 | - | } | |||
| 65 | - | ||||
| 66 | - | // Modify the database | |||
| 67 | - | _, err = memDb.Exec("INSERT INTO users (name) VALUES ('Charlie')") | |||
| 68 | - | if err != nil { | |||
| 69 | - | t.Fatalf("Failed to insert data: %v", err) | |||
| 70 | - | } | |||
| 71 | - | ||||
| 72 | - | // Validate: Checksum should be different | |||
| 73 | - | finalChecksum, err = GenerateDBChecksum(memDb, app) | |||
| 74 | - | if err != nil { | |||
| 75 | - | t.Fatalf("Failed to generate final checksum: %v", err) | |||
| 76 | - | } | |||
| 77 | - | if initialChecksum == finalChecksum { | |||
| 78 | - | t.Fatalf("Checksum mismatch. Expected: %s, Got: %s", initialChecksum, finalChecksum) | |||
| 79 | - | } | |||
| 80 | - | ||||
| 81 | - | // Test: CloseAndBackupDB with modification | |||
| 82 | - | err = CloseAndBackupDB(app, memDb) | |||
| 83 | - | if err != nil { | |||
| 84 | - | t.Fatalf("Failed in CloseAndBackupDB: %v", err) | |||
| 85 | - | } | |||
| 86 | - | ||||
| 87 | - | } | |||
| 88 | - | ||||
| 89 | - | // test for CloseAndBackupDB with NO database modifications (checksum should remain the same and no backup file should be created) | |||
| 90 | - | func TestCloseAndBackupDBWithNoModifications(t *testing.T) { | |||
| 91 | - | // Ensure the directory exists | |||
| 92 | - | err := os.MkdirAll("./tmp/data", 0755) | |||
| 93 | - | if err != nil { | |||
| 94 | - | t.Fatalf("Failed to create directory: %v", err) | |||
| 95 | - | } | |||
| 96 | - | ||||
| 97 | - | // Create a temporary file for testing | |||
| 98 | - | tempFile, err := os.CreateTemp("./tmp/data", "sqlite.db") | |||
| 99 | - | if err != nil { | |||
| 100 | - | t.Fatalf("Failed to create temp file: %v", err) | |||
| 101 | - | } | |||
| 102 | - | tempFile.Close() | |||
| 103 | - | ||||
| 104 | - | app := &App{ | |||
| 105 | - | DBFilename: tempFile.Name(), | |||
| 106 | - | } | |||
| 107 | - | ||||
| 108 | - | // Initialize in-memory database | |||
| 109 | - | memDb, err := sql.Open("sqlite3", ":memory:") | |||
| 110 | - | if err != nil { | |||
| 111 | - | t.Fatalf("Failed to open in-memory database: %v", err) | |||
| 112 | - | } | |||
| 113 | - | defer memDb.Close() | |||
| 114 | - | ||||
| 115 | - | // Create table and insert some data | |||
| 116 | - | _, err = memDb.Exec("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)") | |||
| 117 | - | if err != nil { | |||
| 118 | - | t.Fatalf("Failed to create table: %v", err) | |||
| 119 | - | } | |||
| 120 | - | _, err = memDb.Exec("INSERT INTO users (name) VALUES ('Alice'), ('Bob')") | |||
| 121 | - | if err != nil { | |||
| 122 | - | t.Fatalf("Failed to insert data: %v", err) | |||
| 123 | - | } | |||
| 124 | - | ||||
| 125 | - | // Generate initial checksum | |||
| 126 | - | initialChecksum, err := GenerateDBChecksum(memDb, app) | |||
| 127 | - | if err != nil { | |||
| 128 | - | t.Fatalf("Failed to generate initial checksum: %v", err) | |||
| 129 | - | } | |||
| 130 | - | ||||
| 131 | - | // Validate: Checksum should remain the same | |||
| 132 | - | finalChecksum, err := GenerateDBChecksum(memDb, app) | |||
| 133 | - | if err != nil { | |||
| 134 | - | t.Fatalf("Failed to generate final checksum: %v", err) | |||
| 135 | - | } | |||
| 136 | - | if initialChecksum != finalChecksum { | |||
| 137 | - | t.Fatalf("Checksum mismatch. Expected: %s, Got: %s", initialChecksum, finalChecksum) | |||
| 138 | - | } | |||
| 139 | - | ||||
| 140 | - | // Test: CloseAndBackupDB with modification | |||
| 141 | - | err = CloseAndBackupDB(app, memDb) | |||
| 142 | - | if err != nil { | |||
| 143 | - | t.Fatalf("Failed in CloseAndBackupDB: %v", err) | |||
| 144 | - | } | |||
| 145 | - | ||||
| 146 | - | } | |||
M internal/app/sqliteclose.go +7 -70
| @@ -1,77 +1,14 @@ | |||
| 1 | 1 | package app | |
| 2 | 2 | ||
| 3 | - | import ( | |
| 4 | - | "database/sql" | |
| 5 | - | "fmt" | |
| 6 | - | "os" | |
| 7 | - | "reflect" | |
| 8 | - | ) | |
| 9 | - | ||
| 10 | - | // GetTableNames returns a list of table names in the database. | |
| 11 | - | func getTableNames(a *App) []string { | |
| 12 | - | var tableNames []string | |
| 13 | - | v := reflect.ValueOf(a.DBTables) | |
| 14 | - | t := v.Type() | |
| 15 | - | for i := 0; i < v.NumField(); i++ { | |
| 16 | - | field := t.Field(i) | |
| 17 | - | sqlTag := field.Tag.Get("sql") | |
| 18 | - | if sqlTag != "" { | |
| 19 | - | tableNames = append(tableNames, sqlTag) | |
| 20 | - | } else { | |
| 21 | - | // Use the field name as a fallback | |
| 22 | - | tableNames = append(tableNames, field.Name) | |
| 23 | - | } | |
| 3 | + | // CloseAndSaveDB closes the in-memory database and backup the database file if there is any modification. | |
| 4 | + | func CloseDB(a *App) error { | |
| 5 | + | if a.DB != nil { | |
| 6 | + | saveDB(a) | |
| 7 | + | a.DB.Close() | |
| 24 | 8 | } | |
| 25 | - | return tableNames | |
| 9 | + | return nil | |
| 26 | 10 | } | |
| 27 | 11 | ||
| 28 | - | // CloseAndBackupDB closes the in-memory database and backup the database file if there is any modification. | |
| 29 | - | func CloseAndBackupDB(a *App, memDb *sql.DB) error { | |
| 30 | - | currentChecksum, err := GenerateDBChecksum(memDb, a) | |
| 31 | - | if err != nil { | |
| 32 | - | return err | |
| 33 | - | } | |
| 34 | - | fmt.Printf("Current checksum: %s\n", currentChecksum) | |
| 35 | - | fmt.Printf("Initial checksum: %s\n", a.InitialChecksum) | |
| 36 | - | ||
| 37 | - | if currentChecksum != a.InitialChecksum { | |
| 38 | - | fmt.Printf("Modification has been found in the database. Current checksum: %s\n", currentChecksum) | |
| 39 | - | ||
| 40 | - | // Backup the current db file | |
| 41 | - | backupPath := a.GetDiskDBFilePath() + ".backup" | |
| 42 | - | if err := os.Rename(a.GetDiskDBFilePath(), backupPath); err != nil { | |
| 43 | - | return err | |
| 44 | - | } | |
| 45 | - | fmt.Printf("Backup file has been created: %s\n", backupPath) | |
| 46 | - | ||
| 47 | - | // Save the in-memory database to disk | |
| 48 | - | _, err = memDb.Exec("ATTACH DATABASE '" + a.GetDiskDBFilePath() + "' AS diskdb") | |
| 49 | - | if err != nil { | |
| 50 | - | return err | |
| 51 | - | } | |
| 52 | - | ||
| 53 | - | tables := getTableNames(a) | |
| 54 | - | ||
| 55 | - | for _, table := range tables { | |
| 56 | - | query := fmt.Sprintf("CREATE TABLE diskdb.%s AS SELECT * FROM main.%s", table, table) | |
| 57 | - | _, err = memDb.Exec(query) | |
| 58 | - | if err != nil { | |
| 59 | - | return err | |
| 60 | - | } | |
| 61 | - | } | |
| 62 | - | ||
| 63 | - | _, err = memDb.Exec("DETACH DATABASE diskdb") | |
| 64 | - | if err != nil { | |
| 65 | - | return err | |
| 66 | - | } | |
| 67 | - | } else { | |
| 68 | - | fmt.Printf("No modification has been found in the database.\n") | |
| 69 | - | } | |
| 70 | - | ||
| 71 | - | if err := memDb.Close(); err != nil { | |
| 72 | - | return err | |
| 73 | - | } | |
| 74 | - | fmt.Printf("In-memory database has been closed gracefully.\n") | |
| 75 | - | ||
| 12 | + | func saveDB(a *App) error { | |
| 76 | 13 | return nil | |
| 77 | 14 | } | |
| @@ -1,77 +1,14 @@ | |||||
| 1 | package app | 1 | package app | ||
| 2 | 2 | ||||
| 3 | - | import ( | 3 | + | // CloseAndSaveDB closes the in-memory database and backup the database file if there is any modification. |
| 4 | - | "database/sql" | 4 | + | func CloseDB(a *App) error { |
| 5 | - | "fmt" | 5 | + | if a.DB != nil { |
| 6 | - | "os" | 6 | + | saveDB(a) |
| 7 | - | "reflect" | 7 | + | a.DB.Close() |
| 8 | - | ) | |||
| 9 | - | ||||
| 10 | - | // GetTableNames returns a list of table names in the database. | |||
| 11 | - | func getTableNames(a *App) []string { | |||
| 12 | - | var tableNames []string | |||
| 13 | - | v := reflect.ValueOf(a.DBTables) | |||
| 14 | - | t := v.Type() | |||
| 15 | - | for i := 0; i < v.NumField(); i++ { | |||
| 16 | - | field := t.Field(i) | |||
| 17 | - | sqlTag := field.Tag.Get("sql") | |||
| 18 | - | if sqlTag != "" { | |||
| 19 | - | tableNames = append(tableNames, sqlTag) | |||
| 20 | - | } else { | |||
| 21 | - | // Use the field name as a fallback | |||
| 22 | - | tableNames = append(tableNames, field.Name) | |||
| 23 | - | } | |||
| 24 | } | 8 | } | ||
| 25 | - | return tableNames | 9 | + | return nil |
| 26 | } | 10 | } | ||
| 27 | 11 | ||||
| 28 | - | // CloseAndBackupDB closes the in-memory database and backup the database file if there is any modification. | 12 | + | func saveDB(a *App) error { |
| 29 | - | func CloseAndBackupDB(a *App, memDb *sql.DB) error { | |||
| 30 | - | currentChecksum, err := GenerateDBChecksum(memDb, a) | |||
| 31 | - | if err != nil { | |||
| 32 | - | return err | |||
| 33 | - | } | |||
| 34 | - | fmt.Printf("Current checksum: %s\n", currentChecksum) | |||
| 35 | - | fmt.Printf("Initial checksum: %s\n", a.InitialChecksum) | |||
| 36 | - | ||||
| 37 | - | if currentChecksum != a.InitialChecksum { | |||
| 38 | - | fmt.Printf("Modification has been found in the database. Current checksum: %s\n", currentChecksum) | |||
| 39 | - | ||||
| 40 | - | // Backup the current db file | |||
| 41 | - | backupPath := a.GetDiskDBFilePath() + ".backup" | |||
| 42 | - | if err := os.Rename(a.GetDiskDBFilePath(), backupPath); err != nil { | |||
| 43 | - | return err | |||
| 44 | - | } | |||
| 45 | - | fmt.Printf("Backup file has been created: %s\n", backupPath) | |||
| 46 | - | ||||
| 47 | - | // Save the in-memory database to disk | |||
| 48 | - | _, err = memDb.Exec("ATTACH DATABASE '" + a.GetDiskDBFilePath() + "' AS diskdb") | |||
| 49 | - | if err != nil { | |||
| 50 | - | return err | |||
| 51 | - | } | |||
| 52 | - | ||||
| 53 | - | tables := getTableNames(a) | |||
| 54 | - | ||||
| 55 | - | for _, table := range tables { | |||
| 56 | - | query := fmt.Sprintf("CREATE TABLE diskdb.%s AS SELECT * FROM main.%s", table, table) | |||
| 57 | - | _, err = memDb.Exec(query) | |||
| 58 | - | if err != nil { | |||
| 59 | - | return err | |||
| 60 | - | } | |||
| 61 | - | } | |||
| 62 | - | ||||
| 63 | - | _, err = memDb.Exec("DETACH DATABASE diskdb") | |||
| 64 | - | if err != nil { | |||
| 65 | - | return err | |||
| 66 | - | } | |||
| 67 | - | } else { | |||
| 68 | - | fmt.Printf("No modification has been found in the database.\n") | |||
| 69 | - | } | |||
| 70 | - | ||||
| 71 | - | if err := memDb.Close(); err != nil { | |||
| 72 | - | return err | |||
| 73 | - | } | |||
| 74 | - | fmt.Printf("In-memory database has been closed gracefully.\n") | |||
| 75 | - | ||||
| 76 | return nil | 13 | return nil | ||
| 77 | } | 14 | } | ||
M internal/app/sqliteinit.go +28 -145
| @@ -1,16 +1,13 @@ | |||
| 1 | 1 | package app | |
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | - | "context" | |
| 5 | - | "crypto/md5" | |
| 6 | 4 | "database/sql" | |
| 7 | - | "encoding/hex" | |
| 8 | 5 | "fmt" | |
| 9 | 6 | "os" | |
| 10 | 7 | "path/filepath" | |
| 11 | 8 | ||
| 12 | 9 | pkgsqlite "github.com/bnema/gordon/pkg/sqlite" | |
| 13 | - | "github.com/mattn/go-sqlite3" | |
| 10 | + | _ "github.com/mattn/go-sqlite3" | |
| 14 | 11 | ) | |
| 15 | 12 | ||
| 16 | 13 | const ( | |
| @@ -18,18 +15,38 @@ const ( | |||
| 18 | 15 | DBFilename = "sqlite.db" | |
| 19 | 16 | ) | |
| 20 | 17 | ||
| 21 | - | func (a *App) GetDiskDBFilePath() string { | |
| 18 | + | func (a *App) getDiskDBFilePath() string { | |
| 22 | 19 | DiskDBfilepath := filepath.Join(a.DBDir, a.DBFilename) | |
| 23 | 20 | return DiskDBfilepath | |
| 24 | 21 | } | |
| 25 | 22 | ||
| 23 | + | // ensureDBDir ensures that the database directory exists. | |
| 24 | + | func (a *App) ensureDBDir() error { | |
| 25 | + | if _, err := os.Stat(a.DBDir); os.IsNotExist(err) { | |
| 26 | + | if err := os.MkdirAll(a.DBDir, 0755); err != nil { | |
| 27 | + | return err | |
| 28 | + | } | |
| 29 | + | } | |
| 30 | + | return nil | |
| 31 | + | } | |
| 32 | + | ||
| 33 | + | // checkDBFile checks if the database file exists. | |
| 34 | + | func (a *App) checkDBFile(dbPath string) (bool, error) { | |
| 35 | + | if _, err := os.Stat(dbPath); os.IsNotExist(err) { | |
| 36 | + | return false, nil | |
| 37 | + | } else if err != nil { | |
| 38 | + | return false, err | |
| 39 | + | } | |
| 40 | + | return true, nil | |
| 41 | + | } | |
| 42 | + | ||
| 26 | 43 | // InitializeDB initializes the database | |
| 27 | 44 | func InitializeDB(a *App) (*sql.DB, error) { | |
| 28 | 45 | if err := a.ensureDBDir(); err != nil { | |
| 29 | 46 | return nil, fmt.Errorf("failed to ensure DB directory: %w", err) | |
| 30 | 47 | } | |
| 31 | 48 | ||
| 32 | - | dbPath := a.GetDiskDBFilePath() | |
| 49 | + | dbPath := a.getDiskDBFilePath() | |
| 33 | 50 | ||
| 34 | 51 | dbExists, err := a.checkDBFile(dbPath) | |
| 35 | 52 | if err != nil { | |
| @@ -41,39 +58,15 @@ func InitializeDB(a *App) (*sql.DB, error) { | |||
| 41 | 58 | return nil, fmt.Errorf("failed to bootstrap DB: %w", err) | |
| 42 | 59 | } | |
| 43 | 60 | } | |
| 44 | - | memDb, err := loadDBToMemory(dbPath) | |
| 45 | - | if err != nil { | |
| 46 | - | return nil, fmt.Errorf("failed to load DB to memory: %w", err) | |
| 47 | - | } | |
| 48 | - | // Here we can generate the initial checksum | |
| 49 | - | a.InitialChecksum, err = GenerateDBChecksum(memDb, a) | |
| 61 | + | ||
| 62 | + | db, err := sql.Open("sqlite3", dbPath) | |
| 50 | 63 | if err != nil { | |
| 51 | - | return nil, fmt.Errorf("failed to generate initial checksum: %w", err) | |
| 64 | + | return nil, fmt.Errorf("failed to open disk DB: %w", err) | |
| 52 | 65 | } | |
| 53 | - | // Initialize the DB field in the App struct | |
| 54 | - | a.DB = memDb | |
| 55 | 66 | ||
| 56 | - | return memDb, nil | |
| 57 | - | } | |
| 67 | + | a.DB = db | |
| 58 | 68 | ||
| 59 | - | // ensureDBDir ensures that the database directory exists. | |
| 60 | - | func (a *App) ensureDBDir() error { | |
| 61 | - | if _, err := os.Stat(a.DBDir); os.IsNotExist(err) { | |
| 62 | - | if err := os.MkdirAll(a.DBDir, 0755); err != nil { | |
| 63 | - | return err | |
| 64 | - | } | |
| 65 | - | } | |
| 66 | - | return nil | |
| 67 | - | } | |
| 68 | - | ||
| 69 | - | // checkDBFile checks if the database file exists. | |
| 70 | - | func (a *App) checkDBFile(dbPath string) (bool, error) { | |
| 71 | - | if _, err := os.Stat(dbPath); os.IsNotExist(err) { | |
| 72 | - | return false, nil | |
| 73 | - | } else if err != nil { | |
| 74 | - | return false, err | |
| 75 | - | } | |
| 76 | - | return true, nil | |
| 69 | + | return db, nil | |
| 77 | 70 | } | |
| 78 | 71 | ||
| 79 | 72 | func bootstrapDB(dbPath string, app *App) error { | |
| @@ -100,113 +93,3 @@ func bootstrapDB(dbPath string, app *App) error { | |||
| 100 | 93 | ||
| 101 | 94 | return nil | |
| 102 | 95 | } | |
| 103 | - | ||
| 104 | - | // loadDBToMemory loads the database from disk to memory. | |
| 105 | - | func loadDBToMemory(dbPath string) (*sql.DB, error) { | |
| 106 | - | memDb, err := sql.Open("sqlite3", ":memory:") | |
| 107 | - | if err != nil { | |
| 108 | - | return nil, err | |
| 109 | - | } | |
| 110 | - | ||
| 111 | - | diskDb, err := sql.Open("sqlite3", dbPath) | |
| 112 | - | if err != nil { | |
| 113 | - | return nil, err | |
| 114 | - | } | |
| 115 | - | defer diskDb.Close() | |
| 116 | - | ||
| 117 | - | // Get a single connection from each *sql.DB | |
| 118 | - | memConn, err := memDb.Conn(context.Background()) | |
| 119 | - | if err != nil { | |
| 120 | - | return nil, err | |
| 121 | - | } | |
| 122 | - | defer memConn.Close() | |
| 123 | - | ||
| 124 | - | diskConn, err := diskDb.Conn(context.Background()) | |
| 125 | - | if err != nil { | |
| 126 | - | return nil, err | |
| 127 | - | } | |
| 128 | - | defer diskConn.Close() | |
| 129 | - | ||
| 130 | - | err = memConn.Raw(func(driverConn interface{}) error { | |
| 131 | - | memSqliteConn, ok := driverConn.(*sqlite3.SQLiteConn) | |
| 132 | - | if !ok { | |
| 133 | - | return fmt.Errorf("failed to cast to *sqlite3.SQLiteConn") | |
| 134 | - | } | |
| 135 | - | ||
| 136 | - | return diskConn.Raw(func(driverConn interface{}) error { | |
| 137 | - | diskSqliteConn, ok := driverConn.(*sqlite3.SQLiteConn) | |
| 138 | - | if !ok { | |
| 139 | - | return fmt.Errorf("failed to cast to *sqlite3.SQLiteConn") | |
| 140 | - | } | |
| 141 | - | ||
| 142 | - | backup, err := memSqliteConn.Backup("main", diskSqliteConn, "main") | |
| 143 | - | if err != nil { | |
| 144 | - | return err | |
| 145 | - | } | |
| 146 | - | defer backup.Close() | |
| 147 | - | ||
| 148 | - | done, err := backup.Step(-1) | |
| 149 | - | if err != nil { | |
| 150 | - | return err | |
| 151 | - | } | |
| 152 | - | if !done { | |
| 153 | - | return fmt.Errorf("backup not fully completed") | |
| 154 | - | } | |
| 155 | - | ||
| 156 | - | return backup.Finish() | |
| 157 | - | }) | |
| 158 | - | }) | |
| 159 | - | ||
| 160 | - | if err != nil { | |
| 161 | - | return nil, err | |
| 162 | - | } | |
| 163 | - | ||
| 164 | - | return memDb, nil | |
| 165 | - | } | |
| 166 | - | ||
| 167 | - | // GenerateDBChecksum generates a checksum of the database. | |
| 168 | - | func GenerateDBChecksum(db *sql.DB, a *App) (string, error) { | |
| 169 | - | tables := getTableNames(a) | |
| 170 | - | var finalChecksum []byte | |
| 171 | - | ||
| 172 | - | for _, table := range tables { | |
| 173 | - | hasher := md5.New() // Create a new hasher for each table | |
| 174 | - | ||
| 175 | - | rows, err := db.Query("SELECT * FROM " + table) | |
| 176 | - | if err != nil { | |
| 177 | - | return "", err | |
| 178 | - | } | |
| 179 | - | ||
| 180 | - | for rows.Next() { | |
| 181 | - | cols, err := rows.Columns() | |
| 182 | - | if err != nil { | |
| 183 | - | return "", err | |
| 184 | - | } | |
| 185 | - | ||
| 186 | - | vals := make([]interface{}, len(cols)) | |
| 187 | - | for i := range cols { | |
| 188 | - | vals[i] = new(sql.RawBytes) | |
| 189 | - | } | |
| 190 | - | ||
| 191 | - | err = rows.Scan(vals...) | |
| 192 | - | if err != nil { | |
| 193 | - | return "", err | |
| 194 | - | } | |
| 195 | - | ||
| 196 | - | for i := range vals { | |
| 197 | - | hasher.Write(*vals[i].(*sql.RawBytes)) | |
| 198 | - | } | |
| 199 | - | } | |
| 200 | - | ||
| 201 | - | if err := rows.Err(); err != nil { | |
| 202 | - | rows.Close() | |
| 203 | - | return "", err | |
| 204 | - | } | |
| 205 | - | ||
| 206 | - | rows.Close() | |
| 207 | - | ||
| 208 | - | finalChecksum = append(finalChecksum, hasher.Sum(nil)...) | |
| 209 | - | } | |
| 210 | - | ||
| 211 | - | return hex.EncodeToString(finalChecksum), nil | |
| 212 | - | } | |
| @@ -1,16 +1,13 @@ | |||||
| 1 | package app | 1 | package app | ||
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | - | "context" | |||
| 5 | - | "crypto/md5" | |||
| 6 | "database/sql" | 4 | "database/sql" | ||
| 7 | - | "encoding/hex" | |||
| 8 | "fmt" | 5 | "fmt" | ||
| 9 | "os" | 6 | "os" | ||
| 10 | "path/filepath" | 7 | "path/filepath" | ||
| 11 | 8 | ||||
| 12 | pkgsqlite "github.com/bnema/gordon/pkg/sqlite" | 9 | pkgsqlite "github.com/bnema/gordon/pkg/sqlite" | ||
| 13 | - | "github.com/mattn/go-sqlite3" | 10 | + | _ "github.com/mattn/go-sqlite3" |
| 14 | ) | 11 | ) | ||
| 15 | 12 | ||||
| 16 | const ( | 13 | const ( | ||
| @@ -18,18 +15,38 @@ const ( | |||||
| 18 | DBFilename = "sqlite.db" | 15 | DBFilename = "sqlite.db" | ||
| 19 | ) | 16 | ) | ||
| 20 | 17 | ||||
| 21 | - | func (a *App) GetDiskDBFilePath() string { | 18 | + | func (a *App) getDiskDBFilePath() string { |
| 22 | DiskDBfilepath := filepath.Join(a.DBDir, a.DBFilename) | 19 | DiskDBfilepath := filepath.Join(a.DBDir, a.DBFilename) | ||
| 23 | return DiskDBfilepath | 20 | return DiskDBfilepath | ||
| 24 | } | 21 | } | ||
| 25 | 22 | ||||
| 23 | + | // ensureDBDir ensures that the database directory exists. | |||
| 24 | + | func (a *App) ensureDBDir() error { | |||
| 25 | + | if _, err := os.Stat(a.DBDir); os.IsNotExist(err) { | |||
| 26 | + | if err := os.MkdirAll(a.DBDir, 0755); err != nil { | |||
| 27 | + | return err | |||
| 28 | + | } | |||
| 29 | + | } | |||
| 30 | + | return nil | |||
| 31 | + | } | |||
| 32 | + | ||||
| 33 | + | // checkDBFile checks if the database file exists. | |||
| 34 | + | func (a *App) checkDBFile(dbPath string) (bool, error) { | |||
| 35 | + | if _, err := os.Stat(dbPath); os.IsNotExist(err) { | |||
| 36 | + | return false, nil | |||
| 37 | + | } else if err != nil { | |||
| 38 | + | return false, err | |||
| 39 | + | } | |||
| 40 | + | return true, nil | |||
| 41 | + | } | |||
| 42 | + | ||||
| 26 | // InitializeDB initializes the database | 43 | // InitializeDB initializes the database | ||
| 27 | func InitializeDB(a *App) (*sql.DB, error) { | 44 | func InitializeDB(a *App) (*sql.DB, error) { | ||
| 28 | if err := a.ensureDBDir(); err != nil { | 45 | if err := a.ensureDBDir(); err != nil { | ||
| 29 | return nil, fmt.Errorf("failed to ensure DB directory: %w", err) | 46 | return nil, fmt.Errorf("failed to ensure DB directory: %w", err) | ||
| 30 | } | 47 | } | ||
| 31 | 48 | ||||
| 32 | - | dbPath := a.GetDiskDBFilePath() | 49 | + | dbPath := a.getDiskDBFilePath() |
| 33 | 50 | ||||
| 34 | dbExists, err := a.checkDBFile(dbPath) | 51 | dbExists, err := a.checkDBFile(dbPath) | ||
| 35 | if err != nil { | 52 | if err != nil { | ||
| @@ -41,39 +58,15 @@ func InitializeDB(a *App) (*sql.DB, error) { | |||||
| 41 | return nil, fmt.Errorf("failed to bootstrap DB: %w", err) | 58 | return nil, fmt.Errorf("failed to bootstrap DB: %w", err) | ||
| 42 | } | 59 | } | ||
| 43 | } | 60 | } | ||
| 44 | - | memDb, err := loadDBToMemory(dbPath) | 61 | + | |
| 45 | - | if err != nil { | 62 | + | db, err := sql.Open("sqlite3", dbPath) |
| 46 | - | return nil, fmt.Errorf("failed to load DB to memory: %w", err) | |||
| 47 | - | } | |||
| 48 | - | // Here we can generate the initial checksum | |||
| 49 | - | a.InitialChecksum, err = GenerateDBChecksum(memDb, a) | |||
| 50 | if err != nil { | 63 | if err != nil { | ||
| 51 | - | return nil, fmt.Errorf("failed to generate initial checksum: %w", err) | 64 | + | return nil, fmt.Errorf("failed to open disk DB: %w", err) |
| 52 | } | 65 | } | ||
| 53 | - | // Initialize the DB field in the App struct | |||
| 54 | - | a.DB = memDb | |||
| 55 | 66 | ||||
| 56 | - | return memDb, nil | 67 | + | a.DB = db |
| 57 | - | } | |||
| 58 | 68 | ||||
| 59 | - | // ensureDBDir ensures that the database directory exists. | 69 | + | return db, nil |
| 60 | - | func (a *App) ensureDBDir() error { | |||
| 61 | - | if _, err := os.Stat(a.DBDir); os.IsNotExist(err) { | |||
| 62 | - | if err := os.MkdirAll(a.DBDir, 0755); err != nil { | |||
| 63 | - | return err | |||
| 64 | - | } | |||
| 65 | - | } | |||
| 66 | - | return nil | |||
| 67 | - | } | |||
| 68 | - | ||||
| 69 | - | // checkDBFile checks if the database file exists. | |||
| 70 | - | func (a *App) checkDBFile(dbPath string) (bool, error) { | |||
| 71 | - | if _, err := os.Stat(dbPath); os.IsNotExist(err) { | |||
| 72 | - | return false, nil | |||
| 73 | - | } else if err != nil { | |||
| 74 | - | return false, err | |||
| 75 | - | } | |||
| 76 | - | return true, nil | |||
| 77 | } | 70 | } | ||
| 78 | 71 | ||||
| 79 | func bootstrapDB(dbPath string, app *App) error { | 72 | func bootstrapDB(dbPath string, app *App) error { | ||
| @@ -100,113 +93,3 @@ func bootstrapDB(dbPath string, app *App) error { | |||||
| 100 | 93 | ||||
| 101 | return nil | 94 | return nil | ||
| 102 | } | 95 | } | ||
| 103 | - | ||||
| 104 | - | // loadDBToMemory loads the database from disk to memory. | |||
| 105 | - | func loadDBToMemory(dbPath string) (*sql.DB, error) { | |||
| 106 | - | memDb, err := sql.Open("sqlite3", ":memory:") | |||
| 107 | - | if err != nil { | |||
| 108 | - | return nil, err | |||
| 109 | - | } | |||
| 110 | - | ||||
| 111 | - | diskDb, err := sql.Open("sqlite3", dbPath) | |||
| 112 | - | if err != nil { | |||
| 113 | - | return nil, err | |||
| 114 | - | } | |||
| 115 | - | defer diskDb.Close() | |||
| 116 | - | ||||
| 117 | - | // Get a single connection from each *sql.DB | |||
| 118 | - | memConn, err := memDb.Conn(context.Background()) | |||
| 119 | - | if err != nil { | |||
| 120 | - | return nil, err | |||
| 121 | - | } | |||
| 122 | - | defer memConn.Close() | |||
| 123 | - | ||||
| 124 | - | diskConn, err := diskDb.Conn(context.Background()) | |||
| 125 | - | if err != nil { | |||
| 126 | - | return nil, err | |||
| 127 | - | } | |||
| 128 | - | defer diskConn.Close() | |||
| 129 | - | ||||
| 130 | - | err = memConn.Raw(func(driverConn interface{}) error { | |||
| 131 | - | memSqliteConn, ok := driverConn.(*sqlite3.SQLiteConn) | |||
| 132 | - | if !ok { | |||
| 133 | - | return fmt.Errorf("failed to cast to *sqlite3.SQLiteConn") | |||
| 134 | - | } | |||
| 135 | - | ||||
| 136 | - | return diskConn.Raw(func(driverConn interface{}) error { | |||
| 137 | - | diskSqliteConn, ok := driverConn.(*sqlite3.SQLiteConn) | |||
| 138 | - | if !ok { | |||
| 139 | - | return fmt.Errorf("failed to cast to *sqlite3.SQLiteConn") | |||
| 140 | - | } | |||
| 141 | - | ||||
| 142 | - | backup, err := memSqliteConn.Backup("main", diskSqliteConn, "main") | |||
| 143 | - | if err != nil { | |||
| 144 | - | return err | |||
| 145 | - | } | |||
| 146 | - | defer backup.Close() | |||
| 147 | - | ||||
| 148 | - | done, err := backup.Step(-1) | |||
| 149 | - | if err != nil { | |||
| 150 | - | return err | |||
| 151 | - | } | |||
| 152 | - | if !done { | |||
| 153 | - | return fmt.Errorf("backup not fully completed") | |||
| 154 | - | } | |||
| 155 | - | ||||
| 156 | - | return backup.Finish() | |||
| 157 | - | }) | |||
| 158 | - | }) | |||
| 159 | - | ||||
| 160 | - | if err != nil { | |||
| 161 | - | return nil, err | |||
| 162 | - | } | |||
| 163 | - | ||||
| 164 | - | return memDb, nil | |||
| 165 | - | } | |||
| 166 | - | ||||
| 167 | - | // GenerateDBChecksum generates a checksum of the database. | |||
| 168 | - | func GenerateDBChecksum(db *sql.DB, a *App) (string, error) { | |||
| 169 | - | tables := getTableNames(a) | |||
| 170 | - | var finalChecksum []byte | |||
| 171 | - | ||||
| 172 | - | for _, table := range tables { | |||
| 173 | - | hasher := md5.New() // Create a new hasher for each table | |||
| 174 | - | ||||
| 175 | - | rows, err := db.Query("SELECT * FROM " + table) | |||
| 176 | - | if err != nil { | |||
| 177 | - | return "", err | |||
| 178 | - | } | |||
| 179 | - | ||||
| 180 | - | for rows.Next() { | |||
| 181 | - | cols, err := rows.Columns() | |||
| 182 | - | if err != nil { | |||
| 183 | - | return "", err | |||
| 184 | - | } | |||
| 185 | - | ||||
| 186 | - | vals := make([]interface{}, len(cols)) | |||
| 187 | - | for i := range cols { | |||
| 188 | - | vals[i] = new(sql.RawBytes) | |||
| 189 | - | } | |||
| 190 | - | ||||
| 191 | - | err = rows.Scan(vals...) | |||
| 192 | - | if err != nil { | |||
| 193 | - | return "", err | |||
| 194 | - | } | |||
| 195 | - | ||||
| 196 | - | for i := range vals { | |||
| 197 | - | hasher.Write(*vals[i].(*sql.RawBytes)) | |||
| 198 | - | } | |||
| 199 | - | } | |||
| 200 | - | ||||
| 201 | - | if err := rows.Err(); err != nil { | |||
| 202 | - | rows.Close() | |||
| 203 | - | return "", err | |||
| 204 | - | } | |||
| 205 | - | ||||
| 206 | - | rows.Close() | |||
| 207 | - | ||||
| 208 | - | finalChecksum = append(finalChecksum, hasher.Sum(nil)...) | |||
| 209 | - | } | |||
| 210 | - | ||||
| 211 | - | return hex.EncodeToString(finalChecksum), nil | |||
| 212 | - | } | |||
M internal/app/tokeninit.go +7 -7
| @@ -6,19 +6,19 @@ import ( | |||
| 6 | 6 | "fmt" | |
| 7 | 7 | ) | |
| 8 | 8 | ||
| 9 | - | // HandleNewInitialization checks if an admin user exists in db, and if not, generates a token for the initial login and stores it in the config file | |
| 10 | - | func (a *App) HandleNewTokenInitialization() (string, error) { | |
| 11 | - | // Query to check if a user with id=1 exists | |
| 12 | - | query := `SELECT COUNT(id) FROM user WHERE id = "1"` | |
| 9 | + | // HandleNewInitialization checks if there is ANY user in the database. | |
| 10 | + | func HandleNewTokenInitialization(a *App) (string, error) { | |
| 11 | + | // Check if there is any user in the database | |
| 12 | + | query := "SELECT COUNT(*) FROM user" | |
| 13 | 13 | var count int | |
| 14 | 14 | err := a.DB.QueryRow(query).Scan(&count) | |
| 15 | 15 | if err != nil { | |
| 16 | 16 | return "", fmt.Errorf("failed to check for admin user: %v", err) | |
| 17 | 17 | } | |
| 18 | 18 | ||
| 19 | - | // If count is 1, it means admin user already exists | |
| 20 | - | if count == 1 { | |
| 21 | - | return "", nil | |
| 19 | + | // If count is greater than 0, it means there is at least one user in the database | |
| 20 | + | if count > 0 { | |
| 21 | + | return "", fmt.Errorf("admin user already exists") | |
| 22 | 22 | } | |
| 23 | 23 | ||
| 24 | 24 | // If we reach here, it means admin does not exist, so we generate a token | |
| @@ -6,19 +6,19 @@ import ( | |||||
| 6 | "fmt" | 6 | "fmt" | ||
| 7 | ) | 7 | ) | ||
| 8 | 8 | ||||
| 9 | - | // HandleNewInitialization checks if an admin user exists in db, and if not, generates a token for the initial login and stores it in the config file | 9 | + | // HandleNewInitialization checks if there is ANY user in the database. |
| 10 | - | func (a *App) HandleNewTokenInitialization() (string, error) { | 10 | + | func HandleNewTokenInitialization(a *App) (string, error) { |
| 11 | - | // Query to check if a user with id=1 exists | 11 | + | // Check if there is any user in the database |
| 12 | - | query := `SELECT COUNT(id) FROM user WHERE id = "1"` | 12 | + | query := "SELECT COUNT(*) FROM user" |
| 13 | var count int | 13 | var count int | ||
| 14 | err := a.DB.QueryRow(query).Scan(&count) | 14 | err := a.DB.QueryRow(query).Scan(&count) | ||
| 15 | if err != nil { | 15 | if err != nil { | ||
| 16 | return "", fmt.Errorf("failed to check for admin user: %v", err) | 16 | return "", fmt.Errorf("failed to check for admin user: %v", err) | ||
| 17 | } | 17 | } | ||
| 18 | 18 | ||||
| 19 | - | // If count is 1, it means admin user already exists | 19 | + | // If count is greater than 0, it means there is at least one user in the database |
| 20 | - | if count == 1 { | 20 | + | if count > 0 { |
| 21 | - | return "", nil | 21 | + | return "", fmt.Errorf("admin user already exists") |
| 22 | } | 22 | } | ||
| 23 | 23 | ||||
| 24 | // If we reach here, it means admin does not exist, so we generate a token | 24 | // If we reach here, it means admin does not exist, so we generate a token | ||