645 lines
14 KiB
Go
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(®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
|
|
}
|