2026-07-10 00:01:56 -07:00
|
|
|
package webapp
|
|
|
|
|
|
|
|
|
|
// MetaStore is the hub's metadata persistence, split into one typed repository
|
|
|
|
|
// per entity. It holds ONLY the control plane — accounts, tokens, projects,
|
|
|
|
|
// orgs, invites, shares, devices. File content and the append-only journals
|
|
|
|
|
// live in the object store and never touch this; ephemeral state (one-time
|
|
|
|
|
// login and device codes, rate-limit buckets) stays in memory.
|
|
|
|
|
//
|
|
|
|
|
// A deployment chooses the backend: `file` (JSON on disk, the zero-dependency
|
|
|
|
|
// default) or `sql` (SQLite locally, Postgres/Supabase in production). The
|
|
|
|
|
// service structs (BuiltinAuth, OrgDB, …) keep their in-memory maps, mutexes,
|
|
|
|
|
// and business logic and persist each change through these repos — so reads
|
|
|
|
|
// stay in memory and writes are a single record apiece, which every backend
|
|
|
|
|
// implements as one real row.
|
|
|
|
|
type MetaStore interface {
|
|
|
|
|
Accounts() AccountRepo
|
|
|
|
|
Projects() ProjectRepo
|
|
|
|
|
Orgs() OrgRepo
|
|
|
|
|
Shares() ShareRepo
|
|
|
|
|
Devices() DeviceRepo
|
2026-07-11 14:41:07 -07:00
|
|
|
Reads() ReadRepo
|
2026-07-10 00:01:56 -07:00
|
|
|
Close() error
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// AccountRepo persists accounts, device tokens, and the (singleton) signup
|
|
|
|
|
// policy. Load returns everything at open; every other method is one record.
|
|
|
|
|
type AccountRepo interface {
|
|
|
|
|
Load() (users []*authUser, tokens []authToken, policy *authPolicy, err error)
|
|
|
|
|
PutAccount(u *authUser) error
|
|
|
|
|
DeleteAccount(id string) error
|
|
|
|
|
PutToken(t authToken) error
|
|
|
|
|
DeleteToken(hash string) error
|
|
|
|
|
PutPolicy(p authPolicy) error
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type ProjectRepo interface {
|
|
|
|
|
Load() ([]Project, error)
|
|
|
|
|
Put(p Project) error
|
|
|
|
|
Delete(id string) error
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type OrgRepo interface {
|
|
|
|
|
Load() (orgs []Org, invites []OrgInvite, err error)
|
|
|
|
|
PutOrg(o Org) error
|
|
|
|
|
DeleteOrg(id string) error
|
|
|
|
|
PutInvite(i OrgInvite) error
|
|
|
|
|
DeleteInvite(token string) error
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type ShareRepo interface {
|
|
|
|
|
Load() ([]Share, error)
|
|
|
|
|
Put(s Share) error
|
|
|
|
|
Delete(token string) error
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type DeviceRepo interface {
|
|
|
|
|
Load() ([]DeviceInfo, error)
|
|
|
|
|
Put(d DeviceInfo) error
|
|
|
|
|
}
|
2026-07-11 14:41:07 -07:00
|
|
|
|
|
|
|
|
// ReadRepo persists read-telemetry buckets (see ReadStat). Unlike the other
|
|
|
|
|
// repos it is batch-oriented: reads are telemetry, and the ledger flushes many
|
|
|
|
|
// dirty buckets at once — one file rewrite / one SQL transaction per flush,
|
|
|
|
|
// not one write per bucket.
|
|
|
|
|
type ReadRepo interface {
|
|
|
|
|
Load() ([]ReadStat, error)
|
|
|
|
|
PutBatch(stats []ReadStat) error // upsert by (project, path, day, kind, actor)
|
|
|
|
|
DeleteBatch(keys []ReadStatKey) error
|
|
|
|
|
}
|