feat(sys): add self-check functionality for system settings and frpc binary

This commit is contained in:
2026-05-07 21:07:16 +08:00
parent 44195c9f26
commit 9ffb4263cf
5 changed files with 115 additions and 14 deletions

View File

@@ -20,6 +20,7 @@ All API responses are returned in JSON format:
```
**Important Notes:**
- All endpoints are relative to the base URL `/api`
- For all requests: Authentication token and timestamp are sent in HTTP headers (prefixed with `X-`)
- For **POST** requests: Other data is sent in the request body as JSON
- For **GET** requests: All data is sent in HTTP headers (prefixed with `X-`)
@@ -90,6 +91,44 @@ Get software version and build information.
---
## Self Check
**Endpoint:** `/system/selfCheck`
**Method:** POST
**Content-Type:** application/json
**Permission Level:** Visitor
Check the system configuration and frpc binary.
**Request Headers:**
```
X-Token: your_token
X-Timestamp: 1704067200000
```
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| X-Token | string | Yes | Authentication token |
| X-Timestamp | int64 | Yes | Client timestamp in milliseconds |
**Response (Success):**
```json
{
"success": true,
"message": "Self check passed"
}
```
> If there were any errors, the response will be in error format.
**Response (Error):**
```json
{
"success": false,
"message": "error message"
}
```
---
## Register User
**Endpoint:** `/register`

View File

@@ -499,16 +499,16 @@ func getNumFromMap(m map[string]interface{}, key string) int {
}
func ListInstancesHandler(w http.ResponseWriter, r *http.Request) {
userID, err := utils.Auth(w, r, http.MethodGet)
userID, err := utils.Auth(w, r, http.MethodGet, "superuser", "admin")
if err != nil {
utils.SendErrorResponse(w, http.StatusUnauthorized, err.Error())
postLog.Warning(fmt.Sprintf("[ListInstancesHandler] Auth failed: %v", err))
return
}
instances, err := GetUserInstances(userID)
instances, err := database.DBListFrpcInstances()
if err != nil {
postLog.Error(fmt.Sprintf("[ListInstancesHandler] Failed to get user instances: %v", err))
postLog.Error(fmt.Sprintf("[ListInstancesHandler] Failed to get all instances: %v", err))
utils.SendErrorResponse(w, http.StatusInternalServerError, "Failed to get instances")
return
}
@@ -650,14 +650,14 @@ func StartInstanceHandler(w http.ResponseWriter, r *http.Request) {
if !watchdog.AddInstance(sysName) {
postLog.Warning(fmt.Sprintf("[StartInstanceHandler] Failed to add watchdog instance %s", sysName))
utils.SendSuccessResponse(w, "Instance started successfully but watchdog instance add failed", map[string]interface{}{
"instanceID": instanceID,
"sysName": sysName,
"instanceID": instanceID,
"sysName": sysName,
})
return
} else {
utils.SendSuccessResponse(w, "Instance started successfully", map[string]interface{}{
"instanceID": instanceID,
"sysName": sysName,
"instanceID": instanceID,
"sysName": sysName,
})
}
}
@@ -767,13 +767,13 @@ func StopInstanceHandler(w http.ResponseWriter, r *http.Request) {
if !watchdog.RemoveInstance(sysName) {
postLog.Warning(fmt.Sprintf("[StopInstanceHandler] Failed to remove watchdog instance %s", sysName))
utils.SendSuccessResponse(w, "Instance stopped successfully but watchdog instance remove failed", map[string]interface{}{
"instanceID": instanceID,
"sysName": sysName,
"instanceID": instanceID,
"sysName": sysName,
})
} else {
utils.SendSuccessResponse(w, "Instance stopped successfully", map[string]interface{}{
"instanceID": instanceID,
"sysName": sysName,
"instanceID": instanceID,
"sysName": sysName,
})
}
}
@@ -883,8 +883,8 @@ func RestartInstanceHandler(w http.ResponseWriter, r *http.Request) {
}
utils.SendSuccessResponse(w, "Instance restarted successfully", map[string]interface{}{
"instanceID": instanceID,
"sysName": sysName,
"instanceID": instanceID,
"sysName": sysName,
})
postLog.Info(fmt.Sprintf("[RestartInstanceHandler] Instance %d restarted successfully", instanceID))
}
@@ -1005,7 +1005,7 @@ func GetInstanceInfoHandler(w http.ResponseWriter, r *http.Request) {
response := map[string]interface{}{
"name": instance.Name,
"sysName": sysName,
"sysName": sysName,
"createdAt": instance.CreatedAt.Format(time.RFC3339),
"createdBy": instance.CreatedBy,
"isRunning": isRunning,

View File

@@ -9,6 +9,7 @@ import (
"super-frpc/session"
"super-frpc/utils"
"super-frpc/web"
"super-frpc/sys"
)
func setupRoutes() {
@@ -16,6 +17,8 @@ func setupRoutes() {
http.HandleFunc("/api/system/getLogs", systemLogHandler.Handle)
http.HandleFunc("/api/system/getStatus", GetStatusHandler)
http.HandleFunc("/api/system/getSoftwareInfo", GetSoftwareInfoHandler)
http.HandleFunc("/api/system/selfCheck", sys.SelfCheckHandler)
http.HandleFunc("/api/system/settings/get", handlers.GetSettingsHandler)
http.HandleFunc("/api/system/settings/set", handlers.SetSettingsHandler)

53
sys/selfcheck.go Normal file
View File

@@ -0,0 +1,53 @@
package sys
import (
"net/http"
"fmt"
"super-frpc/global"
"super-frpc/utils"
"super-frpc/postLog"
)
func SelfCheckHandler(w http.ResponseWriter, r *http.Request) {
userID, err := utils.Auth(w, r, http.MethodGet, "visitor", "superuser", "admin")
if err != nil {
utils.SendErrorResponse(w, http.StatusUnauthorized, "invalid token or timestamp")
postLog.Warning(fmt.Sprintf("[SelfCheckHandler] Auth failed: %v, userID: %d", err, userID))
return
}
err = SettingsSelfCheck()
if err != nil {
utils.SendErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
err = FrpBinaryCheck()
if err != nil {
utils.SendErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
utils.SendSuccessResponse(w, "Self check passed", nil)
postLog.Info(fmt.Sprintf("[SelfCheckHandler] [%d] Self check passed", userID))
}
func SettingsSelfCheck() error {
if global.CurrentConfig.ListenAddr == "" {
return fmt.Errorf("listenAddr is not set")
}
if global.CurrentConfig.ListenPort == "0" {
return fmt.Errorf("listenPort is invalid")
}
if global.CurrentConfig.FrpcPath == "" {
return fmt.Errorf("frpcPath is not set")
}
if global.CurrentConfig.InstancePath == "" {
return fmt.Errorf("instancePath is not set")
}
return nil
}
func FrpBinaryCheck() error {
if !utils.IsFileExist(global.CurrentConfig.FrpcPath) {
return fmt.Errorf("frpc binary is not exist")
}
return nil
}

View File

@@ -5,6 +5,7 @@ import (
"fmt"
"net/http"
"strconv"
"os"
"time"
"strings"
@@ -161,3 +162,8 @@ func GetCmdType(source string) string {
func GetCmdParams(source string, param string) string {
return GetTextMiddle(source, "<"+param+">", "</"+param+">")
}
func IsFileExist(filePath string) bool {
_, err := os.Stat(filePath)
return !os.IsNotExist(err)
}