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
+16 -10
View File
@@ -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
}
+73 -36
View File
@@ -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
+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 {
+19 -1
View File
@@ -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))
}
+2
View File
@@ -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)