docs(api): align OpenAPI with runtime contracts (#6409)

Document the cookie-authenticated WebSocket upgrade and its emitted envelopes without exporting pseudo-paths. Align REST response schemas, paged-client filters, and subscription HEAD operations with their runtime implementations, then regenerate frontend and docs artifacts.
This commit is contained in:
Gleb Gudkov
2026-09-04 16:23:41 +03:00
committed by GitHub
parent 3b5273b1d6
commit ed6bc1d898
20 changed files with 4609 additions and 535 deletions
+1 -1
View File
@@ -78,7 +78,7 @@ func documentedContractRoutes(t *testing.T) map[string]bool {
t.Fatalf("read endpoints.ts: %v", err)
}
text := string(source)
methodRe := regexp.MustCompile(`method:\s*'(GET|POST|PUT|DELETE|PATCH|WS)'`)
methodRe := regexp.MustCompile(`method:\s*'(GET|POST|PUT|DELETE|PATCH|HEAD|WS)'`)
pathRe := regexp.MustCompile(`path:\s*'([^']+)'`)
methods := methodRe.FindAllStringSubmatchIndex(text, -1)
if declared := strings.Count(text, "method: '"); len(methods) != declared {
+30 -30
View File
@@ -18,22 +18,22 @@ import (
// so the list payload stays compact even when the panel manages thousands
// of clients. Modals that need the full record still call /get/:email.
type ClientSlim struct {
Email string `json:"email"`
SubID string `json:"subId"`
Enable bool `json:"enable"`
TotalGB int64 `json:"totalGB"`
ExpiryTime int64 `json:"expiryTime"`
LimitIP int `json:"limitIp"`
LimitHwid int `json:"limitHwid"`
Reset int `json:"reset"`
ResetDay int `json:"resetDay"`
ResetMax int `json:"resetMax"`
Group string `json:"group,omitempty"`
Comment string `json:"comment,omitempty"`
InboundIds []int `json:"inboundIds"`
Email string `json:"email" example:"alice@example.com"`
SubID string `json:"subId" example:"abcd1234"`
Enable bool `json:"enable" example:"true"`
TotalGB int64 `json:"totalGB" example:"53687091200"`
ExpiryTime int64 `json:"expiryTime" example:"1735689600000"`
LimitIP int `json:"limitIp" example:"0"`
LimitHwid int `json:"limitHwid" example:"0"`
Reset int `json:"reset" example:"0"`
ResetDay int `json:"resetDay" example:"0"`
ResetMax int `json:"resetMax" example:"0"`
Group string `json:"group,omitempty" example:"staff"`
Comment string `json:"comment,omitempty" example:"Primary device"`
InboundIds []int `json:"inboundIds" example:"[3,5]"`
Traffic *xray.ClientTraffic `json:"traffic,omitempty"`
CreatedAt int64 `json:"createdAt"`
UpdatedAt int64 `json:"updatedAt"`
CreatedAt int64 `json:"createdAt" example:"1735000000000"`
UpdatedAt int64 `json:"updatedAt" example:"1735100000000"`
}
// ClientPageParams are the query params accepted by /panel/api/clients/list/paged.
@@ -70,12 +70,12 @@ type ClientPageParams struct {
// on the clients page stay stable as the user paginates/filters.
type ClientPageResponse struct {
Items []ClientSlim `json:"items"`
Total int `json:"total"`
Filtered int `json:"filtered"`
Page int `json:"page"`
PageSize int `json:"pageSize"`
Total int `json:"total" example:"2000"`
Filtered int `json:"filtered" example:"47"`
Page int `json:"page" example:"1"`
PageSize int `json:"pageSize" example:"25"`
Summary ClientsSummary `json:"summary"`
Groups []string `json:"groups"`
Groups []string `json:"groups" example:"[\"staff\",\"trial\"]"`
}
// ClientsSummary collects per-bucket counts plus the matching email lists so
@@ -83,16 +83,16 @@ type ClientPageResponse struct {
// popovers without shipping the full client array. The counters are exact;
// the lists stop at clientSummaryEmailCap entries and only back the popovers.
type ClientsSummary struct {
Total int `json:"total"`
Active int `json:"active"`
OnlineCount int `json:"onlineCount"`
DepletedCount int `json:"depletedCount"`
ExpiringCount int `json:"expiringCount"`
DeactiveCount int `json:"deactiveCount"`
Online []string `json:"online"`
Depleted []string `json:"depleted"`
Expiring []string `json:"expiring"`
Deactive []string `json:"deactive"`
Total int `json:"total" example:"2000"`
Active int `json:"active" example:"1850"`
OnlineCount int `json:"onlineCount" example:"1"`
DepletedCount int `json:"depletedCount" example:"0"`
ExpiringCount int `json:"expiringCount" example:"0"`
DeactiveCount int `json:"deactiveCount" example:"150"`
Online []string `json:"online" example:"[\"alice@example.com\"]"`
Depleted []string `json:"depleted" example:"[]"`
Expiring []string `json:"expiring" example:"[]"`
Deactive []string `json:"deactive" example:"[\"bob@example.com\"]"`
}
const (
+5 -5
View File
@@ -1148,11 +1148,11 @@ func (s *InboundService) CountClientTraffics() (int64, error) {
}
type InboundTrafficSummary struct {
Id int `json:"id"`
Up int64 `json:"up"`
Down int64 `json:"down"`
Total int64 `json:"total"`
Enable bool `json:"enable"`
Id int `json:"id" example:"1"`
Up int64 `json:"up" example:"1048576"`
Down int64 `json:"down" example:"2097152"`
Total int64 `json:"total" example:"10737418240"`
Enable bool `json:"enable" example:"true"`
}
func (s *InboundService) GetInboundsTrafficSummary() ([]InboundTrafficSummary, error) {
+32 -18
View File
@@ -351,13 +351,27 @@ func (s *ServerService) AggregateSystemMetric(metric string, bucketSeconds int,
}
type LogEntry struct {
DateTime time.Time
FromAddress string
ToAddress string
Inbound string
Outbound string
Email string
Event int
DateTime time.Time `json:"DateTime" example:"2025-01-01T12:00:00Z"`
FromAddress string `json:"FromAddress" example:"192.0.2.10:54321"`
ToAddress string `json:"ToAddress" example:"example.com:443"`
Inbound string `json:"Inbound" example:"inbound-443"`
Outbound string `json:"Outbound" example:"direct"`
Email string `json:"Email" example:"alice@example.com"`
Event int `json:"Event" example:"0"`
}
type NewUUIDResponse struct {
UUID string `json:"uuid" example:"550e8400-e29b-41d4-a716-446655440000"`
}
type MLDSA65Response struct {
Seed string `json:"seed" example:"mldsa65-seed"`
Verify string `json:"verify" example:"mldsa65-verify"`
}
type MLKEM768Response struct {
Seed string `json:"seed" example:"mlkem768-seed"`
Client string `json:"client" example:"mlkem768-client"`
}
func getPublicIP(url string) string {
@@ -2328,7 +2342,7 @@ func (s *ServerService) GetNewX25519Cert() (any, error) {
return keyPair, nil
}
func (s *ServerService) GetNewmldsa65() (any, error) {
func (s *ServerService) GetNewmldsa65() (*MLDSA65Response, error) {
// Run the command
cmd := exec.CommandContext(context.Background(), xray.GetBinaryPath(), "mldsa65")
var out bytes.Buffer
@@ -2343,9 +2357,9 @@ func (s *ServerService) GetNewmldsa65() (any, error) {
return nil, err
}
keyPair := map[string]any{
"seed": seed,
"verify": verify,
keyPair := &MLDSA65Response{
Seed: seed,
Verify: verify,
}
return keyPair, nil
@@ -2636,18 +2650,18 @@ func vlessEncAuthID(label string) string {
}
}
func (s *ServerService) GetNewUUID() (map[string]string, error) {
func (s *ServerService) GetNewUUID() (*NewUUIDResponse, error) {
newUUID, err := uuid.NewRandom()
if err != nil {
return nil, fmt.Errorf("failed to generate UUID: %w", err)
}
return map[string]string{
"uuid": newUUID.String(),
return &NewUUIDResponse{
UUID: newUUID.String(),
}, nil
}
func (s *ServerService) GetNewmlkem768() (any, error) {
func (s *ServerService) GetNewmlkem768() (*MLKEM768Response, error) {
// Run the command
cmd := exec.CommandContext(context.Background(), xray.GetBinaryPath(), "mlkem768")
var out bytes.Buffer
@@ -2662,9 +2676,9 @@ func (s *ServerService) GetNewmlkem768() (any, error) {
return nil, err
}
keyPair := map[string]any{
"seed": seed,
"client": client,
keyPair := &MLKEM768Response{
Seed: seed,
Client: client,
}
return keyPair, nil
+5 -5
View File
@@ -3,9 +3,9 @@ package xray
// Traffic represents network traffic statistics for Xray connections.
// It tracks upload and download bytes for inbound or outbound traffic.
type Traffic struct {
IsInbound bool
IsOutbound bool
Tag string
Up int64
Down int64
IsInbound bool `json:"IsInbound" example:"true"`
IsOutbound bool `json:"IsOutbound" example:"false"`
Tag string `json:"Tag" example:"inbound-443"`
Up int64 `json:"Up" example:"1048576"`
Down int64 `json:"Down" example:"2097152"`
}