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:**
|
**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 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 **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-`)
|
- 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
|
## Register User
|
||||||
|
|
||||||
**Endpoint:** `/register`
|
**Endpoint:** `/register`
|
||||||
|
|||||||
@@ -499,16 +499,16 @@ func getNumFromMap(m map[string]interface{}, key string) int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ListInstancesHandler(w http.ResponseWriter, r *http.Request) {
|
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 {
|
if err != nil {
|
||||||
utils.SendErrorResponse(w, http.StatusUnauthorized, err.Error())
|
utils.SendErrorResponse(w, http.StatusUnauthorized, err.Error())
|
||||||
postLog.Warning(fmt.Sprintf("[ListInstancesHandler] Auth failed: %v", err))
|
postLog.Warning(fmt.Sprintf("[ListInstancesHandler] Auth failed: %v", err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
instances, err := GetUserInstances(userID)
|
instances, err := database.DBListFrpcInstances()
|
||||||
if err != nil {
|
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")
|
utils.SendErrorResponse(w, http.StatusInternalServerError, "Failed to get instances")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -650,14 +650,14 @@ func StartInstanceHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
if !watchdog.AddInstance(sysName) {
|
if !watchdog.AddInstance(sysName) {
|
||||||
postLog.Warning(fmt.Sprintf("[StartInstanceHandler] Failed to add watchdog instance %s", 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{}{
|
utils.SendSuccessResponse(w, "Instance started successfully but watchdog instance add failed", map[string]interface{}{
|
||||||
"instanceID": instanceID,
|
"instanceID": instanceID,
|
||||||
"sysName": sysName,
|
"sysName": sysName,
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
utils.SendSuccessResponse(w, "Instance started successfully", map[string]interface{}{
|
utils.SendSuccessResponse(w, "Instance started successfully", map[string]interface{}{
|
||||||
"instanceID": instanceID,
|
"instanceID": instanceID,
|
||||||
"sysName": sysName,
|
"sysName": sysName,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -767,13 +767,13 @@ func StopInstanceHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
if !watchdog.RemoveInstance(sysName) {
|
if !watchdog.RemoveInstance(sysName) {
|
||||||
postLog.Warning(fmt.Sprintf("[StopInstanceHandler] Failed to remove watchdog instance %s", 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{}{
|
utils.SendSuccessResponse(w, "Instance stopped successfully but watchdog instance remove failed", map[string]interface{}{
|
||||||
"instanceID": instanceID,
|
"instanceID": instanceID,
|
||||||
"sysName": sysName,
|
"sysName": sysName,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
utils.SendSuccessResponse(w, "Instance stopped successfully", map[string]interface{}{
|
utils.SendSuccessResponse(w, "Instance stopped successfully", map[string]interface{}{
|
||||||
"instanceID": instanceID,
|
"instanceID": instanceID,
|
||||||
"sysName": sysName,
|
"sysName": sysName,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -883,8 +883,8 @@ func RestartInstanceHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
utils.SendSuccessResponse(w, "Instance restarted successfully", map[string]interface{}{
|
utils.SendSuccessResponse(w, "Instance restarted successfully", map[string]interface{}{
|
||||||
"instanceID": instanceID,
|
"instanceID": instanceID,
|
||||||
"sysName": sysName,
|
"sysName": sysName,
|
||||||
})
|
})
|
||||||
postLog.Info(fmt.Sprintf("[RestartInstanceHandler] Instance %d restarted successfully", instanceID))
|
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{}{
|
response := map[string]interface{}{
|
||||||
"name": instance.Name,
|
"name": instance.Name,
|
||||||
"sysName": sysName,
|
"sysName": sysName,
|
||||||
"createdAt": instance.CreatedAt.Format(time.RFC3339),
|
"createdAt": instance.CreatedAt.Format(time.RFC3339),
|
||||||
"createdBy": instance.CreatedBy,
|
"createdBy": instance.CreatedBy,
|
||||||
"isRunning": isRunning,
|
"isRunning": isRunning,
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"super-frpc/session"
|
"super-frpc/session"
|
||||||
"super-frpc/utils"
|
"super-frpc/utils"
|
||||||
"super-frpc/web"
|
"super-frpc/web"
|
||||||
|
"super-frpc/sys"
|
||||||
)
|
)
|
||||||
|
|
||||||
func setupRoutes() {
|
func setupRoutes() {
|
||||||
@@ -16,6 +17,8 @@ func setupRoutes() {
|
|||||||
http.HandleFunc("/api/system/getLogs", systemLogHandler.Handle)
|
http.HandleFunc("/api/system/getLogs", systemLogHandler.Handle)
|
||||||
http.HandleFunc("/api/system/getStatus", GetStatusHandler)
|
http.HandleFunc("/api/system/getStatus", GetStatusHandler)
|
||||||
http.HandleFunc("/api/system/getSoftwareInfo", GetSoftwareInfoHandler)
|
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/get", handlers.GetSettingsHandler)
|
||||||
http.HandleFunc("/api/system/settings/set", handlers.SetSettingsHandler)
|
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"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"os"
|
||||||
"time"
|
"time"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -161,3 +162,8 @@ func GetCmdType(source string) string {
|
|||||||
func GetCmdParams(source string, param string) string {
|
func GetCmdParams(source string, param string) string {
|
||||||
return GetTextMiddle(source, "<"+param+">", "</"+param+">")
|
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