feat(sys): add self-check functionality for system settings and frpc binary
This commit is contained in:
39
docs/api.md
39
docs/api.md
@@ -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`
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
53
sys/selfcheck.go
Normal 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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
Reference in New Issue
Block a user