mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2026-10-12 16:00:33 +00:00
Replace database rule synchronization with native rule operations and file-based backup, import, and recovery. Update firewall management views, descriptions, whitelist handling, and translations. Include terminal reconnect fixes and xpack build tags for Darwin build targets. Remove temporary regression test files before committing.
1877 lines
54 KiB
Go
1877 lines
54 KiB
Go
package migrations
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"os/user"
|
|
"path"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
|
"github.com/1Panel-dev/1Panel/agent/app/dto/request"
|
|
"github.com/1Panel-dev/1Panel/agent/app/model"
|
|
providercatalog "github.com/1Panel-dev/1Panel/agent/app/provider"
|
|
"github.com/1Panel-dev/1Panel/agent/app/service"
|
|
"github.com/1Panel-dev/1Panel/agent/constant"
|
|
"github.com/1Panel-dev/1Panel/agent/global"
|
|
migrationutils "github.com/1Panel-dev/1Panel/agent/init/migration/migrations/utils"
|
|
alertwebhook "github.com/1Panel-dev/1Panel/agent/utils/alert_webhook"
|
|
"github.com/1Panel-dev/1Panel/agent/utils/common"
|
|
"github.com/1Panel-dev/1Panel/agent/utils/copier"
|
|
"github.com/1Panel-dev/1Panel/agent/utils/encrypt"
|
|
"github.com/1Panel-dev/1Panel/agent/utils/firewall"
|
|
"github.com/1Panel-dev/1Panel/agent/utils/ssh"
|
|
"github.com/1Panel-dev/1Panel/agent/utils/xpack"
|
|
|
|
"github.com/go-gormigrate/gormigrate/v2"
|
|
"github.com/google/uuid"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
var AddTable = &gormigrate.Migration{
|
|
ID: "20250930-add-table",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
return tx.AutoMigrate(
|
|
&model.AppDetail{},
|
|
&model.AppInstallResource{},
|
|
&model.AppInstall{},
|
|
&model.AppTag{},
|
|
&model.Tag{},
|
|
&model.App{},
|
|
&model.AppLauncher{},
|
|
&model.OllamaModel{},
|
|
&model.BackupAccount{},
|
|
&model.BackupRecord{},
|
|
&model.Clam{},
|
|
&model.ComposeTemplate{},
|
|
&model.Compose{},
|
|
&model.Cronjob{},
|
|
&model.Database{},
|
|
&model.DatabaseMysql{},
|
|
&model.DatabaseUser{},
|
|
&model.DatabaseUserGrant{},
|
|
&model.DatabaseMongodb{},
|
|
&model.DatabasePostgresql{},
|
|
&model.Favorite{},
|
|
&model.FileShare{},
|
|
&model.Host{},
|
|
&model.Ftp{},
|
|
&model.ImageRepo{},
|
|
&model.ScriptLibrary{},
|
|
&model.JobRecords{},
|
|
&model.MonitorBase{},
|
|
&model.MonitorIO{},
|
|
&model.MonitorNetwork{},
|
|
&model.PHPExtensions{},
|
|
&model.Runtime{},
|
|
&model.Setting{},
|
|
&model.Snapshot{},
|
|
&model.Tag{},
|
|
&model.Website{},
|
|
&model.WebsiteAcmeAccount{},
|
|
&model.WebsiteCA{},
|
|
&model.WebsiteTemplate{},
|
|
&model.WebsiteTemplateOutput{},
|
|
&model.WebsiteDnsAccount{},
|
|
&model.WebsiteDomain{},
|
|
&model.WebsiteSSL{},
|
|
&model.Group{},
|
|
&model.AppIgnoreUpgrade{},
|
|
&model.McpServer{},
|
|
&model.RootCert{},
|
|
&model.ClamRecord{},
|
|
)
|
|
},
|
|
}
|
|
|
|
var AddMonitorTable = &gormigrate.Migration{
|
|
ID: "20240813-add-monitor-table",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
return global.MonitorDB.AutoMigrate(
|
|
&model.MonitorBase{},
|
|
&model.MonitorIO{},
|
|
&model.MonitorNetwork{},
|
|
)
|
|
},
|
|
}
|
|
|
|
var InitSetting = &gormigrate.Migration{
|
|
ID: "20240722-init-setting",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
global.CONF.Base.EncryptKey = common.RandStr(16)
|
|
nodeInfo, err := xpack.MultiNodeProvider.LoadNodeInfo(true)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := tx.Create(&model.Setting{Key: "BaseDir", Value: nodeInfo.BaseDir}).Error; err != nil {
|
|
return err
|
|
}
|
|
itemKey, _ := encrypt.StringEncrypt(nodeInfo.ServerKey)
|
|
if err := tx.Create(&model.Setting{Key: "ServerKey", Value: itemKey}).Error; err != nil {
|
|
return err
|
|
}
|
|
itemCrt, _ := encrypt.StringEncrypt(nodeInfo.ServerCrt)
|
|
if err := tx.Create(&model.Setting{Key: "ServerCrt", Value: itemCrt}).Error; err != nil {
|
|
return err
|
|
}
|
|
if err := tx.Create(&model.Setting{Key: "NodeScope", Value: nodeInfo.Scope}).Error; err != nil {
|
|
return err
|
|
}
|
|
if err := tx.Create(&model.Setting{Key: "NodePort", Value: fmt.Sprintf("%v", nodeInfo.NodePort)}).Error; err != nil {
|
|
return err
|
|
}
|
|
if err := tx.Create(&model.Setting{Key: "SystemVersion", Value: nodeInfo.Version}).Error; err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := tx.Create(&model.Setting{Key: "EncryptKey", Value: global.CONF.Base.EncryptKey}).Error; err != nil {
|
|
return err
|
|
}
|
|
if err := tx.Create(&model.Setting{Key: "DockerSockPath", Value: "unix:///var/run/docker.sock"}).Error; err != nil {
|
|
return err
|
|
}
|
|
if err := tx.Create(&model.Setting{Key: "SystemStatus", Value: "Free"}).Error; err != nil {
|
|
return err
|
|
}
|
|
lang := common.LoadParamsWithoutPanic("LANGUAGE")
|
|
if err := tx.Create(&model.Setting{Key: "Language", Value: lang}).Error; err != nil {
|
|
return err
|
|
}
|
|
if err := tx.Create(&model.Setting{Key: "SystemIP", Value: ""}).Error; err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := tx.Create(&model.Setting{Key: "LocalTime", Value: ""}).Error; err != nil {
|
|
return err
|
|
}
|
|
if err := tx.Create(&model.Setting{Key: "TimeZone", Value: common.LoadTimeZoneByCmd()}).Error; err != nil {
|
|
return err
|
|
}
|
|
if err := tx.Create(&model.Setting{Key: "NtpSite", Value: "pool.ntp.org"}).Error; err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := tx.Create(&model.Setting{Key: "LastCleanTime", Value: ""}).Error; err != nil {
|
|
return err
|
|
}
|
|
if err := tx.Create(&model.Setting{Key: "LastCleanSize", Value: ""}).Error; err != nil {
|
|
return err
|
|
}
|
|
if err := tx.Create(&model.Setting{Key: "LastCleanData", Value: ""}).Error; err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := tx.Create(&model.Setting{Key: "DefaultNetwork", Value: "all"}).Error; err != nil {
|
|
return err
|
|
}
|
|
if err := tx.Create(&model.Setting{Key: "MonitorStatus", Value: constant.StatusEnable}).Error; err != nil {
|
|
return err
|
|
}
|
|
if err := tx.Create(&model.Setting{Key: "MonitorStoreDays", Value: "7"}).Error; err != nil {
|
|
return err
|
|
}
|
|
if err := tx.Create(&model.Setting{Key: "MonitorInterval", Value: "5"}).Error; err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := tx.Create(&model.Setting{Key: "ProxyType", Value: ""}).Error; err != nil {
|
|
return err
|
|
}
|
|
if err := tx.Create(&model.Setting{Key: "ProxyUrl", Value: ""}).Error; err != nil {
|
|
return err
|
|
}
|
|
if err := tx.Create(&model.Setting{Key: "ProxyPort", Value: ""}).Error; err != nil {
|
|
return err
|
|
}
|
|
if err := tx.Create(&model.Setting{Key: "ProxyUser", Value: ""}).Error; err != nil {
|
|
return err
|
|
}
|
|
if err := tx.Create(&model.Setting{Key: "ProxyPasswd", Value: ""}).Error; err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := tx.Create(&model.Setting{Key: "AppStoreVersion", Value: ""}).Error; err != nil {
|
|
return err
|
|
}
|
|
if err := tx.Create(&model.Setting{Key: "AppStoreSyncStatus", Value: "SyncSuccess"}).Error; err != nil {
|
|
return err
|
|
}
|
|
if err := tx.Create(&model.Setting{Key: "AppStoreLastModified", Value: "0"}).Error; err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := tx.Create(&model.Setting{Key: "FileRecycleBin", Value: constant.StatusEnable}).Error; err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := tx.Create(&model.Setting{Key: "LocalSSHConn", Value: ""}).Error; err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
},
|
|
}
|
|
|
|
var InitImageRepo = &gormigrate.Migration{
|
|
ID: "20240722-init-imagerepo",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
item := &model.ImageRepo{
|
|
Name: "Docker Hub",
|
|
Protocol: "https",
|
|
DownloadUrl: "docker.io",
|
|
Status: constant.StatusSuccess,
|
|
}
|
|
if err := tx.Create(item).Error; err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
},
|
|
}
|
|
|
|
var InitDefaultCA = &gormigrate.Migration{
|
|
ID: "20240722-init-default-ca",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
caService := service.NewIWebsiteCAService()
|
|
if _, err := caService.Create(request.WebsiteCACreate{
|
|
CommonName: "1Panel-CA",
|
|
Country: "CN",
|
|
KeyType: "EC256",
|
|
Name: "1Panel",
|
|
Organization: "FIT2CLOUD",
|
|
OrganizationUint: "1Panel",
|
|
Province: "Beijing",
|
|
City: "Beijing",
|
|
}); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
},
|
|
}
|
|
|
|
var InitPHPExtensions = &gormigrate.Migration{
|
|
ID: "20240722-add-php-extensions",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
phpExtensions := []model.PHPExtensions{
|
|
{
|
|
Name: "Default",
|
|
Extensions: "bcmath,ftp,gd,gettext,intl,mysqli,pcntl,pdo_mysql,shmop,soap,sockets,sysvsem,xmlrpc,zip",
|
|
},
|
|
{
|
|
Name: "WordPress",
|
|
Extensions: "exif,igbinary,imagick,intl,zip,apcu,memcached,opcache,redis,shmop,mysqli,pdo_mysql,gd",
|
|
},
|
|
{
|
|
Name: "Flarum",
|
|
Extensions: "curl,gd,pdo_mysql,mysqli,bz2,exif,yaf,imap",
|
|
},
|
|
{
|
|
Name: "SeaCMS",
|
|
Extensions: "mysqli,pdo_mysql,gd,curl",
|
|
},
|
|
{
|
|
Name: "Dev",
|
|
Extensions: "bcmath,ftp,gd,gettext,intl,mysqli,pcntl,pdo_mysql,shmop,soap,sockets,sysvsem,xmlrpc,zip,exif,igbinary,imagick,apcu,memcached,opcache,redis,bc,image,dom,iconv,mbstring,mysqlnd,openssl,pdo,tokenizer,xml,curl,bz2,yaf,imap,xdebug,swoole,pdo_pgsql,fileinfo,pgsql,calendar,gmp",
|
|
},
|
|
}
|
|
|
|
for _, ext := range phpExtensions {
|
|
if err := tx.Create(&ext).Error; err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
},
|
|
}
|
|
|
|
var AddTaskTable = &gormigrate.Migration{
|
|
ID: "20241226-add-task",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
return tx.AutoMigrate(
|
|
&model.Task{},
|
|
)
|
|
},
|
|
}
|
|
|
|
var InitBackup = &gormigrate.Migration{
|
|
ID: "20241226-init-backup",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
if err := tx.Create(&model.BackupAccount{
|
|
Name: "localhost",
|
|
Type: "LOCAL",
|
|
BackupPath: path.Join(global.Dir.DataDir, "backup"),
|
|
}).Error; err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
},
|
|
}
|
|
|
|
var InitDefault = &gormigrate.Migration{
|
|
ID: "20250301-init-default",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
if err := tx.Create(&model.Group{Name: "Default", Type: "website", IsDefault: true}).Error; err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
},
|
|
}
|
|
|
|
var UpdateWebsiteExpireDate = &gormigrate.Migration{
|
|
ID: "20250304-update-website",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
targetDate := time.Date(9999, 12, 31, 0, 0, 0, 0, time.UTC)
|
|
|
|
if err := tx.Model(&model.Website{}).
|
|
Where("expire_date = ?", time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC)).
|
|
Update("expire_date", targetDate).Error; err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
},
|
|
}
|
|
|
|
var UpdateRuntime = &gormigrate.Migration{
|
|
ID: "20250624-update-runtime",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
return tx.AutoMigrate(
|
|
&model.Runtime{},
|
|
)
|
|
},
|
|
}
|
|
|
|
var AddSnapshotRule = &gormigrate.Migration{
|
|
ID: "20250703-add-snapshot-rule",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
return tx.AutoMigrate(
|
|
&model.Cronjob{},
|
|
)
|
|
},
|
|
}
|
|
var UpdatePHPRuntime = &gormigrate.Migration{
|
|
ID: "20250702-update-php-runtime",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
service.HandleOldPHPRuntime()
|
|
return nil
|
|
},
|
|
}
|
|
var AddSnapshotIgnore = &gormigrate.Migration{
|
|
ID: "20250716-add-snapshot-ignore",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
return tx.AutoMigrate(
|
|
&model.Snapshot{},
|
|
)
|
|
},
|
|
}
|
|
|
|
var InitAppLauncher = &gormigrate.Migration{
|
|
ID: "20250702-init-app-launcher",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
launchers := []string{"openresty", "mysql", "halo", "redis", "maxkb", "wordpress"}
|
|
for _, val := range launchers {
|
|
var item model.AppLauncher
|
|
_ = tx.Model(&model.AppLauncher{}).Where("key = ?", val).First(&item).Error
|
|
if item.ID == 0 {
|
|
item.Key = val
|
|
_ = tx.Create(&item).Error
|
|
}
|
|
}
|
|
return nil
|
|
},
|
|
}
|
|
|
|
var AddTableAlert = &gormigrate.Migration{
|
|
ID: "20250122-add-table-alert",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
return global.AlertDB.AutoMigrate(&model.Alert{}, &model.AlertTask{}, model.AlertLog{}, model.AlertConfig{})
|
|
},
|
|
}
|
|
|
|
var InitAlertConfig = &gormigrate.Migration{
|
|
ID: "20250705-init-alert-config",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
records := []model.AlertConfig{
|
|
{
|
|
Type: "sms",
|
|
Title: "xpack.alert.smsConfig",
|
|
Status: "Enable",
|
|
Config: `{"alertDailyNum":50}`,
|
|
CreateUser: "system",
|
|
UpdateUser: "system",
|
|
},
|
|
{
|
|
Type: "common",
|
|
Title: "xpack.alert.commonConfig",
|
|
Status: "Enable",
|
|
Config: `{"isOffline":"Disable","alertSendTimeRange":{"noticeAlert":{"sendTimeRange":"08:00:00 - 23:59:59","type":["ssl","siteEndTime","panelPwdEndTime","panelUpdate"]},"resourceAlert":{"sendTimeRange":"00:00:00 - 23:59:59","type":["clams","cronJob","cpu","memory","load","disk"]}}}`,
|
|
CreateUser: "system",
|
|
UpdateUser: "system",
|
|
},
|
|
}
|
|
for _, r := range records {
|
|
if err := global.AlertDB.Model(&model.AlertConfig{}).Create(&r).Error; err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
},
|
|
}
|
|
|
|
var AddMethodToAlertLog = &gormigrate.Migration{
|
|
ID: "20250713-add-method-to-alert_log",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
if err := global.AlertDB.AutoMigrate(&model.AlertLog{}); err != nil {
|
|
return err
|
|
}
|
|
if err := global.AlertDB.Model(&model.AlertLog{}).Where("method IS NULL OR method = ''").Update("method", "sms").Error; err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
},
|
|
}
|
|
|
|
var AddMethodToAlertTask = &gormigrate.Migration{
|
|
ID: "20250723-add-method-to-alert_task",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
if err := global.AlertDB.AutoMigrate(&model.AlertTask{}); err != nil {
|
|
return err
|
|
}
|
|
if err := global.AlertDB.Model(&model.AlertTask{}).Where("method IS NULL OR method = ''").Update("method", "sms").Error; err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
},
|
|
}
|
|
|
|
var UpdateMcpServer = &gormigrate.Migration{
|
|
ID: "20250729-update-mcp-server",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
if err := tx.AutoMigrate(&model.McpServer{}); err != nil {
|
|
return err
|
|
}
|
|
if err := tx.Model(&model.McpServer{}).Where("1=1").Update("output_transport", "sse").Error; err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
},
|
|
}
|
|
|
|
var InitCronjobGroup = &gormigrate.Migration{
|
|
ID: "20250805-init-cronjob-group",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
if err := tx.AutoMigrate(&model.Cronjob{}); err != nil {
|
|
return err
|
|
}
|
|
if err := tx.Model(&model.Cronjob{}).Where("1=1").Updates(map[string]interface{}{"group_id": 0}).Error; err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
},
|
|
}
|
|
|
|
var AddColumnToAlert = &gormigrate.Migration{
|
|
ID: "20250729-add-column-to-alert",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
if err := global.AlertDB.AutoMigrate(&model.Alert{}); err != nil {
|
|
return err
|
|
}
|
|
if err := global.AlertDB.Model(&model.Alert{}).
|
|
Where("advanced_params IS NULL").
|
|
Update("advanced_params", "").Error; err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
},
|
|
}
|
|
|
|
var MigrateAlertMethodConfigIDs = &gormigrate.Migration{
|
|
ID: "20251001-migrate-alert-method-config-ids",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
if err := tx.AutoMigrate(&model.Alert{}, &model.AlertLog{}, &model.AlertTask{}, &model.AlertConfig{}); err != nil {
|
|
return err
|
|
}
|
|
if err := migrateAlertMethodConfigIDs(tx); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
},
|
|
}
|
|
|
|
var MigrateAlertLogTaskMethodConfigIDs = &gormigrate.Migration{
|
|
ID: "20260608-migrate-alert-log-task-method-config-ids",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
if err := tx.AutoMigrate(&model.AlertLog{}, &model.AlertTask{}, &model.AlertConfig{}); err != nil {
|
|
return err
|
|
}
|
|
if err := migrateAlertMethodRecords(tx, &model.AlertLog{}); err != nil {
|
|
return err
|
|
}
|
|
if err := migrateAlertMethodRecords(tx, &model.AlertTask{}); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
},
|
|
}
|
|
|
|
var AddAlertAuditUser = &gormigrate.Migration{
|
|
ID: "20260602-add-alert-audit-user",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
return tx.AutoMigrate(&model.Alert{}, &model.AlertConfig{})
|
|
},
|
|
}
|
|
|
|
var AddAlertConfigUIDAndSecret = &gormigrate.Migration{
|
|
ID: "20260826-add-alert-config-uid-secret",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
return migrateAlertConfigUIDAndSecret(tx)
|
|
},
|
|
}
|
|
|
|
var AddAlertTaskDeliveryLogID = &gormigrate.Migration{
|
|
ID: "20260826-add-alert-task-delivery-log-id",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
return tx.AutoMigrate(&model.AlertTask{})
|
|
},
|
|
}
|
|
|
|
func migrateAlertConfigUIDAndSecret(tx *gorm.DB) error {
|
|
if !tx.Migrator().HasTable(&model.AlertConfig{}) {
|
|
return tx.AutoMigrate(&model.AlertConfig{})
|
|
}
|
|
|
|
if !tx.Migrator().HasColumn(&model.AlertConfig{}, "UID") {
|
|
if err := tx.Exec("ALTER TABLE alert_configs ADD COLUMN uid varchar(64)").Error; err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if !tx.Migrator().HasColumn(&model.AlertConfig{}, "SecretConfig") {
|
|
if err := tx.Exec("ALTER TABLE alert_configs ADD COLUMN secret_config text NOT NULL DEFAULT ''").Error; err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
var configs []struct {
|
|
ID uint
|
|
UID string
|
|
}
|
|
if err := tx.Table("alert_configs").Select("id", "uid").Find(&configs).Error; err != nil {
|
|
return err
|
|
}
|
|
for _, config := range configs {
|
|
if strings.TrimSpace(config.UID) != "" {
|
|
continue
|
|
}
|
|
if err := tx.Table("alert_configs").Where("id = ?", config.ID).Update("uid", uuid.NewString()).Error; err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if err := tx.AutoMigrate(&model.AlertConfig{}); err != nil {
|
|
return err
|
|
}
|
|
|
|
var customConfigs []model.AlertConfig
|
|
if err := tx.Where("type = ?", constant.Custom).Find(&customConfigs).Error; err != nil {
|
|
return err
|
|
}
|
|
for _, config := range customConfigs {
|
|
prepared, status, legacy, err := alertwebhook.NormalizeLegacy(config.Config, config.Status, config.Title)
|
|
if err != nil {
|
|
return fmt.Errorf("normalize legacy custom webhook %d: %w", config.ID, err)
|
|
}
|
|
if !legacy {
|
|
if err := alertwebhook.ValidateStored(config); err != nil && config.Status != constant.AlertDisable {
|
|
if updateErr := tx.Model(&model.AlertConfig{}).Where("id = ?", config.ID).Update("status", constant.AlertDisable).Error; updateErr != nil {
|
|
return updateErr
|
|
}
|
|
}
|
|
continue
|
|
}
|
|
if err := tx.Model(&model.AlertConfig{}).Where("id = ?", config.ID).Updates(map[string]interface{}{
|
|
"config": prepared.Config,
|
|
"secret_config": prepared.SecretConfig,
|
|
"status": status,
|
|
}).Error; err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if tx.Migrator().HasTable(&model.Alert{}) {
|
|
if err := migrateAlertMethodConfigIDs(tx); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if tx.Migrator().HasTable(&model.AlertLog{}) {
|
|
if err := migrateAlertMethodRecords(tx, &model.AlertLog{}); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if tx.Migrator().HasTable(&model.AlertTask{}) {
|
|
if err := migrateAlertMethodRecords(tx, &model.AlertTask{}); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func migrateAlertMethodConfigIDs(tx *gorm.DB) error {
|
|
if err := tx.Model(&model.AlertConfig{}).Where("type = ?", "mail").Update("type", constant.EmailConfig).Error; err != nil {
|
|
return err
|
|
}
|
|
|
|
configIDs, err := loadAlertConfigIDs(tx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var alerts []model.Alert
|
|
if err := tx.Find(&alerts).Error; err != nil {
|
|
return err
|
|
}
|
|
for _, alert := range alerts {
|
|
method := migrateAlertMethodValue(alert.Method, alertLegacyMethodTypeMap(), configIDs)
|
|
if method == alert.Method {
|
|
continue
|
|
}
|
|
if err := tx.Model(&model.Alert{}).Where("id = ?", alert.ID).Update("method", method).Error; err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func migrateAlertMethodRecords(tx *gorm.DB, modelValue interface{}) error {
|
|
configIDs, err := loadAlertConfigIDs(tx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
switch modelValue.(type) {
|
|
case *model.AlertLog:
|
|
var logs []model.AlertLog
|
|
if err := tx.Find(&logs).Error; err != nil {
|
|
return err
|
|
}
|
|
for _, item := range logs {
|
|
method := migrateAlertMethodValue(item.Method, alertLegacyMethodTypeMap(), configIDs)
|
|
if method == item.Method {
|
|
continue
|
|
}
|
|
if err := tx.Model(&model.AlertLog{}).Where("id = ?", item.ID).Update("method", method).Error; err != nil {
|
|
return err
|
|
}
|
|
}
|
|
case *model.AlertTask:
|
|
var tasks []model.AlertTask
|
|
if err := tx.Find(&tasks).Error; err != nil {
|
|
return err
|
|
}
|
|
for _, item := range tasks {
|
|
method := migrateAlertMethodValue(item.Method, alertLegacyMethodTypeMap(), configIDs)
|
|
if method == item.Method {
|
|
continue
|
|
}
|
|
if err := tx.Model(&model.AlertTask{}).Where("id = ?", item.ID).Update("method", method).Error; err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func loadAlertConfigIDs(tx *gorm.DB) (map[string]string, error) {
|
|
configIDs := map[string]string{}
|
|
for _, configType := range alertLegacyMethodTypeMap() {
|
|
if _, ok := configIDs[configType]; ok {
|
|
continue
|
|
}
|
|
var config model.AlertConfig
|
|
if err := tx.Where("type = ?", configType).Order("id ASC").First(&config).Error; err != nil {
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
continue
|
|
}
|
|
return nil, err
|
|
}
|
|
configIDs[configType] = strconv.Itoa(int(config.ID))
|
|
}
|
|
return configIDs, nil
|
|
}
|
|
|
|
func alertLegacyMethodTypeMap() map[string]string {
|
|
return map[string]string{
|
|
"mail": constant.Email,
|
|
constant.Email: constant.Email,
|
|
constant.SMS: constant.SMS,
|
|
constant.Bark: constant.Bark,
|
|
constant.WeChat: constant.WeCom,
|
|
constant.WeCom: constant.WeCom,
|
|
constant.DingTalk: constant.DingTalk,
|
|
constant.FeiShu: constant.FeiShu,
|
|
constant.Custom: constant.Custom,
|
|
}
|
|
}
|
|
|
|
func migrateAlertMethodValue(method string, typeMap map[string]string, configIDs map[string]string) string {
|
|
items := strings.Split(method, ",")
|
|
next := make([]string, 0, len(items))
|
|
seen := map[string]struct{}{}
|
|
for _, item := range items {
|
|
item = strings.TrimSpace(item)
|
|
if item == "" {
|
|
continue
|
|
}
|
|
configType, ok := typeMap[item]
|
|
if ok {
|
|
if id, exists := configIDs[configType]; exists {
|
|
item = id
|
|
}
|
|
}
|
|
if _, exists := seen[item]; exists {
|
|
continue
|
|
}
|
|
seen[item] = struct{}{}
|
|
next = append(next, item)
|
|
}
|
|
return strings.Join(next, ",")
|
|
}
|
|
|
|
var UpdateWebsiteSSL = &gormigrate.Migration{
|
|
ID: "20250819-update-website-ssl",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
if err := tx.AutoMigrate(&model.WebsiteSSL{}); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
},
|
|
}
|
|
|
|
var AddQuickJump = &gormigrate.Migration{
|
|
ID: "20250901-add-quick-jump",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
if err := tx.AutoMigrate(&model.QuickJump{}); err != nil {
|
|
return err
|
|
}
|
|
if err := tx.Create(&model.QuickJump{Name: "Website", Title: "menu.website", Recommend: 10, IsShow: true, Router: "/websites"}).Error; err != nil {
|
|
return err
|
|
}
|
|
if err := tx.Create(&model.QuickJump{Name: "Database", Title: "home.database", Recommend: 30, IsShow: true, Router: "/databases"}).Error; err != nil {
|
|
return err
|
|
}
|
|
if err := tx.Create(&model.QuickJump{Name: "Cronjob", Title: "menu.cronjob", Recommend: 50, IsShow: true, Router: "/cronjobs"}).Error; err != nil {
|
|
return err
|
|
}
|
|
if err := tx.Create(&model.QuickJump{Name: "AppInstalled", Title: "home.appInstalled", Recommend: 70, IsShow: true, Router: "/apps/installed"}).Error; err != nil {
|
|
return err
|
|
}
|
|
if err := tx.Create(&model.QuickJump{Name: "File", Detail: "/", Title: "home.quickDir", Recommend: 90, IsShow: false, Router: "/hosts/files"}).Error; err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
},
|
|
}
|
|
|
|
var UpdateMcpServerAddType = &gormigrate.Migration{
|
|
ID: "20250904-update-mcp-server",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
if err := tx.AutoMigrate(&model.McpServer{}); err != nil {
|
|
return err
|
|
}
|
|
if err := tx.Model(&model.McpServer{}).Where("1=1").Update("type", "npx").Error; err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
},
|
|
}
|
|
|
|
var UpdateMcpServerGatewayConfig = &gormigrate.Migration{
|
|
ID: "20260615-update-mcp-server-gateway-config",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
if err := tx.AutoMigrate(&model.McpServer{}); err != nil {
|
|
return err
|
|
}
|
|
if err := tx.Model(&model.McpServer{}).Where("gateway_image IS NULL OR gateway_image = ''").Where("type = ?", "uvx").Update("gateway_image", "supercorp/supergateway:uvx").Error; err != nil {
|
|
return err
|
|
}
|
|
if err := tx.Model(&model.McpServer{}).Where("gateway_image IS NULL OR gateway_image = ''").Where("type <> ? OR type IS NULL OR type = ''", "uvx").Update("gateway_image", "supercorp/supergateway:3.4.3").Error; err != nil {
|
|
return err
|
|
}
|
|
if err := tx.Model(&model.McpServer{}).Where("protocol_version IS NULL OR protocol_version = ''").Update("protocol_version", "2025-06-18").Error; err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
},
|
|
}
|
|
|
|
var InitLocalSSHConn = &gormigrate.Migration{
|
|
ID: "20250905-init-local-ssh",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
itemPath := ""
|
|
currentInfo, _ := user.Current()
|
|
if len(currentInfo.HomeDir) == 0 {
|
|
itemPath = "/root/.ssh/id_ed25519_1panel"
|
|
} else {
|
|
itemPath = path.Join(currentInfo.HomeDir, ".ssh/id_ed25519_1panel")
|
|
}
|
|
if _, err := os.Stat(itemPath); err != nil {
|
|
_ = service.NewISSHService().CreateRootCert(dto.RootCertOperate{EncryptionMode: "ed25519", Name: "id_ed25519_1panel", Description: "1Panel Terminal"})
|
|
}
|
|
privateKey, _ := os.ReadFile(itemPath)
|
|
connWithKey := ssh.ConnInfo{
|
|
Addr: "127.0.0.1",
|
|
User: "root",
|
|
Port: 22,
|
|
AuthMode: "key",
|
|
PrivateKey: privateKey,
|
|
}
|
|
if _, err := ssh.NewClient(connWithKey); err != nil {
|
|
return nil
|
|
}
|
|
var conn model.LocalConnInfo
|
|
_ = copier.Copy(&conn, &connWithKey)
|
|
conn.PrivateKey = string(privateKey)
|
|
conn.PassPhrase = ""
|
|
localConn, _ := json.Marshal(&conn)
|
|
connAfterEncrypt, _ := encrypt.StringEncrypt(string(localConn))
|
|
if err := tx.Model(&model.Setting{}).Where("key = ?", "LocalSSHConn").Updates(map[string]interface{}{"value": connAfterEncrypt}).Error; err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
},
|
|
}
|
|
|
|
var InitLocalSSHShow = &gormigrate.Migration{
|
|
ID: "20250908-init-local-ssh-show",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
if err := tx.Create(&model.Setting{Key: "LocalSSHConnShow", Value: constant.StatusEnable}).Error; err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
},
|
|
}
|
|
|
|
var InitRecordStatus = &gormigrate.Migration{
|
|
ID: "20250910-init-record-status",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
if err := tx.AutoMigrate(&model.BackupRecord{}); err != nil {
|
|
return err
|
|
}
|
|
if err := tx.Model(&model.BackupRecord{}).Where("1 == 1").Updates(map[string]interface{}{"status": constant.StatusSuccess}).Error; err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
},
|
|
}
|
|
|
|
var AddShowNameForQuickJump = &gormigrate.Migration{
|
|
ID: "20250918-add-show-name-for-quick-jump",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
return tx.AutoMigrate(&model.QuickJump{})
|
|
},
|
|
}
|
|
|
|
var AddAgentQuickJump = &gormigrate.Migration{
|
|
ID: "20260312-add-agent-quick-jump",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
if err := tx.AutoMigrate(&model.QuickJump{}); err != nil {
|
|
return err
|
|
}
|
|
|
|
var quicks []model.QuickJump
|
|
if err := tx.Find(&quicks).Error; err != nil {
|
|
return err
|
|
}
|
|
|
|
var (
|
|
cronjob *model.QuickJump
|
|
database *model.QuickJump
|
|
showList []*model.QuickJump
|
|
)
|
|
for i := range quicks {
|
|
switch quicks[i].Name {
|
|
case "Cronjob":
|
|
cronjob = &quicks[i]
|
|
case "Database":
|
|
database = &quicks[i]
|
|
}
|
|
if quicks[i].IsShow {
|
|
showList = append(showList, &quicks[i])
|
|
}
|
|
}
|
|
|
|
showCount := len(showList)
|
|
updatedIDs := make(map[uint]struct{})
|
|
kickedCronjob := false
|
|
if showCount >= 4 && cronjob != nil && cronjob.IsShow {
|
|
cronjob.IsShow = false
|
|
updatedIDs[cronjob.ID] = struct{}{}
|
|
kickedCronjob = true
|
|
}
|
|
if !kickedCronjob && showCount >= 4 && database != nil && database.IsShow {
|
|
database.IsShow = false
|
|
updatedIDs[database.ID] = struct{}{}
|
|
}
|
|
|
|
for _, item := range quicks {
|
|
if _, ok := updatedIDs[item.ID]; !ok {
|
|
continue
|
|
}
|
|
if err := tx.Model(&model.QuickJump{}).Where("id = ?", item.ID).Update("is_show", item.IsShow).Error; err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return tx.Create(&model.QuickJump{
|
|
Name: "Agent",
|
|
Title: "aiTools.agents.agent",
|
|
Recommend: 1,
|
|
IsShow: true,
|
|
Router: "/ai/agents/agent",
|
|
}).Error
|
|
},
|
|
}
|
|
|
|
var AddTimeoutForClam = &gormigrate.Migration{
|
|
ID: "20250922-add-timeout-for-clam",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
if err := tx.AutoMigrate(&model.Clam{}); err != nil {
|
|
return err
|
|
}
|
|
if err := tx.Model(&model.Clam{}).Where("1 == 1").Updates(map[string]interface{}{"timeout": 18000}).Error; err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
},
|
|
}
|
|
|
|
var UpdateCronjobSpec = &gormigrate.Migration{
|
|
ID: "20250925-update-cronjob-spec",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
var cronjobs []model.Cronjob
|
|
if err := tx.Where("1 == 1").Find(&cronjobs).Error; err != nil {
|
|
return err
|
|
}
|
|
for _, item := range cronjobs {
|
|
if !strings.Contains(item.Spec, ",") {
|
|
continue
|
|
}
|
|
if err := tx.Model(&model.Cronjob{}).Where("id = ?", item.ID).Updates(
|
|
map[string]interface{}{"spec": strings.ReplaceAll(item.Spec, ",", "&&")}).Error; err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
},
|
|
}
|
|
|
|
var UpdateWebsiteSSLAddColumn = &gormigrate.Migration{
|
|
ID: "20250928-update-website-ssl",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
if err := tx.AutoMigrate(&model.WebsiteSSL{}); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
},
|
|
}
|
|
|
|
var AddTensorRTLLMModel = &gormigrate.Migration{
|
|
ID: "20251018-add-tensorrt-llm-model",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
return tx.AutoMigrate(&model.TensorRTLLM{})
|
|
},
|
|
}
|
|
|
|
var UpdateMonitorInterval = &gormigrate.Migration{
|
|
ID: "20251026-update-monitor-interval",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
var monitorInterval model.Setting
|
|
if err := tx.Where("key = ?", "MonitorInterval").First(&monitorInterval).Error; err != nil {
|
|
return err
|
|
}
|
|
interval, _ := strconv.Atoi(monitorInterval.Value)
|
|
if interval == 0 {
|
|
interval = 300
|
|
}
|
|
if err := tx.Model(&model.Setting{}).
|
|
Where("key = ?", "MonitorInterval").
|
|
Updates(map[string]interface{}{"value": fmt.Sprintf("%v", interval*60)}).
|
|
Error; err != nil {
|
|
return err
|
|
}
|
|
if err := tx.Create(&model.Setting{Key: "DefaultIO", Value: "all"}).Error; err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
},
|
|
}
|
|
|
|
var AddMonitorProcess = &gormigrate.Migration{
|
|
ID: "20251030-add-monitor-process",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
return global.MonitorDB.AutoMigrate(&model.MonitorBase{})
|
|
},
|
|
}
|
|
|
|
var UpdateCronJob = &gormigrate.Migration{
|
|
ID: "20251105-update-cronjob",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
return tx.AutoMigrate(&model.Cronjob{})
|
|
},
|
|
}
|
|
|
|
var UpdateTensorrtLLM = &gormigrate.Migration{
|
|
ID: "20251110-update-tensorrt-llm",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
return tx.AutoMigrate(&model.TensorRTLLM{})
|
|
},
|
|
}
|
|
|
|
var AddCommonDescription = &gormigrate.Migration{
|
|
ID: "20251117-add-common-description",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
return tx.AutoMigrate(&model.CommonDescription{})
|
|
},
|
|
}
|
|
|
|
var UpdateDatabase = &gormigrate.Migration{
|
|
ID: "20251117-update-database",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
return tx.AutoMigrate(&model.Database{})
|
|
},
|
|
}
|
|
|
|
var AddGPUMonitor = &gormigrate.Migration{
|
|
ID: "20251122-add-gpu-monitor",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
return global.GPUMonitorDB.AutoMigrate(&model.MonitorGPU{})
|
|
},
|
|
}
|
|
|
|
var UpdateDatabaseMysql = &gormigrate.Migration{
|
|
ID: "20251126-update-database-mysql",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
if err := tx.AutoMigrate(&model.DatabaseMysql{}); err != nil {
|
|
return err
|
|
}
|
|
var data []model.DatabaseMysql
|
|
_ = tx.Where("1 = 1").Find(&data).Error
|
|
for _, item := range data {
|
|
if len(item.Collation) == 0 {
|
|
collation := ""
|
|
switch item.Format {
|
|
case "utf8":
|
|
collation = "utf8_general_ci"
|
|
case "utf8mb4":
|
|
collation = "utf8mb4_general_ci"
|
|
case "gbk":
|
|
collation = "gbk_chinese_ci"
|
|
case "big5":
|
|
collation = "big5_chinese_ci"
|
|
default:
|
|
collation = "utf8mb4_general_ci"
|
|
}
|
|
_ = tx.Model(&model.DatabaseMysql{}).Where("id = ?", item.ID).Updates(map[string]interface{}{"collation": collation}).Error
|
|
}
|
|
}
|
|
return nil
|
|
},
|
|
}
|
|
|
|
var AddDatabaseMongodb = &gormigrate.Migration{
|
|
ID: "20260413-add-database-mongodb",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
return tx.AutoMigrate(&model.DatabaseMongodb{})
|
|
},
|
|
}
|
|
|
|
var InitIptablesStatus = &gormigrate.Migration{
|
|
ID: "20251201-init-iptables-status",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
if err := tx.Create(&model.Setting{Key: "IptablesStatus", Value: constant.StatusDisable}).Error; err != nil {
|
|
return err
|
|
}
|
|
if err := tx.Create(&model.Setting{Key: "IptablesForwardStatus", Value: constant.StatusDisable}).Error; err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
},
|
|
}
|
|
|
|
var InitFirewallPortWhiteList = &gormigrate.Migration{
|
|
ID: "20260601-init-firewall-port-whitelist",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
var setting model.Setting
|
|
if err := tx.Where("key = ?", constant.FirewallPortWhiteList).First(&setting).Error; err != nil {
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return tx.Create(&model.Setting{Key: constant.FirewallPortWhiteList, Value: constant.FirewallPortWhiteListValue}).Error
|
|
}
|
|
return err
|
|
}
|
|
return nil
|
|
},
|
|
}
|
|
|
|
var UpdateWebsite = &gormigrate.Migration{
|
|
ID: "20251203-update-website",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
return tx.AutoMigrate(&model.Website{})
|
|
},
|
|
}
|
|
|
|
var AddisIPtoWebsiteSSL = &gormigrate.Migration{
|
|
ID: "20251223-update-website-ssl",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
return tx.AutoMigrate(&model.WebsiteSSL{})
|
|
},
|
|
}
|
|
|
|
var InitPingStatus = &gormigrate.Migration{
|
|
ID: "20251201-init-ping-status",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
status := firewall.LoadPingStatus()
|
|
if err := tx.Create(&model.Setting{Key: "BanPing", Value: status}).Error; err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
},
|
|
}
|
|
|
|
var UpdateApp = &gormigrate.Migration{
|
|
ID: "20251228-update-app",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
return tx.AutoMigrate(&model.App{})
|
|
},
|
|
}
|
|
|
|
var AddCronjobArgs = &gormigrate.Migration{
|
|
ID: "20260106-add-cronjob-args",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
return tx.AutoMigrate(&model.Cronjob{})
|
|
},
|
|
}
|
|
|
|
var AddWebsiteAcmeAccountColumn = &gormigrate.Migration{
|
|
ID: "20260110-add-website-acme-account",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
return tx.AutoMigrate(&model.WebsiteAcmeAccount{})
|
|
},
|
|
}
|
|
|
|
var AddAgentTables = &gormigrate.Migration{
|
|
ID: "20260205-add-agent-tables",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
return tx.AutoMigrate(
|
|
&model.Agent{},
|
|
&model.AgentAccount{},
|
|
)
|
|
},
|
|
}
|
|
|
|
var AddAgentCustomModelFields = &gormigrate.Migration{
|
|
ID: "20260224-add-agent-custom-model-fields",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
if err := tx.AutoMigrate(&model.Agent{}, &model.AgentAccount{}); err != nil {
|
|
return err
|
|
}
|
|
if err := tx.Model(&model.AgentAccount{}).Where("api_type = '' OR api_type IS NULL").Update("api_type", "openai-completions").Error; err != nil {
|
|
return err
|
|
}
|
|
if err := tx.Model(&model.Agent{}).Where("api_type = '' OR api_type IS NULL").Update("api_type", "openai-completions").Error; err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
},
|
|
}
|
|
|
|
var AddAppInstallSortOrder = &gormigrate.Migration{
|
|
ID: "20260222-add-app-install-sort-order",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
return tx.AutoMigrate(&model.AppInstall{})
|
|
},
|
|
}
|
|
|
|
var AddAgentAccountRememberAPIKey = &gormigrate.Migration{
|
|
ID: "20260225-add-agent-account-remember-api-key",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
if err := tx.AutoMigrate(&model.AgentAccount{}); err != nil {
|
|
return err
|
|
}
|
|
return tx.Model(&model.AgentAccount{}).Where("remember_api_key IS NULL").Update("remember_api_key", true).Error
|
|
},
|
|
}
|
|
|
|
var AddEditionSetting = &gormigrate.Migration{
|
|
ID: "20260224-add-edition-setting",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
var setting model.Setting
|
|
edition := common.LoadParamsWithoutPanic("PANEL_EDITION")
|
|
if err := tx.Where("key = ?", "Edition").First(&setting).Error; err != nil {
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return tx.Create(&model.Setting{Key: "Edition", Value: edition}).Error
|
|
}
|
|
return err
|
|
}
|
|
if setting.Value == "" {
|
|
return tx.Model(&model.Setting{}).Where("key = ?", "Edition").Update("value", edition).Error
|
|
}
|
|
return nil
|
|
},
|
|
}
|
|
|
|
var AddAgentTypeForAgents = &gormigrate.Migration{
|
|
ID: "20260302-add-agent-type-for-agents",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
if err := tx.AutoMigrate(&model.Agent{}); err != nil {
|
|
return err
|
|
}
|
|
if err := tx.Model(&model.Agent{}).Where("agent_type = '' OR agent_type IS NULL").Update("agent_type", constant.AppOpenclaw).Error; err != nil {
|
|
return err
|
|
}
|
|
return tx.Exec(
|
|
"UPDATE agents SET agent_type = ? WHERE app_install_id IN (SELECT ai.id FROM app_installs ai JOIN apps a ON ai.app_id = a.id WHERE a.key = ?)",
|
|
constant.AppCopaw,
|
|
constant.AppCopaw,
|
|
).Error
|
|
},
|
|
}
|
|
|
|
var NormalizeAgentAccountVerifiedStatus = &gormigrate.Migration{
|
|
ID: "20260303-normalize-agent-account-verified-status",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
if err := tx.AutoMigrate(&model.AgentAccount{}); err != nil {
|
|
return err
|
|
}
|
|
return tx.Model(&model.AgentAccount{}).
|
|
Where("provider IN ?", []string{"custom", "ollama", "kimi-coding"}).
|
|
Update("verified", false).Error
|
|
},
|
|
}
|
|
|
|
var NormalizeOllamaAccountAPIType = &gormigrate.Migration{
|
|
ID: "20260304-normalize-ollama-account-api-type",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
if err := tx.AutoMigrate(&model.AgentAccount{}); err != nil {
|
|
return err
|
|
}
|
|
return tx.Model(&model.AgentAccount{}).
|
|
Where("provider = ?", "ollama").
|
|
Update("api_type", "openai-responses").Error
|
|
},
|
|
}
|
|
|
|
var InitAgentAccountModelPool = &gormigrate.Migration{
|
|
ID: "20260319-init-agent-account-model-pool",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
if err := tx.AutoMigrate(&model.AgentAccountModel{}); err != nil {
|
|
return err
|
|
}
|
|
return migrationutils.MigrateAgentAccountModelPool(tx)
|
|
},
|
|
}
|
|
|
|
var AddAgentAccountMasterID = &gormigrate.Migration{
|
|
ID: "20260401-add-agent-account-master-id",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
return tx.AutoMigrate(&model.AgentAccount{})
|
|
},
|
|
}
|
|
|
|
var NormalizeAgentAccountModelIDs = &gormigrate.Migration{
|
|
ID: "20260716-normalize-agent-account-model-ids",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
return migrationutils.NormalizeAgentAccountModelIDs(tx)
|
|
},
|
|
}
|
|
|
|
var AddAgentAccountVerifyModel = &gormigrate.Migration{
|
|
ID: "20260716-add-agent-account-verify-model",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
if err := tx.AutoMigrate(&model.AgentAccount{}); err != nil {
|
|
return err
|
|
}
|
|
var accounts []model.AgentAccount
|
|
if err := tx.Where("verify_model = '' OR verify_model IS NULL").Find(&accounts).Error; err != nil {
|
|
return err
|
|
}
|
|
for _, account := range accounts {
|
|
var accountModel model.AgentAccountModel
|
|
err := tx.Where("account_id = ?", account.ID).Order("sort_order ASC, id ASC").First(&accountModel).Error
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
continue
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := tx.Model(&model.AgentAccount{}).Where("id = ?", account.ID).Update("verify_model", accountModel.Model).Error; err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
},
|
|
}
|
|
|
|
var AddAgentAccountAuthMode = &gormigrate.Migration{
|
|
ID: "20260716-add-agent-account-auth-mode",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
if err := tx.AutoMigrate(&model.AgentAccount{}); err != nil {
|
|
return err
|
|
}
|
|
if err := tx.Model(&model.AgentAccount{}).
|
|
Where("api_type = ? AND (auth_mode IS NULL OR auth_mode = '') AND provider IN ?", "anthropic-messages", []string{"bailian-coding-plan", "ark-coding-plan", "xiaomi"}).
|
|
Update("auth_mode", providercatalog.AuthModeBearer).Error; err != nil {
|
|
return err
|
|
}
|
|
return tx.Model(&model.AgentAccount{}).
|
|
Where("api_type = ? AND (auth_mode IS NULL OR auth_mode = '')", "anthropic-messages").
|
|
Update("auth_mode", providercatalog.AuthModeXAPIKey).Error
|
|
},
|
|
}
|
|
|
|
var AddHostTable = &gormigrate.Migration{
|
|
ID: "20260318-add-host-table",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
if err := tx.AutoMigrate(&model.Host{}); err != nil {
|
|
return err
|
|
}
|
|
if global.CoreDB == nil || !global.CoreDB.Migrator().HasTable("hosts") {
|
|
if err := tx.Create(&model.Group{Name: "Default", Type: "host", IsDefault: true}).Error; err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
var encryptSetting model.Setting
|
|
if err := global.CoreDB.Where("key = ?", "EncryptKey").First(&encryptSetting).Error; err != nil {
|
|
global.LOG.Errorf("failed to get encrypt key from core db, err: %v", err)
|
|
return nil
|
|
}
|
|
coreEncryptKey := strings.TrimSpace(encryptSetting.Value)
|
|
if coreEncryptKey == "" {
|
|
global.LOG.Error("encrypt key from core db is empty")
|
|
return nil
|
|
}
|
|
|
|
groupIDMap := make(map[uint]uint)
|
|
defaultGroupID := uint(0)
|
|
var coreGroups []model.Group
|
|
if err := global.CoreDB.Where("type = ?", "host").Order("id asc").Find(&coreGroups).Error; err != nil {
|
|
return err
|
|
}
|
|
for _, coreGroup := range coreGroups {
|
|
agentGroup := model.Group{
|
|
Name: coreGroup.Name,
|
|
Type: "host",
|
|
IsDefault: coreGroup.IsDefault,
|
|
}
|
|
if agentGroup.IsDefault {
|
|
defaultGroupID = coreGroup.ID
|
|
}
|
|
if err := tx.Create(&agentGroup).Error; err != nil {
|
|
global.LOG.Errorf("failed to create group, group id: %v, err: %v", coreGroup.ID, err)
|
|
continue
|
|
}
|
|
groupIDMap[coreGroup.ID] = agentGroup.ID
|
|
}
|
|
|
|
var coreHosts []model.Host
|
|
if err := global.CoreDB.Order("id asc").Find(&coreHosts).Error; err != nil {
|
|
return err
|
|
}
|
|
for _, coreHost := range coreHosts {
|
|
password, err := encrypt.StringDecryptWithKey(coreHost.Password, coreEncryptKey)
|
|
if err != nil {
|
|
global.LOG.Errorf("failed to decrypt host password, host id: %v, err: %v", coreHost.ID, err)
|
|
continue
|
|
}
|
|
privateKey, err := encrypt.StringDecryptWithKey(coreHost.PrivateKey, coreEncryptKey)
|
|
if err != nil {
|
|
global.LOG.Errorf("failed to decrypt host private key, host id: %v, err: %v", coreHost.ID, err)
|
|
continue
|
|
}
|
|
passPhrase, err := encrypt.StringDecryptWithKey(coreHost.PassPhrase, coreEncryptKey)
|
|
if err != nil {
|
|
global.LOG.Errorf("failed to decrypt host pass phrase, host id: %v, err: %v", coreHost.ID, err)
|
|
continue
|
|
}
|
|
|
|
encryptedPassword, err := encrypt.StringEncrypt(password)
|
|
if err != nil {
|
|
global.LOG.Errorf("failed to encrypt host password, host id: %v, err: %v", coreHost.ID, err)
|
|
continue
|
|
}
|
|
encryptedPrivateKey, err := encrypt.StringEncrypt(privateKey)
|
|
if err != nil {
|
|
global.LOG.Errorf("failed to encrypt host private key, host id: %v, err: %v", coreHost.ID, err)
|
|
continue
|
|
}
|
|
encryptedPassPhrase, err := encrypt.StringEncrypt(passPhrase)
|
|
if err != nil {
|
|
global.LOG.Errorf("failed to encrypt host pass phrase, host id: %v, err: %v", coreHost.ID, err)
|
|
continue
|
|
}
|
|
|
|
groupID := defaultGroupID
|
|
if mappedGroupID, ok := groupIDMap[coreHost.GroupID]; ok && mappedGroupID != 0 {
|
|
groupID = mappedGroupID
|
|
}
|
|
host := model.Host{
|
|
GroupID: groupID,
|
|
Name: coreHost.Name,
|
|
Addr: coreHost.Addr,
|
|
Port: coreHost.Port,
|
|
User: coreHost.User,
|
|
AuthMode: coreHost.AuthMode,
|
|
Password: encryptedPassword,
|
|
PrivateKey: encryptedPrivateKey,
|
|
PassPhrase: encryptedPassPhrase,
|
|
RememberPassword: coreHost.RememberPassword,
|
|
Description: coreHost.Description,
|
|
}
|
|
if err := tx.Create(&host).Error; err != nil {
|
|
global.LOG.Errorf("failed to create host, host id: %v, err: %v", coreHost.ID, err)
|
|
continue
|
|
}
|
|
}
|
|
return nil
|
|
},
|
|
}
|
|
|
|
var AddAITerminalSettings = &gormigrate.Migration{
|
|
ID: "20260318-add-ai-terminal-settings",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
if err := tx.Create(&model.Setting{Key: "AIStatus", Value: constant.StatusDisable}).Error; err != nil {
|
|
return err
|
|
}
|
|
if err := tx.Create(&model.Setting{Key: "AIAccountID", Value: ""}).Error; err != nil {
|
|
return err
|
|
}
|
|
if err := tx.Create(&model.Setting{Key: "AIPrefix", Value: constant.DefaultTerminalAIPrefix}).Error; err != nil {
|
|
return err
|
|
}
|
|
return tx.Create(&model.Setting{
|
|
Key: "AIRiskCommands",
|
|
Value: constant.DefaultTerminalAIRiskCommands,
|
|
}).Error
|
|
},
|
|
}
|
|
|
|
var UpdateAgentQuickJumpTitle = &gormigrate.Migration{
|
|
ID: "20260324-update-agent-quick-jump-title",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
return tx.Model(&model.QuickJump{}).
|
|
Where("title = ?", "aiTools.agents.agents").
|
|
Update("title", "aiTools.agents.agent").Error
|
|
},
|
|
}
|
|
|
|
var FixOpenclaw20260323HTTPPort = &gormigrate.Migration{
|
|
ID: "20260325-fix-openclaw-20260323-http-port",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
return tx.Exec(
|
|
`UPDATE app_installs
|
|
SET http_port = https_port,
|
|
https_port = 0
|
|
WHERE version = ?
|
|
AND https_port > 0
|
|
AND app_id IN (SELECT id FROM apps WHERE key = ?)`,
|
|
"2026.3.24",
|
|
constant.AppOpenclaw,
|
|
).Error
|
|
},
|
|
}
|
|
|
|
var AddAgentRemarkColumn = &gormigrate.Migration{
|
|
ID: "20260330-add-agent-remark-column",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
return tx.AutoMigrate(&model.Agent{})
|
|
},
|
|
}
|
|
|
|
var AddAgentWebsiteBinding = &gormigrate.Migration{
|
|
ID: "20260403-add-agent-website-binding",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
if err := tx.AutoMigrate(&model.Agent{}); err != nil {
|
|
return err
|
|
}
|
|
|
|
var agents []model.Agent
|
|
if err := tx.Find(&agents).Error; err != nil {
|
|
return err
|
|
}
|
|
if len(agents) == 0 {
|
|
return nil
|
|
}
|
|
|
|
var websites []model.Website
|
|
if err := tx.Where("type = ? AND app_install_id > 0", constant.Deployment).Find(&websites).Error; err != nil {
|
|
return err
|
|
}
|
|
websiteMap := service.UniqueDeploymentWebsiteMapByAppInstall(websites)
|
|
for _, agent := range agents {
|
|
if agent.WebsiteID != 0 || agent.AppInstallID == 0 {
|
|
continue
|
|
}
|
|
website, ok := websiteMap[agent.AppInstallID]
|
|
if !ok {
|
|
continue
|
|
}
|
|
if err := tx.Model(&model.Agent{}).Where("id = ?", agent.ID).Update("website_id", website.ID).Error; err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
},
|
|
}
|
|
|
|
var AddFileManageAISettings = &gormigrate.Migration{
|
|
ID: "20260330-add-file-manage-ai-settings",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
rows := []model.Setting{
|
|
{Key: "FileAIStatus", Value: constant.StatusDisable},
|
|
{Key: "FileAIAccountID", Value: ""},
|
|
}
|
|
for i := range rows {
|
|
var exist model.Setting
|
|
if err := tx.Where("`key` = ?", rows[i].Key).First(&exist).Error; err != nil {
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
if err := tx.Create(&rows[i]).Error; err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
},
|
|
}
|
|
|
|
var AddFileShareTable = &gormigrate.Migration{
|
|
ID: "20260410-add-file-share-table",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
return tx.AutoMigrate(&model.FileShare{})
|
|
},
|
|
}
|
|
|
|
var AddFileHistoryTable = &gormigrate.Migration{
|
|
ID: "20260414-add-file-history-table",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
if err := tx.AutoMigrate(&model.FileHistory{}); err != nil {
|
|
return err
|
|
}
|
|
defaultRows := []model.Setting{
|
|
{Key: "FileHistoryStatus", Value: constant.StatusEnable},
|
|
{Key: "FileHistoryMaxPerPath", Value: "20"},
|
|
{Key: "FileHistoryDiskQuotaMB", Value: "1024"},
|
|
}
|
|
for i := range defaultRows {
|
|
var exist model.Setting
|
|
if err := tx.Where("`key` = ?", defaultRows[i].Key).First(&exist).Error; err != nil {
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
if err := tx.Create(&defaultRows[i]).Error; err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
},
|
|
}
|
|
|
|
func loadDatabaseUserTypesForMigration(tx *gorm.DB, mysqlName string) []string {
|
|
var database model.Database
|
|
if err := tx.Where("name = ?", mysqlName).First(&database).Error; err == nil && len(database.Type) != 0 {
|
|
return []string{database.Type}
|
|
}
|
|
|
|
var appKey string
|
|
_ = tx.Table("app_installs").
|
|
Select("apps.`key`").
|
|
Joins("JOIN apps ON apps.id = app_installs.app_id").
|
|
Where("app_installs.name = ?", mysqlName).
|
|
Scan(&appKey).Error
|
|
switch appKey {
|
|
case constant.AppMariaDB, constant.AppMysqlCluster:
|
|
return []string{appKey}
|
|
case constant.AppMysql:
|
|
return []string{constant.AppMysql}
|
|
default:
|
|
return []string{constant.AppMysql, constant.AppMariaDB}
|
|
}
|
|
}
|
|
|
|
func normalizeDatabaseUserHostsForMigration(permission string) []string {
|
|
hostSet := make(map[string]struct{})
|
|
for _, host := range strings.Split(permission, ",") {
|
|
host = strings.TrimSpace(host)
|
|
if len(host) == 0 {
|
|
continue
|
|
}
|
|
hostSet[host] = struct{}{}
|
|
}
|
|
if len(hostSet) == 0 {
|
|
return []string{"%"}
|
|
}
|
|
hosts := make([]string, 0, len(hostSet))
|
|
for host := range hostSet {
|
|
hosts = append(hosts, host)
|
|
}
|
|
return hosts
|
|
}
|
|
|
|
func isDatabaseSystemUserForMigration(username string) bool {
|
|
switch strings.ToLower(username) {
|
|
case "root",
|
|
"mysql.session", "mysql.sys", "mysql.infoschema", "mysqlxsys",
|
|
"mariadb.sys", "mariadb-sys",
|
|
"debian-sys-maint":
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
var AddDatabaseUserTable = &gormigrate.Migration{
|
|
ID: "20260703-add-database-user-table",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
if err := tx.AutoMigrate(&model.DatabaseUser{}, &model.DatabaseUserGrant{}); err != nil {
|
|
return err
|
|
}
|
|
var mysqls []model.DatabaseMysql
|
|
if err := tx.Find(&mysqls).Error; err != nil {
|
|
return err
|
|
}
|
|
for _, item := range mysqls {
|
|
if len(item.Username) == 0 || isDatabaseSystemUserForMigration(item.Username) {
|
|
continue
|
|
}
|
|
for _, dbType := range loadDatabaseUserTypesForMigration(tx, item.MysqlName) {
|
|
if len(dbType) == 0 {
|
|
continue
|
|
}
|
|
for _, host := range normalizeDatabaseUserHostsForMigration(item.Permission) {
|
|
user := model.DatabaseUser{
|
|
Type: dbType,
|
|
Database: item.MysqlName,
|
|
Username: item.Username,
|
|
Host: host,
|
|
Password: item.Password,
|
|
}
|
|
if err := tx.Where("`type` = ? AND database = ? AND username = ? AND host = ?", dbType, item.MysqlName, item.Username, host).FirstOrCreate(&user).Error; err != nil {
|
|
return err
|
|
}
|
|
grant := model.DatabaseUserGrant{
|
|
Type: dbType,
|
|
Database: item.MysqlName,
|
|
DBName: item.Name,
|
|
Username: item.Username,
|
|
Host: host,
|
|
}
|
|
if err := tx.Where("`type` = ? AND database = ? AND db_name = ? AND username = ? AND host = ?", dbType, item.MysqlName, item.Name, item.Username, host).FirstOrCreate(&grant).Error; err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
},
|
|
}
|
|
|
|
// MigrateLegoV5 normalizes data persisted under lego v4 so that lego v5 can read it.
|
|
//
|
|
// Two things changed in lego v5 that affect existing rows:
|
|
//
|
|
// 1. certcrypto.KeyType string values were renamed:
|
|
// "P256"->"EC256", "P384"->"EC384",
|
|
// "2048"->"RSA2048", "3072"->"RSA3072", "4096"->"RSA4096", "8192"->"RSA8192".
|
|
// We update every key_type column we own to the new form.
|
|
//
|
|
// 2. DnsPod provider was removed from upstream lego v5; the frontend already
|
|
// marked it deprecated. Existing DnsPod website_dns_accounts rows are kept
|
|
// so the user can decide what to do, but any website_ssls still pointing
|
|
// at a DnsPod account would fail to renew. We log a warning row count and
|
|
// leave deletion to the operator.
|
|
var MigrateLegoV5 = &gormigrate.Migration{
|
|
ID: "20260523-migrate-lego-v5",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
keyTypeMap := map[string]string{
|
|
"P256": "EC256",
|
|
"P384": "EC384",
|
|
"2048": "RSA2048",
|
|
"3072": "RSA3072",
|
|
"4096": "RSA4096",
|
|
"8192": "RSA8192",
|
|
}
|
|
for old, neu := range keyTypeMap {
|
|
if err := tx.Model(&model.WebsiteAcmeAccount{}).
|
|
Where("key_type = ?", old).
|
|
Update("key_type", neu).Error; err != nil {
|
|
return fmt.Errorf("migrate WebsiteAcmeAccount.key_type %s->%s: %w", old, neu, err)
|
|
}
|
|
if err := tx.Model(&model.WebsiteSSL{}).
|
|
Where("key_type = ?", old).
|
|
Update("key_type", neu).Error; err != nil {
|
|
return fmt.Errorf("migrate WebsiteSSL.key_type %s->%s: %w", old, neu, err)
|
|
}
|
|
if err := tx.Model(&model.WebsiteCA{}).
|
|
Where("key_type = ?", old).
|
|
Update("key_type", neu).Error; err != nil {
|
|
return fmt.Errorf("migrate WebsiteCA.key_type %s->%s: %w", old, neu, err)
|
|
}
|
|
}
|
|
|
|
var dnsPodCount int64
|
|
_ = tx.Model(&model.WebsiteDnsAccount{}).Where("type = ?", "DnsPod").Count(&dnsPodCount).Error
|
|
if dnsPodCount > 0 {
|
|
global.LOG.Warnf("lego v5 removed the DnsPod provider; %d existing DnsPod DNS account(s) will not be usable for renewal -- please switch them to TencentCloud", dnsPodCount)
|
|
}
|
|
|
|
return nil
|
|
},
|
|
}
|
|
|
|
var AddMcpServerGatewayArgs = &gormigrate.Migration{
|
|
ID: "20260714-add-mcp-server-gateway-args",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
return tx.AutoMigrate(&model.McpServer{})
|
|
},
|
|
}
|
|
|
|
var AddBackupRecordArgs = &gormigrate.Migration{
|
|
ID: "20260729-add-backup-record-args",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
return tx.AutoMigrate(&model.BackupRecord{})
|
|
},
|
|
}
|
|
|
|
var AddFtpIdentity = &gormigrate.Migration{
|
|
ID: "20260729-add-ftp-identity",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
if err := tx.AutoMigrate(&model.Ftp{}); err != nil {
|
|
return err
|
|
}
|
|
return tx.Model(&model.Ftp{}).
|
|
Where("1 = 1").
|
|
Updates(map[string]interface{}{
|
|
"uid": constant.WebsiteUID,
|
|
"gid": constant.WebsiteGID,
|
|
}).Error
|
|
},
|
|
}
|
|
|
|
var AddWebsiteTemplateTable = &gormigrate.Migration{
|
|
ID: "20260728-add-website-template-table",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
return tx.AutoMigrate(
|
|
&model.WebsiteTemplate{},
|
|
&model.WebsiteTemplateOutput{},
|
|
)
|
|
},
|
|
}
|
|
|
|
var AddComposePinned = &gormigrate.Migration{
|
|
ID: "20260729-add-compose-pinned",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
return tx.AutoMigrate(&model.Compose{})
|
|
},
|
|
}
|
|
|
|
var AddFirewallRuleTable = &gormigrate.Migration{
|
|
ID: "20260819-add-firewall-v2-tables",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
return tx.AutoMigrate(&model.CommonDescription{})
|
|
},
|
|
}
|
|
|
|
var InitDockerPortGuardStatus = &gormigrate.Migration{
|
|
ID: "20260818-init-docker-port-guard-status",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
return tx.Where("key = ?", "DockerPortGuardStatus").FirstOrCreate(&model.Setting{
|
|
Key: "DockerPortGuardStatus",
|
|
Value: constant.StatusDisable,
|
|
}).Error
|
|
},
|
|
}
|
|
|
|
var NormalizeFirewallBackendSelections = &gormigrate.Migration{
|
|
ID: "20260826-normalize-firewall-backend-selections",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
return tx.Transaction(func(tx *gorm.DB) error {
|
|
result := tx.Model(&model.Setting{}).
|
|
Where(
|
|
"key = ? AND value NOT IN ?",
|
|
constant.FirewallDockerBackendKey,
|
|
[]string{constant.FirewallProviderIptables, constant.FirewallProviderNftables},
|
|
).
|
|
Update("value", constant.FirewallProviderIptables)
|
|
if result.Error != nil {
|
|
return result.Error
|
|
}
|
|
if err := tx.Where("key = ?", constant.FirewallDockerBackendKey).FirstOrCreate(&model.Setting{
|
|
Key: constant.FirewallDockerBackendKey, Value: constant.FirewallProviderIptables,
|
|
}).Error; err != nil {
|
|
return err
|
|
}
|
|
result = tx.Model(&model.Setting{}).
|
|
Where(
|
|
"key = ? AND value NOT IN ?",
|
|
constant.FirewallForwardingBackendKey,
|
|
[]string{constant.FirewallProviderIptables, constant.FirewallProviderNftables},
|
|
).
|
|
Update("value", constant.FirewallProviderIptables)
|
|
if result.Error != nil {
|
|
return result.Error
|
|
}
|
|
if err := tx.Where("key = ?", constant.FirewallForwardingBackendKey).FirstOrCreate(&model.Setting{
|
|
Key: constant.FirewallForwardingBackendKey, Value: constant.FirewallProviderIptables,
|
|
}).Error; err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
})
|
|
},
|
|
}
|
|
|
|
var MigrateFirewallDescriptions = &gormigrate.Migration{
|
|
ID: "20260921-migrate-firewall-descriptions",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
return tx.Transaction(migrationutils.MigrateHostFirewallDescriptions)
|
|
},
|
|
}
|
|
|
|
var AddAcceleratorMetrics = &gormigrate.Migration{
|
|
ID: "20260928-accelerator-vendor-metrics",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
return global.GPUMonitorDB.AutoMigrate(&model.MonitorGPU{})
|
|
},
|
|
}
|
|
|
|
var AddVLLMMonitor = &gormigrate.Migration{
|
|
ID: "20260928-vllm-monitor",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
return global.VLLMMonitorDB.AutoMigrate(&model.MonitorVLLM{})
|
|
},
|
|
}
|
|
|
|
var AddMonitorSettings = &gormigrate.Migration{
|
|
ID: "20260929-monitor-settings",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
for _, key := range []string{"MonitorStatus", "MonitorInterval", "MonitorStoreDays"} {
|
|
var setting model.Setting
|
|
if err := tx.Where("key = ?", key).First(&setting).Error; err != nil {
|
|
return err
|
|
}
|
|
for _, prefix := range []string{"GPU", "VLLM"} {
|
|
if err := tx.Where("key = ?", prefix+key).FirstOrCreate(&model.Setting{Key: prefix + key, Value: setting.Value}).Error; err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
},
|
|
}
|