mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-09-16 15:17:14 +00:00
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:
@@ -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 {
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"`
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user