mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-10-11 17:56:37 +08:00
迁移配置表
This commit is contained in:
@@ -3,105 +3,22 @@
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
import "time"
|
||||
|
||||
"github.com/Rain-kl/Wavelet/internal/db"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// OpenFlareOption stores a hot-reloadable OpenFlare system option.
|
||||
// OpenFlareOption is the key/value DTO used by the OpenFlare option API surface.
|
||||
// Key 与 w_system_configs.key 一致(snake_case)。此类型仅作为
|
||||
// /api/v1/d/option 接口的请求/响应载体,不再映射到独立的 of_options 表。
|
||||
type OpenFlareOption struct {
|
||||
Key string `json:"key" gorm:"column:key;primaryKey;size:128;not null"`
|
||||
Value string `json:"value" gorm:"type:text;not null"`
|
||||
Key string `json:"key"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
// TableName returns the OpenFlare options table name.
|
||||
func (OpenFlareOption) TableName() string {
|
||||
return "of_options"
|
||||
}
|
||||
// StartTime records process start time (seconds) for the public /status endpoint.
|
||||
var StartTime = time.Now().Unix()
|
||||
|
||||
// OptionMap holds the in-memory option snapshot for fast reads and hot reload.
|
||||
var (
|
||||
OptionMap map[string]string
|
||||
OptionMapRWMutex sync.RWMutex
|
||||
|
||||
// StartTime records process start time (seconds) for legacy /api/status.
|
||||
StartTime = time.Now().Unix()
|
||||
|
||||
// Hot-reload mirrors for frequently read options (legacy OpenFlare keys).
|
||||
SystemName = "OpenFlare"
|
||||
ServerAddress = ""
|
||||
Footer = ""
|
||||
HomePageLink = ""
|
||||
PasswordLoginEnabled = true
|
||||
CapLoginEnabled = true
|
||||
PasswordRegisterEnabled = false
|
||||
EmailVerificationEnabled = false
|
||||
SMTPPort = 587
|
||||
AgentDiscoveryToken = ""
|
||||
AgentHeartbeatInterval = 10000
|
||||
AgentWebsocketUpgradeEnabled = true
|
||||
NodeOfflineThreshold = 2 * time.Minute
|
||||
AgentUpdateRepo = "Rain-kl/OpenFlare"
|
||||
GeoIPProvider = "ipinfo"
|
||||
DatabaseAutoCleanupEnabled = false
|
||||
DatabaseAutoCleanupRetentionDays = 30
|
||||
UptimeKumaEnabled = false
|
||||
UptimeKumaURL = ""
|
||||
UptimeKumaUsername = ""
|
||||
UptimeKumaPassword = ""
|
||||
UptimeKumaMonitorScope = "all"
|
||||
UptimeKumaSelectedSites = ""
|
||||
UptimeKumaSyncInterval = 5
|
||||
UptimeKumaInterval = 60
|
||||
UptimeKumaRetry = 0
|
||||
UptimeKumaRetryInterval = 60
|
||||
UptimeKumaTimeout = 48
|
||||
OpenRestyDefaultServerReturnStatus = 421
|
||||
OpenRestyWorkerProcesses = "auto"
|
||||
OpenRestyWorkerConnections = 4096
|
||||
OpenRestyWorkerRlimitNofile = 65535
|
||||
OpenRestyEventsUse = "epoll"
|
||||
OpenRestyEventsMultiAcceptEnabled = true
|
||||
OpenRestyKeepaliveTimeout = 20
|
||||
OpenRestyKeepaliveRequests = 1000
|
||||
OpenRestyClientHeaderTimeout = 15
|
||||
OpenRestyClientBodyTimeout = 15
|
||||
OpenRestyClientMaxBodySize = "64m"
|
||||
OpenRestyLargeClientHeaderBuffers = "4 16k"
|
||||
OpenRestySendTimeout = 30
|
||||
OpenRestyResolvers = ""
|
||||
OpenRestyProxyConnectTimeout = 3
|
||||
OpenRestyProxySendTimeout = 60
|
||||
OpenRestyProxyReadTimeout = 60
|
||||
OpenRestyWebsocketEnabled = true
|
||||
OpenRestyHTTP3Enabled = true
|
||||
OpenRestyProxyRequestBufferingEnabled = false
|
||||
OpenRestyProxyBufferingEnabled = true
|
||||
OpenRestyProxyBuffers = "16 16k"
|
||||
OpenRestyProxyBufferSize = "8k"
|
||||
OpenRestyProxyBusyBuffersSize = "64k"
|
||||
OpenRestyGzipEnabled = true
|
||||
OpenRestyGzipMinLength = 1024
|
||||
OpenRestyGzipCompLevel = 5
|
||||
OpenRestyCacheEnabled = false
|
||||
OpenRestyCachePath = ""
|
||||
OpenRestyCacheLevels = "1:2"
|
||||
OpenRestyCacheInactive = "30m"
|
||||
OpenRestyCacheMaxSize = "1g"
|
||||
OpenRestyCacheKeyTemplate = "$scheme$host$request_uri"
|
||||
OpenRestyCacheLockEnabled = true
|
||||
OpenRestyCacheLockTimeout = "5s"
|
||||
OpenRestyCacheUseStale = "error timeout updating http_500 http_502 http_503 http_504"
|
||||
OpenRestyMainConfigTemplate = defaultOpenRestyMainConfigTemplate
|
||||
)
|
||||
|
||||
const defaultOpenRestyMainConfigTemplate = `# This file is generated by OpenFlare. Do not edit manually.
|
||||
// DefaultOpenRestyMainConfigTemplate 是 OpenResty 主配置模板的内置默认值。
|
||||
// 当 SystemConfig 中缺失 openresty_main_config_template 时作为兜底使用。
|
||||
const DefaultOpenRestyMainConfigTemplate = `# This file is generated by OpenFlare. Do not edit manually.
|
||||
user openflare;
|
||||
worker_processes {{OpenRestyWorkerProcesses}};
|
||||
worker_rlimit_nofile {{OpenRestyWorkerRlimitNofile}};
|
||||
@@ -147,171 +64,3 @@ http {
|
||||
{{OpenRestyResolverDirective}}{{OpenRestyCacheBlock}} include {{OpenRestyRouteConfigInclude}};
|
||||
}
|
||||
`
|
||||
|
||||
// DefaultOpenFlareOptions returns built-in defaults keyed by legacy OpenFlare option names.
|
||||
func DefaultOpenFlareOptions() map[string]string {
|
||||
return map[string]string{
|
||||
"PasswordLoginEnabled": strconv.FormatBool(PasswordLoginEnabled),
|
||||
"CapLoginEnabled": strconv.FormatBool(CapLoginEnabled),
|
||||
"PasswordRegisterEnabled": strconv.FormatBool(PasswordRegisterEnabled),
|
||||
"EmailVerificationEnabled": strconv.FormatBool(EmailVerificationEnabled),
|
||||
"SMTPServer": "",
|
||||
"SMTPPort": strconv.Itoa(SMTPPort),
|
||||
"SMTPAccount": "",
|
||||
"SMTPToken": "",
|
||||
"About": "",
|
||||
"Footer": Footer,
|
||||
"HomePageLink": HomePageLink,
|
||||
"SystemName": SystemName,
|
||||
"ServerAddress": "",
|
||||
"AgentDiscoveryToken": "",
|
||||
"AgentHeartbeatInterval": strconv.Itoa(AgentHeartbeatInterval),
|
||||
"AgentWebsocketUpgradeEnabled": strconv.FormatBool(AgentWebsocketUpgradeEnabled),
|
||||
"NodeOfflineThreshold": strconv.Itoa(int(NodeOfflineThreshold.Milliseconds())),
|
||||
"AgentUpdateRepo": AgentUpdateRepo,
|
||||
"GeoIPProvider": GeoIPProvider,
|
||||
"DatabaseAutoCleanupEnabled": strconv.FormatBool(DatabaseAutoCleanupEnabled),
|
||||
"UptimeKumaEnabled": strconv.FormatBool(UptimeKumaEnabled),
|
||||
"UptimeKumaUrl": UptimeKumaURL,
|
||||
"UptimeKumaUsername": UptimeKumaUsername,
|
||||
optionKeyUptimeKumaPassword: UptimeKumaPassword,
|
||||
"UptimeKumaMonitorScope": UptimeKumaMonitorScope,
|
||||
"UptimeKumaSelectedSites": UptimeKumaSelectedSites,
|
||||
"UptimeKumaSyncInterval": strconv.Itoa(UptimeKumaSyncInterval),
|
||||
"UptimeKumaInterval": strconv.Itoa(UptimeKumaInterval),
|
||||
"UptimeKumaRetry": strconv.Itoa(UptimeKumaRetry),
|
||||
"UptimeKumaRetryInterval": strconv.Itoa(UptimeKumaRetryInterval),
|
||||
"UptimeKumaTimeout": strconv.Itoa(UptimeKumaTimeout),
|
||||
"DatabaseAutoCleanupRetentionDays": strconv.Itoa(DatabaseAutoCleanupRetentionDays),
|
||||
"OpenRestyDefaultServerReturnStatus": strconv.Itoa(OpenRestyDefaultServerReturnStatus),
|
||||
"OpenRestyWorkerProcesses": OpenRestyWorkerProcesses,
|
||||
"OpenRestyWorkerConnections": strconv.Itoa(OpenRestyWorkerConnections),
|
||||
"OpenRestyWorkerRlimitNofile": strconv.Itoa(OpenRestyWorkerRlimitNofile),
|
||||
"OpenRestyEventsUse": OpenRestyEventsUse,
|
||||
"OpenRestyEventsMultiAcceptEnabled": strconv.FormatBool(OpenRestyEventsMultiAcceptEnabled),
|
||||
"OpenRestyKeepaliveTimeout": strconv.Itoa(OpenRestyKeepaliveTimeout),
|
||||
"OpenRestyKeepaliveRequests": strconv.Itoa(OpenRestyKeepaliveRequests),
|
||||
"OpenRestyClientHeaderTimeout": strconv.Itoa(OpenRestyClientHeaderTimeout),
|
||||
"OpenRestyClientBodyTimeout": strconv.Itoa(OpenRestyClientBodyTimeout),
|
||||
"OpenRestyClientMaxBodySize": OpenRestyClientMaxBodySize,
|
||||
"OpenRestyLargeClientHeaderBuffers": OpenRestyLargeClientHeaderBuffers,
|
||||
"OpenRestySendTimeout": strconv.Itoa(OpenRestySendTimeout),
|
||||
"OpenRestyProxyConnectTimeout": strconv.Itoa(OpenRestyProxyConnectTimeout),
|
||||
"OpenRestyProxySendTimeout": strconv.Itoa(OpenRestyProxySendTimeout),
|
||||
"OpenRestyProxyReadTimeout": strconv.Itoa(OpenRestyProxyReadTimeout),
|
||||
"OpenRestyWebsocketEnabled": strconv.FormatBool(OpenRestyWebsocketEnabled),
|
||||
"OpenRestyHTTP3Enabled": strconv.FormatBool(OpenRestyHTTP3Enabled),
|
||||
"OpenRestyProxyRequestBufferingEnabled": strconv.FormatBool(OpenRestyProxyRequestBufferingEnabled),
|
||||
"OpenRestyProxyBufferingEnabled": strconv.FormatBool(OpenRestyProxyBufferingEnabled),
|
||||
"OpenRestyProxyBuffers": OpenRestyProxyBuffers,
|
||||
"OpenRestyProxyBufferSize": OpenRestyProxyBufferSize,
|
||||
"OpenRestyProxyBusyBuffersSize": OpenRestyProxyBusyBuffersSize,
|
||||
"OpenRestyGzipEnabled": strconv.FormatBool(OpenRestyGzipEnabled),
|
||||
"OpenRestyGzipMinLength": strconv.Itoa(OpenRestyGzipMinLength),
|
||||
"OpenRestyGzipCompLevel": strconv.Itoa(OpenRestyGzipCompLevel),
|
||||
"OpenRestyCacheEnabled": strconv.FormatBool(OpenRestyCacheEnabled),
|
||||
"OpenRestyCachePath": OpenRestyCachePath,
|
||||
"OpenRestyCacheLevels": OpenRestyCacheLevels,
|
||||
"OpenRestyCacheInactive": OpenRestyCacheInactive,
|
||||
"OpenRestyCacheMaxSize": OpenRestyCacheMaxSize,
|
||||
"OpenRestyCacheKeyTemplate": OpenRestyCacheKeyTemplate,
|
||||
"OpenRestyCacheLockEnabled": strconv.FormatBool(OpenRestyCacheLockEnabled),
|
||||
"OpenRestyCacheLockTimeout": OpenRestyCacheLockTimeout,
|
||||
"OpenRestyCacheUseStale": OpenRestyCacheUseStale,
|
||||
"OpenRestyMainConfigTemplate": OpenRestyMainConfigTemplate,
|
||||
}
|
||||
}
|
||||
|
||||
// InitOptionMap seeds defaults and overlays persisted options from of_options.
|
||||
func InitOptionMap(ctx context.Context) error {
|
||||
OptionMapRWMutex.Lock()
|
||||
OptionMap = DefaultOpenFlareOptions()
|
||||
OptionMapRWMutex.Unlock()
|
||||
|
||||
options, err := ListOpenFlareOptions(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, option := range options {
|
||||
applyOptionMap(option.Key, option.Value)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListOpenFlareOptions returns all persisted options.
|
||||
func ListOpenFlareOptions(ctx context.Context) ([]OpenFlareOption, error) {
|
||||
var options []OpenFlareOption
|
||||
if err := db.DB(ctx).Find(&options).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return options, nil
|
||||
}
|
||||
|
||||
// UpdateOpenFlareOption updates a single option in DB and memory.
|
||||
func UpdateOpenFlareOption(ctx context.Context, key, value string) error {
|
||||
return UpdateOpenFlareOptions(ctx, []OpenFlareOption{{Key: key, Value: value}})
|
||||
}
|
||||
|
||||
// UpdateOpenFlareOptions batch-updates options in a transaction and refreshes OptionMap.
|
||||
func UpdateOpenFlareOptions(ctx context.Context, options []OpenFlareOption) error {
|
||||
if len(options) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := db.DB(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
for _, item := range options {
|
||||
if item.Key == optionKeyUptimeKumaPassword && strings.TrimSpace(item.Value) == "" {
|
||||
continue
|
||||
}
|
||||
option := OpenFlareOption{Key: item.Key}
|
||||
if err := tx.FirstOrCreate(&option, OpenFlareOption{Key: item.Key}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
option.Value = item.Value
|
||||
if err := tx.Save(&option).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, item := range options {
|
||||
if item.Key == optionKeyUptimeKumaPassword && strings.TrimSpace(item.Value) == "" {
|
||||
continue
|
||||
}
|
||||
applyOptionMap(item.Key, item.Value)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// OptionValue returns a snapshot value from OptionMap.
|
||||
func OptionValue(key string) string {
|
||||
OptionMapRWMutex.RLock()
|
||||
defer OptionMapRWMutex.RUnlock()
|
||||
if OptionMap == nil {
|
||||
return ""
|
||||
}
|
||||
return OptionMap[key]
|
||||
}
|
||||
|
||||
// ResetOptionMapForTest clears in-memory option state for unit tests.
|
||||
func ResetOptionMapForTest() {
|
||||
OptionMapRWMutex.Lock()
|
||||
OptionMap = nil
|
||||
OptionMapRWMutex.Unlock()
|
||||
}
|
||||
|
||||
func applyOptionMap(key, value string) {
|
||||
OptionMapRWMutex.Lock()
|
||||
defer OptionMapRWMutex.Unlock()
|
||||
if OptionMap == nil {
|
||||
OptionMap = make(map[string]string)
|
||||
}
|
||||
OptionMap[key] = value
|
||||
applyEnabledOptionMirror(key, value)
|
||||
applyStringOptionMirror(key, value)
|
||||
applyIntegerOptionMirror(key, value)
|
||||
applySpecialOptionMirror(key, value)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user