feat(instanceMgr): add getInfo endpoint to retrieve instance details

Implement new GET endpoint `/frpcAct/instanceMgr/getInfo` to fetch frpc instance information
Add documentation for the new API endpoint in docs/api.md
The handler returns different information based on user permission level
This commit is contained in:
2026-03-25 22:20:32 +08:00
parent 25f88249c2
commit e83436fe9b
3 changed files with 182 additions and 0 deletions
+85
View File
@@ -1165,6 +1165,91 @@ X-Timestamp: 1704067200000
--- ---
## Get frpc Instance Information
**Endpoint:** `/frpcAct/instanceMgr/getInfo`
**Method:** GET
**Auth Required:** Yes (token)
**Permission Level:** Visitor
Retrieve information about a specific frpc instance. Returns different information based on user permission level.
**Request Headers:**
```
X-Token: your_token
X-Timestamp: 1704067200000
```
**Query Parameters:**
```
instanceID=1
```
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| X-Token | string | Yes | Authentication token |
| X-Timestamp | int64 | Yes | Client timestamp in milliseconds |
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| instanceID | string | Yes | ID of the instance to get information for |
**Response (Admin/Superuser):**
```json
{
"success": true,
"message": "Instance info retrieved successfully",
"data": {
"name": "my_frpc",
"serviceName": "superfrpc_admin_my_frpc",
"createdAt": "2024-01-01T00:00:00Z",
"createdBy": "admin",
"isRunning": true,
"serverAddr": "127.0.0.1",
"serverPort": "7000",
"auth_method": "token",
"bootAtStart": true,
"runUser": "root",
"configPath": "./configs/superfrpc_admin_my_frpc.toml"
}
}
```
**Response (Visitor):**
```json
{
"success": true,
"message": "Instance info retrieved successfully",
"data": {
"name": "my_frpc",
"serviceName": "superfrpc_admin_my_frpc",
"createdAt": "2024-01-01T00:00:00Z",
"createdBy": "admin",
"isRunning": true,
"auth_method": "token",
"bootAtStart": true,
"runUser": "root",
"configPath": "./configs/superfrpc_admin_my_frpc.toml"
}
}
```
| Field | Type | Description |
|-------|------|-------------|
| name | string | Name of the frpc instance |
| serviceName | string | Service name |
| createdAt | string | Instance creation time (ISO 8601 format) |
| createdBy | string | Username of the user who created this instance |
| isRunning | bool | Instance running status: true (running) or false (stopped) |
| serverAddr | string | frps server address (only returned for admin/superuser) |
| serverPort | string | frps server port (only returned for admin/superuser) |
| auth_method | string | Authentication method |
| bootAtStart | bool | Whether the instance starts automatically on system boot |
| runUser | string | User that the instance runs as |
| configPath | string | Path to the instance's configuration file |
---
## Real-time Log Streaming (WebSocket) ## Real-time Log Streaming (WebSocket)
**Endpoint:** `/system/getLogs` **Endpoint:** `/system/getLogs`
+96
View File
@@ -972,3 +972,99 @@ func GetInstanceStatusHandler(w http.ResponseWriter, r *http.Request) {
SendSuccessResponse(w, "Instance status retrieved successfully", responseData) SendSuccessResponse(w, "Instance status retrieved successfully", responseData)
postLog.Info(fmt.Sprintf("[GetInstanceStatusHandler] Retrieved status for instance %d (name: %s), isRunning: %v", instanceID, instance.Name, isRunning)) postLog.Info(fmt.Sprintf("[GetInstanceStatusHandler] Retrieved status for instance %d (name: %s), isRunning: %v", instanceID, instance.Name, isRunning))
} }
func GetInstanceInfoHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
SendErrorResponse(w, http.StatusMethodNotAllowed, "Invalid request method")
postLog.Debug(fmt.Sprintf("[GetInstanceInfoHandler] Invalid request method: %s", r.Method))
return
}
queryParams := r.URL.Query()
instanceIDStr := queryParams.Get("instanceID")
if instanceIDStr == "" {
SendErrorResponse(w, http.StatusBadRequest, "instanceID is required")
return
}
instanceID, err := strconv.Atoi(instanceIDStr)
if err != nil {
SendErrorResponse(w, http.StatusBadRequest, "Invalid instanceID format")
return
}
userID, _, err := ValidateRequestWithHeader(w, r)
if err != nil {
postLog.Error(fmt.Sprintf("[GetInstanceInfoHandler] Failed to validate request header: %v", err))
SendErrorResponse(w, http.StatusBadRequest, "Invalid request header")
return
}
instance, err := DBQueryFrpcInstanceByID(instanceID)
if err == sql.ErrNoRows {
SendErrorResponse(w, http.StatusNotFound, "Instance not found")
postLog.Error(fmt.Sprintf("[GetInstanceInfoHandler] User %d tried to get info of a not existed instance: %d", userID, instanceID))
return
}
if err != nil {
postLog.Error(fmt.Sprintf("[GetInstanceInfoHandler] Failed to query instance: %v", err))
SendErrorResponse(w, http.StatusInternalServerError, "Failed to query instance")
return
}
if instance.UserID != userID {
SendErrorResponse(w, http.StatusForbidden, "Instance not found")
postLog.Error(fmt.Sprintf("[GetInstanceInfoHandler] User %d tried to get info of instance %d that does not belong to them", userID, instanceID))
return
}
userType, err := GetUserType(userID)
if err != nil {
postLog.Error(fmt.Sprintf("[GetInstanceInfoHandler] Failed to get user type: %v", err))
SendErrorResponse(w, http.StatusInternalServerError, "Failed to get user type")
return
}
serviceName, err := GetServiceNameByInstanceID(instanceID)
if err != nil {
postLog.Error(fmt.Sprintf("[GetInstanceInfoHandler] Failed to get service name: %v", err))
SendErrorResponse(w, http.StatusInternalServerError, "Failed to get service name")
return
}
isRunning := IsInstanceRunning(instanceID)
responseData := map[string]interface{}{
"name": instance.Name,
"serviceName": serviceName,
"createdAt": instance.CreatedAt,
"createdBy": instance.CreatedBy,
"isRunning": isRunning,
"auth_method": "",
"bootAtStart": instance.BootAtStart,
"runUser": instance.RunUser,
"configPath": instance.ConfigPath,
}
configContent, err := os.ReadFile(instance.ConfigPath)
if err == nil {
config, err := decodeFrpcConfig(string(configContent))
if err == nil {
if authMethod, ok := config.Global["auth.method"]; ok {
responseData["auth_method"] = authMethod
}
if userType == "admin" || userType == "superuser" {
if serverAddr, ok := config.Global["serverAddr"]; ok {
responseData["serverAddr"] = serverAddr
}
if serverPort, ok := config.Global["serverPort"]; ok {
responseData["serverPort"] = serverPort
}
}
}
}
SendSuccessResponse(w, "Instance info retrieved successfully", responseData)
postLog.Info(fmt.Sprintf("[GetInstanceInfoHandler] Retrieved info for instance %d (name: %s), userType: %s", instanceID, instance.Name, userType))
}
+1
View File
@@ -33,6 +33,7 @@ func setupRoutes() {
http.HandleFunc("/frpcAct/instanceMgr/stop", StopInstanceHandler) http.HandleFunc("/frpcAct/instanceMgr/stop", StopInstanceHandler)
http.HandleFunc("/frpcAct/instanceMgr/restart", RestartInstanceHandler) http.HandleFunc("/frpcAct/instanceMgr/restart", RestartInstanceHandler)
http.HandleFunc("/frpcAct/instanceMgr/status", GetInstanceStatusHandler) http.HandleFunc("/frpcAct/instanceMgr/status", GetInstanceStatusHandler)
http.HandleFunc("/frpcAct/instanceMgr/getInfo", GetInstanceInfoHandler)
http.HandleFunc("/frpcAct/proxyMgr/create", CreateProxyHandler) http.HandleFunc("/frpcAct/proxyMgr/create", CreateProxyHandler)
http.HandleFunc("/frpcAct/proxyMgr/delete", DeleteProxyHandler) http.HandleFunc("/frpcAct/proxyMgr/delete", DeleteProxyHandler)
http.HandleFunc("/frpcAct/proxyMgr/list", ListProxiesHandler) http.HandleFunc("/frpcAct/proxyMgr/list", ListProxiesHandler)