0.1.9 85724f1

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	// if a.DBDir is empty return log "Set a path using storageDir in the config file"
21	return DiskDBfilepath
22}
23
24// ensureDBDir ensures that the database directory exists.
25func (a *App) ensureDBDir() error {
26	if _, err := os.Stat(a.DBDir); os.IsNotExist(err) {
27		if err := os.MkdirAll(a.DBDir, 0755); err != nil {
28			return err
29		}
30	}
31	return nil
32}
33
34// checkDBFile checks if the database file exists.
35func (a *App) checkDBFile(dbPath string) (bool, error) {
36	if _, err := os.Stat(dbPath); os.IsNotExist(err) {
37		return false, nil
38	} else if err != nil {
39		return false, err
40	}
41	return true, nil
42}
43
44// InitializeDB initializes the database
45func InitializeDB(a *App) (*sql.DB, error) {
46	if err := a.ensureDBDir(); err != nil {
47		return nil, fmt.Errorf("failed to ensure DB directory: %w", err)
48	}
49
50	dbPath := a.getDiskDBFilePath()
51
52	dbExists, err := a.checkDBFile(dbPath)
53	if err != nil {
54		return nil, fmt.Errorf("failed to check DB file: %w", err)
55	}
56
57	if !dbExists {
58		if err := bootstrapDB(dbPath, a); err != nil {
59			return nil, fmt.Errorf("failed to bootstrap DB: %w", err)
60		}
61	}
62
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	db, err := sql.Open("sqlite", dbPath)
82	if err != nil {
83		return err
84	}
85	defer db.Close()
86
87	tables := app.DBTables
88
89	if err := pkgsqlite.CreateTable(db, tables.User, "user"); err != nil {
90		return err
91	}
92	if err := pkgsqlite.CreateTable(db, tables.Account, "account"); err != nil {
93		return err
94	}
95	if err := pkgsqlite.CreateTable(db, tables.Sessions, "sessions"); err != nil {
96		return err
97	}
98	if err := pkgsqlite.CreateTable(db, tables.Provider, "provider"); err != nil {
99		return err
100	}
101
102	if err := pkgsqlite.CreateTable(db, tables.Clients, "clients"); err != nil {
103		return err
104	}
105
106	return nil
107}
108
109type DBTable interface {
110	ScanFromRows(rows *sql.Rows) error
111}
112
113type UserWrapper struct {
114	*db.User
115}
116
117type AccountWrapper struct {
118	*db.Account
119}
120
121type ProviderWrapper struct {
122	*db.Provider
123}
124
125type SessionsWrapper struct {
126	*db.Sessions
127}
128
129type ClientsWrapper struct {
130	*db.Clients
131}
132
133func populateTableFromDB(a *App, query string, table DBTable) error {
134	rows, err := a.DB.Query(query)
135	if err != nil {
136		return err
137	}
138	defer rows.Close()
139
140	for rows.Next() {
141		if err := table.ScanFromRows(rows); err != nil {
142			return err
143		}
144	}
145	return rows.Err()
146}
147
148// Implementation of DBTable for User, Account, Provider, Sessions
149func (u *UserWrapper) ScanFromRows(rows *sql.Rows) error {
150	return rows.Scan(&u.ID, &u.Name, &u.Email)
151}
152
153func (a *AccountWrapper) ScanFromRows(rows *sql.Rows) error {
154	return rows.Scan(&a.ID, &a.UserID)
155}
156
157func (p *ProviderWrapper) ScanFromRows(rows *sql.Rows) error {
158	return rows.Scan(&p.ID, &p.AccountID, &p.Name, &p.Login, &p.AvatarURL, &p.ProfileURL, &p.Email)
159}
160
161func (s *SessionsWrapper) ScanFromRows(rows *sql.Rows) error {
162	return rows.Scan(&s.ID, &s.AccountID, &s.AccessToken, &s.BrowserInfo, &s.Expires, &s.IsOnline)
163}
164
165func (c *ClientsWrapper) ScanFromRows(rows *sql.Rows) error {
166	return rows.Scan(&c.ID, &c.AccountID, &c.OS, &c.IP, &c.Hostname, &c.Expires)
167}
168
169// PopulateStructWithTables updates db struct with tables
170func PopulateStructWithTables(a *App) error {
171	tables := map[string]DBTable{
172		"SELECT id, name, email FROM user":                                                    &UserWrapper{&a.DBTables.User},
173		"SELECT id, user_id FROM account":                                                     &AccountWrapper{&a.DBTables.Account},
174		"SELECT id, account_id, name, login, avatar_url, profile_url, email FROM provider":    &ProviderWrapper{&a.DBTables.Provider},
175		"SELECT id, account_id, access_token, browser_info, expires, is_online FROM sessions": &SessionsWrapper{&a.DBTables.Sessions},
176		"SELECT id, account_id, os, ip, hostname, expires FROM clients":                       &ClientsWrapper{&a.DBTables.Clients},
177	}
178
179	for query, table := range tables {
180		if err := populateTableFromDB(a, query, table); err != nil {
181			return err
182		}
183	}
184
185	return nil
186}

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