calcomblock/block_resolver.go
Alex Dunmow 7c7bceb9d7 fix!: absolute routes + live captcha requirement — v2.0.3
Two wasm-era regressions fixed:

Routes: the wasm host forwards the FULL request path with no prefix
strip, but every route was registered relative — the plugin's entire
HTTP surface (slots/book/cancel/date-grid/admin) 404'd at runtime on
every instance since 2.0.0. All routes now register under
/api/plugins/calcomblock (testplugin's httpBase convention), pinned by
a route-matching test.

Captcha requirement: blockConfig was hardcoded nil (no capability
exposed published block content), so bookingRequiresCaptcha always
reported false — the widget never rendered and the gate never ran.
publishedBlockResolver now answers it via pluginsdk v0.2.4's
content.published_block_configs: any currently-published calcom:booking
block matching the posted username+eventTypeSlug with
captchaEnabled=true demands the host-stamped X-Bn-Verified-Captcha
header; an empty config username applies to any posted username.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 23:47:59 +08:00

65 lines
1.8 KiB
Go

package main
import (
"context"
"strings"
)
// httpBase is the prefix the host mounts the plugin HTTP handler at; the wasm
// host forwards full paths, so every route registers under it.
const httpBase = "/api/plugins/calcomblock"
// publishedConfigsSource is the slice of content.Content the resolver needs
// (satisfied by deps.Content).
type publishedConfigsSource interface {
PublishedBlockConfigs(ctx context.Context, blockKey string) ([]map[string]any, error)
}
// publishedBlockResolver answers CalcomBookingRequiresCaptcha from published
// block content served by the host (content.published_block_configs) — the
// server-authoritative source, never the POST body/blockId. A config with an
// empty username applies to any posted username (it falls back to the
// plugin's default account), erring toward requiring captcha.
type publishedBlockResolver struct {
source publishedConfigsSource
}
func (r publishedBlockResolver) CalcomBookingRequiresCaptcha(ctx context.Context, username, eventTypeSlug string) (bool, error) {
configs, err := r.source.PublishedBlockConfigs(ctx, "calcom:booking")
if err != nil {
return false, err
}
for _, cfg := range configs {
if !configBool(cfg["captchaEnabled"]) {
continue
}
if !configFieldMatches(cfg["eventTypeSlug"], eventTypeSlug, false) {
continue
}
if configFieldMatches(cfg["username"], username, true) {
return true, nil
}
}
return false, nil
}
func configBool(v any) bool {
switch b := v.(type) {
case bool:
return b
case string:
return strings.EqualFold(strings.TrimSpace(b), "true")
default:
return false
}
}
func configFieldMatches(cfgValue any, posted string, emptyMatchesAny bool) bool {
s, _ := cfgValue.(string)
s = strings.TrimSpace(s)
if s == "" {
return emptyMatchesAny
}
return strings.EqualFold(s, strings.TrimSpace(posted))
}