blob: 328c7fd8453d7f754685df09b636ecba86295278 [file] [log] [blame]
gioa60f0de2024-07-08 10:49:48 +04001package welcome
2
3import (
giob4a3a192024-08-19 09:55:47 +04004 "bytes"
gioa60f0de2024-07-08 10:49:48 +04005 "database/sql"
giob4a3a192024-08-19 09:55:47 +04006 "encoding/json"
gio81246f02024-07-10 12:02:15 +04007 "errors"
8
9 "github.com/ncruces/go-sqlite3"
gioa60f0de2024-07-08 10:49:48 +040010
giob4a3a192024-08-19 09:55:47 +040011 "github.com/giolekva/pcloud/core/installer"
gioa60f0de2024-07-08 10:49:48 +040012 "github.com/giolekva/pcloud/core/installer/soft"
13)
14
gio81246f02024-07-10 12:02:15 +040015const (
gio11617ac2024-07-15 16:09:04 +040016 errorConstraintPrimaryKeyViolation = 1555
gio81246f02024-07-10 12:02:15 +040017)
18
19var (
20 ErrorAlreadyExists = errors.New("already exists")
21)
22
giob4a3a192024-08-19 09:55:47 +040023type CommitMeta struct {
gioe2e31e12024-08-18 08:20:56 +040024 Status string
giob4a3a192024-08-19 09:55:47 +040025 Error string
26 Hash string
gioa60f0de2024-07-08 10:49:48 +040027 Message string
28}
29
giob4a3a192024-08-19 09:55:47 +040030type Commit struct {
31 CommitMeta
32 Resources installer.ReleaseResources
33}
34
giocfb228c2024-09-06 15:44:31 +040035type LastCommitInfo struct {
36 Hash string
37 Resources installer.ReleaseResources
38}
39
gioa60f0de2024-07-08 10:49:48 +040040type Store interface {
giocafd4e62024-07-31 10:53:40 +040041 CreateUser(username string, password []byte, network string) error
gio81246f02024-07-10 12:02:15 +040042 GetUserPassword(username string) ([]byte, error)
gio11617ac2024-07-15 16:09:04 +040043 GetUserNetwork(username string) (string, error)
gioa60f0de2024-07-08 10:49:48 +040044 GetApps() ([]string, error)
gio81246f02024-07-10 12:02:15 +040045 GetUserApps(username string) ([]string, error)
46 CreateApp(name, username string) error
47 GetAppOwner(name string) (string, error)
gio7fbd4ad2024-08-27 10:06:39 +040048 CreateCommit(name, branch, hash, message, status, error string, resources []byte) error
49 GetCommitHistory(name, branch string) ([]CommitMeta, error)
giob4a3a192024-08-19 09:55:47 +040050 GetCommit(hash string) (Commit, error)
giocfb228c2024-09-06 15:44:31 +040051 GetLastCommitInfo(name, branch string) (LastCommitInfo, error)
gio7fbd4ad2024-08-27 10:06:39 +040052 GetBranches(name string) ([]string, error)
gioe44c1512024-10-06 14:13:55 +040053 DeleteBranch(name, branch string) error
54 DeleteApp(name string) error
gioa60f0de2024-07-08 10:49:48 +040055}
56
57func NewStore(cf soft.RepoIO, db *sql.DB) (Store, error) {
58 s := &storeImpl{cf, db}
59 if err := s.init(); err != nil {
60 return nil, err
61 }
62 return s, nil
63}
64
65type storeImpl struct {
66 cf soft.RepoIO
67 db *sql.DB
68}
69
70func (s *storeImpl) init() error {
71 _, err := s.db.Exec(`
gio81246f02024-07-10 12:02:15 +040072 CREATE TABLE IF NOT EXISTS users (
73 username TEXT PRIMARY KEY,
gio11617ac2024-07-15 16:09:04 +040074 password BLOB,
gio11617ac2024-07-15 16:09:04 +040075 network TEXT
gio81246f02024-07-10 12:02:15 +040076 );
gioa60f0de2024-07-08 10:49:48 +040077 CREATE TABLE IF NOT EXISTS apps (
gio81246f02024-07-10 12:02:15 +040078 name TEXT PRIMARY KEY,
79 username TEXT
gioa60f0de2024-07-08 10:49:48 +040080 );
81 CREATE TABLE IF NOT EXISTS commits (
82 app_name TEXT,
gio7fbd4ad2024-08-27 10:06:39 +040083 branch TEXT,
gioa60f0de2024-07-08 10:49:48 +040084 hash TEXT,
gioe2e31e12024-08-18 08:20:56 +040085 message TEXT,
giob4a3a192024-08-19 09:55:47 +040086 status TEXT,
87 error TEXT,
88 resources JSONB
gioa60f0de2024-07-08 10:49:48 +040089 );
giocfb228c2024-09-06 15:44:31 +040090 CREATE TABLE IF NOT EXISTS branches (
91 app_name TEXT,
92 branch TEXT,
93 hash TEXT,
94 resources JSONB
95 );
gioa60f0de2024-07-08 10:49:48 +040096 `)
97 return err
98
99}
100
giocafd4e62024-07-31 10:53:40 +0400101func (s *storeImpl) CreateUser(username string, password []byte, network string) error {
102 query := `INSERT INTO users (username, password, network) VALUES (?, ?, ?)`
103 _, err := s.db.Exec(query, username, password, network)
gio81246f02024-07-10 12:02:15 +0400104 if err != nil {
105 sqliteErr, ok := err.(*sqlite3.Error)
gio11617ac2024-07-15 16:09:04 +0400106 if ok && sqliteErr.ExtendedCode() == errorConstraintPrimaryKeyViolation {
gio81246f02024-07-10 12:02:15 +0400107 return ErrorAlreadyExists
108 }
109 }
gioa60f0de2024-07-08 10:49:48 +0400110 return err
111}
112
gio81246f02024-07-10 12:02:15 +0400113func (s *storeImpl) GetUserPassword(username string) ([]byte, error) {
114 query := `SELECT password FROM users WHERE username = ?`
115 row := s.db.QueryRow(query, username)
116 if err := row.Err(); err != nil {
117 return nil, err
118 }
119 ret := []byte{}
120 if err := row.Scan(&ret); err != nil {
121 return nil, err
122 }
123 return ret, nil
124}
125
gio11617ac2024-07-15 16:09:04 +0400126func (s *storeImpl) GetUserNetwork(username string) (string, error) {
127 query := `SELECT network FROM users WHERE username = ?`
128 row := s.db.QueryRow(query, username)
129 if err := row.Err(); err != nil {
130 return "", err
131 }
132 var ret string
133 if err := row.Scan(&ret); err != nil {
134 if errors.Is(sql.ErrNoRows, err) {
135 return "", nil
136 }
137 return "", err
138 }
139 return ret, nil
140}
141
gio81246f02024-07-10 12:02:15 +0400142func (s *storeImpl) CreateApp(name, username string) error {
143 query := `INSERT INTO apps (name, username) VALUES (?, ?)`
144 _, err := s.db.Exec(query, name, username)
145 return err
146}
147
148func (s *storeImpl) GetAppOwner(name string) (string, error) {
149 query := `SELECT username FROM apps WHERE name = ?`
150 row := s.db.QueryRow(query, name)
151 if err := row.Err(); err != nil {
152 return "", err
153 }
154 var ret string
155 if err := row.Scan(&ret); err != nil {
156 return "", err
157 }
158 return ret, nil
159}
160
gioa60f0de2024-07-08 10:49:48 +0400161func (s *storeImpl) GetApps() ([]string, error) {
162 query := `SELECT name FROM apps`
163 rows, err := s.db.Query(query)
164 if err != nil {
165 return nil, err
166 }
167 defer rows.Close()
168 ret := []string{}
169 for rows.Next() {
170 if err := rows.Err(); err != nil {
171 return nil, err
172 }
173 var name string
174 if err := rows.Scan(&name); err != nil {
175 return nil, err
176 }
177 ret = append(ret, name)
178
179 }
180 return ret, nil
181}
182
gio81246f02024-07-10 12:02:15 +0400183func (s *storeImpl) GetUserApps(username string) ([]string, error) {
184 query := `SELECT name FROM apps WHERE username = ?`
185 rows, err := s.db.Query(query, username)
186 if err != nil {
187 return nil, err
188 }
189 defer rows.Close()
190 ret := []string{}
191 for rows.Next() {
192 if err := rows.Err(); err != nil {
193 return nil, err
194 }
195 var name string
196 if err := rows.Scan(&name); err != nil {
197 return nil, err
198 }
199 ret = append(ret, name)
200
201 }
202 return ret, nil
203}
204
gio7fbd4ad2024-08-27 10:06:39 +0400205func (s *storeImpl) CreateCommit(name, branch, hash, message, status, error string, resources []byte) error {
giocfb228c2024-09-06 15:44:31 +0400206 tx, err := s.db.Begin()
207 if err != nil {
208 return err
209 }
gio7fbd4ad2024-08-27 10:06:39 +0400210 query := `INSERT INTO commits (app_name, branch, hash, message, status, error, resources) VALUES (?, ?, ?, ?, ?, ?, ?)`
giocfb228c2024-09-06 15:44:31 +0400211 _, err = tx.Exec(query, name, branch, hash, message, status, error, resources)
212 if err != nil {
213 tx.Rollback()
214 return err
215 }
216 branchQuery := `UPDATE branches SET hash = ?, resources = ? WHERE app_name = ? AND branch = ?`
217 r, err := tx.Exec(branchQuery, hash, resources, name, branch)
218 if err != nil {
219 tx.Rollback()
220 return err
221 }
222 if cnt, err := r.RowsAffected(); err != nil {
223 tx.Rollback()
224 return err
225 } else if cnt == 0 {
226 branchQuery := `INSERT INTO branches (app_name, branch, hash, resources) VALUES (?, ?, ?, ?)`
227 _, err := tx.Exec(branchQuery, name, branch, hash, resources)
228 if err != nil {
229 tx.Rollback()
230 return err
231 }
232 }
233 if err := tx.Commit(); err != nil {
234 tx.Rollback()
235 return err
236 }
237 return nil
gioa60f0de2024-07-08 10:49:48 +0400238}
239
gio7fbd4ad2024-08-27 10:06:39 +0400240func (s *storeImpl) GetCommitHistory(name, branch string) ([]CommitMeta, error) {
241 query := `SELECT hash, message, status, error FROM commits WHERE app_name = ? AND branch = ?`
242 rows, err := s.db.Query(query, name, branch)
gioa60f0de2024-07-08 10:49:48 +0400243 if err != nil {
244 return nil, err
245 }
246 defer rows.Close()
giob4a3a192024-08-19 09:55:47 +0400247 ret := []CommitMeta{}
gioa60f0de2024-07-08 10:49:48 +0400248 for rows.Next() {
249 if err := rows.Err(); err != nil {
250 return nil, err
251 }
giob4a3a192024-08-19 09:55:47 +0400252 var c CommitMeta
253 if err := rows.Scan(&c.Hash, &c.Message, &c.Status, &c.Error); err != nil {
gioa60f0de2024-07-08 10:49:48 +0400254 return nil, err
255 }
256 ret = append(ret, c)
257
258 }
259 return ret, nil
260}
giob4a3a192024-08-19 09:55:47 +0400261
262func (s *storeImpl) GetCommit(hash string) (Commit, error) {
263 query := `SELECT hash, message, status, error, resources FROM commits WHERE hash = ?`
264 row := s.db.QueryRow(query, hash)
265 if err := row.Err(); err != nil {
266 return Commit{}, err
267 }
268 var ret Commit
269 var c Commit
270 var res []byte
271 if err := row.Scan(&c.Hash, &c.Message, &c.Status, &c.Error, &res); err != nil {
272 return Commit{}, err
273 }
274 if err := json.NewDecoder(bytes.NewBuffer(res)).Decode(&ret.Resources); err != nil {
275 return Commit{}, err
276 }
277 return ret, nil
278}
gio7fbd4ad2024-08-27 10:06:39 +0400279
giocfb228c2024-09-06 15:44:31 +0400280func (s *storeImpl) GetLastCommitInfo(name, branch string) (LastCommitInfo, error) {
281 query := `SELECT hash, resources FROM branches WHERE app_name = ? AND branch = ?`
282 row := s.db.QueryRow(query, name, branch)
283 if err := row.Err(); err != nil {
284 return LastCommitInfo{}, err
285 }
286 var ret LastCommitInfo
287 var res []byte
288 if err := row.Scan(&ret.Hash, &res); err != nil {
289 return LastCommitInfo{}, err
290 }
291 if err := json.NewDecoder(bytes.NewBuffer(res)).Decode(&ret.Resources); err != nil {
292 return LastCommitInfo{}, err
293 }
294 return ret, nil
295}
296
gio7fbd4ad2024-08-27 10:06:39 +0400297func (s *storeImpl) GetBranches(name string) ([]string, error) {
298 query := `SELECT DISTINCT branch FROM commits WHERE app_name = ?`
299 rows, err := s.db.Query(query, name)
300 if err != nil {
301 return nil, err
302 }
303 defer rows.Close()
304 ret := []string{}
305 for rows.Next() {
306 if err := rows.Err(); err != nil {
307 return nil, err
308 }
309 var b string
310 if err := rows.Scan(&b); err != nil {
311 return nil, err
312 }
313 ret = append(ret, b)
314
315 }
316 return ret, nil
317}
gioe44c1512024-10-06 14:13:55 +0400318
319func (s *storeImpl) DeleteBranch(name, branch string) error {
320 query := `DELETE FROM commits WHERE app_name = ? AND branch = ?`
321 _, err := s.db.Exec(query, name, branch)
322 return err
323}
324
325func (s *storeImpl) DeleteApp(name string) error {
326 query := `DELETE FROM apps WHERE name = ?`
327 _, err := s.db.Exec(query, name)
328 return err
329}