refactor: reorganize codebase into modular packages

feat(global): add global package for shared variables and types
refactor(handlers): move handlers to dedicated package and update imports
refactor(session): extract session management to separate package
refactor(config): move config handling to dedicated package
refactor(router): update route handlers to use new package structure
refactor(main): simplify main.go by moving logic to packages
This commit is contained in:
2026-04-22 12:57:04 +08:00
parent df8df78bab
commit ddf91299e7
11 changed files with 667 additions and 663 deletions
+964
View File
@@ -0,0 +1,964 @@
package instance
import (
"database/sql"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"path/filepath"
"strconv"
"time"
"super-frpc/config"
"super-frpc/database"
"super-frpc/handlers"
"super-frpc/postLog"
"super-frpc/service"
)
func CreateInstanceHandler(w http.ResponseWriter, r *http.Request) {
userID, err := handlers.Auth(w, r, http.MethodPost, "superuser", "admin")
if err != nil {
handlers.SendErrorResponse(w, http.StatusUnauthorized, err.Error())
postLog.Warning(fmt.Sprintf("[CreateInstanceHandler] Auth failed: %v", err))
return
}
body, err := io.ReadAll(r.Body)
if err != nil {
postLog.Error(fmt.Sprintf("[CreateInstanceHandler] Failed to read request body: %v", err))
handlers.SendErrorResponse(w, http.StatusBadRequest, "Failed to read request body")
return
}
defer r.Body.Close()
var reqMap map[string]interface{}
if err := json.Unmarshal(body, &reqMap); err != nil {
postLog.Error(fmt.Sprintf("[CreateInstanceHandler] Failed to unmarshal request body: %v", err))
handlers.SendErrorResponse(w, http.StatusBadRequest, "Invalid request format")
return
}
bootAtStart := false
if bas, ok := reqMap["bootAtStart"]; ok {
switch v := bas.(type) {
case bool:
bootAtStart = v
case string:
if v == "true" {
bootAtStart = true
}
}
}
instanceInfoMap, ok := reqMap["instanceInfo"].(map[string]interface{})
if !ok {
handlers.SendErrorResponse(w, http.StatusBadRequest, "Invalid instanceInfo format")
return
}
instanceInfo := config.InstanceInfo{
Name: getStringFromMap(instanceInfoMap, "name"),
ServerAddr: getStringFromMap(instanceInfoMap, "serverAddr"),
ServerPort: getStringFromMap(instanceInfoMap, "serverPort"),
AuthMethod: getStringFromMap(instanceInfoMap, "auth_method"),
RunUser: getStringFromMap(instanceInfoMap, "runUser"),
}
if additional, ok := reqMap["additionalProperties"].(map[string]interface{}); ok {
instanceInfo.Additional = additional
}
req := config.CreateInstanceRequest{
InstanceInfo: instanceInfo,
BootAtStart: bootAtStart,
RunUser: instanceInfo.RunUser,
Additional: instanceInfo.Additional,
}
if req.InstanceInfo.Name == "" || req.InstanceInfo.ServerAddr == "" ||
req.InstanceInfo.ServerPort == "" || req.InstanceInfo.AuthMethod == "" {
handlers.SendErrorResponse(w, http.StatusBadRequest, "Missing required fields in instanceInfo")
return
}
runUser := req.RunUser
if runUser == "" {
runUser = "root"
}
user, err := database.GetUserByID(userID)
if err != nil {
postLog.Error(fmt.Sprintf("[CreateInstanceHandler] Failed to get user info: %v", err))
handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to get user info")
return
}
configDir, err := service.GetConfigDir()
if err != nil {
postLog.Error(fmt.Sprintf("[CreateInstanceHandler] Failed to get config directory: %v", err))
handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to get config directory")
return
}
configFileName := fmt.Sprintf("superfrpc_%s_%s.toml", user.Username, req.InstanceInfo.Name)
configPath := filepath.Join(configDir, configFileName)
setKeyTextWrapper := func(configPath, key, section, value string) error {
return config.SetKeyText(configPath, key, section, value, os.ReadFile, os.WriteFile)
}
if err := config.HandleConfigFileCreate(configPath, req.InstanceInfo, setKeyTextWrapper); err != nil {
postLog.Error(fmt.Sprintf("[CreateInstanceHandler] Failed to create config file %s: %v", configPath, err))
handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to create config file")
return
}
instance := database.FrpcInstance{
UserID: userID,
Name: req.InstanceInfo.Name,
BootAtStart: req.BootAtStart,
RunUser: runUser,
ConfigPath: configPath,
Watchdog: 0,
}
if err := database.DBAddFrpcInstance(instance); err != nil {
os.Remove(configPath)
postLog.Error(fmt.Sprintf("[CreateInstanceHandler] Failed to save instance %s to database: %v", req.InstanceInfo.Name, err))
handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to save instance to database")
return
}
createdInstance, err := database.DBQueryFrpcInstance(userID, req.InstanceInfo.Name)
if err != nil {
os.Remove(configPath)
postLog.Error(fmt.Sprintf("[CreateInstanceHandler] Failed to query created instance: %v", err))
handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to query created instance")
return
}
if err := service.CreateBootService(createdInstance.ID); err != nil {
database.DBRemoveFrpcInstanceByID(createdInstance.ID)
os.Remove(configPath)
postLog.Error(fmt.Sprintf("[CreateInstanceHandler] Failed to create boot service for instance %s: %v", req.InstanceInfo.Name, err))
handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to create boot service")
return
}
if req.BootAtStart {
if err := service.SetBootAtStart(createdInstance.ID); err != nil {
postLog.Error(fmt.Sprintf("[CreateInstanceHandler] Failed to set boot at start for instance %s: %v", req.InstanceInfo.Name, err))
handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to set boot at start")
return
}
}
handlers.SendSuccessResponse(w, "Instance created successfully", map[string]interface{}{
"name": req.InstanceInfo.Name,
"configPath": configPath,
"bootAtStart": req.BootAtStart,
})
postLog.Info(fmt.Sprintf("[CreateInstanceHandler] Instance %s created successfully: configPath=%s, bootAtStart=%v, runUser=%s, additionalProperties=%v", req.InstanceInfo.Name, configPath, req.BootAtStart, runUser, req.Additional))
}
func DeleteInstanceHandler(w http.ResponseWriter, r *http.Request) {
userID, err := handlers.Auth(w, r, http.MethodPost, "superuser", "admin")
if err != nil {
handlers.SendErrorResponse(w, http.StatusUnauthorized, err.Error())
postLog.Warning(fmt.Sprintf("[DeleteInstanceHandler] Auth failed: %v", err))
return
}
body, err := io.ReadAll(r.Body)
if err != nil {
postLog.Error(fmt.Sprintf("[DeleteInstanceHandler] Failed to read request body: %v", err))
handlers.SendErrorResponse(w, http.StatusBadRequest, "Failed to read request body")
return
}
defer r.Body.Close()
var reqMap map[string]interface{}
if err := json.Unmarshal(body, &reqMap); err != nil {
postLog.Error(fmt.Sprintf("[DeleteInstanceHandler] Failed to unmarshal request body: %v", err))
handlers.SendErrorResponse(w, http.StatusBadRequest, "Invalid request format")
return
}
instanceIDStr := getStringFromMap(reqMap, "instanceID")
if instanceIDStr == "" {
handlers.SendErrorResponse(w, http.StatusBadRequest, "instanceID is required")
return
}
instanceID, err := strconv.Atoi(instanceIDStr)
if err != nil {
handlers.SendErrorResponse(w, http.StatusBadRequest, "Invalid instanceID format")
return
}
instance, err := database.DBQueryFrpcInstanceByID(instanceID)
if err == sql.ErrNoRows {
handlers.SendErrorResponse(w, http.StatusNotFound, "Instance not found")
postLog.Error(fmt.Sprintf("[DeleteInstanceHandler] User %d tried to delete a not existed instance: %d", userID, instanceID))
return
}
if err != nil {
postLog.Error(fmt.Sprintf("[DeleteInstanceHandler] Failed to query instance: %v", err))
handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to query instance")
return
}
if err := service.RemoveBootService(instanceID); err != nil {
postLog.Error(fmt.Sprintf("[DeleteInstanceHandler] Failed to remove boot service for instance %s: %v", instance.Name, err))
handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to remove boot service")
return
}
if _, err := os.Stat(instance.ConfigPath); err == nil {
if err := os.Remove(instance.ConfigPath); err != nil {
postLog.Error(fmt.Sprintf("[DeleteInstanceHandler] Failed to remove config file %s: %v", instance.ConfigPath, err))
handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to remove config file")
return
}
}
if err := database.DBRemoveFrpcInstanceByID(instanceID); err != nil {
postLog.Error(fmt.Sprintf("[DeleteInstanceHandler] Failed to delete instance %d from database: %v", instanceID, err))
handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to delete instance from database")
return
}
handlers.SendSuccessResponse(w, "Instance deleted successfully", map[string]interface{}{
"id": instanceID,
})
postLog.Info(fmt.Sprintf("[DeleteInstanceHandler] Instance %d deleted successfully", instanceID))
}
func ModifyInstanceHandler(w http.ResponseWriter, r *http.Request) {
userID, err := handlers.Auth(w, r, http.MethodPost, "superuser", "admin")
if err != nil {
handlers.SendErrorResponse(w, http.StatusUnauthorized, err.Error())
postLog.Warning(fmt.Sprintf("[ModifyInstanceHandler] Auth failed: %v", err))
return
}
body, err := io.ReadAll(r.Body)
if err != nil {
postLog.Error(fmt.Sprintf("[ModifyInstanceHandler] Failed to read request body: %v", err))
handlers.SendErrorResponse(w, http.StatusBadRequest, "Failed to read request body")
return
}
defer r.Body.Close()
var reqMap map[string]interface{}
if err := json.Unmarshal(body, &reqMap); err != nil {
postLog.Error(fmt.Sprintf("[ModifyInstanceHandler] Failed to unmarshal request body: %v", err))
handlers.SendErrorResponse(w, http.StatusBadRequest, "Invalid request format")
return
}
instanceIDStr := getStringFromMap(reqMap, "instanceID")
if instanceIDStr == "" {
handlers.SendErrorResponse(w, http.StatusBadRequest, "instanceID is required")
return
}
instanceID, err := strconv.Atoi(instanceIDStr)
if err != nil {
handlers.SendErrorResponse(w, http.StatusBadRequest, "Invalid instanceID format")
return
}
modifyType := getStringFromMap(reqMap, "type")
if modifyType == "" {
handlers.SendErrorResponse(w, http.StatusBadRequest, "type is required")
return
}
if modifyType != "configFile" && modifyType != "systemConfig" {
handlers.SendErrorResponse(w, http.StatusBadRequest, fmt.Sprintf("Unknown modify type %s", modifyType))
return
}
modifiedData, ok := reqMap["modifiedData"].(map[string]interface{})
if !ok || modifiedData == nil {
handlers.SendErrorResponse(w, http.StatusBadRequest, "modifiedData is required and must be an object")
return
}
instance, err := database.DBQueryFrpcInstanceByID(instanceID)
if err == sql.ErrNoRows {
postLog.Error(fmt.Sprintf("[ModifyInstanceHandler] User %d tried to modify a not existed instance: %d", userID, instanceID))
handlers.SendErrorResponse(w, http.StatusNotFound, "Instance not found")
return
}
if err != nil {
postLog.Error(fmt.Sprintf("[ModifyInstanceHandler] Failed to query instance: %v", err))
handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to query instance")
return
}
if instance.UserID != userID {
postLog.Error(fmt.Sprintf("[ModifyInstanceHandler] User %d does not have permission to modify instance %d", userID, instanceID))
handlers.SendErrorResponse(w, http.StatusForbidden, "Permission denied")
return
}
user, err := database.GetUserByID(instance.UserID)
if err != nil {
postLog.Error(fmt.Sprintf("[ModifyInstanceHandler] Failed to get user info: %v", err))
handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to get user info")
return
}
if modifyType == "configFile" {
handleConfigFileModify(w, instance, modifiedData)
} else {
handleSystemConfigModify(w, r, instance, modifiedData, user)
}
}
func handleConfigFileModify(w http.ResponseWriter, instance database.FrpcInstance, modifiedData map[string]interface{}) {
configPath := instance.ConfigPath
for key, value := range modifiedData {
configKey := key
if key == "auth_method" {
configKey = "auth.method"
}
if key == "auth_token" {
configKey = "auth.token"
}
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 := config.SetKeyText(configPath, configKey, "", configValue, os.ReadFile, os.WriteFile); err != nil {
postLog.Error(fmt.Sprintf("[handleConfigFileModify] Failed to set key %s: %v", key, err))
handlers.SendErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Failed to set key %s", key))
return
}
}
handlers.SendSuccessResponse(w, "Config file modified successfully", map[string]interface{}{
"instanceName": instance.Name,
"instanceID": instance.ID,
"configPath": configPath,
})
postLog.Info(fmt.Sprintf("[handleConfigFileModify] Config file for instance %s modified successfully", instance.Name))
}
func handleSystemConfigModify(w http.ResponseWriter, r *http.Request, instance database.FrpcInstance, modifiedData map[string]interface{}, user *database.User) {
newName := instance.Name
newRunUser := instance.RunUser
newBootAtStart := instance.BootAtStart
var bootServiceError string
if v, ok := modifiedData["name"].(string); ok && v != "" {
newName = v
}
if v, ok := modifiedData["runUser"].(string); ok {
newRunUser = v
}
if v, ok := modifiedData["bootAtStart"].(bool); ok {
newBootAtStart = v
}
oldConfigPath := instance.ConfigPath
var newConfigPath string
if newName != instance.Name || newRunUser != instance.RunUser {
configDir, err := service.GetConfigDir()
if err != nil {
postLog.Error(fmt.Sprintf("[handleSystemConfigModify] Failed to get config directory: %v", err))
handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to get config directory")
return
}
newConfigFileName := fmt.Sprintf("superfrpc_%s_%s.toml", user.Username, newName)
newConfigPath = filepath.Join(configDir, newConfigFileName)
if oldConfigPath != newConfigPath {
if _, err := os.Stat(oldConfigPath); err == nil {
if err := os.Rename(oldConfigPath, newConfigPath); err != nil {
postLog.Error(fmt.Sprintf("[handleSystemConfigModify] Failed to rename config file %s to %s: %v", oldConfigPath, newConfigPath, err))
handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to rename config file")
return
}
}
}
} else {
newConfigPath = oldConfigPath
}
instance.RunUser = newRunUser
instance.BootAtStart = newBootAtStart
instance.ConfigPath = newConfigPath
if err := database.DBUpdateFrpcInstance(instance); err != nil {
postLog.Error(fmt.Sprintf("[handleSystemConfigModify] Failed to update instance in database: %v", err))
handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to update instance in database")
return
}
if newBootAtStart {
if err := service.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 := service.SetBootAtStart(instance.ID); err != nil {
postLog.Error(fmt.Sprintf("[handleSystemConfigModify] Failed to set boot at start: %v", err))
if bootServiceError != "" {
bootServiceError += "; "
}
bootServiceError += fmt.Sprintf("Failed to set boot at start: %v", err)
}
} else {
if err := service.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
}
instance.Name = newName
data := map[string]interface{}{
"instanceName": newName,
"instanceID": instance.ID,
"configPath": newConfigPath,
"bootAtStart": newBootAtStart,
"runUser": newRunUser,
}
if bootServiceError != "" {
data["bootServiceError"] = bootServiceError
}
handlers.SendSuccessResponse(w, "System config modified successfully", data)
}
func GetUserInstances(userID int) ([]database.FrpcInstance, error) {
rows, err := database.DBQueryUserInstances(userID)
if err != nil {
return nil, err
}
defer rows.Close()
var instances []database.FrpcInstance
for rows.Next() {
var instance database.FrpcInstance
var createdAtStr string
if err := rows.Scan(
&instance.ID, &instance.UserID, &instance.Name, &instance.BootAtStart, &instance.RunUser, &instance.ConfigPath, &createdAtStr, &instance.Watchdog,
); err != nil {
return nil, err
}
instance.CreatedAt, _ = time.Parse(time.RFC3339, createdAtStr)
instances = append(instances, instance)
}
return instances, nil
}
func getStringFromMap(m map[string]interface{}, key string) string {
if v, ok := m[key].(string); ok {
return v
}
return ""
}
func getNumFromMap(m map[string]interface{}, key string) int {
if v, ok := m[key].(int); ok {
return v
}
if v, ok := m[key].(float64); ok {
return int(v)
}
return 0
}
func ListInstancesHandler(w http.ResponseWriter, r *http.Request) {
userID, err := handlers.Auth(w, r, http.MethodGet)
if err != nil {
handlers.SendErrorResponse(w, http.StatusUnauthorized, err.Error())
postLog.Warning(fmt.Sprintf("[ListInstancesHandler] Auth failed: %v", err))
return
}
instances, err := GetUserInstances(userID)
if err != nil {
postLog.Error(fmt.Sprintf("[ListInstancesHandler] Failed to get user instances: %v", err))
handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to get instances")
return
}
instanceList := make([]map[string]interface{}, len(instances))
for i, inst := range instances {
instanceData := map[string]interface{}{
"instanceID": inst.ID,
"name": inst.Name,
"createdAt": inst.CreatedAt,
"createdBy": inst.CreatedBy,
"isRunning": false,
}
err = service.IsInstanceRunning(inst.ID)
if err != nil {
instanceData["isRunning"] = false
} else {
instanceData["isRunning"] = true
}
instanceList[i] = instanceData
}
handlers.SendSuccessResponse(w, "Instances retrieved successfully", instanceList)
postLog.Info(fmt.Sprintf("[ListInstancesHandler] Retrieved %d instances for user %d", len(instances), userID))
}
func StartInstanceHandler(w http.ResponseWriter, r *http.Request) {
userID, err := handlers.Auth(w, r, http.MethodPost, "superuser", "admin")
if err != nil {
handlers.SendErrorResponse(w, http.StatusUnauthorized, err.Error())
postLog.Warning(fmt.Sprintf("[StartInstanceHandler] Auth failed: %v", err))
return
}
body, err := io.ReadAll(r.Body)
if err != nil {
postLog.Error(fmt.Sprintf("[StartInstanceHandler] Failed to read request body: %v", err))
handlers.SendErrorResponse(w, http.StatusBadRequest, "Failed to read request body")
return
}
defer r.Body.Close()
var reqMap map[string]interface{}
if err := json.Unmarshal(body, &reqMap); err != nil {
postLog.Error(fmt.Sprintf("[StartInstanceHandler] Failed to unmarshal request body: %v", err))
handlers.SendErrorResponse(w, http.StatusBadRequest, "Invalid request format")
return
}
instanceIDStr := getStringFromMap(reqMap, "instanceID")
if instanceIDStr == "" {
handlers.SendErrorResponse(w, http.StatusBadRequest, "instanceID is required")
return
}
instanceID, err := strconv.Atoi(instanceIDStr)
if err != nil {
handlers.SendErrorResponse(w, http.StatusBadRequest, "Invalid instanceID format")
return
}
instance, err := database.DBQueryFrpcInstanceByID(instanceID)
if err == sql.ErrNoRows {
handlers.SendErrorResponse(w, http.StatusNotFound, "Instance not found")
postLog.Error(fmt.Sprintf("[StartInstanceHandler] User %d tried to start a not existed instance: %d", userID, instanceID))
return
}
if err != nil {
postLog.Error(fmt.Sprintf("[StartInstanceHandler] Failed to query instance: %v", err))
handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to query instance")
return
}
if instance.UserID != userID {
handlers.SendErrorResponse(w, http.StatusForbidden, "Instance not found")
postLog.Error(fmt.Sprintf("[StartInstanceHandler] User %d tried to start instance %d that does not belong to them", userID, instanceID))
return
}
initType := service.GetInitSystem()
serviceName, err := database.GetServiceNameByInstanceID(instanceID)
if err != nil {
postLog.Error(fmt.Sprintf("[StartInstanceHandler] Failed to get service name: %v", err))
handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to get service name")
return
}
switch initType {
case "windows":
if err := service.StartWindowsService(serviceName); err != nil {
postLog.Error(fmt.Sprintf("[StartInstanceHandler] Failed to start Windows service %s: %v", serviceName, err))
handlers.SendErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Failed to start Windows service: %v", err))
return
}
postLog.Debug(fmt.Sprintf("[StartInstanceHandler] Windows service %s started successfully", serviceName))
case "systemd":
if err := service.StartSystemdService(serviceName); err != nil {
postLog.Error(fmt.Sprintf("[StartInstanceHandler] Failed to start systemd service %s: %v", serviceName, err))
handlers.SendErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Failed to start systemd service: %v", err))
return
}
postLog.Debug(fmt.Sprintf("[StartInstanceHandler] Systemd service %s started successfully", serviceName))
case "init.d":
if err := service.StartInitDService(serviceName); err != nil {
postLog.Error(fmt.Sprintf("[StartInstanceHandler] Failed to start init.d service %s: %v", serviceName, err))
handlers.SendErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Failed to start init.d service: %v", err))
return
}
postLog.Debug(fmt.Sprintf("[StartInstanceHandler] Init.d service %s started successfully", serviceName))
default:
handlers.SendErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Unsupported init system: %s", initType))
return
}
handlers.SendSuccessResponse(w, "Instance started successfully", map[string]interface{}{
"instanceID": instanceID,
"serviceName": serviceName,
})
postLog.Info(fmt.Sprintf("[StartInstanceHandler] Instance %d started successfully", instanceID))
}
func StopInstanceHandler(w http.ResponseWriter, r *http.Request) {
userID, err := handlers.Auth(w, r, http.MethodPost, "superuser", "admin")
if err != nil {
handlers.SendErrorResponse(w, http.StatusUnauthorized, err.Error())
postLog.Warning(fmt.Sprintf("[StopInstanceHandler] Auth failed: %v", err))
return
}
body, err := io.ReadAll(r.Body)
if err != nil {
postLog.Error(fmt.Sprintf("[StopInstanceHandler] Failed to read request body: %v", err))
handlers.SendErrorResponse(w, http.StatusBadRequest, "Failed to read request body")
return
}
defer r.Body.Close()
var reqMap map[string]interface{}
if err := json.Unmarshal(body, &reqMap); err != nil {
postLog.Error(fmt.Sprintf("[StopInstanceHandler] Failed to unmarshal request body: %v", err))
handlers.SendErrorResponse(w, http.StatusBadRequest, "Invalid request format")
return
}
instanceIDStr := getStringFromMap(reqMap, "instanceID")
if instanceIDStr == "" {
handlers.SendErrorResponse(w, http.StatusBadRequest, "instanceID is required")
return
}
instanceID, err := strconv.Atoi(instanceIDStr)
if err != nil {
handlers.SendErrorResponse(w, http.StatusBadRequest, "Invalid instanceID format")
return
}
instance, err := database.DBQueryFrpcInstanceByID(instanceID)
if err == sql.ErrNoRows {
handlers.SendErrorResponse(w, http.StatusNotFound, "Instance not found")
postLog.Error(fmt.Sprintf("[StopInstanceHandler] User %d tried to stop a not existed instance: %d", userID, instanceID))
return
}
if err != nil {
postLog.Error(fmt.Sprintf("[StopInstanceHandler] Failed to query instance: %v", err))
handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to query instance")
return
}
if instance.UserID != userID {
handlers.SendErrorResponse(w, http.StatusForbidden, "Instance not found")
postLog.Error(fmt.Sprintf("[StopInstanceHandler] User %d tried to stop instance %d that does not belong to them", userID, instanceID))
return
}
initType := service.GetInitSystem()
serviceName, err := database.GetServiceNameByInstanceID(instanceID)
if err != nil {
postLog.Error(fmt.Sprintf("[StopInstanceHandler] Failed to get service name: %v", err))
handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to get service name")
return
}
switch initType {
case "windows":
if err := service.StopWindowsService(serviceName); err != nil {
postLog.Error(fmt.Sprintf("[StopInstanceHandler] Failed to stop Windows service %s: %v", serviceName, err))
handlers.SendErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Failed to stop Windows service: %v", err))
return
}
postLog.Debug(fmt.Sprintf("[StopInstanceHandler] Windows service %s stopped successfully", serviceName))
case "systemd":
if err := service.StopSystemdService(serviceName); err != nil {
postLog.Error(fmt.Sprintf("[StopInstanceHandler] Failed to stop systemd service %s: %v", serviceName, err))
handlers.SendErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Failed to stop systemd service: %v", err))
return
}
postLog.Debug(fmt.Sprintf("[StopInstanceHandler] Systemd service %s stopped successfully", serviceName))
case "init.d":
if err := service.StopInitDService(serviceName); err != nil {
postLog.Error(fmt.Sprintf("[StopInstanceHandler] Failed to stop init.d service %s: %v", serviceName, err))
handlers.SendErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Failed to stop init.d service: %v", err))
return
}
postLog.Debug(fmt.Sprintf("[StopInstanceHandler] Init.d service %s stopped successfully", serviceName))
default:
handlers.SendErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Unsupported init system: %s", initType))
return
}
handlers.SendSuccessResponse(w, "Instance stopped successfully", map[string]interface{}{
"instanceID": instanceID,
"serviceName": serviceName,
})
postLog.Info(fmt.Sprintf("[StopInstanceHandler] Instance %d stopped successfully", instanceID))
}
func RestartInstanceHandler(w http.ResponseWriter, r *http.Request) {
userID, err := handlers.Auth(w, r, http.MethodPost, "superuser", "admin")
if err != nil {
handlers.SendErrorResponse(w, http.StatusUnauthorized, err.Error())
postLog.Warning(fmt.Sprintf("[RestartInstanceHandler] Auth failed: %v", err))
return
}
body, err := io.ReadAll(r.Body)
if err != nil {
postLog.Error(fmt.Sprintf("[RestartInstanceHandler] Failed to read request body: %v", err))
handlers.SendErrorResponse(w, http.StatusBadRequest, "Failed to read request body")
return
}
defer r.Body.Close()
var reqMap map[string]interface{}
if err := json.Unmarshal(body, &reqMap); err != nil {
postLog.Error(fmt.Sprintf("[RestartInstanceHandler] Failed to unmarshal request body: %v", err))
handlers.SendErrorResponse(w, http.StatusBadRequest, "Invalid request format")
return
}
instanceIDStr := getStringFromMap(reqMap, "instanceID")
if instanceIDStr == "" {
handlers.SendErrorResponse(w, http.StatusBadRequest, "instanceID is required")
return
}
instanceID, err := strconv.Atoi(instanceIDStr)
if err != nil {
handlers.SendErrorResponse(w, http.StatusBadRequest, "Invalid instanceID format")
return
}
instance, err := database.DBQueryFrpcInstanceByID(instanceID)
if err == sql.ErrNoRows {
handlers.SendErrorResponse(w, http.StatusNotFound, "Instance not found")
postLog.Error(fmt.Sprintf("[RestartInstanceHandler] User %d tried to restart a not existed instance: %d", userID, instanceID))
return
}
if err != nil {
postLog.Error(fmt.Sprintf("[RestartInstanceHandler] Failed to query instance: %v", err))
handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to query instance")
return
}
if instance.UserID != userID {
handlers.SendErrorResponse(w, http.StatusForbidden, "Instance not found")
postLog.Error(fmt.Sprintf("[RestartInstanceHandler] User %d tried to restart instance %d that does not belong to them", userID, instanceID))
return
}
initType := service.GetInitSystem()
serviceName, err := database.GetServiceNameByInstanceID(instanceID)
if err != nil {
postLog.Error(fmt.Sprintf("[RestartInstanceHandler] Failed to get service name: %v", err))
handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to get service name")
return
}
switch initType {
case "windows":
if err := service.RestartWindowsService(serviceName); err != nil {
postLog.Error(fmt.Sprintf("[RestartInstanceHandler] Failed to restart Windows service %s: %v", serviceName, err))
handlers.SendErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Failed to restart Windows service: %v", err))
return
}
postLog.Debug(fmt.Sprintf("[RestartInstanceHandler] Windows service %s restarted successfully", serviceName))
case "systemd":
if err := service.RestartSystemdService(serviceName); err != nil {
postLog.Error(fmt.Sprintf("[RestartInstanceHandler] Failed to restart systemd service %s: %v", serviceName, err))
handlers.SendErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Failed to restart systemd service: %v", err))
return
}
postLog.Debug(fmt.Sprintf("[RestartInstanceHandler] Systemd service %s restarted successfully", serviceName))
case "init.d":
if err := service.RestartInitDService(serviceName); err != nil {
postLog.Error(fmt.Sprintf("[RestartInstanceHandler] Failed to restart init.d service %s: %v", serviceName, err))
handlers.SendErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Failed to restart init.d service: %v", err))
return
}
postLog.Debug(fmt.Sprintf("[RestartInstanceHandler] Init.d service %s restarted successfully", serviceName))
default:
handlers.SendErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Unsupported init system: %s", initType))
return
}
handlers.SendSuccessResponse(w, "Instance restarted successfully", map[string]interface{}{
"instanceID": instanceID,
"serviceName": serviceName,
})
postLog.Info(fmt.Sprintf("[RestartInstanceHandler] Instance %d restarted successfully", instanceID))
}
func GetInstanceStatusHandler(w http.ResponseWriter, r *http.Request) {
userID, err := handlers.Auth(w, r, http.MethodGet)
if err != nil {
handlers.SendErrorResponse(w, http.StatusUnauthorized, err.Error())
postLog.Warning(fmt.Sprintf("[GetInstanceStatusHandler] Auth failed: %v", err))
return
}
queryParams := r.URL.Query()
instanceIDStr := queryParams.Get("instanceID")
if instanceIDStr == "" {
handlers.SendErrorResponse(w, http.StatusBadRequest, "instanceID is required")
return
}
instanceID, err := strconv.Atoi(instanceIDStr)
if err != nil {
handlers.SendErrorResponse(w, http.StatusBadRequest, "Invalid instanceID format")
return
}
instance, err := database.DBQueryFrpcInstanceByID(instanceID)
if err == sql.ErrNoRows {
handlers.SendErrorResponse(w, http.StatusNotFound, "Instance not found")
return
}
if err != nil {
postLog.Error(fmt.Sprintf("[GetInstanceStatusHandler] Failed to query instance: %v", err))
handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to query instance")
return
}
if instance.UserID != userID {
handlers.SendErrorResponse(w, http.StatusForbidden, "Instance not found")
return
}
err = service.IsInstanceRunning(instanceID)
isRunning := err == nil
handlers.SendSuccessResponse(w, "Instance status retrieved successfully", map[string]interface{}{
"instanceID": instanceID,
"isRunning": isRunning,
})
}
func GetInstanceInfoHandler(w http.ResponseWriter, r *http.Request) {
userID, err := handlers.Auth(w, r, http.MethodGet)
if err != nil {
handlers.SendErrorResponse(w, http.StatusUnauthorized, err.Error())
postLog.Warning(fmt.Sprintf("[GetInstanceInfoHandler] Auth failed: %v", err))
return
}
queryParams := r.URL.Query()
instanceIDStr := queryParams.Get("instanceID")
if instanceIDStr == "" {
handlers.SendErrorResponse(w, http.StatusBadRequest, "instanceID is required")
return
}
instanceID, err := strconv.Atoi(instanceIDStr)
if err != nil {
handlers.SendErrorResponse(w, http.StatusBadRequest, "Invalid instanceID format")
return
}
instance, err := database.DBQueryFrpcInstanceByID(instanceID)
if err == sql.ErrNoRows {
handlers.SendErrorResponse(w, http.StatusNotFound, "Instance not found")
return
}
if err != nil {
postLog.Error(fmt.Sprintf("[GetInstanceInfoHandler] Failed to query instance: %v", err))
handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to query instance")
return
}
if instance.UserID != userID {
handlers.SendErrorResponse(w, http.StatusForbidden, "Instance not found")
return
}
user, err := database.GetUserByID(userID)
if err != nil {
postLog.Error(fmt.Sprintf("[GetInstanceInfoHandler] Failed to get user info: %v", err))
handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to get user info")
return
}
serviceName, err := database.GetServiceNameByInstanceID(instanceID)
if err != nil {
postLog.Error(fmt.Sprintf("[GetInstanceInfoHandler] Failed to get service name: %v", err))
handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to get service name")
return
}
configContent, err := os.ReadFile(instance.ConfigPath)
if err != nil {
postLog.Error(fmt.Sprintf("[GetInstanceInfoHandler] Failed to read config file: %v", err))
handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to read config file")
return
}
frpcConfig, err := config.DecodeFrpcConfig(string(configContent))
if err != nil {
postLog.Error(fmt.Sprintf("[GetInstanceInfoHandler] Failed to decode frpc config: %v", err))
handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to decode frpc config")
return
}
err = service.IsInstanceRunning(instanceID)
isRunning := err == nil
response := map[string]interface{}{
"name": instance.Name,
"serviceName": serviceName,
"createdAt": instance.CreatedAt.Format(time.RFC3339),
"createdBy": instance.CreatedBy,
"isRunning": isRunning,
"auth_method": frpcConfig.Global["auth.method"],
"bootAtStart": instance.BootAtStart,
"runUser": instance.RunUser,
"configPath": instance.ConfigPath,
}
if user.Type == "admin" || user.Type == "superuser" {
response["serverAddr"] = frpcConfig.Global["serverAddr"]
response["serverPort"] = frpcConfig.Global["serverPort"]
}
handlers.SendSuccessResponse(w, "Instance info retrieved successfully", response)
}
+352
View File
@@ -0,0 +1,352 @@
package instance
import (
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"strconv"
"super-frpc/config"
"super-frpc/database"
"super-frpc/handlers"
"super-frpc/postLog"
"github.com/BurntSushi/toml"
)
func CreateProxyHandler(w http.ResponseWriter, r *http.Request) {
userID, err := handlers.Auth(w, r, http.MethodPost, "superuser", "admin")
if err != nil {
handlers.SendErrorResponse(w, http.StatusUnauthorized, err.Error())
postLog.Warning(fmt.Sprintf("[CreateProxyHandler] Auth failed: %v", err))
return
}
body, err := io.ReadAll(r.Body)
if err != nil {
postLog.Error(fmt.Sprintf("[CreateProxyHandler] Failed to read request body: %v", err))
handlers.SendErrorResponse(w, http.StatusBadRequest, "Failed to read request body")
return
}
defer r.Body.Close()
var reqMap map[string]interface{}
if err := json.Unmarshal(body, &reqMap); err != nil {
postLog.Error(fmt.Sprintf("[CreateProxyHandler] Failed to unmarshal request body: %v", err))
handlers.SendErrorResponse(w, http.StatusBadRequest, "Invalid request format")
return
}
instanceID := getStringFromMap(reqMap, "instanceID")
if instanceID == "" {
postLog.Error("[CreateProxyHandler] instanceID is required")
handlers.SendErrorResponse(w, http.StatusBadRequest, "instanceID is required")
return
}
proxyInfoMap, ok := reqMap["proxyInfo"].(map[string]interface{})
if !ok {
postLog.Error("[CreateProxyHandler] Invalid proxyInfo format")
handlers.SendErrorResponse(w, http.StatusBadRequest, "Invalid proxyInfo format")
return
}
proxyInfo := config.FrpcProxyInfo{
Name: getStringFromMap(proxyInfoMap, "name"),
Type: getStringFromMap(proxyInfoMap, "type"),
LocalIP: getStringFromMap(proxyInfoMap, "localIP"),
LocalPort: getNumFromMap(proxyInfoMap, "localPort"),
RemotePort: getNumFromMap(proxyInfoMap, "remotePort"),
}
if proxyInfo.Name == "" || proxyInfo.Type == "" || proxyInfo.LocalIP == "" ||
proxyInfo.LocalPort == 0 || proxyInfo.RemotePort == 0 {
postLog.Error("[CreateProxyHandler] Missing required fields in proxyInfo")
handlers.SendErrorResponse(w, http.StatusBadRequest, "Missing required fields in proxyInfo")
return
}
var instance database.FrpcInstance
instanceIDInt, _ := strconv.Atoi(instanceID)
instance, err = database.DBQueryFrpcInstanceByID(instanceIDInt)
if err != nil {
postLog.Error(fmt.Sprintf("[CreateProxyHandler] Failed to query instance: %v", err))
handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to query instance")
return
}
if instance.UserID != userID {
postLog.Error(fmt.Sprintf("[CreateProxyHandler] Instance not found for user %d", userID))
handlers.SendErrorResponse(w, http.StatusNotFound, "Instance not found")
return
}
configContent, err := os.ReadFile(instance.ConfigPath)
if err != nil {
postLog.Error(fmt.Sprintf("[CreateProxyHandler] Failed to read config file %s: %v", instance.ConfigPath, err))
handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to read config file")
return
}
updatedContent, err := config.AddFrpcProxy(string(configContent), proxyInfo)
if err != nil {
postLog.Error(fmt.Sprintf("[CreateProxyHandler] Failed to add proxy: %v", err))
handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to add proxy")
return
}
if err := os.WriteFile(instance.ConfigPath, []byte(updatedContent), 0644); err != nil {
postLog.Error(fmt.Sprintf("[CreateProxyHandler] Failed to write config file %s: %v", instance.ConfigPath, err))
handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to write config file")
return
}
handlers.SendSuccessResponse(w, "Proxy created successfully", map[string]interface{}{
"instanceID": instance.ID,
"configPath": instance.ConfigPath,
"proxyName": proxyInfo.Name,
})
postLog.Info(fmt.Sprintf("[CreateProxyHandler] Proxy %s created successfully for instance %d", proxyInfo.Name, instance.ID))
}
func ModifyProxyHandler(w http.ResponseWriter, r *http.Request) {
userID, err := handlers.Auth(w, r, http.MethodPost, "superuser", "admin")
if err != nil {
handlers.SendErrorResponse(w, http.StatusUnauthorized, err.Error())
postLog.Warning(fmt.Sprintf("[ModifyProxyHandler] Auth failed: %v", err))
return
}
body, err := io.ReadAll(r.Body)
if err != nil {
postLog.Error(fmt.Sprintf("[ModifyProxyHandler] Failed to read request body: %v", err))
handlers.SendErrorResponse(w, http.StatusBadRequest, "Failed to read request body")
return
}
defer r.Body.Close()
var reqMap map[string]interface{}
if err := json.Unmarshal(body, &reqMap); err != nil {
postLog.Error(fmt.Sprintf("[ModifyProxyHandler] Failed to unmarshal request body: %v", err))
handlers.SendErrorResponse(w, http.StatusBadRequest, "Invalid request format")
return
}
instanceID := getStringFromMap(reqMap, "instanceID")
if instanceID == "" {
postLog.Error("[ModifyProxyHandler] instanceID is required")
handlers.SendErrorResponse(w, http.StatusBadRequest, "instanceID is required")
return
}
proxyInfoMap, ok := reqMap["proxyInfo"].(map[string]interface{})
if !ok {
postLog.Error("[ModifyProxyHandler] Invalid proxyInfo format")
handlers.SendErrorResponse(w, http.StatusBadRequest, "Invalid proxyInfo format")
return
}
proxyInfo := config.FrpcProxyInfo{
Name: getStringFromMap(proxyInfoMap, "name"),
Type: getStringFromMap(proxyInfoMap, "type"),
LocalIP: getStringFromMap(proxyInfoMap, "localIP"),
LocalPort: getNumFromMap(proxyInfoMap, "localPort"),
RemotePort: getNumFromMap(proxyInfoMap, "remotePort"),
}
if proxyInfo.Name == "" || proxyInfo.Type == "" || proxyInfo.LocalIP == "" ||
proxyInfo.LocalPort == 0 || proxyInfo.RemotePort == 0 {
postLog.Error("[ModifyProxyHandler] Missing required fields in proxyInfo")
handlers.SendErrorResponse(w, http.StatusBadRequest, "Missing required fields in proxyInfo")
return
}
var instance database.FrpcInstance
instanceIDInt, _ := strconv.Atoi(instanceID)
instance, err = database.DBQueryFrpcInstanceByID(instanceIDInt)
if err != nil {
postLog.Error(fmt.Sprintf("[ModifyProxyHandler] Failed to query instance: %v", err))
handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to query instance")
return
}
if instance.UserID != userID {
postLog.Error(fmt.Sprintf("[ModifyProxyHandler] Instance not found for user %d", userID))
handlers.SendErrorResponse(w, http.StatusNotFound, "Instance not found")
return
}
configContent, err := os.ReadFile(instance.ConfigPath)
if err != nil {
postLog.Error(fmt.Sprintf("[ModifyProxyHandler] Failed to read config file %s: %v", instance.ConfigPath, err))
handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to read config file")
return
}
updatedContent, err := config.ModifyFrpcProxy(string(configContent), proxyInfo)
if err != nil {
postLog.Error(fmt.Sprintf("[ModifyProxyHandler] Failed to modify proxy: %v", err))
handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to modify proxy")
return
}
if err := os.WriteFile(instance.ConfigPath, []byte(updatedContent), 0644); err != nil {
postLog.Error(fmt.Sprintf("[ModifyProxyHandler] Failed to write config file %s: %v", instance.ConfigPath, err))
handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to write config file")
return
}
handlers.SendSuccessResponse(w, "Proxy modified successfully", map[string]interface{}{
"instanceID": instance.ID,
"configPath": instance.ConfigPath,
"proxyName": proxyInfo.Name,
})
postLog.Info(fmt.Sprintf("[ModifyProxyHandler] Proxy %s modified successfully for instance %d", proxyInfo.Name, instance.ID))
}
func DeleteProxyHandler(w http.ResponseWriter, r *http.Request) {
userID, err := handlers.Auth(w, r, http.MethodPost, "superuser", "admin")
if err != nil {
handlers.SendErrorResponse(w, http.StatusUnauthorized, err.Error())
postLog.Warning(fmt.Sprintf("[DeleteProxyHandler] Auth failed: %v", err))
return
}
body, err := io.ReadAll(r.Body)
if err != nil {
postLog.Error(fmt.Sprintf("[DeleteProxyHandler] Failed to read request body: %v", err))
handlers.SendErrorResponse(w, http.StatusBadRequest, "Failed to read request body")
return
}
defer r.Body.Close()
var reqMap map[string]interface{}
if err := json.Unmarshal(body, &reqMap); err != nil {
postLog.Error(fmt.Sprintf("[DeleteProxyHandler] Failed to unmarshal request body: %v", err))
handlers.SendErrorResponse(w, http.StatusBadRequest, "Invalid request format")
return
}
instanceID := getStringFromMap(reqMap, "instanceID")
if instanceID == "" {
postLog.Error("[DeleteProxyHandler] instanceID is required")
handlers.SendErrorResponse(w, http.StatusBadRequest, "instanceID is required")
return
}
proxyName := getStringFromMap(reqMap, "proxyName")
if proxyName == "" {
postLog.Error("[DeleteProxyHandler] proxyName is required")
handlers.SendErrorResponse(w, http.StatusBadRequest, "proxyName is required")
return
}
var instance database.FrpcInstance
instanceIDInt, _ := strconv.Atoi(instanceID)
instance, err = database.DBQueryFrpcInstanceByID(instanceIDInt)
if err != nil {
postLog.Error(fmt.Sprintf("[DeleteProxyHandler] Failed to query instance: %v", err))
handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to query instance")
return
}
if instance.UserID != userID {
postLog.Error(fmt.Sprintf("[DeleteProxyHandler] Instance not found for user %d", userID))
handlers.SendErrorResponse(w, http.StatusNotFound, "Instance not found")
return
}
configContent, err := os.ReadFile(instance.ConfigPath)
if err != nil {
postLog.Error(fmt.Sprintf("[DeleteProxyHandler] Failed to read config file %s: %v", instance.ConfigPath, err))
handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to read config file")
return
}
updatedContent, err := config.RemoveFrpcProxy(string(configContent), proxyName)
if err != nil {
postLog.Error(fmt.Sprintf("[DeleteProxyHandler] Failed to remove proxy: %v", err))
handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to remove proxy")
return
}
if err := os.WriteFile(instance.ConfigPath, []byte(updatedContent), 0644); err != nil {
postLog.Error(fmt.Sprintf("[DeleteProxyHandler] Failed to write config file %s: %v", instance.ConfigPath, err))
handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to write config file")
return
}
handlers.SendSuccessResponse(w, "Proxy deleted successfully", map[string]interface{}{
"instanceID": instance.ID,
"configPath": instance.ConfigPath,
"proxyName": proxyName,
})
postLog.Info(fmt.Sprintf("[DeleteProxyHandler] Proxy %s deleted successfully from instance %d", proxyName, instance.ID))
}
func ListProxiesHandler(w http.ResponseWriter, r *http.Request) {
userID, err := handlers.Auth(w, r, http.MethodGet)
if err != nil {
handlers.SendErrorResponse(w, http.StatusUnauthorized, err.Error())
postLog.Warning(fmt.Sprintf("[ListProxiesHandler] Auth failed: %v", err))
return
}
queryParams := r.URL.Query()
instanceID := queryParams.Get("instanceID")
if instanceID == "" {
postLog.Error("[ListProxiesHandler] instanceID is required")
handlers.SendErrorResponse(w, http.StatusBadRequest, "instanceID is required")
return
}
var instance database.FrpcInstance
instanceIDInt, _ := strconv.Atoi(instanceID)
instance, err = database.DBQueryFrpcInstanceByID(instanceIDInt)
if err != nil {
postLog.Error(fmt.Sprintf("[ListProxiesHandler] Failed to query instance: %v", err))
handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to query instance")
return
}
if instance.UserID != userID {
postLog.Error(fmt.Sprintf("[ListProxiesHandler] Instance not found for user %d", userID))
handlers.SendErrorResponse(w, http.StatusNotFound, "Instance not found")
return
}
configContent, err := os.ReadFile(instance.ConfigPath)
if err != nil {
postLog.Error(fmt.Sprintf("[ListProxiesHandler] Failed to read config file %s: %v", instance.ConfigPath, err))
handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to read config file")
return
}
var cfg config.FrpcConfig
if _, err := toml.Decode(string(configContent), &cfg); err != nil {
postLog.Error(fmt.Sprintf("[ListProxiesHandler] Failed to parse config: %v", err))
handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to parse config file")
return
}
proxyList := make([]map[string]interface{}, len(cfg.Proxies))
for i, proxy := range cfg.Proxies {
proxyData := map[string]interface{}{
"name": proxy["name"],
"type": proxy["type"],
"localIP": proxy["localIP"],
"localPort": proxy["localPort"],
"remotePort": proxy["remotePort"],
}
proxyList[i] = proxyData
}
handlers.SendSuccessResponse(w, "Proxies listed successfully", map[string]interface{}{
"instanceID": instance.ID,
"proxyCount": len(proxyList),
"proxies": proxyList,
})
postLog.Info(fmt.Sprintf("[ListProxiesHandler] Retrieved %d proxies for instance %d", len(proxyList), instance.ID))
}