mirror of
https://github.com/DeBrosOfficial/network.git
synced 2026-01-30 14:33:03 +00:00
176 lines
5.4 KiB
Go
176 lines
5.4 KiB
Go
// Package registry manages function metadata in RQLite and bytecode in IPFS.
|
|
package registry
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/DeBrosOfficial/network/pkg/ipfs"
|
|
"github.com/DeBrosOfficial/network/pkg/rqlite"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
// Ensure Registry implements FunctionRegistry interface.
|
|
var _ FunctionRegistry = (*Registry)(nil)
|
|
|
|
// Registry coordinates between function storage, IPFS storage, and logging.
|
|
type Registry struct {
|
|
functionStore *FunctionStore
|
|
ipfsStore *IPFSStore
|
|
invocationLogger *InvocationLogger
|
|
logger *zap.Logger
|
|
}
|
|
|
|
// NewRegistry creates a new function registry.
|
|
func NewRegistry(db rqlite.Client, ipfsClient ipfs.IPFSClient, cfg RegistryConfig, logger *zap.Logger) *Registry {
|
|
return &Registry{
|
|
functionStore: NewFunctionStore(db, logger),
|
|
ipfsStore: NewIPFSStore(ipfsClient, cfg.IPFSAPIURL, logger),
|
|
invocationLogger: NewInvocationLogger(db, logger),
|
|
logger: logger,
|
|
}
|
|
}
|
|
|
|
// Register deploys a new function or updates an existing one.
|
|
func (r *Registry) Register(ctx context.Context, fn *FunctionDefinition, wasmBytes []byte) (*Function, error) {
|
|
if fn == nil {
|
|
return nil, &ValidationError{Field: "definition", Message: "cannot be nil"}
|
|
}
|
|
fn.Name = strings.TrimSpace(fn.Name)
|
|
fn.Namespace = strings.TrimSpace(fn.Namespace)
|
|
|
|
if fn.Name == "" {
|
|
return nil, &ValidationError{Field: "name", Message: "cannot be empty"}
|
|
}
|
|
if fn.Namespace == "" {
|
|
return nil, &ValidationError{Field: "namespace", Message: "cannot be empty"}
|
|
}
|
|
if len(wasmBytes) == 0 {
|
|
return nil, &ValidationError{Field: "wasmBytes", Message: "cannot be empty"}
|
|
}
|
|
|
|
oldFn, err := r.functionStore.GetByNameInternal(ctx, fn.Namespace, fn.Name)
|
|
if err != nil && err != ErrFunctionNotFound {
|
|
return nil, &DeployError{FunctionName: fn.Name, Cause: err}
|
|
}
|
|
|
|
wasmCID, err := r.ipfsStore.Upload(ctx, wasmBytes, fn.Name)
|
|
if err != nil {
|
|
return nil, &DeployError{FunctionName: fn.Name, Cause: err}
|
|
}
|
|
|
|
savedFunc, err := r.functionStore.Save(ctx, fn, wasmCID, oldFn)
|
|
if err != nil {
|
|
return nil, &DeployError{FunctionName: fn.Name, Cause: err}
|
|
}
|
|
|
|
if err := r.functionStore.SaveEnvVars(ctx, savedFunc.ID, fn.EnvVars); err != nil {
|
|
return nil, &DeployError{FunctionName: fn.Name, Cause: err}
|
|
}
|
|
|
|
r.logger.Info("Function registered",
|
|
zap.String("id", savedFunc.ID),
|
|
zap.String("name", fn.Name),
|
|
zap.String("namespace", fn.Namespace),
|
|
zap.String("wasm_cid", wasmCID),
|
|
zap.Int("version", savedFunc.Version),
|
|
zap.Bool("updated", oldFn != nil),
|
|
)
|
|
|
|
return oldFn, nil
|
|
}
|
|
|
|
// Get retrieves a function by name and optional version.
|
|
func (r *Registry) Get(ctx context.Context, namespace, name string, version int) (*Function, error) {
|
|
return r.functionStore.Get(ctx, namespace, name, version)
|
|
}
|
|
|
|
// List returns all functions for a namespace.
|
|
func (r *Registry) List(ctx context.Context, namespace string) ([]*Function, error) {
|
|
return r.functionStore.List(ctx, namespace)
|
|
}
|
|
|
|
// Delete removes a function. If version is 0, removes all versions.
|
|
func (r *Registry) Delete(ctx context.Context, namespace, name string, version int) error {
|
|
return r.functionStore.Delete(ctx, namespace, name, version)
|
|
}
|
|
|
|
// GetWASMBytes retrieves the compiled WASM bytecode for a function.
|
|
func (r *Registry) GetWASMBytes(ctx context.Context, wasmCID string) ([]byte, error) {
|
|
if wasmCID == "" {
|
|
return nil, &ValidationError{Field: "wasmCID", Message: "cannot be empty"}
|
|
}
|
|
return r.ipfsStore.Get(ctx, wasmCID)
|
|
}
|
|
|
|
// GetLogs retrieves logs for a function.
|
|
func (r *Registry) GetLogs(ctx context.Context, namespace, name string, limit int) ([]LogEntry, error) {
|
|
return r.invocationLogger.GetLogs(ctx, namespace, name, limit)
|
|
}
|
|
|
|
// GetEnvVars retrieves environment variables for a function.
|
|
func (r *Registry) GetEnvVars(ctx context.Context, functionID string) (map[string]string, error) {
|
|
return r.functionStore.GetEnvVars(ctx, functionID)
|
|
}
|
|
|
|
// GetByID retrieves a function by its ID.
|
|
func (r *Registry) GetByID(ctx context.Context, id string) (*Function, error) {
|
|
return r.functionStore.GetByID(ctx, id)
|
|
}
|
|
|
|
// ListVersions returns all versions of a function.
|
|
func (r *Registry) ListVersions(ctx context.Context, namespace, name string) ([]*Function, error) {
|
|
return r.functionStore.ListVersions(ctx, namespace, name)
|
|
}
|
|
|
|
// LogInvocation records a function invocation and its logs to the database.
|
|
func (r *Registry) LogInvocation(ctx context.Context,
|
|
id, functionID, requestID string,
|
|
triggerType interface{},
|
|
callerWallet string,
|
|
inputSize, outputSize int,
|
|
startedAt, completedAt interface{},
|
|
durationMS int64,
|
|
status interface{},
|
|
errorMessage string,
|
|
memoryUsedMB float64,
|
|
logs []LogEntry) error {
|
|
|
|
var startTime, completeTime time.Time
|
|
if t, ok := startedAt.(time.Time); ok {
|
|
startTime = t
|
|
}
|
|
if t, ok := completedAt.(time.Time); ok {
|
|
completeTime = t
|
|
}
|
|
|
|
data := &InvocationRecordData{
|
|
ID: id,
|
|
FunctionID: functionID,
|
|
RequestID: requestID,
|
|
TriggerType: fmt.Sprintf("%v", triggerType),
|
|
CallerWallet: callerWallet,
|
|
InputSize: inputSize,
|
|
OutputSize: outputSize,
|
|
StartedAt: startTime,
|
|
CompletedAt: completeTime,
|
|
DurationMS: durationMS,
|
|
Status: fmt.Sprintf("%v", status),
|
|
ErrorMessage: errorMessage,
|
|
MemoryUsedMB: memoryUsedMB,
|
|
}
|
|
|
|
data.Logs = make([]LogData, len(logs))
|
|
for i, log := range logs {
|
|
data.Logs[i] = LogData{
|
|
Level: log.Level,
|
|
Message: log.Message,
|
|
Timestamp: log.Timestamp,
|
|
}
|
|
}
|
|
|
|
return r.invocationLogger.Log(ctx, data)
|
|
}
|