From 182887a66f1d76309167011e83ef313e412b2c25 Mon Sep 17 00:00:00 2001 From: NanamiAdmin Date: Fri, 27 Feb 2026 23:44:41 +0800 Subject: [PATCH] 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 --- auth.go | 26 ++++++++----- frpc.go | 109 +++++++++++++++++++++++++++++++++++----------------- handlers.go | 95 +++++++++++++++++++++++++++------------------ main.go | 20 +++++++++- router.go | 2 + 5 files changed, 167 insertions(+), 85 deletions(-) diff --git a/auth.go b/auth.go index e8c4704..03bee20 100644 --- a/auth.go +++ b/auth.go @@ -5,9 +5,9 @@ import ( "crypto/sha256" "encoding/base64" "encoding/hex" - "errors" "fmt" "strings" + "super-frpc/postLog" "sync" "time" ) @@ -28,7 +28,7 @@ func GenerateToken(userID int) (string, error) { randomBytes := make([]byte, 32) _, err := rand.Read(randomBytes) if err != nil { - return "", fmt.Errorf("failed to generate random bytes: %w", err) + return "", fmt.Errorf("Failed to generate random bytes: %w", err) } hash := sha256.Sum256(append(randomBytes, []byte(fmt.Sprintf("%d", userID))...)) @@ -43,6 +43,7 @@ func GenerateToken(userID int) (string, error) { UserID: userID, } + postLog.Debug(fmt.Sprintf("[GenerateToken] Generated token for userID %d: %s", userID, token)) return token, nil } @@ -52,15 +53,15 @@ func ValidateToken(userID int, token string) error { tokenInfo, exists := tokenMap[userID] if !exists { - return errors.New("token not found") + return fmt.Errorf("Token not found for userID %d: %s", userID, token) } if tokenInfo.Token != token { - return errors.New("invalid token") + return fmt.Errorf("Invalid token for userID %d: %s", userID, token) } if time.Since(tokenInfo.CreatedAt) > tokenTTL { - return errors.New("token expired") + return fmt.Errorf("Token expired for userID %d: %s", userID, token) } return nil @@ -73,7 +74,7 @@ func RefreshToken(userID int) (string, error) { randomBytes := make([]byte, 32) _, err := rand.Read(randomBytes) if err != nil { - return "", fmt.Errorf("failed to generate random bytes: %w", err) + return "", fmt.Errorf("Failed to generate random bytes: %w", err) } hash := sha256.Sum256(append(randomBytes, []byte(fmt.Sprintf("%d", userID))...)) @@ -85,6 +86,7 @@ func RefreshToken(userID int) (string, error) { UserID: userID, } + postLog.Debug(fmt.Sprintf("[RefreshToken] Refreshed token for userID %d: %s", userID, token)) return token, nil } @@ -92,6 +94,7 @@ func RemoveToken(userID int) { tokenMux.Lock() defer tokenMux.Unlock() delete(tokenMap, userID) + postLog.Debug(fmt.Sprintf("[RemoveToken] Removed token for userID %d: %s", userID, tokenMap[userID].Token)) } func GetTokenInfo(userID int) (*TokenInfo, error) { @@ -100,7 +103,7 @@ func GetTokenInfo(userID int) (*TokenInfo, error) { tokenInfo, exists := tokenMap[userID] if !exists { - return nil, errors.New("token not found") + return nil, fmt.Errorf("Token not found for userID %d: %s", userID, tokenMap[userID].Token) } return tokenInfo, nil @@ -111,10 +114,11 @@ func extractUserIDFromToken(token string) (int, error) { defer tokenMux.RUnlock() for userID, tokenInfo := range tokenMap { if tokenInfo.Token == token { + postLog.Debug(fmt.Sprintf("[extractUserIDFromToken] Extracted userID %d from token: %s", userID, token)) return userID, nil } } - return 0, errors.New("invalid token") + return 0, fmt.Errorf("Invalid token: %s", token) } func CleanupExpiredTokens() { @@ -124,6 +128,7 @@ func CleanupExpiredTokens() { for userID, tokenInfo := range tokenMap { if time.Since(tokenInfo.CreatedAt) > tokenTTL { delete(tokenMap, userID) + postLog.Debug(fmt.Sprintf("[CleanupExpiredTokens] Removed expired token for userID %d: %s", userID, tokenInfo.Token)) } } } @@ -136,12 +141,13 @@ func hashPassword(password string) (string, error) { func verifyPassword(password, hashedPassword string) bool { hash, err := hashPassword(password) if err != nil { + postLog.Error(fmt.Sprintf("[verifyPassword] Failed to hash password: %v", err)) return false } return hash == hashedPassword } -func isValidPassword(password string) bool { +func isValidPassword(password string) bool { // Validate password complexity and generate hash if len(password) < 8 { return false } @@ -175,7 +181,7 @@ func ValidateTimeStamp(timeStamp int64) error { } currentTime := time.Now().UnixMilli() if currentTime-timeStamp > 3000 || timeStamp-currentTime > 3000 { - return errors.New("timestamp out of valid range") + return fmt.Errorf("Timestamp out of valid range: %d", timeStamp) } return nil } diff --git a/frpc.go b/frpc.go index 975abf1..2b5deae 100644 --- a/frpc.go +++ b/frpc.go @@ -12,6 +12,7 @@ import ( "path/filepath" "strconv" "strings" + "super-frpc/postLog" "time" ) @@ -58,13 +59,15 @@ func CloseFrpcDatabase() error { func CreateInstanceHandler(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.Debug(fmt.Sprintf("[CreateInstanceHandler] 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.Error(fmt.Sprintf("[CreateInstanceHandler] Failed to read request body: %v", err)) + SendErrorResponse(w, http.StatusBadRequest, "Failed to read request body") return } defer r.Body.Close() @@ -72,7 +75,8 @@ func CreateInstanceHandler(w http.ResponseWriter, r *http.Request) { // 先解析为map,处理类型不匹配的情况 var reqMap map[string]interface{} if err := json.Unmarshal(body, &reqMap); err != nil { - SendErrorResponse(w, http.StatusBadRequest, "invalid request format") + postLog.Error(fmt.Sprintf("[CreateInstanceHandler] Failed to unmarshal request body: %v", err)) + SendErrorResponse(w, http.StatusBadRequest, "Invalid request format") return } @@ -107,7 +111,7 @@ func CreateInstanceHandler(w http.ResponseWriter, r *http.Request) { // 处理instanceInfo字段 instanceInfoMap, ok := reqMap["instanceInfo"].(map[string]interface{}) if !ok { - SendErrorResponse(w, http.StatusBadRequest, "invalid instanceInfo format") + SendErrorResponse(w, http.StatusBadRequest, "Invalid instanceInfo format") return } @@ -137,24 +141,27 @@ func CreateInstanceHandler(w http.ResponseWriter, r *http.Request) { // 重新序列化为JSON,用于ValidateRequestWithBody reqBody, err := json.Marshal(req) if err != nil { - SendErrorResponse(w, http.StatusBadRequest, "invalid request format") + postLog.Error(fmt.Sprintf("[CreateInstanceHandler] Failed to marshal request body: %v", err)) + SendErrorResponse(w, http.StatusBadRequest, "Invalid request format") return } userID, _, err := ValidateRequestWithBody(w, r, reqBody) if err != nil { + postLog.Error(fmt.Sprintf("[CreateInstanceHandler] Failed to validate request body: %v", err)) SendErrorResponse(w, http.StatusUnauthorized, err.Error()) return } if err := CheckPermission(userID, "superuser", "admin"); err != nil { + postLog.Error(fmt.Sprintf("[CreateInstanceHandler] Failed to check permission: %v", err)) SendErrorResponse(w, http.StatusForbidden, err.Error()) return } if req.InstanceInfo.Name == "" || req.InstanceInfo.ServerAddr == "" || req.InstanceInfo.ServerPort == "" || req.InstanceInfo.AuthMethod == "" { - SendErrorResponse(w, http.StatusBadRequest, "missing required fields in instanceInfo") + SendErrorResponse(w, http.StatusBadRequest, "Missing required fields in instanceInfo") return } @@ -165,13 +172,15 @@ func CreateInstanceHandler(w http.ResponseWriter, r *http.Request) { user, err := GetUserByID(userID) if err != nil { - SendErrorResponse(w, http.StatusInternalServerError, "failed to get user info") + postLog.Error(fmt.Sprintf("[CreateInstanceHandler] Failed to get user info: %v", err)) + SendErrorResponse(w, http.StatusInternalServerError, "Failed to get user info") return } configDir, err := GetConfigDir() if err != nil { - SendErrorResponse(w, http.StatusInternalServerError, "failed to get config directory") + postLog.Error(fmt.Sprintf("[CreateInstanceHandler] Failed to get config directory: %v", err)) + SendErrorResponse(w, http.StatusInternalServerError, "Failed to get config directory") return } @@ -180,7 +189,8 @@ func CreateInstanceHandler(w http.ResponseWriter, r *http.Request) { configContent := generateFrpcConfig(req.InstanceInfo) if err := os.WriteFile(configPath, []byte(configContent), 0644); err != nil { - SendErrorResponse(w, http.StatusInternalServerError, "failed to create config file") + postLog.Error(fmt.Sprintf("[CreateInstanceHandler] Failed to create config file %s: %v", configPath, err)) + SendErrorResponse(w, http.StatusInternalServerError, "Failed to create config file") return } @@ -192,7 +202,8 @@ func CreateInstanceHandler(w http.ResponseWriter, r *http.Request) { if err != nil { os.Remove(configPath) - SendErrorResponse(w, http.StatusInternalServerError, "failed to save instance to database") + postLog.Error(fmt.Sprintf("[CreateInstanceHandler] Failed to save instance %s to database: %v", req.InstanceInfo.Name, err)) + SendErrorResponse(w, http.StatusInternalServerError, "Failed to save instance to database") return } @@ -200,12 +211,13 @@ func CreateInstanceHandler(w http.ResponseWriter, r *http.Request) { if err := createBootService(user.Username, req.InstanceInfo.Name, configPath, runUser); err != nil { frpcDB.Exec("DELETE FROM frpcInstances WHERE userID = ? AND name = ?", userID, req.InstanceInfo.Name) os.Remove(configPath) - SendErrorResponse(w, http.StatusInternalServerError, "failed to create boot service") + postLog.Error(fmt.Sprintf("[CreateInstanceHandler] Failed to create boot service for instance %s: %v", req.InstanceInfo.Name, err)) + SendErrorResponse(w, http.StatusInternalServerError, "Failed to create boot service") return } } - SendSuccessResponse(w, "instance created successfully", map[string]interface{}{ + SendSuccessResponse(w, "Instance created successfully", map[string]interface{}{ "name": req.InstanceInfo.Name, "configPath": configPath, "bootAtStart": req.BootAtStart, @@ -214,31 +226,35 @@ func CreateInstanceHandler(w http.ResponseWriter, r *http.Request) { func DeleteInstanceHandler(w http.ResponseWriter, r *http.Request, instanceName string) { if r.Method != http.MethodPost { - SendErrorResponse(w, http.StatusMethodNotAllowed, "invalid request method") + SendErrorResponse(w, http.StatusMethodNotAllowed, "Invalid request method") return } body, err := io.ReadAll(r.Body) if err != nil { - SendErrorResponse(w, http.StatusBadRequest, "failed to read request body") + postLog.Error(fmt.Sprintf("[DeleteInstanceHandler] Failed to read request body: %v", err)) + SendErrorResponse(w, http.StatusBadRequest, "Failed to read request body") return } defer r.Body.Close() userID, _, err := ValidateRequestWithBody(w, r, body) if err != nil { + postLog.Error(fmt.Sprintf("[DeleteInstanceHandler] Failed to validate request body: %v", err)) SendErrorResponse(w, http.StatusUnauthorized, err.Error()) return } if err := CheckPermission(userID, "superuser", "admin"); err != nil { + postLog.Error(fmt.Sprintf("[DeleteInstanceHandler] Failed to check permission: %v", err)) SendErrorResponse(w, http.StatusForbidden, err.Error()) return } user, err := GetUserByID(userID) if err != nil { - SendErrorResponse(w, http.StatusInternalServerError, "failed to get user info") + postLog.Error(fmt.Sprintf("[DeleteInstanceHandler] Failed to get user info: %v", err)) + SendErrorResponse(w, http.StatusInternalServerError, "Failed to get user info") return } @@ -255,68 +271,78 @@ func DeleteInstanceHandler(w http.ResponseWriter, r *http.Request, instanceName return } if err != nil { - SendErrorResponse(w, http.StatusInternalServerError, "failed to query instance") + postLog.Error(fmt.Sprintf("[DeleteInstanceHandler] Failed to query instance: %v", err)) + SendErrorResponse(w, http.StatusInternalServerError, "Failed to query instance") return } if instance.BootAtStart { if err := removeBootService(user.Username, instanceName); err != nil { - SendErrorResponse(w, http.StatusInternalServerError, "failed to remove boot service") + postLog.Error(fmt.Sprintf("[DeleteInstanceHandler] Failed to remove boot service for instance %s: %v", instanceName, err)) + SendErrorResponse(w, http.StatusInternalServerError, "Failed to remove boot service") return } } if _, err := os.Stat(instance.ConfigPath); err == nil { if err := os.Remove(instance.ConfigPath); err != nil { - SendErrorResponse(w, http.StatusInternalServerError, "failed to remove config file") + postLog.Error(fmt.Sprintf("[DeleteInstanceHandler] Failed to remove config file %s: %v", instance.ConfigPath, err)) + SendErrorResponse(w, http.StatusInternalServerError, "Failed to remove config file") return } } _, err = frpcDB.Exec("DELETE FROM frpcInstances WHERE id = ?", instance.ID) if err != nil { - SendErrorResponse(w, http.StatusInternalServerError, "failed to delete instance from database") + postLog.Error(fmt.Sprintf("[DeleteInstanceHandler] Failed to delete instance %s from database: %v", instanceName, err)) + SendErrorResponse(w, http.StatusInternalServerError, "Failed to delete instance from database") return } - SendSuccessResponse(w, "instance deleted successfully", map[string]interface{}{ + SendSuccessResponse(w, "Instance deleted successfully", map[string]interface{}{ "name": instanceName, }) } func ModifyInstanceHandler(w http.ResponseWriter, r *http.Request, instanceName string) { if r.Method != http.MethodPost { - SendErrorResponse(w, http.StatusMethodNotAllowed, "invalid request method") + postLog.Error(fmt.Sprintf("[ModifyInstanceHandler] Invalid request method: %s", r.Method)) + SendErrorResponse(w, http.StatusMethodNotAllowed, "Invalid request method") return } body, err := io.ReadAll(r.Body) if err != nil { - SendErrorResponse(w, http.StatusBadRequest, "failed to read request body") + postLog.Error(fmt.Sprintf("[ModifyInstanceHandler] Failed to read request body: %v", err)) + SendErrorResponse(w, http.StatusBadRequest, "Failed to read request body") return } defer r.Body.Close() var reqMap map[string]interface{} if err := json.Unmarshal(body, &reqMap); err != nil { - SendErrorResponse(w, http.StatusBadRequest, "invalid request format") + postLog.Error(fmt.Sprintf("[ModifyInstanceHandler] Failed to unmarshal request body: %v", err)) + SendErrorResponse(w, http.StatusBadRequest, "Invalid request format") return } userID, _, err := ValidateRequestWithBody(w, r, body) if err != nil { + postLog.Error(fmt.Sprintf("[ModifyInstanceHandler] Failed to validate request body: %v", err)) SendErrorResponse(w, http.StatusUnauthorized, err.Error()) return } if err := CheckPermission(userID, "superuser", "admin"); err != nil { + postLog.Error(fmt.Sprintf("[ModifyInstanceHandler] Failed to check permission: %v", err)) SendErrorResponse(w, http.StatusForbidden, err.Error()) return } user, err := GetUserByID(userID) if err != nil { - SendErrorResponse(w, http.StatusInternalServerError, "failed to get user info") + postLog.Error(fmt.Sprintf("[ModifyInstanceHandler] Failed to get user info: %v", err)) + SendErrorResponse(w, http.StatusInternalServerError, "Failed to get user info") return } @@ -333,7 +359,8 @@ func ModifyInstanceHandler(w http.ResponseWriter, r *http.Request, instanceName return } if err != nil { - SendErrorResponse(w, http.StatusInternalServerError, "failed to query instance") + postLog.Error(fmt.Sprintf("[ModifyInstanceHandler] Failed to query instance: %v", err)) + SendErrorResponse(w, http.StatusInternalServerError, "Failed to query instance") return } @@ -368,7 +395,8 @@ func ModifyInstanceHandler(w http.ResponseWriter, r *http.Request, instanceName if newName != instance.Name || newRunUser != instance.RunUser { configDir, err := GetConfigDir() if err != nil { - SendErrorResponse(w, http.StatusInternalServerError, "failed to get config directory") + postLog.Error(fmt.Sprintf("[ModifyInstanceHandler] Failed to get config directory: %v", err)) + SendErrorResponse(w, http.StatusInternalServerError, "Failed to get config directory") return } @@ -378,7 +406,8 @@ func ModifyInstanceHandler(w http.ResponseWriter, r *http.Request, instanceName if oldConfigPath != newConfigPath { if _, err := os.Stat(oldConfigPath); err == nil { if err := os.Rename(oldConfigPath, newConfigPath); err != nil { - SendErrorResponse(w, http.StatusInternalServerError, "failed to rename config file") + postLog.Error(fmt.Sprintf("[ModifyInstanceHandler] Failed to rename config file %s to %s: %v", oldConfigPath, newConfigPath, err)) + SendErrorResponse(w, http.StatusInternalServerError, "Failed to rename config file") return } } @@ -397,7 +426,8 @@ func ModifyInstanceHandler(w http.ResponseWriter, r *http.Request, instanceName configContent := generateFrpcConfig(info) if err := os.WriteFile(newConfigPath, []byte(configContent), 0644); err != nil { - SendErrorResponse(w, http.StatusInternalServerError, "failed to update config file") + postLog.Error(fmt.Sprintf("[ModifyInstanceHandler] Failed to update config file: %v", err)) + SendErrorResponse(w, http.StatusInternalServerError, "Failed to update config file") return } @@ -408,7 +438,8 @@ func ModifyInstanceHandler(w http.ResponseWriter, r *http.Request, instanceName `, newName, newServerAddr, newServerPort, newAuthMethod, newBootAtStart, newRunUser, newConfigPath, instance.ID) if err != nil { - SendErrorResponse(w, http.StatusInternalServerError, "failed to update instance in database") + postLog.Error(fmt.Sprintf("[ModifyInstanceHandler] Failed to update instance in database: %v", err)) + SendErrorResponse(w, http.StatusInternalServerError, "Failed to update instance in database") return } @@ -421,7 +452,7 @@ func ModifyInstanceHandler(w http.ResponseWriter, r *http.Request, instanceName createBootService(user.Username, newName, newConfigPath, newRunUser) } - SendSuccessResponse(w, "instance modified successfully", map[string]interface{}{ + SendSuccessResponse(w, "Instance modified successfully", map[string]interface{}{ "name": newName, "configPath": newConfigPath, }) @@ -429,19 +460,21 @@ func ModifyInstanceHandler(w http.ResponseWriter, r *http.Request, instanceName func ListInstancesHandler(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { - SendErrorResponse(w, http.StatusMethodNotAllowed, "invalid request method") + SendErrorResponse(w, http.StatusMethodNotAllowed, "Invalid request method") return } userID, _, err := ValidateRequestWithHeader(w, r) if err != nil { - SendErrorResponse(w, http.StatusUnauthorized, err.Error()) + postLog.Error(fmt.Sprintf("[ListInstancesHandler] Failed to validate request: %v", err)) + SendErrorResponse(w, http.StatusUnauthorized, "Failed to validate request") return } userType, err := GetUserType(userID) if err != nil { - SendErrorResponse(w, http.StatusInternalServerError, "failed to get user type") + postLog.Error(fmt.Sprintf("[ListInstancesHandler] Failed to get user type: %v", err)) + SendErrorResponse(w, http.StatusInternalServerError, "Failed to get user type") return } @@ -450,7 +483,8 @@ func ListInstancesHandler(w http.ResponseWriter, r *http.Request) { FROM frpcInstances WHERE userID = ? `, userID) if err != nil { - SendErrorResponse(w, http.StatusInternalServerError, "failed to query instances") + postLog.Error(fmt.Sprintf("[ListInstancesHandler] Failed to query instances: %v", err)) + SendErrorResponse(w, http.StatusInternalServerError, "Failed to query instances") return } defer rows.Close() @@ -463,7 +497,8 @@ func ListInstancesHandler(w http.ResponseWriter, r *http.Request) { &instance.ID, &instance.UserID, &instance.Name, &instance.ServerAddr, &instance.ServerPort, &instance.AuthMethod, &instance.BootAtStart, &instance.RunUser, &instance.ConfigPath, &createdAtStr, ); err != nil { - SendErrorResponse(w, http.StatusInternalServerError, "failed to scan instance") + postLog.Error(fmt.Sprintf("[ListInstancesHandler] Failed to scan instance: %v", err)) + SendErrorResponse(w, http.StatusInternalServerError, "Failed to scan instance") return } @@ -491,7 +526,7 @@ func ListInstancesHandler(w http.ResponseWriter, r *http.Request) { instances = []map[string]interface{}{} } - SendSuccessResponse(w, "instances retrieved successfully", instances) + SendSuccessResponse(w, "Instances retrieved successfully", instances) } func generateFrpcConfig(info InstanceInfo) string { @@ -511,6 +546,7 @@ func generateFrpcConfig(info InstanceInfo) string { func GetConfigDir() (string, error) { config, err := GetConfig() if err != nil { + postLog.Error(fmt.Sprintf("[GetConfigDir] Failed to get config: %v", err)) return "", err } return config.InstancePath, nil @@ -519,6 +555,7 @@ func GetConfigDir() (string, error) { func GetFrpcPath() (string, error) { config, err := GetConfig() if err != nil { + postLog.Error(fmt.Sprintf("[GetFrpcPath] Failed to get config: %v", err)) return "", err } return config.FrpcPath, nil diff --git a/handlers.go b/handlers.go index d581466..01cdea4 100644 --- a/handlers.go +++ b/handlers.go @@ -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 { diff --git a/main.go b/main.go index a9d43ad..822034d 100644 --- a/main.go +++ b/main.go @@ -11,7 +11,25 @@ import ( "time" ) +type SoftwareInfo struct { + Name string + Version string + Developer string + BuildVer int16 + Description string + BuildType string +} + func main() { + softwareInfo := SoftwareInfo{ + Name: "Super-frpc", + Version: "0.0.1", + Developer: "Madobi Nanami", + BuildVer: 1, + Description: "A backend application for managing local frpc instances, allowing users to easily start, stop, restart, and perform daily maintenance operations on frpc instances. It also provides automated error handling, such as automatic restart when an instance crashes.", + BuildType: "debug", + } + postLog.Info(fmt.Sprintf("%s %s (Build %d.%s) by %s", softwareInfo.Name, softwareInfo.Version, softwareInfo.BuildVer, softwareInfo.BuildType, softwareInfo.Developer)) configPath := flag.String("config", "./config.json", "path to config file") dbPath := flag.String("db", "./database.db", "path to database file") flag.Parse() @@ -23,7 +41,7 @@ func main() { "configDir": "./configs" }` if err := os.WriteFile(*configPath, []byte(defaultConfig), 0644); err != nil { - postLog.Fatal(fmt.Sprintf("Failed to create default config file: %v", err)) + postLog.Warning(fmt.Sprintf("Failed to create default config file: %v", err)) } postLog.Info(fmt.Sprintf("Created default config file at %s", *configPath)) } diff --git a/router.go b/router.go index 68c8da6..281ba88 100644 --- a/router.go +++ b/router.go @@ -3,9 +3,11 @@ package main import ( "net/http" "strings" + "super-frpc/postLog" ) func setupRoutes() { + postLog.Info("Setting up routes...") http.HandleFunc("/register", RegisterHandler) http.HandleFunc("/login", LoginHandler)