refactor(frpc): restructure instance management and config handling
- Move instance-related structs and functions to config.go - Remove serverAddr, serverPort, and authMethod from database schema - Implement new config parsing and encoding with nested key support - Update service management to use instanceID instead of username/name - Add GetServiceNameByInstanceID helper function - Update API documentation for auth.method field change
This commit is contained in:
@@ -5,6 +5,10 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
@@ -15,6 +19,40 @@ type Config struct {
|
||||
Debug bool `json:"debug"`
|
||||
}
|
||||
|
||||
type InstanceInfo struct {
|
||||
Name string `json:"name"`
|
||||
ServerAddr string `json:"serverAddr"`
|
||||
ServerPort string `json:"serverPort"`
|
||||
AuthMethod string `json:"auth_method"`
|
||||
RunUser string `json:"runUser"`
|
||||
Additional map[string]interface{} `json:"additionalProperties"`
|
||||
}
|
||||
|
||||
type FrpcProxyInfo struct {
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
LocalIP string `json:"local_ip"`
|
||||
LocalPort string `json:"local_port"`
|
||||
RemotePort string `json:"remote_port"`
|
||||
}
|
||||
|
||||
type CreateInstanceRequest struct {
|
||||
InstanceInfo InstanceInfo `json:"instanceInfo"`
|
||||
BootAtStart bool `json:"bootAtStart"`
|
||||
RunUser string `json:"runUser"`
|
||||
Additional map[string]interface{} `json:"additionalProperties"`
|
||||
}
|
||||
|
||||
type CreateProxyRequest struct {
|
||||
InstanceID string `json:"instanceID"`
|
||||
ProxyInfo FrpcProxyInfo `json:"proxyInfo"`
|
||||
}
|
||||
|
||||
type FrpcConfig struct {
|
||||
Global map[string]interface{} `toml:"-"`
|
||||
Proxies []map[string]interface{} `toml:"proxies"`
|
||||
}
|
||||
|
||||
var globalConfig *Config
|
||||
|
||||
func LoadConfig(configPath string) (*Config, error) {
|
||||
@@ -72,3 +110,241 @@ func SaveConfig(configPath string, config *Config) error {
|
||||
globalConfig = config
|
||||
return nil
|
||||
}
|
||||
|
||||
func decodeFrpcConfig(configContent string) (FrpcConfig, error) {
|
||||
var config FrpcConfig
|
||||
|
||||
var rawMap map[string]interface{}
|
||||
meta, err := toml.Decode(configContent, &rawMap)
|
||||
if err != nil {
|
||||
return config, fmt.Errorf("failed to parse config: %w", err)
|
||||
}
|
||||
|
||||
config.Global = make(map[string]interface{})
|
||||
config.Proxies = make([]map[string]interface{}, 0)
|
||||
|
||||
getNestedValue := func(m map[string]interface{}, key string) (interface{}, bool) {
|
||||
parts := strings.Split(key, ".")
|
||||
current := m
|
||||
for i, part := range parts {
|
||||
if i == len(parts)-1 {
|
||||
val, ok := current[part]
|
||||
return val, ok
|
||||
}
|
||||
next, ok := current[part].(map[string]interface{})
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
current = next
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
for _, key := range meta.Keys() {
|
||||
keyStr := key.String()
|
||||
if keyStr == "proxies" {
|
||||
if proxies, ok := rawMap["proxies"].([]map[string]interface{}); ok {
|
||||
config.Proxies = proxies
|
||||
} else if proxies, ok := rawMap["proxies"].([]interface{}); ok {
|
||||
for _, p := range proxies {
|
||||
if pm, ok := p.(map[string]interface{}); ok {
|
||||
config.Proxies = append(config.Proxies, pm)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if !strings.HasPrefix(keyStr, "proxies.") {
|
||||
if val, ok := getNestedValue(rawMap, keyStr); ok {
|
||||
config.Global[keyStr] = val
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return config, nil
|
||||
}
|
||||
|
||||
func encodeFrpcConfig(config FrpcConfig) (string, error) {
|
||||
var buf strings.Builder
|
||||
|
||||
if len(config.Global) > 0 {
|
||||
for key, value := range config.Global {
|
||||
switch v := value.(type) {
|
||||
case string:
|
||||
buf.WriteString(fmt.Sprintf("%s = %q\n", key, v))
|
||||
case int:
|
||||
buf.WriteString(fmt.Sprintf("%s = %d\n", key, v))
|
||||
case int64:
|
||||
buf.WriteString(fmt.Sprintf("%s = %d\n", key, v))
|
||||
case float64:
|
||||
buf.WriteString(fmt.Sprintf("%s = %v\n", key, v))
|
||||
case bool:
|
||||
buf.WriteString(fmt.Sprintf("%s = %v\n", key, v))
|
||||
default:
|
||||
data, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to marshal value for key %s: %w", key, err)
|
||||
}
|
||||
buf.WriteString(fmt.Sprintf("%s = %s\n", key, string(data)))
|
||||
}
|
||||
}
|
||||
buf.WriteString("\n")
|
||||
}
|
||||
|
||||
if len(config.Proxies) > 0 {
|
||||
for _, proxy := range config.Proxies {
|
||||
buf.WriteString("[[proxies]]\n")
|
||||
for key, value := range proxy {
|
||||
switch v := value.(type) {
|
||||
case string:
|
||||
buf.WriteString(fmt.Sprintf("%s = %q\n", key, v))
|
||||
case int:
|
||||
buf.WriteString(fmt.Sprintf("%s = %d\n", key, v))
|
||||
case int64:
|
||||
buf.WriteString(fmt.Sprintf("%s = %d\n", key, v))
|
||||
case float64:
|
||||
buf.WriteString(fmt.Sprintf("%s = %v\n", key, v))
|
||||
case bool:
|
||||
buf.WriteString(fmt.Sprintf("%s = %v\n", key, v))
|
||||
default:
|
||||
data, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to marshal value for key %s: %w", key, err)
|
||||
}
|
||||
buf.WriteString(fmt.Sprintf("%s = %s\n", key, string(data)))
|
||||
}
|
||||
}
|
||||
buf.WriteString("\n")
|
||||
}
|
||||
}
|
||||
|
||||
return strings.TrimSuffix(buf.String(), "\n"), nil
|
||||
}
|
||||
|
||||
func generateFrpcConfig(info InstanceInfo) string {
|
||||
config := FrpcConfig{
|
||||
Global: make(map[string]interface{}),
|
||||
}
|
||||
|
||||
config.Global["serverAddr"] = info.ServerAddr
|
||||
config.Global["serverPort"] = info.ServerPort
|
||||
config.Global["auth.method"] = info.AuthMethod
|
||||
|
||||
for key, value := range info.Additional {
|
||||
config.Global[key] = value
|
||||
}
|
||||
|
||||
result, err := encodeFrpcConfig(config)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func addFrpcProxy(configContent string, info FrpcProxyInfo) (string, error) {
|
||||
config, err := decodeFrpcConfig(configContent)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to parse config: %w", err)
|
||||
}
|
||||
|
||||
proxy := map[string]interface{}{
|
||||
"name": info.Name,
|
||||
"type": info.Type,
|
||||
"localIP": info.LocalIP,
|
||||
"localPort": info.LocalPort,
|
||||
"remotePort": info.RemotePort,
|
||||
}
|
||||
|
||||
config.Proxies = append(config.Proxies, proxy)
|
||||
|
||||
result, err := encodeFrpcConfig(config)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to write config: %w", err)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func removeFrpcProxy(configContent string, proxyName string) (string, error) {
|
||||
config, err := decodeFrpcConfig(configContent)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to parse config: %w", err)
|
||||
}
|
||||
|
||||
var found bool
|
||||
var newProxies []map[string]interface{}
|
||||
for _, proxy := range config.Proxies {
|
||||
if name, ok := proxy["name"].(string); ok && name == proxyName {
|
||||
found = true
|
||||
continue
|
||||
}
|
||||
newProxies = append(newProxies, proxy)
|
||||
}
|
||||
|
||||
if !found {
|
||||
return "", fmt.Errorf("proxy %s not found", proxyName)
|
||||
}
|
||||
|
||||
config.Proxies = newProxies
|
||||
|
||||
result, err := encodeFrpcConfig(config)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to write config: %w", err)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func getKeyText(configPath, key, section string) (value string, err error) {
|
||||
configContent, err := os.ReadFile(configPath)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to read config file: %w", err)
|
||||
}
|
||||
|
||||
config, err := decodeFrpcConfig(string(configContent))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to parse config: %w", err)
|
||||
}
|
||||
|
||||
if config.Global == nil {
|
||||
return "", fmt.Errorf("config file has no global fields")
|
||||
}
|
||||
|
||||
if v, ok := config.Global[key]; ok {
|
||||
value = fmt.Sprintf("%v", v)
|
||||
}
|
||||
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func setKeyText(configPath, key, section string, value string) error {
|
||||
configContent, err := os.ReadFile(configPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read config file: %w", err)
|
||||
}
|
||||
|
||||
config, err := decodeFrpcConfig(string(configContent))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse config: %w", err)
|
||||
}
|
||||
|
||||
if config.Global == nil {
|
||||
config.Global = make(map[string]interface{})
|
||||
}
|
||||
|
||||
if intVal, err := strconv.Atoi(value); err == nil {
|
||||
config.Global[key] = intVal
|
||||
} else {
|
||||
config.Global[key] = value
|
||||
}
|
||||
|
||||
result, err := encodeFrpcConfig(config)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to write config: %w", err)
|
||||
}
|
||||
|
||||
if err := os.WriteFile(configPath, []byte(result), 0644); err != nil {
|
||||
return fmt.Errorf("failed to write config file: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user