0.0.970 cdbcd03

sqliteinit.go RawHistoryFolder

1package server
2
3import (
4	"database/sql"
5	"fmt"
6	"os"
7	"path/filepath"
8
9	"github.com/bnema/gordon/internal/db"
10	pkgsqlite "github.com/bnema/gordon/pkg/sqlite"
11	_ "modernc.org/sqlite"
12)
13
14const (
15	DBFilename = "sqlite.db"
16)
17
18func (a *App) getDiskDBFilePath() string {
19	DiskDBfilepath := filepath.Join(a.DBDir, a.DBFilename)
20	return DiskDBfilepath
21}
22
23// ensureDBDir ensures that the database directory exists.
24func (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.
34func (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
43// InitializeDB initializes the database
44func InitializeDB(a *App) (*sql.DB, error) {
45	if err := a.ensureDBDir(); err != nil {
46		return nil, fmt.Errorf("failed to ensure DB directory: %w", err)
47	}
48
49	dbPath := a.getDiskDBFilePath()
50
51	dbExists, err := a.checkDBFile(dbPath)
52	if err != nil {
53		return nil, fmt.Errorf("failed to check DB file: %w", err)
54	}
55
56	if !dbExists {
57		if err := bootstrapDB(dbPath, a); err != nil {
58			return nil, fmt.Errorf("failed to bootstrap DB: %w", err)
59		}
60	}
61
62	// Change here from "sqlite3" to "sqlite"
63	db, err := sql.Open("sqlite", dbPath)
64	if err != nil {
65		return nil, fmt.Errorf("failed to open disk DB: %w", err)
66	}
67
68	a.DB = db
69
70	if dbExists {
71		// Populate the struct tables from the database
72		if err := PopulateStructWithTables(a); err != nil {
73			return nil, err
74		}
75	}
76
77	return db, nil
78}
79
80func bootstrapDB(dbPath string, app *App) error {
81	// Change here from "sqlite3" to "sqlite"
82	db, err := sql.Open("sqlite", dbPath)
83	if err != nil {
84		return err
85	}
86	defer db.Close()
87
88	tables := app.DBTables
89
90	if err := pkgsqlite.CreateTable(db, tables.User, "user"); err != nil {
91		return err
92	}
93	if err := pkgsqlite.CreateTable(db, tables.Account, "account"); err != nil {
94		return err
95	}
96	if err := pkgsqlite.CreateTable(db, tables.Sessions, "sessions"); err != nil {
97		return err
98	}
99	if err := pkgsqlite.CreateTable(db, tables.Provider, "provider"); err != nil {
100		return err
101	}
102
103	return nil
104}
105
106type DBTable interface {
107	ScanFromRows(rows *sql.Rows) error
108}
109
110type UserWrapper struct {
111	*db.User
112}
113
114type AccountWrapper struct {
115	*db.Account
116}
117
118type ProviderWrapper struct {
119	*db.Provider
120}
121
122type SessionsWrapper struct {
123	*db.Sessions
124}
125
126func populateTableFromDB(a *App, query string, table DBTable) error {
127	rows, err := a.DB.Query(query)
128	if err != nil {
129		return err
130	}
131	defer rows.Close()
132
133	for rows.Next() {
134		if err := table.ScanFromRows(rows); err != nil {
135			return err
136		}
137	}
138	return rows.Err()
139}
140
141// Implementation of DBTable for User, Account, Provider, Sessions
142func (u *UserWrapper) ScanFromRows(rows *sql.Rows) error {
143	return rows.Scan(&u.ID, &u.Name, &u.Email)
144}
145
146func (a *AccountWrapper) ScanFromRows(rows *sql.Rows) error {
147	return rows.Scan(&a.ID, &a.UserID)
148}
149
150func (p *ProviderWrapper) ScanFromRows(rows *sql.Rows) error {
151	return rows.Scan(&p.ID, &p.AccountID, &p.Name, &p.Login, &p.AvatarURL, &p.ProfileURL, &p.Email)
152}
153
154func (s *SessionsWrapper) ScanFromRows(rows *sql.Rows) error {
155	return rows.Scan(&s.ID, &s.AccountID, &s.AccessToken, &s.BrowserInfo, &s.Expires, &s.IsOnline)
156}
157
158// PopulateStructWithTables updates db struct with tables
159func PopulateStructWithTables(a *App) error {
160	tables := map[string]DBTable{
161		"SELECT id, name, email FROM user":                                                    &UserWrapper{&a.DBTables.User},
162		"SELECT id, user_id FROM account":                                                     &AccountWrapper{&a.DBTables.Account},
163		"SELECT id, account_id, name, login, avatar_url, profile_url, email FROM provider":    &ProviderWrapper{&a.DBTables.Provider},
164		"SELECT id, account_id, access_token, browser_info, expires, is_online FROM sessions": &SessionsWrapper{&a.DBTables.Sessions},
165	}
166
167	for query, table := range tables {
168		if err := populateTableFromDB(a, query, table); err != nil {
169			return err
170		}
171	}
172
173	return nil
174}

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