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:
@@ -9,43 +9,10 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"super-frpc/postLog"
|
||||
"time"
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
)
|
||||
|
||||
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 FrpcConfig struct {
|
||||
Common map[string]interface{} `toml:"common"`
|
||||
Proxies []map[string]interface{} `toml:"proxies"`
|
||||
Additional map[string]interface{} `toml:"-"`
|
||||
}
|
||||
|
||||
var frpcDB *sql.DB
|
||||
|
||||
func CloseFrpcDatabase() error {
|
||||
@@ -165,9 +132,6 @@ func CreateInstanceHandler(w http.ResponseWriter, r *http.Request) {
|
||||
instance := FrpcInstance{
|
||||
UserID: userID,
|
||||
Name: req.InstanceInfo.Name,
|
||||
ServerAddr: req.InstanceInfo.ServerAddr,
|
||||
ServerPort: req.InstanceInfo.ServerPort,
|
||||
AuthMethod: req.InstanceInfo.AuthMethod,
|
||||
BootAtStart: req.BootAtStart,
|
||||
RunUser: runUser,
|
||||
ConfigPath: configPath,
|
||||
@@ -180,16 +144,24 @@ func CreateInstanceHandler(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if err := createBootService(user.Username, req.InstanceInfo.Name, configPath, runUser); err != nil {
|
||||
frpcDB.Exec("DELETE FROM frpcInstances WHERE userID = ? AND name = ?", userID, req.InstanceInfo.Name)
|
||||
os.Remove(configPath)
|
||||
postLog.Error(fmt.Sprintf("[CreateInstanceHandler] Failed to create boot service for instance %s: %v", req.InstanceInfo.Name, err))
|
||||
SendErrorResponse(w, http.StatusInternalServerError, "Failed to create boot service")
|
||||
return
|
||||
}
|
||||
createdInstance, err := DBQueryFrpcInstance(userID, req.InstanceInfo.Name)
|
||||
if err != nil {
|
||||
os.Remove(configPath)
|
||||
postLog.Error(fmt.Sprintf("[CreateInstanceHandler] Failed to query created instance: %v", err))
|
||||
SendErrorResponse(w, http.StatusInternalServerError, "Failed to query created instance")
|
||||
return
|
||||
}
|
||||
|
||||
if err := createBootService(createdInstance.ID); err != nil {
|
||||
frpcDB.Exec("DELETE FROM frpcInstances WHERE userID = ? AND name = ?", userID, req.InstanceInfo.Name)
|
||||
os.Remove(configPath)
|
||||
postLog.Error(fmt.Sprintf("[CreateInstanceHandler] Failed to create boot service for instance %s: %v", req.InstanceInfo.Name, err))
|
||||
SendErrorResponse(w, http.StatusInternalServerError, "Failed to create boot service")
|
||||
return
|
||||
}
|
||||
|
||||
if req.BootAtStart {
|
||||
if err := setBootAtStart(user.Username, req.InstanceInfo.Name); err != nil {
|
||||
if err := setBootAtStart(createdInstance.ID); err != nil {
|
||||
postLog.Error(fmt.Sprintf("[CreateInstanceHandler] Failed to set boot at start for instance %s: %v", req.InstanceInfo.Name, err))
|
||||
SendErrorResponse(w, http.StatusInternalServerError, "Failed to set boot at start")
|
||||
return
|
||||
@@ -250,15 +222,7 @@ func DeleteInstanceHandler(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
user, err := GetUserByID(userID)
|
||||
if err != nil {
|
||||
postLog.Error(fmt.Sprintf("[DeleteInstanceHandler] Failed to get user info: %v", err))
|
||||
SendErrorResponse(w, http.StatusInternalServerError, "Failed to get user info")
|
||||
return
|
||||
}
|
||||
|
||||
var instance FrpcInstance
|
||||
instance, err = DBQueryFrpcInstanceByID(instanceID)
|
||||
instance, err := DBQueryFrpcInstanceByID(instanceID)
|
||||
if err == sql.ErrNoRows {
|
||||
SendErrorResponse(w, http.StatusNotFound, "Instance not found")
|
||||
postLog.Error(fmt.Sprintf("[DeleteInstanceHandler] User %d tried to delete a not existed instance: %d", userID, instanceID))
|
||||
@@ -270,7 +234,7 @@ func DeleteInstanceHandler(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if err := removeBootService(user.Username, instance.Name); err != nil {
|
||||
if err := removeBootService(instanceID); err != nil {
|
||||
postLog.Error(fmt.Sprintf("[DeleteInstanceHandler] Failed to remove boot service for instance %s: %v", instance.Name, err))
|
||||
SendErrorResponse(w, http.StatusInternalServerError, "Failed to remove boot service")
|
||||
return
|
||||
@@ -395,40 +359,40 @@ func ModifyInstanceHandler(w http.ResponseWriter, r *http.Request) {
|
||||
func handleConfigFileModify(w http.ResponseWriter, instance FrpcInstance, modifiedData map[string]interface{}, username string) {
|
||||
configPath := instance.ConfigPath
|
||||
|
||||
configContent, err := os.ReadFile(configPath)
|
||||
if err != nil {
|
||||
postLog.Error(fmt.Sprintf("[handleConfigFileModify] Failed to read config file %s: %v", configPath, err))
|
||||
SendErrorResponse(w, http.StatusInternalServerError, "Failed to read config file")
|
||||
return
|
||||
}
|
||||
for key, value := range modifiedData {
|
||||
configKey := key
|
||||
if key == "auth_method" {
|
||||
configKey = "auth.method"
|
||||
}
|
||||
if key == "auth_token" {
|
||||
configKey = "auth.token"
|
||||
}
|
||||
|
||||
updatedConfig, err := updateCommonSection(string(configContent), modifiedData)
|
||||
if err != nil {
|
||||
postLog.Error(fmt.Sprintf("[handleConfigFileModify] Failed to update common section: %v", err))
|
||||
SendErrorResponse(w, http.StatusInternalServerError, "Failed to update config file")
|
||||
return
|
||||
}
|
||||
|
||||
if err := os.WriteFile(configPath, []byte(updatedConfig), 0644); err != nil {
|
||||
postLog.Error(fmt.Sprintf("[handleConfigFileModify] Failed to write config file %s: %v", configPath, err))
|
||||
SendErrorResponse(w, http.StatusInternalServerError, "Failed to write config file")
|
||||
return
|
||||
}
|
||||
|
||||
if v, ok := modifiedData["server_addr"].(string); ok && v != "" {
|
||||
instance.ServerAddr = v
|
||||
}
|
||||
if v, ok := modifiedData["server_port"].(string); ok && v != "" {
|
||||
instance.ServerPort = v
|
||||
}
|
||||
if v, ok := modifiedData["auth_method"].(string); ok && v != "" {
|
||||
instance.AuthMethod = v
|
||||
}
|
||||
|
||||
if err := DBUpdateFrpcInstance(instance); err != nil {
|
||||
postLog.Error(fmt.Sprintf("[handleConfigFileModify] Failed to update instance in database: %v", err))
|
||||
SendErrorResponse(w, http.StatusInternalServerError, "Failed to update instance in database")
|
||||
return
|
||||
var configValue string
|
||||
if key == "serverPort" {
|
||||
switch v := value.(type) {
|
||||
case float64:
|
||||
configValue = strconv.Itoa(int(v))
|
||||
case int:
|
||||
configValue = strconv.Itoa(v)
|
||||
case string:
|
||||
var intVal int
|
||||
if _, err := fmt.Sscanf(v, "%d", &intVal); err == nil {
|
||||
configValue = strconv.Itoa(intVal)
|
||||
} else {
|
||||
configValue = v
|
||||
}
|
||||
default:
|
||||
configValue = fmt.Sprintf("%v", value)
|
||||
}
|
||||
} else {
|
||||
configValue = fmt.Sprintf("%v", value)
|
||||
}
|
||||
if err := setKeyText(configPath, configKey, "", configValue); err != nil {
|
||||
postLog.Error(fmt.Sprintf("[handleConfigFileModify] Failed to set key %s: %v", key, err))
|
||||
SendErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Failed to set key %s", key))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
SendSuccessResponse(w, "Config file modified successfully", map[string]interface{}{
|
||||
@@ -439,24 +403,6 @@ func handleConfigFileModify(w http.ResponseWriter, instance FrpcInstance, modifi
|
||||
postLog.Info(fmt.Sprintf("[handleConfigFileModify] Config file for instance %s modified successfully", instance.Name))
|
||||
}
|
||||
|
||||
func updateCommonSection(configContent string, modifiedData map[string]interface{}) (string, error) {
|
||||
var config FrpcConfig
|
||||
if _, err := toml.Decode(configContent, &config); err != nil {
|
||||
return "", fmt.Errorf("failed to parse config: %w", err)
|
||||
}
|
||||
|
||||
for key, value := range modifiedData {
|
||||
config.Common[key] = value
|
||||
}
|
||||
|
||||
var buf strings.Builder
|
||||
if err := toml.NewEncoder(&buf).Encode(config); err != nil {
|
||||
return "", fmt.Errorf("failed to write config: %w", err)
|
||||
}
|
||||
|
||||
return buf.String(), nil
|
||||
}
|
||||
|
||||
func handleSystemConfigModify(w http.ResponseWriter, r *http.Request, instance FrpcInstance, modifiedData map[string]interface{}, user *User) {
|
||||
newName := instance.Name
|
||||
newRunUser := instance.RunUser
|
||||
@@ -512,12 +458,12 @@ func handleSystemConfigModify(w http.ResponseWriter, r *http.Request, instance F
|
||||
}
|
||||
|
||||
if newBootAtStart {
|
||||
if err := removeBootAtStart(user.Username, instance.Name); err != nil {
|
||||
if err := removeBootAtStart(instance.ID); err != nil {
|
||||
postLog.Error(fmt.Sprintf("[handleSystemConfigModify] Failed to remove boot at start: %v", err))
|
||||
bootServiceError = fmt.Sprintf("Failed to remove boot at start: %v", err)
|
||||
}
|
||||
instance.Name = newName
|
||||
if err := setBootAtStart(user.Username, newName); err != nil {
|
||||
if err := setBootAtStart(instance.ID); err != nil {
|
||||
postLog.Error(fmt.Sprintf("[handleSystemConfigModify] Failed to set boot at start: %v", err))
|
||||
if bootServiceError != "" {
|
||||
bootServiceError += "; "
|
||||
@@ -525,7 +471,7 @@ func handleSystemConfigModify(w http.ResponseWriter, r *http.Request, instance F
|
||||
bootServiceError += fmt.Sprintf("Failed to set boot at start: %v", err)
|
||||
}
|
||||
} else {
|
||||
if err := removeBootAtStart(user.Username, instance.Name); err != nil {
|
||||
if err := removeBootAtStart(instance.ID); err != nil {
|
||||
postLog.Error(fmt.Sprintf("[handleSystemConfigModify] Failed to remove boot at start: %v", err))
|
||||
bootServiceError = fmt.Sprintf("Failed to remove boot at start: %v", err)
|
||||
}
|
||||
@@ -546,30 +492,9 @@ func handleSystemConfigModify(w http.ResponseWriter, r *http.Request, instance F
|
||||
SendSuccessResponse(w, "System config modified successfully", data)
|
||||
}
|
||||
|
||||
func generateFrpcConfig(info InstanceInfo) string {
|
||||
config := FrpcConfig{
|
||||
Common: make(map[string]interface{}),
|
||||
}
|
||||
|
||||
config.Common["server_addr"] = info.ServerAddr
|
||||
config.Common["server_port"] = info.ServerPort
|
||||
config.Common["auth_method"] = info.AuthMethod
|
||||
|
||||
for key, value := range info.Additional {
|
||||
config.Common[key] = value
|
||||
}
|
||||
|
||||
var buf strings.Builder
|
||||
if err := toml.NewEncoder(&buf).Encode(config); err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func GetUserInstances(userID int) ([]FrpcInstance, error) {
|
||||
rows, err := frpcDB.Query(`
|
||||
SELECT id, userID, name, serverAddr, serverPort, auth_method, bootAtStart, runUser, configPath, createdAt
|
||||
SELECT id, userID, name, bootAtStart, runUser, configPath, createdAt
|
||||
FROM frpcInstances WHERE userID = ?
|
||||
`, userID)
|
||||
if err != nil {
|
||||
@@ -582,8 +507,7 @@ func GetUserInstances(userID int) ([]FrpcInstance, error) {
|
||||
var instance FrpcInstance
|
||||
var createdAtStr string
|
||||
if err := rows.Scan(
|
||||
&instance.ID, &instance.UserID, &instance.Name, &instance.ServerAddr, &instance.ServerPort,
|
||||
&instance.AuthMethod, &instance.BootAtStart, &instance.RunUser, &instance.ConfigPath, &createdAtStr,
|
||||
&instance.ID, &instance.UserID, &instance.Name, &instance.BootAtStart, &instance.RunUser, &instance.ConfigPath, &createdAtStr,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -642,9 +566,15 @@ func ListInstancesHandler(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
if userType == "admin" || userType == "superuser" {
|
||||
instanceData["serverAddr"] = inst.ServerAddr
|
||||
instanceData["serverPort"] = inst.ServerPort
|
||||
instanceData["auth_method"] = inst.AuthMethod
|
||||
serverAddr, err := getKeyText(inst.ConfigPath, "serverAddr", "")
|
||||
serverPort, err := getKeyText(inst.ConfigPath, "serverPort", "")
|
||||
authMethod, err := getKeyText(inst.ConfigPath, "auth.method", "")
|
||||
if err != nil {
|
||||
postLog.Error(fmt.Sprintf("[ListInstancesHandler] Failed to read config for instance %d: %v", inst.ID, err))
|
||||
}
|
||||
instanceData["serverAddr"] = serverAddr
|
||||
instanceData["serverPort"] = serverPort
|
||||
instanceData["auth_method"] = authMethod
|
||||
}
|
||||
|
||||
instanceList[i] = instanceData
|
||||
@@ -725,16 +655,14 @@ func StartInstanceHandler(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
user, err := GetUserByID(instance.UserID)
|
||||
initType := GetInitSystem()
|
||||
serviceName, err := GetServiceNameByInstanceID(instanceID)
|
||||
if err != nil {
|
||||
postLog.Error(fmt.Sprintf("[StartInstanceHandler] Failed to get user info: %v", err))
|
||||
SendErrorResponse(w, http.StatusInternalServerError, "Failed to get user info")
|
||||
postLog.Error(fmt.Sprintf("[StartInstanceHandler] Failed to get service name: %v", err))
|
||||
SendErrorResponse(w, http.StatusInternalServerError, "Failed to get service name")
|
||||
return
|
||||
}
|
||||
|
||||
initType := GetInitSystem()
|
||||
serviceName := fmt.Sprintf("superfrpc_%s_%s", user.Username, instance.Name)
|
||||
|
||||
switch initType {
|
||||
case "windows":
|
||||
if err := StartWindowsService(serviceName); err != nil {
|
||||
@@ -841,16 +769,14 @@ func StopInstanceHandler(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
user, err := GetUserByID(instance.UserID)
|
||||
initType := GetInitSystem()
|
||||
serviceName, err := GetServiceNameByInstanceID(instanceID)
|
||||
if err != nil {
|
||||
postLog.Error(fmt.Sprintf("[StopInstanceHandler] Failed to get user info: %v", err))
|
||||
SendErrorResponse(w, http.StatusInternalServerError, "Failed to get user info")
|
||||
postLog.Error(fmt.Sprintf("[StopInstanceHandler] Failed to get service name: %v", err))
|
||||
SendErrorResponse(w, http.StatusInternalServerError, "Failed to get service name")
|
||||
return
|
||||
}
|
||||
|
||||
initType := GetInitSystem()
|
||||
serviceName := fmt.Sprintf("superfrpc_%s_%s", user.Username, instance.Name)
|
||||
|
||||
switch initType {
|
||||
case "windows":
|
||||
if err := StopWindowsService(serviceName); err != nil {
|
||||
@@ -957,16 +883,14 @@ func RestartInstanceHandler(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
user, err := GetUserByID(instance.UserID)
|
||||
initType := GetInitSystem()
|
||||
serviceName, err := GetServiceNameByInstanceID(instanceID)
|
||||
if err != nil {
|
||||
postLog.Error(fmt.Sprintf("[RestartInstanceHandler] Failed to get user info: %v", err))
|
||||
SendErrorResponse(w, http.StatusInternalServerError, "Failed to get user info")
|
||||
postLog.Error(fmt.Sprintf("[RestartInstanceHandler] Failed to get service name: %v", err))
|
||||
SendErrorResponse(w, http.StatusInternalServerError, "Failed to get service name")
|
||||
return
|
||||
}
|
||||
|
||||
initType := GetInitSystem()
|
||||
serviceName := fmt.Sprintf("superfrpc_%s_%s", user.Username, instance.Name)
|
||||
|
||||
switch initType {
|
||||
case "windows":
|
||||
if err := RestartWindowsService(serviceName); err != nil {
|
||||
@@ -1047,16 +971,14 @@ func GetInstanceStatusHandler(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
user, err := GetUserByID(instance.UserID)
|
||||
if err != nil {
|
||||
postLog.Error(fmt.Sprintf("[GetInstanceStatusHandler] Failed to get user info: %v", err))
|
||||
SendErrorResponse(w, http.StatusInternalServerError, "Failed to get user info")
|
||||
return
|
||||
}
|
||||
|
||||
initType := GetInitSystem()
|
||||
|
||||
serviceName := fmt.Sprintf("superfrpc_%s_%s", user.Username, instance.Name)
|
||||
serviceName, err := GetServiceNameByInstanceID(instanceID)
|
||||
if err != nil {
|
||||
postLog.Error(fmt.Sprintf("[GetInstanceStatusHandler] Failed to get service name: %v", err))
|
||||
SendErrorResponse(w, http.StatusInternalServerError, "Failed to get service name")
|
||||
return
|
||||
}
|
||||
|
||||
responseData := map[string]interface{}{
|
||||
"name": instance.Name,
|
||||
@@ -1065,7 +987,7 @@ func GetInstanceStatusHandler(w http.ResponseWriter, r *http.Request) {
|
||||
"isRunning": false,
|
||||
}
|
||||
|
||||
isRunning := IsInstanceRunning(instance.Name)
|
||||
isRunning := IsInstanceRunning(instanceID)
|
||||
responseData["isRunning"] = isRunning
|
||||
|
||||
SendSuccessResponse(w, "Instance status retrieved successfully", responseData)
|
||||
|
||||
Reference in New Issue
Block a user