-
Notifications
You must be signed in to change notification settings - Fork 36
/
new.go
152 lines (126 loc) · 3.71 KB
/
new.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
// SPDX-License-Identifier: MIT
// Package sqlite implements the SQLite backend of the roomdb interfaces.
//
// It uses sql-migrate (github.com/rubenv/sql-migrate) for it's schema definition and maintenance.
// For query construction/ORM it uses SQLBoiler (https://github.com/volatiletech/sqlboiler).
//
// The process of updating the schema and ORM can be summarized as follows:
//
// 1. Make changes to the interfaces in package roomdb
// 2. Add a new migration to the 'migrations' folder
// 3. Run 'go test -run Simple', which applies all the migrations
// 4. Run sqlboiler to generate package models
// 5. Implement the interface as needed by using the models package
//
// For convenience step 3 and 4 are combined in the generate_models bash script.
package sqlite
import (
"database/sql"
"fmt"
"log"
"os"
"path/filepath"
"time"
migrate "github.com/rubenv/sql-migrate"
"github.com/ssb-ngi-pointer/go-ssb-room/v2/internal/repo"
)
type Database struct {
db *sql.DB
AuthFallback AuthFallback
AuthWithSSB AuthWithSSB
Members Members
Aliases Aliases
Invites Invites
Config Config
DeniedKeys DeniedKeys
PinnedNotices PinnedNotices
Notices Notices
}
// Open looks for a database file 'fname'
func Open(r repo.Interface) (*Database, error) {
fname := r.GetPath("roomdb")
if dir := filepath.Dir(fname); dir != "" {
err := os.MkdirAll(dir, 0700)
if err != nil && !os.IsExist(err) {
return nil, fmt.Errorf("roomdb: failed to create folder for database (%q): %w", dir, err)
}
}
// enable constraint enforcment for relations
fname += "?_foreign_keys=on"
db, err := sql.Open("sqlite3", fname)
if err != nil {
return nil, fmt.Errorf("roomdb: failed to open sqlite database: %w", err)
}
if err := db.Ping(); err != nil {
return nil, fmt.Errorf("roomdb: sqlite ping failed: %w", err)
}
n, err := migrate.Exec(db, "sqlite3", migrationSource, migrate.Up)
if err != nil {
return nil, fmt.Errorf("roomdb: failed to apply database mirations: %w", err)
}
if n > 0 {
// TODO: hook up logging
log.Printf("roomdb: applied %d migrations", n)
}
if err := deleteConsumedInvites(db); err != nil {
return nil, err
}
if err := deleteConsumedResetTokens(db); err != nil {
return nil, err
}
// scrub old invites and reset tokens
go func() { // server might not restart as often
fiveDays := 5 * 24 * time.Hour
ticker := time.NewTicker(fiveDays)
for range ticker.C {
err := transact(db, func(tx *sql.Tx) error {
if err := deleteConsumedResetTokens(tx); err != nil {
return err
}
return deleteConsumedInvites(tx)
})
if err != nil {
// TODO: hook up logging
log.Printf("roomdb: failed to clean up old invites: %s", err.Error())
}
}
}()
ml := Members{db}
roomdb := &Database{
db: db,
Aliases: Aliases{db},
AuthFallback: AuthFallback{db},
AuthWithSSB: AuthWithSSB{db},
Config: Config{db},
DeniedKeys: DeniedKeys{db},
Invites: Invites{db: db, members: ml},
Notices: Notices{db},
Members: ml,
PinnedNotices: PinnedNotices{db},
}
return roomdb, nil
}
// Close closes the contained sql database object
func (t Database) Close() error {
return t.db.Close()
}
func transact(db *sql.DB, fn func(tx *sql.Tx) error) error {
var err error
var tx *sql.Tx
tx, err = db.Begin()
if err != nil {
return fmt.Errorf("transact: could not begin transaction: %w", err)
}
if err = fn(tx); err != nil {
if err2 := tx.Rollback(); err2 != nil {
err = fmt.Errorf("rollback failed after %s: %s", err.Error(), err2.Error())
} else {
err = fmt.Errorf("transaction failed, rolling back: %w", err)
}
return err
}
if err = tx.Commit(); err != nil {
return fmt.Errorf("transact: could not commit transaction: %w", err)
}
return nil
}