feat: publish sanitized custom.97 source

This commit is contained in:
cneicy committed 2026-10-27 20:43:39 +08:00
1 parent 8e6ed0317b
commit 86c0276156
1022 files changed
+69903 -19207

No files matched your search

+62 -34
View File
@@ -1,6 +1,7 @@
package model
import (
"database/sql"
"fmt"
"log"
"net/url"
@@ -71,7 +72,7 @@ func createRootAccountIfNeed() error {
var user User
//if user.Status != common.UserStatusEnabled {
if err := DB.First(&user).Error; err != nil {
common.SysLog("no user exists, create a root user for you: username is root, password is 123456")
common.SysLog(common.LogText("no user exists, create a root user for you: username is root, password is 123456"))
hashedPassword, err := common.Password2Hash("123456")
if err != nil {
return err
@@ -95,7 +96,7 @@ func CheckSetup() {
if setup == nil {
// No setup record exists, check if we have a root user
if RootUserExists() {
common.SysLog("system is not initialized, but root user exists")
common.SysLog(common.LogText("system is not initialized, but root user exists"))
// Create setup record
newSetup := Setup{
Version: common.Version,
@@ -103,16 +104,16 @@ func CheckSetup() {
}
err := DB.Create(&newSetup).Error
if err != nil {
common.SysLog("failed to create setup record: " + err.Error())
common.SysLog(common.LogText("failed to create setup record: %s", err.Error()))
}
constant.Setup = true
} else {
common.SysLog("system is not initialized and no root user exists")
common.SysLog(common.LogText("system is not initialized and no root user exists"))
constant.Setup = false
}
} else {
// Setup record exists, system is initialized
common.SysLog("system is already initialized at: " + time.Unix(setup.InitializedAt, 0).String())
common.SysLog(common.LogText("system is already initialized at: %s", time.Unix(setup.InitializedAt, 0).String()))
constant.Setup = true
}
}
@@ -144,13 +145,13 @@ func chooseDB(envName string, isLog bool) (*gorm.DB, common.DatabaseType, error)
if !isLog {
return nil, "", fmt.Errorf("%s does not support ClickHouse; use SQLite, MySQL, or PostgreSQL for the primary database and LOG_SQL_DSN for ClickHouse logs", envName)
}
common.SysLog("using ClickHouse as log database")
common.SysLog(common.LogText("using ClickHouse as log database"))
db, err := gorm.Open(clickhouse.Open(normalizeClickHouseDSN(dsn)), newGormConfig(false))
return db, common.DatabaseTypeClickHouse, err
}
if strings.HasPrefix(dsn, "postgres://") || strings.HasPrefix(dsn, "postgresql://") {
// Use PostgreSQL
common.SysLog("using PostgreSQL as database")
common.SysLog(common.LogText("using PostgreSQL as database"))
// 同时关闭 pgx 隐式与 GORM 显式预处理语句:命名 prepared statement 与
// 事务池代理(PgBouncer/Neon/Supabase)不兼容,会触发 FATAL 08P01/42P05。
db, err := gorm.Open(postgresMigrationDialector{postgres.Dialector{Config: &postgres.Config{
@@ -160,12 +161,12 @@ func chooseDB(envName string, isLog bool) (*gorm.DB, common.DatabaseType, error)
return db, common.DatabaseTypePostgreSQL, err
}
if strings.HasPrefix(dsn, "local") {
common.SysLog("SQL_DSN not set, using SQLite as database")
common.SysLog(common.LogText("SQL_DSN not set, using SQLite as database"))
db, err := gorm.Open(sqlite.Open(common.SQLitePath), newGormConfig(true))
return db, common.DatabaseTypeSQLite, err
}
// Use MySQL
common.SysLog("using MySQL as database")
common.SysLog(common.LogText("using MySQL as database"))
// check parseTime
if !strings.Contains(dsn, "parseTime") {
if strings.Contains(dsn, "?") {
@@ -178,7 +179,7 @@ func chooseDB(envName string, isLog bool) (*gorm.DB, common.DatabaseType, error)
return db, common.DatabaseTypeMySQL, err
}
// Use SQLite
common.SysLog("SQL_DSN not set, using SQLite as database")
common.SysLog(common.LogText("SQL_DSN not set, using SQLite as database"))
db, err := gorm.Open(sqlite.Open(common.SQLitePath), newGormConfig(true))
return db, common.DatabaseTypeSQLite, err
}
@@ -208,17 +209,23 @@ func InitDB() (err error) {
if err != nil {
return err
}
sqlDB.SetMaxIdleConns(common.GetEnvOrDefault("SQL_MAX_IDLE_CONNS", 100))
sqlDB.SetMaxOpenConns(common.GetEnvOrDefault("SQL_MAX_OPEN_CONNS", 1000))
sqlDB.SetMaxIdleConns(common.GetEnvOrDefault("SQL_MAX_IDLE_CONNS", 10))
sqlDB.SetMaxOpenConns(common.GetEnvOrDefault("SQL_MAX_OPEN_CONNS", 40))
sqlDB.SetConnMaxLifetime(time.Second * time.Duration(common.GetEnvOrDefault("SQL_MAX_LIFETIME", 60)))
monitorDatabasePool("main", sqlDB)
if !common.IsMasterNode {
// Only the master node migrates. A node that cannot read the deadline
// keeps rejecting legacy access tokens instead of refusing to start.
if err := EnsureLegacyAccessTokenRetireAt(common.GetTimestamp()); err != nil {
common.SysError(common.LogText("initialize legacy access token deadline: %s", err.Error()))
}
return nil
}
if common.UsingMainDatabase(common.DatabaseTypeMySQL) {
//_, _ = sqlDB.Exec("ALTER TABLE channels MODIFY model_mapping TEXT;") // TODO: delete this line when most users have upgraded
}
common.SysLog("database migration started")
common.SysLog(common.LogText("database migration started"))
err = migrateDB()
if err == nil {
err = MigrateTokenAstraAutoSwitch()
@@ -268,14 +275,15 @@ func InitLogDB() (err error) {
if err != nil {
return err
}
sqlDB.SetMaxIdleConns(common.GetEnvOrDefault("SQL_MAX_IDLE_CONNS", 100))
sqlDB.SetMaxOpenConns(common.GetEnvOrDefault("SQL_MAX_OPEN_CONNS", 1000))
sqlDB.SetMaxIdleConns(common.GetEnvOrDefault("SQL_MAX_IDLE_CONNS", 3))
sqlDB.SetMaxOpenConns(common.GetEnvOrDefault("SQL_MAX_OPEN_CONNS", 10))
sqlDB.SetConnMaxLifetime(time.Second * time.Duration(common.GetEnvOrDefault("SQL_MAX_LIFETIME", 60)))
monitorDatabasePool("log", sqlDB)
if !common.IsMasterNode {
return nil
}
common.SysLog("database migration started")
common.SysLog(common.LogText("database migration started"))
err = migrateLOGDB()
return err
} else {
@@ -284,6 +292,22 @@ func InitLogDB() (err error) {
return err
}
func monitorDatabasePool(name string, db *sql.DB) {
go func() {
var lastWaitCount int64
ticker := time.NewTicker(30 * time.Second)
defer ticker.Stop()
for range ticker.C {
stats := db.Stats()
if stats.WaitCount == lastWaitCount && stats.InUse < stats.MaxOpenConnections {
continue
}
lastWaitCount = stats.WaitCount
common.SysError(common.LogText("database pool %s: open=%d in_use=%d idle=%d max_open=%d wait_count=%d wait_duration=%s", name, stats.OpenConnections, stats.InUse, stats.Idle, stats.MaxOpenConnections, stats.WaitCount, stats.WaitDuration))
}
}()
}
var userQuotaColumns = []string{"quota", "used_quota", "aff_quota", "aff_history"}
// ensureUserQuotaColumns rejects a legacy 32-bit wallet schema before any
@@ -291,7 +315,7 @@ var userQuotaColumns = []string{"quota", "used_quota", "aff_quota", "aff_history
// an existing wallet; operators must migrate it explicitly before starting.
func ensureUserQuotaColumns(db *gorm.DB, dbType common.DatabaseType) error {
if common.GetEnvOrDefaultBool("SKIP_64BIT_QUOTA_SCHEMA_CHECK", false) {
common.SysLog("SKIP_64BIT_QUOTA_SCHEMA_CHECK=true; skipping user quota schema check")
common.SysLog(common.LogText("SKIP_64BIT_QUOTA_SCHEMA_CHECK=true; skipping user quota schema check"))
return nil
}
if db == nil || dbType == common.DatabaseTypeSQLite {
@@ -344,7 +368,7 @@ func migrateDB() error {
return err
}
if err := migrateOptionPrimaryKey(DB); err != nil {
common.SysError("failed to migrate options primary key: " + err.Error())
common.SysError(common.LogText("failed to migrate options primary key: %s", err.Error()))
}
err := DB.AutoMigrate(
@@ -389,6 +413,7 @@ func migrateDB() error {
&DetectionSchedule{},
&CasbinRule{},
&AuthzRole{},
&UserAccessToken{},
)
if err != nil {
return err
@@ -396,6 +421,9 @@ func migrateDB() error {
if err := InitializeUserAuthVersions(); err != nil {
return err
}
if err := EnsureLegacyAccessTokenRetireAt(common.GetTimestamp()); err != nil {
return fmt.Errorf("initialize legacy access token deadline: %w", err)
}
if err := InitializeExternalIdentityClaims(); err != nil {
return err
}
@@ -619,7 +647,7 @@ func migrateTokenModelLimitsToText() error {
if err := DB.Raw(`SELECT data_type FROM information_schema.columns
WHERE table_schema = current_schema() AND table_name = ? AND column_name = ?`,
tableName, columnName).Scan(&dataType).Error; err != nil {
common.SysLog(fmt.Sprintf("Warning: failed to query metadata for %s.%s: %v", tableName, columnName, err))
common.SysLog(common.LogText("Warning: failed to query metadata for %s.%s: %v", tableName, columnName, err))
} else if dataType == "text" {
return nil
}
@@ -629,7 +657,7 @@ func migrateTokenModelLimitsToText() error {
if err := DB.Raw(`SELECT COLUMN_TYPE FROM information_schema.columns
WHERE table_schema = DATABASE() AND table_name = ? AND column_name = ?`,
tableName, columnName).Scan(&columnType).Error; err != nil {
common.SysLog(fmt.Sprintf("Warning: failed to query metadata for %s.%s: %v", tableName, columnName, err))
common.SysLog(common.LogText("Warning: failed to query metadata for %s.%s: %v", tableName, columnName, err))
} else if strings.ToLower(columnType) == "text" {
return nil
}
@@ -642,7 +670,7 @@ func migrateTokenModelLimitsToText() error {
if err := DB.Exec(alterSQL).Error; err != nil {
return fmt.Errorf("failed to migrate %s.%s to text: %w", tableName, columnName, err)
}
common.SysLog(fmt.Sprintf("Successfully migrated %s.%s to text", tableName, columnName))
common.SysLog(common.LogText("Successfully migrated %s.%s to text", tableName, columnName))
}
return nil
}
@@ -676,7 +704,7 @@ func migrateSubscriptionPlanPriceAmount() {
if err := DB.Raw(`SELECT data_type FROM information_schema.columns
WHERE table_schema = current_schema() AND table_name = ? AND column_name = ?`,
tableName, columnName).Scan(&dataType).Error; err != nil {
common.SysLog(fmt.Sprintf("Warning: failed to query metadata for %s.%s: %v", tableName, columnName, err))
common.SysLog(common.LogText("Warning: failed to query metadata for %s.%s: %v", tableName, columnName, err))
} else if dataType == "numeric" {
return // Already decimal/numeric
}
@@ -688,7 +716,7 @@ func migrateSubscriptionPlanPriceAmount() {
if err := DB.Raw(`SELECT COLUMN_TYPE FROM information_schema.columns
WHERE table_schema = DATABASE() AND table_name = ? AND column_name = ?`,
tableName, columnName).Scan(&columnType).Error; err != nil {
common.SysLog(fmt.Sprintf("Warning: failed to query metadata for %s.%s: %v", tableName, columnName, err))
common.SysLog(common.LogText("Warning: failed to query metadata for %s.%s: %v", tableName, columnName, err))
} else if strings.HasPrefix(strings.ToLower(columnType), "decimal") {
return // Already decimal
}
@@ -700,9 +728,9 @@ func migrateSubscriptionPlanPriceAmount() {
if alterSQL != "" {
if err := DB.Exec(alterSQL).Error; err != nil {
common.SysLog(fmt.Sprintf("Warning: failed to migrate %s.%s to decimal: %v", tableName, columnName, err))
common.SysLog(common.LogText("Warning: failed to migrate %s.%s to decimal: %v", tableName, columnName, err))
} else {
common.SysLog(fmt.Sprintf("Successfully migrated %s.%s to decimal(10,6)", tableName, columnName))
common.SysLog(common.LogText("Successfully migrated %s.%s to decimal(10,6)", tableName, columnName))
}
}
}
@@ -736,7 +764,7 @@ func checkMySQLChineseSupport(db *gorm.DB) error {
var schemaCharset, schemaCollation string
err := db.Raw("SELECT DEFAULT_CHARACTER_SET_NAME, DEFAULT_COLLATION_NAME FROM information_schema.SCHEMATA WHERE SCHEMA_NAME = DATABASE()").Row().Scan(&schemaCharset, &schemaCollation)
if err != nil {
return fmt.Errorf("读取当前库默认字符集/排序规则失败 / Failed to read schema default charset/collation: %v", err)
return fmt.Errorf("Failed to read schema default charset/collation: %v", err)
}
toLower := func(s string) string { return strings.ToLower(s) }
@@ -768,8 +796,8 @@ func checkMySQLChineseSupport(db *gorm.DB) error {
// 1) 当前库默认值必须支持中文
if !isChineseCapable(schemaCharset, schemaCollation) {
return fmt.Errorf("当前库默认字符集/排序规则不支持中文:schema(%s/%s)。请将库设置为 utf8mb4/utf8/gbk/big5/gb18030 / Schema default charset/collation is not Chinese-capable: schema(%s/%s). Please set to utf8mb4/utf8/gbk/big5/gb18030",
schemaCharset, schemaCollation, schemaCharset, schemaCollation)
return fmt.Errorf("Schema default charset/collation is not Chinese-capable: schema(%s/%s). Please set to utf8mb4/utf8/gbk/big5/gb18030",
schemaCharset, schemaCollation)
}
// 2) 所有物理表的排序规则(隐含字符集)必须支持中文
@@ -779,7 +807,7 @@ func checkMySQLChineseSupport(db *gorm.DB) error {
}
var tables []tableInfo
if err := db.Raw("SELECT TABLE_NAME, TABLE_COLLATION FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_TYPE = 'BASE TABLE'").Scan(&tables).Error; err != nil {
return fmt.Errorf("读取表排序规则失败 / Failed to read table collations: %v", err)
return fmt.Errorf("Failed to read table collations: %v", err)
}
var badTables []string
@@ -811,8 +839,8 @@ func checkMySQLChineseSupport(db *gorm.DB) error {
shown = shown[:maxShow]
}
return fmt.Errorf(
"存在不支持中文的表,请修复其排序规则/字符集。示例(最多展示 %d 项):%v / Found tables not Chinese-capable. Please fix their collation/charset. Examples (showing up to %d): %v",
maxShow, shown, maxShow, shown,
"Found tables not Chinese-capable. Please fix their collation/charset. Examples (showing up to %d): %v",
maxShow, shown,
)
}
return nil
@@ -833,17 +861,17 @@ func PingDB() error {
sqlDB, err := DB.DB()
if err != nil {
log.Printf("Error getting sql.DB from GORM: %v", err)
log.Print(common.LogText("Error getting sql.DB from GORM: %v", err))
return err
}
err = sqlDB.Ping()
if err != nil {
log.Printf("Error pinging DB: %v", err)
log.Print(common.LogText("Error pinging DB: %v", err))
return err
}
lastPingTime = time.Now()
common.SysLog("Database pinged successfully")
common.SysLog(common.LogText("Database pinged successfully"))
return nil
}