init
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
// Command wasm is the browser entry point for sing-vis. Built with
|
||||
// `GOOS=js GOARCH=wasm`, it exposes a single JS-callable function,
|
||||
// `singvisAnalyze`, that runs the exact same internal/engine analysis the old
|
||||
// HTTP server did — only now it runs entirely in the browser (in a Web Worker).
|
||||
//
|
||||
// Under js/wasm, net/http transparently uses the browser Fetch API, so the DoH
|
||||
// resolver and remote rule-set fetching keep working (subject to CORS).
|
||||
//
|
||||
//go:build js && wasm
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"syscall/js"
|
||||
|
||||
"sing-vis/internal/dnsx"
|
||||
"sing-vis/internal/engine"
|
||||
)
|
||||
|
||||
// analyzeRequest is the single JSON argument passed from JS. It mirrors the old
|
||||
// POST /api/analyze body.
|
||||
type analyzeRequest struct {
|
||||
Config string `json:"config"`
|
||||
Inputs []string `json:"inputs"`
|
||||
RuleSetFiles map[string]engine.RuleSetFile `json:"ruleSetFiles"`
|
||||
DoHServer string `json:"dohServer"`
|
||||
Network string `json:"network"` // optional: tcp/udp assumption
|
||||
AssumeResolved *bool `json:"assumeResolved"` // default true
|
||||
}
|
||||
|
||||
const defaultDoHServer = "https://1.1.1.1/dns-query"
|
||||
|
||||
func main() {
|
||||
js.Global().Set("singvisAnalyze", js.FuncOf(analyze))
|
||||
// Signal readiness to the host (worker) so it need not poll.
|
||||
if ready := js.Global().Get("singvisReady"); ready.Type() == js.TypeFunction {
|
||||
ready.Invoke()
|
||||
}
|
||||
select {} // keep the Go runtime alive to service calls
|
||||
}
|
||||
|
||||
// analyze is the JS-facing entry point. It takes one JSON string argument and
|
||||
// returns a Promise<string> that resolves to the marshaled engine.Result (or
|
||||
// rejects with an Error). The work runs in a goroutine so the JS event loop is
|
||||
// never blocked while DoH / rule-set fetches are in flight.
|
||||
func analyze(_ js.Value, args []js.Value) any {
|
||||
var input string
|
||||
if len(args) > 0 && args[0].Type() == js.TypeString {
|
||||
input = args[0].String()
|
||||
}
|
||||
handler := js.FuncOf(func(_ js.Value, promiseArgs []js.Value) any {
|
||||
resolve := promiseArgs[0]
|
||||
reject := promiseArgs[1]
|
||||
go func() {
|
||||
out, err := runAnalyze(input)
|
||||
if err != nil {
|
||||
reject.Invoke(jsError(err))
|
||||
return
|
||||
}
|
||||
resolve.Invoke(out)
|
||||
}()
|
||||
return nil
|
||||
})
|
||||
return js.Global().Get("Promise").New(handler)
|
||||
}
|
||||
|
||||
func runAnalyze(input string) (string, error) {
|
||||
var req analyzeRequest
|
||||
if err := json.Unmarshal([]byte(input), &req); err != nil {
|
||||
return "", fmt.Errorf("invalid request: %w", err)
|
||||
}
|
||||
doh := req.DoHServer
|
||||
if doh == "" {
|
||||
doh = defaultDoHServer
|
||||
}
|
||||
assumeResolved := true
|
||||
if req.AssumeResolved != nil {
|
||||
assumeResolved = *req.AssumeResolved
|
||||
}
|
||||
result, err := engine.Analyze(context.Background(), engine.Request{
|
||||
Config: req.Config,
|
||||
Inputs: req.Inputs,
|
||||
RuleSetFiles: req.RuleSetFiles,
|
||||
Network: req.Network,
|
||||
AssumeResolved: assumeResolved,
|
||||
Resolver: dnsx.NewDoHResolver(doh),
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
out, err := json.Marshal(result)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(out), nil
|
||||
}
|
||||
|
||||
func jsError(err error) js.Value {
|
||||
return js.Global().Get("Error").New(err.Error())
|
||||
}
|
||||
Reference in New Issue
Block a user