rewrite sqlite.go logic flow
- commit
- fac544b5b2b0d4a849c8602cf021fc8554c609b3
- author
- Brice · · 2023-09-15 05:39 UTC
- parent
- 2dbb406
- signature
- unsigned
8 files · +120 -38
A :memory +0 -0 binary file: no text diff
binary file: no text diff
M cmd/main.go +14 -1
| @@ -4,17 +4,30 @@ import ( | |||
| 4 | 4 | "fmt" | |
| 5 | 5 | "log/slog" | |
| 6 | 6 | ||
| 7 | + | "github.com/labstack/echo/v4" | |
| 8 | + | ||
| 7 | 9 | "github.com/bnema/gordon/internal/app" | |
| 8 | 10 | "github.com/bnema/gordon/internal/httpserve" | |
| 9 | - | "github.com/labstack/echo/v4" | |
| 10 | 11 | ) | |
| 11 | 12 | ||
| 12 | 13 | func main() { | |
| 13 | 14 | a := app.NewApp() | |
| 15 | + | ||
| 16 | + | // Initialize database | |
| 17 | + | db, err := app.InitializeDB(a) | |
| 18 | + | if err != nil { | |
| 19 | + | slog.Error("Failed to load database", err) | |
| 20 | + | } else { | |
| 21 | + | slog.Info("Database loaded") | |
| 22 | + | } | |
| 23 | + | ||
| 24 | + | fmt.Println(db) | |
| 25 | + | ||
| 14 | 26 | e := echo.New() | |
| 15 | 27 | e.HideBanner = true | |
| 16 | 28 | e.HidePort = true | |
| 17 | 29 | e = httpserve.RegisterRoutes(e, a) | |
| 18 | 30 | slog.Info("Starting server", "port", a.HttpPort) | |
| 19 | 31 | slog.Error("Server error", e.Start(fmt.Sprintf(":%d", a.HttpPort))) | |
| 32 | + | ||
| 20 | 33 | } | |
| @@ -4,17 +4,30 @@ import ( | |||||
| 4 | "fmt" | 4 | "fmt" | ||
| 5 | "log/slog" | 5 | "log/slog" | ||
| 6 | 6 | ||||
| 7 | + | "github.com/labstack/echo/v4" | |||
| 8 | + | ||||
| 7 | "github.com/bnema/gordon/internal/app" | 9 | "github.com/bnema/gordon/internal/app" | ||
| 8 | "github.com/bnema/gordon/internal/httpserve" | 10 | "github.com/bnema/gordon/internal/httpserve" | ||
| 9 | - | "github.com/labstack/echo/v4" | |||
| 10 | ) | 11 | ) | ||
| 11 | 12 | ||||
| 12 | func main() { | 13 | func main() { | ||
| 13 | a := app.NewApp() | 14 | a := app.NewApp() | ||
| 15 | + | ||||
| 16 | + | // Initialize database | |||
| 17 | + | db, err := app.InitializeDB(a) | |||
| 18 | + | if err != nil { | |||
| 19 | + | slog.Error("Failed to load database", err) | |||
| 20 | + | } else { | |||
| 21 | + | slog.Info("Database loaded") | |||
| 22 | + | } | |||
| 23 | + | ||||
| 24 | + | fmt.Println(db) | |||
| 25 | + | ||||
| 14 | e := echo.New() | 26 | e := echo.New() | ||
| 15 | e.HideBanner = true | 27 | e.HideBanner = true | ||
| 16 | e.HidePort = true | 28 | e.HidePort = true | ||
| 17 | e = httpserve.RegisterRoutes(e, a) | 29 | e = httpserve.RegisterRoutes(e, a) | ||
| 18 | slog.Info("Starting server", "port", a.HttpPort) | 30 | slog.Info("Starting server", "port", a.HttpPort) | ||
| 19 | slog.Error("Server error", e.Start(fmt.Sprintf(":%d", a.HttpPort))) | 31 | slog.Error("Server error", e.Start(fmt.Sprintf(":%d", a.HttpPort))) | ||
| 32 | + | ||||
| 20 | } | 33 | } | ||
M go.mod +3 -0
| @@ -24,6 +24,7 @@ require ( | |||
| 24 | 24 | github.com/gorilla/securecookie v1.1.1 // indirect | |
| 25 | 25 | github.com/gorilla/sessions v1.2.1 // indirect | |
| 26 | 26 | github.com/klauspost/compress v1.16.7 // indirect | |
| 27 | + | github.com/kr/text v0.1.0 // indirect | |
| 27 | 28 | github.com/labstack/gommon v0.4.0 // indirect | |
| 28 | 29 | github.com/mattn/go-colorable v0.1.13 // indirect | |
| 29 | 30 | github.com/mattn/go-isatty v0.0.19 // indirect | |
| @@ -35,6 +36,7 @@ require ( | |||
| 35 | 36 | github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b // indirect | |
| 36 | 37 | github.com/opencontainers/runc v1.1.9 // indirect | |
| 37 | 38 | github.com/pkg/errors v0.9.1 // indirect | |
| 39 | + | github.com/rogpeppe/go-internal v1.11.0 // indirect | |
| 38 | 40 | github.com/sirupsen/logrus v1.9.3 // indirect | |
| 39 | 41 | github.com/valyala/bytebufferpool v1.0.0 // indirect | |
| 40 | 42 | github.com/valyala/fasttemplate v1.2.2 // indirect | |
| @@ -45,5 +47,6 @@ require ( | |||
| 45 | 47 | golang.org/x/text v0.11.0 // indirect | |
| 46 | 48 | golang.org/x/time v0.3.0 // indirect | |
| 47 | 49 | golang.org/x/tools v0.7.0 // indirect | |
| 50 | + | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect | |
| 48 | 51 | gotest.tools/v3 v3.5.0 // indirect | |
| 49 | 52 | ) | |
| @@ -24,6 +24,7 @@ require ( | |||||
| 24 | github.com/gorilla/securecookie v1.1.1 // indirect | 24 | github.com/gorilla/securecookie v1.1.1 // indirect | ||
| 25 | github.com/gorilla/sessions v1.2.1 // indirect | 25 | github.com/gorilla/sessions v1.2.1 // indirect | ||
| 26 | github.com/klauspost/compress v1.16.7 // indirect | 26 | github.com/klauspost/compress v1.16.7 // indirect | ||
| 27 | + | github.com/kr/text v0.1.0 // indirect | |||
| 27 | github.com/labstack/gommon v0.4.0 // indirect | 28 | github.com/labstack/gommon v0.4.0 // indirect | ||
| 28 | github.com/mattn/go-colorable v0.1.13 // indirect | 29 | github.com/mattn/go-colorable v0.1.13 // indirect | ||
| 29 | github.com/mattn/go-isatty v0.0.19 // indirect | 30 | github.com/mattn/go-isatty v0.0.19 // indirect | ||
| @@ -35,6 +36,7 @@ require ( | |||||
| 35 | github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b // indirect | 36 | github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b // indirect | ||
| 36 | github.com/opencontainers/runc v1.1.9 // indirect | 37 | github.com/opencontainers/runc v1.1.9 // indirect | ||
| 37 | github.com/pkg/errors v0.9.1 // indirect | 38 | github.com/pkg/errors v0.9.1 // indirect | ||
| 39 | + | github.com/rogpeppe/go-internal v1.11.0 // indirect | |||
| 38 | github.com/sirupsen/logrus v1.9.3 // indirect | 40 | github.com/sirupsen/logrus v1.9.3 // indirect | ||
| 39 | github.com/valyala/bytebufferpool v1.0.0 // indirect | 41 | github.com/valyala/bytebufferpool v1.0.0 // indirect | ||
| 40 | github.com/valyala/fasttemplate v1.2.2 // indirect | 42 | github.com/valyala/fasttemplate v1.2.2 // indirect | ||
| @@ -45,5 +47,6 @@ require ( | |||||
| 45 | golang.org/x/text v0.11.0 // indirect | 47 | golang.org/x/text v0.11.0 // indirect | ||
| 46 | golang.org/x/time v0.3.0 // indirect | 48 | golang.org/x/time v0.3.0 // indirect | ||
| 47 | golang.org/x/tools v0.7.0 // indirect | 49 | golang.org/x/tools v0.7.0 // indirect | ||
| 50 | + | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect | |||
| 48 | gotest.tools/v3 v3.5.0 // indirect | 51 | gotest.tools/v3 v3.5.0 // indirect | ||
| 49 | ) | 52 | ) | ||
M go.sum +9 -1 generated file: collapsed
M internal/app/app.go +1 -0
| @@ -27,6 +27,7 @@ const ( | |||
| 27 | 27 | type App struct { | |
| 28 | 28 | TemplateFS fs.FS | |
| 29 | 29 | PublicFS fs.FS | |
| 30 | + | DB interface{} | |
| 30 | 31 | BuildVersion string | |
| 31 | 32 | BuildDir string | |
| 32 | 33 | DBDir string | |
| @@ -27,6 +27,7 @@ const ( | |||||
| 27 | type App struct { | 27 | type App struct { | ||
| 28 | TemplateFS fs.FS | 28 | TemplateFS fs.FS | ||
| 29 | PublicFS fs.FS | 29 | PublicFS fs.FS | ||
| 30 | + | DB interface{} | |||
| 30 | BuildVersion string | 31 | BuildVersion string | ||
| 31 | BuildDir string | 32 | BuildDir string | ||
| 32 | DBDir string | 33 | DBDir string | ||
M internal/app/sqlite.go +69 -34
| @@ -5,58 +5,78 @@ import ( | |||
| 5 | 5 | "database/sql" | |
| 6 | 6 | "fmt" | |
| 7 | 7 | "os" | |
| 8 | + | "path/filepath" | |
| 8 | 9 | ||
| 9 | - | sqlite3 "github.com/mattn/go-sqlite3" | |
| 10 | + | "github.com/bnema/gordon/internal/database/migrate" | |
| 11 | + | "github.com/mattn/go-sqlite3" | |
| 10 | 12 | ) | |
| 11 | 13 | ||
| 12 | - | // Initialize in-memory SQLite database and returns its connection | |
| 13 | - | func InitDB() (db *sql.DB, err error) { | |
| 14 | - | db, err = sql.Open("sqlite3", "file::memory?cache=shared") | |
| 15 | - | if err != nil { | |
| 16 | - | return nil, fmt.Errorf("failed to open in-memory database: %v", err) | |
| 17 | - | } | |
| 18 | - | return | |
| 19 | - | } | |
| 20 | - | ||
| 21 | - | // CloseDB closes the database connection and backs up the in-memory database to disk | |
| 22 | - | func CloseDB(db *sql.DB, a *App) error { | |
| 23 | - | if err := BackupDB(db, a); err != nil { | |
| 24 | - | return err | |
| 25 | - | } | |
| 14 | + | // InitializeDB initializes the SQLite database. If a database file exists on disk, it loads it into memory. | |
| 15 | + | // Otherwise, it creates a new in-memory database and bootstraps it. | |
| 16 | + | func InitializeDB(a *App) (*sql.DB, error) { | |
| 17 | + | dbfp := a.GetDBFilePath() | |
| 26 | 18 | ||
| 27 | - | if err := db.Close(); err != nil { | |
| 28 | - | return fmt.Errorf("failed to close database: %v", err) | |
| 19 | + | // Check if the directory exists, if not create it | |
| 20 | + | dir := filepath.Dir(a.DBDir) | |
| 21 | + | fmt.Println("dir", dir) | |
| 22 | + | if _, err := os.Stat(dir); os.IsNotExist(err) { | |
| 23 | + | err = os.MkdirAll(dir, 0755) | |
| 24 | + | if err != nil { | |
| 25 | + | return nil, fmt.Errorf("failed to create directory: %v", err) | |
| 26 | + | } | |
| 29 | 27 | } | |
| 30 | 28 | ||
| 31 | - | return nil | |
| 32 | - | } | |
| 29 | + | // If the database file doesn't exist on disk | |
| 30 | + | if _, err := os.Stat(dbfp); os.IsNotExist(err) { | |
| 31 | + | memDB, err := initInMemoryDB() | |
| 32 | + | if err != nil { | |
| 33 | + | return nil, err | |
| 34 | + | } | |
| 33 | 35 | ||
| 34 | - | // LoadDBIntoMemory loads the database from disk into memory | |
| 35 | - | func LoadDBIntoMemory(a *App) (*sql.DB, error) { | |
| 36 | - | dbfp := a.GetDBFilePath() | |
| 36 | + | // Run the bootstrap | |
| 37 | + | RunBootstrap(memDB) | |
| 37 | 38 | ||
| 38 | - | if _, err := os.Stat(dbfp); err != nil { | |
| 39 | - | return nil, nil | |
| 39 | + | // Explicitly backup the in-memory database to disk | |
| 40 | + | err = BackupDB(memDB, a) | |
| 41 | + | if err != nil { | |
| 42 | + | return nil, fmt.Errorf("failed to backup in-memory database to disk: %v", err) | |
| 43 | + | } | |
| 40 | 44 | } | |
| 41 | 45 | ||
| 42 | - | memDB, err := InitDB() | |
| 46 | + | memDB, err := initInMemoryDB() | |
| 43 | 47 | if err != nil { | |
| 44 | 48 | return nil, err | |
| 45 | 49 | } | |
| 46 | - | ||
| 47 | 50 | diskDB, err := sql.Open("sqlite3", dbfp) | |
| 48 | 51 | if err != nil { | |
| 49 | 52 | return nil, fmt.Errorf("failed to open database file: %v", err) | |
| 50 | 53 | } | |
| 54 | + | defer diskDB.Close() | |
| 51 | 55 | ||
| 52 | 56 | if err := transferData(diskDB, memDB); err != nil { | |
| 53 | 57 | return nil, err | |
| 54 | 58 | } | |
| 55 | - | ||
| 56 | 59 | return memDB, nil | |
| 57 | 60 | } | |
| 58 | 61 | ||
| 59 | - | // transferData transfers data from srcDB to dstDB | |
| 62 | + | func initInMemoryDB() (*sql.DB, error) { | |
| 63 | + | db, err := sql.Open("sqlite3", "file::memory?cache=shared") | |
| 64 | + | if err != nil { | |
| 65 | + | return nil, fmt.Errorf("failed to open in-memory database: %v", err) | |
| 66 | + | } | |
| 67 | + | return db, nil | |
| 68 | + | } | |
| 69 | + | ||
| 70 | + | func CloseAndBackupDB(db *sql.DB, a *App) error { | |
| 71 | + | if err := BackupDB(db, a); err != nil { | |
| 72 | + | return err | |
| 73 | + | } | |
| 74 | + | if err := db.Close(); err != nil { | |
| 75 | + | return fmt.Errorf("failed to close database: %v", err) | |
| 76 | + | } | |
| 77 | + | return nil | |
| 78 | + | } | |
| 79 | + | ||
| 60 | 80 | func transferData(srcDB, dstDB *sql.DB) error { | |
| 61 | 81 | srcConn, err := srcDB.Conn(context.Background()) | |
| 62 | 82 | if err != nil { | |
| @@ -74,35 +94,50 @@ func transferData(srcDB, dstDB *sql.DB) error { | |||
| 74 | 94 | return dstConn.Raw(func(dstRawConn interface{}) error { | |
| 75 | 95 | srcSqlite3Conn := srcRawConn.(*sqlite3.SQLiteConn) | |
| 76 | 96 | dstSqlite3Conn := dstRawConn.(*sqlite3.SQLiteConn) | |
| 77 | - | ||
| 78 | 97 | _, err := srcSqlite3Conn.Backup("main", dstSqlite3Conn, "main") | |
| 79 | 98 | return err | |
| 80 | 99 | }) | |
| 81 | 100 | }) | |
| 82 | 101 | } | |
| 83 | 102 | ||
| 84 | - | // BackupDB backs up the in-memory database to disk | |
| 85 | 103 | func BackupDB(db *sql.DB, a *App) error { | |
| 86 | 104 | dbfp := a.GetDBFilePath() | |
| 87 | - | ||
| 88 | 105 | conn, err := db.Conn(context.Background()) | |
| 89 | 106 | if err != nil { | |
| 90 | 107 | return fmt.Errorf("failed to get underlying database connection: %v", err) | |
| 91 | 108 | } | |
| 109 | + | defer conn.Close() | |
| 92 | 110 | ||
| 93 | 111 | return conn.Raw(func(sqliteConn interface{}) error { | |
| 94 | 112 | sqlite3Conn := sqliteConn.(*sqlite3.SQLiteConn) | |
| 95 | - | backupDB, err := sqlite3Conn.Backup(dbfp, sqlite3Conn, "main") | |
| 113 | + | diskDB, err := sql.Open("sqlite3", dbfp) | |
| 114 | + | fmt.Println("diskDB", diskDB) | |
| 115 | + | if err != nil { | |
| 116 | + | return fmt.Errorf("failed to open destination database file: %v", err) | |
| 117 | + | } | |
| 118 | + | defer diskDB.Close() | |
| 119 | + | ||
| 120 | + | diskConn, err := diskDB.Conn(context.Background()) | |
| 96 | 121 | if err != nil { | |
| 97 | - | return fmt.Errorf("failed to create backup: %v", err) | |
| 122 | + | return fmt.Errorf("failed to get connection from diskDB: %v", err) | |
| 98 | 123 | } | |
| 124 | + | defer diskConn.Close() | |
| 125 | + | ||
| 126 | + | var backupDB *sqlite3.SQLiteBackup | |
| 127 | + | err = diskConn.Raw(func(diskRawConn interface{}) error { | |
| 128 | + | backupDB, err = sqlite3Conn.Backup("main", diskRawConn.(*sqlite3.SQLiteConn), "main") | |
| 129 | + | return err | |
| 130 | + | }) | |
| 99 | 131 | defer backupDB.Close() | |
| 100 | 132 | ||
| 101 | 133 | _, err = backupDB.Step(-1) | |
| 102 | 134 | if err != nil { | |
| 103 | 135 | return fmt.Errorf("failed to backup in-memory database: %v", err) | |
| 104 | 136 | } | |
| 105 | - | ||
| 106 | 137 | return nil | |
| 107 | 138 | }) | |
| 108 | 139 | } | |
| 140 | + | ||
| 141 | + | func RunBootstrap(db *sql.DB) error { | |
| 142 | + | return migrate.CreateUserTable(db) | |
| 143 | + | } | |
| @@ -5,58 +5,78 @@ import ( | |||||
| 5 | "database/sql" | 5 | "database/sql" | ||
| 6 | "fmt" | 6 | "fmt" | ||
| 7 | "os" | 7 | "os" | ||
| 8 | + | "path/filepath" | |||
| 8 | 9 | ||||
| 9 | - | sqlite3 "github.com/mattn/go-sqlite3" | 10 | + | "github.com/bnema/gordon/internal/database/migrate" |
| 11 | + | "github.com/mattn/go-sqlite3" | |||
| 10 | ) | 12 | ) | ||
| 11 | 13 | ||||
| 12 | - | // Initialize in-memory SQLite database and returns its connection | 14 | + | // InitializeDB initializes the SQLite database. If a database file exists on disk, it loads it into memory. |
| 13 | - | func InitDB() (db *sql.DB, err error) { | 15 | + | // Otherwise, it creates a new in-memory database and bootstraps it. |
| 14 | - | db, err = sql.Open("sqlite3", "file::memory?cache=shared") | 16 | + | func InitializeDB(a *App) (*sql.DB, error) { |
| 15 | - | if err != nil { | 17 | + | dbfp := a.GetDBFilePath() |
| 16 | - | return nil, fmt.Errorf("failed to open in-memory database: %v", err) | |||
| 17 | - | } | |||
| 18 | - | return | |||
| 19 | - | } | |||
| 20 | - | ||||
| 21 | - | // CloseDB closes the database connection and backs up the in-memory database to disk | |||
| 22 | - | func CloseDB(db *sql.DB, a *App) error { | |||
| 23 | - | if err := BackupDB(db, a); err != nil { | |||
| 24 | - | return err | |||
| 25 | - | } | |||
| 26 | 18 | ||||
| 27 | - | if err := db.Close(); err != nil { | 19 | + | // Check if the directory exists, if not create it |
| 28 | - | return fmt.Errorf("failed to close database: %v", err) | 20 | + | dir := filepath.Dir(a.DBDir) |
| 21 | + | fmt.Println("dir", dir) | |||
| 22 | + | if _, err := os.Stat(dir); os.IsNotExist(err) { | |||
| 23 | + | err = os.MkdirAll(dir, 0755) | |||
| 24 | + | if err != nil { | |||
| 25 | + | return nil, fmt.Errorf("failed to create directory: %v", err) | |||
| 26 | + | } | |||
| 29 | } | 27 | } | ||
| 30 | 28 | ||||
| 31 | - | return nil | 29 | + | // If the database file doesn't exist on disk |
| 32 | - | } | 30 | + | if _, err := os.Stat(dbfp); os.IsNotExist(err) { |
| 31 | + | memDB, err := initInMemoryDB() | |||
| 32 | + | if err != nil { | |||
| 33 | + | return nil, err | |||
| 34 | + | } | |||
| 33 | 35 | ||||
| 34 | - | // LoadDBIntoMemory loads the database from disk into memory | 36 | + | // Run the bootstrap |
| 35 | - | func LoadDBIntoMemory(a *App) (*sql.DB, error) { | 37 | + | RunBootstrap(memDB) |
| 36 | - | dbfp := a.GetDBFilePath() | |||
| 37 | 38 | ||||
| 38 | - | if _, err := os.Stat(dbfp); err != nil { | 39 | + | // Explicitly backup the in-memory database to disk |
| 39 | - | return nil, nil | 40 | + | err = BackupDB(memDB, a) |
| 41 | + | if err != nil { | |||
| 42 | + | return nil, fmt.Errorf("failed to backup in-memory database to disk: %v", err) | |||
| 43 | + | } | |||
| 40 | } | 44 | } | ||
| 41 | 45 | ||||
| 42 | - | memDB, err := InitDB() | 46 | + | memDB, err := initInMemoryDB() |
| 43 | if err != nil { | 47 | if err != nil { | ||
| 44 | return nil, err | 48 | return nil, err | ||
| 45 | } | 49 | } | ||
| 46 | - | ||||
| 47 | diskDB, err := sql.Open("sqlite3", dbfp) | 50 | diskDB, err := sql.Open("sqlite3", dbfp) | ||
| 48 | if err != nil { | 51 | if err != nil { | ||
| 49 | return nil, fmt.Errorf("failed to open database file: %v", err) | 52 | return nil, fmt.Errorf("failed to open database file: %v", err) | ||
| 50 | } | 53 | } | ||
| 54 | + | defer diskDB.Close() | |||
| 51 | 55 | ||||
| 52 | if err := transferData(diskDB, memDB); err != nil { | 56 | if err := transferData(diskDB, memDB); err != nil { | ||
| 53 | return nil, err | 57 | return nil, err | ||
| 54 | } | 58 | } | ||
| 55 | - | ||||
| 56 | return memDB, nil | 59 | return memDB, nil | ||
| 57 | } | 60 | } | ||
| 58 | 61 | ||||
| 59 | - | // transferData transfers data from srcDB to dstDB | 62 | + | func initInMemoryDB() (*sql.DB, error) { |
| 63 | + | db, err := sql.Open("sqlite3", "file::memory?cache=shared") | |||
| 64 | + | if err != nil { | |||
| 65 | + | return nil, fmt.Errorf("failed to open in-memory database: %v", err) | |||
| 66 | + | } | |||
| 67 | + | return db, nil | |||
| 68 | + | } | |||
| 69 | + | ||||
| 70 | + | func CloseAndBackupDB(db *sql.DB, a *App) error { | |||
| 71 | + | if err := BackupDB(db, a); err != nil { | |||
| 72 | + | return err | |||
| 73 | + | } | |||
| 74 | + | if err := db.Close(); err != nil { | |||
| 75 | + | return fmt.Errorf("failed to close database: %v", err) | |||
| 76 | + | } | |||
| 77 | + | return nil | |||
| 78 | + | } | |||
| 79 | + | ||||
| 60 | func transferData(srcDB, dstDB *sql.DB) error { | 80 | func transferData(srcDB, dstDB *sql.DB) error { | ||
| 61 | srcConn, err := srcDB.Conn(context.Background()) | 81 | srcConn, err := srcDB.Conn(context.Background()) | ||
| 62 | if err != nil { | 82 | if err != nil { | ||
| @@ -74,35 +94,50 @@ func transferData(srcDB, dstDB *sql.DB) error { | |||||
| 74 | return dstConn.Raw(func(dstRawConn interface{}) error { | 94 | return dstConn.Raw(func(dstRawConn interface{}) error { | ||
| 75 | srcSqlite3Conn := srcRawConn.(*sqlite3.SQLiteConn) | 95 | srcSqlite3Conn := srcRawConn.(*sqlite3.SQLiteConn) | ||
| 76 | dstSqlite3Conn := dstRawConn.(*sqlite3.SQLiteConn) | 96 | dstSqlite3Conn := dstRawConn.(*sqlite3.SQLiteConn) | ||
| 77 | - | ||||
| 78 | _, err := srcSqlite3Conn.Backup("main", dstSqlite3Conn, "main") | 97 | _, err := srcSqlite3Conn.Backup("main", dstSqlite3Conn, "main") | ||
| 79 | return err | 98 | return err | ||
| 80 | }) | 99 | }) | ||
| 81 | }) | 100 | }) | ||
| 82 | } | 101 | } | ||
| 83 | 102 | ||||
| 84 | - | // BackupDB backs up the in-memory database to disk | |||
| 85 | func BackupDB(db *sql.DB, a *App) error { | 103 | func BackupDB(db *sql.DB, a *App) error { | ||
| 86 | dbfp := a.GetDBFilePath() | 104 | dbfp := a.GetDBFilePath() | ||
| 87 | - | ||||
| 88 | conn, err := db.Conn(context.Background()) | 105 | conn, err := db.Conn(context.Background()) | ||
| 89 | if err != nil { | 106 | if err != nil { | ||
| 90 | return fmt.Errorf("failed to get underlying database connection: %v", err) | 107 | return fmt.Errorf("failed to get underlying database connection: %v", err) | ||
| 91 | } | 108 | } | ||
| 109 | + | defer conn.Close() | |||
| 92 | 110 | ||||
| 93 | return conn.Raw(func(sqliteConn interface{}) error { | 111 | return conn.Raw(func(sqliteConn interface{}) error { | ||
| 94 | sqlite3Conn := sqliteConn.(*sqlite3.SQLiteConn) | 112 | sqlite3Conn := sqliteConn.(*sqlite3.SQLiteConn) | ||
| 95 | - | backupDB, err := sqlite3Conn.Backup(dbfp, sqlite3Conn, "main") | 113 | + | diskDB, err := sql.Open("sqlite3", dbfp) |
| 114 | + | fmt.Println("diskDB", diskDB) | |||
| 115 | + | if err != nil { | |||
| 116 | + | return fmt.Errorf("failed to open destination database file: %v", err) | |||
| 117 | + | } | |||
| 118 | + | defer diskDB.Close() | |||
| 119 | + | ||||
| 120 | + | diskConn, err := diskDB.Conn(context.Background()) | |||
| 96 | if err != nil { | 121 | if err != nil { | ||
| 97 | - | return fmt.Errorf("failed to create backup: %v", err) | 122 | + | return fmt.Errorf("failed to get connection from diskDB: %v", err) |
| 98 | } | 123 | } | ||
| 124 | + | defer diskConn.Close() | |||
| 125 | + | ||||
| 126 | + | var backupDB *sqlite3.SQLiteBackup | |||
| 127 | + | err = diskConn.Raw(func(diskRawConn interface{}) error { | |||
| 128 | + | backupDB, err = sqlite3Conn.Backup("main", diskRawConn.(*sqlite3.SQLiteConn), "main") | |||
| 129 | + | return err | |||
| 130 | + | }) | |||
| 99 | defer backupDB.Close() | 131 | defer backupDB.Close() | ||
| 100 | 132 | ||||
| 101 | _, err = backupDB.Step(-1) | 133 | _, err = backupDB.Step(-1) | ||
| 102 | if err != nil { | 134 | if err != nil { | ||
| 103 | return fmt.Errorf("failed to backup in-memory database: %v", err) | 135 | return fmt.Errorf("failed to backup in-memory database: %v", err) | ||
| 104 | } | 136 | } | ||
| 105 | - | ||||
| 106 | return nil | 137 | return nil | ||
| 107 | }) | 138 | }) | ||
| 108 | } | 139 | } | ||
| 140 | + | ||||
| 141 | + | func RunBootstrap(db *sql.DB) error { | |||
| 142 | + | return migrate.CreateUserTable(db) | |||
| 143 | + | } | |||
A internal/database/migrate/bootstrapdb.go +24 -0
| @@ -0,0 +1,24 @@ | |||
| 1 | + | package migrate | |
| 2 | + | ||
| 3 | + | import ( | |
| 4 | + | "database/sql" | |
| 5 | + | "fmt" | |
| 6 | + | ) | |
| 7 | + | ||
| 8 | + | // createUserTable creates the 'user' table in the provided SQLite database. | |
| 9 | + | func CreateUserTable(db *sql.DB) error { | |
| 10 | + | createTableSQL := ` | |
| 11 | + | CREATE TABLE user ( | |
| 12 | + | id INTEGER PRIMARY KEY AUTOINCREMENT, | |
| 13 | + | username TEXT NOT NULL, | |
| 14 | + | password TEXT NOT NULL, -- This should be hashed and salted in a real-world scenario. | |
| 15 | + | oauth_token TEXT | |
| 16 | + | );` | |
| 17 | + | ||
| 18 | + | _, err := db.Exec(createTableSQL) | |
| 19 | + | if err != nil { | |
| 20 | + | return fmt.Errorf("failed to create user table: %v", err) | |
| 21 | + | } | |
| 22 | + | ||
| 23 | + | return nil | |
| 24 | + | } | |
| @@ -0,0 +1,24 @@ | |||||
| 1 | + | package migrate | |||
| 2 | + | ||||
| 3 | + | import ( | |||
| 4 | + | "database/sql" | |||
| 5 | + | "fmt" | |||
| 6 | + | ) | |||
| 7 | + | ||||
| 8 | + | // createUserTable creates the 'user' table in the provided SQLite database. | |||
| 9 | + | func CreateUserTable(db *sql.DB) error { | |||
| 10 | + | createTableSQL := ` | |||
| 11 | + | CREATE TABLE user ( | |||
| 12 | + | id INTEGER PRIMARY KEY AUTOINCREMENT, | |||
| 13 | + | username TEXT NOT NULL, | |||
| 14 | + | password TEXT NOT NULL, -- This should be hashed and salted in a real-world scenario. | |||
| 15 | + | oauth_token TEXT | |||
| 16 | + | );` | |||
| 17 | + | ||||
| 18 | + | _, err := db.Exec(createTableSQL) | |||
| 19 | + | if err != nil { | |||
| 20 | + | return fmt.Errorf("failed to create user table: %v", err) | |||
| 21 | + | } | |||
| 22 | + | ||||
| 23 | + | return nil | |||
| 24 | + | } | |||
M internal/httpserve/handler/adminroutes.go +0 -2
| @@ -1,8 +1,6 @@ | |||
| 1 | 1 | package handler | |
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | - | "fmt" | |
| 5 | - | ||
| 6 | 4 | "github.com/bnema/gordon/internal/app" | |
| 7 | 5 | "github.com/bnema/gordon/internal/gotemplate/render" | |
| 8 | 6 | "github.com/bnema/gordon/internal/httpserve/middleware" | |
| @@ -1,8 +1,6 @@ | |||||
| 1 | package handler | 1 | package handler | ||
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | - | "fmt" | |||
| 5 | - | ||||
| 6 | "github.com/bnema/gordon/internal/app" | 4 | "github.com/bnema/gordon/internal/app" | ||
| 7 | "github.com/bnema/gordon/internal/gotemplate/render" | 5 | "github.com/bnema/gordon/internal/gotemplate/render" | ||
| 8 | "github.com/bnema/gordon/internal/httpserve/middleware" | 6 | "github.com/bnema/gordon/internal/httpserve/middleware" | ||