refactor: consolidate authentication and DID handling logic
This commit is contained in:
1 parent
ce3d073abd
commit
f8d362352d
23 files changed
+144
-217
No files matched your search
@@ -2,8 +2,6 @@ package forms
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"errors"
|
||||
"github.com/onsonr/sonr/pkg/blocks/layout"
|
||||
)
|
||||
|
||||
@@ -17,30 +15,6 @@ func (d CreateProfileData) IsHumanLabel() string {
|
||||
return fmt.Sprintf("What is %d + %d?", d.FirstNumber, d.LastNumber)
|
||||
}
|
||||
|
||||
func ValidateCreateProfileForm(formData map[string][]string) error {
|
||||
// Validate ishuman slider
|
||||
ishumanValues := formData["is_human"]
|
||||
if len(ishumanValues) == 0 {
|
||||
return errors.New("human verification is required")
|
||||
}
|
||||
|
||||
ishumanSum, err := strconv.Atoi(ishumanValues[0])
|
||||
if err != nil {
|
||||
return errors.New("invalid human verification value")
|
||||
}
|
||||
|
||||
// Get the expected sum from the form data
|
||||
firstNum, _ := strconv.Atoi(formData["first_number"][0])
|
||||
lastNum, _ := strconv.Atoi(formData["last_number"][0])
|
||||
expectedSum := firstNum + lastNum
|
||||
|
||||
if ishumanSum != expectedSum {
|
||||
return errors.New("incorrect sum for human verification")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ProfileForm is a standard form styled like a card
|
||||
templ CreateProfile(action string, method string, data CreateProfileData) {
|
||||
<form action={ templ.SafeURL(action) } method={ method }>
|
||||
@@ -61,26 +35,7 @@ templ CreateProfile(action string, method string, data CreateProfileData) {
|
||||
</div>
|
||||
</sl-input>
|
||||
@layout.Spacer()
|
||||
<sl-range
|
||||
name="is_human"
|
||||
label={ data.IsHumanLabel() }
|
||||
help-text="Prove you are a human."
|
||||
min="0"
|
||||
max={ fmt.Sprint(data.FirstNumber + data.LastNumber) }
|
||||
step="1"
|
||||
onchange="validateSum(this)"
|
||||
></sl-range>
|
||||
<script>
|
||||
function validateSum(slider) {
|
||||
const expectedSum = { fmt.Sprint(data.FirstNumber + data.LastNumber) };
|
||||
const value = parseInt(slider.value);
|
||||
if (value !== expectedSum) {
|
||||
slider.setCustomValidity("Please select the correct sum");
|
||||
} else {
|
||||
slider.setCustomValidity("");
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<sl-range name="is_human" label={ data.IsHumanLabel() } help-text="Prove you are a human." min="0" max="9" step="1"></sl-range>
|
||||
<div slot="footer">
|
||||
<sl-button href="/" outline>
|
||||
<sl-icon slot="prefix" name="arrow-left" library="sonr"></sl-icon>
|
||||
@@ -101,6 +56,3 @@ templ CreateProfile(action string, method string, data CreateProfileData) {
|
||||
</sl-card>
|
||||
</form>
|
||||
}
|
||||
|
||||
templ isHumanSlider(targetSum string) {
|
||||
}
|
||||
@@ -126,29 +126,4 @@ func CreateProfile(action string, method string, data CreateProfileData) templ.C
|
||||
})
|
||||
}
|
||||
|
||||
func isHumanSlider(targetSum string) templ.Component {
|
||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||
return templ_7745c5c3_CtxErr
|
||||
}
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var6 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var6 == nil {
|
||||
templ_7745c5c3_Var6 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
return templ_7745c5c3_Err
|
||||
})
|
||||
}
|
||||
|
||||
var _ = templruntime.GeneratedTemplate
|
||||
@@ -0,0 +1,46 @@
|
||||
package forms
|
||||
|
||||
import "github.com/onsonr/sonr/pkg/blocks/layout"
|
||||
|
||||
type SetupWalletData struct {
|
||||
TurnstileSiteKey string
|
||||
FirstNumber int
|
||||
LastNumber int
|
||||
}
|
||||
|
||||
// SetupWallet is a standard form styled like a card
|
||||
templ SetupWallet(action string, method string, data CreateProfileData) {
|
||||
<form action={ templ.SafeURL(action) } method={ method }>
|
||||
<sl-card class="card-form gap-4 max-w-lg">
|
||||
<div slot="header">
|
||||
<div class="w-full py-1">
|
||||
<sl-progress-bar value="50"></sl-progress-bar>
|
||||
</div>
|
||||
</div>
|
||||
@layout.Spacer()
|
||||
<sl-input name="handle" placeholder="thoughtdiff" type="text" label="Handle" minlength="4" maxlength="12" required>
|
||||
<div slot="prefix">
|
||||
<sl-icon name="at-sign" library="sonr"></sl-icon>
|
||||
</div>
|
||||
</sl-input>
|
||||
@layout.Spacer()
|
||||
<div slot="footer">
|
||||
<sl-button href="/" outline>
|
||||
<sl-icon slot="prefix" name="arrow-left" library="sonr"></sl-icon>
|
||||
Skip for now
|
||||
</sl-button>
|
||||
<sl-button type="submit">
|
||||
Done
|
||||
<sl-icon slot="suffix" name="arrow-right" library="sonr"></sl-icon>
|
||||
</sl-button>
|
||||
</div>
|
||||
<style>
|
||||
.card-form [slot='footer'] {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
</sl-card>
|
||||
</form>
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.2.793
|
||||
package forms
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
import "github.com/a-h/templ"
|
||||
import templruntime "github.com/a-h/templ/runtime"
|
||||
|
||||
import "github.com/onsonr/sonr/pkg/blocks/layout"
|
||||
|
||||
type SetupWalletData struct {
|
||||
TurnstileSiteKey string
|
||||
FirstNumber int
|
||||
LastNumber int
|
||||
}
|
||||
|
||||
// SetupWallet is a standard form styled like a card
|
||||
func SetupWallet(action string, method string, data CreateProfileData) templ.Component {
|
||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||
return templ_7745c5c3_CtxErr
|
||||
}
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var1 == nil {
|
||||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<form action=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var2 templ.SafeURL = templ.SafeURL(action)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var2)))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" method=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var3 string
|
||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(method)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `pkg/blocks/forms/setup_wallet.templ`, Line: 13, Col: 55}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"><sl-card class=\"card-form gap-4 max-w-lg\"><div slot=\"header\"><div class=\"w-full py-1\"><sl-progress-bar value=\"50\"></sl-progress-bar></div></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = layout.Spacer().Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<sl-input name=\"handle\" placeholder=\"thoughtdiff\" type=\"text\" label=\"Handle\" minlength=\"4\" maxlength=\"12\" required><div slot=\"prefix\"><sl-icon name=\"at-sign\" library=\"sonr\"></sl-icon></div></sl-input>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = layout.Spacer().Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div slot=\"footer\"><sl-button href=\"/\" outline><sl-icon slot=\"prefix\" name=\"arrow-left\" library=\"sonr\"></sl-icon> Skip for now</sl-button> <sl-button type=\"submit\">Done <sl-icon slot=\"suffix\" name=\"arrow-right\" library=\"sonr\"></sl-icon></sl-button></div><style>\n \t\t.card-form [slot='footer'] {\n \t\tdisplay: flex;\n \t\tjustify-content: space-between;\n \t\talign-items: center;\n \t\t}\n\t\t</style></sl-card></form>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
})
|
||||
}
|
||||
|
||||
var _ = templruntime.GeneratedTemplate
|
||||
@@ -1,4 +1,4 @@
|
||||
package controller
|
||||
package dids
|
||||
|
||||
import (
|
||||
"github.com/labstack/echo/v4"
|
||||
@@ -1,7 +1,7 @@
|
||||
//go:build js && wasm
|
||||
// +build js,wasm
|
||||
|
||||
package controller
|
||||
package dids
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -1,47 +0,0 @@
|
||||
package passkeys
|
||||
|
||||
import (
|
||||
"github.com/go-webauthn/webauthn/protocol"
|
||||
"github.com/go-webauthn/webauthn/protocol/webauthncose"
|
||||
)
|
||||
|
||||
func defaultPrimaryAttestationFormats() []protocol.AttestationFormat {
|
||||
return []protocol.AttestationFormat{
|
||||
protocol.AttestationFormatApple,
|
||||
protocol.AttestationFormatAndroidKey,
|
||||
protocol.AttestationFormatAndroidSafetyNet,
|
||||
protocol.AttestationFormatFIDOUniversalSecondFactor,
|
||||
}
|
||||
}
|
||||
|
||||
func defaultSecondaryAttestationFormats() []protocol.AttestationFormat {
|
||||
return []protocol.AttestationFormat{
|
||||
protocol.AttestationFormatPacked,
|
||||
protocol.AttestationFormatTPM,
|
||||
}
|
||||
}
|
||||
|
||||
func defaultAuthenticatorSelection() protocol.AuthenticatorSelection {
|
||||
return protocol.AuthenticatorSelection{
|
||||
AuthenticatorAttachment: protocol.Platform,
|
||||
ResidentKey: protocol.ResidentKeyRequirementRequired,
|
||||
UserVerification: protocol.VerificationRequired,
|
||||
}
|
||||
}
|
||||
|
||||
func buildCredentialParameters() []protocol.CredentialParameter {
|
||||
return []protocol.CredentialParameter{
|
||||
{
|
||||
Type: "public-key",
|
||||
Algorithm: webauthncose.AlgES256,
|
||||
},
|
||||
{
|
||||
Type: "public-key",
|
||||
Algorithm: webauthncose.AlgES256K,
|
||||
},
|
||||
{
|
||||
Type: "public-key",
|
||||
Algorithm: webauthncose.AlgEdDSA,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
package passkeys
|
||||
|
||||
import (
|
||||
"github.com/go-webauthn/webauthn/protocol"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/onsonr/sonr/crypto/mpc"
|
||||
"github.com/onsonr/sonr/pkg/common"
|
||||
)
|
||||
|
||||
func Create(c echo.Context, handle string, ks mpc.Keyset) protocol.PublicKeyCredentialCreationOptions {
|
||||
origin := c.Request().Host
|
||||
svcName := c.Request().Host
|
||||
addr := ks.Address()
|
||||
return buildRegisterOptions(addr, handle, ks, origin, svcName)
|
||||
}
|
||||
|
||||
func buildRegisterOptions(addr string, handle string, ks mpc.Keyset, origin string, svcName string) protocol.PublicKeyCredentialCreationOptions {
|
||||
return protocol.PublicKeyCredentialCreationOptions{
|
||||
Attestation: protocol.PreferDirectAttestation,
|
||||
AttestationFormats: defaultPrimaryAttestationFormats(),
|
||||
AuthenticatorSelection: defaultAuthenticatorSelection(),
|
||||
RelyingParty: buildServiceEntity(origin, svcName),
|
||||
Extensions: buildExtensions(ks),
|
||||
Parameters: buildCredentialParameters(),
|
||||
Timeout: 10000,
|
||||
User: buildUserEntity(addr, handle),
|
||||
}
|
||||
}
|
||||
|
||||
func buildExtensions(ks mpc.Keyset) protocol.AuthenticationExtensions {
|
||||
return protocol.AuthenticationExtensions{
|
||||
"largeBlob": common.LargeBlob{
|
||||
Support: "required",
|
||||
Write: ks.UserJSON(),
|
||||
},
|
||||
"payment": common.Payment{
|
||||
IsPayment: true,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func buildServiceEntity(name string, host string) protocol.RelyingPartyEntity {
|
||||
return protocol.RelyingPartyEntity{
|
||||
CredentialEntity: protocol.CredentialEntity{
|
||||
Name: name,
|
||||
},
|
||||
ID: host,
|
||||
}
|
||||
}
|
||||
|
||||
func buildUserEntity(userAddress string, userHandle string) protocol.UserEntity {
|
||||
return protocol.UserEntity{
|
||||
ID: userAddress,
|
||||
DisplayName: userHandle,
|
||||
CredentialEntity: protocol.CredentialEntity{
|
||||
Name: userAddress,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
package passkeys
|
||||
|
||||
//
|
||||
// import (
|
||||
// "github.com/go-webauthn/webauthn/protocol"
|
||||
// "github.com/labstack/echo/v4"
|
||||
// "github.com/onsonr/sonr/crypto/mpc"
|
||||
// )
|
||||
//
|
||||
// func Link(c echo.Context, handle string, ks mpc.Keyset) protocol.PublicKeyCredentialCreationOptions {
|
||||
// origin := c.Request().Host
|
||||
// svcName := c.Request().Host
|
||||
// addr := ks.Address()
|
||||
// return c.String(200, origin+" "+svcName+" "+addr+" "+handle)
|
||||
// }
|
||||
@@ -1 +0,0 @@
|
||||
package passkeys
|
||||
@@ -3,8 +3,8 @@ package handlers
|
||||
import (
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/onsonr/sonr/pkg/common/response"
|
||||
"github.com/onsonr/sonr/pkg/gateway/internal/pages/index"
|
||||
"github.com/onsonr/sonr/pkg/gateway/internal/session"
|
||||
"github.com/onsonr/sonr/pkg/gateway/pages/index"
|
||||
)
|
||||
|
||||
func HandleIndex(c echo.Context) error {
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
"github.com/onsonr/sonr/pkg/blocks/forms"
|
||||
"github.com/onsonr/sonr/pkg/common/response"
|
||||
"github.com/onsonr/sonr/pkg/gateway/internal/database"
|
||||
"github.com/onsonr/sonr/pkg/gateway/internal/pages/register"
|
||||
"github.com/onsonr/sonr/pkg/gateway/pages/register"
|
||||
)
|
||||
|
||||
func HandleRegisterView(c echo.Context) error {
|
||||
@@ -24,16 +24,6 @@ func HandleRegisterView(c echo.Context) error {
|
||||
}
|
||||
|
||||
func HandleRegisterStart(c echo.Context) error {
|
||||
// Validate the form submission
|
||||
formData := make(map[string][]string)
|
||||
for key, values := range c.Request().Form {
|
||||
formData[key] = values
|
||||
}
|
||||
|
||||
if err := forms.ValidateCreateProfileForm(formData); err != nil {
|
||||
return echo.NewHTTPError(http.StatusBadRequest, err.Error())
|
||||
}
|
||||
|
||||
challenge, _ := protocol.CreateChallenge()
|
||||
handle := c.FormValue("handle")
|
||||
firstName := c.FormValue("first_name")
|
||||
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,211 @@
|
||||
//go:build js && wasm
|
||||
// +build js,wasm
|
||||
|
||||
package vault
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall/js"
|
||||
)
|
||||
|
||||
var (
|
||||
// Global buffer pool to reduce allocations
|
||||
bufferPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return new(bytes.Buffer)
|
||||
},
|
||||
}
|
||||
|
||||
// Cached JS globals
|
||||
jsGlobal = js.Global()
|
||||
jsUint8Array = jsGlobal.Get("Uint8Array")
|
||||
jsResponse = jsGlobal.Get("Response")
|
||||
jsPromise = jsGlobal.Get("Promise")
|
||||
jsWasmHTTP = jsGlobal.Get("wasmhttp")
|
||||
)
|
||||
|
||||
// ServeFetch serves HTTP requests with optimized handler management
|
||||
func ServeFetch(handler http.Handler) func() {
|
||||
h := handler
|
||||
if h == nil {
|
||||
h = http.DefaultServeMux
|
||||
}
|
||||
|
||||
// Optimize prefix handling
|
||||
prefix := strings.TrimRight(jsWasmHTTP.Get("path").String(), "/")
|
||||
if prefix != "" {
|
||||
mux := http.NewServeMux()
|
||||
mux.Handle(prefix+"/", http.StripPrefix(prefix, h))
|
||||
h = mux
|
||||
}
|
||||
|
||||
// Create request handler function
|
||||
cb := js.FuncOf(func(_ js.Value, args []js.Value) interface{} {
|
||||
promise, resolve, reject := newPromiseOptimized()
|
||||
|
||||
go handleRequest(h, args[1], resolve, reject)
|
||||
|
||||
return promise
|
||||
})
|
||||
|
||||
jsWasmHTTP.Call("setHandler", cb)
|
||||
return cb.Release
|
||||
}
|
||||
|
||||
// handleRequest processes the request with panic recovery
|
||||
func handleRequest(h http.Handler, jsReq js.Value, resolve, reject func(interface{})) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
var errMsg string
|
||||
if err, ok := r.(error); ok {
|
||||
errMsg = fmt.Sprintf("wasmhttp: panic: %+v", err)
|
||||
} else {
|
||||
errMsg = fmt.Sprintf("wasmhttp: panic: %v", r)
|
||||
}
|
||||
reject(errMsg)
|
||||
}
|
||||
}()
|
||||
|
||||
recorder := newResponseRecorder()
|
||||
h.ServeHTTP(recorder, buildRequest(jsReq))
|
||||
resolve(recorder.jsResponse())
|
||||
}
|
||||
|
||||
// buildRequest creates an http.Request from JS Request
|
||||
func buildRequest(jsReq js.Value) *http.Request {
|
||||
// Get request body
|
||||
arrayBuffer, err := awaitPromiseOptimized(jsReq.Call("arrayBuffer"))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Create body buffer
|
||||
jsBody := jsUint8Array.New(arrayBuffer)
|
||||
bodyLen := jsBody.Get("length").Int()
|
||||
body := make([]byte, bodyLen)
|
||||
js.CopyBytesToGo(body, jsBody)
|
||||
|
||||
// Create request
|
||||
req := httptest.NewRequest(
|
||||
jsReq.Get("method").String(),
|
||||
jsReq.Get("url").String(),
|
||||
bytes.NewReader(body),
|
||||
)
|
||||
|
||||
// Set headers efficiently
|
||||
headers := jsReq.Get("headers")
|
||||
headersIt := headers.Call("entries")
|
||||
for {
|
||||
entry := headersIt.Call("next")
|
||||
if entry.Get("done").Bool() {
|
||||
break
|
||||
}
|
||||
pair := entry.Get("value")
|
||||
req.Header.Set(pair.Index(0).String(), pair.Index(1).String())
|
||||
}
|
||||
|
||||
return req
|
||||
}
|
||||
|
||||
// ResponseRecorder with optimized buffer handling
|
||||
type ResponseRecorder struct {
|
||||
*httptest.ResponseRecorder
|
||||
buffer *bytes.Buffer
|
||||
}
|
||||
|
||||
func newResponseRecorder() *ResponseRecorder {
|
||||
return &ResponseRecorder{
|
||||
ResponseRecorder: httptest.NewRecorder(),
|
||||
buffer: bufferPool.Get().(*bytes.Buffer),
|
||||
}
|
||||
}
|
||||
|
||||
// jsResponse creates a JS Response with optimized memory usage
|
||||
func (rr *ResponseRecorder) jsResponse() js.Value {
|
||||
defer func() {
|
||||
rr.buffer.Reset()
|
||||
bufferPool.Put(rr.buffer)
|
||||
}()
|
||||
|
||||
res := rr.Result()
|
||||
defer res.Body.Close()
|
||||
|
||||
// Prepare response body
|
||||
body := js.Undefined()
|
||||
if res.ContentLength != 0 {
|
||||
if _, err := io.Copy(rr.buffer, res.Body); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
bodyBytes := rr.buffer.Bytes()
|
||||
body = jsUint8Array.New(len(bodyBytes))
|
||||
js.CopyBytesToJS(body, bodyBytes)
|
||||
}
|
||||
|
||||
// Prepare response init object
|
||||
init := make(map[string]interface{}, 3)
|
||||
if res.StatusCode != 0 {
|
||||
init["status"] = res.StatusCode
|
||||
}
|
||||
|
||||
if len(res.Header) > 0 {
|
||||
headers := make(map[string]interface{}, len(res.Header))
|
||||
for k, v := range res.Header {
|
||||
if len(v) > 0 {
|
||||
headers[k] = v[0]
|
||||
}
|
||||
}
|
||||
init["headers"] = headers
|
||||
}
|
||||
|
||||
return jsResponse.New(body, init)
|
||||
}
|
||||
|
||||
// newPromiseOptimized creates a new JavaScript Promise with optimized callback handling
|
||||
func newPromiseOptimized() (js.Value, func(interface{}), func(interface{})) {
|
||||
var (
|
||||
resolve func(interface{})
|
||||
reject func(interface{})
|
||||
promiseFunc = js.FuncOf(func(_ js.Value, args []js.Value) interface{} {
|
||||
resolve = func(v interface{}) { args[0].Invoke(v) }
|
||||
reject = func(v interface{}) { args[1].Invoke(v) }
|
||||
return js.Undefined()
|
||||
})
|
||||
)
|
||||
defer promiseFunc.Release()
|
||||
|
||||
return jsPromise.New(promiseFunc), resolve, reject
|
||||
}
|
||||
|
||||
// awaitPromiseOptimized waits for Promise resolution with optimized channel handling
|
||||
func awaitPromiseOptimized(promise js.Value) (js.Value, error) {
|
||||
done := make(chan struct{})
|
||||
var (
|
||||
result js.Value
|
||||
err error
|
||||
)
|
||||
|
||||
thenFunc := js.FuncOf(func(_ js.Value, args []js.Value) interface{} {
|
||||
result = args[0]
|
||||
close(done)
|
||||
return nil
|
||||
})
|
||||
defer thenFunc.Release()
|
||||
|
||||
catchFunc := js.FuncOf(func(_ js.Value, args []js.Value) interface{} {
|
||||
err = js.Error{Value: args[0]}
|
||||
close(done)
|
||||
return nil
|
||||
})
|
||||
defer catchFunc.Release()
|
||||
|
||||
promise.Call("then", thenFunc).Call("catch", catchFunc)
|
||||
<-done
|
||||
|
||||
return result, err
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
//go:build js && wasm
|
||||
// +build js,wasm
|
||||
|
||||
package vault
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
func WasmContextMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
// Extract WASM context from headers
|
||||
if wasmCtx := c.Request().Header.Get("X-Wasm-Context"); wasmCtx != "" {
|
||||
if ctx, err := DecodeWasmContext(wasmCtx); err == nil {
|
||||
c.Set("wasm_context", ctx)
|
||||
}
|
||||
}
|
||||
return next(c)
|
||||
}
|
||||
}
|
||||
|
||||
// decodeWasmContext decodes the WASM context from a base64 encoded string
|
||||
func DecodeWasmContext(ctx string) (map[string]any, error) {
|
||||
decoded, err := base64.StdEncoding.DecodeString(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var ctxData map[string]any
|
||||
err = json.Unmarshal(decoded, &ctxData)
|
||||
return ctxData, err
|
||||
}
|
||||
Reference in new issue
Block a user