Antonio Mika
·
2026-10-09
1package pgsdb
2
3import (
4 "fmt"
5 "log/slog"
6 "slices"
7 "time"
8
9 "github.com/google/uuid"
10 "github.com/picosh/pico/pkg/db"
11 "github.com/picosh/pico/pkg/shared"
12)
13
14type MemoryDB struct {
15 Logger *slog.Logger
16 Users []*db.User
17 Projects []*db.Project
18 Pubkeys []*db.PublicKey
19 Feature *db.FeatureFlag
20 Features []*db.FeatureFlag
21 FormEntries []*db.FormEntry
22}
23
24var _ PgsDB = (*MemoryDB)(nil)
25
26func NewDBMemory(logger *slog.Logger) *MemoryDB {
27 d := &MemoryDB{
28 Logger: logger,
29 }
30 d.Logger.Info("connecting to our in-memory database. All data created during runtime will be lost on exit.")
31 return d
32}
33
34func (me *MemoryDB) SetupTestData() {
35 user := &db.User{
36 ID: uuid.NewString(),
37 Name: "testusr",
38 }
39 me.Users = append(me.Users, user)
40 feature := db.NewFeatureFlag(
41 user.ID,
42 "plus",
43 uint64(25*shared.MB),
44 int64(10*shared.MB),
45 int64(5*shared.KB),
46 )
47 expiresAt := time.Now().Add(time.Hour * 24)
48 feature.ExpiresAt = &expiresAt
49 me.Feature = feature
50}
51
52var errNotImpl = fmt.Errorf("not implemented")
53
54func (me *MemoryDB) FindUsers() ([]*db.User, error) {
55 users := []*db.User{}
56 return users, errNotImpl
57}
58
59func (me *MemoryDB) FindUserByPubkey(key string) (*db.User, error) {
60 for _, pk := range me.Pubkeys {
61 if pk.Key == key {
62 return me.FindUser(pk.UserID)
63 }
64 }
65 return nil, fmt.Errorf("user not found")
66}
67
68func (me *MemoryDB) FindUser(userID string) (*db.User, error) {
69 for _, user := range me.Users {
70 if user.ID == userID {
71 return user, nil
72 }
73 }
74 return nil, fmt.Errorf("user not found")
75}
76
77func (me *MemoryDB) FindUserByName(name string) (*db.User, error) {
78 for _, user := range me.Users {
79 if user.Name == name {
80 return user, nil
81 }
82 }
83 return nil, fmt.Errorf("user not found")
84}
85
86func (me *MemoryDB) FindFeature(userID, name string) (*db.FeatureFlag, error) {
87 for _, ff := range me.Features {
88 if ff.UserID == userID && ff.Name == name {
89 return ff, nil
90 }
91 }
92 if me.Feature != nil && (me.Feature.Name == name || me.Feature.Name == "") {
93 return me.Feature, nil
94 }
95 return nil, fmt.Errorf("feature flag not found")
96}
97
98func (me *MemoryDB) Close() error {
99 return nil
100}
101
102func (me *MemoryDB) FindTotalSizeForUser(userID string) (int, error) {
103 return 0, errNotImpl
104}
105
106func (me *MemoryDB) InsertProject(userID, name, projectDir string) (string, error) {
107 id := uuid.NewString()
108 now := time.Now()
109 me.Projects = append(me.Projects, &db.Project{
110 ID: id,
111 UserID: userID,
112 Name: name,
113 ProjectDir: projectDir,
114 CreatedAt: &now,
115 UpdatedAt: &now,
116 })
117 return id, nil
118}
119
120func (me *MemoryDB) UpdateProject(userID, name string) error {
121 project, err := me.FindProjectByName(userID, name)
122 if err != nil {
123 return err
124 }
125
126 now := time.Now()
127 project.UpdatedAt = &now
128
129 return nil
130}
131
132func (me *MemoryDB) UpsertProject(userID, projectName, projectDir string) (*db.Project, error) {
133 project, err := me.FindProjectByName(userID, projectName)
134 if err == nil {
135 // this just updates the `createdAt` timestamp, useful for book-keeping
136 err = me.UpdateProject(userID, projectName)
137 if err != nil {
138 me.Logger.Error("could not update project", "err", err)
139 return nil, err
140 }
141 return project, nil
142 }
143
144 _, err = me.InsertProject(userID, projectName, projectName)
145 if err != nil {
146 me.Logger.Error("could not create project", "err", err)
147 return nil, err
148 }
149 return me.FindProjectByName(userID, projectName)
150}
151
152func (me *MemoryDB) LinkToProject(userID, projectID, projectDir string, commit bool) error {
153 for _, project := range me.Projects {
154 if project.ID == projectID && project.UserID == userID {
155 if commit {
156 project.ProjectDir = projectDir
157 }
158 return nil
159 }
160 }
161 return fmt.Errorf("project not found by id %s", projectID)
162}
163
164func (me *MemoryDB) RemoveProject(projectID string) error {
165 me.Projects = slices.DeleteFunc(me.Projects, func(project *db.Project) bool {
166 return project.ID == projectID
167 })
168 return nil
169}
170
171func (me *MemoryDB) FindProjectByName(userID, name string) (*db.Project, error) {
172 for _, project := range me.Projects {
173 if project.UserID != userID {
174 continue
175 }
176
177 if project.Name != name {
178 continue
179 }
180
181 return project, nil
182 }
183 return nil, fmt.Errorf("project not found by name %s", name)
184}
185
186func (me *MemoryDB) FindProjectLinks(userID, name string) ([]*db.Project, error) {
187 links := []*db.Project{}
188 for _, project := range me.Projects {
189 if project.UserID == userID && project.ProjectDir == name && project.Name != name {
190 links = append(links, project)
191 }
192 }
193 return links, nil
194}
195
196func (me *MemoryDB) FindProjectsByPrefix(userID, prefix string) ([]*db.Project, error) {
197 return []*db.Project{}, errNotImpl
198}
199
200func (me *MemoryDB) FindProjectsByUser(userID string) ([]*db.Project, error) {
201 pjs := []*db.Project{}
202 for _, project := range me.Projects {
203 if project.UserID != userID {
204 continue
205 }
206 pjs = append(pjs, project)
207 }
208 return pjs, nil
209}
210
211func (me *MemoryDB) FindProjects(userID string) ([]*db.Project, error) {
212 return []*db.Project{}, errNotImpl
213}
214
215func (me *MemoryDB) UpdateProjectAcl(userID, name string, acl db.ProjectAcl) error {
216 project, err := me.FindProjectByName(userID, name)
217 if err != nil {
218 return err
219 }
220 project.Acl = acl
221 return nil
222}
223
224func (me *MemoryDB) RegisterAdmin(username, pubkey, pubkeyName string) error {
225 return errNotImpl
226}
227
228func (me *MemoryDB) InsertAccessLog(*db.AccessLog) error {
229 return errNotImpl
230}
231
232func (me *MemoryDB) InsertFormEntry(userID, name string, data map[string]interface{}) error {
233 id := uuid.NewString()
234 now := time.Now()
235 entry := &db.FormEntry{
236 ID: id,
237 UserID: userID,
238 Name: name,
239 Data: data,
240 CreatedAt: &now,
241 }
242 me.FormEntries = append(me.FormEntries, entry)
243 return nil
244}
245
246func (me *MemoryDB) FindFormEntriesByUserAndName(userID, name string) ([]*db.FormEntry, error) {
247 entries := []*db.FormEntry{}
248 for _, entry := range me.FormEntries {
249 if entry.UserID == userID && entry.Name == name {
250 entries = append(entries, entry)
251 }
252 }
253 return entries, nil
254}
255
256func (me *MemoryDB) FindFormNamesByUser(userID string) ([]string, error) {
257 names := make(map[string]bool)
258 for _, entry := range me.FormEntries {
259 if entry.UserID == userID {
260 names[entry.Name] = true
261 }
262 }
263 result := []string{}
264 for name := range names {
265 result = append(result, name)
266 }
267 return result, nil
268}
269
270func (me *MemoryDB) RemoveFormEntriesByUserAndName(userID, name string) error {
271 filtered := []*db.FormEntry{}
272 for _, entry := range me.FormEntries {
273 if entry.UserID != userID || entry.Name != name {
274 filtered = append(filtered, entry)
275 }
276 }
277 me.FormEntries = filtered
278 return nil
279}