refactor(config): console_log 提到顶层并细分 web/mqtt/llm/sql;data_dir 归入 ai
- 新增顶层 ConsoleLogConfig{Web, MQTT, LLM, SQL},默认全 true,方便后续按模块扩展
- WebConfig.ConsoleLog 移除;NewRouter/NewHTTPServer 改为显式接 consoleLog 参数
- AIConfig 增加 DataDir,原 Config.DataDir(仅 ai 模块使用)下沉到 ai.data_dir
- OpenStore(cfg, consoleLog) 通过 console_log.sql 控制 gorm logger 级别(Warn/Silent)
- gorm logger 始终设 IgnoreRecordNotFoundError=true,修掉 GetBoolRuntimeSetting
正常缺省路径仍刷屏 'record not found' 的噪声
- 同步更新 install.sh 模板、win/etc 默认 yaml 与所有测试调用点
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
+5
-1
@@ -79,12 +79,16 @@ web:
|
|||||||
port: 8080
|
port: 8080
|
||||||
socket_path: ${SOCKET_PATH}
|
socket_path: ${SOCKET_PATH}
|
||||||
static_dir: ${INSTALL_DIR}/dist
|
static_dir: ${INSTALL_DIR}/dist
|
||||||
console_log: true
|
|
||||||
admin:
|
admin:
|
||||||
username: admin
|
username: admin
|
||||||
password: admin
|
password: admin
|
||||||
session_secret: ""
|
session_secret: ""
|
||||||
session_secure: false
|
session_secure: false
|
||||||
|
console_log:
|
||||||
|
web: true
|
||||||
|
mqtt: true
|
||||||
|
llm: true
|
||||||
|
sql: true
|
||||||
EOF
|
EOF
|
||||||
chown "${SERVICE_USER}:${SERVICE_USER}" "${CONFIG_DIR}/config.yaml"
|
chown "${SERVICE_USER}:${SERVICE_USER}" "${CONFIG_DIR}/config.yaml"
|
||||||
chmod 0640 "${CONFIG_DIR}/config.yaml"
|
chmod 0640 "${CONFIG_DIR}/config.yaml"
|
||||||
|
|||||||
+54
-15
@@ -23,7 +23,7 @@ type Config struct {
|
|||||||
Database DatabaseConfig `yaml:"database"`
|
Database DatabaseConfig `yaml:"database"`
|
||||||
Web WebConfig `yaml:"web"`
|
Web WebConfig `yaml:"web"`
|
||||||
AI AIConfig `yaml:"ai"`
|
AI AIConfig `yaml:"ai"`
|
||||||
DataDir string `yaml:"data_dir"`
|
ConsoleLog ConsoleLogConfig `yaml:"console_log"`
|
||||||
Key []byte `yaml:"-"`
|
Key []byte `yaml:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,7 +66,6 @@ type WebConfig struct {
|
|||||||
SocketPath string `yaml:"socket_path"`
|
SocketPath string `yaml:"socket_path"`
|
||||||
StaticDir string `yaml:"static_dir"`
|
StaticDir string `yaml:"static_dir"`
|
||||||
MapTileCacheDir string `yaml:"map_tile_cache_dir"`
|
MapTileCacheDir string `yaml:"map_tile_cache_dir"`
|
||||||
ConsoleLog bool `yaml:"console_log"`
|
|
||||||
Admin WebAdminConfig `yaml:"admin"`
|
Admin WebAdminConfig `yaml:"admin"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,7 +77,16 @@ type WebAdminConfig struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type AIConfig struct {
|
type AIConfig struct {
|
||||||
Enabled bool `yaml:"enabled"`
|
Enabled bool `yaml:"enabled"`
|
||||||
|
DataDir string `yaml:"data_dir"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConsoleLogConfig 控制各模块是否在控制台打印日志。后续若新增模块,按需扩展。
|
||||||
|
type ConsoleLogConfig struct {
|
||||||
|
Web bool `yaml:"web"`
|
||||||
|
MQTT bool `yaml:"mqtt"`
|
||||||
|
LLM bool `yaml:"llm"`
|
||||||
|
SQL bool `yaml:"sql"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type rawConfig struct {
|
type rawConfig struct {
|
||||||
@@ -87,11 +95,19 @@ type rawConfig struct {
|
|||||||
Database *rawDatabaseConfig `yaml:"database"`
|
Database *rawDatabaseConfig `yaml:"database"`
|
||||||
Web *rawWebConfig `yaml:"web"`
|
Web *rawWebConfig `yaml:"web"`
|
||||||
AI *rawAIConfig `yaml:"ai"`
|
AI *rawAIConfig `yaml:"ai"`
|
||||||
DataDir *string `yaml:"data_dir"`
|
ConsoleLog *rawConsoleLogConfig `yaml:"console_log"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type rawConsoleLogConfig struct {
|
||||||
|
Web *bool `yaml:"web"`
|
||||||
|
MQTT *bool `yaml:"mqtt"`
|
||||||
|
LLM *bool `yaml:"llm"`
|
||||||
|
SQL *bool `yaml:"sql"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type rawAIConfig struct {
|
type rawAIConfig struct {
|
||||||
Enabled *bool `yaml:"enabled"`
|
Enabled *bool `yaml:"enabled"`
|
||||||
|
DataDir *string `yaml:"data_dir"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type rawMQTTConfig struct {
|
type rawMQTTConfig struct {
|
||||||
@@ -133,7 +149,6 @@ type rawWebConfig struct {
|
|||||||
SocketPath *string `yaml:"socket_path"`
|
SocketPath *string `yaml:"socket_path"`
|
||||||
StaticDir *string `yaml:"static_dir"`
|
StaticDir *string `yaml:"static_dir"`
|
||||||
MapTileCacheDir *string `yaml:"map_tile_cache_dir"`
|
MapTileCacheDir *string `yaml:"map_tile_cache_dir"`
|
||||||
ConsoleLog *bool `yaml:"console_log"`
|
|
||||||
Admin *rawWebAdminConfig `yaml:"admin"`
|
Admin *rawWebAdminConfig `yaml:"admin"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,7 +188,6 @@ func Default() *Config {
|
|||||||
SocketPath: defaultWebSocketPath(),
|
SocketPath: defaultWebSocketPath(),
|
||||||
StaticDir: "./dist",
|
StaticDir: "./dist",
|
||||||
MapTileCacheDir: defaultMapTileCacheDir(),
|
MapTileCacheDir: defaultMapTileCacheDir(),
|
||||||
ConsoleLog: true,
|
|
||||||
Admin: WebAdminConfig{
|
Admin: WebAdminConfig{
|
||||||
Username: "admin",
|
Username: "admin",
|
||||||
Password: "admin",
|
Password: "admin",
|
||||||
@@ -183,8 +197,14 @@ func Default() *Config {
|
|||||||
},
|
},
|
||||||
AI: AIConfig{
|
AI: AIConfig{
|
||||||
Enabled: false,
|
Enabled: false,
|
||||||
|
DataDir: defaultDataDir(),
|
||||||
|
},
|
||||||
|
ConsoleLog: ConsoleLogConfig{
|
||||||
|
Web: true,
|
||||||
|
MQTT: true,
|
||||||
|
LLM: true,
|
||||||
|
SQL: true,
|
||||||
},
|
},
|
||||||
DataDir: defaultDataDir(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -431,11 +451,6 @@ func normalize(raw rawConfig) (*Config, bool) {
|
|||||||
} else {
|
} else {
|
||||||
cfg.Web.MapTileCacheDir = *raw.Web.MapTileCacheDir
|
cfg.Web.MapTileCacheDir = *raw.Web.MapTileCacheDir
|
||||||
}
|
}
|
||||||
if raw.Web.ConsoleLog == nil {
|
|
||||||
changed = true
|
|
||||||
} else {
|
|
||||||
cfg.Web.ConsoleLog = *raw.Web.ConsoleLog
|
|
||||||
}
|
|
||||||
if raw.Web.Admin == nil {
|
if raw.Web.Admin == nil {
|
||||||
changed = true
|
changed = true
|
||||||
} else {
|
} else {
|
||||||
@@ -470,12 +485,36 @@ func normalize(raw rawConfig) (*Config, bool) {
|
|||||||
} else {
|
} else {
|
||||||
cfg.AI.Enabled = *raw.AI.Enabled
|
cfg.AI.Enabled = *raw.AI.Enabled
|
||||||
}
|
}
|
||||||
|
if raw.AI.DataDir == nil {
|
||||||
|
changed = true
|
||||||
|
} else {
|
||||||
|
cfg.AI.DataDir = *raw.AI.DataDir
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if raw.DataDir == nil {
|
if raw.ConsoleLog == nil {
|
||||||
changed = true
|
changed = true
|
||||||
} else {
|
} else {
|
||||||
cfg.DataDir = *raw.DataDir
|
if raw.ConsoleLog.Web == nil {
|
||||||
|
changed = true
|
||||||
|
} else {
|
||||||
|
cfg.ConsoleLog.Web = *raw.ConsoleLog.Web
|
||||||
|
}
|
||||||
|
if raw.ConsoleLog.MQTT == nil {
|
||||||
|
changed = true
|
||||||
|
} else {
|
||||||
|
cfg.ConsoleLog.MQTT = *raw.ConsoleLog.MQTT
|
||||||
|
}
|
||||||
|
if raw.ConsoleLog.LLM == nil {
|
||||||
|
changed = true
|
||||||
|
} else {
|
||||||
|
cfg.ConsoleLog.LLM = *raw.ConsoleLog.LLM
|
||||||
|
}
|
||||||
|
if raw.ConsoleLog.SQL == nil {
|
||||||
|
changed = true
|
||||||
|
} else {
|
||||||
|
cfg.ConsoleLog.SQL = *raw.ConsoleLog.SQL
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return cfg, changed
|
return cfg, changed
|
||||||
|
|||||||
+15
-2
@@ -4,6 +4,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
@@ -11,6 +12,7 @@ import (
|
|||||||
"github.com/glebarez/sqlite"
|
"github.com/glebarez/sqlite"
|
||||||
"gorm.io/driver/mysql"
|
"gorm.io/driver/mysql"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
|
gormlogger "gorm.io/gorm/logger"
|
||||||
|
|
||||||
"meshtastic_mqtt_server/internal/config"
|
"meshtastic_mqtt_server/internal/config"
|
||||||
)
|
)
|
||||||
@@ -571,7 +573,7 @@ func (TracerouteRecord) TableName() string {
|
|||||||
return "traceroute"
|
return "traceroute"
|
||||||
}
|
}
|
||||||
|
|
||||||
func OpenStore(cfg config.DatabaseConfig) (*Store, error) {
|
func OpenStore(cfg config.DatabaseConfig, consoleLog bool) (*Store, error) {
|
||||||
var dialector gorm.Dialector
|
var dialector gorm.Dialector
|
||||||
switch cfg.Driver {
|
switch cfg.Driver {
|
||||||
case config.DriverSQLite:
|
case config.DriverSQLite:
|
||||||
@@ -585,7 +587,18 @@ func OpenStore(cfg config.DatabaseConfig) (*Store, error) {
|
|||||||
return nil, fmt.Errorf("unsupported database driver %q", cfg.Driver)
|
return nil, fmt.Errorf("unsupported database driver %q", cfg.Driver)
|
||||||
}
|
}
|
||||||
|
|
||||||
db, err := gorm.Open(dialector, &gorm.Config{})
|
logLevel := gormlogger.Warn
|
||||||
|
if !consoleLog {
|
||||||
|
logLevel = gormlogger.Silent
|
||||||
|
}
|
||||||
|
db, err := gorm.Open(dialector, &gorm.Config{
|
||||||
|
Logger: gormlogger.New(log.New(os.Stderr, "\r\n", log.LstdFlags), gormlogger.Config{
|
||||||
|
SlowThreshold: 200 * time.Millisecond,
|
||||||
|
LogLevel: logLevel,
|
||||||
|
IgnoreRecordNotFoundError: true,
|
||||||
|
Colorful: false,
|
||||||
|
}),
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("open %s database: %w", cfg.Driver, err)
|
return nil, fmt.Errorf("open %s database: %w", cfg.Driver, err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -978,7 +978,7 @@ func openTestStore(t *testing.T) *Store {
|
|||||||
st, err := OpenStore(config.DatabaseConfig{
|
st, err := OpenStore(config.DatabaseConfig{
|
||||||
Driver: config.DriverSQLite,
|
Driver: config.DriverSQLite,
|
||||||
SQLite: config.SQLiteConfig{Path: filepath.Join(t.TempDir(), "mesh_mqtt_go.db")},
|
SQLite: config.SQLiteConfig{Path: filepath.Join(t.TempDir(), "mesh_mqtt_go.db")},
|
||||||
})
|
}, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("OpenStore() error = %v", err)
|
t.Fatalf("OpenStore() error = %v", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ func OpenStore(t *testing.T) *store.Store {
|
|||||||
st, err := store.OpenStore(config.DatabaseConfig{
|
st, err := store.OpenStore(config.DatabaseConfig{
|
||||||
Driver: config.DriverSQLite,
|
Driver: config.DriverSQLite,
|
||||||
SQLite: config.SQLiteConfig{Path: filepath.Join(t.TempDir(), "mesh_mqtt_go.db")},
|
SQLite: config.SQLiteConfig{Path: filepath.Join(t.TempDir(), "mesh_mqtt_go.db")},
|
||||||
})
|
}, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("OpenStore() error = %v", err)
|
t.Fatalf("OpenStore() error = %v", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ func TestMapTileProxyFetchesAndCaches(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cacheDir := t.TempDir()
|
cacheDir := t.TempDir()
|
||||||
router := NewRouter(configpkg.WebConfig{StaticDir: t.TempDir(), MapTileCacheDir: cacheDir}, st, nil, nil, nil, nil, nil, nil)
|
router := NewRouter(configpkg.WebConfig{StaticDir: t.TempDir(), MapTileCacheDir: cacheDir}, false, st, nil, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
url := "/api/map/" + row.URLTemplateHash + "?x=1&y=2&z=3"
|
url := "/api/map/" + row.URLTemplateHash + "?x=1&y=2&z=3"
|
||||||
for i := 0; i < 2; i++ {
|
for i := 0; i < 2; i++ {
|
||||||
@@ -75,7 +75,7 @@ func TestMapTileProxyRejectsInvalidCoordinates(t *testing.T) {
|
|||||||
t.Fatalf("CreateMapTileSource() error = %v", err)
|
t.Fatalf("CreateMapTileSource() error = %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
router := NewRouter(configpkg.WebConfig{StaticDir: t.TempDir(), MapTileCacheDir: t.TempDir()}, st, nil, nil, nil, nil, nil, nil)
|
router := NewRouter(configpkg.WebConfig{StaticDir: t.TempDir(), MapTileCacheDir: t.TempDir()}, false, st, nil, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
cases := []string{
|
cases := []string{
|
||||||
"/api/map/" + row.URLTemplateHash + "?y=0&z=0",
|
"/api/map/" + row.URLTemplateHash + "?y=0&z=0",
|
||||||
@@ -106,7 +106,7 @@ func TestMapTileProxyUnknownAndDisabledSource(t *testing.T) {
|
|||||||
t.Fatalf("CreateMapTileSource(proxy disabled) error = %v", err)
|
t.Fatalf("CreateMapTileSource(proxy disabled) error = %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
router := NewRouter(configpkg.WebConfig{StaticDir: t.TempDir(), MapTileCacheDir: t.TempDir()}, st, nil, nil, nil, nil, nil, nil)
|
router := NewRouter(configpkg.WebConfig{StaticDir: t.TempDir(), MapTileCacheDir: t.TempDir()}, false, st, nil, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
cases := []string{
|
cases := []string{
|
||||||
"/api/map/not-a-hash?x=0&y=0&z=0",
|
"/api/map/not-a-hash?x=0&y=0&z=0",
|
||||||
@@ -147,7 +147,7 @@ func TestMapTileProxyUpstreamStatus(t *testing.T) {
|
|||||||
t.Fatalf("CreateMapTileSource(500) error = %v", err)
|
t.Fatalf("CreateMapTileSource(500) error = %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
router := NewRouter(configpkg.WebConfig{StaticDir: t.TempDir(), MapTileCacheDir: t.TempDir()}, st, nil, nil, nil, nil, nil, nil)
|
router := NewRouter(configpkg.WebConfig{StaticDir: t.TempDir(), MapTileCacheDir: t.TempDir()}, false, st, nil, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
url string
|
url string
|
||||||
|
|||||||
+4
-4
@@ -27,10 +27,10 @@ import (
|
|||||||
"meshtastic_mqtt_server/internal/webutil"
|
"meshtastic_mqtt_server/internal/webutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewHTTPServer(cfg configpkg.WebConfig, store *storepkg.Store, sessions *auth.Manager, mqttStatus MQTTStatusProvider, blocking *blockingpkg.Cache, forwarder mqttforwardpkg.Reloader, settings *rspkg.Cache, botSender botpkg.TextSender) *http.Server {
|
func NewHTTPServer(cfg configpkg.WebConfig, consoleLog bool, store *storepkg.Store, sessions *auth.Manager, mqttStatus MQTTStatusProvider, blocking *blockingpkg.Cache, forwarder mqttforwardpkg.Reloader, settings *rspkg.Cache, botSender botpkg.TextSender) *http.Server {
|
||||||
return &http.Server{
|
return &http.Server{
|
||||||
Addr: net.JoinHostPort(cfg.Host, strconv.Itoa(cfg.Port)),
|
Addr: net.JoinHostPort(cfg.Host, strconv.Itoa(cfg.Port)),
|
||||||
Handler: NewRouter(cfg, store, sessions, mqttStatus, blocking, forwarder, settings, botSender),
|
Handler: NewRouter(cfg, consoleLog, store, sessions, mqttStatus, blocking, forwarder, settings, botSender),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,9 +60,9 @@ func ServeUnixSocket(server *http.Server, socketPath string) error {
|
|||||||
return server.Serve(listener)
|
return server.Serve(listener)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRouter(cfg configpkg.WebConfig, store *storepkg.Store, sessions *auth.Manager, mqttStatus MQTTStatusProvider, blocking *blockingpkg.Cache, forwarder mqttforwardpkg.Reloader, settings *rspkg.Cache, botSender botpkg.TextSender) *gin.Engine {
|
func NewRouter(cfg configpkg.WebConfig, consoleLog bool, store *storepkg.Store, sessions *auth.Manager, mqttStatus MQTTStatusProvider, blocking *blockingpkg.Cache, forwarder mqttforwardpkg.Reloader, settings *rspkg.Cache, botSender botpkg.TextSender) *gin.Engine {
|
||||||
r := gin.New()
|
r := gin.New()
|
||||||
if cfg.ConsoleLog {
|
if consoleLog {
|
||||||
r.Use(gin.Logger(), gin.Recovery())
|
r.Use(gin.Logger(), gin.Recovery())
|
||||||
} else {
|
} else {
|
||||||
r.Use(gin.Recovery())
|
r.Use(gin.Recovery())
|
||||||
|
|||||||
@@ -221,7 +221,7 @@ func parseArgs() (*configpkg.Config, error) {
|
|||||||
|
|
||||||
// run 创建 MQTT broker 和 Web 服务,并阻塞等待退出信号。
|
// run 创建 MQTT broker 和 Web 服务,并阻塞等待退出信号。
|
||||||
func run(cfg *configpkg.Config) error {
|
func run(cfg *configpkg.Config) error {
|
||||||
store, err := storepkg.OpenStore(cfg.Database)
|
store, err := storepkg.OpenStore(cfg.Database, cfg.ConsoleLog.SQL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -306,7 +306,7 @@ func run(cfg *configpkg.Config) error {
|
|||||||
|
|
||||||
aiService, err = ai.NewService(ai.Config{
|
aiService, err = ai.NewService(ai.Config{
|
||||||
LLMProviders: providerConfigs,
|
LLMProviders: providerConfigs,
|
||||||
DataDir: cfg.DataDir,
|
DataDir: cfg.AI.DataDir,
|
||||||
Enabled: cfg.AI.Enabled,
|
Enabled: cfg.AI.Enabled,
|
||||||
ToolConfigStore: store,
|
ToolConfigStore: store,
|
||||||
}, store.DB(), botSenderAdapter)
|
}, store.DB(), botSenderAdapter)
|
||||||
@@ -332,7 +332,7 @@ func run(cfg *configpkg.Config) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
mqttStatus := webpkg.MQTTRuntimeStatus{Server: server, Address: mqttAddr, TLS: cfg.MQTT.TLS.Enabled, Stats: messageStats, DBQueue: dbQueue}
|
mqttStatus := webpkg.MQTTRuntimeStatus{Server: server, Address: mqttAddr, TLS: cfg.MQTT.TLS.Enabled, Stats: messageStats, DBQueue: dbQueue}
|
||||||
handler := webpkg.NewRouter(cfg.Web, store, sessions, mqttStatus, blocking, forwardManager, settings, botSender)
|
handler := webpkg.NewRouter(cfg.Web, cfg.ConsoleLog.Web, store, sessions, mqttStatus, blocking, forwardManager, settings, botSender)
|
||||||
webAddresses := []string{}
|
webAddresses := []string{}
|
||||||
if cfg.Web.PortEnabled {
|
if cfg.Web.PortEnabled {
|
||||||
httpServer := &http.Server{
|
httpServer := &http.Server{
|
||||||
|
|||||||
Reference in New Issue
Block a user