Files
mc-gateway/internal/adminservice/service.go
tursom 13ab1f6964
Some checks failed
Go / build (.exe, 386, windows, windows-386) (push) Has been cancelled
Go / build (.exe, amd64, windows, windows-amd64) (push) Has been cancelled
Go / build (.exe, arm64, windows, windows-arm64) (push) Has been cancelled
Go / build (386, freebsd, freebsd-386) (push) Has been cancelled
Go / build (386, linux, linux-386) (push) Has been cancelled
Go / build (386, netbsd, netbsd-386) (push) Has been cancelled
Go / build (386, openbsd, openbsd-386) (push) Has been cancelled
Go / build (386, plan9, plan9-386) (push) Has been cancelled
Go / build (amd64, darwin, darwin-amd64) (push) Has been cancelled
Go / build (amd64, dragonfly, dragonfly-amd64) (push) Has been cancelled
Go / build (amd64, freebsd, freebsd-amd64) (push) Has been cancelled
Go / build (amd64, illumos, illumos-amd64) (push) Has been cancelled
Go / build (amd64, linux, linux-amd64) (push) Has been cancelled
Go / build (amd64, netbsd, netbsd-amd64) (push) Has been cancelled
Go / build (amd64, openbsd, openbsd-amd64) (push) Has been cancelled
Go / build (amd64, plan9, plan9-amd64) (push) Has been cancelled
Go / build (amd64, solaris, solaris-amd64) (push) Has been cancelled
Go / build (arm, 6, linux, linux-armv6) (push) Has been cancelled
Go / build (arm, 7, linux, linux-armv7) (push) Has been cancelled
Go / build (arm, freebsd, freebsd-arm) (push) Has been cancelled
Go / build (arm, netbsd, netbsd-arm) (push) Has been cancelled
Go / build (arm, openbsd, openbsd-arm) (push) Has been cancelled
Go / build (arm, plan9, plan9-arm) (push) Has been cancelled
Go / build (arm64, darwin, darwin-arm64) (push) Has been cancelled
Go / build (arm64, freebsd, freebsd-arm64) (push) Has been cancelled
Go / build (arm64, linux, linux-arm64) (push) Has been cancelled
Go / build (arm64, netbsd, netbsd-arm64) (push) Has been cancelled
Go / build (arm64, openbsd, openbsd-arm64) (push) Has been cancelled
Go / build (loong64, linux, linux-loong64) (push) Has been cancelled
Go / build (mips, linux, linux-mips) (push) Has been cancelled
Go / build (mips64, linux, linux-mips64) (push) Has been cancelled
Go / build (mips64le, linux, linux-mips64le) (push) Has been cancelled
Go / build (mipsle, linux, linux-mipsle) (push) Has been cancelled
Go / build (ppc64, aix, aix-ppc64) (push) Has been cancelled
Go / build (ppc64, linux, linux-ppc64) (push) Has been cancelled
Go / build (ppc64, openbsd, openbsd-ppc64) (push) Has been cancelled
Go / build (ppc64le, linux, linux-ppc64le) (push) Has been cancelled
Go / build (riscv64, freebsd, freebsd-riscv64) (push) Has been cancelled
Go / build (riscv64, linux, linux-riscv64) (push) Has been cancelled
Go / build (riscv64, openbsd, openbsd-riscv64) (push) Has been cancelled
Go / build (s390x, linux, linux-s390x) (push) Has been cancelled
Go / merge-artifacts (push) Has been cancelled
feat: add sqlite-backed admin management
2026-06-25 08:53:44 +08:00

191 lines
4.4 KiB
Go

