chore: caddy config file(json)

nginx: proxy version api
This commit is contained in:
zhaoyupeng
2025-12-05 18:39:30 +08:00
parent f4f3590aec
commit c53c15fa8c
15 changed files with 1153 additions and 60 deletions

55
pkg/model/caddy.go Normal file
View File

@@ -0,0 +1,55 @@
package model
type CaddyUpstream struct {
Dial []string `json:"dial"`
}
type CaddyActive struct {
Interval string `json:"interval"`
Timeout string `json:"timeout"`
Port int `json:"port"`
}
type CaddyPassive struct {
MaxFails int `json:"max_fails"`
FailDuration string `json:"fail_duration"`
}
type CaddyHealthCheck struct {
Active *CaddyActive `json:"active"`
Passive *CaddyPassive `json:"passive"`
}
type CaddySelection struct {
Policy string `json:"policy"`
}
type CaddyLoadBalancing struct {
Selection *CaddySelection `json:"selection"`
}
type CaddyHandle struct {
Handler string `json:"handler"`
Upstreams []*CaddyUpstream `json:"upstreams"`
HealthChecks *CaddyHealthCheck `json:"health_checks"`
LoadBalancing *CaddyLoadBalancing `json:"load_balancing"`
}
type CaddyRoute struct {
Handle []*CaddyHandle `json:"handle"`
}
type CaddyServer struct {
Listen []string `json:"listen"`
Routes []*CaddyRoute `json:"routes"`
}
type CaddyLayer4 struct {
Servers map[string]*CaddyServer `json:"servers"`
}
type CaddyApp struct {
Layer4 *CaddyLayer4 `json:"layer4"`
}
type CaddyConfig map[string]*CaddyApp

View File

@@ -0,0 +1,73 @@
{
"apps": {
"layer4": {
"servers": {
"proxy_8443_tcp_backends": {
"listen": [":8443"],
"routes": [
{
"handle": [
{
"handler": "proxy",
"upstreams": [
{"dial": ["10.118.2.11:32443"]},
{"dial": ["10.118.2.12:32443"]}
],
"health_checks": {
"active": {
"interval": "5s",
"timeout": "2s",
"port": 32443
},
"passive": {
"max_fails": 1,
"fail_duration": "30s"
}
},
"load_balancing": {
"selection": {
"policy": "round_robin"
}
}
}
]
}
]
},
"proxy_443_tcp_backends": {
"listen": [":443"],
"routes": [
{
"handle": [
{
"handler": "proxy",
"upstreams": [
{"dial": ["10.118.2.11:31443"]},
{"dial": ["10.118.2.12:31443"]}
],
"health_checks": {
"active": {
"interval": "5s",
"timeout": "2s",
"port": 31443
},
"passive": {
"max_fails": 1,
"fail_duration": "30s"
}
},
"load_balancing": {
"selection": {
"policy": "round_robin"
}
}
}
]
}
]
}
}
}
}
}

View File

