feat(handlers): add duplicate login check and improve request handling

refactor: move postLog package to root directory and enhance logging
fix(frpc): improve request parsing and type handling for instance creation
chore: update config paths and enable debug mode
This commit is contained in:
2026-02-27 21:04:08 +08:00
parent 5020d61780
commit 8f711a3823
6 changed files with 110 additions and 25 deletions
+5
View File
@@ -0,0 +1,5 @@
[common]
server_addr = 127.0.0.1
server_port = 7000
auth_method = token
=
BIN
View File
Binary file not shown.
+77 -3
View File
@@ -10,6 +10,7 @@ import (
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"strconv"
"strings" "strings"
"time" "time"
) )
@@ -102,13 +103,79 @@ func CreateInstanceHandler(w http.ResponseWriter, r *http.Request) {
} }
defer r.Body.Close() defer r.Body.Close()
var req CreateInstanceRequest // 先解析为map,处理类型不匹配的情况
if err := json.Unmarshal(body, &req); err != nil { var reqMap map[string]interface{}
if err := json.Unmarshal(body, &reqMap); err != nil {
SendErrorResponse(w, http.StatusBadRequest, "invalid request format") SendErrorResponse(w, http.StatusBadRequest, "invalid request format")
return return
} }
userID, _, err := ValidateRequestWithBody(w, r, body) // 处理timeStamp字段
timeStamp := int64(0)
if ts, ok := reqMap["timeStamp"]; ok {
switch v := ts.(type) {
case float64:
timeStamp = int64(v)
case string:
if v != "" {
if parsed, err := strconv.ParseInt(v, 10, 64); err == nil {
timeStamp = parsed
}
}
}
}
// 处理bootAtStart字段
bootAtStart := false
if bas, ok := reqMap["bootAtStart"]; ok {
switch v := bas.(type) {
case bool:
bootAtStart = v
case string:
if v == "true" {
bootAtStart = true
}
}
}
// 处理instanceInfo字段
instanceInfoMap, ok := reqMap["instanceInfo"].(map[string]interface{})
if !ok {
SendErrorResponse(w, http.StatusBadRequest, "invalid instanceInfo format")
return
}
instanceInfo := InstanceInfo{
Name: getStringFromMap(instanceInfoMap, "name"),
ServerAddr: getStringFromMap(instanceInfoMap, "serverAddr"),
ServerPort: getStringFromMap(instanceInfoMap, "serverPort"),
AuthMethod: getStringFromMap(instanceInfoMap, "auth_method"),
RunUser: getStringFromMap(reqMap, "runUser"),
}
// 处理additionalProperties字段
if additional, ok := reqMap["additionalProperties"].(map[string]interface{}); ok {
instanceInfo.Additional = additional
}
// 构建请求结构体
req := CreateInstanceRequest{
Token: getStringFromMap(reqMap, "token"),
TimeStamp: timeStamp,
InstanceInfo: instanceInfo,
BootAtStart: bootAtStart,
RunUser: instanceInfo.RunUser,
Additional: instanceInfo.Additional,
}
// 重新序列化为JSON,用于ValidateRequestWithBody
reqBody, err := json.Marshal(req)
if err != nil {
SendErrorResponse(w, http.StatusBadRequest, "invalid request format")
return
}
userID, _, err := ValidateRequestWithBody(w, r, reqBody)
if err != nil { if err != nil {
SendErrorResponse(w, http.StatusUnauthorized, err.Error()) SendErrorResponse(w, http.StatusUnauthorized, err.Error())
return return
@@ -745,3 +812,10 @@ func GetUserInstances(userID int) ([]FrpcInstance, error) {
return instances, nil return instances, nil
} }
func getStringFromMap(m map[string]interface{}, key string) string {
if v, ok := m[key].(string); ok {
return v
}
return ""
}
+7 -1
View File
@@ -7,7 +7,7 @@ import (
"io" "io"
"net/http" "net/http"
"strconv" "strconv"
"super-frpc/modules" "super-frpc/postLog"
"time" "time"
) )
@@ -150,6 +150,12 @@ func LoginHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
existingTokenInfo, err := GetTokenInfo(user.UserID)
if err == nil && existingTokenInfo != nil {
SendErrorResponse(w, http.StatusConflict, "user is already logged in")
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")
+15 -15
View File
@@ -6,7 +6,7 @@ import (
"net/http" "net/http"
"os" "os"
"os/signal" "os/signal"
"super-frpc/modules" "super-frpc/postLog"
"syscall" "syscall"
"time" "time"
) )
@@ -23,31 +23,31 @@ 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.Fatal(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))
} }
config, err := LoadConfig(*configPath) config, err := LoadConfig(*configPath)
if err != nil { if err != nil {
postLog.Fatal(fmt.Sprintf("failed to load config: %v", err)) postLog.Fatal(fmt.Sprintf("Failed to load config: %v", err))
} }
// Initialize logger with debug mode // Initialize logger with debug mode
postLog.SetDebugMode(config.Debug) postLog.SetDebugMode(config.Debug)
if err := InitDatabase(*dbPath); err != nil { if err := InitDatabase(*dbPath); err != nil {
postLog.Fatal(fmt.Sprintf("failed to initialize database: %v", err)) postLog.Fatal(fmt.Sprintf("Failed to initialize database: %v", err))
} }
postLog.Info("database initialized successfully") postLog.Info("Database initialized successfully")
if err := InitFrpcDatabase(*dbPath); err != nil { if err := InitFrpcDatabase(*dbPath); err != nil {
postLog.Warning(fmt.Sprintf("failed to initialize frpc database: %v", err)) postLog.Warning(fmt.Sprintf("Failed to initialize frpc database: %v", err))
} }
_, err = GetConfig() _, err = GetConfig()
if err != nil { if err != nil {
postLog.Fatal(fmt.Sprintf("failed to get config: %v", err)) postLog.Fatal(fmt.Sprintf("Failed to get config: %v", err))
} }
setupRoutes() setupRoutes()
@@ -61,9 +61,9 @@ func main() {
} }
go func() { go func() {
postLog.Info(fmt.Sprintf("server starting on %s", addr)) postLog.Info(fmt.Sprintf("Server starting on %s", addr))
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed { if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
postLog.Fatal(fmt.Sprintf("failed to start server: %v", err)) postLog.Fatal(fmt.Sprintf("Failed to start server: %v", err))
} }
}() }()
@@ -71,19 +71,19 @@ func main() {
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit <-quit
postLog.Info("shutting down server...") postLog.Info("Shutting down server...")
if err := server.Close(); err != nil { if err := server.Close(); err != nil {
postLog.Error(fmt.Sprintf("server closed with error: %v", err)) postLog.Error(fmt.Sprintf("Server closed with error: %v", err))
} }
if err := CloseDatabase(); err != nil { if err := CloseDatabase(); err != nil {
postLog.Error(fmt.Sprintf("error closing database: %v", err)) postLog.Error(fmt.Sprintf("Error closing database: %v", err))
} }
if err := CloseFrpcDatabase(); err != nil { if err := CloseFrpcDatabase(); err != nil {
postLog.Error(fmt.Sprintf("error closing frpc database: %v", err)) postLog.Error(fmt.Sprintf("Error closing frpc database: %v", err))
} }
postLog.Info("server stopped") postLog.Info("Server stopped")
} }