package adminservice
import (
"encoding/json"
"errors"
"fmt"
"strconv"
"strings"
)
const (
NameTCPAdmin = "tcp_admin"
NameKCP = "kcp"
NameQUIC = "quic"
NameWebSocket = "websocket"
DefaultKCPPort = 25566
DefaultKCPDataShards = 10
DefaultKCPParityShards = 3
DefaultQUICPort = 25565
DefaultWebSocketPort = 25566
DefaultWebSocketPath = "/"
)
var defaultQUICApplicationProtocols = []string{"minecraft", "quic", "raw", "h3"}
type Record struct {
Name string `json:"name"`
Enabled bool `json:"enabled"`
Port int `json:"port"`
Options map[string]any `json:"options"`
RestartRequired bool `json:"restart_required"`
CreatedAt int64 `json:"created_at"`
UpdatedAt int64 `json:"updated_at"`
UpdatedBy string `json:"updated_by"`
Running bool `json:"running"`
}
func DefaultRecords(tcpAdminPort int) []Record {
return []Record{
{Name: NameTCPAdmin, Enabled: true, Port: tcpAdminPort, Options: map[string]any{}},
{Name: NameKCP, Enabled: false, Port: DefaultKCPPort, Options: map[string]any{
"data_shards": DefaultKCPDataShards,
"parity_shards": DefaultKCPParityShards,
}},
{Name: NameQUIC, Enabled: false, Port: DefaultQUICPort, Options: map[string]any{
"application_protocols": append([]string(nil), defaultQUICApplicationProtocols...),
}},
{Name: NameWebSocket, Enabled: false, Port: DefaultWebSocketPort, Options: map[string]any{
"path": DefaultWebSocketPath,
}},
}
}
func DefaultPort(name string, tcpAdminPort int) int {
switch name {
case NameTCPAdmin:
return tcpAdminPort
case NameKCP:
return DefaultKCPPort
case NameQUIC:
return DefaultQUICPort
case NameWebSocket:
return DefaultWebSocketPort
default:
return 0
}
}
func ValidateUpdate(name string, enabled bool, port int) error {
if !IsKnown(name) {
return fmt.Errorf("unknown service %q", name)
}
if port < 1 || port > 65535 {
return errors.New("port must be an integer from 1 to 65535")
}
if name == NameTCPAdmin && !enabled {
return errors.New("tcp_admin cannot be disabled")
}
return nil
}
func IsKnown(name string) bool {
switch name {
case NameTCPAdmin, NameKCP, NameQUIC, NameWebSocket:
return true
default:
return false
}
}
func DecodeOptions(optionsJSON string) map[string]any {
options := map[string]any{}
if strings.TrimSpace(optionsJSON) == "" {
return options
}
if err := json.Unmarshal([]byte(optionsJSON), &options); err != nil {
return map[string]any{}
}
return options
}
func NormalizeOptions(name string, options map[string]any) map[string]any {
if options == nil {
options = map[string]any{}
}
normalized := map[string]any{}
for key, value := range options {
normalized[key] = value
}
switch name {
case NameKCP:
if IntOption(normalized, "data_shards", 0) <= 0 {
normalized["data_shards"] = DefaultKCPDataShards
}
if IntOption(normalized, "parity_shards", 0) <= 0 {
normalized["parity_shards"] = DefaultKCPParityShards
}
case NameQUIC:
if len(StringSliceOption(normalized, "application_protocols")) == 0 {
normalized["application_protocols"] = append([]string(nil), defaultQUICApplicationProtocols...)
}
case NameWebSocket:
p := StringOption(normalized, "path", DefaultWebSocketPath)
if !strings.HasPrefix(p, "/") {
p = "/" + p
}
normalized["path"] = p
}
return normalized
}
func IntOption(options map[string]any, key string, fallback int) int {
value, ok := options[key]
if !ok {
return fallback
}
switch v := value.(type) {
case int:
return v
case int64:
return int(v)
case float64:
return int(v)
case json.Number:
i, err := v.Int64()
if err == nil {
return int(i)
}
case string:
i, err := strconv.Atoi(v)
if err == nil {
return i
}
}
return fallback
}
func StringOption(options map[string]any, key, fallback string) string {
value, ok := options[key]
if !ok {
return fallback
}
if s, ok := value.(string); ok && s != "" {
return s
}
return fallback
}
func StringSliceOption(options map[string]any, key string) []string {
value, ok := options[key]
if !ok {
return nil
}
switch v := value.(type) {
case []string:
return v
case []any:
out := make([]string, 0, len(v))
for _, item := range v {
if s, ok := item.(string); ok && s != "" {
out = append(out, s)
}
}
return out
}
return nil
}