blob: 7efdeafee1d4322137c854643779c751f5d137d1 [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)
gioa60f0de2024-07-08 10:49:48 +040053}
54
55func NewStore(cf soft.RepoIO, db *sql.DB) (Store, error) {
56 s := &storeImpl{cf, db}
57 if err := s.init(); err != nil {
58 return nil, err
59 }
60 return s, nil
61}
62
63type storeImpl struct {
64 cf soft.RepoIO
65 db *sql.DB
66}
67
68func (s *storeImpl) init() error {
69 _, err := s.db.Exec(`
gio81246f02024-07-10 12:02:15 +040070 CREATE TABLE IF NOT EXISTS users (
71 username TEXT PRIMARY KEY,
gio11617ac2024-07-15 16:09:04 +040072 password BLOB,
gio11617ac2024-07-15 16:09:04 +040073 network TEXT
gio81246f02024-07-10 12:02:15 +040074 );
gioa60f0de2024-07-08 10:49:48 +040075 CREATE TABLE IF NOT EXISTS apps (
gio81246f02024-07-10 12:02:15 +040076 name TEXT PRIMARY KEY,
77 username TEXT
gioa60f0de2024-07-08 10:49:48 +040078 );
79 CREATE TABLE IF NOT EXISTS commits (
80 app_name TEXT,
gio7fbd4ad2024-08-27 10:06:39 +040081 branch TEXT,
gioa60f0de2024-07-08 10:49:48 +040082 hash TEXT,
gioe2e31e12024-08-18 08:20:56 +040083 message TEXT,
giob4a3a192024-08-19 09:55:47 +040084 status TEXT,
85 error TEXT,
86 resources JSONB
gioa60f0de2024-07-08 10:49:48 +040087 );
giocfb228c2024-09-06 15:44:31 +040088 CREATE TABLE IF NOT EXISTS branches (
89 app_name TEXT,
90 branch TEXT,
91 hash TEXT,
92 resources JSONB
93 );
gioa60f0de2024-07-08 10:49:48 +040094 `)
95 return err
96
97}
98
giocafd4e62024-07-31 10:53:40 +040099func (s *storeImpl) CreateUser(username string, password []byte, network string) error {
100 query := `INSERT INTO users (username, password, network) VALUES (?, ?, ?)`
101 _, err := s.db.Exec(query, username, password, network)
gio81246f02024-07-10 12:02:15 +0400102 if err != nil {
103 sqliteErr, ok := err.(*sqlite3.Error)
gio11617ac2024-07-15 16:09:04 +0400104 if ok && sqliteErr.ExtendedCode() == errorConstraintPrimaryKeyViolation {
gio81246f02024-07-10 12:02:15 +0400105 return ErrorAlreadyExists
106 }
107 }
gioa60f0de2024-07-08 10:49:48 +0400108 return err
109}
110
gio81246f02024-07-10 12:02:15 +0400111func (s *storeImpl) GetUserPassword(username string) ([]byte, error) {
112 query := `SELECT password FROM users WHERE username = ?`
113 row := s.db.QueryRow(query, username)
114 if err := row.Err(); err != nil {
115 return nil, err
116 }
117 ret := []byte{}
118 if err := row.Scan(&ret); err != nil {
119 return nil, err
120 }
121 return ret, nil
122}
123
gio11617ac2024-07-15 16:09:04 +0400124func (s *storeImpl) GetUserNetwork(username string) (string, error) {
125 query := `SELECT network FROM users WHERE username = ?`
126 row := s.db.QueryRow(query, username)
127 if err := row.Err(); err != nil {
128 return "", err
129 }
130 var ret string
131 if err := row.Scan(&ret); err != nil {
132 if errors.Is(sql.ErrNoRows, err) {
133 return "", nil
134 }
135 return "", err
136 }
137 return ret, nil
138}
139
gio81246f02024-07-10 12:02:15 +0400140func (s *storeImpl) CreateApp(name, username string) error {
141 query := `INSERT INTO apps (name, username) VALUES (?, ?)`
142 _, err := s.db.Exec(query, name, username)
143 return err
144}
145
146func (s *storeImpl) GetAppOwner(name string) (string, error) {
147 query := `SELECT username FROM apps WHERE name = ?`
148 row := s.db.QueryRow(query, name)
149 if err := row.Err(); err != nil {
150 return "", err
151 }
152 var ret string
153 if err := row.Scan(&ret); err != nil {
154 return "", err
155 }
156 return ret, nil
157}
158
gioa60f0de2024-07-08 10:49:48 +0400159func (s *storeImpl) GetApps() ([]string, error) {
160 query := `SELECT name FROM apps`
161 rows, err := s.db.Query(query)
162 if err != nil {
163 return nil, err
164 }
165 defer rows.Close()
166 ret := []string{}
167 for rows.Next() {
168 if err := rows.Err(); err != nil {
169 return nil, err
170 }
171 var name string
172 if err := rows.Scan(&name); err != nil {
173 return nil, err
174 }
175 ret = append(ret, name)
176
177 }
178 return ret, nil
179}
180
gio81246f02024-07-10 12:02:15 +0400181func (s *storeImpl) GetUserApps(username string) ([]string, error) {
182 query := `SELECT name FROM apps WHERE username = ?`
183 rows, err := s.db.Query(query, username)
184 if err != nil {
185 return nil, err
186 }
187 defer rows.Close()
188 ret := []string{}
189 for rows.Next() {
190 if err := rows.Err(); err != nil {
191 return nil, err
192 }
193 var name string
194 if err := rows.Scan(&name); err != nil {
195 return nil, err
196 }
197 ret = append(ret, name)
198
199 }
200 return ret, nil
201}
202
gio7fbd4ad2024-08-27 10:06:39 +0400203func (s *storeImpl) CreateCommit(name, branch, hash, message, status, error string, resources []byte) error {
giocfb228c2024-09-06 15:44:31 +0400204 tx, err := s.db.Begin()
205 if err != nil {
206 return err
207 }
gio7fbd4ad2024-08-27 10:06:39 +0400208 query := `INSERT INTO commits (app_name, branch, hash, message, status, error, resources) VALUES (?, ?, ?, ?, ?, ?, ?)`
giocfb228c2024-09-06 15:44:31 +0400209 _, err = tx.Exec(query, name, branch, hash, message, status, error, resources)
210 if err != nil {
211 tx.Rollback()
212 return err
213 }
214 branchQuery := `UPDATE branches SET hash = ?, resources = ? WHERE app_name = ? AND branch = ?`
215 r, err := tx.Exec(branchQuery, hash, resources, name, branch)
216 if err != nil {
217 tx.Rollback()
218 return err
219 }
220 if cnt, err := r.RowsAffected(); err != nil {
221 tx.Rollback()
222 return err
223 } else if cnt == 0 {
224 branchQuery := `INSERT INTO branches (app_name, branch, hash, resources) VALUES (?, ?, ?, ?)`
225 _, err := tx.Exec(branchQuery, name, branch, hash, resources)
226 if err != nil {
227 tx.Rollback()
228 return err
229 }
230 }
231 if err := tx.Commit(); err != nil {
232 tx.Rollback()
233 return err
234 }
235 return nil
gioa60f0de2024-07-08 10:49:48 +0400236}
237
gio7fbd4ad2024-08-27 10:06:39 +0400238func (s *storeImpl) GetCommitHistory(name, branch string) ([]CommitMeta, error) {
239 query := `SELECT hash, message, status, error FROM commits WHERE app_name = ? AND branch = ?`
240 rows, err := s.db.Query(query, name, branch)
gioa60f0de2024-07-08 10:49:48 +0400241 if err != nil {
242 return nil, err
243 }
244 defer rows.Close()
giob4a3a192024-08-19 09:55:47 +0400245 ret := []CommitMeta{}
gioa60f0de2024-07-08 10:49:48 +0400246 for rows.Next() {
247 if err := rows.Err(); err != nil {
248 return nil, err
249 }
giob4a3a192024-08-19 09:55:47 +0400250 var c CommitMeta
251 if err := rows.Scan(&c.Hash, &c.Message, &c.Status, &c.Error); err != nil {
gioa60f0de2024-07-08 10:49:48 +0400252 return nil, err
253 }
254 ret = append(ret, c)
255
256 }
257 return ret, nil
258}
giob4a3a192024-08-19 09:55:47 +0400259
260func (s *storeImpl) GetCommit(hash string) (Commit, error) {
261 query := `SELECT hash, message, status, error, resources FROM commits WHERE hash = ?`
262 row := s.db.QueryRow(query, hash)
263 if err := row.Err(); err != nil {
264 return Commit{}, err
265 }
266 var ret Commit
267 var c Commit
268 var res []byte
269 if err := row.Scan(&c.Hash, &c.Message, &c.Status, &c.Error, &res); err != nil {
270 return Commit{}, err
271 }
272 if err := json.NewDecoder(bytes.NewBuffer(res)).Decode(&ret.Resources); err != nil {
273 return Commit{}, err
274 }
275 return ret, nil
276}
gio7fbd4ad2024-08-27 10:06:39 +0400277
giocfb228c2024-09-06 15:44:31 +0400278func (s *storeImpl) GetLastCommitInfo(name, branch string) (LastCommitInfo, error) {
279 query := `SELECT hash, resources FROM branches WHERE app_name = ? AND branch = ?`
280 row := s.db.QueryRow(query, name, branch)
281 if err := row.Err(); err != nil {
282 return LastCommitInfo{}, err
283 }
284 var ret LastCommitInfo
285 var res []byte
286 if err := row.Scan(&ret.Hash, &res); err != nil {
287 return LastCommitInfo{}, err
288 }
289 if err := json.NewDecoder(bytes.NewBuffer(res)).Decode(&ret.Resources); err != nil {
290 return LastCommitInfo{}, err
291 }
292 return ret, nil
293}
294
gio7fbd4ad2024-08-27 10:06:39 +0400295func (s *storeImpl) GetBranches(name string) ([]string, error) {
296 query := `SELECT DISTINCT branch FROM commits WHERE app_name = ?`
297 rows, err := s.db.Query(query, name)
298 if err != nil {
299 return nil, err
300 }
301 defer rows.Close()
302 ret := []string{}
303 for rows.Next() {
304 if err := rows.Err(); err != nil {
305 return nil, err
306 }
307 var b string
308 if err := rows.Scan(&b); err != nil {
309 return nil, err
310 }
311 ret = append(ret, b)
312
313 }
314 return ret, nil
315}