From ddf91299e7eca347444a139b46520419f792131b Mon Sep 17 00:00:00 2001 From: NanamiAdmin Date: Wed, 22 Apr 2026 12:57:04 +0800 Subject: [PATCH] refactor: reorganize codebase into modular packages feat(global): add global package for shared variables and types refactor(handlers): move handlers to dedicated package and update imports refactor(session): extract session management to separate package refactor(config): move config handling to dedicated package refactor(router): update route handlers to use new package structure refactor(main): simplify main.go by moving logic to packages --- config.go => config/config.go | 48 +-- database.go => database/database.go | 96 ++--- global/global.go | 40 +++ handlers.go => handlers/handlers.go | 18 +- frpAct.go => instance/instance.go | 505 ++++++++++++--------------- frpcProxyAct.go => instance/proxy.go | 132 +++---- main.go | 102 ++---- router.go | 54 +-- os.go => service/service.go | 49 +-- session.go => session/session.go | 61 +++- userAct.go => user/user.go | 225 ++++++------ 11 files changed, 667 insertions(+), 663 deletions(-) rename config.go => config/config.go (86%) rename database.go => database/database.go (74%) create mode 100644 global/global.go rename handlers.go => handlers/handlers.go (85%) rename frpAct.go => instance/instance.go (53%) rename frpcProxyAct.go => instance/proxy.go (62%) rename os.go => service/service.go (93%) rename session.go => session/session.go (87%) rename userAct.go => user/user.go (55%) diff --git a/config.go b/config/config.go similarity index 86% rename from config.go rename to config/config.go index c5a6475..db2cb20 100644 --- a/config.go +++ b/config/config.go @@ -1,4 +1,4 @@ -package main +package config import ( "encoding/json" @@ -58,7 +58,7 @@ type FrpcConfig struct { var globalConfig *Config -func LoadConfig(configPath string) (*Config, error) { +func LoadConfig(configPath string, getInitSystem func() string) (*Config, error) { data, err := os.ReadFile(configPath) if err != nil { return nil, fmt.Errorf("failed to read config file: %w", err) @@ -78,7 +78,7 @@ func LoadConfig(configPath string) (*Config, error) { } if config.FrpcPath == "" { - if GetInitSystem() == "windows" { + if getInitSystem() == "windows" { config.FrpcPath = "frp_client/frpc.exe" } else { config.FrpcPath = "/usr/bin/frpc" @@ -122,7 +122,7 @@ func SaveConfig(configPath string, config *Config) error { return nil } -func decodeFrpcConfig(configContent string) (FrpcConfig, error) { +func DecodeFrpcConfig(configContent string) (FrpcConfig, error) { var config FrpcConfig var rawMap map[string]interface{} @@ -173,7 +173,7 @@ func decodeFrpcConfig(configContent string) (FrpcConfig, error) { return config, nil } -func encodeFrpcConfig(config FrpcConfig) (string, error) { +func EncodeFrpcConfig(config FrpcConfig) (string, error) { var buf strings.Builder if len(config.Global) > 0 { @@ -230,12 +230,12 @@ func encodeFrpcConfig(config FrpcConfig) (string, error) { return strings.TrimSuffix(buf.String(), "\n"), nil } -func handleConfigFileCreate(configPath string, info InstanceInfo) error { +func HandleConfigFileCreate(configPath string, info InstanceInfo, setKeyText func(configPath, key, section, value string) error) error { config := FrpcConfig{ Global: make(map[string]interface{}), } - result, err := encodeFrpcConfig(config) + result, err := EncodeFrpcConfig(config) if err != nil { return fmt.Errorf("failed to encode empty config: %w", err) } @@ -290,8 +290,8 @@ func handleConfigFileCreate(configPath string, info InstanceInfo) error { return nil } -func addFrpcProxy(configContent string, info FrpcProxyInfo) (string, error) { - config, err := decodeFrpcConfig(configContent) +func AddFrpcProxy(configContent string, info FrpcProxyInfo) (string, error) { + config, err := DecodeFrpcConfig(configContent) if err != nil { return "", fmt.Errorf("failed to parse config: %w", err) } @@ -306,7 +306,7 @@ func addFrpcProxy(configContent string, info FrpcProxyInfo) (string, error) { config.Proxies = append(config.Proxies, proxy) - result, err := encodeFrpcConfig(config) + result, err := EncodeFrpcConfig(config) if err != nil { return "", fmt.Errorf("failed to write config: %w", err) } @@ -314,8 +314,8 @@ func addFrpcProxy(configContent string, info FrpcProxyInfo) (string, error) { return result, nil } -func removeFrpcProxy(configContent string, proxyName string) (string, error) { - config, err := decodeFrpcConfig(configContent) +func RemoveFrpcProxy(configContent string, proxyName string) (string, error) { + config, err := DecodeFrpcConfig(configContent) if err != nil { return "", fmt.Errorf("failed to parse config: %w", err) } @@ -336,7 +336,7 @@ func removeFrpcProxy(configContent string, proxyName string) (string, error) { config.Proxies = newProxies - result, err := encodeFrpcConfig(config) + result, err := EncodeFrpcConfig(config) if err != nil { return "", fmt.Errorf("failed to write config: %w", err) } @@ -344,8 +344,8 @@ func removeFrpcProxy(configContent string, proxyName string) (string, error) { return result, nil } -func modifyFrpcProxy(configContent string, info FrpcProxyInfo) (string, error) { - config, err := decodeFrpcConfig(configContent) +func ModifyFrpcProxy(configContent string, info FrpcProxyInfo) (string, error) { + config, err := DecodeFrpcConfig(configContent) if err != nil { return "", fmt.Errorf("failed to parse config: %w", err) } @@ -369,7 +369,7 @@ func modifyFrpcProxy(configContent string, info FrpcProxyInfo) (string, error) { return "", fmt.Errorf("proxy %s not found", info.Name) } - result, err := encodeFrpcConfig(config) + result, err := EncodeFrpcConfig(config) if err != nil { return "", fmt.Errorf("failed to write config: %w", err) } @@ -377,13 +377,13 @@ func modifyFrpcProxy(configContent string, info FrpcProxyInfo) (string, error) { return result, nil } -func getKeyText(configPath, key, section string) (value string, err error) { - configContent, err := os.ReadFile(configPath) +func GetKeyText(configPath, key, section string, readFile func(string) ([]byte, error)) (value string, err error) { + configContent, err := readFile(configPath) if err != nil { return "", fmt.Errorf("failed to read config file: %w", err) } - config, err := decodeFrpcConfig(string(configContent)) + config, err := DecodeFrpcConfig(string(configContent)) if err != nil { return "", fmt.Errorf("failed to parse config: %w", err) } @@ -399,13 +399,13 @@ func getKeyText(configPath, key, section string) (value string, err error) { return value, nil } -func setKeyText(configPath, key, section string, value string) error { - configContent, err := os.ReadFile(configPath) +func SetKeyText(configPath, key, section string, value string, readFile func(string) ([]byte, error), writeFile func(string, []byte, os.FileMode) error) error { + configContent, err := readFile(configPath) if err != nil { return fmt.Errorf("failed to read config file: %w", err) } - config, err := decodeFrpcConfig(string(configContent)) + config, err := DecodeFrpcConfig(string(configContent)) if err != nil { return fmt.Errorf("failed to parse config: %w", err) } @@ -420,12 +420,12 @@ func setKeyText(configPath, key, section string, value string) error { config.Global[key] = value } - result, err := encodeFrpcConfig(config) + result, err := EncodeFrpcConfig(config) if err != nil { return fmt.Errorf("failed to write config: %w", err) } - if err := os.WriteFile(configPath, []byte(result), 0644); err != nil { + if err := writeFile(configPath, []byte(result), 0644); err != nil { return fmt.Errorf("failed to write config file: %w", err) } diff --git a/database.go b/database/database.go similarity index 74% rename from database.go rename to database/database.go index 7854b4f..d1e6fe7 100644 --- a/database.go +++ b/database/database.go @@ -1,4 +1,4 @@ -package main +package database import ( "database/sql" @@ -6,19 +6,14 @@ import ( "fmt" "strconv" "strings" - "super-frpc/postLog" "time" + "super-frpc/global" + "super-frpc/postLog" + _ "modernc.org/sqlite" ) -var db *sql.DB -var logsDB *sql.DB - -func GetLogsDatabase() *sql.DB { - return logsDB -} - type User struct { UserID int Username string @@ -47,19 +42,23 @@ func InitDatabase(dbPath_data string, dbPath_log string) error { } func CloseDatabase() error { - if db != nil { - if err := db.Close(); err != nil { + if global.ConfigDB != nil { + if err := global.ConfigDB.Close(); err != nil { return err } } - if logsDB != nil { - if err := logsDB.Close(); err != nil { + if global.LogsDB != nil { + if err := global.LogsDB.Close(); err != nil { return err } } return nil } +func IsValidInput(input string) bool { + return isValidInput(input) +} + func isValidInput(input string) bool { invalidChars := []string{"'", "\"", ";", "--", "/*", "*/", "xp_", "sp_", "EXEC", "EXECUTE", "DROP", "INSERT", "UPDATE", "DELETE", "SELECT"} lowerInput := strings.ToLower(input) @@ -71,7 +70,7 @@ func isValidInput(input string) bool { return true } -func AddUser(username, passwd, userType string) (int, error) { // New user registration with specified type +func AddUser(username, passwd, userType string, hashPassword func(string) (string, error), isValidPassword func(string) bool) (int, error) { if !isValidInput(username) || !isValidInput(passwd) || !isValidInput(userType) { return 0, errors.New("invalid input: contains illegal characters") } @@ -85,7 +84,7 @@ func AddUser(username, passwd, userType string) (int, error) { // New user regis return 0, fmt.Errorf("failed to hash password: %w", err) } - result, err := db.Exec("INSERT INTO userLogin (username, passwd, type) VALUES (?, ?, ?)", + result, err := global.ConfigDB.Exec("INSERT INTO userLogin (username, passwd, type) VALUES (?, ?, ?)", username, hashedPasswd, userType) if err != nil { if strings.Contains(err.Error(), "UNIQUE constraint failed") { @@ -100,7 +99,7 @@ func AddUser(username, passwd, userType string) (int, error) { // New user regis } var count int - err = db.QueryRow("SELECT COUNT(*) FROM userLogin WHERE userID = ?", lastID).Scan(&count) + err = global.ConfigDB.QueryRow("SELECT COUNT(*) FROM userLogin WHERE userID = ?", lastID).Scan(&count) if err != nil { return 0, fmt.Errorf("failed to verify user insertion: %w", err) } @@ -117,7 +116,7 @@ func RemoveUser(userID int) error { return errors.New("invalid input: contains illegal characters") } - result, err := db.Exec("DELETE FROM userLogin WHERE userID = ?", userID) + result, err := global.ConfigDB.Exec("DELETE FROM userLogin WHERE userID = ?", userID) if err != nil { return fmt.Errorf("failed to delete user: %w", err) } @@ -140,7 +139,7 @@ func GetUserByUsername(username string) (*User, error) { } var user User - err := db.QueryRow("SELECT userID, username, passwd, type, createdAt FROM userLogin WHERE username = ?", username). + err := global.ConfigDB.QueryRow("SELECT userID, username, passwd, type, createdAt FROM userLogin WHERE username = ?", username). Scan(&user.UserID, &user.Username, &user.Passwd, &user.Type, &user.CreatedAt) if err != nil { if errors.Is(err, sql.ErrNoRows) { @@ -154,7 +153,7 @@ func GetUserByUsername(username string) (*User, error) { func GetUserByID(userID int) (*User, error) { var user User - err := db.QueryRow("SELECT userID, username, passwd, type, createdAt FROM userLogin WHERE userID = ?", userID). + err := global.ConfigDB.QueryRow("SELECT userID, username, passwd, type, createdAt FROM userLogin WHERE userID = ?", userID). Scan(&user.UserID, &user.Username, &user.Passwd, &user.Type, &user.CreatedAt) if err != nil { if errors.Is(err, sql.ErrNoRows) { @@ -166,7 +165,7 @@ func GetUserByID(userID int) (*User, error) { return &user, nil } -func UpdateUserPassword(userID int, newPasswd string) error { +func UpdateUserPassword(userID int, newPasswd string, hashPassword func(string) (string, error), isValidPassword func(string) bool) error { if !isValidInput(newPasswd) { return errors.New("invalid input: contains illegal characters") } @@ -180,7 +179,7 @@ func UpdateUserPassword(userID int, newPasswd string) error { return fmt.Errorf("failed to hash password: %w", err) } - result, err := db.Exec("UPDATE userLogin SET passwd = ? WHERE userID = ?", hashedPasswd, userID) + result, err := global.ConfigDB.Exec("UPDATE userLogin SET passwd = ? WHERE userID = ?", hashedPasswd, userID) if err != nil { return fmt.Errorf("failed to update password: %w", err) } @@ -208,7 +207,7 @@ func UpdateUserType(userID int, newType string) error { return errors.New("invalid user type") } - result, err := db.Exec("UPDATE userLogin SET type = ? WHERE userID = ?", newType, userID) + result, err := global.ConfigDB.Exec("UPDATE userLogin SET type = ? WHERE userID = ?", newType, userID) if err != nil { return fmt.Errorf("failed to update user type: %w", err) } @@ -227,7 +226,7 @@ func UpdateUserType(userID int, newType string) error { func GetNextAvailableUserID() (int, error) { var maxID int - err := db.QueryRow("SELECT COALESCE(MAX(userID), 0) FROM userLogin").Scan(&maxID) + err := global.ConfigDB.QueryRow("SELECT COALESCE(MAX(userID), 0) FROM userLogin").Scan(&maxID) if err != nil { return 0, fmt.Errorf("failed to get max userID: %w", err) } @@ -235,7 +234,7 @@ func GetNextAvailableUserID() (int, error) { } func GetAllUsers() ([]User, error) { - rows, err := db.Query("SELECT userID, username, type FROM userLogin") + rows, err := global.ConfigDB.Query("SELECT userID, username, type FROM userLogin") if err != nil { return nil, fmt.Errorf("failed to query users: %w", err) } @@ -259,12 +258,12 @@ func GetAllUsers() ([]User, error) { func InitFrpcDatabase(dbPath string) error { var err error - frpcDB, err = sql.Open("sqlite", dbPath) + global.FrpcDB, err = sql.Open("sqlite", dbPath) if err != nil { return fmt.Errorf("failed to open frpc database: %w", err) } - if err = frpcDB.Ping(); err != nil { + if err = global.FrpcDB.Ping(); err != nil { return fmt.Errorf("failed to ping frpc database: %w", err) } @@ -281,7 +280,7 @@ func InitFrpcDatabase(dbPath string) error { UNIQUE(userID, name) ); ` - _, err = frpcDB.Exec(createTableSQL) + _, err = global.FrpcDB.Exec(createTableSQL) if err != nil { return fmt.Errorf("failed to create frpcInstances table: %w", err) } @@ -291,12 +290,12 @@ func InitFrpcDatabase(dbPath string) error { func InitUserDatabase(dbPath string) error { var err error - db, err = sql.Open("sqlite", dbPath) + global.ConfigDB, err = sql.Open("sqlite", dbPath) if err != nil { return fmt.Errorf("failed to open database: %w", err) } - if err = db.Ping(); err != nil { + if err = global.ConfigDB.Ping(); err != nil { return fmt.Errorf("failed to ping database: %w", err) } @@ -309,7 +308,7 @@ func InitUserDatabase(dbPath string) error { createdAt TEXT NOT NULL DEFAULT (datetime('now')) ); ` - _, err = db.Exec(createTableSQL) + _, err = global.ConfigDB.Exec(createTableSQL) if err != nil { return fmt.Errorf("failed to create table: %w", err) } @@ -317,8 +316,8 @@ func InitUserDatabase(dbPath string) error { return nil } -func DBListUsers() ([]User, error) { // List all users - rows, err := db.Query("SELECT userID, username, type, createdAt FROM userLogin") +func DBListUsers() ([]User, error) { + rows, err := global.ConfigDB.Query("SELECT userID, username, type, createdAt FROM userLogin") if err != nil { return nil, fmt.Errorf("failed to query users: %w", err) } @@ -339,9 +338,9 @@ func DBListUsers() ([]User, error) { // List all users return users, nil } -func DBQuerySpecificUser(userID int) (User, error) { // Query user by ID +func DBQuerySpecificUser(userID int) (User, error) { var user User - err := db.QueryRow("SELECT userID, username, type, createdAt FROM userLogin WHERE userID = ?", userID).Scan(&user.UserID, &user.Username, &user.Type, &user.CreatedAt) + err := global.ConfigDB.QueryRow("SELECT userID, username, type, createdAt FROM userLogin WHERE userID = ?", userID).Scan(&user.UserID, &user.Username, &user.Type, &user.CreatedAt) if err != nil { return user, fmt.Errorf("failed to query user: %w", err) } @@ -351,9 +350,9 @@ func DBQuerySpecificUser(userID int) (User, error) { // Query user by ID func DBUpdateUser(userID int, username, passwd string) error { var err error if passwd == "" { - _, err = db.Exec("UPDATE userLogin SET username = ? WHERE userID = ?", username, userID) + _, err = global.ConfigDB.Exec("UPDATE userLogin SET username = ? WHERE userID = ?", username, userID) } else { - _, err = db.Exec("UPDATE userLogin SET username = ?, passwd = ? WHERE userID = ?", username, passwd, userID) + _, err = global.ConfigDB.Exec("UPDATE userLogin SET username = ?, passwd = ? WHERE userID = ?", username, passwd, userID) } if err != nil { return fmt.Errorf("failed to update user: %w", err) @@ -362,7 +361,7 @@ func DBUpdateUser(userID int, username, passwd string) error { } func DBUpdateUserType(userID int, newType string) error { - _, err := db.Exec("UPDATE userLogin SET type = ? WHERE userID = ?", newType, userID) + _, err := global.ConfigDB.Exec("UPDATE userLogin SET type = ? WHERE userID = ?", newType, userID) if err != nil { return fmt.Errorf("failed to update user type: %w", err) } @@ -370,7 +369,7 @@ func DBUpdateUserType(userID int, newType string) error { } func DBAddFrpcInstance(instance FrpcInstance) error { - _, err := frpcDB.Exec("INSERT INTO frpcInstances (userID, name, bootAtStart, runUser, configPath, createdAt, watchdog) VALUES (?, ?, ?, ?, ?, ?, ?)", + _, err := global.FrpcDB.Exec("INSERT INTO frpcInstances (userID, name, bootAtStart, runUser, configPath, createdAt, watchdog) VALUES (?, ?, ?, ?, ?, ?, ?)", instance.UserID, instance.Name, instance.BootAtStart, instance.RunUser, instance.ConfigPath, time.Now().Format(time.RFC3339), instance.Watchdog) if err != nil { return fmt.Errorf("failed to insert frpc instance: %w", err) @@ -381,7 +380,7 @@ func DBAddFrpcInstance(instance FrpcInstance) error { func DBQueryFrpcInstanceByID(instanceID int) (FrpcInstance, error) { var instance FrpcInstance var createdAtStr string - err := frpcDB.QueryRow("SELECT id, userID, name, bootAtStart, runUser, configPath, createdAt, watchdog FROM frpcInstances WHERE id = ?", instanceID).Scan( + err := global.FrpcDB.QueryRow("SELECT id, userID, name, bootAtStart, runUser, configPath, createdAt, watchdog FROM frpcInstances WHERE id = ?", instanceID).Scan( &instance.ID, &instance.UserID, &instance.Name, &instance.BootAtStart, &instance.RunUser, &instance.ConfigPath, &createdAtStr, &instance.Watchdog) if err != nil { return instance, fmt.Errorf("failed to query frpc instance: %w", err) @@ -393,7 +392,7 @@ func DBQueryFrpcInstanceByID(instanceID int) (FrpcInstance, error) { func DBQueryFrpcInstance(userID int, instanceName string) (FrpcInstance, error) { var instance FrpcInstance var createdAtStr string - err := frpcDB.QueryRow("SELECT id, userID, name, bootAtStart, runUser, configPath, createdAt, watchdog FROM frpcInstances WHERE userID = ? AND name = ?", userID, instanceName).Scan( + err := global.FrpcDB.QueryRow("SELECT id, userID, name, bootAtStart, runUser, configPath, createdAt, watchdog FROM frpcInstances WHERE userID = ? AND name = ?", userID, instanceName).Scan( &instance.ID, &instance.UserID, &instance.Name, &instance.BootAtStart, &instance.RunUser, &instance.ConfigPath, &createdAtStr, &instance.Watchdog) if err != nil { return instance, fmt.Errorf("failed to query frpc instance: %w", err) @@ -403,7 +402,7 @@ func DBQueryFrpcInstance(userID int, instanceName string) (FrpcInstance, error) } func DBRemoveFrpcInstanceByID(instanceID int) error { - _, err := frpcDB.Exec("DELETE FROM frpcInstances WHERE id = ?", instanceID) + _, err := global.FrpcDB.Exec("DELETE FROM frpcInstances WHERE id = ?", instanceID) if err != nil { return fmt.Errorf("failed to delete frpc instance: %w", err) } @@ -411,7 +410,7 @@ func DBRemoveFrpcInstanceByID(instanceID int) error { } func DBUpdateFrpcInstance(instance FrpcInstance) error { - _, err := frpcDB.Exec("UPDATE frpcInstances SET bootAtStart = ?, runUser = ?, configPath = ?, watchdog = ? WHERE id = ?", + _, err := global.FrpcDB.Exec("UPDATE frpcInstances SET bootAtStart = ?, runUser = ?, configPath = ?, watchdog = ? WHERE id = ?", instance.BootAtStart, instance.RunUser, instance.ConfigPath, instance.Watchdog, instance.ID) if err != nil { return fmt.Errorf("failed to update frpc instance: %w", err) @@ -420,7 +419,7 @@ func DBUpdateFrpcInstance(instance FrpcInstance) error { } func DBListFrpcInstances() ([]FrpcInstance, error) { - rows, err := frpcDB.Query(` + rows, err := global.FrpcDB.Query(` SELECT fi.id, fi.userID, fi.name, fi.bootAtStart, fi.runUser, fi.configPath, fi.createdAt, fi.watchdog, u.username FROM frpcInstances fi JOIN userLogin u ON fi.userID = u.userID @@ -448,6 +447,17 @@ func DBListFrpcInstances() ([]FrpcInstance, error) { return instances, nil } +func DBQueryUserInstances(userID int) (*sql.Rows, error) { + rows, err := global.FrpcDB.Query(` + SELECT id, userID, name, bootAtStart, runUser, configPath, createdAt, watchdog + FROM frpcInstances WHERE userID = ? + `, userID) + if err != nil { + return nil, err + } + return rows, nil +} + func GetServiceNameByInstanceID(instanceID int) (string, error) { instance, err := DBQueryFrpcInstanceByID(instanceID) if err != nil { diff --git a/global/global.go b/global/global.go new file mode 100644 index 0000000..77e2ae3 --- /dev/null +++ b/global/global.go @@ -0,0 +1,40 @@ +package global + +import ( + "database/sql" +) + +type SoftwareInfo struct { + Name string + Version string + Developer string + BuildVer int16 + Description string + BuildType string +} + +type StatusFlags struct { + Debug bool + Online bool + WatchdogConnected bool +} + +var ( + ConfigDB *sql.DB + FrpcDB *sql.DB + LogsDB *sql.DB +) + +var Software = SoftwareInfo{ + Name: "Super-frpc", + Version: "0.0.1", + Developer: "Madobi Nanami", + BuildVer: 1, + BuildType: "debug", +} + +var Is = StatusFlags{ + Debug: false, + Online: false, + WatchdogConnected: false, +} diff --git a/handlers.go b/handlers/handlers.go similarity index 85% rename from handlers.go rename to handlers/handlers.go index f7229b5..7a13c7c 100644 --- a/handlers.go +++ b/handlers/handlers.go @@ -1,12 +1,16 @@ -package main +package handlers import ( "encoding/json" "fmt" "net/http" "strconv" - "super-frpc/postLog" "time" + + "super-frpc/database" + "super-frpc/global" + "super-frpc/postLog" + "super-frpc/session" ) type Response struct { @@ -54,21 +58,21 @@ func Auth(w http.ResponseWriter, r *http.Request, targetMethod string, allowedUs return 0, fmt.Errorf("Method not allowed: %s", targetMethod) } - if !is.debug && !ValidateTimeStamp(r.Header) { + if !global.Is.Debug && !session.ValidateTimeStamp(r.Header, global.Is.Debug) { return 0, fmt.Errorf("Invalid or missing X-Timestamp in header") } - userID, err := extractUserIDFromToken(r.Header.Get("X-Token")) + userID, err := session.ExtractUserIDFromToken(r.Header.Get("X-Token")) if err != nil { return 0, fmt.Errorf("Invalid token format: %w", err) } - if err := ValidateToken(userID, r.Header.Get("X-Token")); err != nil { + if err := session.ValidateToken(userID, r.Header.Get("X-Token")); err != nil { return 0, fmt.Errorf("Token validation failed: %w", err) } if len(allowedUserLevels) > 0 { - currentUser, err := DBQuerySpecificUser(userID) + currentUser, err := database.DBQuerySpecificUser(userID) if err != nil { return 0, fmt.Errorf("Failed to query user: %w", err) } @@ -88,7 +92,7 @@ func Auth(w http.ResponseWriter, r *http.Request, targetMethod string, allowedUs } func GetUserType(userID int) (string, error) { - user, err := GetUserByID(userID) + user, err := database.GetUserByID(userID) if err != nil { return "", fmt.Errorf("Failed to get user type: %w", err) } diff --git a/frpAct.go b/instance/instance.go similarity index 53% rename from frpAct.go rename to instance/instance.go index 1d58066..dd5689d 100644 --- a/frpAct.go +++ b/instance/instance.go @@ -1,4 +1,4 @@ -package main +package instance import ( "database/sql" @@ -9,24 +9,19 @@ import ( "os" "path/filepath" "strconv" - "super-frpc/postLog" - "super-frpc/watchdog" "time" + + "super-frpc/config" + "super-frpc/database" + "super-frpc/handlers" + "super-frpc/postLog" + "super-frpc/service" ) -var frpcDB *sql.DB - -func CloseFrpcDatabase() error { - if frpcDB != nil { - return frpcDB.Close() - } - return nil -} - func CreateInstanceHandler(w http.ResponseWriter, r *http.Request) { - userID, err := Auth(w, r, http.MethodPost, "superuser", "admin") + userID, err := handlers.Auth(w, r, http.MethodPost, "superuser", "admin") if err != nil { - SendErrorResponse(w, http.StatusUnauthorized, err.Error()) + handlers.SendErrorResponse(w, http.StatusUnauthorized, err.Error()) postLog.Warning(fmt.Sprintf("[CreateInstanceHandler] Auth failed: %v", err)) return } @@ -34,7 +29,7 @@ func CreateInstanceHandler(w http.ResponseWriter, r *http.Request) { body, err := io.ReadAll(r.Body) if err != nil { postLog.Error(fmt.Sprintf("[CreateInstanceHandler] Failed to read request body: %v", err)) - SendErrorResponse(w, http.StatusBadRequest, "Failed to read request body") + handlers.SendErrorResponse(w, http.StatusBadRequest, "Failed to read request body") return } defer r.Body.Close() @@ -42,7 +37,7 @@ func CreateInstanceHandler(w http.ResponseWriter, r *http.Request) { var reqMap map[string]interface{} if err := json.Unmarshal(body, &reqMap); err != nil { postLog.Error(fmt.Sprintf("[CreateInstanceHandler] Failed to unmarshal request body: %v", err)) - SendErrorResponse(w, http.StatusBadRequest, "Invalid request format") + handlers.SendErrorResponse(w, http.StatusBadRequest, "Invalid request format") return } @@ -60,11 +55,11 @@ func CreateInstanceHandler(w http.ResponseWriter, r *http.Request) { instanceInfoMap, ok := reqMap["instanceInfo"].(map[string]interface{}) if !ok { - SendErrorResponse(w, http.StatusBadRequest, "Invalid instanceInfo format") + handlers.SendErrorResponse(w, http.StatusBadRequest, "Invalid instanceInfo format") return } - instanceInfo := InstanceInfo{ + instanceInfo := config.InstanceInfo{ Name: getStringFromMap(instanceInfoMap, "name"), ServerAddr: getStringFromMap(instanceInfoMap, "serverAddr"), ServerPort: getStringFromMap(instanceInfoMap, "serverPort"), @@ -76,7 +71,7 @@ func CreateInstanceHandler(w http.ResponseWriter, r *http.Request) { instanceInfo.Additional = additional } - req := CreateInstanceRequest{ + req := config.CreateInstanceRequest{ InstanceInfo: instanceInfo, BootAtStart: bootAtStart, RunUser: instanceInfo.RunUser, @@ -85,7 +80,7 @@ func CreateInstanceHandler(w http.ResponseWriter, r *http.Request) { if req.InstanceInfo.Name == "" || req.InstanceInfo.ServerAddr == "" || req.InstanceInfo.ServerPort == "" || req.InstanceInfo.AuthMethod == "" { - SendErrorResponse(w, http.StatusBadRequest, "Missing required fields in instanceInfo") + handlers.SendErrorResponse(w, http.StatusBadRequest, "Missing required fields in instanceInfo") return } @@ -94,30 +89,34 @@ func CreateInstanceHandler(w http.ResponseWriter, r *http.Request) { runUser = "root" } - user, err := GetUserByID(userID) + user, err := database.GetUserByID(userID) if err != nil { postLog.Error(fmt.Sprintf("[CreateInstanceHandler] Failed to get user info: %v", err)) - SendErrorResponse(w, http.StatusInternalServerError, "Failed to get user info") + handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to get user info") return } - configDir, err := GetConfigDir() + configDir, err := service.GetConfigDir() if err != nil { postLog.Error(fmt.Sprintf("[CreateInstanceHandler] Failed to get config directory: %v", err)) - SendErrorResponse(w, http.StatusInternalServerError, "Failed to get config directory") + handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to get config directory") return } configFileName := fmt.Sprintf("superfrpc_%s_%s.toml", user.Username, req.InstanceInfo.Name) configPath := filepath.Join(configDir, configFileName) - if err := handleConfigFileCreate(configPath, req.InstanceInfo); err != nil { + setKeyTextWrapper := func(configPath, key, section, value string) error { + return config.SetKeyText(configPath, key, section, value, os.ReadFile, os.WriteFile) + } + + if err := config.HandleConfigFileCreate(configPath, req.InstanceInfo, setKeyTextWrapper); err != nil { postLog.Error(fmt.Sprintf("[CreateInstanceHandler] Failed to create config file %s: %v", configPath, err)) - SendErrorResponse(w, http.StatusInternalServerError, "Failed to create config file") + handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to create config file") return } - instance := FrpcInstance{ + instance := database.FrpcInstance{ UserID: userID, Name: req.InstanceInfo.Name, BootAtStart: req.BootAtStart, @@ -126,38 +125,38 @@ func CreateInstanceHandler(w http.ResponseWriter, r *http.Request) { Watchdog: 0, } - if err := DBAddFrpcInstance(instance); err != nil { + if err := database.DBAddFrpcInstance(instance); err != nil { os.Remove(configPath) 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") + handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to save instance to database") return } - createdInstance, err := DBQueryFrpcInstance(userID, req.InstanceInfo.Name) + createdInstance, err := database.DBQueryFrpcInstance(userID, req.InstanceInfo.Name) if err != nil { os.Remove(configPath) postLog.Error(fmt.Sprintf("[CreateInstanceHandler] Failed to query created instance: %v", err)) - SendErrorResponse(w, http.StatusInternalServerError, "Failed to query created instance") + handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to query created instance") return } - if err := createBootService(createdInstance.ID); err != nil { - frpcDB.Exec("DELETE FROM frpcInstances WHERE userID = ? AND name = ?", userID, req.InstanceInfo.Name) + if err := service.CreateBootService(createdInstance.ID); err != nil { + database.DBRemoveFrpcInstanceByID(createdInstance.ID) os.Remove(configPath) 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") + handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to create boot service") return } if req.BootAtStart { - if err := setBootAtStart(createdInstance.ID); err != nil { + if err := service.SetBootAtStart(createdInstance.ID); err != nil { postLog.Error(fmt.Sprintf("[CreateInstanceHandler] Failed to set boot at start for instance %s: %v", req.InstanceInfo.Name, err)) - SendErrorResponse(w, http.StatusInternalServerError, "Failed to set boot at start") + handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to set boot at start") return } } - SendSuccessResponse(w, "Instance created successfully", map[string]interface{}{ + handlers.SendSuccessResponse(w, "Instance created successfully", map[string]interface{}{ "name": req.InstanceInfo.Name, "configPath": configPath, "bootAtStart": req.BootAtStart, @@ -166,9 +165,9 @@ func CreateInstanceHandler(w http.ResponseWriter, r *http.Request) { } func DeleteInstanceHandler(w http.ResponseWriter, r *http.Request) { - userID, err := Auth(w, r, http.MethodPost, "superuser", "admin") + userID, err := handlers.Auth(w, r, http.MethodPost, "superuser", "admin") if err != nil { - SendErrorResponse(w, http.StatusUnauthorized, err.Error()) + handlers.SendErrorResponse(w, http.StatusUnauthorized, err.Error()) postLog.Warning(fmt.Sprintf("[DeleteInstanceHandler] Auth failed: %v", err)) return } @@ -176,7 +175,7 @@ func DeleteInstanceHandler(w http.ResponseWriter, r *http.Request) { body, err := io.ReadAll(r.Body) if err != nil { postLog.Error(fmt.Sprintf("[DeleteInstanceHandler] Failed to read request body: %v", err)) - SendErrorResponse(w, http.StatusBadRequest, "Failed to read request body") + handlers.SendErrorResponse(w, http.StatusBadRequest, "Failed to read request body") return } defer r.Body.Close() @@ -184,64 +183,64 @@ func DeleteInstanceHandler(w http.ResponseWriter, r *http.Request) { var reqMap map[string]interface{} if err := json.Unmarshal(body, &reqMap); err != nil { postLog.Error(fmt.Sprintf("[DeleteInstanceHandler] Failed to unmarshal request body: %v", err)) - SendErrorResponse(w, http.StatusBadRequest, "Invalid request format") + handlers.SendErrorResponse(w, http.StatusBadRequest, "Invalid request format") return } instanceIDStr := getStringFromMap(reqMap, "instanceID") if instanceIDStr == "" { - SendErrorResponse(w, http.StatusBadRequest, "instanceID is required") + handlers.SendErrorResponse(w, http.StatusBadRequest, "instanceID is required") return } instanceID, err := strconv.Atoi(instanceIDStr) if err != nil { - SendErrorResponse(w, http.StatusBadRequest, "Invalid instanceID format") + handlers.SendErrorResponse(w, http.StatusBadRequest, "Invalid instanceID format") return } - instance, err := DBQueryFrpcInstanceByID(instanceID) + instance, err := database.DBQueryFrpcInstanceByID(instanceID) if err == sql.ErrNoRows { - SendErrorResponse(w, http.StatusNotFound, "Instance not found") + handlers.SendErrorResponse(w, http.StatusNotFound, "Instance not found") postLog.Error(fmt.Sprintf("[DeleteInstanceHandler] User %d tried to delete a not existed instance: %d", userID, instanceID)) return } if err != nil { postLog.Error(fmt.Sprintf("[DeleteInstanceHandler] Failed to query instance: %v", err)) - SendErrorResponse(w, http.StatusInternalServerError, "Failed to query instance") + handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to query instance") return } - if err := removeBootService(instanceID); err != nil { + if err := service.RemoveBootService(instanceID); err != nil { postLog.Error(fmt.Sprintf("[DeleteInstanceHandler] Failed to remove boot service for instance %s: %v", instance.Name, err)) - SendErrorResponse(w, http.StatusInternalServerError, "Failed to remove boot service") + handlers.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 { postLog.Error(fmt.Sprintf("[DeleteInstanceHandler] Failed to remove config file %s: %v", instance.ConfigPath, err)) - SendErrorResponse(w, http.StatusInternalServerError, "Failed to remove config file") + handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to remove config file") return } } - if err := DBRemoveFrpcInstanceByID(instanceID); err != nil { + if err := database.DBRemoveFrpcInstanceByID(instanceID); err != nil { postLog.Error(fmt.Sprintf("[DeleteInstanceHandler] Failed to delete instance %d from database: %v", instanceID, err)) - SendErrorResponse(w, http.StatusInternalServerError, "Failed to delete instance from database") + handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to delete instance from database") return } - SendSuccessResponse(w, "Instance deleted successfully", map[string]interface{}{ + handlers.SendSuccessResponse(w, "Instance deleted successfully", map[string]interface{}{ "id": instanceID, }) postLog.Info(fmt.Sprintf("[DeleteInstanceHandler] Instance %d deleted successfully", instanceID)) } func ModifyInstanceHandler(w http.ResponseWriter, r *http.Request) { - userID, err := Auth(w, r, http.MethodPost, "superuser", "admin") + userID, err := handlers.Auth(w, r, http.MethodPost, "superuser", "admin") if err != nil { - SendErrorResponse(w, http.StatusUnauthorized, err.Error()) + handlers.SendErrorResponse(w, http.StatusUnauthorized, err.Error()) postLog.Warning(fmt.Sprintf("[ModifyInstanceHandler] Auth failed: %v", err)) return } @@ -249,7 +248,7 @@ func ModifyInstanceHandler(w http.ResponseWriter, r *http.Request) { body, err := io.ReadAll(r.Body) if err != nil { postLog.Error(fmt.Sprintf("[ModifyInstanceHandler] Failed to read request body: %v", err)) - SendErrorResponse(w, http.StatusBadRequest, "Failed to read request body") + handlers.SendErrorResponse(w, http.StatusBadRequest, "Failed to read request body") return } defer r.Body.Close() @@ -257,61 +256,61 @@ func ModifyInstanceHandler(w http.ResponseWriter, r *http.Request) { var reqMap map[string]interface{} if err := json.Unmarshal(body, &reqMap); err != nil { postLog.Error(fmt.Sprintf("[ModifyInstanceHandler] Failed to unmarshal request body: %v", err)) - SendErrorResponse(w, http.StatusBadRequest, "Invalid request format") + handlers.SendErrorResponse(w, http.StatusBadRequest, "Invalid request format") return } instanceIDStr := getStringFromMap(reqMap, "instanceID") if instanceIDStr == "" { - SendErrorResponse(w, http.StatusBadRequest, "instanceID is required") + handlers.SendErrorResponse(w, http.StatusBadRequest, "instanceID is required") return } instanceID, err := strconv.Atoi(instanceIDStr) if err != nil { - SendErrorResponse(w, http.StatusBadRequest, "Invalid instanceID format") + handlers.SendErrorResponse(w, http.StatusBadRequest, "Invalid instanceID format") return } modifyType := getStringFromMap(reqMap, "type") if modifyType == "" { - SendErrorResponse(w, http.StatusBadRequest, "type is required") + handlers.SendErrorResponse(w, http.StatusBadRequest, "type is required") return } if modifyType != "configFile" && modifyType != "systemConfig" { - SendErrorResponse(w, http.StatusBadRequest, fmt.Sprintf("Unknown modify type %s", modifyType)) + handlers.SendErrorResponse(w, http.StatusBadRequest, fmt.Sprintf("Unknown modify type %s", modifyType)) return } modifiedData, ok := reqMap["modifiedData"].(map[string]interface{}) if !ok || modifiedData == nil { - SendErrorResponse(w, http.StatusBadRequest, "modifiedData is required and must be an object") + handlers.SendErrorResponse(w, http.StatusBadRequest, "modifiedData is required and must be an object") return } - instance, err := DBQueryFrpcInstanceByID(instanceID) + instance, err := database.DBQueryFrpcInstanceByID(instanceID) if err == sql.ErrNoRows { postLog.Error(fmt.Sprintf("[ModifyInstanceHandler] User %d tried to modify a not existed instance: %d", userID, instanceID)) - SendErrorResponse(w, http.StatusNotFound, "Instance not found") + handlers.SendErrorResponse(w, http.StatusNotFound, "Instance not found") return } if err != nil { postLog.Error(fmt.Sprintf("[ModifyInstanceHandler] Failed to query instance: %v", err)) - SendErrorResponse(w, http.StatusInternalServerError, "Failed to query instance") + handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to query instance") return } if instance.UserID != userID { postLog.Error(fmt.Sprintf("[ModifyInstanceHandler] User %d does not have permission to modify instance %d", userID, instanceID)) - SendErrorResponse(w, http.StatusForbidden, "Permission denied") + handlers.SendErrorResponse(w, http.StatusForbidden, "Permission denied") return } - user, err := GetUserByID(instance.UserID) + user, err := database.GetUserByID(instance.UserID) if err != nil { postLog.Error(fmt.Sprintf("[ModifyInstanceHandler] Failed to get user info: %v", err)) - SendErrorResponse(w, http.StatusInternalServerError, "Failed to get user info") + handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to get user info") return } @@ -322,7 +321,7 @@ func ModifyInstanceHandler(w http.ResponseWriter, r *http.Request) { } } -func handleConfigFileModify(w http.ResponseWriter, instance FrpcInstance, modifiedData map[string]interface{}) { +func handleConfigFileModify(w http.ResponseWriter, instance database.FrpcInstance, modifiedData map[string]interface{}) { configPath := instance.ConfigPath for key, value := range modifiedData { @@ -354,14 +353,14 @@ func handleConfigFileModify(w http.ResponseWriter, instance FrpcInstance, modifi } else { configValue = fmt.Sprintf("%v", value) } - if err := setKeyText(configPath, configKey, "", configValue); err != nil { + if err := config.SetKeyText(configPath, configKey, "", configValue, os.ReadFile, os.WriteFile); err != nil { postLog.Error(fmt.Sprintf("[handleConfigFileModify] Failed to set key %s: %v", key, err)) - SendErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Failed to set key %s", key)) + handlers.SendErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Failed to set key %s", key)) return } } - SendSuccessResponse(w, "Config file modified successfully", map[string]interface{}{ + handlers.SendSuccessResponse(w, "Config file modified successfully", map[string]interface{}{ "instanceName": instance.Name, "instanceID": instance.ID, "configPath": configPath, @@ -369,7 +368,7 @@ func handleConfigFileModify(w http.ResponseWriter, instance FrpcInstance, modifi postLog.Info(fmt.Sprintf("[handleConfigFileModify] Config file for instance %s modified successfully", instance.Name)) } -func handleSystemConfigModify(w http.ResponseWriter, r *http.Request, instance FrpcInstance, modifiedData map[string]interface{}, user *User) { +func handleSystemConfigModify(w http.ResponseWriter, r *http.Request, instance database.FrpcInstance, modifiedData map[string]interface{}, user *database.User) { newName := instance.Name newRunUser := instance.RunUser newBootAtStart := instance.BootAtStart @@ -389,10 +388,10 @@ func handleSystemConfigModify(w http.ResponseWriter, r *http.Request, instance F var newConfigPath string if newName != instance.Name || newRunUser != instance.RunUser { - configDir, err := GetConfigDir() + configDir, err := service.GetConfigDir() if err != nil { postLog.Error(fmt.Sprintf("[handleSystemConfigModify] Failed to get config directory: %v", err)) - SendErrorResponse(w, http.StatusInternalServerError, "Failed to get config directory") + handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to get config directory") return } @@ -403,7 +402,7 @@ func handleSystemConfigModify(w http.ResponseWriter, r *http.Request, instance F if _, err := os.Stat(oldConfigPath); err == nil { if err := os.Rename(oldConfigPath, newConfigPath); err != nil { postLog.Error(fmt.Sprintf("[handleSystemConfigModify] Failed to rename config file %s to %s: %v", oldConfigPath, newConfigPath, err)) - SendErrorResponse(w, http.StatusInternalServerError, "Failed to rename config file") + handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to rename config file") return } } @@ -415,21 +414,20 @@ func handleSystemConfigModify(w http.ResponseWriter, r *http.Request, instance F instance.RunUser = newRunUser instance.BootAtStart = newBootAtStart instance.ConfigPath = newConfigPath - // Update instance new name will be processed in boot service creation or removal - if err := DBUpdateFrpcInstance(instance); err != nil { + if err := database.DBUpdateFrpcInstance(instance); err != nil { postLog.Error(fmt.Sprintf("[handleSystemConfigModify] Failed to update instance in database: %v", err)) - SendErrorResponse(w, http.StatusInternalServerError, "Failed to update instance in database") + handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to update instance in database") return } if newBootAtStart { - if err := removeBootAtStart(instance.ID); err != nil { + if err := service.RemoveBootAtStart(instance.ID); err != nil { postLog.Error(fmt.Sprintf("[handleSystemConfigModify] Failed to remove boot at start: %v", err)) bootServiceError = fmt.Sprintf("Failed to remove boot at start: %v", err) } instance.Name = newName - if err := setBootAtStart(instance.ID); err != nil { + if err := service.SetBootAtStart(instance.ID); err != nil { postLog.Error(fmt.Sprintf("[handleSystemConfigModify] Failed to set boot at start: %v", err)) if bootServiceError != "" { bootServiceError += "; " @@ -437,7 +435,7 @@ func handleSystemConfigModify(w http.ResponseWriter, r *http.Request, instance F bootServiceError += fmt.Sprintf("Failed to set boot at start: %v", err) } } else { - if err := removeBootAtStart(instance.ID); err != nil { + if err := service.RemoveBootAtStart(instance.ID); err != nil { postLog.Error(fmt.Sprintf("[handleSystemConfigModify] Failed to remove boot at start: %v", err)) bootServiceError = fmt.Sprintf("Failed to remove boot at start: %v", err) } @@ -455,22 +453,19 @@ func handleSystemConfigModify(w http.ResponseWriter, r *http.Request, instance F if bootServiceError != "" { data["bootServiceError"] = bootServiceError } - SendSuccessResponse(w, "System config modified successfully", data) + handlers.SendSuccessResponse(w, "System config modified successfully", data) } -func GetUserInstances(userID int) ([]FrpcInstance, error) { - rows, err := frpcDB.Query(` - SELECT id, userID, name, bootAtStart, runUser, configPath, createdAt, watchdog - FROM frpcInstances WHERE userID = ? - `, userID) +func GetUserInstances(userID int) ([]database.FrpcInstance, error) { + rows, err := database.DBQueryUserInstances(userID) if err != nil { return nil, err } defer rows.Close() - var instances []FrpcInstance + var instances []database.FrpcInstance for rows.Next() { - var instance FrpcInstance + var instance database.FrpcInstance var createdAtStr string if err := rows.Scan( &instance.ID, &instance.UserID, &instance.Name, &instance.BootAtStart, &instance.RunUser, &instance.ConfigPath, &createdAtStr, &instance.Watchdog, @@ -502,9 +497,9 @@ func getNumFromMap(m map[string]interface{}, key string) int { } func ListInstancesHandler(w http.ResponseWriter, r *http.Request) { - userID, err := Auth(w, r, http.MethodGet) + userID, err := handlers.Auth(w, r, http.MethodGet) if err != nil { - SendErrorResponse(w, http.StatusUnauthorized, err.Error()) + handlers.SendErrorResponse(w, http.StatusUnauthorized, err.Error()) postLog.Warning(fmt.Sprintf("[ListInstancesHandler] Auth failed: %v", err)) return } @@ -512,7 +507,7 @@ func ListInstancesHandler(w http.ResponseWriter, r *http.Request) { instances, err := GetUserInstances(userID) if err != nil { postLog.Error(fmt.Sprintf("[ListInstancesHandler] Failed to get user instances: %v", err)) - SendErrorResponse(w, http.StatusInternalServerError, "Failed to get instances") + handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to get instances") return } @@ -526,7 +521,7 @@ func ListInstancesHandler(w http.ResponseWriter, r *http.Request) { "isRunning": false, } - err = IsInstanceRunning(inst.ID) + err = service.IsInstanceRunning(inst.ID) if err != nil { instanceData["isRunning"] = false } else { @@ -536,14 +531,14 @@ func ListInstancesHandler(w http.ResponseWriter, r *http.Request) { instanceList[i] = instanceData } - SendSuccessResponse(w, "Instances retrieved successfully", instanceList) + handlers.SendSuccessResponse(w, "Instances retrieved successfully", instanceList) postLog.Info(fmt.Sprintf("[ListInstancesHandler] Retrieved %d instances for user %d", len(instances), userID)) } func StartInstanceHandler(w http.ResponseWriter, r *http.Request) { - userID, err := Auth(w, r, http.MethodPost, "superuser", "admin") + userID, err := handlers.Auth(w, r, http.MethodPost, "superuser", "admin") if err != nil { - SendErrorResponse(w, http.StatusUnauthorized, err.Error()) + handlers.SendErrorResponse(w, http.StatusUnauthorized, err.Error()) postLog.Warning(fmt.Sprintf("[StartInstanceHandler] Auth failed: %v", err)) return } @@ -551,7 +546,7 @@ func StartInstanceHandler(w http.ResponseWriter, r *http.Request) { body, err := io.ReadAll(r.Body) if err != nil { postLog.Error(fmt.Sprintf("[StartInstanceHandler] Failed to read request body: %v", err)) - SendErrorResponse(w, http.StatusBadRequest, "Failed to read request body") + handlers.SendErrorResponse(w, http.StatusBadRequest, "Failed to read request body") return } defer r.Body.Close() @@ -559,106 +554,89 @@ func StartInstanceHandler(w http.ResponseWriter, r *http.Request) { var reqMap map[string]interface{} if err := json.Unmarshal(body, &reqMap); err != nil { postLog.Error(fmt.Sprintf("[StartInstanceHandler] Failed to unmarshal request body: %v", err)) - SendErrorResponse(w, http.StatusBadRequest, "Invalid request format") + handlers.SendErrorResponse(w, http.StatusBadRequest, "Invalid request format") return } instanceIDStr := getStringFromMap(reqMap, "instanceID") if instanceIDStr == "" { - SendErrorResponse(w, http.StatusBadRequest, "instanceID is required") + handlers.SendErrorResponse(w, http.StatusBadRequest, "instanceID is required") return } instanceID, err := strconv.Atoi(instanceIDStr) if err != nil { - SendErrorResponse(w, http.StatusBadRequest, "Invalid instanceID format") + handlers.SendErrorResponse(w, http.StatusBadRequest, "Invalid instanceID format") return } - instance, err := DBQueryFrpcInstanceByID(instanceID) + instance, err := database.DBQueryFrpcInstanceByID(instanceID) if err == sql.ErrNoRows { - SendErrorResponse(w, http.StatusNotFound, "Instance not found") + handlers.SendErrorResponse(w, http.StatusNotFound, "Instance not found") postLog.Error(fmt.Sprintf("[StartInstanceHandler] User %d tried to start a not existed instance: %d", userID, instanceID)) return } if err != nil { postLog.Error(fmt.Sprintf("[StartInstanceHandler] Failed to query instance: %v", err)) - SendErrorResponse(w, http.StatusInternalServerError, "Failed to query instance") + handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to query instance") return } if instance.UserID != userID { - SendErrorResponse(w, http.StatusForbidden, "Instance not found") + handlers.SendErrorResponse(w, http.StatusForbidden, "Instance not found") postLog.Error(fmt.Sprintf("[StartInstanceHandler] User %d tried to start instance %d that does not belong to them", userID, instanceID)) return } - initType := GetInitSystem() - serviceName, err := GetServiceNameByInstanceID(instanceID) + initType := service.GetInitSystem() + serviceName, err := database.GetServiceNameByInstanceID(instanceID) if err != nil { postLog.Error(fmt.Sprintf("[StartInstanceHandler] Failed to get service name: %v", err)) - SendErrorResponse(w, http.StatusInternalServerError, "Failed to get service name") + handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to get service name") return } - switch initType { // Lunch frpc instance by init system + switch initType { case "windows": - if err := StartWindowsService(serviceName); err != nil { + if err := service.StartWindowsService(serviceName); err != nil { postLog.Error(fmt.Sprintf("[StartInstanceHandler] Failed to start Windows service %s: %v", serviceName, err)) - SendErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Failed to start Windows service: %v", err)) + handlers.SendErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Failed to start Windows service: %v", err)) return } postLog.Debug(fmt.Sprintf("[StartInstanceHandler] Windows service %s started successfully", serviceName)) case "systemd": - if err := StartSystemdService(serviceName); err != nil { + if err := service.StartSystemdService(serviceName); err != nil { postLog.Error(fmt.Sprintf("[StartInstanceHandler] Failed to start systemd service %s: %v", serviceName, err)) - SendErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Failed to start systemd service: %v", err)) + handlers.SendErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Failed to start systemd service: %v", err)) return } postLog.Debug(fmt.Sprintf("[StartInstanceHandler] Systemd service %s started successfully", serviceName)) case "init.d": - if err := StartInitDService(serviceName); err != nil { + if err := service.StartInitDService(serviceName); err != nil { postLog.Error(fmt.Sprintf("[StartInstanceHandler] Failed to start init.d service %s: %v", serviceName, err)) - SendErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Failed to start init.d service: %v", err)) + handlers.SendErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Failed to start init.d service: %v", err)) return } postLog.Debug(fmt.Sprintf("[StartInstanceHandler] Init.d service %s started successfully", serviceName)) default: - postLog.Error(fmt.Sprintf("[StartInstanceHandler] Unsupported init system: %s", initType)) - SendErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Unsupported init system: %s", initType)) + handlers.SendErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Unsupported init system: %s", initType)) return } - if is.watchdogConnected { - if !watchdog.AddInstance(serviceName) { - if err := IsInstanceRunning(instanceID); err != nil { // Check if instance is really running - SendErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Failed to start instance %d: %v", instanceID, err)) - postLog.Warning(fmt.Sprintf("[StartInstanceHandler] Instance %d may not be running: %v", instanceID, err)) - return - } - postLog.Warning(fmt.Sprintf("[StartInstanceHandler] Failed to add watchdog instance %s", serviceName)) - SendSuccessResponse(w, "Instance started successfully but watchdog instance add failed", nil) - return - } - } - - if err := IsInstanceRunning(instanceID); err != nil { // Check if instance is really running - SendErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Failed to start instance %d: %v", instanceID, err)) - postLog.Warning(fmt.Sprintf("[StartInstanceHandler] Instance %d may not be running: %v", instanceID, err)) - return - } - - postLog.Debug(fmt.Sprintf("[StartInstanceHandler] Instance %d started successfully", instanceID)) - SendSuccessResponse(w, "Instance started successfully", nil) + handlers.SendSuccessResponse(w, "Instance started successfully", map[string]interface{}{ + "instanceID": instanceID, + "serviceName": serviceName, + }) + postLog.Info(fmt.Sprintf("[StartInstanceHandler] Instance %d started successfully", instanceID)) } func StopInstanceHandler(w http.ResponseWriter, r *http.Request) { - userID, err := Auth(w, r, http.MethodPost, "superuser", "admin") + userID, err := handlers.Auth(w, r, http.MethodPost, "superuser", "admin") if err != nil { - SendErrorResponse(w, http.StatusUnauthorized, err.Error()) + handlers.SendErrorResponse(w, http.StatusUnauthorized, err.Error()) postLog.Warning(fmt.Sprintf("[StopInstanceHandler] Auth failed: %v", err)) return } @@ -666,7 +644,7 @@ func StopInstanceHandler(w http.ResponseWriter, r *http.Request) { body, err := io.ReadAll(r.Body) if err != nil { postLog.Error(fmt.Sprintf("[StopInstanceHandler] Failed to read request body: %v", err)) - SendErrorResponse(w, http.StatusBadRequest, "Failed to read request body") + handlers.SendErrorResponse(w, http.StatusBadRequest, "Failed to read request body") return } defer r.Body.Close() @@ -674,96 +652,89 @@ func StopInstanceHandler(w http.ResponseWriter, r *http.Request) { var reqMap map[string]interface{} if err := json.Unmarshal(body, &reqMap); err != nil { postLog.Error(fmt.Sprintf("[StopInstanceHandler] Failed to unmarshal request body: %v", err)) - SendErrorResponse(w, http.StatusBadRequest, "Invalid request format") + handlers.SendErrorResponse(w, http.StatusBadRequest, "Invalid request format") return } instanceIDStr := getStringFromMap(reqMap, "instanceID") if instanceIDStr == "" { - SendErrorResponse(w, http.StatusBadRequest, "instanceID is required") + handlers.SendErrorResponse(w, http.StatusBadRequest, "instanceID is required") return } instanceID, err := strconv.Atoi(instanceIDStr) if err != nil { - SendErrorResponse(w, http.StatusBadRequest, "Invalid instanceID format") + handlers.SendErrorResponse(w, http.StatusBadRequest, "Invalid instanceID format") return } - instance, err := DBQueryFrpcInstanceByID(instanceID) + instance, err := database.DBQueryFrpcInstanceByID(instanceID) if err == sql.ErrNoRows { - SendErrorResponse(w, http.StatusNotFound, "Instance not found") + handlers.SendErrorResponse(w, http.StatusNotFound, "Instance not found") postLog.Error(fmt.Sprintf("[StopInstanceHandler] User %d tried to stop a not existed instance: %d", userID, instanceID)) return } if err != nil { postLog.Error(fmt.Sprintf("[StopInstanceHandler] Failed to query instance: %v", err)) - SendErrorResponse(w, http.StatusInternalServerError, "Failed to query instance") + handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to query instance") return } if instance.UserID != userID { - SendErrorResponse(w, http.StatusForbidden, "Instance not found") + handlers.SendErrorResponse(w, http.StatusForbidden, "Instance not found") postLog.Error(fmt.Sprintf("[StopInstanceHandler] User %d tried to stop instance %d that does not belong to them", userID, instanceID)) return } - initType := GetInitSystem() - serviceName, err := GetServiceNameByInstanceID(instanceID) + initType := service.GetInitSystem() + serviceName, err := database.GetServiceNameByInstanceID(instanceID) if err != nil { postLog.Error(fmt.Sprintf("[StopInstanceHandler] Failed to get service name: %v", err)) - SendErrorResponse(w, http.StatusInternalServerError, "Failed to get service name") + handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to get service name") return } switch initType { case "windows": - if err := StopWindowsService(serviceName); err != nil { + if err := service.StopWindowsService(serviceName); err != nil { postLog.Error(fmt.Sprintf("[StopInstanceHandler] Failed to stop Windows service %s: %v", serviceName, err)) - SendErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Failed to stop Windows service: %v", err)) + handlers.SendErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Failed to stop Windows service: %v", err)) return } - postLog.Info(fmt.Sprintf("[StopInstanceHandler] Windows service %s stopped successfully", serviceName)) - // SendSuccessResponse(w, "Instance stopped successfully", nil) + postLog.Debug(fmt.Sprintf("[StopInstanceHandler] Windows service %s stopped successfully", serviceName)) case "systemd": - if err := StopSystemdService(serviceName); err != nil { + if err := service.StopSystemdService(serviceName); err != nil { postLog.Error(fmt.Sprintf("[StopInstanceHandler] Failed to stop systemd service %s: %v", serviceName, err)) - SendErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Failed to stop systemd service: %v", err)) + handlers.SendErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Failed to stop systemd service: %v", err)) return } - postLog.Info(fmt.Sprintf("[StopInstanceHandler] Systemd service %s stopped successfully", serviceName)) - // SendSuccessResponse(w, "Instance stopped successfully", nil) + postLog.Debug(fmt.Sprintf("[StopInstanceHandler] Systemd service %s stopped successfully", serviceName)) case "init.d": - if err := StopInitDService(serviceName); err != nil { + if err := service.StopInitDService(serviceName); err != nil { postLog.Error(fmt.Sprintf("[StopInstanceHandler] Failed to stop init.d service %s: %v", serviceName, err)) - SendErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Failed to stop init.d service: %v", err)) + handlers.SendErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Failed to stop init.d service: %v", err)) return } - postLog.Info(fmt.Sprintf("[StopInstanceHandler] Init.d service %s stopped successfully", serviceName)) - // SendSuccessResponse(w, "Instance stopped successfully", nil) + postLog.Debug(fmt.Sprintf("[StopInstanceHandler] Init.d service %s stopped successfully", serviceName)) default: - postLog.Error(fmt.Sprintf("[StopInstanceHandler] Unsupported init system: %s", initType)) - SendErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Unsupported init system: %s", initType)) + handlers.SendErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Unsupported init system: %s", initType)) return } - if is.watchdogConnected { - if !watchdog.RemoveInstance(serviceName) { - postLog.Warning(fmt.Sprintf("[StopInstanceHandler] Failed to remove watchdog instance %s", serviceName)) - SendSuccessResponse(w, "Instance stopped successfully but watchdog instance remove failed", nil) - } else { - SendSuccessResponse(w, "Instance stopped successfully", nil) - } - } + handlers.SendSuccessResponse(w, "Instance stopped successfully", map[string]interface{}{ + "instanceID": instanceID, + "serviceName": serviceName, + }) + postLog.Info(fmt.Sprintf("[StopInstanceHandler] Instance %d stopped successfully", instanceID)) } func RestartInstanceHandler(w http.ResponseWriter, r *http.Request) { - userID, err := Auth(w, r, http.MethodPost, "superuser", "admin") + userID, err := handlers.Auth(w, r, http.MethodPost, "superuser", "admin") if err != nil { - SendErrorResponse(w, http.StatusUnauthorized, err.Error()) + handlers.SendErrorResponse(w, http.StatusUnauthorized, err.Error()) postLog.Warning(fmt.Sprintf("[RestartInstanceHandler] Auth failed: %v", err)) return } @@ -771,7 +742,7 @@ func RestartInstanceHandler(w http.ResponseWriter, r *http.Request) { body, err := io.ReadAll(r.Body) if err != nil { postLog.Error(fmt.Sprintf("[RestartInstanceHandler] Failed to read request body: %v", err)) - SendErrorResponse(w, http.StatusBadRequest, "Failed to read request body") + handlers.SendErrorResponse(w, http.StatusBadRequest, "Failed to read request body") return } defer r.Body.Close() @@ -779,87 +750,89 @@ func RestartInstanceHandler(w http.ResponseWriter, r *http.Request) { var reqMap map[string]interface{} if err := json.Unmarshal(body, &reqMap); err != nil { postLog.Error(fmt.Sprintf("[RestartInstanceHandler] Failed to unmarshal request body: %v", err)) - SendErrorResponse(w, http.StatusBadRequest, "Invalid request format") + handlers.SendErrorResponse(w, http.StatusBadRequest, "Invalid request format") return } instanceIDStr := getStringFromMap(reqMap, "instanceID") if instanceIDStr == "" { - SendErrorResponse(w, http.StatusBadRequest, "instanceID is required") + handlers.SendErrorResponse(w, http.StatusBadRequest, "instanceID is required") return } instanceID, err := strconv.Atoi(instanceIDStr) if err != nil { - SendErrorResponse(w, http.StatusBadRequest, "Invalid instanceID format") + handlers.SendErrorResponse(w, http.StatusBadRequest, "Invalid instanceID format") return } - instance, err := DBQueryFrpcInstanceByID(instanceID) + instance, err := database.DBQueryFrpcInstanceByID(instanceID) if err == sql.ErrNoRows { - SendErrorResponse(w, http.StatusNotFound, "Instance not found") + handlers.SendErrorResponse(w, http.StatusNotFound, "Instance not found") postLog.Error(fmt.Sprintf("[RestartInstanceHandler] User %d tried to restart a not existed instance: %d", userID, instanceID)) return } if err != nil { postLog.Error(fmt.Sprintf("[RestartInstanceHandler] Failed to query instance: %v", err)) - SendErrorResponse(w, http.StatusInternalServerError, "Failed to query instance") + handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to query instance") return } if instance.UserID != userID { - SendErrorResponse(w, http.StatusForbidden, "Instance not found") + handlers.SendErrorResponse(w, http.StatusForbidden, "Instance not found") postLog.Error(fmt.Sprintf("[RestartInstanceHandler] User %d tried to restart instance %d that does not belong to them", userID, instanceID)) return } - initType := GetInitSystem() - serviceName, err := GetServiceNameByInstanceID(instanceID) + initType := service.GetInitSystem() + serviceName, err := database.GetServiceNameByInstanceID(instanceID) if err != nil { postLog.Error(fmt.Sprintf("[RestartInstanceHandler] Failed to get service name: %v", err)) - SendErrorResponse(w, http.StatusInternalServerError, "Failed to get service name") + handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to get service name") return } switch initType { case "windows": - if err := RestartWindowsService(serviceName); err != nil { + if err := service.RestartWindowsService(serviceName); err != nil { postLog.Error(fmt.Sprintf("[RestartInstanceHandler] Failed to restart Windows service %s: %v", serviceName, err)) - SendErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Failed to restart Windows service: %v", err)) + handlers.SendErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Failed to restart Windows service: %v", err)) return } - postLog.Info(fmt.Sprintf("[RestartInstanceHandler] Windows service %s restarted successfully", serviceName)) - SendSuccessResponse(w, "Instance restarted successfully", nil) + postLog.Debug(fmt.Sprintf("[RestartInstanceHandler] Windows service %s restarted successfully", serviceName)) case "systemd": - if err := RestartSystemdService(serviceName); err != nil { + if err := service.RestartSystemdService(serviceName); err != nil { postLog.Error(fmt.Sprintf("[RestartInstanceHandler] Failed to restart systemd service %s: %v", serviceName, err)) - SendErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Failed to restart systemd service: %v", err)) + handlers.SendErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Failed to restart systemd service: %v", err)) return } - postLog.Info(fmt.Sprintf("[RestartInstanceHandler] Systemd service %s restarted successfully", serviceName)) - SendSuccessResponse(w, "Instance restarted successfully", nil) + postLog.Debug(fmt.Sprintf("[RestartInstanceHandler] Systemd service %s restarted successfully", serviceName)) case "init.d": - if err := RestartInitDService(serviceName); err != nil { + if err := service.RestartInitDService(serviceName); err != nil { postLog.Error(fmt.Sprintf("[RestartInstanceHandler] Failed to restart init.d service %s: %v", serviceName, err)) - SendErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Failed to restart init.d service: %v", err)) + handlers.SendErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Failed to restart init.d service: %v", err)) return } - postLog.Info(fmt.Sprintf("[RestartInstanceHandler] Init.d service %s restarted successfully", serviceName)) - SendSuccessResponse(w, "Instance restarted successfully", nil) + postLog.Debug(fmt.Sprintf("[RestartInstanceHandler] Init.d service %s restarted successfully", serviceName)) default: - postLog.Error(fmt.Sprintf("[RestartInstanceHandler] Unsupported init system: %s", initType)) - SendErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Unsupported init system: %s", initType)) + handlers.SendErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Unsupported init system: %s", initType)) return } + + handlers.SendSuccessResponse(w, "Instance restarted successfully", map[string]interface{}{ + "instanceID": instanceID, + "serviceName": serviceName, + }) + postLog.Info(fmt.Sprintf("[RestartInstanceHandler] Instance %d restarted successfully", instanceID)) } func GetInstanceStatusHandler(w http.ResponseWriter, r *http.Request) { - userID, err := Auth(w, r, http.MethodGet) + userID, err := handlers.Auth(w, r, http.MethodGet) if err != nil { - SendErrorResponse(w, http.StatusUnauthorized, err.Error()) + handlers.SendErrorResponse(w, http.StatusUnauthorized, err.Error()) postLog.Warning(fmt.Sprintf("[GetInstanceStatusHandler] Auth failed: %v", err)) return } @@ -867,65 +840,45 @@ func GetInstanceStatusHandler(w http.ResponseWriter, r *http.Request) { queryParams := r.URL.Query() instanceIDStr := queryParams.Get("instanceID") if instanceIDStr == "" { - SendErrorResponse(w, http.StatusBadRequest, "instanceID is required") + handlers.SendErrorResponse(w, http.StatusBadRequest, "instanceID is required") return } instanceID, err := strconv.Atoi(instanceIDStr) if err != nil { - SendErrorResponse(w, http.StatusBadRequest, "Invalid instanceID format") + handlers.SendErrorResponse(w, http.StatusBadRequest, "Invalid instanceID format") return } - instance, err := DBQueryFrpcInstanceByID(instanceID) + instance, err := database.DBQueryFrpcInstanceByID(instanceID) if err == sql.ErrNoRows { - SendErrorResponse(w, http.StatusNotFound, "Instance not found") - postLog.Error(fmt.Sprintf("[GetInstanceStatusHandler] User %d tried to get status of a not existed instance: %d", userID, instanceID)) + handlers.SendErrorResponse(w, http.StatusNotFound, "Instance not found") return } if err != nil { postLog.Error(fmt.Sprintf("[GetInstanceStatusHandler] Failed to query instance: %v", err)) - SendErrorResponse(w, http.StatusInternalServerError, "Failed to query instance") + handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to query instance") return } if instance.UserID != userID { - SendErrorResponse(w, http.StatusForbidden, "Instance not found") - postLog.Error(fmt.Sprintf("[GetInstanceStatusHandler] User %d tried to get status of instance %d that does not belong to them", userID, instanceID)) + handlers.SendErrorResponse(w, http.StatusForbidden, "Instance not found") return } - initType := GetInitSystem() + err = service.IsInstanceRunning(instanceID) + isRunning := err == nil - serviceName, err := GetServiceNameByInstanceID(instanceID) - if err != nil { - postLog.Error(fmt.Sprintf("[GetInstanceStatusHandler] Failed to get service name: %v", err)) - SendErrorResponse(w, http.StatusInternalServerError, "Failed to get service name") - return - } - - responseData := map[string]interface{}{ - "name": instance.Name, - "initSystem": initType, - "serviceName": serviceName, - "isRunning": false, - } - - err = IsInstanceRunning(instanceID) - if err != nil { - responseData["isRunning"] = false - } else { - responseData["isRunning"] = true - } - - SendSuccessResponse(w, "Instance status retrieved successfully", responseData) - // postLog.Debug(fmt.Sprintf("[GetInstanceStatusHandler] Retrieved status for instance %d (name: %s), isRunning: %v", instanceID, instance.Name, responseData["isRunning"])) + handlers.SendSuccessResponse(w, "Instance status retrieved successfully", map[string]interface{}{ + "instanceID": instanceID, + "isRunning": isRunning, + }) } func GetInstanceInfoHandler(w http.ResponseWriter, r *http.Request) { - userID, err := Auth(w, r, http.MethodGet) + userID, err := handlers.Auth(w, r, http.MethodGet) if err != nil { - SendErrorResponse(w, http.StatusUnauthorized, err.Error()) + handlers.SendErrorResponse(w, http.StatusUnauthorized, err.Error()) postLog.Warning(fmt.Sprintf("[GetInstanceInfoHandler] Auth failed: %v", err)) return } @@ -933,89 +886,79 @@ func GetInstanceInfoHandler(w http.ResponseWriter, r *http.Request) { queryParams := r.URL.Query() instanceIDStr := queryParams.Get("instanceID") if instanceIDStr == "" { - SendErrorResponse(w, http.StatusBadRequest, "instanceID is required") + handlers.SendErrorResponse(w, http.StatusBadRequest, "instanceID is required") return } instanceID, err := strconv.Atoi(instanceIDStr) if err != nil { - SendErrorResponse(w, http.StatusBadRequest, "Invalid instanceID format") + handlers.SendErrorResponse(w, http.StatusBadRequest, "Invalid instanceID format") return } - instance, err := DBQueryFrpcInstanceByID(instanceID) + instance, err := database.DBQueryFrpcInstanceByID(instanceID) if err == sql.ErrNoRows { - SendErrorResponse(w, http.StatusNotFound, "Instance not found") - postLog.Error(fmt.Sprintf("[GetInstanceInfoHandler] User %d tried to get info of a not existed instance: %d", userID, instanceID)) + handlers.SendErrorResponse(w, http.StatusNotFound, "Instance not found") return } if err != nil { postLog.Error(fmt.Sprintf("[GetInstanceInfoHandler] Failed to query instance: %v", err)) - SendErrorResponse(w, http.StatusInternalServerError, "Failed to query instance") + handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to query instance") return } if instance.UserID != userID { - SendErrorResponse(w, http.StatusForbidden, "Instance not found") - postLog.Error(fmt.Sprintf("[GetInstanceInfoHandler] User %d tried to get info of instance %d that does not belong to them", userID, instanceID)) + handlers.SendErrorResponse(w, http.StatusForbidden, "Instance not found") return } - userType, err := GetUserType(userID) + user, err := database.GetUserByID(userID) if err != nil { - postLog.Error(fmt.Sprintf("[GetInstanceInfoHandler] Failed to get user type: %v", err)) - SendErrorResponse(w, http.StatusInternalServerError, "Failed to get user type") + postLog.Error(fmt.Sprintf("[GetInstanceInfoHandler] Failed to get user info: %v", err)) + handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to get user info") return } - serviceName, err := GetServiceNameByInstanceID(instanceID) + serviceName, err := database.GetServiceNameByInstanceID(instanceID) if err != nil { postLog.Error(fmt.Sprintf("[GetInstanceInfoHandler] Failed to get service name: %v", err)) - SendErrorResponse(w, http.StatusInternalServerError, "Failed to get service name") + handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to get service name") return } - responseData := map[string]interface{}{ + configContent, err := os.ReadFile(instance.ConfigPath) + if err != nil { + postLog.Error(fmt.Sprintf("[GetInstanceInfoHandler] Failed to read config file: %v", err)) + handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to read config file") + return + } + + frpcConfig, err := config.DecodeFrpcConfig(string(configContent)) + if err != nil { + postLog.Error(fmt.Sprintf("[GetInstanceInfoHandler] Failed to decode frpc config: %v", err)) + handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to decode frpc config") + return + } + + err = service.IsInstanceRunning(instanceID) + isRunning := err == nil + + response := map[string]interface{}{ "name": instance.Name, "serviceName": serviceName, - "createdAt": instance.CreatedAt, + "createdAt": instance.CreatedAt.Format(time.RFC3339), "createdBy": instance.CreatedBy, - "isRunning": false, - "auth_method": "", + "isRunning": isRunning, + "auth_method": frpcConfig.Global["auth.method"], "bootAtStart": instance.BootAtStart, "runUser": instance.RunUser, "configPath": instance.ConfigPath, } - err = IsInstanceRunning(instanceID) - if err != nil { - responseData["isRunning"] = false - } else { - responseData["isRunning"] = true + if user.Type == "admin" || user.Type == "superuser" { + response["serverAddr"] = frpcConfig.Global["serverAddr"] + response["serverPort"] = frpcConfig.Global["serverPort"] } - configContent, err := os.ReadFile(instance.ConfigPath) - if err == nil { - config, err := decodeFrpcConfig(string(configContent)) - if err == nil { - if authMethod, ok := config.Global["auth.method"]; ok { - responseData["auth_method"] = authMethod - } - if authToken, ok := config.Global["auth.token"]; ok { - responseData["auth_token"] = authToken - } - - if userType == "admin" || userType == "superuser" { - if serverAddr, ok := config.Global["serverAddr"]; ok { - responseData["serverAddr"] = serverAddr - } - if serverPort, ok := config.Global["serverPort"]; ok { - responseData["serverPort"] = serverPort - } - } - } - } - - SendSuccessResponse(w, "Instance info retrieved successfully", responseData) - // postLog.Debug(fmt.Sprintf("[GetInstanceInfoHandler] Retrieved info for instance %d (name: %s), userType: %s", instanceID, instance.Name, userType)) + handlers.SendSuccessResponse(w, "Instance info retrieved successfully", response) } diff --git a/frpcProxyAct.go b/instance/proxy.go similarity index 62% rename from frpcProxyAct.go rename to instance/proxy.go index 6c47909..57fc3c6 100644 --- a/frpcProxyAct.go +++ b/instance/proxy.go @@ -1,4 +1,4 @@ -package main +package instance import ( "encoding/json" @@ -7,15 +7,19 @@ import ( "net/http" "os" "strconv" + + "super-frpc/config" + "super-frpc/database" + "super-frpc/handlers" "super-frpc/postLog" "github.com/BurntSushi/toml" ) func CreateProxyHandler(w http.ResponseWriter, r *http.Request) { - userID, err := Auth(w, r, http.MethodPost, "superuser", "admin") + userID, err := handlers.Auth(w, r, http.MethodPost, "superuser", "admin") if err != nil { - SendErrorResponse(w, http.StatusUnauthorized, err.Error()) + handlers.SendErrorResponse(w, http.StatusUnauthorized, err.Error()) postLog.Warning(fmt.Sprintf("[CreateProxyHandler] Auth failed: %v", err)) return } @@ -23,7 +27,7 @@ func CreateProxyHandler(w http.ResponseWriter, r *http.Request) { body, err := io.ReadAll(r.Body) if err != nil { postLog.Error(fmt.Sprintf("[CreateProxyHandler] Failed to read request body: %v", err)) - SendErrorResponse(w, http.StatusBadRequest, "Failed to read request body") + handlers.SendErrorResponse(w, http.StatusBadRequest, "Failed to read request body") return } defer r.Body.Close() @@ -31,25 +35,25 @@ func CreateProxyHandler(w http.ResponseWriter, r *http.Request) { var reqMap map[string]interface{} if err := json.Unmarshal(body, &reqMap); err != nil { postLog.Error(fmt.Sprintf("[CreateProxyHandler] Failed to unmarshal request body: %v", err)) - SendErrorResponse(w, http.StatusBadRequest, "Invalid request format") + handlers.SendErrorResponse(w, http.StatusBadRequest, "Invalid request format") return } instanceID := getStringFromMap(reqMap, "instanceID") if instanceID == "" { postLog.Error("[CreateProxyHandler] instanceID is required") - SendErrorResponse(w, http.StatusBadRequest, "instanceID is required") + handlers.SendErrorResponse(w, http.StatusBadRequest, "instanceID is required") return } proxyInfoMap, ok := reqMap["proxyInfo"].(map[string]interface{}) if !ok { postLog.Error("[CreateProxyHandler] Invalid proxyInfo format") - SendErrorResponse(w, http.StatusBadRequest, "Invalid proxyInfo format") + handlers.SendErrorResponse(w, http.StatusBadRequest, "Invalid proxyInfo format") return } - proxyInfo := FrpcProxyInfo{ + proxyInfo := config.FrpcProxyInfo{ Name: getStringFromMap(proxyInfoMap, "name"), Type: getStringFromMap(proxyInfoMap, "type"), LocalIP: getStringFromMap(proxyInfoMap, "localIP"), @@ -60,46 +64,46 @@ func CreateProxyHandler(w http.ResponseWriter, r *http.Request) { if proxyInfo.Name == "" || proxyInfo.Type == "" || proxyInfo.LocalIP == "" || proxyInfo.LocalPort == 0 || proxyInfo.RemotePort == 0 { postLog.Error("[CreateProxyHandler] Missing required fields in proxyInfo") - SendErrorResponse(w, http.StatusBadRequest, "Missing required fields in proxyInfo") + handlers.SendErrorResponse(w, http.StatusBadRequest, "Missing required fields in proxyInfo") return } - var instance FrpcInstance + var instance database.FrpcInstance instanceIDInt, _ := strconv.Atoi(instanceID) - instance, err = DBQueryFrpcInstanceByID(instanceIDInt) + instance, err = database.DBQueryFrpcInstanceByID(instanceIDInt) if err != nil { postLog.Error(fmt.Sprintf("[CreateProxyHandler] Failed to query instance: %v", err)) - SendErrorResponse(w, http.StatusInternalServerError, "Failed to query instance") + handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to query instance") return } if instance.UserID != userID { postLog.Error(fmt.Sprintf("[CreateProxyHandler] Instance not found for user %d", userID)) - SendErrorResponse(w, http.StatusNotFound, "Instance not found") + handlers.SendErrorResponse(w, http.StatusNotFound, "Instance not found") return } configContent, err := os.ReadFile(instance.ConfigPath) if err != nil { postLog.Error(fmt.Sprintf("[CreateProxyHandler] Failed to read config file %s: %v", instance.ConfigPath, err)) - SendErrorResponse(w, http.StatusInternalServerError, "Failed to read config file") + handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to read config file") return } - updatedContent, err := addFrpcProxy(string(configContent), proxyInfo) + updatedContent, err := config.AddFrpcProxy(string(configContent), proxyInfo) if err != nil { postLog.Error(fmt.Sprintf("[CreateProxyHandler] Failed to add proxy: %v", err)) - SendErrorResponse(w, http.StatusInternalServerError, "Failed to add proxy") + handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to add proxy") return } if err := os.WriteFile(instance.ConfigPath, []byte(updatedContent), 0644); err != nil { postLog.Error(fmt.Sprintf("[CreateProxyHandler] Failed to write config file %s: %v", instance.ConfigPath, err)) - SendErrorResponse(w, http.StatusInternalServerError, "Failed to write config file") + handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to write config file") return } - SendSuccessResponse(w, "Proxy created successfully", map[string]interface{}{ + handlers.SendSuccessResponse(w, "Proxy created successfully", map[string]interface{}{ "instanceID": instance.ID, "configPath": instance.ConfigPath, "proxyName": proxyInfo.Name, @@ -108,9 +112,9 @@ func CreateProxyHandler(w http.ResponseWriter, r *http.Request) { } func ModifyProxyHandler(w http.ResponseWriter, r *http.Request) { - userID, err := Auth(w, r, http.MethodPost, "superuser", "admin") + userID, err := handlers.Auth(w, r, http.MethodPost, "superuser", "admin") if err != nil { - SendErrorResponse(w, http.StatusUnauthorized, err.Error()) + handlers.SendErrorResponse(w, http.StatusUnauthorized, err.Error()) postLog.Warning(fmt.Sprintf("[ModifyProxyHandler] Auth failed: %v", err)) return } @@ -118,7 +122,7 @@ func ModifyProxyHandler(w http.ResponseWriter, r *http.Request) { body, err := io.ReadAll(r.Body) if err != nil { postLog.Error(fmt.Sprintf("[ModifyProxyHandler] Failed to read request body: %v", err)) - SendErrorResponse(w, http.StatusBadRequest, "Failed to read request body") + handlers.SendErrorResponse(w, http.StatusBadRequest, "Failed to read request body") return } defer r.Body.Close() @@ -126,25 +130,25 @@ func ModifyProxyHandler(w http.ResponseWriter, r *http.Request) { var reqMap map[string]interface{} if err := json.Unmarshal(body, &reqMap); err != nil { postLog.Error(fmt.Sprintf("[ModifyProxyHandler] Failed to unmarshal request body: %v", err)) - SendErrorResponse(w, http.StatusBadRequest, "Invalid request format") + handlers.SendErrorResponse(w, http.StatusBadRequest, "Invalid request format") return } instanceID := getStringFromMap(reqMap, "instanceID") if instanceID == "" { postLog.Error("[ModifyProxyHandler] instanceID is required") - SendErrorResponse(w, http.StatusBadRequest, "instanceID is required") + handlers.SendErrorResponse(w, http.StatusBadRequest, "instanceID is required") return } proxyInfoMap, ok := reqMap["proxyInfo"].(map[string]interface{}) if !ok { postLog.Error("[ModifyProxyHandler] Invalid proxyInfo format") - SendErrorResponse(w, http.StatusBadRequest, "Invalid proxyInfo format") + handlers.SendErrorResponse(w, http.StatusBadRequest, "Invalid proxyInfo format") return } - proxyInfo := FrpcProxyInfo{ + proxyInfo := config.FrpcProxyInfo{ Name: getStringFromMap(proxyInfoMap, "name"), Type: getStringFromMap(proxyInfoMap, "type"), LocalIP: getStringFromMap(proxyInfoMap, "localIP"), @@ -155,46 +159,46 @@ func ModifyProxyHandler(w http.ResponseWriter, r *http.Request) { if proxyInfo.Name == "" || proxyInfo.Type == "" || proxyInfo.LocalIP == "" || proxyInfo.LocalPort == 0 || proxyInfo.RemotePort == 0 { postLog.Error("[ModifyProxyHandler] Missing required fields in proxyInfo") - SendErrorResponse(w, http.StatusBadRequest, "Missing required fields in proxyInfo") + handlers.SendErrorResponse(w, http.StatusBadRequest, "Missing required fields in proxyInfo") return } - var instance FrpcInstance + var instance database.FrpcInstance instanceIDInt, _ := strconv.Atoi(instanceID) - instance, err = DBQueryFrpcInstanceByID(instanceIDInt) + instance, err = database.DBQueryFrpcInstanceByID(instanceIDInt) if err != nil { postLog.Error(fmt.Sprintf("[ModifyProxyHandler] Failed to query instance: %v", err)) - SendErrorResponse(w, http.StatusInternalServerError, "Failed to query instance") + handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to query instance") return } if instance.UserID != userID { postLog.Error(fmt.Sprintf("[ModifyProxyHandler] Instance not found for user %d", userID)) - SendErrorResponse(w, http.StatusNotFound, "Instance not found") + handlers.SendErrorResponse(w, http.StatusNotFound, "Instance not found") return } configContent, err := os.ReadFile(instance.ConfigPath) if err != nil { postLog.Error(fmt.Sprintf("[ModifyProxyHandler] Failed to read config file %s: %v", instance.ConfigPath, err)) - SendErrorResponse(w, http.StatusInternalServerError, "Failed to read config file") + handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to read config file") return } - updatedContent, err := modifyFrpcProxy(string(configContent), proxyInfo) + updatedContent, err := config.ModifyFrpcProxy(string(configContent), proxyInfo) if err != nil { postLog.Error(fmt.Sprintf("[ModifyProxyHandler] Failed to modify proxy: %v", err)) - SendErrorResponse(w, http.StatusInternalServerError, "Failed to modify proxy") + handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to modify proxy") return } if err := os.WriteFile(instance.ConfigPath, []byte(updatedContent), 0644); err != nil { postLog.Error(fmt.Sprintf("[ModifyProxyHandler] Failed to write config file %s: %v", instance.ConfigPath, err)) - SendErrorResponse(w, http.StatusInternalServerError, "Failed to write config file") + handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to write config file") return } - SendSuccessResponse(w, "Proxy modified successfully", map[string]interface{}{ + handlers.SendSuccessResponse(w, "Proxy modified successfully", map[string]interface{}{ "instanceID": instance.ID, "configPath": instance.ConfigPath, "proxyName": proxyInfo.Name, @@ -203,9 +207,9 @@ func ModifyProxyHandler(w http.ResponseWriter, r *http.Request) { } func DeleteProxyHandler(w http.ResponseWriter, r *http.Request) { - userID, err := Auth(w, r, http.MethodPost, "superuser", "admin") + userID, err := handlers.Auth(w, r, http.MethodPost, "superuser", "admin") if err != nil { - SendErrorResponse(w, http.StatusUnauthorized, err.Error()) + handlers.SendErrorResponse(w, http.StatusUnauthorized, err.Error()) postLog.Warning(fmt.Sprintf("[DeleteProxyHandler] Auth failed: %v", err)) return } @@ -213,7 +217,7 @@ func DeleteProxyHandler(w http.ResponseWriter, r *http.Request) { body, err := io.ReadAll(r.Body) if err != nil { postLog.Error(fmt.Sprintf("[DeleteProxyHandler] Failed to read request body: %v", err)) - SendErrorResponse(w, http.StatusBadRequest, "Failed to read request body") + handlers.SendErrorResponse(w, http.StatusBadRequest, "Failed to read request body") return } defer r.Body.Close() @@ -221,60 +225,60 @@ func DeleteProxyHandler(w http.ResponseWriter, r *http.Request) { var reqMap map[string]interface{} if err := json.Unmarshal(body, &reqMap); err != nil { postLog.Error(fmt.Sprintf("[DeleteProxyHandler] Failed to unmarshal request body: %v", err)) - SendErrorResponse(w, http.StatusBadRequest, "Invalid request format") + handlers.SendErrorResponse(w, http.StatusBadRequest, "Invalid request format") return } instanceID := getStringFromMap(reqMap, "instanceID") if instanceID == "" { postLog.Error("[DeleteProxyHandler] instanceID is required") - SendErrorResponse(w, http.StatusBadRequest, "instanceID is required") + handlers.SendErrorResponse(w, http.StatusBadRequest, "instanceID is required") return } proxyName := getStringFromMap(reqMap, "proxyName") if proxyName == "" { postLog.Error("[DeleteProxyHandler] proxyName is required") - SendErrorResponse(w, http.StatusBadRequest, "proxyName is required") + handlers.SendErrorResponse(w, http.StatusBadRequest, "proxyName is required") return } - var instance FrpcInstance + var instance database.FrpcInstance instanceIDInt, _ := strconv.Atoi(instanceID) - instance, err = DBQueryFrpcInstanceByID(instanceIDInt) + instance, err = database.DBQueryFrpcInstanceByID(instanceIDInt) if err != nil { postLog.Error(fmt.Sprintf("[DeleteProxyHandler] Failed to query instance: %v", err)) - SendErrorResponse(w, http.StatusInternalServerError, "Failed to query instance") + handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to query instance") return } if instance.UserID != userID { postLog.Error(fmt.Sprintf("[DeleteProxyHandler] Instance not found for user %d", userID)) - SendErrorResponse(w, http.StatusNotFound, "Instance not found") + handlers.SendErrorResponse(w, http.StatusNotFound, "Instance not found") return } configContent, err := os.ReadFile(instance.ConfigPath) if err != nil { postLog.Error(fmt.Sprintf("[DeleteProxyHandler] Failed to read config file %s: %v", instance.ConfigPath, err)) - SendErrorResponse(w, http.StatusInternalServerError, "Failed to read config file") + handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to read config file") return } - updatedContent, err := removeFrpcProxy(string(configContent), proxyName) + updatedContent, err := config.RemoveFrpcProxy(string(configContent), proxyName) if err != nil { postLog.Error(fmt.Sprintf("[DeleteProxyHandler] Failed to remove proxy: %v", err)) - SendErrorResponse(w, http.StatusInternalServerError, "Failed to remove proxy") + handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to remove proxy") return } if err := os.WriteFile(instance.ConfigPath, []byte(updatedContent), 0644); err != nil { postLog.Error(fmt.Sprintf("[DeleteProxyHandler] Failed to write config file %s: %v", instance.ConfigPath, err)) - SendErrorResponse(w, http.StatusInternalServerError, "Failed to write config file") + handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to write config file") return } - SendSuccessResponse(w, "Proxy deleted successfully", map[string]interface{}{ + handlers.SendSuccessResponse(w, "Proxy deleted successfully", map[string]interface{}{ "instanceID": instance.ID, "configPath": instance.ConfigPath, "proxyName": proxyName, @@ -283,9 +287,9 @@ func DeleteProxyHandler(w http.ResponseWriter, r *http.Request) { } func ListProxiesHandler(w http.ResponseWriter, r *http.Request) { - userID, err := Auth(w, r, http.MethodGet) + userID, err := handlers.Auth(w, r, http.MethodGet) if err != nil { - SendErrorResponse(w, http.StatusUnauthorized, err.Error()) + handlers.SendErrorResponse(w, http.StatusUnauthorized, err.Error()) postLog.Warning(fmt.Sprintf("[ListProxiesHandler] Auth failed: %v", err)) return } @@ -294,41 +298,41 @@ func ListProxiesHandler(w http.ResponseWriter, r *http.Request) { instanceID := queryParams.Get("instanceID") if instanceID == "" { postLog.Error("[ListProxiesHandler] instanceID is required") - SendErrorResponse(w, http.StatusBadRequest, "instanceID is required") + handlers.SendErrorResponse(w, http.StatusBadRequest, "instanceID is required") return } - var instance FrpcInstance + var instance database.FrpcInstance instanceIDInt, _ := strconv.Atoi(instanceID) - instance, err = DBQueryFrpcInstanceByID(instanceIDInt) + instance, err = database.DBQueryFrpcInstanceByID(instanceIDInt) if err != nil { postLog.Error(fmt.Sprintf("[ListProxiesHandler] Failed to query instance: %v", err)) - SendErrorResponse(w, http.StatusInternalServerError, "Failed to query instance") + handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to query instance") return } if instance.UserID != userID { postLog.Error(fmt.Sprintf("[ListProxiesHandler] Instance not found for user %d", userID)) - SendErrorResponse(w, http.StatusNotFound, "Instance not found") + handlers.SendErrorResponse(w, http.StatusNotFound, "Instance not found") return } configContent, err := os.ReadFile(instance.ConfigPath) if err != nil { postLog.Error(fmt.Sprintf("[ListProxiesHandler] Failed to read config file %s: %v", instance.ConfigPath, err)) - SendErrorResponse(w, http.StatusInternalServerError, "Failed to read config file") + handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to read config file") return } - var config FrpcConfig - if _, err := toml.Decode(string(configContent), &config); err != nil { + var cfg config.FrpcConfig + if _, err := toml.Decode(string(configContent), &cfg); err != nil { postLog.Error(fmt.Sprintf("[ListProxiesHandler] Failed to parse config: %v", err)) - SendErrorResponse(w, http.StatusInternalServerError, "Failed to parse config file") + handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to parse config file") return } - proxyList := make([]map[string]interface{}, len(config.Proxies)) - for i, proxy := range config.Proxies { + proxyList := make([]map[string]interface{}, len(cfg.Proxies)) + for i, proxy := range cfg.Proxies { proxyData := map[string]interface{}{ "name": proxy["name"], "type": proxy["type"], @@ -339,7 +343,7 @@ func ListProxiesHandler(w http.ResponseWriter, r *http.Request) { proxyList[i] = proxyData } - SendSuccessResponse(w, "Proxies listed successfully", map[string]interface{}{ + handlers.SendSuccessResponse(w, "Proxies listed successfully", map[string]interface{}{ "instanceID": instance.ID, "proxyCount": len(proxyList), "proxies": proxyList, diff --git a/main.go b/main.go index 6ff99da..4cb1ffb 100644 --- a/main.go +++ b/main.go @@ -6,49 +6,21 @@ import ( "net/http" "os" "os/signal" + "super-frpc/config" + "super-frpc/database" "super-frpc/frpLogger" + "super-frpc/global" + "super-frpc/handlers" "super-frpc/postLog" + "super-frpc/session" + "super-frpc/service" "super-frpc/watchdog" "syscall" "time" ) -type SoftwareInfo struct { - Name string - Version string - Developer string - BuildVer int16 - Description string - BuildType string -} - -var softwareInfo SoftwareInfo = SoftwareInfo{ - Name: "Super-frpc", - Version: "0.0.1", - Developer: "Madobi Nanami", - BuildVer: 1, - BuildType: "debug", -} - -type StatusInfo struct { - ServerStatus string - WatchdogStatus string -} - -type Is struct { - debug bool - online bool - watchdogConnected bool -} - -var is Is = Is { - debug: false, - online: false, - watchdogConnected: false, -} - func main() { - postLog.Info(fmt.Sprintf("%s %s (Build %d.%s) by %s", softwareInfo.Name, softwareInfo.Version, softwareInfo.BuildVer, softwareInfo.BuildType, softwareInfo.Developer)) + postLog.Info(fmt.Sprintf("%s %s (Build %d.%s) by %s", global.Software.Name, global.Software.Version, global.Software.BuildVer, global.Software.BuildType, global.Software.Developer)) configPath := flag.String("config", "./config.json", "path to config file") dbPath_data := flag.String("db", "./database.db", "path to database file") dbPath_log := flag.String("log", "./logs.db", "path to logs database file") @@ -66,32 +38,32 @@ func main() { postLog.Info(fmt.Sprintf("Created default config file at %s", *configPath)) } - config, err := LoadConfig(*configPath) + cfg, err := config.LoadConfig(*configPath, service.GetInitSystem) if err != nil { postLog.Fatal(fmt.Sprintf("Failed to load config: %v", err)) } - postLog.SetDebugMode(config.Debug) - is.debug = config.Debug + postLog.SetDebugMode(cfg.Debug) + global.Is.Debug = cfg.Debug if err := postLog.InitLogsDatabase(*dbPath_log); err != nil { postLog.Fatal(fmt.Sprintf("Failed to initialize logs database: %v", err)) } postLog.Info("Logs database initialized successfully") - if err := InitDatabase(*dbPath_data, *dbPath_log); err != nil { + if err := database.InitDatabase(*dbPath_data, *dbPath_log); err != nil { postLog.Fatal(fmt.Sprintf("Failed to initialize database: %v", err)) } postLog.Info("Database initialized successfully") - if err := InitFrpcDatabase(*dbPath_data); err != nil { + if err := database.InitFrpcDatabase(*dbPath_data); err != nil { postLog.Fatal(fmt.Sprintf("Failed to initialize frpc database: %v", err)) } - frpLogger.SetDatabase(db, frpcDB) - frpLogger.SetDebugMode(config.Debug) + frpLogger.SetDatabase(global.ConfigDB, global.FrpcDB) + frpLogger.SetDebugMode(cfg.Debug) - _, err = GetConfig() + _, err = config.GetConfig() if err != nil { postLog.Fatal(fmt.Sprintf("Failed to get config: %v", err)) } @@ -104,15 +76,15 @@ func main() { if err != nil { postLog.Error(fmt.Sprintf("Unable to initialize Watchdog: %s", err)) } else { - if !watchdog.Connect("127.0.0.1", config.Watchdog.Port) { - postLog.Error(fmt.Sprintf("Failed to connect to Watchdog at %s:%d", "127.0.0.1", config.Watchdog.Port)) + if !watchdog.Connect("127.0.0.1", cfg.Watchdog.Port) { + postLog.Error(fmt.Sprintf("Failed to connect to Watchdog at %s:%d", "127.0.0.1", cfg.Watchdog.Port)) } else { - postLog.Info(fmt.Sprintf("Connected to Watchdog at %s:%d", "127.0.0.1", config.Watchdog.Port)) - is.watchdogConnected = true + postLog.Info(fmt.Sprintf("Connected to Watchdog at %s:%d", "127.0.0.1", cfg.Watchdog.Port)) + global.Is.WatchdogConnected = true } } - addr := fmt.Sprintf("%s:%s", config.ListenAddr, config.ListenPort) + addr := fmt.Sprintf("%s:%s", cfg.ListenAddr, cfg.ListenPort) server := &http.Server{ Addr: addr, ReadTimeout: 15 * time.Second, @@ -122,7 +94,7 @@ func main() { go func() { postLog.Info(fmt.Sprintf("Server starting on %s", addr)) - is.online = true + global.Is.Online = true if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed { postLog.Fatal(fmt.Sprintf("Failed to start server: %v", err)) } @@ -132,8 +104,8 @@ func main() { ticker := time.NewTicker(1 * time.Hour) defer ticker.Stop() for range ticker.C { - CleanupExpiredTokens() - CleanupExpiredSessions() + session.CleanupExpiredTokens() + session.CleanupExpiredSessions() } }() @@ -147,37 +119,29 @@ func main() { postLog.Error(fmt.Sprintf("Server closed with error: %v", err)) } - if err := CloseDatabase(); err != nil { + if err := database.CloseDatabase(); err != nil { postLog.Error(fmt.Sprintf("Error closing database: %v", err)) } - if err := CloseFrpcDatabase(); err != nil { - postLog.Error(fmt.Sprintf("Error closing frpc database: %v", err)) - } - watchdog.Close() postLog.Info("Server stopped") } func GetStatusHandler(w http.ResponseWriter, r *http.Request) { - statusInfo := StatusInfo{ - ServerStatus: "Offline", - WatchdogStatus: "Offline", + statusInfo := map[string]string{ + "ServerStatus": "Offline", + "WatchdogStatus": "Offline", } - if !is.online { - statusInfo.ServerStatus = "Offline" - } else { - statusInfo.ServerStatus = "Online" + if global.Is.Online { + statusInfo["ServerStatus"] = "Online" } - if !is.watchdogConnected { - statusInfo.WatchdogStatus = "Offline" - } else { - statusInfo.WatchdogStatus = "Online" + if global.Is.WatchdogConnected { + statusInfo["WatchdogStatus"] = "Online" } - SendSuccessResponse(w, "getStatus", statusInfo) + handlers.SendSuccessResponse(w, "getStatus", statusInfo) } func GetSoftwareInfoHandler(w http.ResponseWriter, r *http.Request) { - SendSuccessResponse(w, "getSoftwareInfo", softwareInfo) + handlers.SendSuccessResponse(w, "getSoftwareInfo", global.Software) } diff --git a/router.go b/router.go index 558e5da..1e80b26 100644 --- a/router.go +++ b/router.go @@ -4,7 +4,11 @@ import ( "fmt" "net/http" "super-frpc/frpLogger" + "super-frpc/handlers" + "super-frpc/instance" "super-frpc/postLog" + "super-frpc/session" + "super-frpc/user" ) func setupRoutes() { @@ -13,33 +17,33 @@ func setupRoutes() { systemLogHandler := postLog.NewLogSocketHandler(postLog.GetLogBroadcaster()) http.HandleFunc("/system/getLogs", systemLogHandler.Handle) - http.HandleFunc("/register", RegisterHandler) - http.HandleFunc("/login", LoginHandler) - http.HandleFunc("/logout", LogoutHandler) + http.HandleFunc("/register", user.RegisterHandler) + http.HandleFunc("/login", user.LoginHandler) + http.HandleFunc("/logout", user.LogoutHandler) - http.HandleFunc("/userMgr/create", CreateUserHandler) - http.HandleFunc("/userMgr/remove", RemoveUserHandler) - http.HandleFunc("/userMgr/list", ListUserHandler) - http.HandleFunc("/userMgr/modify", ModifyUserHandler) - http.HandleFunc("/userMgr/modifyType", ModifyUserTypeHandler) + http.HandleFunc("/userMgr/create", user.CreateUserHandler) + http.HandleFunc("/userMgr/remove", user.RemoveUserHandler) + http.HandleFunc("/userMgr/list", user.ListUserHandler) + http.HandleFunc("/userMgr/modify", user.ModifyUserHandler) + http.HandleFunc("/userMgr/modifyType", user.ModifyUserTypeHandler) - http.HandleFunc("/sessionMgr/list", ListActiveSessionsHandler) - http.HandleFunc("/sessionMgr/remove", RemoveSessionHandler) + http.HandleFunc("/sessionMgr/list", user.ListActiveSessionsHandler) + http.HandleFunc("/sessionMgr/remove", user.RemoveSessionHandler) - http.HandleFunc("/frpcAct/instanceMgr/create", CreateInstanceHandler) - http.HandleFunc("/frpcAct/instanceMgr/delete", DeleteInstanceHandler) - http.HandleFunc("/frpcAct/instanceMgr/modify", ModifyInstanceHandler) - http.HandleFunc("/frpcAct/instanceMgr/list", ListInstancesHandler) - http.HandleFunc("/frpcAct/instanceMgr/start", StartInstanceHandler) - http.HandleFunc("/frpcAct/instanceMgr/stop", StopInstanceHandler) - http.HandleFunc("/frpcAct/instanceMgr/restart", RestartInstanceHandler) - http.HandleFunc("/frpcAct/instanceMgr/status", GetInstanceStatusHandler) - http.HandleFunc("/frpcAct/instanceMgr/getInfo", GetInstanceInfoHandler) - http.HandleFunc("/frpcAct/instanceMgr/logs", frpLogger.NewInstanceLogHandler(ValidateTokenFromMap).ServeHTTP) - http.HandleFunc("/frpcAct/proxyMgr/create", CreateProxyHandler) - http.HandleFunc("/frpcAct/proxyMgr/modify", ModifyProxyHandler) - http.HandleFunc("/frpcAct/proxyMgr/delete", DeleteProxyHandler) - http.HandleFunc("/frpcAct/proxyMgr/list", ListProxiesHandler) + http.HandleFunc("/frpcAct/instanceMgr/create", instance.CreateInstanceHandler) + http.HandleFunc("/frpcAct/instanceMgr/delete", instance.DeleteInstanceHandler) + http.HandleFunc("/frpcAct/instanceMgr/modify", instance.ModifyInstanceHandler) + http.HandleFunc("/frpcAct/instanceMgr/list", instance.ListInstancesHandler) + http.HandleFunc("/frpcAct/instanceMgr/start", instance.StartInstanceHandler) + http.HandleFunc("/frpcAct/instanceMgr/stop", instance.StopInstanceHandler) + http.HandleFunc("/frpcAct/instanceMgr/restart", instance.RestartInstanceHandler) + http.HandleFunc("/frpcAct/instanceMgr/status", instance.GetInstanceStatusHandler) + http.HandleFunc("/frpcAct/instanceMgr/getInfo", instance.GetInstanceInfoHandler) + http.HandleFunc("/frpcAct/instanceMgr/logs", frpLogger.NewInstanceLogHandler(session.ValidateTokenFromMap).ServeHTTP) + http.HandleFunc("/frpcAct/proxyMgr/create", instance.CreateProxyHandler) + http.HandleFunc("/frpcAct/proxyMgr/modify", instance.ModifyProxyHandler) + http.HandleFunc("/frpcAct/proxyMgr/delete", instance.DeleteProxyHandler) + http.HandleFunc("/frpcAct/proxyMgr/list", instance.ListProxiesHandler) http.HandleFunc("/", NotFoundHandler) @@ -49,5 +53,5 @@ func setupRoutes() { func NotFoundHandler(w http.ResponseWriter, r *http.Request) { postLog.Error(fmt.Sprintf("Route not found: %s %s", r.Method, r.URL.Path)) - SendErrorResponse(w, http.StatusNotFound, "Invalid request path") + handlers.SendErrorResponse(w, http.StatusNotFound, "Invalid request path") } diff --git a/os.go b/service/service.go similarity index 93% rename from os.go rename to service/service.go index a7467fb..f53ed67 100644 --- a/os.go +++ b/service/service.go @@ -1,4 +1,4 @@ -package main +package service import ( "fmt" @@ -8,25 +8,28 @@ import ( "runtime" "strconv" "strings" + + "super-frpc/config" + "super-frpc/database" "super-frpc/postLog" ) func GetConfigDir() (string, error) { - config, err := GetConfig() + cfg, err := config.GetConfig() if err != nil { postLog.Error(fmt.Sprintf("[GetConfigDir] Failed to get config: %v", err)) return "", err } - return config.InstancePath, nil + return cfg.InstancePath, nil } func GetFrpcPath() (string, error) { - config, err := GetConfig() + cfg, err := config.GetConfig() if err != nil { postLog.Error(fmt.Sprintf("[GetFrpcPath] Failed to get config: %v", err)) return "", err } - return config.FrpcPath, nil + return cfg.FrpcPath, nil } func GetInitSystem() string { @@ -44,8 +47,8 @@ func GetInitSystem() string { return "unknown" } -func createBootService(instanceID int) error { - instance, err := DBQueryFrpcInstanceByID(instanceID) +func CreateBootService(instanceID int) error { + instance, err := database.DBQueryFrpcInstanceByID(instanceID) if err != nil { return fmt.Errorf("failed to query frpc instance: %w", err) } @@ -70,17 +73,17 @@ func createSystemdService(instanceID int, configPath, runUser string) error { return err } - serviceName, err := GetServiceNameByInstanceID(instanceID) + serviceName, err := database.GetServiceNameByInstanceID(instanceID) if err != nil { return err } - instance, err := DBQueryFrpcInstanceByID(instanceID) + instance, err := database.DBQueryFrpcInstanceByID(instanceID) if err != nil { return fmt.Errorf("failed to query frpc instance: %w", err) } - user, err := GetUserByID(instance.UserID) + user, err := database.GetUserByID(instance.UserID) if err != nil { return fmt.Errorf("failed to get user info: %w", err) } @@ -125,17 +128,17 @@ func createInitDService(instanceID int, configPath, runUser string) error { return err } - serviceName, err := GetServiceNameByInstanceID(instanceID) + serviceName, err := database.GetServiceNameByInstanceID(instanceID) if err != nil { return err } - instance, err := DBQueryFrpcInstanceByID(instanceID) + instance, err := database.DBQueryFrpcInstanceByID(instanceID) if err != nil { return fmt.Errorf("failed to query frpc instance: %w", err) } - user, err := GetUserByID(instance.UserID) + user, err := database.GetUserByID(instance.UserID) if err != nil { return fmt.Errorf("failed to get user info: %w", err) } @@ -268,7 +271,7 @@ func createWindowsBootService(instanceID int, configPath string) error { return err } - serviceName, err := GetServiceNameByInstanceID(instanceID) + serviceName, err := database.GetServiceNameByInstanceID(instanceID) if err != nil { return err } @@ -285,9 +288,9 @@ func createWindowsBootService(instanceID int, configPath string) error { return nil } -func setBootAtStart(instanceID int) error { +func SetBootAtStart(instanceID int) error { initType := GetInitSystem() - serviceName, err := GetServiceNameByInstanceID(instanceID) + serviceName, err := database.GetServiceNameByInstanceID(instanceID) if err != nil { return err } @@ -319,9 +322,9 @@ func setBootAtStart(instanceID int) error { } } -func removeBootAtStart(instanceID int) error { +func RemoveBootAtStart(instanceID int) error { initType := GetInitSystem() - serviceName, err := GetServiceNameByInstanceID(instanceID) + serviceName, err := database.GetServiceNameByInstanceID(instanceID) if err != nil { return err } @@ -353,9 +356,9 @@ func removeBootAtStart(instanceID int) error { } } -func removeBootService(instanceID int) error { +func RemoveBootService(instanceID int) error { initType := GetInitSystem() - serviceName, err := GetServiceNameByInstanceID(instanceID) + serviceName, err := database.GetServiceNameByInstanceID(instanceID) if err != nil { return err } @@ -393,13 +396,13 @@ func removeBootService(instanceID int) error { func IsInstanceRunning(instanceID int) error { initType := GetInitSystem() - serviceName, err := GetServiceNameByInstanceID(instanceID) + serviceName, err := database.GetServiceNameByInstanceID(instanceID) if err != nil { postLog.Error(fmt.Sprintf("[IsInstanceRunning] Failed to get service name: %v", err)) return err } - instance, err := DBQueryFrpcInstanceByID(instanceID) + instance, err := database.DBQueryFrpcInstanceByID(instanceID) if err != nil { postLog.Error(fmt.Sprintf("[IsInstanceRunning] Failed to query instance: %v", err)) return err @@ -504,7 +507,7 @@ func IsInstanceRunning(instanceID int) error { func GetInstancePid(instanceID int) int { initType := GetInitSystem() - serviceName, err := GetServiceNameByInstanceID(instanceID) + serviceName, err := database.GetServiceNameByInstanceID(instanceID) if err != nil { postLog.Error(fmt.Sprintf("[GetInstancePid] Failed to get service name: %v", err)) return 0 diff --git a/session.go b/session/session.go similarity index 87% rename from session.go rename to session/session.go index 2b2f159..6f3c731 100644 --- a/session.go +++ b/session/session.go @@ -1,4 +1,4 @@ -package main +package session import ( "crypto/rand" @@ -9,9 +9,11 @@ import ( "net/http" "strconv" "strings" - "super-frpc/postLog" "sync" "time" + + "super-frpc/database" + "super-frpc/postLog" ) type TokenInfo struct { @@ -20,12 +22,6 @@ type TokenInfo struct { UserID int } -var ( - tokenMap = make(map[int]*TokenInfo) // userID -> TokenInfo - tokenMux sync.RWMutex - tokenTTL = time.Hour -) - type Session struct { ID string UserID int @@ -34,13 +30,19 @@ type Session struct { } var ( - sessionMap = make(map[string]*Session) // sessionID -> Session + tokenMap = make(map[int]*TokenInfo) + tokenMux sync.RWMutex + tokenTTL = time.Hour +) + +var ( + sessionMap = make(map[string]*Session) sessionMux sync.RWMutex sessionTTL = time.Hour ) var ( - sessionTokenMap = make(map[string]string) // sessionID -> token + sessionTokenMap = make(map[string]string) sessionTokenMux sync.RWMutex ) @@ -130,7 +132,7 @@ func GetTokenInfo(userID int) (*TokenInfo, error) { return tokenInfo, nil } -func extractUserIDFromToken(token string) (int, error) { +func ExtractUserIDFromToken(token string) (int, error) { tokenMux.RLock() defer tokenMux.RUnlock() for userID, tokenInfo := range tokenMap { @@ -178,13 +180,13 @@ func CleanupExpiredSessions() { } } -func hashPassword(password string) (string, error) { +func HashPassword(password string) (string, error) { hash := sha256.Sum256([]byte(password)) return hex.EncodeToString(hash[:]), nil } -func verifyPassword(password, hashedPassword string) bool { - hash, err := hashPassword(password) +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 @@ -192,7 +194,7 @@ func verifyPassword(password, hashedPassword string) bool { return hash == hashedPassword } -func isValidPassword(password string) bool { // Validate password complexity and generate hash +func IsValidPassword(password string) bool { if len(password) < 8 { return false } @@ -220,8 +222,8 @@ func isValidPassword(password string) bool { // Validate password complexity and return hasUpper && hasLower && hasDigit && hasSpecial } -func ValidateTimeStamp(header http.Header) bool { - if is.debug { +func ValidateTimeStamp(header http.Header, debug bool) bool { + if debug { return true } @@ -243,7 +245,7 @@ func ValidateTimeStamp(header http.Header) bool { } func GetUserIDFromToken(token string) (int, error) { - userID, err := extractUserIDFromToken(token) + userID, err := ExtractUserIDFromToken(token) if err != nil { return 0, fmt.Errorf("Failed to extract userID from token: %w", err) } @@ -259,7 +261,7 @@ func DeleteTokenInfo(userID int) error { } func GetUsernameByID(userID int) string { - user, err := GetUserByID(userID) + user, err := database.GetUserByID(userID) if err != nil { return "" } @@ -346,3 +348,24 @@ func ValidateTokenFromMap(token string) (int, error) { } return 0, fmt.Errorf("invalid token") } + +func GetSessionTokenMap() *sync.RWMutex { + return &sessionTokenMux +} + +func GetSessionTokenMapSnapshot() map[string]string { + sessionTokenMux.RLock() + defer sessionTokenMux.RUnlock() + snapshot := make(map[string]string) + for k, v := range sessionTokenMap { + snapshot[k] = v + } + return snapshot +} + +func GetSessionToken(sessionID string) (string, bool) { + sessionTokenMux.RLock() + defer sessionTokenMux.RUnlock() + token, exists := sessionTokenMap[sessionID] + return token, exists +} diff --git a/userAct.go b/user/user.go similarity index 55% rename from userAct.go rename to user/user.go index 1d669ff..da05c67 100644 --- a/userAct.go +++ b/user/user.go @@ -1,11 +1,16 @@ -package main +package user import ( "encoding/json" "fmt" "io" "net/http" + + "super-frpc/database" + "super-frpc/global" + "super-frpc/handlers" "super-frpc/postLog" + "super-frpc/session" ) type RegisterRequest struct { @@ -25,13 +30,13 @@ type CreateUserRequest struct { } type ModifyUserRequest struct { - UserID int `json:"userID"` + UserID int `json:"userID"` Username string `json:"username"` Passwd string `json:"passwd"` } type ModifyUserTypeRequest struct { - UserID int `json:"userID"` + UserID int `json:"userID"` Type string `json:"newType"` } @@ -45,20 +50,20 @@ type RemoveSessionRequest struct { func RegisterHandler(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { - SendErrorResponse(w, http.StatusMethodNotAllowed, "Invalid request method") + handlers.SendErrorResponse(w, http.StatusMethodNotAllowed, "Invalid request method") postLog.Warning(fmt.Sprintf("[RegisterHandler] Invalid request method: %s", r.Method)) return } - if !ValidateTimeStamp(r.Header) { - SendErrorResponse(w, http.StatusBadRequest, "Invalid or missing X-Timestamp in header") + if !session.ValidateTimeStamp(r.Header, global.Is.Debug) { + handlers.SendErrorResponse(w, http.StatusBadRequest, "Invalid or missing X-Timestamp in header") postLog.Warning(fmt.Sprintf("[RegisterHandler] Invalid or missing X-Timestamp in header: %s", r.Header.Get("X-Timestamp"))) return } body, err := io.ReadAll(r.Body) if err != nil { - SendErrorResponse(w, http.StatusBadRequest, "Failed to read request body") + handlers.SendErrorResponse(w, http.StatusBadRequest, "Failed to read request body") postLog.Warning(fmt.Sprintf("[RegisterHandler] Failed to read request body: %v", err)) return } @@ -66,33 +71,32 @@ func RegisterHandler(w http.ResponseWriter, r *http.Request) { var req RegisterRequest if err := json.Unmarshal(body, &req); err != nil { - SendErrorResponse(w, http.StatusBadRequest, "Invalid request format") + handlers.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") + handlers.SendErrorResponse(w, http.StatusBadRequest, "Username and password are required") postLog.Warning("[RegisterHandler] New user registration failed: username or password is empty") return } if !isValidInput(req.Username) || !isValidInput(req.Passwd) { - SendErrorResponse(w, http.StatusBadRequest, "Invalid input: contains illegal characters") + handlers.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)") + if !session.IsValidPassword(req.Passwd) { + handlers.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 } - // Check weather the user is the first user, if it is, give the superuser level - userList, err := DBListUsers() + userList, err := database.DBListUsers() if err != nil { - SendErrorResponse(w, http.StatusInternalServerError, err.Error()) + handlers.SendErrorResponse(w, http.StatusInternalServerError, err.Error()) postLog.Error(fmt.Sprintf("[RegisterHandler] Failed to list users: %v", err)) return } @@ -103,21 +107,21 @@ func RegisterHandler(w http.ResponseWriter, r *http.Request) { newUserType = "visitor" } - userID, err := AddUser(req.Username, req.Passwd, newUserType) + userID, err := database.AddUser(req.Username, req.Passwd, newUserType, session.HashPassword, session.IsValidPassword) if err != nil { - SendErrorResponse(w, http.StatusInternalServerError, err.Error()) + handlers.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) + user, err := database.GetUserByID(userID) if err != nil { - SendErrorResponse(w, http.StatusInternalServerError, "Failed to retrieve user after registration") + handlers.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 } - SendSuccessResponse(w, "user registered successfully", map[string]interface{}{ + handlers.SendSuccessResponse(w, "user registered successfully", map[string]interface{}{ "userID": user.UserID, "username": user.Username, "type": user.Type, @@ -126,19 +130,19 @@ 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") + handlers.SendErrorResponse(w, http.StatusMethodNotAllowed, "Invalid request method") postLog.Warning(fmt.Sprintf("[LoginHandler] Invalid request method: %s", r.Method)) return } - if !ValidateTimeStamp(r.Header) { - SendErrorResponse(w, http.StatusBadRequest, "Invalid or missing X-Timestamp in header") + if !session.ValidateTimeStamp(r.Header, global.Is.Debug) { + handlers.SendErrorResponse(w, http.StatusBadRequest, "Invalid or missing X-Timestamp in header") return } body, err := io.ReadAll(r.Body) if err != nil { - SendErrorResponse(w, http.StatusBadRequest, "Failed to read request body") + handlers.SendErrorResponse(w, http.StatusBadRequest, "Failed to read request body") postLog.Warning(fmt.Sprintf("[LoginHandler] Failed to read request body: %v", err)) return } @@ -146,57 +150,57 @@ func LoginHandler(w http.ResponseWriter, r *http.Request) { var req LoginRequest if err := json.Unmarshal(body, &req); err != nil { - SendErrorResponse(w, http.StatusBadRequest, "Invalid request format") + handlers.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") + handlers.SendErrorResponse(w, http.StatusBadRequest, "Username and password are required") postLog.Warning("[LoginHandler] Login failed: username or password is empty") return } if !isValidInput(req.Username) || !isValidInput(req.Passwd) { - SendErrorResponse(w, http.StatusBadRequest, "Invalid input: contains illegal characters") + handlers.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) + user, err := database.GetUserByUsername(req.Username) if err != nil { - SendErrorResponse(w, http.StatusUnauthorized, "User not exist") + handlers.SendErrorResponse(w, http.StatusUnauthorized, "User not exist") postLog.Warning(fmt.Sprintf("[LoginHandler] Login failed: User not exist \"%s\"", req.Username)) return } - if !verifyPassword(req.Passwd, user.Passwd) { - SendErrorResponse(w, http.StatusUnauthorized, "Invalid password") + if !session.VerifyPassword(req.Passwd, user.Passwd) { + handlers.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) + existingTokenInfo, err := session.GetTokenInfo(user.UserID) if err == nil && existingTokenInfo != nil { - SendErrorResponse(w, http.StatusConflict, "User is already logged in") + handlers.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) + token, err := session.GenerateToken(user.UserID) if err != nil { - SendErrorResponse(w, http.StatusInternalServerError, "Failed to generate token") + handlers.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 } - if err := JoinSession(user.UserID, user.Username, token); err != nil { - SendErrorResponse(w, http.StatusInternalServerError, "Failed to create session") + if err := session.JoinSession(user.UserID, user.Username, token); err != nil { + handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to create session") postLog.Error(fmt.Sprintf("[LoginHandler] Failed to create session for user \"%s\": %v", req.Username, err)) return } - SendSuccessResponse(w, "Login successful", map[string]interface{}{ + handlers.SendSuccessResponse(w, "Login successful", map[string]interface{}{ "token": token, "userID": user.UserID, "username": user.Username, @@ -206,16 +210,17 @@ func LoginHandler(w http.ResponseWriter, r *http.Request) { } func LogoutHandler(w http.ResponseWriter, r *http.Request) { - userID, err := Auth(w, r, http.MethodGet) + userID, err := handlers.Auth(w, r, http.MethodGet) if err != nil { - SendErrorResponse(w, http.StatusUnauthorized, err.Error()) + handlers.SendErrorResponse(w, http.StatusUnauthorized, err.Error()) postLog.Warning(fmt.Sprintf("[LogoutHandler] Auth failed: %v", err)) return } + sessionTokenMux := session.GetSessionTokenMap() sessionTokenMux.RLock() sessionID := "" - for sid, token := range sessionTokenMap { + for sid, token := range session.GetSessionTokenMapSnapshot() { if token == r.Header.Get("X-Token") { sessionID = sid break @@ -224,32 +229,32 @@ func LogoutHandler(w http.ResponseWriter, r *http.Request) { sessionTokenMux.RUnlock() if sessionID == "" { - SendErrorResponse(w, http.StatusNotFound, "Session not found for token") - postLog.Warning(fmt.Sprintf("[LogoutHandler] Session not found for token from user [%d]%s", userID, GetUsernameByID(userID))) + handlers.SendErrorResponse(w, http.StatusNotFound, "Session not found for token") + postLog.Warning(fmt.Sprintf("[LogoutHandler] Session not found for token from user [%d]%s", userID, session.GetUsernameByID(userID))) return } - if err := RemoveSession(sessionID); err != nil { - SendErrorResponse(w, http.StatusInternalServerError, "Failed to logout") - postLog.Error(fmt.Sprintf("[LogoutHandler] Failed to logout user [%d]%s: %v", userID, GetUsernameByID(userID), err)) + if err := session.RemoveSession(sessionID); err != nil { + handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to logout") + postLog.Error(fmt.Sprintf("[LogoutHandler] Failed to logout user [%d]%s: %v", userID, session.GetUsernameByID(userID), err)) return } - SendSuccessResponse(w, "Logout successful", nil) - postLog.Info(fmt.Sprintf("[LogoutHandler] User [%d]%s Logout successful", userID, GetUsernameByID(userID))) + handlers.SendSuccessResponse(w, "Logout successful", nil) + postLog.Info(fmt.Sprintf("[LogoutHandler] User [%d]%s Logout successful", userID, session.GetUsernameByID(userID))) } func RemoveSessionHandler(w http.ResponseWriter, r *http.Request) { - userID, err := Auth(w, r, http.MethodPost, "superuser") + userID, err := handlers.Auth(w, r, http.MethodPost, "superuser") if err != nil { - SendErrorResponse(w, http.StatusUnauthorized, err.Error()) + handlers.SendErrorResponse(w, http.StatusUnauthorized, err.Error()) postLog.Warning(fmt.Sprintf("[RemoveSessionHandler] Auth failed: %v", err)) return } body, err := io.ReadAll(r.Body) if err != nil { - SendErrorResponse(w, http.StatusBadRequest, "Failed to read request body") + handlers.SendErrorResponse(w, http.StatusBadRequest, "Failed to read request body") postLog.Warning(fmt.Sprintf("[RemoveSessionHandler] Failed to read request body: %v", err)) return } @@ -257,38 +262,38 @@ func RemoveSessionHandler(w http.ResponseWriter, r *http.Request) { var req RemoveSessionRequest if err := json.Unmarshal(body, &req); err != nil { - SendErrorResponse(w, http.StatusBadRequest, "Invalid request format") + handlers.SendErrorResponse(w, http.StatusBadRequest, "Invalid request format") postLog.Warning(fmt.Sprintf("[RemoveSessionHandler] Invalid request format: %v", err)) return } if req.SessionID == "" { - SendErrorResponse(w, http.StatusBadRequest, "SessionID is required") + handlers.SendErrorResponse(w, http.StatusBadRequest, "SessionID is required") postLog.Warning("[RemoveSessionHandler] SessionID is empty") return } - if err := RemoveSession(req.SessionID); err != nil { - SendErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Failed to remove session: %v", err)) + if err := session.RemoveSession(req.SessionID); err != nil { + handlers.SendErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Failed to remove session: %v", err)) postLog.Error(fmt.Sprintf("[RemoveSessionHandler] Failed to remove session %s: %v", req.SessionID, err)) return } - postLog.Info(fmt.Sprintf("[RemoveSessionHandler] User [%d]%s removed session %s", userID, GetUsernameByID(userID), req.SessionID)) - SendSuccessResponse(w, "Session removed successfully", nil) + postLog.Info(fmt.Sprintf("[RemoveSessionHandler] User [%d]%s removed session %s", userID, session.GetUsernameByID(userID), req.SessionID)) + handlers.SendSuccessResponse(w, "Session removed successfully", nil) } func CreateUserHandler(w http.ResponseWriter, r *http.Request) { - _, err := Auth(w, r, http.MethodPost, "superuser") + _, err := handlers.Auth(w, r, http.MethodPost, "superuser") if err != nil { - SendErrorResponse(w, http.StatusUnauthorized, err.Error()) + handlers.SendErrorResponse(w, http.StatusUnauthorized, err.Error()) postLog.Warning(fmt.Sprintf("[CreateUserHandler] Auth failed: %v", err)) return } body, err := io.ReadAll(r.Body) if err != nil { - SendErrorResponse(w, http.StatusBadRequest, "Failed to read request body") + handlers.SendErrorResponse(w, http.StatusBadRequest, "Failed to read request body") postLog.Warning(fmt.Sprintf("[CreateUserHandler] Failed to read request body: %v", err)) return } @@ -296,38 +301,38 @@ func CreateUserHandler(w http.ResponseWriter, r *http.Request) { var req CreateUserRequest if err := json.Unmarshal(body, &req); err != nil { - SendErrorResponse(w, http.StatusBadRequest, "Invalid request format") + handlers.SendErrorResponse(w, http.StatusBadRequest, "Invalid request format") postLog.Warning(fmt.Sprintf("[CreateUserHandler] Invalid request format: %v", err)) return } if req.Username == "" || req.Passwd == "" || req.Type == "" { - SendErrorResponse(w, http.StatusBadRequest, "Username, password, and type are required") + handlers.SendErrorResponse(w, http.StatusBadRequest, "Username, password, and type are required") postLog.Warning("[CreateUserHandler] CreateUser failed: username, password, or type is empty") return } if req.Type != "admin" && req.Type != "user" && req.Type != "superuser" { - SendErrorResponse(w, http.StatusBadRequest, "Invalid type: must be 'admin' or 'user' or 'superuser'") + handlers.SendErrorResponse(w, http.StatusBadRequest, "Invalid type: must be 'admin' or 'user' or 'superuser'") postLog.Warning(fmt.Sprintf("[CreateUserHandler] CreateUser failed: invalid type: %s", req.Type)) return } - userID, err := AddUser(req.Username, req.Passwd, req.Type) + userID, err := database.AddUser(req.Username, req.Passwd, req.Type, session.HashPassword, session.IsValidPassword) if err != nil { - SendErrorResponse(w, http.StatusInternalServerError, err.Error()) + handlers.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) + user, err := database.GetUserByID(userID) if err != nil { - SendErrorResponse(w, http.StatusInternalServerError, "Failed to retrieve user after creation") + handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to retrieve user after creation") postLog.Error(fmt.Sprintf("[CreateUserHandler] Failed to retrieve user \"%s\" after creation: %v", req.Username, err)) return } - SendSuccessResponse(w, "User created successfully", map[string]interface{}{ + handlers.SendSuccessResponse(w, "User created successfully", map[string]interface{}{ "userID": user.UserID, "username": user.Username, "type": user.Type, @@ -335,16 +340,16 @@ func CreateUserHandler(w http.ResponseWriter, r *http.Request) { postLog.Info(fmt.Sprintf("[CreateUserHandler] User \"%s\" created successfully with ID: %d", req.Username, userID)) } -func ModifyUserHandler (w http.ResponseWriter, r *http.Request) { - _, err := Auth(w, r, http.MethodPost, "user", "admin", "superuser") +func ModifyUserHandler(w http.ResponseWriter, r *http.Request) { + _, err := handlers.Auth(w, r, http.MethodPost, "user", "admin", "superuser") if err != nil { - SendErrorResponse(w, http.StatusUnauthorized, fmt.Sprintf("Auth failed: %v", err)) + handlers.SendErrorResponse(w, http.StatusUnauthorized, fmt.Sprintf("Auth failed: %v", err)) return } body, err := io.ReadAll(r.Body) if err != nil { - SendErrorResponse(w, http.StatusBadRequest, "Failed to read request body") + handlers.SendErrorResponse(w, http.StatusBadRequest, "Failed to read request body") postLog.Warning(fmt.Sprintf("[ModifyUserHandler] Failed to read request body: %v", err)) return } @@ -353,43 +358,43 @@ func ModifyUserHandler (w http.ResponseWriter, r *http.Request) { var req ModifyUserRequest if err := json.Unmarshal(body, &req); err != nil { - SendErrorResponse(w, http.StatusBadRequest, "Invalid request format") + handlers.SendErrorResponse(w, http.StatusBadRequest, "Invalid request format") postLog.Warning(fmt.Sprintf("[ModifyUserHandler] Invalid request format: %v", err)) return } if req.UserID == 0 { - SendErrorResponse(w, http.StatusBadRequest, "UserID is required") + handlers.SendErrorResponse(w, http.StatusBadRequest, "UserID is required") postLog.Warning("[ModifyUserHandler] ModifyUser failed: UserID is empty") return } if req.Username == "" { - SendErrorResponse(w, http.StatusBadRequest, "Username is required") + handlers.SendErrorResponse(w, http.StatusBadRequest, "Username is required") postLog.Warning("[ModifyUserHandler] ModifyUser failed: username is empty") return } - if err := DBUpdateUser(req.UserID, req.Username, req.Passwd); err != nil { - SendErrorResponse(w, http.StatusInternalServerError, err.Error()) + if err := database.DBUpdateUser(req.UserID, req.Username, req.Passwd); err != nil { + handlers.SendErrorResponse(w, http.StatusInternalServerError, err.Error()) postLog.Error(fmt.Sprintf("[ModifyUserHandler] Failed to update user [%d]: %v", req.UserID, err)) return } - SendSuccessResponse(w, "User updated successfully", nil) - postLog.Info(fmt.Sprintf("[ModifyUserHandler] User [%d]%s updated successfully to: %s", req.UserID, GetUsernameByID(req.UserID), req.Username)) + handlers.SendSuccessResponse(w, "User updated successfully", nil) + postLog.Info(fmt.Sprintf("[ModifyUserHandler] User [%d]%s updated successfully to: %s", req.UserID, session.GetUsernameByID(req.UserID), req.Username)) } -func ModifyUserTypeHandler (w http.ResponseWriter, r *http.Request) { - _, err := Auth(w, r, http.MethodPost, "superuser") +func ModifyUserTypeHandler(w http.ResponseWriter, r *http.Request) { + _, err := handlers.Auth(w, r, http.MethodPost, "superuser") if err != nil { - SendErrorResponse(w, http.StatusUnauthorized, err.Error()) + handlers.SendErrorResponse(w, http.StatusUnauthorized, err.Error()) postLog.Warning(fmt.Sprintf("[ModifyUserTypeHandler] Auth failed: %v", err)) return } body, err := io.ReadAll(r.Body) if err != nil { - SendErrorResponse(w, http.StatusBadRequest, "Failed to read request body") + handlers.SendErrorResponse(w, http.StatusBadRequest, "Failed to read request body") postLog.Warning(fmt.Sprintf("[ModifyUserTypeHandler] Failed to read request body: %v", err)) return } @@ -398,40 +403,40 @@ func ModifyUserTypeHandler (w http.ResponseWriter, r *http.Request) { var req ModifyUserTypeRequest if err := json.Unmarshal(body, &req); err != nil { - SendErrorResponse(w, http.StatusBadRequest, "Invalid request format") + handlers.SendErrorResponse(w, http.StatusBadRequest, "Invalid request format") postLog.Warning(fmt.Sprintf("[ModifyUserTypeHandler] Invalid request format: %v", err)) return } if req.UserID == 0 { - SendErrorResponse(w, http.StatusBadRequest, "UserID is required") + handlers.SendErrorResponse(w, http.StatusBadRequest, "UserID is required") postLog.Warning("[ModifyUserTypeHandler] ModifyUserType failed: UserID is empty") return } if req.Type != "admin" && req.Type != "visitor" && req.Type != "superuser" { - SendErrorResponse(w, http.StatusBadRequest, "Invalid type: must be 'admin' or 'visitor' or 'superuser'") + handlers.SendErrorResponse(w, http.StatusBadRequest, "Invalid type: must be 'admin' or 'visitor' or 'superuser'") postLog.Warning(fmt.Sprintf("[ModifyUserTypeHandler] ModifyUserType failed: invalid type: %s", req.Type)) return } - if err := DBUpdateUserType(req.UserID, req.Type); err != nil { - SendErrorResponse(w, http.StatusInternalServerError, err.Error()) + if err := database.DBUpdateUserType(req.UserID, req.Type); err != nil { + handlers.SendErrorResponse(w, http.StatusInternalServerError, err.Error()) postLog.Error(fmt.Sprintf("[ModifyUserTypeHandler] Failed to update user type [%d]: %v", req.UserID, err)) return } - SendSuccessResponse(w, "User type updated successfully", nil) - postLog.Info(fmt.Sprintf("[ModifyUserTypeHandler] User [%d]%s type updated successfully to: %s", req.UserID, GetUsernameByID(req.UserID), req.Type)) + handlers.SendSuccessResponse(w, "User type updated successfully", nil) + postLog.Info(fmt.Sprintf("[ModifyUserTypeHandler] User [%d]%s type updated successfully to: %s", req.UserID, session.GetUsernameByID(req.UserID), req.Type)) } func RemoveUserHandler(w http.ResponseWriter, r *http.Request) { - _, err := Auth(w, r, http.MethodPost, "superuser") + _, err := handlers.Auth(w, r, http.MethodPost, "superuser") if err != nil { - SendErrorResponse(w, http.StatusUnauthorized, err.Error()) + handlers.SendErrorResponse(w, http.StatusUnauthorized, err.Error()) postLog.Warning(fmt.Sprintf("[RemoveUserHandler] Auth failed: %v", err)) return } body, err := io.ReadAll(r.Body) if err != nil { - SendErrorResponse(w, http.StatusBadRequest, "Failed to read request body") + handlers.SendErrorResponse(w, http.StatusBadRequest, "Failed to read request body") postLog.Warning(fmt.Sprintf("[RemoveUserHandler] Failed to read request body: %v", err)) return } @@ -439,49 +444,53 @@ func RemoveUserHandler(w http.ResponseWriter, r *http.Request) { var req RemoveUserRequest if err := json.Unmarshal(body, &req); err != nil { - SendErrorResponse(w, http.StatusBadRequest, "Invalid request format") + handlers.SendErrorResponse(w, http.StatusBadRequest, "Invalid request format") postLog.Warning(fmt.Sprintf("[RemoveUserHandler] Invalid request format: %v", err)) return } if req.TargetUserID == 0 { - SendErrorResponse(w, http.StatusBadRequest, "TargetUserID is required") + handlers.SendErrorResponse(w, http.StatusBadRequest, "TargetUserID is required") postLog.Warning("[RemoveUserHandler] RemoveUser failed: TargetUserID is empty") return } - if err := RemoveUser(req.TargetUserID); err != nil { - SendErrorResponse(w, http.StatusInternalServerError, err.Error()) + if err := database.RemoveUser(req.TargetUserID); err != nil { + handlers.SendErrorResponse(w, http.StatusInternalServerError, err.Error()) postLog.Error(fmt.Sprintf("[RemoveUserHandler] Failed to remove user [%d]: %v", req.TargetUserID, err)) return } - SendSuccessResponse(w, "User removed successfully", nil) + handlers.SendSuccessResponse(w, "User removed successfully", nil) } func ListUserHandler(w http.ResponseWriter, r *http.Request) { - _, err := Auth(w, r, http.MethodGet, "superuser") + _, err := handlers.Auth(w, r, http.MethodGet, "superuser") if err != nil { - SendErrorResponse(w, http.StatusUnauthorized, err.Error()) + handlers.SendErrorResponse(w, http.StatusUnauthorized, err.Error()) postLog.Warning(fmt.Sprintf("[ListUserHandler] Auth failed: %v", err)) return } - userList, err := DBListUsers() + userList, err := database.DBListUsers() if err != nil { - SendErrorResponse(w, http.StatusInternalServerError, "Failed to list users") + handlers.SendErrorResponse(w, http.StatusInternalServerError, "Failed to list users") postLog.Error(fmt.Sprintf("[ListUserHandler] Failed to list users: %v", err)) return } - SendSuccessResponse(w, "User list retrieved successfully", userList) + handlers.SendSuccessResponse(w, "User list retrieved successfully", userList) } func ListActiveSessionsHandler(w http.ResponseWriter, r *http.Request) { - userID, err := Auth(w, r, http.MethodGet, "superuser", "admin") + userID, err := handlers.Auth(w, r, http.MethodGet, "superuser", "admin") if err != nil { - SendErrorResponse(w, http.StatusUnauthorized, err.Error()) + handlers.SendErrorResponse(w, http.StatusUnauthorized, err.Error()) postLog.Warning(fmt.Sprintf("[ListActiveSessionsHandler] Auth failed: %v", err)) return } - sessions := ListActiveSessions() - postLog.Debug(fmt.Sprintf("[ListActiveSessionsHandler] User [%d]%s listed %d active sessions", userID, GetUsernameByID(userID), len(sessions))) - SendSuccessResponse(w, "Active sessions listed", sessions) + sessions := session.ListActiveSessions() + postLog.Debug(fmt.Sprintf("[ListActiveSessionsHandler] User [%d]%s listed %d active sessions", userID, session.GetUsernameByID(userID), len(sessions))) + handlers.SendSuccessResponse(w, "Active sessions listed", sessions) +} + +func isValidInput(input string) bool { + return database.IsValidInput(input) }