Files
proxy-platform/internal/handler/install.go
T

265 lines
6.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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,
})
}
}