blob: 06be06a9eafce7c3a56cfd1af22a184caa8c3826 [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
gioa60f0de2024-07-08 10:49:48 +040035type Store interface {
giocafd4e62024-07-31 10:53:40 +040036 CreateUser(username string, password []byte, network string) error
gio81246f02024-07-10 12:02:15 +040037 GetUserPassword(username string) ([]byte, error)
gio11617ac2024-07-15 16:09:04 +040038 GetUserNetwork(username string) (string, error)
gioa60f0de2024-07-08 10:49:48 +040039 GetApps() ([]string, error)
gio81246f02024-07-10 12:02:15 +040040 GetUserApps(username string) ([]string, error)
41 CreateApp(name, username string) error
42 GetAppOwner(name string) (string, error)
giob4a3a192024-08-19 09:55:47 +040043 CreateCommit(name, hash, message, status, error string, resources []byte) error
44 GetCommitHistory(name string) ([]CommitMeta, error)
45 GetCommit(hash string) (Commit, error)
gioa60f0de2024-07-08 10:49:48 +040046}
47
48func NewStore(cf soft.RepoIO, db *sql.DB) (Store, error) {
49 s := &storeImpl{cf, db}
50 if err := s.init(); err != nil {
51 return nil, err
52 }
53 return s, nil
54}
55
56type storeImpl struct {
57 cf soft.RepoIO
58 db *sql.DB
59}
60
61func (s *storeImpl) init() error {
62 _, err := s.db.Exec(`
gio81246f02024-07-10 12:02:15 +040063 CREATE TABLE IF NOT EXISTS users (
64 username TEXT PRIMARY KEY,
gio11617ac2024-07-15 16:09:04 +040065 password BLOB,
gio11617ac2024-07-15 16:09:04 +040066 network TEXT
gio81246f02024-07-10 12:02:15 +040067 );
gioa60f0de2024-07-08 10:49:48 +040068 CREATE TABLE IF NOT EXISTS apps (
gio81246f02024-07-10 12:02:15 +040069 name TEXT PRIMARY KEY,
70 username TEXT
gioa60f0de2024-07-08 10:49:48 +040071 );
72 CREATE TABLE IF NOT EXISTS commits (
73 app_name TEXT,
74 hash TEXT,
gioe2e31e12024-08-18 08:20:56 +040075 message TEXT,
giob4a3a192024-08-19 09:55:47 +040076 status TEXT,
77 error TEXT,
78 resources JSONB
gioa60f0de2024-07-08 10:49:48 +040079 );
80 `)
81 return err
82
83}
84
giocafd4e62024-07-31 10:53:40 +040085func (s *storeImpl) CreateUser(username string, password []byte, network string) error {
86 query := `INSERT INTO users (username, password, network) VALUES (?, ?, ?)`
87 _, err := s.db.Exec(query, username, password, network)
gio81246f02024-07-10 12:02:15 +040088 if err != nil {
89 sqliteErr, ok := err.(*sqlite3.Error)
gio11617ac2024-07-15 16:09:04 +040090 if ok && sqliteErr.ExtendedCode() == errorConstraintPrimaryKeyViolation {
gio81246f02024-07-10 12:02:15 +040091 return ErrorAlreadyExists
92 }
93 }
gioa60f0de2024-07-08 10:49:48 +040094 return err
95}
96
gio81246f02024-07-10 12:02:15 +040097func (s *storeImpl) GetUserPassword(username string) ([]byte, error) {
98 query := `SELECT password FROM users WHERE username = ?`
99 row := s.db.QueryRow(query, username)
100 if err := row.Err(); err != nil {
101 return nil, err
102 }
103 ret := []byte{}
104 if err := row.Scan(&ret); err != nil {
105 return nil, err
106 }
107 return ret, nil
108}
109
gio11617ac2024-07-15 16:09:04 +0400110func (s *storeImpl) GetUserNetwork(username string) (string, error) {
111 query := `SELECT network FROM users WHERE username = ?`
112 row := s.db.QueryRow(query, username)
113 if err := row.Err(); err != nil {
114 return "", err
115 }
116 var ret string
117 if err := row.Scan(&ret); err != nil {
118 if errors.Is(sql.ErrNoRows, err) {
119 return "", nil
120 }
121 return "", err
122 }
123 return ret, nil
124}
125
gio81246f02024-07-10 12:02:15 +0400126func (s *storeImpl) CreateApp(name, username string) error {
127 query := `INSERT INTO apps (name, username) VALUES (?, ?)`
128 _, err := s.db.Exec(query, name, username)
129 return err
130}
131
132func (s *storeImpl) GetAppOwner(name string) (string, error) {
133 query := `SELECT username FROM apps WHERE name = ?`
134 row := s.db.QueryRow(query, name)
135 if err := row.Err(); err != nil {
136 return "", err
137 }
138 var ret string
139 if err := row.Scan(&ret); err != nil {
140 return "", err
141 }
142 return ret, nil
143}
144
gioa60f0de2024-07-08 10:49:48 +0400145func (s *storeImpl) GetApps() ([]string, error) {
146 query := `SELECT name FROM apps`
147 rows, err := s.db.Query(query)
148 if err != nil {
149 return nil, err
150 }
151 defer rows.Close()
152 ret := []string{}
153 for rows.Next() {
154 if err := rows.Err(); err != nil {
155 return nil, err
156 }
157 var name string
158 if err := rows.Scan(&name); err != nil {
159 return nil, err
160 }
161 ret = append(ret, name)
162
163 }
164 return ret, nil
165}
166
gio81246f02024-07-10 12:02:15 +0400167func (s *storeImpl) GetUserApps(username string) ([]string, error) {
168 query := `SELECT name FROM apps WHERE username = ?`
169 rows, err := s.db.Query(query, username)
170 if err != nil {
171 return nil, err
172 }
173 defer rows.Close()
174 ret := []string{}
175 for rows.Next() {
176 if err := rows.Err(); err != nil {
177 return nil, err
178 }
179 var name string
180 if err := rows.Scan(&name); err != nil {
181 return nil, err
182 }
183 ret = append(ret, name)
184
185 }
186 return ret, nil
187}
188
giob4a3a192024-08-19 09:55:47 +0400189func (s *storeImpl) CreateCommit(name, hash, message, status, error string, resources []byte) error {
190 query := `INSERT INTO commits (app_name, hash, message, status, error, resources) VALUES (?, ?, ?, ?, ?, ?)`
191 _, err := s.db.Exec(query, name, hash, message, status, error, resources)
gioa60f0de2024-07-08 10:49:48 +0400192 return err
193}
194
giob4a3a192024-08-19 09:55:47 +0400195func (s *storeImpl) GetCommitHistory(name string) ([]CommitMeta, error) {
196 query := `SELECT hash, message, status, error FROM commits WHERE app_name = ?`
gioa60f0de2024-07-08 10:49:48 +0400197 rows, err := s.db.Query(query, name)
198 if err != nil {
199 return nil, err
200 }
201 defer rows.Close()
giob4a3a192024-08-19 09:55:47 +0400202 ret := []CommitMeta{}
gioa60f0de2024-07-08 10:49:48 +0400203 for rows.Next() {
204 if err := rows.Err(); err != nil {
205 return nil, err
206 }
giob4a3a192024-08-19 09:55:47 +0400207 var c CommitMeta
208 if err := rows.Scan(&c.Hash, &c.Message, &c.Status, &c.Error); err != nil {
gioa60f0de2024-07-08 10:49:48 +0400209 return nil, err
210 }
211 ret = append(ret, c)
212
213 }
214 return ret, nil
215}
giob4a3a192024-08-19 09:55:47 +0400216
217func (s *storeImpl) GetCommit(hash string) (Commit, error) {
218 query := `SELECT hash, message, status, error, resources FROM commits WHERE hash = ?`
219 row := s.db.QueryRow(query, hash)
220 if err := row.Err(); err != nil {
221 return Commit{}, err
222 }
223 var ret Commit
224 var c Commit
225 var res []byte
226 if err := row.Scan(&c.Hash, &c.Message, &c.Status, &c.Error, &res); err != nil {
227 return Commit{}, err
228 }
229 if err := json.NewDecoder(bytes.NewBuffer(res)).Decode(&ret.Resources); err != nil {
230 return Commit{}, err
231 }
232 return ret, nil
233}