repos / git-pr

a self-hosted git collaboration server
git clone https://github.com/picosh/git-pr.git

commit
7b83c3e
parent
966a19a
author
Eric Bower
date
2024-10-21 13:25:30 -0400 EDT
refactor: separate models from sqlite db

We will likely want to support postgresql so this change will make that
easier.
5 files changed,  +96, -106
M ssh.go
M web.go
M backend.go
+2, -1
 1@@ -8,12 +8,13 @@ import (
 2 
 3 	"github.com/charmbracelet/soft-serve/pkg/utils"
 4 	"github.com/charmbracelet/ssh"
 5+	"github.com/jmoiron/sqlx"
 6 	gossh "golang.org/x/crypto/ssh"
 7 )
 8 
 9 type Backend struct {
10 	Logger *slog.Logger
11-	DB     *DB
12+	DB     *sqlx.DB
13 	Cfg    *GitCfg
14 }
15 
A models.go
+85, -0
 1@@ -0,0 +1,85 @@
 2+package git
 3+
 4+import (
 5+	"database/sql"
 6+	"time"
 7+
 8+	"github.com/bluekeyes/go-gitdiff/gitdiff"
 9+	_ "modernc.org/sqlite"
10+)
11+
12+// User is a db model for users.
13+type User struct {
14+	ID        int64     `db:"id"`
15+	Pubkey    string    `db:"pubkey"`
16+	Name      string    `db:"name"`
17+	CreatedAt time.Time `db:"created_at"`
18+	UpdatedAt time.Time `db:"updated_at"`
19+}
20+
21+// Acl is a db model for access control.
22+type Acl struct {
23+	ID         int64          `db:"id"`
24+	Pubkey     sql.NullString `db:"pubkey"`
25+	IpAddress  sql.NullString `db:"ip_address"`
26+	Permission string         `db:"permission"`
27+	CreatedAt  time.Time      `db:"created_at"`
28+}
29+
30+// PatchRequest is a database model for patches submitted to a Repo.
31+type PatchRequest struct {
32+	ID        int64     `db:"id"`
33+	UserID    int64     `db:"user_id"`
34+	RepoID    string    `db:"repo_id"`
35+	Name      string    `db:"name"`
36+	Text      string    `db:"text"`
37+	Status    string    `db:"status"`
38+	CreatedAt time.Time `db:"created_at"`
39+	UpdatedAt time.Time `db:"updated_at"`
40+	// only used for aggregate queries
41+	LastUpdated string `db:"last_updated"`
42+}
43+
44+type Patchset struct {
45+	ID             int64     `db:"id"`
46+	UserID         int64     `db:"user_id"`
47+	PatchRequestID int64     `db:"patch_request_id"`
48+	Review         bool      `db:"review"`
49+	CreatedAt      time.Time `db:"created_at"`
50+}
51+
52+// Patch is a database model for a single entry in a patchset.
53+// This usually corresponds to a git commit.
54+type Patch struct {
55+	ID            int64          `db:"id"`
56+	UserID        int64          `db:"user_id"`
57+	PatchsetID    int64          `db:"patchset_id"`
58+	AuthorName    string         `db:"author_name"`
59+	AuthorEmail   string         `db:"author_email"`
60+	AuthorDate    time.Time      `db:"author_date"`
61+	Title         string         `db:"title"`
62+	Body          string         `db:"body"`
63+	BodyAppendix  string         `db:"body_appendix"`
64+	CommitSha     string         `db:"commit_sha"`
65+	ContentSha    string         `db:"content_sha"`
66+	BaseCommitSha sql.NullString `db:"base_commit_sha"`
67+	RawText       string         `db:"raw_text"`
68+	CreatedAt     time.Time      `db:"created_at"`
69+	Files         []*gitdiff.File
70+}
71+
72+func (p *Patch) CalcDiff() string {
73+	return p.RawText
74+}
75+
76+// EventLog is a event log for RSS or other notification systems.
77+type EventLog struct {
78+	ID             int64         `db:"id"`
79+	UserID         int64         `db:"user_id"`
80+	RepoID         string        `db:"repo_id"`
81+	PatchRequestID sql.NullInt64 `db:"patch_request_id"`
82+	PatchsetID     sql.NullInt64 `db:"patchset_id"`
83+	Event          string        `db:"event"`
84+	Data           string        `db:"data"`
85+	CreatedAt      time.Time     `db:"created_at"`
86+}
R db.go => sqlite.go
+7, -103
  1@@ -1,99 +1,13 @@
  2 package git
  3 
  4 import (
  5-	"database/sql"
  6 	"fmt"
  7 	"log/slog"
  8-	"time"
  9 
 10-	"github.com/bluekeyes/go-gitdiff/gitdiff"
 11 	"github.com/jmoiron/sqlx"
 12-	_ "modernc.org/sqlite"
 13 )
 14 
 15-// User is a db model for users.
 16-type User struct {
 17-	ID        int64     `db:"id"`
 18-	Pubkey    string    `db:"pubkey"`
 19-	Name      string    `db:"name"`
 20-	CreatedAt time.Time `db:"created_at"`
 21-	UpdatedAt time.Time `db:"updated_at"`
 22-}
 23-
 24-// Acl is a db model for access control.
 25-type Acl struct {
 26-	ID         int64          `db:"id"`
 27-	Pubkey     sql.NullString `db:"pubkey"`
 28-	IpAddress  sql.NullString `db:"ip_address"`
 29-	Permission string         `db:"permission"`
 30-	CreatedAt  time.Time      `db:"created_at"`
 31-}
 32-
 33-// PatchRequest is a database model for patches submitted to a Repo.
 34-type PatchRequest struct {
 35-	ID        int64     `db:"id"`
 36-	UserID    int64     `db:"user_id"`
 37-	RepoID    string    `db:"repo_id"`
 38-	Name      string    `db:"name"`
 39-	Text      string    `db:"text"`
 40-	Status    string    `db:"status"`
 41-	CreatedAt time.Time `db:"created_at"`
 42-	UpdatedAt time.Time `db:"updated_at"`
 43-	// only used for aggregate queries
 44-	LastUpdated string `db:"last_updated"`
 45-}
 46-
 47-type Patchset struct {
 48-	ID             int64     `db:"id"`
 49-	UserID         int64     `db:"user_id"`
 50-	PatchRequestID int64     `db:"patch_request_id"`
 51-	Review         bool      `db:"review"`
 52-	CreatedAt      time.Time `db:"created_at"`
 53-}
 54-
 55-// Patch is a database model for a single entry in a patchset.
 56-// This usually corresponds to a git commit.
 57-type Patch struct {
 58-	ID            int64          `db:"id"`
 59-	UserID        int64          `db:"user_id"`
 60-	PatchsetID    int64          `db:"patchset_id"`
 61-	AuthorName    string         `db:"author_name"`
 62-	AuthorEmail   string         `db:"author_email"`
 63-	AuthorDate    time.Time      `db:"author_date"`
 64-	Title         string         `db:"title"`
 65-	Body          string         `db:"body"`
 66-	BodyAppendix  string         `db:"body_appendix"`
 67-	CommitSha     string         `db:"commit_sha"`
 68-	ContentSha    string         `db:"content_sha"`
 69-	BaseCommitSha sql.NullString `db:"base_commit_sha"`
 70-	RawText       string         `db:"raw_text"`
 71-	CreatedAt     time.Time      `db:"created_at"`
 72-	Files         []*gitdiff.File
 73-}
 74-
 75-func (p *Patch) CalcDiff() string {
 76-	return p.RawText
 77-}
 78-
 79-// EventLog is a event log for RSS or other notification systems.
 80-type EventLog struct {
 81-	ID             int64         `db:"id"`
 82-	UserID         int64         `db:"user_id"`
 83-	RepoID         string        `db:"repo_id"`
 84-	PatchRequestID sql.NullInt64 `db:"patch_request_id"`
 85-	PatchsetID     sql.NullInt64 `db:"patchset_id"`
 86-	Event          string        `db:"event"`
 87-	Data           string        `db:"data"`
 88-	CreatedAt      time.Time     `db:"created_at"`
 89-}
 90-
 91-// DB is the interface for a pico/git database.
 92-type DB struct {
 93-	*sqlx.DB
 94-	logger *slog.Logger
 95-}
 96-
 97-var schema = `
 98+var sqliteSchema = `
 99 CREATE TABLE IF NOT EXISTS app_users (
100   id INTEGER PRIMARY KEY AUTOINCREMENT,
101   pubkey TEXT NOT NULL UNIQUE,
102@@ -198,33 +112,23 @@ var sqliteMigrations = []string{
103 }
104 
105 // Open opens a database connection.
106-func Open(dsn string, logger *slog.Logger) (*DB, error) {
107+func SqliteOpen(dsn string, logger *slog.Logger) (*sqlx.DB, error) {
108 	logger.Info("opening db file", "dsn", dsn)
109 	db, err := sqlx.Connect("sqlite", dsn)
110 	if err != nil {
111 		return nil, err
112 	}
113 
114-	d := &DB{
115-		DB:     db,
116-		logger: logger,
117-	}
118-
119-	err = d.upgrade()
120+	err = sqliteUpgrade(db)
121 	if err != nil {
122-		d.Close()
123+		db.Close()
124 		return nil, err
125 	}
126 
127-	return d, nil
128-}
129-
130-// Close implements db.DB.
131-func (d *DB) Close() error {
132-	return d.DB.Close()
133+	return db, nil
134 }
135 
136-func (db *DB) upgrade() error {
137+func sqliteUpgrade(db *sqlx.DB) error {
138 	var version int
139 	if err := db.QueryRow("PRAGMA user_version").Scan(&version); err != nil {
140 		return fmt.Errorf("failed to query schema version: %v", err)
141@@ -245,7 +149,7 @@ func (db *DB) upgrade() error {
142 	}()
143 
144 	if version == 0 {
145-		if _, err := tx.Exec(schema); err != nil {
146+		if _, err := tx.Exec(sqliteSchema); err != nil {
147 			return fmt.Errorf("failed to initialize schema: %v", err)
148 		}
149 	} else {
M ssh.go
+1, -1
1@@ -33,7 +33,7 @@ func authHandler(pr *PrCmd) func(ctx ssh.Context, key ssh.PublicKey) bool {
2 
3 func GitSshServer(cfg *GitCfg) {
4 	dbpath := filepath.Join(cfg.DataDir, "pr.db")
5-	dbh, err := Open(dbpath, cfg.Logger)
6+	dbh, err := SqliteOpen(dbpath, cfg.Logger)
7 	if err != nil {
8 		panic(fmt.Sprintf("cannot find database file, check folder and perms: %s: %s", dbpath, err))
9 	}
M web.go
+1, -1
1@@ -703,7 +703,7 @@ func StartWebServer(cfg *GitCfg) {
2 	addr := fmt.Sprintf("%s:%s", cfg.Host, cfg.WebPort)
3 
4 	dbpath := filepath.Join(cfg.DataDir, "pr.db")
5-	dbh, err := Open(dbpath, cfg.Logger)
6+	dbh, err := SqliteOpen(dbpath, cfg.Logger)
7 	if err != nil {
8 		panic(fmt.Sprintf("cannot find database file, check folder and perms: %s: %s", dbpath, err))
9 	}