Files
mesh-host/internal/link/files.go
T
2026-10-21 14:34:29 +02:00

84 lines
2.6 KiB
Go

package link
import (
"context"
"fmt"
"io"
"time"
"git.novox.be/novox/mesh-sdk/go/declaration"
"github.com/nats-io/nats.go"
)
// FilesBus is a link that can read an object of the bucket the controller keeps a declaration's large files in
// (novox/hq ADR 0486 §1, §2). Separate from Bus, as HaveBus is: a link that cannot is still a link, and a declaration
// whose files are given by digest is then refused whole, naming that.
type FilesBus interface {
// FileObject is the object of that name, no larger than limit bytes, read within ctx. **Reads by name only**: a
// node-engine's account may neither list the bucket nor write it (ADR 0486 §2).
FileObject(ctx context.Context, name string, limit int64) ([]byte, error)
}
type filesKey struct{}
// WithFiles is ctx carrying the link a declaration's file references are fetched on.
func WithFiles(ctx context.Context, bus any) context.Context {
if f, ok := bus.(FilesBus); ok {
return context.WithValue(ctx, filesKey{}, f)
}
return ctx
}
// FilesFrom is the fetch the apply's context carries, or nil when the node has no such link.
func FilesFrom(ctx context.Context) func(context.Context, string, int64) ([]byte, error) {
if f, ok := ctx.Value(filesKey{}).(FilesBus); ok {
return f.FileObject
}
return nil
}
// FileObject reads one object by name from the declaration-files bucket. **Only binds**: the bucket is the controller's
// to make, so one that is not there is said as what it is. Nothing waits longer than ctx allows.
func (b OverNATS) FileObject(ctx context.Context, name string, limit int64) ([]byte, error) {
js := b.JS
wait := 10 * time.Second
if at, ok := ctx.Deadline(); ok {
if wait = time.Until(at); wait <= 0 {
return nil, context.DeadlineExceeded
}
}
if b.Conn != nil {
bounded, err := b.Conn.JetStream(nats.MaxWait(wait))
if err != nil {
return nil, err
}
js = bounded
}
store, err := js.ObjectStore(declaration.FileBucket)
if err != nil {
return nil, fmt.Errorf("the object bucket %s: %w", declaration.FileBucket, err)
}
res, err := store.Get(name, nats.Context(ctx))
if err != nil {
return nil, err
}
defer res.Close()
body, err := io.ReadAll(io.LimitReader(res, limit+1))
if err != nil {
return nil, err
}
if int64(len(body)) > limit {
return nil, fmt.Errorf("the object is larger than the %d bytes the declaration names", limit)
}
return body, nil
}
func (l *natsLink) FileObject(ctx context.Context, name string, limit int64) ([]byte, error) {
return OverNATS{Conn: l.conn, JS: l.js}.FileObject(ctx, name, limit)
}
var (
_ FilesBus = OverNATS{}
_ FilesBus = (*natsLink)(nil)
)