this repo has no description
1package tangledalertbot
2
3import (
4 "database/sql"
5 "errors"
6 "fmt"
7 "log/slog"
8 "os"
9
10 _ "github.com/glebarez/go-sqlite"
11)
12
13// Database is a sqlite database
14type Database struct {
15 db *sql.DB
16}
17
18// NewDatabase will open a new database. It will ping the database to ensure it is available and error if not
19func NewDatabase(dbPath string) (*Database, error) {
20 if dbPath != ":memory:" {
21 err := createDbFile(dbPath)
22 if err != nil {
23 return nil, fmt.Errorf("create db file: %w", err)
24 }
25 }
26
27 db, err := sql.Open("sqlite", dbPath)
28 if err != nil {
29 return nil, fmt.Errorf("open database: %w", err)
30 }
31
32 err = db.Ping()
33 if err != nil {
34 return nil, fmt.Errorf("ping db: %w", err)
35 }
36
37 err = createIssuesTable(db)
38 if err != nil {
39 return nil, fmt.Errorf("creating issues table: %w", err)
40 }
41
42 err = createCommentsTable(db)
43 if err != nil {
44 return nil, fmt.Errorf("creating comments table: %w", err)
45 }
46
47 return &Database{db: db}, nil
48}
49
50// Close will cleanly stop the database connection
51func (d *Database) Close() {
52 err := d.db.Close()
53 if err != nil {
54 slog.Error("failed to close db", "error", err)
55 }
56}
57
58func createDbFile(dbFilename string) error {
59 if _, err := os.Stat(dbFilename); !errors.Is(err, os.ErrNotExist) {
60 return nil
61 }
62
63 f, err := os.Create(dbFilename)
64 if err != nil {
65 return fmt.Errorf("create db file : %w", err)
66 }
67 err = f.Close()
68 if err != nil {
69 return fmt.Errorf("failed to close DB file: %w", err)
70 }
71 return nil
72}
73
74func createIssuesTable(db *sql.DB) error {
75 createTableSQL := `CREATE TABLE IF NOT EXISTS issues (
76 "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
77 "authorDid" TEXT,
78 "rkey" TEXT,
79 "title" TEXT,
80 "body" TEXT,
81 "repo" TEXT,
82 "createdAt" integer NOT NULL,
83 UNIQUE(authorDid,rkey)
84 );`
85
86 slog.Info("Create issues table...")
87 statement, err := db.Prepare(createTableSQL)
88 if err != nil {
89 return fmt.Errorf("prepare DB statement to create issues table: %w", err)
90 }
91 _, err = statement.Exec()
92 if err != nil {
93 return fmt.Errorf("exec sql statement to create issues table: %w", err)
94 }
95 slog.Info("issues table created")
96
97 return nil
98}
99
100func createCommentsTable(db *sql.DB) error {
101 createTableSQL := `CREATE TABLE IF NOT EXISTS comments (
102 "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
103 "authorDid" TEXT,
104 "rkey" TEXT,
105 "body" TEXT,
106 "issue" TEXT,
107 "replyTo" TEXT,
108 "createdAt" integer NOT NULL,
109 UNIQUE(authorDid,rkey)
110 );`
111
112 slog.Info("Create comments table...")
113 statement, err := db.Prepare(createTableSQL)
114 if err != nil {
115 return fmt.Errorf("prepare DB statement to create comments table: %w", err)
116 }
117 _, err = statement.Exec()
118 if err != nil {
119 return fmt.Errorf("exec sql statement to create comments table: %w", err)
120 }
121 slog.Info("comments table created")
122
123 return nil
124}
125
126// CreateIssue will insert a issue into a database
127func (d *Database) CreateIssue(issue Issue) error {
128 sql := `INSERT INTO issues (authorDid, rkey, title, body, repo, createdAt) VALUES (?, ?, ?, ?, ?, ?) ON CONFLICT(authorDid, rkey) DO NOTHING;`
129 _, err := d.db.Exec(sql, issue.AuthorDID, issue.RKey, issue.Title, issue.Body, issue.Repo, issue.CreatedAt)
130 if err != nil {
131 return fmt.Errorf("exec insert issue: %w", err)
132 }
133 return nil
134}
135
136// CreateComment will insert a comment into a database
137func (d *Database) CreateComment(comment Comment) error {
138 sql := `INSERT INTO comments (authorDid, rkey, body, issue, replyTo, createdAt) VALUES (?, ?, ?, ?, ?, ?) ON CONFLICT(authorDid, rkey) DO NOTHING;`
139 _, err := d.db.Exec(sql, comment.AuthorDID, comment.RKey, comment.Body, comment.Issue, comment.ReplyTo, comment.CreatedAt)
140 if err != nil {
141 return fmt.Errorf("exec insert comment: %w", err)
142 }
143 return nil
144}