265 lines
6.8 KiB
Go
265 lines
6.8 KiB
Go
package handler
|
||
|
||
import (
|
||
"net/http"
|
||
"os"
|
||
"path/filepath"
|
||
|
||
"proxy-platform/internal/config"
|
||
"proxy-platform/internal/models"
|
||
"proxy-platform/internal/repository"
|
||
|
||
"github.com/gin-gonic/gin"
|
||
"go.uber.org/zap"
|
||
"golang.org/x/crypto/bcrypt"
|
||
"gorm.io/driver/sqlite"
|
||
"gorm.io/gorm"
|
||
)
|
||
|
||
// CheckInstall 检查安装状态
|
||
func CheckInstall() gin.HandlerFunc {
|
||
return func(c *gin.Context) {
|
||
// 检查数据库是否存在安装记录
|
||
dbPath := "data/proxy.db"
|
||
installed := false
|
||
|
||
if _, err := os.Stat(dbPath); err == nil {
|
||
// 数据库文件存在,检查安装状态
|
||
db, err := gorm.Open(sqlite.Open(dbPath), &gorm.Config{})
|
||
if err == nil {
|
||
var status models.InstallStatus
|
||
if result := db.First(&status); result.Error == nil {
|
||
installed = status.Installed
|
||
}
|
||
sqlDB, _ := db.DB()
|
||
sqlDB.Close()
|
||
}
|
||
}
|
||
|
||
c.JSON(http.StatusOK, gin.H{
|
||
"installed": installed,
|
||
})
|
||
}
|
||
}
|
||
|
||
// InstallRequest 安装请求
|
||
type InstallRequest struct {
|
||
DbType string `json:"db_type" binding:"required"` // sqlite, postgres
|
||
AdminUser string `json:"admin_user" binding:"required"`
|
||
AdminPass string `json:"admin_pass" binding:"required"`
|
||
PlatformName string `json:"platform_name"`
|
||
|
||
// SQLite 配置
|
||
SqlitePath string `json:"sqlite_path"`
|
||
|
||
// PostgreSQL 配置(可选)
|
||
PgHost string `json:"pg_host"`
|
||
PgPort int `json:"pg_port"`
|
||
PgUser string `json:"pg_user"`
|
||
PgPassword string `json:"pg_password"`
|
||
PgDatabase string `json:"pg_database"`
|
||
|
||
// Redis 配置(可选)
|
||
RedisEnabled bool `json:"redis_enabled"`
|
||
RedisHost string `json:"redis_host"`
|
||
RedisPort int `json:"redis_port"`
|
||
RedisPassword string `json:"redis_password"`
|
||
RedisDB int `json:"redis_db"`
|
||
}
|
||
|
||
// DoInstall 执行安装
|
||
func DoInstall(logger *zap.Logger) gin.HandlerFunc {
|
||
return func(c *gin.Context) {
|
||
var req InstallRequest
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||
return
|
||
}
|
||
|
||
// 验证必填项
|
||
if req.AdminUser == "" || req.AdminPass == "" {
|
||
c.JSON(http.StatusBadRequest, gin.H{"error": "用户名和密码不能为空"})
|
||
return
|
||
}
|
||
|
||
if req.DbType != "sqlite" && req.DbType != "postgres" {
|
||
c.JSON(http.StatusBadRequest, gin.H{"error": "数据库类型只能是 sqlite 或 postgres"})
|
||
return
|
||
}
|
||
|
||
if req.DbType == "postgres" {
|
||
if req.PgHost == "" || req.PgDatabase == "" {
|
||
c.JSON(http.StatusBadRequest, gin.H{"error": "PostgreSQL 配置不完整"})
|
||
return
|
||
}
|
||
}
|
||
|
||
// 确定数据库路径
|
||
dbPath := req.SqlitePath
|
||
if dbPath == "" {
|
||
dbPath = "data/proxy.db"
|
||
}
|
||
|
||
// 创建数据目录
|
||
dbDir := filepath.Dir(dbPath)
|
||
if err := os.MkdirAll(dbDir, 0755); err != nil {
|
||
c.JSON(http.StatusInternalServerError, gin.H{"error": "创建数据目录失败: " + err.Error()})
|
||
return
|
||
}
|
||
|
||
// 初始化数据库
|
||
var db *gorm.DB
|
||
var err error
|
||
|
||
if req.DbType == "sqlite" {
|
||
db, err = gorm.Open(sqlite.Open(dbPath), &gorm.Config{})
|
||
} else {
|
||
// PostgreSQL 支持(需要额外配置 postgres driver)
|
||
c.JSON(http.StatusBadRequest, gin.H{"error": "PostgreSQL 支持需要额外配置,请使用 SQLite"})
|
||
return
|
||
}
|
||
|
||
if err != nil {
|
||
c.JSON(http.StatusInternalServerError, gin.H{"error": "数据库连接失败: " + err.Error()})
|
||
return
|
||
}
|
||
|
||
// 自动迁移
|
||
if err := db.AutoMigrate(
|
||
&models.User{},
|
||
&models.Node{},
|
||
&models.NodeGroup{},
|
||
&models.UnlockStatus{},
|
||
&models.IPChangeLog{},
|
||
&models.ConnectionLog{},
|
||
&models.IPRefreshRule{},
|
||
&models.InstallStatus{},
|
||
); err != nil {
|
||
c.JSON(http.StatusInternalServerError, gin.H{"error": "数据库迁移失败: " + err.Error()})
|
||
return
|
||
}
|
||
|
||
// 创建管理员用户
|
||
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(req.AdminPass), bcrypt.DefaultCost)
|
||
if err != nil {
|
||
c.JSON(http.StatusInternalServerError, gin.H{"error": "密码加密失败"})
|
||
return
|
||
}
|
||
|
||
adminUser := &models.User{
|
||
Username: req.AdminUser,
|
||
PasswordHash: string(hashedPassword),
|
||
Status: "active",
|
||
TrafficQuota: 0, // 无限制
|
||
}
|
||
|
||
userRepo := repository.NewUserRepository(db)
|
||
if err := userRepo.Create(adminUser); err != nil {
|
||
c.JSON(http.StatusInternalServerError, gin.H{"error": "创建管理员失败: " + err.Error()})
|
||
return
|
||
}
|
||
|
||
// 创建安装状态记录
|
||
platformName := req.PlatformName
|
||
if platformName == "" {
|
||
platformName = "代理管理平台"
|
||
}
|
||
|
||
installStatus := &models.InstallStatus{
|
||
Installed: true,
|
||
DbType: req.DbType,
|
||
RedisEnabled: req.RedisEnabled,
|
||
AdminUser: req.AdminUser,
|
||
PlatformName: platformName,
|
||
}
|
||
|
||
if err := db.Create(installStatus).Error; err != nil {
|
||
c.JSON(http.StatusInternalServerError, gin.H{"error": "保存安装状态失败: " + err.Error()})
|
||
return
|
||
}
|
||
|
||
// 生成配置文件
|
||
cfg := &config.Config{
|
||
Server: config.ServerConfig{
|
||
Host: "0.0.0.0",
|
||
Port: 8080,
|
||
Mode: "release",
|
||
},
|
||
SOCKS5: config.SOCKS5Config{
|
||
Host: "0.0.0.0",
|
||
Port: 1080,
|
||
MaxConnections: 10000,
|
||
Timeout: 30,
|
||
},
|
||
Database: config.DatabaseConfig{
|
||
Type: req.DbType,
|
||
Path: dbPath,
|
||
},
|
||
Redis: config.RedisConfig{
|
||
Enabled: req.RedisEnabled,
|
||
Host: req.RedisHost,
|
||
Port: req.RedisPort,
|
||
Password: req.RedisPassword,
|
||
DB: req.RedisDB,
|
||
},
|
||
Scheduler: config.SchedulerConfig{
|
||
Strategy: "least_latency",
|
||
HealthCheckInterval: 60,
|
||
UnlockCheckInterval: 300,
|
||
NodeTimeout: 120,
|
||
},
|
||
Logging: config.LoggingConfig{
|
||
Level: "info",
|
||
Output: "stdout",
|
||
},
|
||
Install: config.InstallConfig{
|
||
Installed: true,
|
||
},
|
||
}
|
||
|
||
// 保存配置文件
|
||
configPath := "configs/scheduler.yaml"
|
||
if err := config.Save(configPath, cfg); err != nil {
|
||
logger.Error("保存配置文件失败", zap.Error(err))
|
||
}
|
||
|
||
// 关闭数据库连接
|
||
sqlDB, _ := db.DB()
|
||
sqlDB.Close()
|
||
|
||
logger.Info("安装完成",
|
||
zap.String("db_type", req.DbType),
|
||
zap.Bool("redis_enabled", req.RedisEnabled),
|
||
zap.String("admin_user", req.AdminUser),
|
||
)
|
||
|
||
c.JSON(http.StatusOK, gin.H{
|
||
"message": "安装成功",
|
||
"db_type": req.DbType,
|
||
"redis_enabled": req.RedisEnabled,
|
||
"admin_user": req.AdminUser,
|
||
})
|
||
}
|
||
}
|
||
|
||
// GetInstallStatus 获取安装状态(从数据库)
|
||
func GetInstallStatus(db *gorm.DB) gin.HandlerFunc {
|
||
return func(c *gin.Context) {
|
||
var status models.InstallStatus
|
||
if result := db.First(&status); result.Error != nil {
|
||
c.JSON(http.StatusOK, gin.H{
|
||
"installed": false,
|
||
})
|
||
return
|
||
}
|
||
|
||
c.JSON(http.StatusOK, gin.H{
|
||
"installed": status.Installed,
|
||
"db_type": status.DbType,
|
||
"redis_enabled": status.RedisEnabled,
|
||
"platform_name": status.PlatformName,
|
||
"admin_user": status.AdminUser,
|
||
})
|
||
}
|
||
}
|