@@ -37,6 +37,10 @@ server {
proxy_pass http://u-api-service/api/v2_2/client/download/check;
}
location /api/v1/version {
proxy_pass http://u-api-service/api/v2_2/client/version;
}
location /api/ {
proxy_pass http://hs-client-server;
proxy_http_version 1.1;
@@ -140,10 +144,6 @@ server {
client_max_body_size 50M;
# location /api/v1/pkg/config/setup {
# proxy_pass http://u-api-service/api/v2_2/client/download/version;
# }
location /api/v1/pkg/archive {
proxy_pass http://u-api-service/api/v2_2/client/download/check;
}
@@ -152,18 +152,22 @@ server {
proxy_pass http://u-api-service/api/v2_2/client/download/version;
}
location /api/v1/version {
proxy_pass http://u-api-service/api/v2_2/client/version;
}
location /static/config/rc.json {
proxy_pass http://u-api-service/api/v2_2/client/rc/json?os=win;
}
location = /api/v1/version {
proxy_pass http://hs-client-without-auth-server;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $proxy_protocol_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_read_timeout 300s;
}
# location = /api/v1/version {
# proxy_pass http://hs-client-without-auth-server;
# proxy_http_version 1.1;
# proxy_set_header Host $host;
# proxy_set_header X-Real-IP $proxy_protocol_addr;
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# proxy_read_timeout 300s;
# }
location /api/v1/pkg {
proxy_pass http://hs-client-without-auth-server;

318
pkg/syscheck/syscheck.go Normal file
View File

@@ -0,0 +1,318 @@
package syscheck
import (
"context"
"fmt"
"strconv"
"strings"
)
// CheckResult represents the result of a system check
type CheckResult struct {
Name string
Passed bool
Actual string
Expected string
Message string
}
// DiskInfo represents disk information
type DiskInfo struct {
AvailableGB float64
WriteSpeed float64 // MB/s
ReadSpeed float64 // MB/s
}
// MemInfo represents memory information
type MemInfo struct {
TotalGB float64
}
// CPUInfo represents CPU information
type CPUInfo struct {
Cores int
FrequencyMHz float64
}
// CommandExecutor defines interface for executing commands
type CommandExecutor interface {
ExecuteCommand(ctx context.Context, cmds ...string) (string, error)
}
// CheckDiskSpace checks if disk space meets minimum requirements
func CheckDiskSpace(ctx context.Context, executor CommandExecutor, minGB float64) (*CheckResult, error) {
// Use df to check available disk space on root partition
output, err := executor.ExecuteCommand(ctx, "df", "-BG", "/")
if err != nil {
return nil, fmt.Errorf("failed to check disk space: %w", err)
}
lines := strings.Split(strings.TrimSpace(output), "\n")
if len(lines) < 2 {
return nil, fmt.Errorf("unexpected df output format")
}
fields := strings.Fields(lines[1])
if len(fields) < 4 {
return nil, fmt.Errorf("unexpected df fields count")
}
// Parse available space (4th field, format: "500G")
availableStr := strings.TrimSuffix(fields[3], "G")
available, err := strconv.ParseFloat(availableStr, 64)
if err != nil {
return nil, fmt.Errorf("failed to parse available disk space: %w", err)
}
result := &CheckResult{
Name: "Disk Space",
Passed: available >= minGB,
Actual: fmt.Sprintf("%.2f GB", available),
Expected: fmt.Sprintf(">= %.2f GB", minGB),
}
if !result.Passed {
result.Message = fmt.Sprintf("Insufficient disk space: %.2f GB available, %.2f GB required", available, minGB)
}
return result, nil
}
// CheckDiskPerformance checks disk read/write performance
func CheckDiskPerformance(ctx context.Context, executor CommandExecutor, minWriteMBps, minReadMBps float64) (*CheckResult, error) {
// Use dd to test write performance
writeCmd := "dd if=/dev/zero of=/tmp/test_write bs=1M count=1024 oflag=direct 2>&1 | tail -1"
writeOutput, err := executor.ExecuteCommand(ctx, "bash", "-c", writeCmd)
if err != nil {
return nil, fmt.Errorf("failed to check disk write performance: %w", err)
}
// Parse write speed from dd output (format: "... copied, X.XX s, XXX MB/s")
writeSpeed, err := parseDDSpeed(writeOutput)
if err != nil {
return nil, fmt.Errorf("failed to parse write speed: %w, output: %s", err, writeOutput)
}
// Test read performance and clean up test file
readCmd := "dd if=/tmp/test_write of=/dev/null bs=1M count=1024 iflag=direct 2>&1 | tail -1; rm -f /tmp/test_write"
readOutput, err := executor.ExecuteCommand(ctx, "bash", "-c", readCmd)
if err != nil {
return nil, fmt.Errorf("failed to check disk read performance: %w", err)
}
// Parse read speed from dd output
readSpeed, err := parseDDSpeed(readOutput)
if err != nil {
return nil, fmt.Errorf("failed to parse read speed: %w, output: %s", err, readOutput)
}
passed := writeSpeed >= minWriteMBps && readSpeed >= minReadMBps
result := &CheckResult{
Name: "Disk Performance",
Passed: passed,
Actual: fmt.Sprintf("Write: %.2f MB/s, Read: %.2f MB/s", writeSpeed, readSpeed),
Expected: fmt.Sprintf("Write: >= %.2f MB/s, Read: >= %.2f MB/s", minWriteMBps, minReadMBps),
}
if !passed {
result.Message = fmt.Sprintf("Insufficient disk performance")
}
return result, nil
}
// parseDDSpeed parses the speed from dd command output
// Expected format: "104857600 bytes (105 MB, 100 MiB) copied, 0.125749 s, 834 MB/s"
func parseDDSpeed(output string) (float64, error) {
output = strings.TrimSpace(output)
if output == "" {
return 0, fmt.Errorf("empty output")
}
// Find the last occurrence of "MB/s" or "GB/s"
var speed float64
var unit string
// Try to match "XXX MB/s" or "XXX GB/s" pattern
if idx := strings.LastIndex(output, " MB/s"); idx != -1 {
// Extract the number before " MB/s"
fields := strings.Fields(output[:idx])
if len(fields) == 0 {
return 0, fmt.Errorf("no speed value found")
}
speedStr := fields[len(fields)-1]
var err error
speed, err = strconv.ParseFloat(speedStr, 64)
if err != nil {
return 0, fmt.Errorf("failed to parse speed value '%s': %w", speedStr, err)
}
unit = "MB/s"
} else if idx := strings.LastIndex(output, " GB/s"); idx != -1 {
// Extract the number before " GB/s"
fields := strings.Fields(output[:idx])
if len(fields) == 0 {
return 0, fmt.Errorf("no speed value found")
}
speedStr := fields[len(fields)-1]
var err error
speed, err = strconv.ParseFloat(speedStr, 64)
if err != nil {
return 0, fmt.Errorf("failed to parse speed value '%s': %w", speedStr, err)
}
unit = "GB/s"
speed *= 1024 // Convert GB/s to MB/s
} else {
return 0, fmt.Errorf("no MB/s or GB/s found in output")
}
if unit == "MB/s" || unit == "GB/s" {
return speed, nil
}
return 0, fmt.Errorf("unexpected unit: %s", unit)
}
// CheckMemory checks if system memory meets minimum requirements
func CheckMemory(ctx context.Context, executor CommandExecutor, minGB float64) (*CheckResult, error) {
// Use free -m to check memory in MB for better precision
output, err := executor.ExecuteCommand(ctx, "free", "-m")
if err != nil {
return nil, fmt.Errorf("failed to check memory: %w", err)
}
lines := strings.Split(strings.TrimSpace(output), "\n")
if len(lines) < 2 {
return nil, fmt.Errorf("unexpected free output format")
}
fields := strings.Fields(lines[1])
if len(fields) < 2 {
return nil, fmt.Errorf("unexpected free fields count")
}
// Parse total memory in MB
totalMB, err := strconv.ParseFloat(fields[1], 64)
if err != nil {
return nil, fmt.Errorf("failed to parse memory size: %w", err)
}
// Convert MB to GB (1 GB = 1024 MB)
totalGB := totalMB / 1024.0
result := &CheckResult{
Name: "Memory Size",
Passed: totalGB >= minGB,
Actual: fmt.Sprintf("%.2f GB", totalGB),
Expected: fmt.Sprintf(">= %.2f GB", minGB),
}
if !result.Passed {
result.Message = fmt.Sprintf("Insufficient memory: %.2f GB available, %.2f GB required", totalGB, minGB)
}
return result, nil
}
// CheckCPUCores checks if CPU core count meets minimum requirements
func CheckCPUCores(ctx context.Context, executor CommandExecutor, minCores int) (*CheckResult, error) {
// Read /proc/cpuinfo to get CPU core count (more universal than nproc)
output, err := executor.ExecuteCommand(ctx, "cat", "/proc/cpuinfo")
if err != nil {
return nil, fmt.Errorf("failed to check CPU cores: %w", err)
}
// Count the number of "processor" lines
cores := 0
lines := strings.Split(output, "\n")
for _, line := range lines {
if strings.HasPrefix(strings.TrimSpace(line), "processor") {
cores++
}
}
if cores == 0 {
return nil, fmt.Errorf("failed to parse CPU cores from /proc/cpuinfo")
}
result := &CheckResult{
Name: "CPU Cores",
Passed: cores >= minCores,
Actual: fmt.Sprintf("%d cores", cores),
Expected: fmt.Sprintf(">= %d cores", minCores),
}
if !result.Passed {
result.Message = fmt.Sprintf("Insufficient CPU cores: %d cores available, %d cores required", cores, minCores)
}
return result, nil
}
// CheckCPUFrequency checks if CPU frequency meets minimum requirements
func CheckCPUFrequency(ctx context.Context, executor CommandExecutor, minGHz float64) (*CheckResult, error) {
// Read /proc/cpuinfo to get CPU frequency (more universal than lscpu)
output, err := executor.ExecuteCommand(ctx, "cat", "/proc/cpuinfo")
if err != nil {
return nil, fmt.Errorf("failed to check CPU frequency: %w", err)
}
var maxFreqMHz float64
lines := strings.Split(output, "\n")
// Try to parse from "cpu MHz" field (runtime frequency)
for _, line := range lines {
line = strings.TrimSpace(line)
if strings.HasPrefix(line, "cpu MHz") {
fields := strings.Split(line, ":")
if len(fields) >= 2 {
freqStr := strings.TrimSpace(fields[1])
freq, err := strconv.ParseFloat(freqStr, 64)
if err == nil && freq > maxFreqMHz {
maxFreqMHz = freq
}
}
}
}
// If not found, try to parse from "model name" field (base frequency)
if maxFreqMHz == 0 {
for _, line := range lines {
line = strings.TrimSpace(line)
if strings.HasPrefix(line, "model name") {
// Look for pattern like "@ 2.60GHz"
if idx := strings.Index(line, "@"); idx != -1 {
freqPart := line[idx+1:]
// Extract GHz value
if ghzIdx := strings.Index(freqPart, "GHz"); ghzIdx != -1 {
freqStr := strings.TrimSpace(freqPart[:ghzIdx])
freqGHz, err := strconv.ParseFloat(freqStr, 64)
if err == nil {
maxFreqMHz = freqGHz * 1000.0
break
}
}
}
}
}
}
if maxFreqMHz == 0 {
return nil, fmt.Errorf("failed to parse CPU frequency from /proc/cpuinfo")
}
freqGHz := maxFreqMHz / 1000.0
minMHz := minGHz * 1000.0
result := &CheckResult{
Name: "CPU Frequency",
Passed: maxFreqMHz >= minMHz,
Actual: fmt.Sprintf("%.2f GHz", freqGHz),
Expected: fmt.Sprintf(">= %.2f GHz", minGHz),
}
if !result.Passed {
result.Message = fmt.Sprintf("Insufficient CPU frequency: %.2f GHz available, %.2f GHz required", freqGHz, minGHz)
}
return result, nil
}