diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..608d3c6 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,15 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Launch Package", + "type": "go", + "request": "launch", + "mode": "auto", + "program": "${fileDirname}" + } + ] +} \ No newline at end of file diff --git a/README.md b/README.md index d90cbdb..b17e50d 100644 --- a/README.md +++ b/README.md @@ -355,6 +355,33 @@ Modify system-level settings such as instance name, boot-at-start configuration, } ``` +**Response with bootServiceError (when boot service operations fail):** +```json +{ + "success": true, + "message": "System config modified successfully", + "data": { + "instanceName": "new_frpc_name", + "instanceID": 1, + "configPath": "./configs/superfrpc_user_new_frpc_name.toml", + "bootAtStart": true, + "runUser": "www-data", + "bootServiceError": "Failed to create boot service: unsupported init system" + } +} +``` + +> **Note about bootServiceError:** +> - The `bootServiceError` field appears in the response when boot service operations (create/remove) fail +> - This field is only present when there is an error; it will not be included in the response if operations succeed +> - The overall request still returns `success: true` because the system config modification (database update, config file rename, etc.) succeeded +> - Common scenarios where `bootServiceError` may appear: +> - Enabling `bootAtStart` when the init system is not supported (e.g., on Windows or systems without systemd/init.d) +> - Disabling `bootAtStart` when the service file cannot be removed due to permission issues +> - Changing instance name or run user when `bootAtStart` is enabled (requires removing old service and creating new one) +> - The error message provides detailed information about what went wrong, which should be displayed to the user +> - Frontend applications should check for this field and display the error message to the user, even though the request was technically successful + --- ### 6. List frpc Instances diff --git a/database.db b/database.db index 48bc049..32f8d0c 100644 Binary files a/database.db and b/database.db differ diff --git a/frpc.go b/frpc.go index 3cde13c..c3876fa 100644 --- a/frpc.go +++ b/frpc.go @@ -350,7 +350,7 @@ func ModifyInstanceHandler(w http.ResponseWriter, r *http.Request) { } if modifyType != "configFile" && modifyType != "systemConfig" { // Detect valid modify type - SendErrorResponse(w, http.StatusBadRequest, "type must be 'configFile' or 'systemConfig'") + SendErrorResponse(w, http.StatusBadRequest, fmt.Sprintf("Unknown modify type %s", modifyType)) return } @@ -501,6 +501,7 @@ func handleSystemConfigModify(w http.ResponseWriter, r *http.Request, instance F newRunUser := instance.RunUser newBootAtStart := instance.BootAtStart oldBootAtStart := instance.BootAtStart + var bootServiceError string if v, ok := modifiedData["name"].(string); ok && v != "" { newName = v @@ -556,27 +557,38 @@ func handleSystemConfigModify(w http.ResponseWriter, r *http.Request, instance F if oldBootAtStart && !newBootAtStart { if err := removeBootService(user.Username, instance.Name); err != nil { postLog.Error(fmt.Sprintf("[handleSystemConfigModify] Failed to remove boot service: %v", err)) + bootServiceError = fmt.Sprintf("Failed to remove boot service: %v", err) } } else if !oldBootAtStart && newBootAtStart { if err := createBootService(user.Username, newName, newConfigPath, newRunUser); err != nil { postLog.Error(fmt.Sprintf("[handleSystemConfigModify] Failed to create boot service: %v", err)) + bootServiceError = fmt.Sprintf("Failed to create boot service: %v", err) } } else if oldBootAtStart && newBootAtStart && (instance.Name != newName || instance.RunUser != newRunUser) { if err := removeBootService(user.Username, instance.Name); err != nil { postLog.Error(fmt.Sprintf("[handleSystemConfigModify] Failed to remove old boot service: %v", err)) + bootServiceError = fmt.Sprintf("Failed to remove old boot service: %v", err) } if err := createBootService(user.Username, newName, newConfigPath, newRunUser); err != nil { postLog.Error(fmt.Sprintf("[handleSystemConfigModify] Failed to create new boot service: %v", err)) + if bootServiceError != "" { + bootServiceError += "; " + } + bootServiceError += fmt.Sprintf("Failed to create new boot service: %v", err) } } - SendSuccessResponse(w, "System config modified successfully", map[string]interface{}{ + data := map[string]interface{}{ "instanceName": newName, "instanceID": instance.ID, "configPath": newConfigPath, "bootAtStart": newBootAtStart, "runUser": newRunUser, - }) + } + if bootServiceError != "" { + data["bootServiceError"] = bootServiceError + } + SendSuccessResponse(w, "System config modified successfully", data) postLog.Info(fmt.Sprintf("[handleSystemConfigModify] System config for instance %s modified successfully: bootAtStart=%v, runUser=%s", newName, newBootAtStart, newRunUser)) } @@ -614,6 +626,7 @@ func ListInstancesHandler(w http.ResponseWriter, r *http.Request) { var responseInstances []map[string]interface{} for _, instance := range instanceList { instanceData := map[string]interface{}{ + "instanceID": instance.ID, "name": instance.Name, "serverAddr": instance.ServerAddr, "serverPort": instance.ServerPort,