Demo app, collections/features CRUD, QR codes, docs
CI / test (push) Successful in 4s

Demo app (web/):
- Collections: select, rename, remove (x button per row), delete
- Features: add point (lon/lat validation), remove, list in selected collection
- QR codes for pk (private) and pb (public) keys
- Restore public key from private key
- 409 Conflict handled for already-registered login
- Title: Momswap Geo Backend Use-Cases Test

Backend:
- PATCH /v1/collections/{id} for rename
- DELETE /v1/collections/{id}
- Clearer lon/lat validation errors (-180..180, -90..90)

Client:
- updateCollection, deleteCollection, derivePublicKey

Docs:
- docs/frontend-development.md (demo app, local dev)
- README links to all docs

Made-with: Cursor
This commit is contained in:
2026-03-01 13:41:54 +00:00
parent ceeac1a1ee
commit ef3957b618
15 changed files with 512 additions and 26 deletions
+32 -2
View File
@@ -244,8 +244,11 @@ func validatePoint(point store.Point) error {
return fmt.Errorf("%w: coordinates must have lon/lat", ErrBadRequest)
}
lon, lat := point.Coordinates[0], point.Coordinates[1]
if lon < -180 || lon > 180 || lat < -90 || lat > 90 {
return fmt.Errorf("%w: invalid lon/lat bounds", ErrBadRequest)
if lon < -180 || lon > 180 {
return fmt.Errorf("%w: longitude must be -180 to 180, got %.2f", ErrBadRequest, lon)
}
if lat < -90 || lat > 90 {
return fmt.Errorf("%w: latitude must be -90 to 90, got %.2f", ErrBadRequest, lat)
}
return nil
}
@@ -272,6 +275,33 @@ func (s *Service) ListCollections(ownerKey string) []store.Collection {
return s.store.ListCollectionsByOwner(ownerKey)
}
func (s *Service) UpdateCollection(ownerKey, collectionID, name string) (store.Collection, error) {
collection, err := s.store.GetCollection(collectionID)
if err != nil {
return store.Collection{}, ErrCollectionMiss
}
if collection.OwnerKey != ownerKey {
return store.Collection{}, ErrForbidden
}
if name == "" {
return store.Collection{}, fmt.Errorf("%w: collection name required", ErrBadRequest)
}
collection.Name = name
s.store.SaveCollection(collection)
return collection, nil
}
func (s *Service) DeleteCollection(ownerKey, collectionID string) error {
collection, err := s.store.GetCollection(collectionID)
if err != nil {
return ErrCollectionMiss
}
if collection.OwnerKey != ownerKey {
return ErrForbidden
}
return s.store.DeleteCollection(collectionID)
}
func (s *Service) CreateFeature(ownerKey, collectionID string, geometry store.Point, properties map[string]interface{}) (store.Feature, error) {
collection, err := s.store.GetCollection(collectionID)
if err != nil {