mirror of
https://github.com/yusing/godoxy.git
synced 2026-04-23 08:48:32 +02:00
refactor(api): restructured API for type safety, maintainability and docs generation
- These changes makes the API incombatible with previous versions - Added new types for error handling, success responses, and health checks. - Updated health check logic to utilize the new types for better clarity and structure. - Refactored existing handlers to improve response consistency and error handling. - Updated Makefile to include a new target for generating API types from Swagger. - Updated "new agent" API to respond an encrypted cert pair
This commit is contained in:
@@ -2,84 +2,80 @@ package routes
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/yusing/go-proxy/internal/homepage"
|
||||
"github.com/yusing/go-proxy/internal/watcher/health"
|
||||
"github.com/yusing/go-proxy/internal/types"
|
||||
)
|
||||
|
||||
func getHealthInfo(r Route) map[string]string {
|
||||
mon := r.HealthMonitor()
|
||||
if mon == nil {
|
||||
return map[string]string{
|
||||
"status": "unknown",
|
||||
"uptime": "n/a",
|
||||
"latency": "n/a",
|
||||
"detail": "n/a",
|
||||
}
|
||||
}
|
||||
return map[string]string{
|
||||
"status": mon.Status().String(),
|
||||
"uptime": mon.Uptime().Round(time.Second).String(),
|
||||
"latency": mon.Latency().Round(time.Microsecond).String(),
|
||||
"detail": mon.Detail(),
|
||||
}
|
||||
type HealthInfo struct {
|
||||
Status types.HealthStatus `json:"status" swaggertype:"string" enums:"healthy,unhealthy,napping,starting,error,unknown"`
|
||||
Uptime time.Duration `json:"uptime" swaggertype:"number"` // uptime in milliseconds
|
||||
Latency time.Duration `json:"latency" swaggertype:"number"` // latency in microseconds
|
||||
Detail string `json:"detail"`
|
||||
}
|
||||
|
||||
type HealthInfoRaw struct {
|
||||
Status health.Status `json:"status"`
|
||||
Latency time.Duration `json:"latency"`
|
||||
}
|
||||
|
||||
func (info *HealthInfoRaw) MarshalJSON() ([]byte, error) {
|
||||
func (info *HealthInfo) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(map[string]any{
|
||||
"status": info.Status.String(),
|
||||
"latency": info.Latency.Milliseconds(),
|
||||
"latency": info.Latency.Microseconds(),
|
||||
"uptime": info.Uptime.Milliseconds(),
|
||||
"detail": info.Detail,
|
||||
})
|
||||
}
|
||||
|
||||
func (info *HealthInfoRaw) UnmarshalJSON(data []byte) error {
|
||||
var v map[string]any
|
||||
func (info *HealthInfo) UnmarshalJSON(data []byte) error {
|
||||
var v struct {
|
||||
Status string `json:"status"`
|
||||
Latency int64 `json:"latency"`
|
||||
Uptime int64 `json:"uptime"`
|
||||
Detail string `json:"detail"`
|
||||
}
|
||||
if err := json.Unmarshal(data, &v); err != nil {
|
||||
return err
|
||||
}
|
||||
if status, ok := v["status"].(string); ok {
|
||||
info.Status = health.NewStatus(status)
|
||||
|
||||
// overflow check
|
||||
if math.MaxInt64/time.Microsecond < time.Duration(v.Latency) {
|
||||
return fmt.Errorf("latency overflow: %d", v.Latency)
|
||||
}
|
||||
if latency, ok := v["latency"].(float64); ok {
|
||||
info.Latency = time.Duration(latency)
|
||||
if math.MaxInt64/time.Millisecond < time.Duration(v.Uptime) {
|
||||
return fmt.Errorf("uptime overflow: %d", v.Uptime)
|
||||
}
|
||||
|
||||
info.Status = types.NewHealthStatusFromString(v.Status)
|
||||
info.Latency = time.Duration(v.Latency) * time.Microsecond
|
||||
info.Uptime = time.Duration(v.Uptime) * time.Millisecond
|
||||
info.Detail = v.Detail
|
||||
return nil
|
||||
}
|
||||
|
||||
func getHealthInfoRaw(r Route) *HealthInfoRaw {
|
||||
mon := r.HealthMonitor()
|
||||
if mon == nil {
|
||||
return &HealthInfoRaw{
|
||||
Status: health.StatusUnknown,
|
||||
Latency: time.Duration(0),
|
||||
}
|
||||
}
|
||||
return &HealthInfoRaw{
|
||||
Status: mon.Status(),
|
||||
Latency: mon.Latency(),
|
||||
}
|
||||
}
|
||||
|
||||
func HealthMap() map[string]map[string]string {
|
||||
healthMap := make(map[string]map[string]string, NumRoutes())
|
||||
func GetHealthInfo() map[string]HealthInfo {
|
||||
healthMap := make(map[string]HealthInfo, NumRoutes())
|
||||
for r := range Iter {
|
||||
healthMap[r.Name()] = getHealthInfo(r)
|
||||
}
|
||||
return healthMap
|
||||
}
|
||||
|
||||
func HealthInfo() map[string]*HealthInfoRaw {
|
||||
healthMap := make(map[string]*HealthInfoRaw, NumRoutes())
|
||||
for r := range Iter {
|
||||
healthMap[r.Name()] = getHealthInfoRaw(r)
|
||||
func getHealthInfo(r types.Route) HealthInfo {
|
||||
mon := r.HealthMonitor()
|
||||
if mon == nil {
|
||||
return HealthInfo{
|
||||
Status: types.StatusUnknown,
|
||||
Detail: "n/a",
|
||||
}
|
||||
}
|
||||
return HealthInfo{
|
||||
Status: mon.Status(),
|
||||
Uptime: mon.Uptime(),
|
||||
Latency: mon.Latency(),
|
||||
Detail: mon.Detail(),
|
||||
}
|
||||
return healthMap
|
||||
}
|
||||
|
||||
func HomepageCategories() []string {
|
||||
@@ -99,7 +95,13 @@ func HomepageCategories() []string {
|
||||
return categories
|
||||
}
|
||||
|
||||
func HomepageConfig(categoryFilter, providerFilter string) homepage.Homepage {
|
||||
func HomepageItems(proto, hostname, categoryFilter, providerFilter string) homepage.Homepage {
|
||||
switch proto {
|
||||
case "http", "https":
|
||||
default:
|
||||
proto = "http"
|
||||
}
|
||||
|
||||
hp := make(homepage.Homepage)
|
||||
|
||||
for _, r := range HTTP.Iter {
|
||||
@@ -110,13 +112,32 @@ func HomepageConfig(categoryFilter, providerFilter string) homepage.Homepage {
|
||||
if categoryFilter != "" && item.Category != categoryFilter {
|
||||
continue
|
||||
}
|
||||
|
||||
// clear url if invalid
|
||||
_, err := url.Parse(item.URL)
|
||||
if err != nil {
|
||||
item.URL = ""
|
||||
}
|
||||
|
||||
// append hostname if provided and only if alias is not FQDN
|
||||
if hostname != "" && item.URL == "" {
|
||||
if !strings.Contains(item.Alias, ".") {
|
||||
item.URL = fmt.Sprintf("%s://%s.%s", proto, item.Alias, hostname)
|
||||
}
|
||||
}
|
||||
|
||||
// prepend protocol if not exists
|
||||
if !strings.HasPrefix(item.URL, "http://") && !strings.HasPrefix(item.URL, "https://") {
|
||||
item.URL = fmt.Sprintf("%s://%s", proto, item.URL)
|
||||
}
|
||||
|
||||
hp.Add(item)
|
||||
}
|
||||
return hp
|
||||
}
|
||||
|
||||
func ByProvider() map[string][]Route {
|
||||
rts := make(map[string][]Route)
|
||||
func ByProvider() map[string][]types.Route {
|
||||
rts := make(map[string][]types.Route)
|
||||
for r := range Iter {
|
||||
rts[r.ProviderName()] = append(rts[r.ProviderName()], r)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user