Files
sub2api-cn-relay-manager/internal/host/sub2api/capability_probe.go
phamnazage-jpg 77b7f7f660
Some checks are pending
CI / Build & Test (push) Waiting to run
CI / Lint (push) Waiting to run
CI / Security Scan (push) Waiting to run
CI / Docker Build (push) Waiting to run
CI / Release (push) Blocked by required conditions
feat: harden runtime import and frontend verification workflows
2026-06-04 20:02:36 +08:00

93 lines
2.6 KiB
Go

package sub2api
import (
"bytes"
"context"
"net/http"
"strings"
)
func (c *Client) ProbeCapabilities(ctx context.Context) (HostCapabilities, error) {
groups, err := c.probeEndpoint(ctx, http.MethodGet, "/api/v1/admin/groups", nil)
if err != nil {
return HostCapabilities{}, err
}
channels, err := c.probeEndpoint(ctx, http.MethodGet, "/api/v1/admin/channels", nil)
if err != nil {
return HostCapabilities{}, err
}
plans, err := c.probeEndpoint(ctx, http.MethodGet, "/api/v1/admin/payment/plans", nil)
if err != nil {
return HostCapabilities{}, err
}
accounts, err := c.probeEndpoint(ctx, http.MethodGet, "/api/v1/admin/accounts", nil)
if err != nil {
return HostCapabilities{}, err
}
accountTest, err := c.probeEndpoint(ctx, http.MethodPost, "/api/v1/admin/accounts/__probe__/test", map[string]any{})
if err != nil {
return HostCapabilities{}, err
}
accountModels, err := c.probeEndpoint(ctx, http.MethodGet, "/api/v1/admin/accounts/__probe__/models", nil)
if err != nil {
return HostCapabilities{}, err
}
subscriptions, err := c.probeEndpoint(ctx, http.MethodGet, "/api/v1/admin/subscriptions/assign", nil)
if err != nil {
return HostCapabilities{}, err
}
return HostCapabilities{
Groups: groups,
Channels: channels,
Plans: plans,
Accounts: accounts,
AccountTest: accountTest,
AccountModels: accountModels,
Subscriptions: subscriptions,
}, nil
}
func (c *Client) probeEndpoint(ctx context.Context, method, path string, requestBody any) (bool, error) {
statusCode, _, body, err := c.perform(ctx, method, path, requestBody)
if err != nil {
return false, err
}
switch {
case statusCode >= http.StatusOK && statusCode < http.StatusMultipleChoices:
return true, nil
case statusCode == http.StatusUnauthorized || statusCode == http.StatusForbidden:
return false, newHTTPError(method, path, statusCode, body)
case statusCode == http.StatusNotFound || statusCode == http.StatusMethodNotAllowed:
return false, nil
case statusCode >= http.StatusBadRequest && statusCode < http.StatusInternalServerError:
return true, nil
default:
return false, newHTTPError(method, path, statusCode, body)
}
}
func looksLikeExistingEndpoint(headers http.Header, body []byte) bool {
contentType := strings.ToLower(headers.Get("Content-Type"))
trimmedBody := bytes.TrimSpace(body)
if strings.Contains(contentType, "application/json") || strings.Contains(contentType, "text/event-stream") {
return true
}
if len(trimmedBody) == 0 {
return false
}
if trimmedBody[0] == '{' || trimmedBody[0] == '[' {
return true
}
return false
}