diff --git a/docs/api.md b/docs/api.md index bd74727..65ac324 100644 --- a/docs/api.md +++ b/docs/api.md @@ -784,6 +784,88 @@ X-Timestamp: 1704067200000 --- +## List Proxies + +**Endpoint:** `/frpcAct/proxyMgr/list` +**Method:** GET +**Auth Required:** Yes (token) +**Permission Level:** None (any permission level) + +Retrieve a list of all proxy configurations for a specific frpc instance. The proxy configurations are read from the instance's config file. + +**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 | Instance ID (the ID of the frpc instance) | + +**Response:** +```json +{ + "success": true, + "message": "Proxies listed successfully", + "data": { + "instanceID": 1, + "instanceName": "my_frpc", + "proxyCount": 2, + "proxies": [ + { + "name": "ssh_proxy", + "type": "tcp", + "local_ip": "127.0.0.1", + "local_port": "22", + "remote_port": "6000" + }, + { + "name": "web_proxy", + "type": "http", + "local_ip": "127.0.0.1", + "local_port": "8080", + "remote_port": "80" + } + ] + } +} +``` + +| Field | Type | Description | +|-------|------|-------------| +| instanceID | int | Instance ID | +| instanceName | string | Name of the frpc instance | +| proxyCount | int | Number of proxies | +| proxies | array | List of proxy configurations | + +**Proxy Object:** +| Field | Type | Description | +|-------|------|-------------| +| name | string | Proxy name | +| type | string | Proxy type (e.g., tcp, udp, http, https) | +| local_ip | string | Local IP address to forward to | +| local_port | string | Local port to forward from | +| remote_port | string | Remote port on frps to expose | + +> **Note:** +> - The proxy configurations are read from the existing config file +> - This endpoint does not modify the database, only reads the config file +> - The returned proxy list represents the current configuration in the file +> - Changes to the config file will be reflected in subsequent requests + +--- + ## List frpc Instances **Endpoint:** `/frpcAct/instanceMgr/list` diff --git a/frpcProxyAct.go b/frpcProxyAct.go index 75002f3..3083e96 100644 --- a/frpcProxyAct.go +++ b/frpcProxyAct.go @@ -3,13 +3,14 @@ package main import ( "encoding/json" "fmt" - "github.com/BurntSushi/toml" "io" "net/http" "os" "strconv" "strings" "super-frpc/postLog" + + "github.com/BurntSushi/toml" ) type CreateProxyRequest struct { @@ -279,3 +280,75 @@ func removeFrpcProxy(configContent string, proxyName string) (string, error) { return buf.String(), nil } + +func ListProxiesHandler(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodGet { + SendErrorResponse(w, http.StatusMethodNotAllowed, "Invalid request method") + postLog.Debug(fmt.Sprintf("[ListProxiesHandler] Invalid request method: %s", r.Method)) + return + } + + queryParams := r.URL.Query() + instanceID := queryParams.Get("instanceID") + if instanceID == "" { + postLog.Error("[ListProxiesHandler] instanceID is required") + SendErrorResponse(w, http.StatusBadRequest, "instanceID is required") + return + } + + userID, _, err := ValidateRequestWithHeader(w, r) + if err != nil { + postLog.Error(fmt.Sprintf("[ListProxiesHandler] Failed to validate request header: %v", err)) + SendErrorResponse(w, http.StatusBadRequest, "Invalid request header") + return + } + + var instance FrpcInstance + instanceIDInt, _ := strconv.Atoi(instanceID) + instance, err = DBQueryFrpcInstanceByID(instanceIDInt) + if err != nil { + postLog.Error(fmt.Sprintf("[ListProxiesHandler] Failed to query instance: %v", err)) + SendErrorResponse(w, http.StatusInternalServerError, "Failed to query instance") + return + } + + if instance.UserID != userID { + postLog.Error(fmt.Sprintf("[ListProxiesHandler] Instance not found for user %d", userID)) + SendErrorResponse(w, http.StatusNotFound, "Instance not found") + return + } + + configContent, err := os.ReadFile(instance.ConfigPath) + if err != nil { + postLog.Error(fmt.Sprintf("[ListProxiesHandler] Failed to read config file %s: %v", instance.ConfigPath, err)) + SendErrorResponse(w, http.StatusInternalServerError, "Failed to read config file") + return + } + + var config FrpcConfig + if _, err := toml.Decode(string(configContent), &config); err != nil { + postLog.Error(fmt.Sprintf("[ListProxiesHandler] Failed to parse config: %v", err)) + SendErrorResponse(w, http.StatusInternalServerError, "Failed to parse config file") + return + } + + proxyList := make([]map[string]interface{}, len(config.Proxies)) + for i, proxy := range config.Proxies { + proxyData := map[string]interface{}{ + "name": proxy["name"], + "type": proxy["type"], + "localIP": proxy["localIP"], + "localPort": proxy["localPort"], + "remotePort": proxy["remotePort"], + } + proxyList[i] = proxyData + } + + SendSuccessResponse(w, "Proxies listed successfully", map[string]interface{}{ + "instanceID": instance.ID, + "instanceName": instance.Name, + "proxyCount": len(proxyList), + "proxies": proxyList, + }) + postLog.Info(fmt.Sprintf("[ListProxiesHandler] Retrieved %d proxies for instance %s", len(proxyList), instance.Name)) +} diff --git a/router.go b/router.go index db1745d..562bc1a 100644 --- a/router.go +++ b/router.go @@ -31,6 +31,7 @@ func setupRoutes() { http.HandleFunc("/frpcAct/instanceMgr/list", ListInstancesHandler) http.HandleFunc("/frpcAct/proxyMgr/create", CreateProxyHandler) http.HandleFunc("/frpcAct/proxyMgr/delete", DeleteProxyHandler) + http.HandleFunc("/frpcAct/proxyMgr/list", ListProxiesHandler) http.HandleFunc("/", NotFoundHandler)