detached fac544b

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 (
44 "fmt"
55 "log/slog"
66
7+ "github.com/labstack/echo/v4"
8+
79 "github.com/bnema/gordon/internal/app"
810 "github.com/bnema/gordon/internal/httpserve"
9- "github.com/labstack/echo/v4"
1011 )
1112
1213 func main() {
1314 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+
1426 e := echo.New()
1527 e.HideBanner = true
1628 e.HidePort = true
1729 e = httpserve.RegisterRoutes(e, a)
1830 slog.Info("Starting server", "port", a.HttpPort)
1931 slog.Error("Server error", e.Start(fmt.Sprintf(":%d", a.HttpPort)))
32+
2033 }
@@ -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 = true27 e.HideBanner = true
16 e.HidePort = true28 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 (
2424 github.com/gorilla/securecookie v1.1.1 // indirect
2525 github.com/gorilla/sessions v1.2.1 // indirect
2626 github.com/klauspost/compress v1.16.7 // indirect
27+ github.com/kr/text v0.1.0 // indirect
2728 github.com/labstack/gommon v0.4.0 // indirect
2829 github.com/mattn/go-colorable v0.1.13 // indirect
2930 github.com/mattn/go-isatty v0.0.19 // indirect
@@ -35,6 +36,7 @@ require (
3536 github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b // indirect
3637 github.com/opencontainers/runc v1.1.9 // indirect
3738 github.com/pkg/errors v0.9.1 // indirect
39+ github.com/rogpeppe/go-internal v1.11.0 // indirect
3840 github.com/sirupsen/logrus v1.9.3 // indirect
3941 github.com/valyala/bytebufferpool v1.0.0 // indirect
4042 github.com/valyala/fasttemplate v1.2.2 // indirect
@@ -45,5 +47,6 @@ require (
4547 golang.org/x/text v0.11.0 // indirect
4648 golang.org/x/time v0.3.0 // indirect
4749 golang.org/x/tools v0.7.0 // indirect
50+ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
4851 gotest.tools/v3 v3.5.0 // indirect
4952 )
@@ -24,6 +24,7 @@ require (
24 github.com/gorilla/securecookie v1.1.1 // indirect24 github.com/gorilla/securecookie v1.1.1 // indirect
25 github.com/gorilla/sessions v1.2.1 // indirect25 github.com/gorilla/sessions v1.2.1 // indirect
26 github.com/klauspost/compress v1.16.7 // indirect26 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 // indirect28 github.com/labstack/gommon v0.4.0 // indirect
28 github.com/mattn/go-colorable v0.1.13 // indirect29 github.com/mattn/go-colorable v0.1.13 // indirect
29 github.com/mattn/go-isatty v0.0.19 // indirect30 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 // indirect36 github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b // indirect
36 github.com/opencontainers/runc v1.1.9 // indirect37 github.com/opencontainers/runc v1.1.9 // indirect
37 github.com/pkg/errors v0.9.1 // indirect38 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 // indirect40 github.com/sirupsen/logrus v1.9.3 // indirect
39 github.com/valyala/bytebufferpool v1.0.0 // indirect41 github.com/valyala/bytebufferpool v1.0.0 // indirect
40 github.com/valyala/fasttemplate v1.2.2 // indirect42 github.com/valyala/fasttemplate v1.2.2 // indirect
@@ -45,5 +47,6 @@ require (
45 golang.org/x/text v0.11.0 // indirect47 golang.org/x/text v0.11.0 // indirect
46 golang.org/x/time v0.3.0 // indirect48 golang.org/x/time v0.3.0 // indirect
47 golang.org/x/tools v0.7.0 // indirect49 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 // indirect51 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 (
2727 type App struct {
2828 TemplateFS fs.FS
2929 PublicFS fs.FS
30+ DB interface{}
3031 BuildVersion string
3132 BuildDir string
3233 DBDir string
@@ -27,6 +27,7 @@ const (
27 type App struct {27 type App struct {
28 TemplateFS fs.FS28 TemplateFS fs.FS
29 PublicFS fs.FS29 PublicFS fs.FS
30+ DB interface{}
30 BuildVersion string31 BuildVersion string
31 BuildDir string32 BuildDir string
32 DBDir string33 DBDir string
M internal/app/sqlite.go +69 -34
@@ -5,58 +5,78 @@ import (
55 "database/sql"
66 "fmt"
77 "os"
8+ "path/filepath"
89
9- sqlite3 "github.com/mattn/go-sqlite3"
10+ "github.com/bnema/gordon/internal/database/migrate"
11+ "github.com/mattn/go-sqlite3"
1012 )
1113
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()
2618
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+ }
2927 }
3028
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+ }
3335
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)
3738
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+ }
4044 }
4145
42- memDB, err := InitDB()
46+ memDB, err := initInMemoryDB()
4347 if err != nil {
4448 return nil, err
4549 }
46-
4750 diskDB, err := sql.Open("sqlite3", dbfp)
4851 if err != nil {
4952 return nil, fmt.Errorf("failed to open database file: %v", err)
5053 }
54+ defer diskDB.Close()
5155
5256 if err := transferData(diskDB, memDB); err != nil {
5357 return nil, err
5458 }
55-
5659 return memDB, nil
5760 }
5861
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+
6080 func transferData(srcDB, dstDB *sql.DB) error {
6181 srcConn, err := srcDB.Conn(context.Background())
6282 if err != nil {
@@ -74,35 +94,50 @@ func transferData(srcDB, dstDB *sql.DB) error {
7494 return dstConn.Raw(func(dstRawConn interface{}) error {
7595 srcSqlite3Conn := srcRawConn.(*sqlite3.SQLiteConn)
7696 dstSqlite3Conn := dstRawConn.(*sqlite3.SQLiteConn)
77-
7897 _, err := srcSqlite3Conn.Backup("main", dstSqlite3Conn, "main")
7998 return err
8099 })
81100 })
82101 }
83102
84-// BackupDB backs up the in-memory database to disk
85103 func BackupDB(db *sql.DB, a *App) error {
86104 dbfp := a.GetDBFilePath()
87-
88105 conn, err := db.Conn(context.Background())
89106 if err != nil {
90107 return fmt.Errorf("failed to get underlying database connection: %v", err)
91108 }
109+ defer conn.Close()
92110
93111 return conn.Raw(func(sqliteConn interface{}) error {
94112 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())
96121 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)
98123 }
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+ })
99131 defer backupDB.Close()
100132
101133 _, err = backupDB.Step(-1)
102134 if err != nil {
103135 return fmt.Errorf("failed to backup in-memory database: %v", err)
104136 }
105-
106137 return nil
107138 })
108139 }
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 connection14+// 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 nil29+ // 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 memory36+ // 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, nil40+ 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, err48 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, err57 return nil, err
54 }58 }
55-
56 return memDB, nil59 return memDB, nil
57 }60 }
58 61
59-// transferData transfers data from srcDB to dstDB62+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 err98 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 nil137 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 @@
11 package handler
22
33 import (
4- "fmt"
5-
64 "github.com/bnema/gordon/internal/app"
75 "github.com/bnema/gordon/internal/gotemplate/render"
86 "github.com/bnema/gordon/internal/httpserve/middleware"
@@ -1,8 +1,6 @@
1 package handler1 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"

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 7

Tags 106

↑↓ moveenter openesc close