A URL shortener service that uses ATProto to allow self hosting and ensuring the user owns their data
1package database
2
3import (
4 "database/sql"
5 "errors"
6 "fmt"
7 "log/slog"
8 "os"
9
10 _ "github.com/glebarez/go-sqlite"
11)
12
13type DB struct {
14 db *sql.DB
15}
16
17func New(dbPath string) (*DB, error) {
18 if dbPath != ":memory:" {
19 err := createDbFile(dbPath)
20 if err != nil {
21 return nil, fmt.Errorf("create db file: %w", err)
22 }
23 }
24
25 db, err := sql.Open("sqlite", dbPath)
26 if err != nil {
27 return nil, fmt.Errorf("open database: %w", err)
28 }
29
30 err = db.Ping()
31 if err != nil {
32 return nil, fmt.Errorf("ping db: %w", err)
33 }
34
35 err = createOauthRequestsTable(db)
36 if err != nil {
37 return nil, fmt.Errorf("creating oauth requests table: %w", err)
38 }
39
40 err = createOauthSessionsTable(db)
41 if err != nil {
42 return nil, fmt.Errorf("creating oauth sessions table: %w", err)
43 }
44
45 err = createURLsTable(db)
46 if err != nil {
47 return nil, fmt.Errorf("creating status table: %w", err)
48 }
49
50 return &DB{db: db}, nil
51}
52
53func (d *DB) Close() {
54 err := d.db.Close()
55 if err != nil {
56 slog.Error("failed to close db", "error", err)
57 }
58}
59
60func createDbFile(dbFilename string) error {
61 if _, err := os.Stat(dbFilename); !errors.Is(err, os.ErrNotExist) {
62 return nil
63 }
64
65 f, err := os.Create(dbFilename)
66 if err != nil {
67 return fmt.Errorf("create db file : %w", err)
68 }
69 f.Close()
70 return nil
71}