服务端媒体源导入/保存/客户端输出链路修复:支持 snake/camel、subcategories/sources,默认客户端可见,保存后发布兼容 media-types.json。
build-winui / winui (push) Has been cancelled
build-winui / winui (push) Has been cancelled
新增数据库同步 Job API、持久化状态、实时输出、最新任务恢复,以及系统日志聚合接口。 管理端优化:日志中心、运维实时状态框、同步输出自动滚动、仪表盘“输出”列、真实延迟空态、本地 favicon/avatar。 新增 server/unified-management/assets/favicon.ico 和 developer-avatar.png,并接好 /favicon.ico、/admin/favicon.ico、/setup/favicon.ico、/assets/*。 WinUI 随机放映室卡片优先显示子接口原始 Description。 Inno 安装器输出框改为选区末尾 + SendMessage 滚动到底部。
This commit is contained in:
@@ -69,6 +69,7 @@ func Open(cfg *config.Config) (*Store, error) {
|
||||
SchemaVersion: CurrentSchemaVersion,
|
||||
SQLiteReady: true,
|
||||
LastRecoveredAt: Now(),
|
||||
LastHealthStatus: "not_configured",
|
||||
},
|
||||
}
|
||||
if err := store.migrate(local, localDialect); err != nil {
|
||||
@@ -86,6 +87,7 @@ func Open(cfg *config.Config) (*Store, error) {
|
||||
store.markFailover(err)
|
||||
}
|
||||
}
|
||||
store.restoreDatabaseSyncStatus()
|
||||
go store.maintain()
|
||||
return store, nil
|
||||
}
|
||||
@@ -171,14 +173,17 @@ func (s *Store) ReconfigureDatabase(cfg *config.Config) error {
|
||||
s.status.RemoteReady = remote != nil
|
||||
s.status.LastError = ""
|
||||
s.status.FailoverActive = false
|
||||
s.status.LastHealthCheckedAt = Now()
|
||||
if remote != nil {
|
||||
s.db = remote
|
||||
s.dialect = remoteDialect
|
||||
s.status.ActiveProvider = "mysql"
|
||||
s.status.LastHealthStatus = "ok"
|
||||
} else {
|
||||
s.db = local
|
||||
s.dialect = localDialect
|
||||
s.status.ActiveProvider = "sqlite"
|
||||
s.status.LastHealthStatus = "not_configured"
|
||||
}
|
||||
s.status.LastRecoveredAt = Now()
|
||||
s.mu.Unlock()
|
||||
@@ -188,6 +193,7 @@ func (s *Store) ReconfigureDatabase(cfg *config.Config) error {
|
||||
if oldLocal != nil && oldLocal != local {
|
||||
_ = oldLocal.Close()
|
||||
}
|
||||
s.restoreDatabaseSyncStatus()
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -237,7 +243,11 @@ func (s *Store) insertID(query string, args ...any) (int64, error) {
|
||||
}
|
||||
|
||||
func (s *Store) maintain() {
|
||||
ticker := time.NewTicker(time.Duration(s.cfg.Database.HealthIntervalSec) * time.Second)
|
||||
interval := s.cfg.Database.HealthIntervalSec
|
||||
if interval <= 0 {
|
||||
interval = 60
|
||||
}
|
||||
ticker := time.NewTicker(time.Duration(interval) * time.Second)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
select {
|
||||
@@ -276,12 +286,18 @@ func (s *Store) openRemote() error {
|
||||
s.status.FailoverActive = false
|
||||
s.status.LastError = ""
|
||||
s.status.LastRecoveredAt = Now()
|
||||
s.status.LastHealthCheckedAt = Now()
|
||||
s.status.LastHealthStatus = "ok"
|
||||
s.mu.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Store) checkRemote() {
|
||||
if !strings.EqualFold(s.cfg.Database.Provider, "mysql") {
|
||||
s.mu.Lock()
|
||||
s.status.LastHealthCheckedAt = Now()
|
||||
s.status.LastHealthStatus = "not_configured"
|
||||
s.mu.Unlock()
|
||||
return
|
||||
}
|
||||
s.mu.RLock()
|
||||
@@ -311,6 +327,8 @@ func (s *Store) checkRemote() {
|
||||
s.status.FailoverActive = false
|
||||
s.status.LastError = ""
|
||||
s.status.LastRecoveredAt = Now()
|
||||
s.status.LastHealthCheckedAt = Now()
|
||||
s.status.LastHealthStatus = "ok"
|
||||
s.mu.Unlock()
|
||||
}
|
||||
|
||||
@@ -329,4 +347,6 @@ func (s *Store) markFailover(err error) {
|
||||
s.status.FailoverActive = !strings.EqualFold(s.cfg.Database.Provider, "sqlite")
|
||||
s.status.LastError = err.Error()
|
||||
s.status.LastFailoverAt = Now()
|
||||
s.status.LastHealthCheckedAt = Now()
|
||||
s.status.LastHealthStatus = "error"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user