mirror of
https://tangled.org/evan.jarrett.net/at-container-registry
synced 2026-09-02 00:06:58 +00:00
111 lines
3.0 KiB
Go
111 lines
3.0 KiB
Go
package testpds
|
|
|
|
import (
|
|
"crypto/sha256"
|
|
"encoding/base32"
|
|
"encoding/json"
|
|
"sync"
|
|
)
|
|
|
|
// recordStore is the in-memory backing for the fake PDS's repo.* endpoints.
|
|
// Records are keyed by (did, collection, rkey). Values are stored as raw JSON
|
|
// so the fake never has to know the lexicon shape.
|
|
type recordStore struct {
|
|
mu sync.RWMutex
|
|
records map[string]map[string]map[string]json.RawMessage
|
|
}
|
|
|
|
func newRecordStore() *recordStore {
|
|
return &recordStore{records: make(map[string]map[string]map[string]json.RawMessage)}
|
|
}
|
|
|
|
func (s *recordStore) put(did, collection, rkey string, value json.RawMessage) {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
if s.records[did] == nil {
|
|
s.records[did] = make(map[string]map[string]json.RawMessage)
|
|
}
|
|
if s.records[did][collection] == nil {
|
|
s.records[did][collection] = make(map[string]json.RawMessage)
|
|
}
|
|
s.records[did][collection][rkey] = value
|
|
}
|
|
|
|
func (s *recordStore) get(did, collection, rkey string) (json.RawMessage, bool) {
|
|
s.mu.RLock()
|
|
defer s.mu.RUnlock()
|
|
if s.records[did] == nil || s.records[did][collection] == nil {
|
|
return nil, false
|
|
}
|
|
v, ok := s.records[did][collection][rkey]
|
|
return v, ok
|
|
}
|
|
|
|
func (s *recordStore) delete(did, collection, rkey string) bool {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
if s.records[did] == nil || s.records[did][collection] == nil {
|
|
return false
|
|
}
|
|
if _, ok := s.records[did][collection][rkey]; !ok {
|
|
return false
|
|
}
|
|
delete(s.records[did][collection], rkey)
|
|
return true
|
|
}
|
|
|
|
// list returns a snapshot of all (rkey, value) pairs in the named collection,
|
|
// in undefined order. Callers are expected to sort if they care.
|
|
func (s *recordStore) list(did, collection string) []recordEntry {
|
|
s.mu.RLock()
|
|
defer s.mu.RUnlock()
|
|
if s.records[did] == nil || s.records[did][collection] == nil {
|
|
return nil
|
|
}
|
|
out := make([]recordEntry, 0, len(s.records[did][collection]))
|
|
for rkey, v := range s.records[did][collection] {
|
|
out = append(out, recordEntry{RKey: rkey, Value: v})
|
|
}
|
|
return out
|
|
}
|
|
|
|
type recordEntry struct {
|
|
RKey string
|
|
Value json.RawMessage
|
|
}
|
|
|
|
// fakeCID synthesizes a deterministic-looking CID for a record. Production
|
|
// code rarely validates this against the actual record content; it just
|
|
// echoes it back. We use a SHA-256 of the value, base32-encoded with a CIDv1
|
|
// prefix-like marker.
|
|
var b32 = base32.StdEncoding.WithPadding(base32.NoPadding)
|
|
|
|
func fakeCID(value []byte) string {
|
|
h := sha256.Sum256(value)
|
|
return "bafy" + b32.EncodeToString(h[:24])
|
|
}
|
|
|
|
// blobStore is a simple content-addressed store for ATProto blobs. The
|
|
// uploadBlob handler writes bytes here; sync.getBlob reads them back by CID.
|
|
type blobStore struct {
|
|
mu sync.RWMutex
|
|
blobs map[string][]byte
|
|
}
|
|
|
|
func newBlobStore() *blobStore {
|
|
return &blobStore{blobs: make(map[string][]byte)}
|
|
}
|
|
|
|
func (b *blobStore) put(cid string, data []byte) {
|
|
b.mu.Lock()
|
|
defer b.mu.Unlock()
|
|
b.blobs[cid] = data
|
|
}
|
|
|
|
func (b *blobStore) get(cid string) ([]byte, bool) {
|
|
b.mu.RLock()
|
|
defer b.mu.RUnlock()
|
|
v, ok := b.blobs[cid]
|
|
return v, ok
|
|
}
|