diff --git a/README.md b/README.md index ee73ed0..68857c9 100644 --- a/README.md +++ b/README.md @@ -68,6 +68,12 @@ All API responses are returned in JSON format: } ``` +**Important Notes:** +- For **POST** requests: All data is sent in the request body as JSON +- For **GET** requests: All data is sent in HTTP headers (prefixed with `X-`) +- All requests require authentication (except `/register` and `/login`) +- Timestamps are in milliseconds (Unix timestamp) + --- ### 1. Register User @@ -269,13 +275,16 @@ You can modify multiple fields at once: **Method:** GET **Auth Required:** Yes (token) -**Request:** -```json -{ - "token": "your_token", - "timeStamp": 1704067200000 -} +**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 (admin/superuser):** ```json diff --git a/frpc.go b/frpc.go index 50031ca..975abf1 100644 --- a/frpc.go +++ b/frpc.go @@ -433,14 +433,7 @@ func ListInstancesHandler(w http.ResponseWriter, r *http.Request) { return } - body, err := io.ReadAll(r.Body) - if err != nil { - SendErrorResponse(w, http.StatusBadRequest, "failed to read request body") - return - } - defer r.Body.Close() - - userID, _, err := ValidateRequestWithBody(w, r, body) + userID, _, err := ValidateRequestWithHeader(w, r) if err != nil { SendErrorResponse(w, http.StatusUnauthorized, err.Error()) return diff --git a/handlers.go b/handlers.go index 0de4608..d581466 100644 --- a/handlers.go +++ b/handlers.go @@ -249,6 +249,47 @@ func ValidateRequestWithBody(w http.ResponseWriter, r *http.Request, body []byte return userID, token, nil } +func ValidateRequestWithHeader(w http.ResponseWriter, r *http.Request, requiredFields ...string) (int, string, error) { + token := r.Header.Get("X-Token") + if token == "" { + return 0, "", errors.New("token is required in header") + } + + timeStampStr := r.Header.Get("X-Timestamp") + timeStamp := int64(0) + if timeStampStr != "" { + var err error + timeStamp, err = strconv.ParseInt(timeStampStr, 10, 64) + if err != nil { + return 0, "", errors.New("invalid timestamp format in header") + } + } else if !globalConfig.Debug { + return 0, "", errors.New("timestamp is required in header") + } + + if err := ValidateTimeStamp(timeStamp); err != nil { + return 0, "", err + } + + userID, err := extractUserIDFromToken(token) + if err != nil { + return 0, "", err + } + + if err := ValidateToken(userID, token); err != nil { + return 0, "", err + } + + for _, field := range requiredFields { + headerValue := r.Header.Get(fmt.Sprintf("X-%s", field)) + if headerValue == "" { + return 0, "", fmt.Errorf("required field %s is missing in header", field) + } + } + + return userID, token, nil +} + func GetUserType(userID int) (string, error) { user, err := GetUserByID(userID) if err != nil { diff --git a/router.go b/router.go index 53a3069..68c8da6 100644 --- a/router.go +++ b/router.go @@ -21,16 +21,24 @@ func setupRoutes() { remainingPath := path[len("/frpcAct/instanceMgr/"):] - if r.Method == http.MethodPost { - if remainingPath == "create" { - CreateInstanceHandler(w, r) + if r.Method == http.MethodGet { + if remainingPath == "list" { + ListInstancesHandler(w, r) return } - if remainingPath == "list" { + instanceName := strings.Trim(remainingPath, "/") + if instanceName != "" { ListInstancesHandler(w, r) return } + } + + if r.Method == http.MethodPost { + if remainingPath == "create" { + CreateInstanceHandler(w, r) + return + } if strings.HasSuffix(remainingPath, "/delete") { instanceName := strings.TrimSuffix(remainingPath, "/delete") @@ -47,12 +55,6 @@ func setupRoutes() { return } } - - instanceName := strings.Trim(remainingPath, "/") - if instanceName != "" { - ListInstancesHandler(w, r) - return - } } SendErrorResponse(w, http.StatusNotFound, "endpoint not found")