feat: server-driven configurator settings via qt_settings
Replaces hardcoded JS category filters and config-type buttons with server-pushed settings synced from qt_settings (MariaDB) → local_qt_settings (SQLite). - new table local_qt_settings (AutoMigrate) — synced after component sync - GET /api/configurator-settings returns config_types, tab_config, always_visible_tabs, required_categories with hardcoded fallbacks - new-config modal: type buttons rendered from server data (Сервер/СХД static fallback) - configurator: TAB_CONFIG and category filter driven by server; required-category badge on tabs - SyncQtSettings wired into SyncComponents and SyncAll handlers (non-fatal on old server) - bible-local/server-contract-qt-settings.md — contract for server-side agent - page titles: OFS → QFS Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -125,3 +125,102 @@ func (h *ComponentHandler) GetCategories(c *gin.Context) {
|
||||
|
||||
c.JSON(http.StatusOK, models.DefaultCategories)
|
||||
}
|
||||
|
||||
func (h *ComponentHandler) GetConfiguratorSettings(c *gin.Context) {
|
||||
s, _ := h.localDB.GetConfiguratorSettings()
|
||||
if s == nil {
|
||||
s = &localdb.ConfiguratorSettings{}
|
||||
}
|
||||
|
||||
if len(s.ConfigTypes) == 0 {
|
||||
s.ConfigTypes = defaultConfigTypes()
|
||||
}
|
||||
if len(s.TabConfig) == 0 {
|
||||
s.TabConfig = defaultTabConfig()
|
||||
}
|
||||
if len(s.AlwaysVisibleTabs) == 0 {
|
||||
s.AlwaysVisibleTabs = []string{"base", "storage", "pci"}
|
||||
}
|
||||
if len(s.RequiredCategories) == 0 {
|
||||
s.RequiredCategories = map[string][]string{"server": {"CPU", "MEM", "BB"}}
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, s)
|
||||
}
|
||||
|
||||
func defaultConfigTypes() []localdb.ConfigTypeDef {
|
||||
return []localdb.ConfigTypeDef{
|
||||
{
|
||||
Code: "server",
|
||||
NameRu: "Сервер",
|
||||
DisplayOrder: 10,
|
||||
Categories: []string{
|
||||
"MB", "CPU", "MEM", "RAID",
|
||||
"SSD", "HDD", "M2", "EDSFF", "HHHL",
|
||||
"GPU", "NIC", "HCA", "DPU", "HBA",
|
||||
"PSU", "PS", "ACC", "RISERS", "CARD", "BB",
|
||||
},
|
||||
},
|
||||
{
|
||||
Code: "storage",
|
||||
NameRu: "СХД",
|
||||
DisplayOrder: 20,
|
||||
Categories: []string{
|
||||
"DKC", "CPU", "MEM", "PS",
|
||||
"SSD", "HDD", "M2", "EDSFF", "HHHL",
|
||||
"NIC", "HBA", "HCA", "ACC", "CARD",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func defaultTabConfig() []localdb.TabDef {
|
||||
return []localdb.TabDef{
|
||||
{
|
||||
Key: "base",
|
||||
Label: "Base",
|
||||
SingleSelect: true,
|
||||
Categories: []string{"MB", "CPU", "MEM", "ENC", "DKC", "CTL"},
|
||||
},
|
||||
{
|
||||
Key: "storage",
|
||||
Label: "Storage",
|
||||
SingleSelect: false,
|
||||
Categories: []string{"RAID", "M2", "SSD", "HDD", "EDSFF", "HHHL"},
|
||||
Sections: []localdb.TabSection{
|
||||
{Title: "RAID Контроллеры", Categories: []string{"RAID"}},
|
||||
{Title: "Диски", Categories: []string{"M2", "SSD", "HDD", "EDSFF", "HHHL"}},
|
||||
},
|
||||
},
|
||||
{
|
||||
Key: "pci",
|
||||
Label: "PCI",
|
||||
SingleSelect: false,
|
||||
Categories: []string{"GPU", "DPU", "NIC", "HCA", "HBA", "HIC"},
|
||||
Sections: []localdb.TabSection{
|
||||
{Title: "GPU / DPU", Categories: []string{"GPU", "DPU"}},
|
||||
{Title: "NIC / HCA", Categories: []string{"NIC", "HCA"}},
|
||||
{Title: "HBA", Categories: []string{"HBA"}},
|
||||
{Title: "HIC", Categories: []string{"HIC"}},
|
||||
},
|
||||
},
|
||||
{
|
||||
Key: "power",
|
||||
Label: "Power",
|
||||
SingleSelect: false,
|
||||
Categories: []string{"PS", "PSU"},
|
||||
},
|
||||
{
|
||||
Key: "accessories",
|
||||
Label: "Accessories",
|
||||
SingleSelect: false,
|
||||
Categories: []string{"ACC", "CARD"},
|
||||
},
|
||||
{
|
||||
Key: "sw",
|
||||
Label: "SW",
|
||||
SingleSelect: false,
|
||||
Categories: []string{"SW"},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -203,6 +203,10 @@ func (h *SyncHandler) SyncComponents(c *gin.Context) {
|
||||
_ = h.localDB.SetComponentSyncResult("ok", "", now)
|
||||
h.localDB.AppendSyncLog("components", "ok", "", result.TotalSynced, now, result.Duration.Milliseconds())
|
||||
|
||||
if err := h.localDB.SyncQtSettings(mariaDB); err != nil {
|
||||
slog.Warn("qt_settings sync failed", "error", err)
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, SyncResultResponse{
|
||||
Success: true,
|
||||
Message: "Components synced successfully",
|
||||
@@ -339,6 +343,10 @@ func (h *SyncHandler) SyncAll(c *gin.Context) {
|
||||
h.localDB.AppendSyncLog("components", "ok", "", compResult.TotalSynced, compNow, compResult.Duration.Milliseconds())
|
||||
componentsSynced = compResult.TotalSynced
|
||||
|
||||
if err := h.localDB.SyncQtSettings(mariaDB); err != nil {
|
||||
slog.Warn("qt_settings sync failed", "error", err)
|
||||
}
|
||||
|
||||
// Sync pricelists
|
||||
plNow := time.Now()
|
||||
pricelistsSynced, err = h.syncService.SyncPricelists()
|
||||
|
||||
Reference in New Issue
Block a user