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
This commit is contained in:
@@ -0,0 +1,475 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"super-frpc/global"
|
||||
"super-frpc/postLog"
|
||||
|
||||
_ "modernc.org/sqlite"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
UserID int
|
||||
Username string
|
||||
Passwd string
|
||||
Type string
|
||||
CreatedAt string
|
||||
}
|
||||
|
||||
type FrpcInstance struct {
|
||||
ID int
|
||||
UserID int
|
||||
Name string
|
||||
BootAtStart bool
|
||||
RunUser string
|
||||
ConfigPath string
|
||||
CreatedAt time.Time
|
||||
CreatedBy string
|
||||
Watchdog int
|
||||
}
|
||||
|
||||
func InitDatabase(dbPath_data string, dbPath_log string) error {
|
||||
InitUserDatabase(dbPath_data)
|
||||
InitFrpcDatabase(dbPath_data)
|
||||
postLog.InitLogsDatabase(dbPath_log)
|
||||
return nil
|
||||
}
|
||||
|
||||
func CloseDatabase() error {
|
||||
if global.ConfigDB != nil {
|
||||
if err := global.ConfigDB.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
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)
|
||||
for _, chars := range invalidChars {
|
||||
if strings.Contains(lowerInput, chars) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
if !isValidPassword(passwd) {
|
||||
return 0, errors.New("password does not meet complexity requirements")
|
||||
}
|
||||
|
||||
hashedPasswd, err := hashPassword(passwd)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to hash password: %w", err)
|
||||
}
|
||||
|
||||
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") {
|
||||
return 0, errors.New("username already exists")
|
||||
}
|
||||
return 0, fmt.Errorf("failed to insert user: %w", err)
|
||||
}
|
||||
|
||||
lastID, err := result.LastInsertId()
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to get last insert id: %w", err)
|
||||
}
|
||||
|
||||
var count int
|
||||
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)
|
||||
}
|
||||
|
||||
if count == 0 {
|
||||
return 0, errors.New("user insertion verification failed")
|
||||
}
|
||||
|
||||
return int(lastID), nil
|
||||
}
|
||||
|
||||
func RemoveUser(userID int) error {
|
||||
if !isValidInput(strconv.Itoa(userID)) {
|
||||
return errors.New("invalid input: contains illegal characters")
|
||||
}
|
||||
|
||||
result, err := global.ConfigDB.Exec("DELETE FROM userLogin WHERE userID = ?", userID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to delete user: %w", err)
|
||||
}
|
||||
|
||||
rowsAffected, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get rows affected: %w", err)
|
||||
}
|
||||
|
||||
if rowsAffected == 0 {
|
||||
return errors.New("user not found")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetUserByUsername(username string) (*User, error) {
|
||||
if !isValidInput(username) {
|
||||
return nil, errors.New("invalid input: contains illegal characters")
|
||||
}
|
||||
|
||||
var user User
|
||||
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) {
|
||||
return nil, errors.New("user not found")
|
||||
}
|
||||
return nil, fmt.Errorf("failed to query user: %w", err)
|
||||
}
|
||||
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
func GetUserByID(userID int) (*User, error) {
|
||||
var user User
|
||||
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) {
|
||||
return nil, errors.New("user not found")
|
||||
}
|
||||
return nil, fmt.Errorf("failed to query user: %w", err)
|
||||
}
|
||||
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
if !isValidPassword(newPasswd) {
|
||||
return errors.New("password does not meet complexity requirements")
|
||||
}
|
||||
|
||||
hashedPasswd, err := hashPassword(newPasswd)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to hash password: %w", err)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
rowsAffected, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get rows affected: %w", err)
|
||||
}
|
||||
|
||||
if rowsAffected == 0 {
|
||||
return errors.New("user not found")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func UpdateUserType(userID int, newType string) error {
|
||||
validTypes := map[string]bool{
|
||||
"superuser": true,
|
||||
"admin": true,
|
||||
"visitor": true,
|
||||
}
|
||||
|
||||
if !validTypes[newType] {
|
||||
return errors.New("invalid user type")
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
rowsAffected, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get rows affected: %w", err)
|
||||
}
|
||||
|
||||
if rowsAffected == 0 {
|
||||
return errors.New("user not found")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetNextAvailableUserID() (int, error) {
|
||||
var maxID int
|
||||
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)
|
||||
}
|
||||
return maxID + 1, nil
|
||||
}
|
||||
|
||||
func GetAllUsers() ([]User, error) {
|
||||
rows, err := global.ConfigDB.Query("SELECT userID, username, type FROM userLogin")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to query users: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var users []User
|
||||
for rows.Next() {
|
||||
var user User
|
||||
if err := rows.Scan(&user.UserID, &user.Username, &user.Type); err != nil {
|
||||
return nil, fmt.Errorf("failed to scan user: %w", err)
|
||||
}
|
||||
users = append(users, user)
|
||||
}
|
||||
|
||||
if err = rows.Err(); err != nil {
|
||||
return nil, fmt.Errorf("rows error: %w", err)
|
||||
}
|
||||
|
||||
return users, nil
|
||||
}
|
||||
|
||||
func InitFrpcDatabase(dbPath string) error {
|
||||
var err error
|
||||
global.FrpcDB, err = sql.Open("sqlite", dbPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open frpc database: %w", err)
|
||||
}
|
||||
|
||||
if err = global.FrpcDB.Ping(); err != nil {
|
||||
return fmt.Errorf("failed to ping frpc database: %w", err)
|
||||
}
|
||||
|
||||
createTableSQL := `
|
||||
CREATE TABLE IF NOT EXISTS frpcInstances (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
userID INTEGER NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
bootAtStart INTEGER NOT NULL DEFAULT 0,
|
||||
runUser TEXT NOT NULL DEFAULT 'root',
|
||||
configPath TEXT NOT NULL,
|
||||
createdAt TEXT NOT NULL,
|
||||
watchdog INTEGER NOT NULL,
|
||||
UNIQUE(userID, name)
|
||||
);
|
||||
`
|
||||
_, err = global.FrpcDB.Exec(createTableSQL)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create frpcInstances table: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func InitUserDatabase(dbPath string) error {
|
||||
var err error
|
||||
global.ConfigDB, err = sql.Open("sqlite", dbPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open database: %w", err)
|
||||
}
|
||||
|
||||
if err = global.ConfigDB.Ping(); err != nil {
|
||||
return fmt.Errorf("failed to ping database: %w", err)
|
||||
}
|
||||
|
||||
createTableSQL := `
|
||||
CREATE TABLE IF NOT EXISTS userLogin (
|
||||
userID INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
username TEXT UNIQUE NOT NULL,
|
||||
passwd TEXT NOT NULL,
|
||||
type TEXT NOT NULL DEFAULT 'visitor',
|
||||
createdAt TEXT NOT NULL DEFAULT (datetime('now'))
|
||||
);
|
||||
`
|
||||
_, err = global.ConfigDB.Exec(createTableSQL)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create table: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
defer rows.Close()
|
||||
var users []User
|
||||
for rows.Next() {
|
||||
var user User
|
||||
if err := rows.Scan(&user.UserID, &user.Username, &user.Type, &user.CreatedAt); err != nil {
|
||||
return nil, fmt.Errorf("failed to scan user: %w", err)
|
||||
}
|
||||
users = append(users, user)
|
||||
}
|
||||
|
||||
if err = rows.Err(); err != nil {
|
||||
return nil, fmt.Errorf("rows error: %w", err)
|
||||
}
|
||||
|
||||
return users, nil
|
||||
}
|
||||
|
||||
func DBQuerySpecificUser(userID int) (User, error) {
|
||||
var user User
|
||||
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)
|
||||
}
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func DBUpdateUser(userID int, username, passwd string) error {
|
||||
var err error
|
||||
if passwd == "" {
|
||||
_, err = global.ConfigDB.Exec("UPDATE userLogin SET username = ? WHERE userID = ?", username, userID)
|
||||
} else {
|
||||
_, 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)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func DBUpdateUserType(userID int, newType string) error {
|
||||
_, 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)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func DBAddFrpcInstance(instance FrpcInstance) error {
|
||||
_, 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)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func DBQueryFrpcInstanceByID(instanceID int) (FrpcInstance, error) {
|
||||
var instance FrpcInstance
|
||||
var createdAtStr string
|
||||
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)
|
||||
}
|
||||
instance.CreatedAt, _ = time.Parse(time.RFC3339, createdAtStr)
|
||||
return instance, nil
|
||||
}
|
||||
|
||||
func DBQueryFrpcInstance(userID int, instanceName string) (FrpcInstance, error) {
|
||||
var instance FrpcInstance
|
||||
var createdAtStr string
|
||||
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)
|
||||
}
|
||||
instance.CreatedAt, _ = time.Parse(time.RFC3339, createdAtStr)
|
||||
return instance, nil
|
||||
}
|
||||
|
||||
func DBRemoveFrpcInstanceByID(instanceID int) error {
|
||||
_, err := global.FrpcDB.Exec("DELETE FROM frpcInstances WHERE id = ?", instanceID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to delete frpc instance: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func DBUpdateFrpcInstance(instance FrpcInstance) error {
|
||||
_, 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)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func DBListFrpcInstances() ([]FrpcInstance, error) {
|
||||
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
|
||||
`)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to query all frpc instances: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var instances []FrpcInstance
|
||||
for rows.Next() {
|
||||
var instance FrpcInstance
|
||||
var createdAtStr string
|
||||
if err := rows.Scan(&instance.ID, &instance.UserID, &instance.Name, &instance.BootAtStart, &instance.RunUser, &instance.ConfigPath, &createdAtStr, &instance.Watchdog, &instance.CreatedBy); err != nil {
|
||||
return nil, fmt.Errorf("failed to scan frpc instance: %w", err)
|
||||
}
|
||||
instance.CreatedAt, _ = time.Parse(time.RFC3339, createdAtStr)
|
||||
instances = append(instances, instance)
|
||||
}
|
||||
|
||||
if err = rows.Err(); err != nil {
|
||||
return nil, fmt.Errorf("rows error: %w", err)
|
||||
}
|
||||
|
||||
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 {
|
||||
return "", fmt.Errorf("failed to query frpc instance: %w", err)
|
||||
}
|
||||
|
||||
user, err := GetUserByID(instance.UserID)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to get user info: %w", err)
|
||||
}
|
||||
|
||||
serviceName := fmt.Sprintf("superfrpc_%s_%s", user.Username, instance.Name)
|
||||
postLog.Debug(fmt.Sprintf("[GetServiceNameByInstanceID] instanceID: %d, serviceName: %s", instanceID, serviceName))
|
||||
return serviceName, nil
|
||||
}
|
||||
Reference in New Issue
Block a user