mirror of
https://github.com/tale/headplane.git
synced 2026-07-26 07:48:14 +00:00
167 lines
4.4 KiB
Go
167 lines
4.4 KiB
Go
//go:build js && wasm
|
|
|
|
package hp_ipn
|
|
|
|
import (
|
|
"errors"
|
|
"log"
|
|
"syscall/js"
|
|
)
|
|
|
|
// Represents the options needed to initialize the TsWasmNet module.
|
|
type TsWasmNetOptions struct {
|
|
// Tailscale control URL, e.g., "https://controlplane.tailscale.com"
|
|
ControlURL string
|
|
|
|
// Pre-authentication key for the Tailnet
|
|
PreAuthKey string
|
|
|
|
// Optional hostname, autogenerated if not provided.
|
|
Hostname string
|
|
}
|
|
|
|
// Parses the provided JS object to validate and extract the TsWasmNetOptions.
|
|
func ParseTsWasmNetOptions(obj js.Value) (*TsWasmNetOptions, error) {
|
|
if obj.IsUndefined() || obj.IsNull() {
|
|
return nil, errors.New("TsWasmNetOptions cannot be undefined or null")
|
|
}
|
|
|
|
cUrl := safeString("ControlURL", obj)
|
|
preAuthKey := safeString("PreAuthKey", obj)
|
|
hostname := safeString("Hostname", obj)
|
|
|
|
if cUrl == "" || preAuthKey == "" || hostname == "" {
|
|
return nil, errors.New("missing required fields in TsWasmNetOptions")
|
|
}
|
|
|
|
return &TsWasmNetOptions{
|
|
ControlURL: cUrl,
|
|
PreAuthKey: preAuthKey,
|
|
Hostname: hostname,
|
|
}, nil
|
|
}
|
|
|
|
// Options passed from the JS side to pass data to xterm.js.
|
|
type SSHXtermConfig struct {
|
|
Timeout int // Timeout in seconds for the PTY connection.
|
|
Rows int // Number of rows in the PTY.
|
|
Cols int // Number of columns in the PTY.
|
|
OnStdout func(data string) // Fires when the PTY has output.
|
|
OnStderr func(error string) // Fires when the PTY has an error.
|
|
OnStdin js.Value // Passes a function to the JS side to provide input.
|
|
OnConnect func() // Fires when the PTY is opened.
|
|
OnDisconnect func() // Fires when the PTY is closed.
|
|
}
|
|
|
|
// Parses the provided JS object to validate and extract SSHXtermConfig.
|
|
func ParseSSHXtermConfig(obj js.Value) (*SSHXtermConfig, error) {
|
|
if obj.IsUndefined() || obj.IsNull() {
|
|
return nil, errors.New("SSHXtermConfig cannot be undefined or null")
|
|
}
|
|
|
|
timeout := safeInt("timeout", obj)
|
|
rows := safeInt("rows", obj)
|
|
cols := safeInt("cols", obj)
|
|
|
|
if rows <= 0 || cols <= 0 {
|
|
return nil, errors.New("`rows` and `cols` must be positive integers")
|
|
}
|
|
|
|
if timeout <= 0 {
|
|
timeout = 30 // Default timeout to 30 seconds if not specified
|
|
}
|
|
|
|
config := &SSHXtermConfig{
|
|
Timeout: timeout,
|
|
Rows: rows,
|
|
Cols: cols,
|
|
}
|
|
|
|
onStdout := obj.Get("onStdout")
|
|
if onStdout.IsUndefined() || onStdout.IsNull() || (onStdout.Type() != js.TypeFunction) {
|
|
return nil, errors.New("`onStdout` is required and must be a function")
|
|
}
|
|
|
|
config.OnStdout = func(data string) {
|
|
onStdout.Invoke(data)
|
|
}
|
|
|
|
onStderr := obj.Get("onStderr")
|
|
if onStderr.IsUndefined() || onStderr.IsNull() || (onStderr.Type() != js.TypeFunction) {
|
|
return nil, errors.New("`onStderr` is required and must be a function")
|
|
}
|
|
|
|
config.OnStderr = func(error string) {
|
|
onStderr.Invoke(error)
|
|
}
|
|
|
|
onStdin := obj.Get("onStdin")
|
|
if onStdin.IsUndefined() || onStdin.IsNull() || (onStdin.Type() != js.TypeFunction) {
|
|
return nil, errors.New("`onStdin` is required and must be a function")
|
|
}
|
|
|
|
config.OnStdin = onStdin
|
|
|
|
onConnect := obj.Get("onConnect")
|
|
if onConnect.IsUndefined() || onConnect.IsNull() || (onConnect.Type() != js.TypeFunction) {
|
|
return nil, errors.New("`onConnect` is required and must be a function")
|
|
}
|
|
|
|
config.OnConnect = func() {
|
|
onConnect.Invoke()
|
|
}
|
|
|
|
onDisconnect := obj.Get("onDisconnect")
|
|
if onDisconnect.IsUndefined() || onDisconnect.IsNull() || (onDisconnect.Type() != js.TypeFunction) {
|
|
return nil, errors.New("`onDisconnect` is required and must be a function")
|
|
}
|
|
|
|
config.OnDisconnect = func() {
|
|
onDisconnect.Invoke()
|
|
}
|
|
|
|
return config, nil
|
|
}
|
|
|
|
func (t *SSHXtermConfig) PassStdinHandler(fn func(string)) {
|
|
handler := js.FuncOf(func(this js.Value, args []js.Value) any {
|
|
if len(args) != 1 || args[0].Type() != js.TypeString {
|
|
return nil
|
|
}
|
|
|
|
fn(args[0].String())
|
|
return nil
|
|
})
|
|
|
|
log.Printf("Passing stdin handler to JS: %v", handler)
|
|
t.OnStdin.Invoke(handler)
|
|
}
|
|
|
|
// Retrieves a string value from a JS object safely.
|
|
func safeString(key string, obj js.Value) string {
|
|
if obj.IsUndefined() || obj.IsNull() {
|
|
return ""
|
|
}
|
|
|
|
val := obj.Get(key)
|
|
if val.IsUndefined() || val.IsNull() {
|
|
return ""
|
|
}
|
|
|
|
return val.String()
|
|
}
|
|
|
|
// Retrieves an integer value from a JS object safely.
|
|
func safeInt(key string, obj js.Value) int {
|
|
if obj.IsUndefined() || obj.IsNull() {
|
|
return 0
|
|
}
|
|
|
|
val := obj.Get(key)
|
|
if val.IsUndefined() || val.IsNull() {
|
|
return 0
|
|
}
|
|
|
|
return val.Int()
|
|
}
|