2025-09-27 10:17:16 +08:00

645 lines
14 KiB
Go

package migrate
import (
"fmt"
"time"
"github.com/perfect-panel/ppanel-server/internal/model/auth"
"github.com/perfect-panel/ppanel-server/internal/model/payment"
"github.com/perfect-panel/ppanel-server/internal/model/subscribeType"
"github.com/perfect-panel/ppanel-server/internal/model/system"
"github.com/perfect-panel/ppanel-server/internal/model/user"
"github.com/perfect-panel/ppanel-server/pkg/constant"
"github.com/perfect-panel/ppanel-server/pkg/email"
"github.com/perfect-panel/ppanel-server/pkg/logger"
"github.com/perfect-panel/ppanel-server/pkg/sms"
"github.com/perfect-panel/ppanel-server/pkg/tool"
"github.com/perfect-panel/ppanel-server/pkg/uuidx"
"gorm.io/gorm"
)
func InitPPanelSQL(db *gorm.DB) error {
logger.Info("PPanel SQL initialization started")
startTime := time.Now()
defer func() {
logger.Info("PPanel SQL initialization completed", logger.Field("duration", time.Since(startTime).String()))
}()
return db.Transaction(func(tx *gorm.DB) error {
var err error
defer func() {
// If an error occurs, delete all tables
if err != nil {
logger.Debugf("PPanel SQL initialization completed, err: %v", err.Error())
tables, _ := tx.Migrator().GetTables()
for _, table := range tables {
tx.Exec(fmt.Sprintf("DROP TABLE IF EXISTS `%s`", table))
}
}
}()
// init ppanel.sql file
if err = ExecuteSQLFile(tx, "database/ppanel.sql"); err != nil {
return err
}
//Insert basic system data
if err = insertBasicSystemData(tx); err != nil {
return err
}
// insert into OAuth config
if err = insertAuthMethodConfig(tx); err != nil {
return err
}
// insert into Payment config
if err = insertPaymentConfig(tx); err != nil {
return err
}
// insert into SubscribeType
if err = insertSubscribeType(tx); err != nil {
return err
}
return err
})
}
func insertBasicSystemData(tx *gorm.DB) error {
if err := insertSiteConfig(tx); err != nil {
return err
}
if err := insertSubscribeConfig(tx); err != nil {
return err
}
if err := insertVerifyConfig(tx); err != nil {
return err
}
if err := insertSeverConfig(tx); err != nil {
return err
}
if err := insertInviteConfig(tx); err != nil {
return err
}
if err := insertRegisterConfig(tx); err != nil {
return err
}
if err := insertCurrencyConfig(tx); err != nil {
return err
}
if err := insertVerifyCodeConfig(tx); err != nil {
return err
}
version := system.System{
Category: "system",
Key: "Version",
Value: constant.Version,
Type: "string",
Desc: "System Version",
}
if err := tx.Model(&system.System{}).Save(&version).Error; err != nil {
return err
}
return nil
}
// insertSiteConfig
func insertSiteConfig(tx *gorm.DB) error {
siteConfig := []system.System{
{
Category: "site",
Key: "SiteLogo",
Value: "/favicon.svg",
Type: "string",
Desc: "Site Logo",
},
{
Category: "site",
Key: "SiteName",
Value: "Perfect Panel",
Type: "string",
Desc: "Site Name",
},
{
Category: "site",
Key: "SiteDesc",
Value: "PPanel is a pure, professional, and perfect open-source proxy panel tool, designed to be your ideal choice for learning and practical use.",
Type: "string",
Desc: "Site Description",
},
{
Category: "site",
Key: "Host",
Value: "",
Type: "string",
Desc: "Site Host",
},
{
Category: "site",
Key: "Keywords",
Value: "Perfect Panel,PPanel",
Type: "string",
Desc: "Site Keywords",
},
{
Category: "site",
Key: "CustomHTML",
Value: "",
Type: "string",
Desc: "Custom HTML",
},
{
Category: "site",
Key: "CustomData",
Value: "{\"website\":\"\",\"contacts\":{\"email\":\"\",\"telephone\":\"\",\"address\":\"\"},\"community\":{\"telegram\":\"\",\"twitter\":\"\",\"discord\":\"\",\"instagram\":\"\",\"linkedin\":\"\",\"facebook\":\"\",\"github\":\"\"}}",
Type: "string",
Desc: "Custom data",
},
{
Category: "tos",
Key: "TosContent",
Value: "Welcome to use Perfect Panel",
Type: "string",
Desc: "Terms of Service",
},
{
Category: "tos",
Key: "PrivacyPolicy",
Value: "",
Type: "string",
Desc: "PrivacyPolicy",
},
{
Category: "ad",
Key: "WebAD",
Value: "false",
Type: "bool",
Desc: "Display ad on the web",
},
}
return tx.Model(&system.System{}).Save(&siteConfig).Error
}
// insertSubscribeConfig
func insertSubscribeConfig(tx *gorm.DB) error {
subscribeConfig := []system.System{
{
Category: "subscribe",
Key: "SingleModel",
Value: "false",
Type: "bool",
Desc: "是否单订阅模式",
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
},
{
Category: "subscribe",
Key: "SubscribePath",
Value: "/api/subscribe",
Type: "string",
Desc: "订阅路径",
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
},
{
Category: "subscribe",
Key: "SubscribeDomain",
Value: "",
Type: "string",
Desc: "订阅域名",
},
{
Category: "subscribe",
Key: "PanDomain",
Value: "false",
Type: "bool",
Desc: "是否使用泛域名",
},
}
return tx.Model(&system.System{}).Save(&subscribeConfig).Error
}
// insertVerifyConfig
func insertVerifyConfig(tx *gorm.DB) error {
verifyConfig := []system.System{
{
Category: "verify",
Key: "TurnstileSiteKey",
Value: "",
Type: "string",
Desc: "TurnstileSiteKey",
},
{
Category: "verify",
Key: "TurnstileSecret",
Value: "",
Type: "string",
Desc: "TurnstileSecret",
},
{
Category: "verify",
Key: "EnableLoginVerify",
Value: "false",
Type: "bool",
Desc: "is enable login verify",
},
{
Category: "verify",
Key: "EnableRegisterVerify",
Value: "false",
Type: "bool",
Desc: "is enable register verify",
},
{
Category: "verify",
Key: "EnableResetPasswordVerify",
Value: "false",
Type: "bool",
Desc: "is enable reset password verify",
},
}
return tx.Model(&system.System{}).Save(&verifyConfig).Error
}
// insertSeverConfig
func insertSeverConfig(tx *gorm.DB) error {
serverConfig := []system.System{
{
Category: "server",
Key: "NodeSecret",
Value: "12345678",
Type: "string",
Desc: "node secret",
},
{
Category: "server",
Key: "NodePullInterval",
Value: "10",
Type: "int",
Desc: "node pull interval",
},
{
Category: "server",
Key: "NodePushInterval",
Value: "60",
Type: "int",
Desc: "node push interval",
},
{
Category: "server",
Key: "NodeMultiplierConfig",
Value: "[]",
Type: "string",
Desc: "node multiplier config",
},
}
return tx.Model(&system.System{}).Save(&serverConfig).Error
}
// insertInviteConfig
func insertInviteConfig(tx *gorm.DB) error {
inviteConfig := []system.System{
{
Category: "invite",
Key: "ForcedInvite",
Value: "false",
Type: "bool",
Desc: "Forced invite",
},
{
Category: "invite",
Key: "ReferralPercentage",
Value: "20",
Type: "int",
Desc: "Referral percentage",
},
{
Category: "invite",
Key: "OnlyFirstPurchase",
Value: "false",
Type: "bool",
Desc: "Only first purchase",
},
}
return tx.Model(&system.System{}).Save(&inviteConfig).Error
}
// insertRegisterConfig
func insertRegisterConfig(tx *gorm.DB) error {
registerConfig := []system.System{
{
Category: "register",
Key: "StopRegister",
Value: "false",
Type: "bool",
Desc: "is stop register",
},
{
Category: "register",
Key: "EnableTrial",
Value: "false",
Type: "bool",
Desc: "is enable trial",
},
{
Category: "register",
Key: "TrialSubscribe",
Value: "",
Type: "int",
Desc: "Trial subscription",
},
{
Category: "register",
Key: "TrialTime",
Value: "24",
Type: "int",
Desc: "Trial time",
},
{
Category: "register",
Key: "TrialTimeUnit",
Value: "Hour",
Type: "string",
Desc: "Trial time unit",
},
{
Category: "register",
Key: "EnableIpRegisterLimit",
Value: "false",
Type: "bool",
Desc: "is enable IP register limit",
},
{
Category: "register",
Key: "IpRegisterLimit",
Value: "3",
Type: "int",
Desc: "IP Register Limit",
},
{
Category: "register",
Key: "IpRegisterLimitDuration",
Value: "64",
Type: "int",
Desc: "IP Register Limit Duration (minutes)",
},
}
return tx.Model(&system.System{}).Save(&registerConfig).Error
}
// insertAuthMethodConfig
func insertAuthMethodConfig(tx *gorm.DB) error {
// insert into OAuth config
var methods []auth.Auth
methods = append(methods, []auth.Auth{
initEmailConfig(),
initMobileConfig(),
{
Method: "apple",
Config: new(auth.AppleAuthConfig).Marshal(),
},
{
Method: "google",
Config: new(auth.GoogleAuthConfig).Marshal(),
},
{
Method: "github",
Config: new(auth.GithubAuthConfig).Marshal(),
},
{
Method: "facebook",
Config: new(auth.FacebookAuthConfig).Marshal(),
},
{
Method: "telegram",
Config: new(auth.TelegramAuthConfig).Marshal(),
},
{
Method: "device",
Config: new(auth.DeviceConfig).Marshal(),
},
}...)
return tx.Model(&auth.Auth{}).Save(&methods).Error
}
// insertPaymentConfig
func insertPaymentConfig(tx *gorm.DB) error {
enable := true
payments := []payment.Payment{
{
Id: -1,
Name: "Balance",
Platform: "balance",
Icon: "",
Domain: "",
Config: "",
FeeMode: 0,
FeePercent: 0,
FeeAmount: 0,
Enable: &enable,
},
}
// reset auto increment
if err := tx.Exec("ALTER TABLE `payment` AUTO_INCREMENT = 1").Error; err != nil {
logger.Errorw("Reset auto increment failed", logger.Field("error", err))
return err
}
return tx.Model(&payment.Payment{}).Save(&payments).Error
}
// insertSubscribeType
func insertSubscribeType(tx *gorm.DB) error {
// insert into subscribe type
var subscribeTypes []subscribeType.SubscribeType
subscribeTypes = append(subscribeTypes, []subscribeType.SubscribeType{
{
Name: "Clash",
Mark: "Clash",
},
{
Name: "Hiddify",
Mark: "Hiddify",
},
{
Name: "Loon",
Mark: "Loon",
},
{
Name: "NekoBox",
Mark: "NekoBox",
},
{
Name: "NekoRay",
Mark: "NekoRay",
},
{
Name: "Netch",
Mark: "Netch",
},
{
Name: "Quantumult",
Mark: "Quantumult",
},
{
Name: "Shadowrocket",
Mark: "Shadowrocket",
},
{
Name: "Singbox",
Mark: "Singbox",
},
{
Name: "Surfboard",
Mark: "Surfboard",
},
{
Name: "Surge",
Mark: "Surge",
},
{
Name: "V2box",
Mark: "V2box",
},
{
Name: "V2rayN",
Mark: "V2rayN",
},
{
Name: "V2rayNg",
Mark: "V2rayNg",
},
}...)
// insert into payment
return tx.Save(&subscribeTypes).Error
}
// CreateAdminUser create admin user
func CreateAdminUser(email, password string, tx *gorm.DB) error {
enable := true
return tx.Transaction(func(tx *gorm.DB) error {
// Prevent duplicate creation
if tx.Model(&user.User{}).Find(&user.User{}).RowsAffected != 0 {
logger.Info("User already exists, skip creating administrator account")
return nil
}
u := user.User{
Password: tool.EncodePassWord(password),
IsAdmin: &enable,
ReferCode: uuidx.UserInviteCode(time.Now().Unix()),
}
if err := tx.Model(&user.User{}).Save(&u).Error; err != nil {
return err
}
method := user.AuthMethods{
UserId: u.Id,
AuthType: "email",
AuthIdentifier: email,
Verified: true,
}
if err := tx.Model(&user.AuthMethods{}).Save(&method).Error; err != nil {
return err
}
return nil
})
}
func initEmailConfig() auth.Auth {
enable := true
smtpConfig := new(auth.SMTPConfig)
emailConfig := auth.EmailAuthConfig{
Platform: "smtp",
PlatformConfig: smtpConfig,
EnableVerify: false,
EnableDomainSuffix: false,
DomainSuffixList: "",
VerifyEmailTemplate: email.DefaultEmailVerifyTemplate,
ExpirationEmailTemplate: email.DefaultExpirationEmailTemplate,
MaintenanceEmailTemplate: email.DefaultMaintenanceEmailTemplate,
TrafficExceedEmailTemplate: email.DefaultTrafficExceedEmailTemplate,
}
authMethod := auth.Auth{
Method: "email",
Config: emailConfig.Marshal(),
Enabled: &enable,
}
return authMethod
}
func initMobileConfig() auth.Auth {
cfg := new(auth.AlibabaCloudConfig)
mobileConfig := auth.MobileAuthConfig{
Platform: sms.AlibabaCloud.String(),
PlatformConfig: cfg,
EnableWhitelist: false,
Whitelist: make([]string, 0),
}
authMethod := auth.Auth{
Method: "mobile",
Config: mobileConfig.Marshal(),
}
return authMethod
}
// insert into currency config
func insertCurrencyConfig(tx *gorm.DB) error {
currencyConfig := []system.System{
{
Category: "currency",
Key: "Currency",
Value: "USD",
Type: "string",
Desc: "Currency",
},
{
Category: "currency",
Key: "CurrencySymbol",
Value: "$",
Type: "string",
Desc: "Currency Symbol",
},
{
Category: "currency",
Key: "CurrencyUnit",
Value: "USD",
Type: "string",
Desc: "Currency Unit",
},
{
Category: "currency",
Key: "AccessKey",
Value: "",
Type: "string",
Desc: "Exchangerate Access Key",
},
}
return tx.Model(&system.System{}).Save(&currencyConfig).Error
}
// insert into verify code config
func insertVerifyCodeConfig(tx *gorm.DB) error {
verifyCodeConfig := []system.System{
{
Category: "verify_code",
Key: "VerifyCodeExpireTime",
Value: "300",
Type: "int",
Desc: "Verify code expire time",
},
{
Category: "verify_code",
Key: "VerifyCodeLimit",
Value: "15",
Type: "int",
Desc: "limits of verify code",
},
{
Category: "verify_code",
Key: "VerifyCodeInterval",
Value: "60",
Type: "int",
Desc: "Interval of verify code",
},
}
return tx.Model(&system.System{}).Save(&verifyCodeConfig).Error
}