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