feat(logging): add detailed logging throughout application components

- Implement logging in router setup, auth handlers, and frpc operations
- Add SoftwareInfo struct for version tracking and logging
- Enhance error messages with more context and logging
- Replace direct error returns with formatted error logging
- Add debug logs for token operations and request validations
This commit is contained in:
2026-02-27 23:44:41 +08:00
parent 3285e9026a
commit 182887a66f
5 changed files with 167 additions and 85 deletions
+57 -38
View File
@@ -2,7 +2,6 @@ package main
import (
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
@@ -32,25 +31,29 @@ type Response struct {
func RegisterHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
SendErrorResponse(w, http.StatusMethodNotAllowed, "invalid request method")
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")
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")
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")
SendErrorResponse(w, http.StatusBadRequest, "Username and password are required")
postLog.Warning("[RegisterHandler] New user registration failed: username or password is empty")
return
}
@@ -60,12 +63,14 @@ func RegisterHandler(w http.ResponseWriter, r *http.Request) {
}
if !isValidInput(req.Username) || !isValidInput(req.Passwd) {
SendErrorResponse(w, http.StatusBadRequest, "invalid input: contains illegal characters")
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)")
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
}
@@ -80,19 +85,22 @@ func RegisterHandler(w http.ResponseWriter, r *http.Request) {
"visitor": true,
}
if !validTypes[userType] {
SendErrorResponse(w, http.StatusBadRequest, "invalid user type")
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")
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
}
@@ -105,67 +113,78 @@ func RegisterHandler(w http.ResponseWriter, r *http.Request) {
func LoginHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
SendErrorResponse(w, http.StatusMethodNotAllowed, "invalid request method")
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")
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")
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")
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")
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 or password")
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 username or password")
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")
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")
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{}{
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) {
@@ -177,7 +196,7 @@ func SendErrorResponse(w http.ResponseWriter, statusCode int, message string) {
}
jsonResp, err := json.Marshal(resp)
if err != nil {
postLog.Error(fmt.Sprintf("failed to marshal error response: %v", err))
postLog.Error(fmt.Sprintf("Failed to marshal error response: %v", err))
return
}
w.Write(jsonResp)
@@ -193,7 +212,7 @@ func SendSuccessResponse(w http.ResponseWriter, message string, data interface{}
}
jsonResp, err := json.Marshal(resp)
if err != nil {
postLog.Error(fmt.Sprintf("failed to marshal success response: %v", err))
postLog.Error(fmt.Sprintf("Failed to marshal success response: %v", err))
return
}
w.Write(jsonResp)
@@ -202,7 +221,7 @@ func SendSuccessResponse(w http.ResponseWriter, message string, data interface{}
func ValidateRequest(w http.ResponseWriter, r *http.Request, requiredFields ...string) (int, string, error) {
body, err := io.ReadAll(r.Body)
if err != nil {
return 0, "", errors.New("failed to read request body")
return 0, "", fmt.Errorf("Failed to read request body: %w", err)
}
defer r.Body.Close()
@@ -212,37 +231,37 @@ func ValidateRequest(w http.ResponseWriter, r *http.Request, requiredFields ...s
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, "", errors.New("invalid request format")
return 0, "", fmt.Errorf("Invalid request format: %w", err)
}
token, ok := reqMap["token"].(string)
if !ok || token == "" {
return 0, "", errors.New("token is required")
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, "", errors.New("timeStamp is required")
return 0, "", fmt.Errorf("Timestamp is required: %d", timeStamp)
}
if err := ValidateTimeStamp(timeStamp); err != nil {
return 0, "", err
return 0, "", fmt.Errorf("Invalid timestamp: %w", err)
}
userID, err := extractUserIDFromToken(token)
if err != nil {
return 0, "", err
return 0, "", fmt.Errorf("Invalid token format: %w", err)
}
if err := ValidateToken(userID, token); err != nil {
return 0, "", err
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", field)
return 0, "", fmt.Errorf("required field %s is missing: %s", field, reqMap[field])
}
}
@@ -252,7 +271,7 @@ func ValidateRequestWithBody(w http.ResponseWriter, r *http.Request, body []byte
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")
return 0, "", fmt.Errorf("Token is required in header: %s", token)
}
timeStampStr := r.Header.Get("X-Timestamp")
@@ -261,10 +280,10 @@ func ValidateRequestWithHeader(w http.ResponseWriter, r *http.Request, requiredF
var err error
timeStamp, err = strconv.ParseInt(timeStampStr, 10, 64)
if err != nil {
return 0, "", errors.New("invalid timestamp format in header")
return 0, "", fmt.Errorf("Invalid timestamp format in header: %w", err)
}
} else if !globalConfig.Debug {
return 0, "", errors.New("timestamp is required in header")
return 0, "", fmt.Errorf("Timestamp is required in header: %s", timeStampStr)
}
if err := ValidateTimeStamp(timeStamp); err != nil {
@@ -273,17 +292,17 @@ func ValidateRequestWithHeader(w http.ResponseWriter, r *http.Request, requiredF
userID, err := extractUserIDFromToken(token)
if err != nil {
return 0, "", err
return 0, "", fmt.Errorf("Invalid token format in header: %w", err)
}
if err := ValidateToken(userID, token); err != nil {
return 0, "", err
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", field)
return 0, "", fmt.Errorf("required field %s is missing in header: %s", field, headerValue)
}
}
@@ -293,7 +312,7 @@ func ValidateRequestWithHeader(w http.ResponseWriter, r *http.Request, requiredF
func GetUserType(userID int) (string, error) {
user, err := GetUserByID(userID)
if err != nil {
return "", err
return "", fmt.Errorf("Failed to get user type: %w", err)
}
return user.Type, nil
}
@@ -301,7 +320,7 @@ func GetUserType(userID int) (string, error) {
func CheckPermission(userID int, requiredTypes ...string) error {
userType, err := GetUserType(userID)
if err != nil {
return err
return fmt.Errorf("Failed to check permission: %w", err)
}
for _, t := range requiredTypes {
@@ -310,7 +329,7 @@ func CheckPermission(userID int, requiredTypes ...string) error {
}
}
return errors.New("permission denied")
return fmt.Errorf("Permission denied for user type %s", userType)
}
func GetClientIP(r *http.Request) string {