同步历史版本代码
This commit is contained in:
@@ -116,7 +116,7 @@ VALUES (1, 'site', 'SiteLogo', '/favicon.svg', 'string', 'Site Logo', '2025-04-2
|
||||
'2025-04-22 14:25:16.641'),
|
||||
(37, 'currency', 'AccessKey', '', 'string', 'Exchangerate Access Key', '2025-04-22 14:25:16.641',
|
||||
'2025-04-22 14:25:16.641'),
|
||||
(38, 'verify_code', 'VerifyCodeExpireTime', '300', 'int', 'Verify code expire time', '2025-04-22 14:25:16.641',
|
||||
(38, 'verify_code', 'VerifyCodeExpireTime', '900', 'int', 'Verify code expire time', '2025-04-22 14:25:16.641',
|
||||
'2025-04-22 14:25:16.641'),
|
||||
(39, 'verify_code', 'VerifyCodeLimit', '15', 'int', 'limits of verify code', '2025-04-22 14:25:16.641',
|
||||
'2025-04-22 14:25:16.641'),
|
||||
|
||||
@@ -1,2 +1,17 @@
|
||||
-- Remove status column from redemption_code table
|
||||
ALTER TABLE `redemption_code` DROP COLUMN `status`;
|
||||
SET @dbname = DATABASE();
|
||||
SET @tablename = 'redemption_code';
|
||||
SET @colname = 'status';
|
||||
SET @sql = (
|
||||
SELECT IF(
|
||||
COUNT(*) > 0,
|
||||
'ALTER TABLE `redemption_code` DROP COLUMN `status`;',
|
||||
'SELECT "Column `status` does not exist";'
|
||||
)
|
||||
FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = @dbname
|
||||
AND TABLE_NAME = @tablename
|
||||
AND COLUMN_NAME = @colname
|
||||
);
|
||||
PREPARE stmt FROM @sql;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
@@ -1,2 +1,17 @@
|
||||
-- Add status column to redemption_code table
|
||||
ALTER TABLE `redemption_code` ADD COLUMN `status` TINYINT NOT NULL DEFAULT 1 COMMENT 'Status: 1=enabled, 0=disabled' AFTER `quantity`;
|
||||
SET @dbname = DATABASE();
|
||||
SET @tablename = 'redemption_code';
|
||||
SET @colname = 'status';
|
||||
SET @sql = (
|
||||
SELECT IF(
|
||||
COUNT(*) = 0,
|
||||
'ALTER TABLE `redemption_code` ADD COLUMN `status` TINYINT NOT NULL DEFAULT 1 COMMENT ''Status: 1=enabled, 0=disabled'' AFTER `quantity`;',
|
||||
'SELECT "Column `status` already exists";'
|
||||
)
|
||||
FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = @dbname
|
||||
AND TABLE_NAME = @tablename
|
||||
AND COLUMN_NAME = @colname
|
||||
);
|
||||
PREPARE stmt FROM @sql;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
DROP TABLE IF EXISTS `log_message`;
|
||||
@@ -0,0 +1,27 @@
|
||||
CREATE TABLE IF NOT EXISTS `log_message` (
|
||||
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`platform` VARCHAR(32) NOT NULL,
|
||||
`app_version` VARCHAR(32) NULL,
|
||||
`os_name` VARCHAR(32) NULL,
|
||||
`os_version` VARCHAR(32) NULL,
|
||||
`device_id` VARCHAR(64) NULL,
|
||||
`user_id` BIGINT NULL DEFAULT NULL,
|
||||
`session_id` VARCHAR(64) NULL,
|
||||
`level` TINYINT UNSIGNED NOT NULL DEFAULT 3,
|
||||
`error_code` VARCHAR(64) NULL,
|
||||
`message` TEXT NOT NULL,
|
||||
`stack` MEDIUMTEXT NULL,
|
||||
`context` JSON NULL,
|
||||
`client_ip` VARCHAR(45) NULL,
|
||||
`user_agent` VARCHAR(255) NULL,
|
||||
`locale` VARCHAR(16) NULL,
|
||||
`digest` VARCHAR(64) NULL,
|
||||
`occurred_at` DATETIME NULL,
|
||||
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uniq_digest` (`digest`),
|
||||
KEY `idx_platform_time` (`platform`, `created_at`),
|
||||
KEY `idx_user_time` (`user_id`, `created_at`),
|
||||
KEY `idx_device_time` (`device_id`, `created_at`),
|
||||
KEY `idx_error_code` (`error_code`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
@@ -0,0 +1 @@
|
||||
DROP TABLE IF EXISTS `apple_iap_transactions`;
|
||||
@@ -0,0 +1,15 @@
|
||||
CREATE TABLE IF NOT EXISTS `apple_iap_transactions` (
|
||||
`id` bigint(20) NOT NULL AUTO_INCREMENT,
|
||||
`user_id` bigint(20) NOT NULL COMMENT 'User ID',
|
||||
`original_transaction_id` varchar(255) NOT NULL COMMENT 'Original Transaction ID',
|
||||
`transaction_id` varchar(255) NOT NULL COMMENT 'Transaction ID',
|
||||
`product_id` varchar(255) NOT NULL COMMENT 'Product ID',
|
||||
`purchase_at` datetime NOT NULL COMMENT 'Purchase Time',
|
||||
`revocation_at` datetime DEFAULT NULL COMMENT 'Revocation Time',
|
||||
`jws_hash` varchar(255) NOT NULL COMMENT 'JWS Hash',
|
||||
`created_at` datetime DEFAULT NULL COMMENT 'Create Time',
|
||||
`updated_at` datetime DEFAULT NULL COMMENT 'Update Time',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uni_original` (`original_transaction_id`),
|
||||
KEY `idx_user_id` (`user_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
@@ -0,0 +1 @@
|
||||
ALTER TABLE user DROP COLUMN last_login_time;
|
||||
@@ -0,0 +1 @@
|
||||
ALTER TABLE user ADD COLUMN last_login_time DATETIME DEFAULT NULL COMMENT 'Last Login Time';
|
||||
@@ -0,0 +1 @@
|
||||
ALTER TABLE auth_method MODIFY config TEXT NOT NULL COMMENT 'Auth Configuration';
|
||||
@@ -0,0 +1 @@
|
||||
ALTER TABLE auth_method MODIFY config MEDIUMTEXT NOT NULL COMMENT 'Auth Configuration';
|
||||
@@ -0,0 +1,2 @@
|
||||
DROP TABLE IF EXISTS `user_family_member`;
|
||||
DROP TABLE IF EXISTS `user_family`;
|
||||
@@ -0,0 +1,31 @@
|
||||
CREATE TABLE IF NOT EXISTS `user_family` (
|
||||
`id` BIGINT NOT NULL AUTO_INCREMENT,
|
||||
`owner_user_id` BIGINT NOT NULL COMMENT 'Owner User ID',
|
||||
`max_members` INT NOT NULL DEFAULT 5 COMMENT 'Max members in family',
|
||||
`status` TINYINT NOT NULL DEFAULT 1 COMMENT 'Status: 1=active, 0=disabled',
|
||||
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
`updated_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
||||
`deleted_at` DATETIME(3) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uniq_owner_user_id` (`owner_user_id`),
|
||||
KEY `idx_status` (`status`),
|
||||
KEY `idx_deleted_at` (`deleted_at`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `user_family_member` (
|
||||
`id` BIGINT NOT NULL AUTO_INCREMENT,
|
||||
`family_id` BIGINT NOT NULL COMMENT 'Family ID',
|
||||
`user_id` BIGINT NOT NULL COMMENT 'Member User ID',
|
||||
`role` TINYINT NOT NULL DEFAULT 2 COMMENT 'Role: 1=owner, 2=member',
|
||||
`status` TINYINT NOT NULL DEFAULT 1 COMMENT 'Status: 1=active, 2=left, 3=removed',
|
||||
`join_source` VARCHAR(32) NOT NULL DEFAULT '' COMMENT 'Join source',
|
||||
`joined_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
`left_at` DATETIME(3) DEFAULT NULL,
|
||||
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
`updated_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
||||
`deleted_at` DATETIME(3) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uniq_user_id` (`user_id`),
|
||||
KEY `idx_family_status` (`family_id`, `status`),
|
||||
KEY `idx_deleted_at` (`deleted_at`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
@@ -0,0 +1,184 @@
|
||||
package initialize
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/perfect-panel/server/internal/svc"
|
||||
"github.com/perfect-panel/server/pkg/logger"
|
||||
"github.com/pkg/errors"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type schemaTablePatch struct {
|
||||
table string
|
||||
ddl string
|
||||
}
|
||||
|
||||
type schemaColumnPatch struct {
|
||||
table string
|
||||
column string
|
||||
ddl string
|
||||
}
|
||||
|
||||
func EnsureSchemaCompatibility(ctx *svc.ServiceContext) error {
|
||||
tablePatches := []schemaTablePatch{
|
||||
{
|
||||
table: "log_message",
|
||||
ddl: `CREATE TABLE IF NOT EXISTS log_message (
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
platform VARCHAR(32) NOT NULL,
|
||||
app_version VARCHAR(32) NULL,
|
||||
os_name VARCHAR(32) NULL,
|
||||
os_version VARCHAR(32) NULL,
|
||||
device_id VARCHAR(64) NULL,
|
||||
user_id BIGINT NULL DEFAULT NULL,
|
||||
session_id VARCHAR(64) NULL,
|
||||
level TINYINT UNSIGNED NOT NULL DEFAULT 3,
|
||||
error_code VARCHAR(64) NULL,
|
||||
message TEXT NOT NULL,
|
||||
stack MEDIUMTEXT NULL,
|
||||
context JSON NULL,
|
||||
client_ip VARCHAR(45) NULL,
|
||||
user_agent VARCHAR(255) NULL,
|
||||
locale VARCHAR(16) NULL,
|
||||
digest VARCHAR(64) NULL,
|
||||
occurred_at DATETIME NULL,
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uniq_digest (digest),
|
||||
KEY idx_platform_time (platform, created_at),
|
||||
KEY idx_user_time (user_id, created_at),
|
||||
KEY idx_device_time (device_id, created_at),
|
||||
KEY idx_error_code (error_code)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;`,
|
||||
},
|
||||
{
|
||||
table: "user_family",
|
||||
ddl: `CREATE TABLE IF NOT EXISTS user_family (
|
||||
id BIGINT NOT NULL AUTO_INCREMENT,
|
||||
owner_user_id BIGINT NOT NULL COMMENT 'Owner User ID',
|
||||
max_members INT NOT NULL DEFAULT 5 COMMENT 'Max members in family',
|
||||
status TINYINT NOT NULL DEFAULT 1 COMMENT 'Status: 1=active, 0=disabled',
|
||||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
||||
deleted_at DATETIME(3) DEFAULT NULL,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uniq_owner_user_id (owner_user_id),
|
||||
KEY idx_status (status),
|
||||
KEY idx_deleted_at (deleted_at)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;`,
|
||||
},
|
||||
{
|
||||
table: "user_family_member",
|
||||
ddl: `CREATE TABLE IF NOT EXISTS user_family_member (
|
||||
id BIGINT NOT NULL AUTO_INCREMENT,
|
||||
family_id BIGINT NOT NULL COMMENT 'Family ID',
|
||||
user_id BIGINT NOT NULL COMMENT 'Member User ID',
|
||||
role TINYINT NOT NULL DEFAULT 2 COMMENT 'Role: 1=owner, 2=member',
|
||||
status TINYINT NOT NULL DEFAULT 1 COMMENT 'Status: 1=active, 2=left, 3=removed',
|
||||
join_source VARCHAR(32) NOT NULL DEFAULT '' COMMENT 'Join source',
|
||||
joined_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
left_at DATETIME(3) DEFAULT NULL,
|
||||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
||||
deleted_at DATETIME(3) DEFAULT NULL,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uniq_user_id (user_id),
|
||||
KEY idx_family_status (family_id, status),
|
||||
KEY idx_deleted_at (deleted_at)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;`,
|
||||
},
|
||||
}
|
||||
|
||||
columnPatches := []schemaColumnPatch{
|
||||
{
|
||||
table: "user",
|
||||
column: "rules",
|
||||
ddl: "ALTER TABLE `user` ADD COLUMN `rules` TEXT NULL COMMENT 'User rules for subscription';",
|
||||
},
|
||||
{
|
||||
table: "user",
|
||||
column: "last_login_time",
|
||||
ddl: "ALTER TABLE `user` ADD COLUMN `last_login_time` DATETIME DEFAULT NULL COMMENT 'Last Login Time';",
|
||||
},
|
||||
{
|
||||
table: "user",
|
||||
column: "member_status",
|
||||
ddl: "ALTER TABLE `user` ADD COLUMN `member_status` VARCHAR(20) NOT NULL DEFAULT '' COMMENT 'Member Status';",
|
||||
},
|
||||
{
|
||||
table: "user",
|
||||
column: "remark",
|
||||
ddl: "ALTER TABLE `user` ADD COLUMN `remark` VARCHAR(255) NOT NULL DEFAULT '' COMMENT 'Remark';",
|
||||
},
|
||||
{
|
||||
table: "user_subscribe",
|
||||
column: "note",
|
||||
ddl: "ALTER TABLE `user_subscribe` ADD COLUMN `note` VARCHAR(500) NOT NULL DEFAULT '' COMMENT 'User note for subscription';",
|
||||
},
|
||||
{
|
||||
table: "redemption_code",
|
||||
column: "status",
|
||||
ddl: "ALTER TABLE `redemption_code` ADD COLUMN `status` TINYINT NOT NULL DEFAULT 1 COMMENT 'Status: 1=enabled, 0=disabled';",
|
||||
},
|
||||
}
|
||||
|
||||
for _, patch := range tablePatches {
|
||||
exists, err := tableExists(ctx.DB, patch.table)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "check table %s failed", patch.table)
|
||||
}
|
||||
if exists {
|
||||
continue
|
||||
}
|
||||
if err = ctx.DB.Exec(patch.ddl).Error; err != nil {
|
||||
return errors.Wrapf(err, "create table %s failed", patch.table)
|
||||
}
|
||||
logger.Infof("[SchemaCompat] created missing table: %s", patch.table)
|
||||
}
|
||||
|
||||
for _, patch := range columnPatches {
|
||||
exists, err := columnExists(ctx.DB, patch.table, patch.column)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "check column %s.%s failed", patch.table, patch.column)
|
||||
}
|
||||
if exists {
|
||||
continue
|
||||
}
|
||||
if err = ctx.DB.Exec(patch.ddl).Error; err != nil {
|
||||
return errors.Wrapf(err, "add column %s.%s failed", patch.table, patch.column)
|
||||
}
|
||||
logger.Infof("[SchemaCompat] added missing column: %s.%s", patch.table, patch.column)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func tableExists(db *gorm.DB, table string) (bool, error) {
|
||||
var count int64
|
||||
err := db.Raw("SELECT COUNT(*) FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ?", table).Scan(&count).Error
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return count > 0, nil
|
||||
}
|
||||
|
||||
func columnExists(db *gorm.DB, table, column string) (bool, error) {
|
||||
var count int64
|
||||
err := db.Raw(
|
||||
"SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ? AND COLUMN_NAME = ?",
|
||||
table,
|
||||
column,
|
||||
).Scan(&count).Error
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return count > 0, nil
|
||||
}
|
||||
|
||||
func _schemaCompatDebug(table, column string) string {
|
||||
if column == "" {
|
||||
return table
|
||||
}
|
||||
return fmt.Sprintf("%s.%s", table, column)
|
||||
}
|
||||
+38
-19
@@ -7,7 +7,6 @@ import (
|
||||
"github.com/perfect-panel/server/pkg/logger"
|
||||
|
||||
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||
"github.com/perfect-panel/server/internal/config"
|
||||
"github.com/perfect-panel/server/internal/logic/telegram"
|
||||
"github.com/perfect-panel/server/internal/model/auth"
|
||||
"github.com/perfect-panel/server/internal/svc"
|
||||
@@ -15,33 +14,49 @@ import (
|
||||
)
|
||||
|
||||
func Telegram(svc *svc.ServiceContext) {
|
||||
if !svc.Config.Telegram.Enable {
|
||||
logger.Info("Telegram disabled, skipping initialization")
|
||||
return
|
||||
}
|
||||
|
||||
// Prefer BotToken from DB auth method, fallback to config file
|
||||
var usedToken string
|
||||
var webHookDomain string
|
||||
method, err := svc.AuthModel.FindOneByMethod(context.Background(), "telegram")
|
||||
if err != nil {
|
||||
logger.Errorf("[Init Telegram Config] Get Telegram Config Error: %s", err.Error())
|
||||
return
|
||||
if err == nil {
|
||||
tgConfig := new(auth.TelegramAuthConfig)
|
||||
if err = tgConfig.Unmarshal(method.Config); err == nil {
|
||||
usedToken = tgConfig.BotToken
|
||||
webHookDomain = tgConfig.WebHookDomain
|
||||
} else {
|
||||
logger.Errorf("[Init Telegram Config] Unmarshal Telegram Config Error: %s", err.Error())
|
||||
}
|
||||
} else {
|
||||
logger.Debugf("[Init Telegram Config] No Telegram method in DB, fallback to file config: %s", err.Error())
|
||||
}
|
||||
var tg config.Telegram
|
||||
|
||||
tgConfig := new(auth.TelegramAuthConfig)
|
||||
if err = tgConfig.Unmarshal(method.Config); err != nil {
|
||||
logger.Errorf("[Init Telegram Config] Unmarshal Telegram Config Error: %s", err.Error())
|
||||
return
|
||||
if usedToken == "" {
|
||||
usedToken = svc.Config.Telegram.BotToken
|
||||
}
|
||||
|
||||
if tgConfig.BotToken == "" {
|
||||
if webHookDomain == "" {
|
||||
webHookDomain = svc.Config.Telegram.WebHookDomain
|
||||
}
|
||||
if usedToken == "" {
|
||||
logger.Debug("[Init Telegram Config] Telegram Token is empty")
|
||||
return
|
||||
}
|
||||
|
||||
bot, err := tgbotapi.NewBotAPI(tg.BotToken)
|
||||
bot, err := tgbotapi.NewBotAPI(usedToken)
|
||||
if err != nil {
|
||||
logger.Error("[Init Telegram Config] New Bot API Error: ", logger.Field("error", err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
if tgConfig.WebHookDomain == "" || svc.Config.Debug {
|
||||
// set Long Polling mode
|
||||
if webHookDomain == "" || svc.Config.Debug {
|
||||
// Ensure webhook is removed to avoid long polling conflict
|
||||
if _, derr := bot.MakeRequest("deleteWebhook", tgbotapi.Params{}); derr != nil {
|
||||
logger.Errorf("[Init Telegram Config] Delete webhook failed: %s", derr.Error())
|
||||
}
|
||||
// Long Polling mode
|
||||
updateConfig := tgbotapi.NewUpdate(0)
|
||||
updateConfig.Timeout = 60
|
||||
updates := bot.GetUpdatesChan(updateConfig)
|
||||
@@ -55,7 +70,7 @@ func Telegram(svc *svc.ServiceContext) {
|
||||
}
|
||||
}()
|
||||
} else {
|
||||
wh, err := tgbotapi.NewWebhook(fmt.Sprintf("%s/v1/telegram/webhook?secret=%s", tgConfig.WebHookDomain, tool.Md5Encode(tgConfig.BotToken, false)))
|
||||
wh, err := tgbotapi.NewWebhook(fmt.Sprintf("%s/v1/telegram/webhook?secret=%s", webHookDomain, tool.Md5Encode(usedToken, false)))
|
||||
if err != nil {
|
||||
logger.Errorf("[Init Telegram Config] New Webhook Error: %s", err.Error())
|
||||
return
|
||||
@@ -74,9 +89,13 @@ func Telegram(svc *svc.ServiceContext) {
|
||||
}
|
||||
svc.Config.Telegram.BotID = user.ID
|
||||
svc.Config.Telegram.BotName = user.UserName
|
||||
svc.Config.Telegram.EnableNotify = tg.EnableNotify
|
||||
svc.Config.Telegram.WebHookDomain = tg.WebHookDomain
|
||||
svc.Config.Telegram.BotToken = usedToken
|
||||
svc.Config.Telegram.WebHookDomain = webHookDomain
|
||||
svc.TelegramBot = bot
|
||||
|
||||
logger.Info("[Init Telegram Config] Webhook set success")
|
||||
if webHookDomain == "" || svc.Config.Debug {
|
||||
logger.Info("[Init Telegram Config] Long polling mode initialized")
|
||||
} else {
|
||||
logger.Info("[Init Telegram Config] Webhook set success")
|
||||
}
|
||||
}
|
||||
|
||||
+30
-4
@@ -4,6 +4,7 @@ import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
gomigrate "github.com/golang-migrate/migrate/v4"
|
||||
"github.com/perfect-panel/server/internal/model/user"
|
||||
"gorm.io/gorm"
|
||||
|
||||
@@ -18,16 +19,41 @@ func Migrate(ctx *svc.ServiceContext) {
|
||||
Config: ctx.Config.MySQL,
|
||||
}
|
||||
now := time.Now()
|
||||
if err := migrate.Migrate(mc.Dsn()).Up(); err != nil {
|
||||
migrator := migrate.Migrate(mc.Dsn())
|
||||
if err := migrator.Up(); err != nil {
|
||||
if errors.Is(err, migrate.NoChange) {
|
||||
logger.Info("[Migrate] database not change")
|
||||
return
|
||||
} else {
|
||||
var dirtyErr gomigrate.ErrDirty
|
||||
if errors.As(err, &dirtyErr) {
|
||||
logger.Errorf("[Migrate] Dirty database version %d detected, trying auto-recovery", dirtyErr.Version)
|
||||
forceVersion := int(dirtyErr.Version) - 1
|
||||
if forceVersion < 0 {
|
||||
forceVersion = 0
|
||||
}
|
||||
if forceErr := migrator.Force(forceVersion); forceErr != nil {
|
||||
logger.Errorf("[Migrate] Force version error: %v", forceErr.Error())
|
||||
panic(forceErr)
|
||||
}
|
||||
logger.Infof("[Migrate] Force version to %d, retrying migration", forceVersion)
|
||||
if retryErr := migrator.Up(); retryErr != nil && !errors.Is(retryErr, migrate.NoChange) {
|
||||
logger.Errorf("[Migrate] Retry Up error: %v", retryErr.Error())
|
||||
panic(retryErr)
|
||||
}
|
||||
} else {
|
||||
logger.Errorf("[Migrate] Up error: %v", err.Error())
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
logger.Errorf("[Migrate] Up error: %v", err.Error())
|
||||
panic(err)
|
||||
} else {
|
||||
logger.Info("[Migrate] Database change, took " + time.Since(now).String())
|
||||
}
|
||||
|
||||
if err := EnsureSchemaCompatibility(ctx); err != nil {
|
||||
logger.Errorf("[SchemaCompat] repair failed: %v", err.Error())
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// if not found admin user
|
||||
err := ctx.DB.Transaction(func(tx *gorm.DB) error {
|
||||
var count int64
|
||||
|
||||
Reference in New Issue
Block a user