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 72eb90957c
commit e3b3a3aa98
5 changed files with 167 additions and 85 deletions
+16 -10
View File
@@ -5,9 +5,9 @@ import (
"crypto/sha256" "crypto/sha256"
"encoding/base64" "encoding/base64"
"encoding/hex" "encoding/hex"
"errors"
"fmt" "fmt"
"strings" "strings"
"super-frpc/postLog"
"sync" "sync"
"time" "time"
) )
@@ -28,7 +28,7 @@ func GenerateToken(userID int) (string, error) {
randomBytes := make([]byte, 32) randomBytes := make([]byte, 32)
_, err := rand.Read(randomBytes) _, err := rand.Read(randomBytes)
if err != nil { 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))...)) hash := sha256.Sum256(append(randomBytes, []byte(fmt.Sprintf("%d", userID))...))
@@ -43,6 +43,7 @@ func GenerateToken(userID int) (string, error) {
UserID: userID, UserID: userID,
} }
postLog.Debug(fmt.Sprintf("[GenerateToken] Generated token for userID %d: %s", userID, token))
return token, nil return token, nil
} }
@@ -52,15 +53,15 @@ func ValidateToken(userID int, token string) error {
tokenInfo, exists := tokenMap[userID] tokenInfo, exists := tokenMap[userID]
if !exists { if !exists {
return errors.New("token not found") return fmt.Errorf("Token not found for userID %d: %s", userID, token)
} }
if tokenInfo.Token != 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 { if time.Since(tokenInfo.CreatedAt) > tokenTTL {
return errors.New("token expired") return fmt.Errorf("Token expired for userID %d: %s", userID, token)
} }
return nil return nil
@@ -73,7 +74,7 @@ func RefreshToken(userID int) (string, error) {
randomBytes := make([]byte, 32) randomBytes := make([]byte, 32)
_, err := rand.Read(randomBytes) _, err := rand.Read(randomBytes)
if err != nil { 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))...)) hash := sha256.Sum256(append(randomBytes, []byte(fmt.Sprintf("%d", userID))...))
@@ -85,6 +86,7 @@ func RefreshToken(userID int) (string, error) {
UserID: userID, UserID: userID,
} }
postLog.Debug(fmt.Sprintf("[RefreshToken] Refreshed token for userID %d: %s", userID, token))
return token, nil return token, nil
} }
@@ -92,6 +94,7 @@ func RemoveToken(userID int) {
tokenMux.Lock() tokenMux.Lock()
defer tokenMux.Unlock() defer tokenMux.Unlock()
delete(tokenMap, userID) delete(tokenMap, userID)
postLog.Debug(fmt.Sprintf("[RemoveToken] Removed token for userID %d: %s", userID, tokenMap[userID].Token))
} }
func GetTokenInfo(userID int) (*TokenInfo, error) { func GetTokenInfo(userID int) (*TokenInfo, error) {
@@ -100,7 +103,7 @@ func GetTokenInfo(userID int) (*TokenInfo, error) {
tokenInfo, exists := tokenMap[userID] tokenInfo, exists := tokenMap[userID]
if !exists { 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 return tokenInfo, nil
@@ -111,10 +114,11 @@ func extractUserIDFromToken(token string) (int, error) {
defer tokenMux.RUnlock() defer tokenMux.RUnlock()
for userID, tokenInfo := range tokenMap { for userID, tokenInfo := range tokenMap {
if tokenInfo.Token == token { if tokenInfo.Token == token {
postLog.Debug(fmt.Sprintf("[extractUserIDFromToken] Extracted userID %d from token: %s", userID, token))
return userID, nil return userID, nil
} }
} }
return 0, errors.New("invalid token") return 0, fmt.Errorf("Invalid token: %s", token)
} }
func CleanupExpiredTokens() { func CleanupExpiredTokens() {
@@ -124,6 +128,7 @@ func CleanupExpiredTokens() {
for userID, tokenInfo := range tokenMap { for userID, tokenInfo := range tokenMap {
if time.Since(tokenInfo.CreatedAt) > tokenTTL { if time.Since(tokenInfo.CreatedAt) > tokenTTL {
delete(tokenMap, userID) 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 { func verifyPassword(password, hashedPassword string) bool {
hash, err := hashPassword(password) hash, err := hashPassword(password)
if err != nil { if err != nil {
postLog.Error(fmt.Sprintf("[verifyPassword] Failed to hash password: %v", err))
return false return false
} }
return hash == hashedPassword return hash == hashedPassword
} }
func isValidPassword(password string) bool { func isValidPassword(password string) bool { // Validate password complexity and generate hash
if len(password) < 8 { if len(password) < 8 {
return false return false
} }
@@ -175,7 +181,7 @@ func ValidateTimeStamp(timeStamp int64) error {
} }
currentTime := time.Now().UnixMilli() currentTime := time.Now().UnixMilli()
if currentTime-timeStamp > 3000 || timeStamp-currentTime > 3000 { 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 return nil
} }
+73 -36
View File
@@ -12,6 +12,7 @@ import (
"path/filepath" "path/filepath"
"strconv" "strconv"
"strings" "strings"
"super-frpc/postLog"
"time" "time"
) )
@@ -58,13 +59,15 @@ func CloseFrpcDatabase() error {
func CreateInstanceHandler(w http.ResponseWriter, r *http.Request) { func CreateInstanceHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost { 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 return
} }
body, err := io.ReadAll(r.Body) body, err := io.ReadAll(r.Body)
if err != nil { 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 return
} }
defer r.Body.Close() defer r.Body.Close()
@@ -72,7 +75,8 @@ func CreateInstanceHandler(w http.ResponseWriter, r *http.Request) {
// 先解析为map,处理类型不匹配的情况 // 先解析为map,处理类型不匹配的情况
var reqMap map[string]interface{} var reqMap map[string]interface{}
if err := json.Unmarshal(body, &reqMap); err != nil { 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 return
} }
@@ -107,7 +111,7 @@ func CreateInstanceHandler(w http.ResponseWriter, r *http.Request) {
// 处理instanceInfo字段 // 处理instanceInfo字段
instanceInfoMap, ok := reqMap["instanceInfo"].(map[string]interface{}) instanceInfoMap, ok := reqMap["instanceInfo"].(map[string]interface{})
if !ok { if !ok {
SendErrorResponse(w, http.StatusBadRequest, "invalid instanceInfo format") SendErrorResponse(w, http.StatusBadRequest, "Invalid instanceInfo format")
return return
} }
@@ -137,24 +141,27 @@ func CreateInstanceHandler(w http.ResponseWriter, r *http.Request) {
// 重新序列化为JSON,用于ValidateRequestWithBody // 重新序列化为JSON,用于ValidateRequestWithBody
reqBody, err := json.Marshal(req) reqBody, err := json.Marshal(req)
if err != nil { 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 return
} }
userID, _, err := ValidateRequestWithBody(w, r, reqBody) userID, _, err := ValidateRequestWithBody(w, r, reqBody)
if err != nil { if err != nil {
postLog.Error(fmt.Sprintf("[CreateInstanceHandler] Failed to validate request body: %v", err))
SendErrorResponse(w, http.StatusUnauthorized, err.Error()) SendErrorResponse(w, http.StatusUnauthorized, err.Error())
return return
} }
if err := CheckPermission(userID, "superuser", "admin"); err != nil { 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()) SendErrorResponse(w, http.StatusForbidden, err.Error())
return return
} }
if req.InstanceInfo.Name == "" || req.InstanceInfo.ServerAddr == "" || if req.InstanceInfo.Name == "" || req.InstanceInfo.ServerAddr == "" ||
req.InstanceInfo.ServerPort == "" || req.InstanceInfo.AuthMethod == "" { 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 return
} }
@@ -165,13 +172,15 @@ func CreateInstanceHandler(w http.ResponseWriter, r *http.Request) {
user, err := GetUserByID(userID) user, err := GetUserByID(userID)
if err != nil { 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 return
} }
configDir, err := GetConfigDir() configDir, err := GetConfigDir()
if err != nil { 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 return
} }
@@ -180,7 +189,8 @@ func CreateInstanceHandler(w http.ResponseWriter, r *http.Request) {
configContent := generateFrpcConfig(req.InstanceInfo) configContent := generateFrpcConfig(req.InstanceInfo)
if err := os.WriteFile(configPath, []byte(configContent), 0644); err != nil { 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 return
} }
@@ -192,7 +202,8 @@ func CreateInstanceHandler(w http.ResponseWriter, r *http.Request) {
if err != nil { if err != nil {
os.Remove(configPath) 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 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 { 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) frpcDB.Exec("DELETE FROM frpcInstances WHERE userID = ? AND name = ?", userID, req.InstanceInfo.Name)
os.Remove(configPath) 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 return
} }
} }
SendSuccessResponse(w, "instance created successfully", map[string]interface{}{ SendSuccessResponse(w, "Instance created successfully", map[string]interface{}{
"name": req.InstanceInfo.Name, "name": req.InstanceInfo.Name,
"configPath": configPath, "configPath": configPath,
"bootAtStart": req.BootAtStart, "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) { func DeleteInstanceHandler(w http.ResponseWriter, r *http.Request, instanceName string) {
if r.Method != http.MethodPost { if r.Method != http.MethodPost {
SendErrorResponse(w, http.StatusMethodNotAllowed, "invalid request method") SendErrorResponse(w, http.StatusMethodNotAllowed, "Invalid request method")
return return
} }
body, err := io.ReadAll(r.Body) body, err := io.ReadAll(r.Body)
if err != nil { 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 return
} }
defer r.Body.Close() defer r.Body.Close()
userID, _, err := ValidateRequestWithBody(w, r, body) userID, _, err := ValidateRequestWithBody(w, r, body)
if err != nil { if err != nil {
postLog.Error(fmt.Sprintf("[DeleteInstanceHandler] Failed to validate request body: %v", err))
SendErrorResponse(w, http.StatusUnauthorized, err.Error()) SendErrorResponse(w, http.StatusUnauthorized, err.Error())
return return
} }
if err := CheckPermission(userID, "superuser", "admin"); err != nil { 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()) SendErrorResponse(w, http.StatusForbidden, err.Error())
return return
} }
user, err := GetUserByID(userID) user, err := GetUserByID(userID)
if err != nil { 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 return
} }
@@ -255,68 +271,78 @@ func DeleteInstanceHandler(w http.ResponseWriter, r *http.Request, instanceName
return return
} }
if err != nil { 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 return
} }
if instance.BootAtStart { if instance.BootAtStart {
if err := removeBootService(user.Username, instanceName); err != nil { 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 return
} }
} }
if _, err := os.Stat(instance.ConfigPath); err == nil { if _, err := os.Stat(instance.ConfigPath); err == nil {
if err := os.Remove(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 return
} }
} }
_, err = frpcDB.Exec("DELETE FROM frpcInstances WHERE id = ?", instance.ID) _, err = frpcDB.Exec("DELETE FROM frpcInstances WHERE id = ?", instance.ID)
if err != nil { 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 return
} }
SendSuccessResponse(w, "instance deleted successfully", map[string]interface{}{ SendSuccessResponse(w, "Instance deleted successfully", map[string]interface{}{
"name": instanceName, "name": instanceName,
}) })
} }
func ModifyInstanceHandler(w http.ResponseWriter, r *http.Request, instanceName string) { func ModifyInstanceHandler(w http.ResponseWriter, r *http.Request, instanceName string) {
if r.Method != http.MethodPost { 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 return
} }
body, err := io.ReadAll(r.Body) body, err := io.ReadAll(r.Body)
if err != nil { 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 return
} }
defer r.Body.Close() defer r.Body.Close()
var reqMap map[string]interface{} var reqMap map[string]interface{}
if err := json.Unmarshal(body, &reqMap); err != nil { 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 return
} }
userID, _, err := ValidateRequestWithBody(w, r, body) userID, _, err := ValidateRequestWithBody(w, r, body)
if err != nil { if err != nil {
postLog.Error(fmt.Sprintf("[ModifyInstanceHandler] Failed to validate request body: %v", err))
SendErrorResponse(w, http.StatusUnauthorized, err.Error()) SendErrorResponse(w, http.StatusUnauthorized, err.Error())
return return
} }
if err := CheckPermission(userID, "superuser", "admin"); err != nil { 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()) SendErrorResponse(w, http.StatusForbidden, err.Error())
return return
} }
user, err := GetUserByID(userID) user, err := GetUserByID(userID)
if err != nil { 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 return
} }
@@ -333,7 +359,8 @@ func ModifyInstanceHandler(w http.ResponseWriter, r *http.Request, instanceName
return return
} }
if err != nil { 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 return
} }
@@ -368,7 +395,8 @@ func ModifyInstanceHandler(w http.ResponseWriter, r *http.Request, instanceName
if newName != instance.Name || newRunUser != instance.RunUser { if newName != instance.Name || newRunUser != instance.RunUser {
configDir, err := GetConfigDir() configDir, err := GetConfigDir()
if err != nil { 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 return
} }
@@ -378,7 +406,8 @@ func ModifyInstanceHandler(w http.ResponseWriter, r *http.Request, instanceName
if oldConfigPath != newConfigPath { if oldConfigPath != newConfigPath {
if _, err := os.Stat(oldConfigPath); err == nil { if _, err := os.Stat(oldConfigPath); err == nil {
if err := os.Rename(oldConfigPath, newConfigPath); 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 return
} }
} }
@@ -397,7 +426,8 @@ func ModifyInstanceHandler(w http.ResponseWriter, r *http.Request, instanceName
configContent := generateFrpcConfig(info) configContent := generateFrpcConfig(info)
if err := os.WriteFile(newConfigPath, []byte(configContent), 0644); err != nil { 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 return
} }
@@ -408,7 +438,8 @@ func ModifyInstanceHandler(w http.ResponseWriter, r *http.Request, instanceName
`, newName, newServerAddr, newServerPort, newAuthMethod, newBootAtStart, newRunUser, newConfigPath, instance.ID) `, newName, newServerAddr, newServerPort, newAuthMethod, newBootAtStart, newRunUser, newConfigPath, instance.ID)
if err != nil { 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 return
} }
@@ -421,7 +452,7 @@ func ModifyInstanceHandler(w http.ResponseWriter, r *http.Request, instanceName
createBootService(user.Username, newName, newConfigPath, newRunUser) createBootService(user.Username, newName, newConfigPath, newRunUser)
} }
SendSuccessResponse(w, "instance modified successfully", map[string]interface{}{ SendSuccessResponse(w, "Instance modified successfully", map[string]interface{}{
"name": newName, "name": newName,
"configPath": newConfigPath, "configPath": newConfigPath,
}) })
@@ -429,19 +460,21 @@ func ModifyInstanceHandler(w http.ResponseWriter, r *http.Request, instanceName
func ListInstancesHandler(w http.ResponseWriter, r *http.Request) { func ListInstancesHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet { if r.Method != http.MethodGet {
SendErrorResponse(w, http.StatusMethodNotAllowed, "invalid request method") SendErrorResponse(w, http.StatusMethodNotAllowed, "Invalid request method")
return return
} }
userID, _, err := ValidateRequestWithHeader(w, r) userID, _, err := ValidateRequestWithHeader(w, r)
if err != nil { 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 return
} }
userType, err := GetUserType(userID) userType, err := GetUserType(userID)
if err != nil { 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 return
} }
@@ -450,7 +483,8 @@ func ListInstancesHandler(w http.ResponseWriter, r *http.Request) {
FROM frpcInstances WHERE userID = ? FROM frpcInstances WHERE userID = ?
`, userID) `, userID)
if err != nil { 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 return
} }
defer rows.Close() 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.ID, &instance.UserID, &instance.Name, &instance.ServerAddr, &instance.ServerPort,
&instance.AuthMethod, &instance.BootAtStart, &instance.RunUser, &instance.ConfigPath, &createdAtStr, &instance.AuthMethod, &instance.BootAtStart, &instance.RunUser, &instance.ConfigPath, &createdAtStr,
); err != nil { ); 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 return
} }
@@ -491,7 +526,7 @@ func ListInstancesHandler(w http.ResponseWriter, r *http.Request) {
instances = []map[string]interface{}{} instances = []map[string]interface{}{}
} }
SendSuccessResponse(w, "instances retrieved successfully", instances) SendSuccessResponse(w, "Instances retrieved successfully", instances)
} }
func generateFrpcConfig(info InstanceInfo) string { func generateFrpcConfig(info InstanceInfo) string {
@@ -511,6 +546,7 @@ func generateFrpcConfig(info InstanceInfo) string {
func GetConfigDir() (string, error) { func GetConfigDir() (string, error) {
config, err := GetConfig() config, err := GetConfig()
if err != nil { if err != nil {
postLog.Error(fmt.Sprintf("[GetConfigDir] Failed to get config: %v", err))
return "", err return "", err
} }
return config.InstancePath, nil return config.InstancePath, nil
@@ -519,6 +555,7 @@ func GetConfigDir() (string, error) {
func GetFrpcPath() (string, error) { func GetFrpcPath() (string, error) {
config, err := GetConfig() config, err := GetConfig()
if err != nil { if err != nil {
postLog.Error(fmt.Sprintf("[GetFrpcPath] Failed to get config: %v", err))
return "", err return "", err
} }
return config.FrpcPath, nil return config.FrpcPath, nil
+57 -38
View File
@@ -2,7 +2,6 @@ package main
import ( import (
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
@@ -32,25 +31,29 @@ type Response struct {
func RegisterHandler(w http.ResponseWriter, r *http.Request) { func RegisterHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost { 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 return
} }
body, err := io.ReadAll(r.Body) body, err := io.ReadAll(r.Body)
if err != nil { 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 return
} }
defer r.Body.Close() defer r.Body.Close()
var req RegisterRequest var req RegisterRequest
if err := json.Unmarshal(body, &req); err != nil { 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 return
} }
if req.Username == "" || req.Passwd == "" { 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 return
} }
@@ -60,12 +63,14 @@ func RegisterHandler(w http.ResponseWriter, r *http.Request) {
} }
if !isValidInput(req.Username) || !isValidInput(req.Passwd) { 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 return
} }
if !isValidPassword(req.Passwd) { 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 return
} }
@@ -80,19 +85,22 @@ func RegisterHandler(w http.ResponseWriter, r *http.Request) {
"visitor": true, "visitor": true,
} }
if !validTypes[userType] { 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 return
} }
userID, err := AddUser(req.Username, req.Passwd, userType) userID, err := AddUser(req.Username, req.Passwd, userType)
if err != nil { if err != nil {
SendErrorResponse(w, http.StatusInternalServerError, err.Error()) SendErrorResponse(w, http.StatusInternalServerError, err.Error())
postLog.Error(fmt.Sprintf("[RegisterHandler] Failed to register user \"%s\": %v", req.Username, err))
return return
} }
user, err := GetUserByID(userID) user, err := GetUserByID(userID)
if err != nil { 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 return
} }
@@ -105,67 +113,78 @@ func RegisterHandler(w http.ResponseWriter, r *http.Request) {
func LoginHandler(w http.ResponseWriter, r *http.Request) { func LoginHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost { 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 return
} }
body, err := io.ReadAll(r.Body) body, err := io.ReadAll(r.Body)
if err != nil { 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 return
} }
defer r.Body.Close() defer r.Body.Close()
var req LoginRequest var req LoginRequest
if err := json.Unmarshal(body, &req); err != nil { 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 return
} }
if req.Username == "" || req.Passwd == "" { 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 return
} }
if err := ValidateTimeStamp(req.TimeStamp); err != nil { if err := ValidateTimeStamp(req.TimeStamp); err != nil {
SendErrorResponse(w, http.StatusBadRequest, err.Error()) SendErrorResponse(w, http.StatusBadRequest, err.Error())
postLog.Warning(fmt.Sprintf("[LoginHandler] User \"%s\" Login failed: invalid timestamp \"%d\"", req.Username, req.TimeStamp))
return return
} }
if !isValidInput(req.Username) || !isValidInput(req.Passwd) { 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 return
} }
user, err := GetUserByUsername(req.Username) user, err := GetUserByUsername(req.Username)
if err != nil { 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 return
} }
if !verifyPassword(req.Passwd, user.Passwd) { 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 return
} }
existingTokenInfo, err := GetTokenInfo(user.UserID) existingTokenInfo, err := GetTokenInfo(user.UserID)
if err == nil && existingTokenInfo != nil { 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 return
} }
token, err := GenerateToken(user.UserID) token, err := GenerateToken(user.UserID)
if err != nil { 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 return
} }
SendSuccessResponse(w, "login successful", map[string]interface{}{ SendSuccessResponse(w, "Login successful", map[string]interface{}{
"token": token, "token": token,
"userID": user.UserID, "userID": user.UserID,
"username": user.Username, "username": user.Username,
"type": user.Type, "type": user.Type,
}) })
postLog.Info(fmt.Sprintf("[LoginHandler] User \"%s\" Login successful", req.Username))
} }
func SendErrorResponse(w http.ResponseWriter, statusCode int, message string) { 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) jsonResp, err := json.Marshal(resp)
if err != nil { 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 return
} }
w.Write(jsonResp) w.Write(jsonResp)
@@ -193,7 +212,7 @@ func SendSuccessResponse(w http.ResponseWriter, message string, data interface{}
} }
jsonResp, err := json.Marshal(resp) jsonResp, err := json.Marshal(resp)
if err != nil { 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 return
} }
w.Write(jsonResp) 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) { func ValidateRequest(w http.ResponseWriter, r *http.Request, requiredFields ...string) (int, string, error) {
body, err := io.ReadAll(r.Body) body, err := io.ReadAll(r.Body)
if err != nil { 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() 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) { func ValidateRequestWithBody(w http.ResponseWriter, r *http.Request, body []byte, requiredFields ...string) (int, string, error) {
var reqMap map[string]interface{} var reqMap map[string]interface{}
if err := json.Unmarshal(body, &reqMap); err != nil { 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) token, ok := reqMap["token"].(string)
if !ok || token == "" { if !ok || token == "" {
return 0, "", errors.New("token is required") return 0, "", fmt.Errorf("Token is required: %s", token)
} }
timeStamp := int64(0) timeStamp := int64(0)
if ts, ok := reqMap["timeStamp"].(float64); ok { if ts, ok := reqMap["timeStamp"].(float64); ok {
timeStamp = int64(ts) timeStamp = int64(ts)
} else if !globalConfig.Debug { } 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 { if err := ValidateTimeStamp(timeStamp); err != nil {
return 0, "", err return 0, "", fmt.Errorf("Invalid timestamp: %w", err)
} }
userID, err := extractUserIDFromToken(token) userID, err := extractUserIDFromToken(token)
if err != nil { if err != nil {
return 0, "", err return 0, "", fmt.Errorf("Invalid token format: %w", err)
} }
if err := ValidateToken(userID, token); err != nil { if err := ValidateToken(userID, token); err != nil {
return 0, "", err return 0, "", fmt.Errorf("Token validation failed: %w", err)
} }
for _, field := range requiredFields { for _, field := range requiredFields {
if _, ok := reqMap[field]; !ok { 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) { func ValidateRequestWithHeader(w http.ResponseWriter, r *http.Request, requiredFields ...string) (int, string, error) {
token := r.Header.Get("X-Token") token := r.Header.Get("X-Token")
if 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") timeStampStr := r.Header.Get("X-Timestamp")
@@ -261,10 +280,10 @@ func ValidateRequestWithHeader(w http.ResponseWriter, r *http.Request, requiredF
var err error var err error
timeStamp, err = strconv.ParseInt(timeStampStr, 10, 64) timeStamp, err = strconv.ParseInt(timeStampStr, 10, 64)
if err != nil { 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 { } 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 { if err := ValidateTimeStamp(timeStamp); err != nil {
@@ -273,17 +292,17 @@ func ValidateRequestWithHeader(w http.ResponseWriter, r *http.Request, requiredF
userID, err := extractUserIDFromToken(token) userID, err := extractUserIDFromToken(token)
if err != nil { if err != nil {
return 0, "", err return 0, "", fmt.Errorf("Invalid token format in header: %w", err)
} }
if err := ValidateToken(userID, token); err != nil { 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 { for _, field := range requiredFields {
headerValue := r.Header.Get(fmt.Sprintf("X-%s", field)) headerValue := r.Header.Get(fmt.Sprintf("X-%s", field))
if headerValue == "" { 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) { func GetUserType(userID int) (string, error) {
user, err := GetUserByID(userID) user, err := GetUserByID(userID)
if err != nil { if err != nil {
return "", err return "", fmt.Errorf("Failed to get user type: %w", err)
} }
return user.Type, nil return user.Type, nil
} }
@@ -301,7 +320,7 @@ func GetUserType(userID int) (string, error) {
func CheckPermission(userID int, requiredTypes ...string) error { func CheckPermission(userID int, requiredTypes ...string) error {
userType, err := GetUserType(userID) userType, err := GetUserType(userID)
if err != nil { if err != nil {
return err return fmt.Errorf("Failed to check permission: %w", err)
} }
for _, t := range requiredTypes { 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 { func GetClientIP(r *http.Request) string {
+19 -1
View File
@@ -11,7 +11,25 @@ import (
"time" "time"
) )
type SoftwareInfo struct {
Name string
Version string
Developer string
BuildVer int16
Description string
BuildType string
}
func main() { 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") configPath := flag.String("config", "./config.json", "path to config file")
dbPath := flag.String("db", "./database.db", "path to database file") dbPath := flag.String("db", "./database.db", "path to database file")
flag.Parse() flag.Parse()
@@ -23,7 +41,7 @@ func main() {
"configDir": "./configs" "configDir": "./configs"
}` }`
if err := os.WriteFile(*configPath, []byte(defaultConfig), 0644); err != nil { 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)) postLog.Info(fmt.Sprintf("Created default config file at %s", *configPath))
} }
+2
View File
@@ -3,9 +3,11 @@ package main
import ( import (
"net/http" "net/http"
"strings" "strings"
"super-frpc/postLog"
) )
func setupRoutes() { func setupRoutes() {
postLog.Info("Setting up routes...")
http.HandleFunc("/register", RegisterHandler) http.HandleFunc("/register", RegisterHandler)
http.HandleFunc("/login", LoginHandler) http.HandleFunc("/login", LoginHandler)