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)) }