// Package store hides the persistence layer behind a Store interface. // Two implementations: Memory (dev convenience, used when DATABASE_URL is // empty) and Postgres (production via pgx). Handlers depend on the // interface — never on a concrete type. package store import ( "context" "errors" "time" ) // Sentinel errors. var ( ErrNotFound = errors.New("not found") ErrConflict = errors.New("conflict") ErrInvalidInput = errors.New("invalid input") ) // TenantCreate is the input shape for Store.CreateTenant. type TenantCreate struct { Slug string Name string Plan string // optional, defaults to "starter" Kind string // optional, defaults to "customer" SalesOwner string // optional } // TenantUpdate captures partial mutations. Nil fields are left untouched. type TenantUpdate struct { Status *string Plan *string ErpCustomerID *string StripeCustID *string TrialEndsAt *time.Time ContractStart *time.Time ContractEnd *time.Time SalesOwner *string } // APIKeyCreate is the input shape for Store.CreateAPIKey. type APIKeyCreate struct { TenantID string Product string // empty = applies to all products Name string Scopes []string Prefix string Hash string // argon2id encoded CreatedBy string } // AuditFilter narrows /v1/audit GET results. type AuditFilter struct { TenantID string Product string ActorID string Action string Since *time.Time Until *time.Time Limit int Cursor int64 // id > Cursor (ascending) is the next page anchor } // Store is the persistence contract. Implementations: // - Memory — in-process, used when DATABASE_URL is empty (dev convenience). // - Postgres — pgxpool-backed, used in stage + prod. type Store interface { // Tenants CreateTenant(ctx context.Context, in TenantCreate) (*Tenant, error) GetTenant(ctx context.Context, id string) (*Tenant, error) GetTenantBySlug(ctx context.Context, slug string) (*Tenant, error) UpdateTenant(ctx context.Context, id string, in TenantUpdate) (*Tenant, error) // Entitlements UpsertTenantProduct(ctx context.Context, tp TenantProduct) (*TenantProduct, error) ListTenantProducts(ctx context.Context, tenantID string) ([]TenantProduct, error) // API keys CreateAPIKey(ctx context.Context, in APIKeyCreate) (*APIKey, error) FindAPIKeyByPrefix(ctx context.Context, prefix string) (*APIKey, string, error) // returns key + hash TouchAPIKeyUsed(ctx context.Context, id string) error RevokeAPIKey(ctx context.Context, id string) error ListAPIKeys(ctx context.Context, tenantID string) ([]APIKey, error) // Audit AppendAudit(ctx context.Context, ev AuditEvent) (*AuditEvent, error) ListAudit(ctx context.Context, f AuditFilter) ([]AuditEvent, int64, error) // returns rows + next cursor (0 = none) // Lifecycle Close() Ping(ctx context.Context) error }