mirror of
https://github.com/hanzoai/base.git
synced 2026-10-30 15:03:47 +00:00
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.
79 lines
2.1 KiB
Go
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
|
|
}
|