Files
zeekay 054d65663a they are Bases, one per org — the word tenant is gone
Base's own registry was already org-native in every method — OrgDB(orgID),
HasOrg(orgID), orgDir(orgID) — and only the type names said tenant. So this is
the vocabulary catching up with the code, not a change of behaviour.

	TenantRegistry -> Bases          app.Bases().OrgDB(org)
	TenantConfig   -> BasesConfig
	MultiTenant    -> PerOrg         it enables per-org databases; say that
	MULTI_TENANT   -> MULTI_BASE
	core/tenant.go -> core/bases.go

Tenant was the wrong noun twice over: it is the multi-tenancy word, and this
product's unit is the thing a customer creates per org — a Base. Identity here
is org, user, project, and nothing else.

The UI never said it; this was Go-side only. 64 packages green.

One test was collateral and is fixed rather than renamed: TestToHTTPHeader
asserts a pure snake_case-to-Header-Case transform, so rewriting its EXPECTED
value while its input stayed "tenant_id" made it assert something false. The case
is dropped — org_id already covers that transform.
2026-08-10 18:23:40 -07:00

79 lines
2.1 KiB
Go

package encreplica
import (
"context"
"os"
"path/filepath"
"strings"
)
// LocalBlobs is a directory-backed Blobs for dev/test. Production swaps in an
// S3- or hanzoai/vfs-backed Blobs; the age encryption boundary lives in Client
// and is identical for every backend.
type LocalBlobs struct{ root string }
// NewLocalBlobs returns a directory-backed Blobs rooted at dir.
func NewLocalBlobs(dir string) *LocalBlobs { return &LocalBlobs{root: dir} }
func (b *LocalBlobs) osPath(key string) string {
return filepath.Join(b.root, filepath.FromSlash(key))
}
// Put writes key atomically (temp + rename).
func (b *LocalBlobs) Put(_ context.Context, key string, data []byte) error {
p := b.osPath(key)
if err := os.MkdirAll(filepath.Dir(p), 0o700); err != nil {
return err
}
tmp := p + ".tmp"
if err := os.WriteFile(tmp, data, 0o600); err != nil {
return err
}
return os.Rename(tmp, p)
}
// Get returns key's bytes; a missing key yields an error satisfying
// errors.Is(err, os.ErrNotExist).
func (b *LocalBlobs) Get(_ context.Context, key string) ([]byte, error) {
return os.ReadFile(b.osPath(key))
}
// List returns the full keys of the (non-directory) entries directly under
// prefix. A missing prefix yields an empty slice (not an error).
func (b *LocalBlobs) List(_ context.Context, prefix string) ([]string, error) {
dir := b.osPath(strings.TrimSuffix(prefix, "/"))
entries, err := os.ReadDir(dir)
if os.IsNotExist(err) {
return nil, nil
}
if err != nil {
return nil, err
}
keys := make([]string, 0, len(entries))
for _, e := range entries {
if e.IsDir() || strings.HasSuffix(e.Name(), ".tmp") {
continue
}
keys = append(keys, prefix+e.Name())
}
return keys, nil
}
// Delete removes key; a missing key is not an error.
func (b *LocalBlobs) Delete(_ context.Context, key string) error {
err := os.Remove(b.osPath(key))
if os.IsNotExist(err) {
return nil
}
return err
}
// DeleteAll removes every blob for this base.
func (b *LocalBlobs) DeleteAll(_ context.Context) error {
err := os.RemoveAll(b.root)
if os.IsNotExist(err) {
return nil
}
return err
}