package main import ( "encoding/json" "fmt" "io" "net/http" "strconv" "super-frpc/postLog" "time" ) type RegisterRequest struct { Username string `json:"username"` Passwd string `json:"passwd"` TimeStamp int64 `json:"timeStamp"` Type string `json:"type"` } type LoginRequest struct { Username string `json:"username"` Passwd string `json:"passwd"` TimeStamp int64 `json:"timeStamp"` } type Response struct { Success bool `json:"success"` Message string `json:"message,omitempty"` Data interface{} `json:"data,omitempty"` } func RegisterHandler(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { SendErrorResponse(w, http.StatusMethodNotAllowed, "Invalid request method") postLog.Warning(fmt.Sprintf("[RegisterHandler] Invalid request method: %s", r.Method)) return } body, err := io.ReadAll(r.Body) if err != nil { SendErrorResponse(w, http.StatusBadRequest, "Failed to read request body") postLog.Warning(fmt.Sprintf("[RegisterHandler] Failed to read request body: %v", err)) return } defer r.Body.Close() var req RegisterRequest if err := json.Unmarshal(body, &req); err != nil { SendErrorResponse(w, http.StatusBadRequest, "Invalid request format") postLog.Warning(fmt.Sprintf("[RegisterHandler] Invalid request format: %v", err)) return } if req.Username == "" || req.Passwd == "" { SendErrorResponse(w, http.StatusBadRequest, "Username and password are required") postLog.Warning("[RegisterHandler] New user registration failed: username or password is empty") return } if err := ValidateTimeStamp(req.TimeStamp); err != nil { SendErrorResponse(w, http.StatusBadRequest, err.Error()) return } if !isValidInput(req.Username) || !isValidInput(req.Passwd) { SendErrorResponse(w, http.StatusBadRequest, "Invalid input: contains illegal characters") postLog.Debug(fmt.Sprintf("[RegisterHandler] New user registration failed: username or password contains illegal characters \"%s\":\"%s\"", req.Username, req.Passwd)) return } if !isValidPassword(req.Passwd) { SendErrorResponse(w, http.StatusBadRequest, "Password does not meet complexity requirements (must contain uppercase, lowercase, digit, and special character)") postLog.Debug(fmt.Sprintf("[RegisterHandler] New user registration failed: password \"%s\" does not meet complexity requirements", req.Passwd)) return } userType := req.Type if userType == "" { userType = "visitor" } validTypes := map[string]bool{ "superuser": true, "admin": true, "visitor": true, } if !validTypes[userType] { SendErrorResponse(w, http.StatusBadRequest, "Invalid user type") postLog.Warning(fmt.Sprintf("[RegisterHandler] New user registration failed: invalid user type \"%s\"", userType)) return } userID, err := AddUser(req.Username, req.Passwd, userType) if err != nil { SendErrorResponse(w, http.StatusInternalServerError, err.Error()) postLog.Error(fmt.Sprintf("[RegisterHandler] Failed to register user \"%s\": %v", req.Username, err)) return } user, err := GetUserByID(userID) if err != nil { SendErrorResponse(w, http.StatusInternalServerError, "Failed to retrieve user after registration") postLog.Error(fmt.Sprintf("[RegisterHandler] Failed to retrieve user \"%s\" after registration: %v", req.Username, err)) return } SendSuccessResponse(w, "user registered successfully", map[string]interface{}{ "userID": user.UserID, "username": user.Username, "type": user.Type, }) } func LoginHandler(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { SendErrorResponse(w, http.StatusMethodNotAllowed, "Invalid request method") postLog.Warning(fmt.Sprintf("[LoginHandler] Invalid request method: %s", r.Method)) return } body, err := io.ReadAll(r.Body) if err != nil { SendErrorResponse(w, http.StatusBadRequest, "Failed to read request body") postLog.Warning(fmt.Sprintf("[LoginHandler] Failed to read request body: %v", err)) return } defer r.Body.Close() var req LoginRequest if err := json.Unmarshal(body, &req); err != nil { SendErrorResponse(w, http.StatusBadRequest, "Invalid request format") postLog.Warning(fmt.Sprintf("[LoginHandler] Invalid request format: %v", err)) return } if req.Username == "" || req.Passwd == "" { SendErrorResponse(w, http.StatusBadRequest, "Username and password are required") postLog.Warning("[LoginHandler] Login failed: username or password is empty") return } if err := ValidateTimeStamp(req.TimeStamp); err != nil { SendErrorResponse(w, http.StatusBadRequest, err.Error()) postLog.Warning(fmt.Sprintf("[LoginHandler] User \"%s\" Login failed: invalid timestamp \"%d\"", req.Username, req.TimeStamp)) return } if !isValidInput(req.Username) || !isValidInput(req.Passwd) { SendErrorResponse(w, http.StatusBadRequest, "Invalid input: contains illegal characters") postLog.Debug(fmt.Sprintf("[LoginHandler] Login failed: username or password contains illegal characters \"%s\":\"%s\"", req.Username, req.Passwd)) return } user, err := GetUserByUsername(req.Username) if err != nil { SendErrorResponse(w, http.StatusUnauthorized, "Invalid username") postLog.Warning(fmt.Sprintf("[LoginHandler] Login failed: invalid username \"%s\"", req.Username)) return } if !verifyPassword(req.Passwd, user.Passwd) { SendErrorResponse(w, http.StatusUnauthorized, "Invalid password") postLog.Warning(fmt.Sprintf("[LoginHandler] Login failed: invalid password for user \"%s\"", req.Username)) return } existingTokenInfo, err := GetTokenInfo(user.UserID) if err == nil && existingTokenInfo != nil { SendErrorResponse(w, http.StatusConflict, "User is already logged in") postLog.Warning(fmt.Sprintf("[LoginHandler] Login failed: user \"%s\" is already logged in", req.Username)) return } token, err := GenerateToken(user.UserID) if err != nil { SendErrorResponse(w, http.StatusInternalServerError, "Failed to generate token") postLog.Error(fmt.Sprintf("[LoginHandler] Failed to generate token for user \"%s\": %v", req.Username, err)) return } SendSuccessResponse(w, "Login successful", map[string]interface{}{ "token": token, "userID": user.UserID, "username": user.Username, "type": user.Type, }) postLog.Info(fmt.Sprintf("[LoginHandler] User \"%s\" Login successful", req.Username)) } func SendErrorResponse(w http.ResponseWriter, statusCode int, message string) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(statusCode) resp := Response{ Success: false, Message: message, } jsonResp, err := json.Marshal(resp) if err != nil { postLog.Error(fmt.Sprintf("Failed to marshal error response: %v", err)) return } w.Write(jsonResp) } func SendSuccessResponse(w http.ResponseWriter, message string, data interface{}) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) resp := Response{ Success: true, Message: message, Data: data, } jsonResp, err := json.Marshal(resp) if err != nil { postLog.Error(fmt.Sprintf("Failed to marshal success response: %v", err)) return } w.Write(jsonResp) } func ValidateRequest(w http.ResponseWriter, r *http.Request, requiredFields ...string) (int, string, error) { body, err := io.ReadAll(r.Body) if err != nil { return 0, "", fmt.Errorf("Failed to read request body: %w", err) } defer r.Body.Close() return ValidateRequestWithBody(w, r, body, requiredFields...) } func ValidateRequestWithBody(w http.ResponseWriter, r *http.Request, body []byte, requiredFields ...string) (int, string, error) { var reqMap map[string]interface{} if err := json.Unmarshal(body, &reqMap); err != nil { return 0, "", fmt.Errorf("Invalid request format: %w", err) } token, ok := reqMap["token"].(string) if !ok || token == "" { return 0, "", fmt.Errorf("Token is required: %s", token) } timeStamp := int64(0) if ts, ok := reqMap["timeStamp"].(float64); ok { timeStamp = int64(ts) } else if !globalConfig.Debug { return 0, "", fmt.Errorf("Timestamp is required: %d", timeStamp) } if err := ValidateTimeStamp(timeStamp); err != nil { return 0, "", fmt.Errorf("Invalid timestamp: %w", err) } userID, err := extractUserIDFromToken(token) if err != nil { return 0, "", fmt.Errorf("Invalid token format: %w", err) } if err := ValidateToken(userID, token); err != nil { return 0, "", fmt.Errorf("Token validation failed: %w", err) } for _, field := range requiredFields { if _, ok := reqMap[field]; !ok { return 0, "", fmt.Errorf("required field %s is missing: %s", field, reqMap[field]) } } 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, "", fmt.Errorf("Token is required in header: %s", token) } 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, "", fmt.Errorf("Invalid timestamp format in header: %w", err) } } else if !globalConfig.Debug { return 0, "", fmt.Errorf("Timestamp is required in header: %s", timeStampStr) } if err := ValidateTimeStamp(timeStamp); err != nil { return 0, "", err } userID, err := extractUserIDFromToken(token) if err != nil { return 0, "", fmt.Errorf("Invalid token format in header: %w", err) } if err := ValidateToken(userID, token); err != nil { return 0, "", fmt.Errorf("Token validation failed in header: %w", 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: %s", field, headerValue) } } return userID, token, nil } func GetUserType(userID int) (string, error) { user, err := GetUserByID(userID) if err != nil { return "", fmt.Errorf("Failed to get user type: %w", err) } return user.Type, nil } func CheckPermission(userID int, requiredTypes ...string) error { userType, err := GetUserType(userID) if err != nil { return fmt.Errorf("Failed to check permission: %w", err) } for _, t := range requiredTypes { if userType == t { return nil } } return fmt.Errorf("Permission denied for user type %s", userType) } func GetClientIP(r *http.Request) string { forwarded := r.Header.Get("X-Forwarded-For") if forwarded != "" { return forwarded } return r.RemoteAddr } func LogRequest(r *http.Request, userID int) { postLog.Info(fmt.Sprintf("[%s] %s %s - UserID: %d - IP: %s", time.Now().Format("2006-01-02 15:04:05"), r.Method, r.URL.Path, userID, GetClientIP(r), )) } func IntToString(i int) string { return strconv.Itoa(i) }