mirror of
https://github.com/DeBrosOfficial/network.git
synced 2026-01-30 03:43:04 +00:00
feat: add API examples for Orama Network Gateway
- Introduced a new `example.http` file containing comprehensive API examples for the Orama Network Gateway, demonstrating various functionalities including health checks, distributed cache operations, decentralized storage interactions, real-time pub/sub messaging, and serverless function management. - Updated the README to include a section on serverless functions using WebAssembly (WASM), detailing the build, deployment, invocation, and management processes for serverless functions. - Removed outdated debug configuration file to streamline project structure.
This commit is contained in:
parent
a9844a1451
commit
df5b11b175
@ -1,68 +0,0 @@
|
|||||||
// Project-local debug tasks
|
|
||||||
//
|
|
||||||
// For more documentation on how to configure debug tasks,
|
|
||||||
// see: https://zed.dev/docs/debugger
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"label": "Gateway Go (Delve)",
|
|
||||||
"adapter": "Delve",
|
|
||||||
"request": "launch",
|
|
||||||
"mode": "debug",
|
|
||||||
"program": "./cmd/gateway",
|
|
||||||
"env": {
|
|
||||||
"GATEWAY_ADDR": ":6001",
|
|
||||||
"GATEWAY_BOOTSTRAP_PEERS": "/ip4/localhost/tcp/4001/p2p/12D3KooWSHHwEY6cga3ng7tD1rzStAU58ogQXVMX3LZJ6Gqf6dee",
|
|
||||||
"GATEWAY_NAMESPACE": "default",
|
|
||||||
"GATEWAY_API_KEY": "ak_iGustrsFk9H8uXpwczCATe5U:default"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "E2E Test Go (Delve)",
|
|
||||||
"adapter": "Delve",
|
|
||||||
"request": "launch",
|
|
||||||
"mode": "test",
|
|
||||||
"buildFlags": "-tags e2e",
|
|
||||||
"program": "./e2e",
|
|
||||||
"env": {
|
|
||||||
"GATEWAY_API_KEY": "ak_iGustrsFk9H8uXpwczCATe5U:default"
|
|
||||||
},
|
|
||||||
"args": ["-test.v"]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"adapter": "Delve",
|
|
||||||
"label": "Gateway Go 6001 Port (Delve)",
|
|
||||||
"request": "launch",
|
|
||||||
"mode": "debug",
|
|
||||||
"program": "./cmd/gateway",
|
|
||||||
"env": {
|
|
||||||
"GATEWAY_ADDR": ":6001",
|
|
||||||
"GATEWAY_BOOTSTRAP_PEERS": "/ip4/localhost/tcp/4001/p2p/12D3KooWSHHwEY6cga3ng7tD1rzStAU58ogQXVMX3LZJ6Gqf6dee",
|
|
||||||
"GATEWAY_NAMESPACE": "default",
|
|
||||||
"GATEWAY_API_KEY": "ak_iGustrsFk9H8uXpwczCATe5U:default"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"adapter": "Delve",
|
|
||||||
"label": "Network CLI - peers (Delve)",
|
|
||||||
"request": "launch",
|
|
||||||
"mode": "debug",
|
|
||||||
"program": "./cmd/cli",
|
|
||||||
"args": ["peers"]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"adapter": "Delve",
|
|
||||||
"label": "Network CLI - PubSub Subscribe (Delve)",
|
|
||||||
"request": "launch",
|
|
||||||
"mode": "debug",
|
|
||||||
"program": "./cmd/cli",
|
|
||||||
"args": ["pubsub", "subscribe", "monitoring"]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"adapter": "Delve",
|
|
||||||
"label": "Node Go (Delve)",
|
|
||||||
"request": "launch",
|
|
||||||
"mode": "debug",
|
|
||||||
"program": "./cmd/node",
|
|
||||||
"args": ["--config", "configs/node.yaml"]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
53
README.md
53
README.md
@ -127,6 +127,54 @@ make build
|
|||||||
./bin/orama auth logout
|
./bin/orama auth logout
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Serverless Functions (WASM)
|
||||||
|
|
||||||
|
Orama supports high-performance serverless function execution using WebAssembly (WASM). Functions are isolated, secure, and can interact with network services like the distributed cache.
|
||||||
|
|
||||||
|
### 1. Build Functions
|
||||||
|
|
||||||
|
Functions must be compiled to WASM. We recommend using [TinyGo](https://tinygo.org/).
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Build example functions to examples/functions/bin/
|
||||||
|
./examples/functions/build.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Deployment
|
||||||
|
|
||||||
|
Deploy your compiled `.wasm` file to the network via the Gateway.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Deploy a function
|
||||||
|
curl -X POST http://localhost:6001/v1/functions \
|
||||||
|
-H "Authorization: Bearer <your_api_key>" \
|
||||||
|
-F "name=hello-world" \
|
||||||
|
-F "namespace=default" \
|
||||||
|
-F "wasm=@./examples/functions/bin/hello.wasm"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Invocation
|
||||||
|
|
||||||
|
Trigger your function with a JSON payload. The function receives the payload via `stdin` and returns its response via `stdout`.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Invoke via HTTP
|
||||||
|
curl -X POST http://localhost:6001/v1/functions/hello-world/invoke \
|
||||||
|
-H "Authorization: Bearer <your_api_key>" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"name": "Developer"}'
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Management
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# List all functions in a namespace
|
||||||
|
curl http://localhost:6001/v1/functions?namespace=default
|
||||||
|
|
||||||
|
# Delete a function
|
||||||
|
curl -X DELETE http://localhost:6001/v1/functions/hello-world?namespace=default
|
||||||
|
```
|
||||||
|
|
||||||
## Production Deployment
|
## Production Deployment
|
||||||
|
|
||||||
### Prerequisites
|
### Prerequisites
|
||||||
@ -260,6 +308,11 @@ sudo orama install
|
|||||||
- `POST /v1/pubsub/publish` - Publish message
|
- `POST /v1/pubsub/publish` - Publish message
|
||||||
- `GET /v1/pubsub/topics` - List topics
|
- `GET /v1/pubsub/topics` - List topics
|
||||||
- `GET /v1/pubsub/ws?topic=<name>` - WebSocket subscribe
|
- `GET /v1/pubsub/ws?topic=<name>` - WebSocket subscribe
|
||||||
|
- `POST /v1/functions` - Deploy function (multipart/form-data)
|
||||||
|
- `POST /v1/functions/{name}/invoke` - Invoke function
|
||||||
|
- `GET /v1/functions` - List functions
|
||||||
|
- `DELETE /v1/functions/{name}` - Delete function
|
||||||
|
- `GET /v1/functions/{name}/logs` - Get function logs
|
||||||
|
|
||||||
See `openapi/gateway.yaml` for complete API specification.
|
See `openapi/gateway.yaml` for complete API specification.
|
||||||
|
|
||||||
|
|||||||
158
example.http
Normal file
158
example.http
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
### Orama Network Gateway API Examples
|
||||||
|
# This file is designed for the VS Code "REST Client" extension.
|
||||||
|
# It demonstrates the core capabilities of the DeBros Network Gateway.
|
||||||
|
|
||||||
|
@baseUrl = http://localhost:6001
|
||||||
|
@apiKey = ak_X32jj2fiin8zzv0hmBKTC5b5:default
|
||||||
|
@contentType = application/json
|
||||||
|
|
||||||
|
############################################################
|
||||||
|
### 1. SYSTEM & HEALTH
|
||||||
|
############################################################
|
||||||
|
|
||||||
|
# @name HealthCheck
|
||||||
|
GET {{baseUrl}}/v1/health
|
||||||
|
X-API-Key: {{apiKey}}
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
# @name SystemStatus
|
||||||
|
# Returns the full status of the gateway and connected services
|
||||||
|
GET {{baseUrl}}/v1/status
|
||||||
|
X-API-Key: {{apiKey}}
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
# @name NetworkStatus
|
||||||
|
# Returns the P2P network status and PeerID
|
||||||
|
GET {{baseUrl}}/v1/network/status
|
||||||
|
X-API-Key: {{apiKey}}
|
||||||
|
|
||||||
|
|
||||||
|
############################################################
|
||||||
|
### 2. DISTRIBUTED CACHE (OLRIC)
|
||||||
|
############################################################
|
||||||
|
|
||||||
|
# @name CachePut
|
||||||
|
# Stores a value in the distributed cache (DMap)
|
||||||
|
POST {{baseUrl}}/v1/cache/put
|
||||||
|
X-API-Key: {{apiKey}}
|
||||||
|
Content-Type: {{contentType}}
|
||||||
|
|
||||||
|
{
|
||||||
|
"dmap": "demo-cache",
|
||||||
|
"key": "video-demo",
|
||||||
|
"value": "Hello from REST Client!"
|
||||||
|
}
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
# @name CacheGet
|
||||||
|
# Retrieves a value from the distributed cache
|
||||||
|
POST {{baseUrl}}/v1/cache/get
|
||||||
|
X-API-Key: {{apiKey}}
|
||||||
|
Content-Type: {{contentType}}
|
||||||
|
|
||||||
|
{
|
||||||
|
"dmap": "demo-cache",
|
||||||
|
"key": "video-demo"
|
||||||
|
}
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
# @name CacheScan
|
||||||
|
# Scans for keys in a specific DMap
|
||||||
|
POST {{baseUrl}}/v1/cache/scan
|
||||||
|
X-API-Key: {{apiKey}}
|
||||||
|
Content-Type: {{contentType}}
|
||||||
|
|
||||||
|
{
|
||||||
|
"dmap": "demo-cache"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
############################################################
|
||||||
|
### 3. DECENTRALIZED STORAGE (IPFS)
|
||||||
|
############################################################
|
||||||
|
|
||||||
|
# @name StorageUpload
|
||||||
|
# Uploads a file to IPFS (Multipart)
|
||||||
|
POST {{baseUrl}}/v1/storage/upload
|
||||||
|
X-API-Key: {{apiKey}}
|
||||||
|
Content-Type: multipart/form-data; boundary=boundary
|
||||||
|
|
||||||
|
--boundary
|
||||||
|
Content-Disposition: form-data; name="file"; filename="demo.txt"
|
||||||
|
Content-Type: text/plain
|
||||||
|
|
||||||
|
This is a demonstration of decentralized storage on the Sonr Network.
|
||||||
|
--boundary--
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
# @name StorageStatus
|
||||||
|
# Check the pinning status and replication of a CID
|
||||||
|
# Replace {cid} with the CID returned from the upload above
|
||||||
|
@demoCid = bafkreid76y6x6v2n5o4n6n5o4n6n5o4n6n5o4n6n5o4
|
||||||
|
GET {{baseUrl}}/v1/storage/status/{{demoCid}}
|
||||||
|
X-API-Key: {{apiKey}}
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
# @name StorageDownload
|
||||||
|
# Retrieve content directly from IPFS via the gateway
|
||||||
|
GET {{baseUrl}}/v1/storage/get/{{demoCid}}
|
||||||
|
X-API-Key: {{apiKey}}
|
||||||
|
|
||||||
|
|
||||||
|
############################################################
|
||||||
|
### 4. REAL-TIME PUB/SUB
|
||||||
|
############################################################
|
||||||
|
|
||||||
|
# @name ListTopics
|
||||||
|
# Lists all active topics in the current namespace
|
||||||
|
GET {{baseUrl}}/v1/pubsub/topics
|
||||||
|
X-API-Key: {{apiKey}}
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
# @name PublishMessage
|
||||||
|
# Publishes a base64 encoded message to a topic
|
||||||
|
POST {{baseUrl}}/v1/pubsub/publish
|
||||||
|
X-API-Key: {{apiKey}}
|
||||||
|
Content-Type: {{contentType}}
|
||||||
|
|
||||||
|
{
|
||||||
|
"topic": "network-updates",
|
||||||
|
"data_base64": "U29uciBOZXR3b3JrIGlzIGF3ZXNvbWUh"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
############################################################
|
||||||
|
### 5. SERVERLESS FUNCTIONS
|
||||||
|
############################################################
|
||||||
|
|
||||||
|
# @name ListFunctions
|
||||||
|
# Lists all deployed serverless functions
|
||||||
|
GET {{baseUrl}}/v1/functions
|
||||||
|
X-API-Key: {{apiKey}}
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
# @name InvokeFunction
|
||||||
|
# Invokes a deployed function by name
|
||||||
|
# Path: /v1/invoke/{namespace}/{functionName}
|
||||||
|
POST {{baseUrl}}/v1/invoke/default/hello
|
||||||
|
X-API-Key: {{apiKey}}
|
||||||
|
Content-Type: {{contentType}}
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "Developer"
|
||||||
|
}
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
# @name WhoAmI
|
||||||
|
# Validates the API Key and returns caller identity
|
||||||
|
GET {{baseUrl}}/v1/auth/whoami
|
||||||
|
X-API-Key: {{apiKey}}
|
||||||
@ -9,6 +9,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/DeBrosOfficial/network/pkg/gateway/auth"
|
||||||
"github.com/DeBrosOfficial/network/pkg/serverless"
|
"github.com/DeBrosOfficial/network/pkg/serverless"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
@ -578,7 +579,14 @@ func (h *ServerlessHandlers) getNamespaceFromRequest(r *http.Request) string {
|
|||||||
return ns
|
return ns
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to extract from JWT (if authentication middleware has set it)
|
// Try context (set by auth middleware)
|
||||||
|
if v := r.Context().Value(ctxKeyNamespaceOverride); v != nil {
|
||||||
|
if ns, ok := v.(string); ok && ns != "" {
|
||||||
|
return ns
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try header as fallback
|
||||||
if ns := r.Header.Get("X-Namespace"); ns != "" {
|
if ns := r.Header.Get("X-Namespace"); ns != "" {
|
||||||
return ns
|
return ns
|
||||||
}
|
}
|
||||||
@ -588,9 +596,29 @@ func (h *ServerlessHandlers) getNamespaceFromRequest(r *http.Request) string {
|
|||||||
|
|
||||||
// getWalletFromRequest extracts wallet address from JWT
|
// getWalletFromRequest extracts wallet address from JWT
|
||||||
func (h *ServerlessHandlers) getWalletFromRequest(r *http.Request) string {
|
func (h *ServerlessHandlers) getWalletFromRequest(r *http.Request) string {
|
||||||
|
// 1. Try X-Wallet header (legacy/direct bypass)
|
||||||
if wallet := r.Header.Get("X-Wallet"); wallet != "" {
|
if wallet := r.Header.Get("X-Wallet"); wallet != "" {
|
||||||
return wallet
|
return wallet
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 2. Try JWT claims from context
|
||||||
|
if v := r.Context().Value(ctxKeyJWT); v != nil {
|
||||||
|
if claims, ok := v.(*auth.JWTClaims); ok && claims != nil {
|
||||||
|
subj := strings.TrimSpace(claims.Sub)
|
||||||
|
// Ensure it's not an API key (standard Orama logic)
|
||||||
|
if !strings.HasPrefix(strings.ToLower(subj), "ak_") && !strings.Contains(subj, ":") {
|
||||||
|
return subj
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Fallback to API key identity (namespace)
|
||||||
|
if v := r.Context().Value(ctxKeyNamespaceOverride); v != nil {
|
||||||
|
if ns, ok := v.(string); ok && ns != "" {
|
||||||
|
return ns
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -3,6 +3,7 @@ package serverless
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
@ -14,6 +15,13 @@ import (
|
|||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// contextAwareHostServices is an internal interface for services that need to know about
|
||||||
|
// the current invocation context.
|
||||||
|
type contextAwareHostServices interface {
|
||||||
|
SetInvocationContext(invCtx *InvocationContext)
|
||||||
|
ClearContext()
|
||||||
|
}
|
||||||
|
|
||||||
// Ensure Engine implements FunctionExecutor interface.
|
// Ensure Engine implements FunctionExecutor interface.
|
||||||
var _ FunctionExecutor = (*Engine)(nil)
|
var _ FunctionExecutor = (*Engine)(nil)
|
||||||
|
|
||||||
@ -111,6 +119,11 @@ func NewEngine(cfg *Config, registry FunctionRegistry, hostServices HostServices
|
|||||||
opt(engine)
|
opt(engine)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Register host functions
|
||||||
|
if err := engine.registerHostModule(context.Background()); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to register host module: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
return engine, nil
|
return engine, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -303,6 +316,12 @@ func (e *Engine) getOrCompileModule(ctx context.Context, wasmCID string) (wazero
|
|||||||
|
|
||||||
// executeModule instantiates and runs a WASM module.
|
// executeModule instantiates and runs a WASM module.
|
||||||
func (e *Engine) executeModule(ctx context.Context, compiled wazero.CompiledModule, fn *Function, input []byte, invCtx *InvocationContext) ([]byte, error) {
|
func (e *Engine) executeModule(ctx context.Context, compiled wazero.CompiledModule, fn *Function, input []byte, invCtx *InvocationContext) ([]byte, error) {
|
||||||
|
// Set invocation context for host functions if the service supports it
|
||||||
|
if hf, ok := e.hostServices.(contextAwareHostServices); ok {
|
||||||
|
hf.SetInvocationContext(invCtx)
|
||||||
|
defer hf.ClearContext()
|
||||||
|
}
|
||||||
|
|
||||||
// Create buffers for stdin/stdout (WASI uses these for I/O)
|
// Create buffers for stdin/stdout (WASI uses these for I/O)
|
||||||
stdin := bytes.NewReader(input)
|
stdin := bytes.NewReader(input)
|
||||||
stdout := new(bytes.Buffer)
|
stdout := new(bytes.Buffer)
|
||||||
@ -455,3 +474,175 @@ func (e *Engine) logInvocation(ctx context.Context, fn *Function, invCtx *Invoca
|
|||||||
e.logger.Warn("Failed to log invocation", zap.Error(logErr))
|
e.logger.Warn("Failed to log invocation", zap.Error(logErr))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// registerHostModule registers the Orama host functions with the wazero runtime.
|
||||||
|
func (e *Engine) registerHostModule(ctx context.Context) error {
|
||||||
|
// Register under both "env" and "host" to support different import styles
|
||||||
|
// The user requested "env" in instructions but "host" in expected result.
|
||||||
|
for _, moduleName := range []string{"env", "host"} {
|
||||||
|
_, err := e.runtime.NewHostModuleBuilder(moduleName).
|
||||||
|
NewFunctionBuilder().WithFunc(e.hGetCallerWallet).Export("get_caller_wallet").
|
||||||
|
NewFunctionBuilder().WithFunc(e.hGetRequestID).Export("get_request_id").
|
||||||
|
NewFunctionBuilder().WithFunc(e.hGetEnv).Export("get_env").
|
||||||
|
NewFunctionBuilder().WithFunc(e.hGetSecret).Export("get_secret").
|
||||||
|
NewFunctionBuilder().WithFunc(e.hDBQuery).Export("db_query").
|
||||||
|
NewFunctionBuilder().WithFunc(e.hDBExecute).Export("db_execute").
|
||||||
|
NewFunctionBuilder().WithFunc(e.hCacheGet).Export("cache_get").
|
||||||
|
NewFunctionBuilder().WithFunc(e.hCacheSet).Export("cache_set").
|
||||||
|
NewFunctionBuilder().WithFunc(e.hLogInfo).Export("log_info").
|
||||||
|
NewFunctionBuilder().WithFunc(e.hLogError).Export("log_error").
|
||||||
|
Instantiate(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Engine) hGetCallerWallet(ctx context.Context, mod api.Module) uint64 {
|
||||||
|
wallet := e.hostServices.GetCallerWallet(ctx)
|
||||||
|
return e.writeToGuest(ctx, mod, []byte(wallet))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Engine) hGetRequestID(ctx context.Context, mod api.Module) uint64 {
|
||||||
|
rid := e.hostServices.GetRequestID(ctx)
|
||||||
|
return e.writeToGuest(ctx, mod, []byte(rid))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Engine) hGetEnv(ctx context.Context, mod api.Module, keyPtr, keyLen uint32) uint64 {
|
||||||
|
key, ok := mod.Memory().Read(keyPtr, keyLen)
|
||||||
|
if !ok {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
val, _ := e.hostServices.GetEnv(ctx, string(key))
|
||||||
|
return e.writeToGuest(ctx, mod, []byte(val))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Engine) hGetSecret(ctx context.Context, mod api.Module, namePtr, nameLen uint32) uint64 {
|
||||||
|
name, ok := mod.Memory().Read(namePtr, nameLen)
|
||||||
|
if !ok {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
val, err := e.hostServices.GetSecret(ctx, string(name))
|
||||||
|
if err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return e.writeToGuest(ctx, mod, []byte(val))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Engine) hDBQuery(ctx context.Context, mod api.Module, queryPtr, queryLen, argsPtr, argsLen uint32) uint64 {
|
||||||
|
query, ok := mod.Memory().Read(queryPtr, queryLen)
|
||||||
|
if !ok {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
var args []interface{}
|
||||||
|
if argsLen > 0 {
|
||||||
|
argsData, ok := mod.Memory().Read(argsPtr, argsLen)
|
||||||
|
if !ok {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(argsData, &args); err != nil {
|
||||||
|
e.logger.Error("failed to unmarshal db_query arguments", zap.Error(err))
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
results, err := e.hostServices.DBQuery(ctx, string(query), args)
|
||||||
|
if err != nil {
|
||||||
|
e.logger.Error("host function db_query failed", zap.Error(err), zap.String("query", string(query)))
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return e.writeToGuest(ctx, mod, results)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Engine) hDBExecute(ctx context.Context, mod api.Module, queryPtr, queryLen, argsPtr, argsLen uint32) uint32 {
|
||||||
|
query, ok := mod.Memory().Read(queryPtr, queryLen)
|
||||||
|
if !ok {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
var args []interface{}
|
||||||
|
if argsLen > 0 {
|
||||||
|
argsData, ok := mod.Memory().Read(argsPtr, argsLen)
|
||||||
|
if !ok {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(argsData, &args); err != nil {
|
||||||
|
e.logger.Error("failed to unmarshal db_execute arguments", zap.Error(err))
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
affected, err := e.hostServices.DBExecute(ctx, string(query), args)
|
||||||
|
if err != nil {
|
||||||
|
e.logger.Error("host function db_execute failed", zap.Error(err), zap.String("query", string(query)))
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return uint32(affected)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Engine) hCacheGet(ctx context.Context, mod api.Module, keyPtr, keyLen uint32) uint64 {
|
||||||
|
key, ok := mod.Memory().Read(keyPtr, keyLen)
|
||||||
|
if !ok {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
val, err := e.hostServices.CacheGet(ctx, string(key))
|
||||||
|
if err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return e.writeToGuest(ctx, mod, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Engine) hCacheSet(ctx context.Context, mod api.Module, keyPtr, keyLen, valPtr, valLen uint32, ttl int64) {
|
||||||
|
key, ok := mod.Memory().Read(keyPtr, keyLen)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
val, ok := mod.Memory().Read(valPtr, valLen)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_ = e.hostServices.CacheSet(ctx, string(key), val, ttl)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Engine) hLogInfo(ctx context.Context, mod api.Module, ptr, size uint32) {
|
||||||
|
msg, ok := mod.Memory().Read(ptr, size)
|
||||||
|
if ok {
|
||||||
|
e.hostServices.LogInfo(ctx, string(msg))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Engine) hLogError(ctx context.Context, mod api.Module, ptr, size uint32) {
|
||||||
|
msg, ok := mod.Memory().Read(ptr, size)
|
||||||
|
if ok {
|
||||||
|
e.hostServices.LogError(ctx, string(msg))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Engine) writeToGuest(ctx context.Context, mod api.Module, data []byte) uint64 {
|
||||||
|
if len(data) == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
// Try to find a non-conflicting allocator first, fallback to malloc
|
||||||
|
malloc := mod.ExportedFunction("orama_alloc")
|
||||||
|
if malloc == nil {
|
||||||
|
malloc = mod.ExportedFunction("malloc")
|
||||||
|
}
|
||||||
|
|
||||||
|
if malloc == nil {
|
||||||
|
e.logger.Warn("WASM module missing malloc/orama_alloc export, cannot return string/bytes to guest")
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
results, err := malloc.Call(ctx, uint64(len(data)))
|
||||||
|
if err != nil {
|
||||||
|
e.logger.Error("failed to call malloc in WASM module", zap.Error(err))
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
ptr := uint32(results[0])
|
||||||
|
if !mod.Memory().Write(ptr, data) {
|
||||||
|
e.logger.Error("failed to write to WASM memory")
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return (uint64(ptr) << 32) | uint64(len(data))
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user