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(®isterConfig).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(¤cyConfig).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 }