mirror of
https://gitcode.com/JianFeeeee/ModelRouter.git
synced 2026-09-21 09:28:00 +00:00
feat: config.yaml only, OpenRouter free models, remove runtime.json config
- Move all config (auto rules, keys) from runtime.json to config.yaml - Store now only holds runtime sources (WebUI-created) - Add OpenRouter free models to config.yaml - One-time migration from legacy runtime.json on startup - Fix gateway tests for new config structure - Update core.go with migrateFromRuntime, saveConfig, seedKeys/seedAuto - Remove SaveKey/KeyByValue/AutoRules from Store - Add Config.Save() with YAML marshaling - Update WebUI admin keys visibility (show all keys including admin) - Bump binary to 11MB with luajit
This commit is contained in:
@ -1,3 +1,8 @@
|
||||
// Package config — runtime source overlay.
|
||||
//
|
||||
// Only runtime-sourced (WebUI-created) sources live in the JSON store file;
|
||||
// auto rules and gateway keys now live in config.yaml. The store also tracks
|
||||
// deleted sources/adapters so a restart does not resurrect them.
|
||||
package config
|
||||
|
||||
import (
|
||||
@ -7,8 +12,8 @@ import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Store persists web-UI editable runtime state (sources added/edited) to a
|
||||
// JSON file so edits survive restarts. Base YAML sources are merged underneath.
|
||||
// Store persists web-UI editable runtime sources to a JSON file so edits
|
||||
// survive restarts. Base YAML sources are merged underneath.
|
||||
type Store struct {
|
||||
mu sync.Mutex
|
||||
path string
|
||||
@ -26,8 +31,7 @@ func NewStore(path string) *Store {
|
||||
return s
|
||||
}
|
||||
|
||||
// SecretBox exposes the box used for persist-time encryption of source
|
||||
// api_key / header values read from other places (e.g. YAML). May be nil.
|
||||
// SecretBox exposes the box used for persist-time encryption. May be nil.
|
||||
func (s *Store) SecretBox() *SecretBox {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
@ -112,6 +116,7 @@ func (s *Store) Remove(name string) (bool, error) {
|
||||
return removed, s.persistLocked()
|
||||
}
|
||||
|
||||
// DeletedSources returns the set of source names that were deleted via UI.
|
||||
func (s *Store) DeletedSources() map[string]bool {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
@ -122,6 +127,26 @@ func (s *Store) DeletedSources() map[string]bool {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeletedAdapters returns the set of adapter names that were deleted via UI.
|
||||
func (s *Store) DeletedAdapters() map[string]bool {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
out := map[string]bool{}
|
||||
for _, name := range s.data.DeletedAdapters {
|
||||
out[name] = true
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// LoadLegacy returns the full legacy RuntimeConfig from the file (used for
|
||||
// one-time migration into config.yaml). Returns nil if file is missing.
|
||||
func (s *Store) LoadLegacy() *RuntimeConfig {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
cp := s.data
|
||||
return &cp
|
||||
}
|
||||
|
||||
func (s *Store) persistLocked() error {
|
||||
if s.box != nil {
|
||||
s.encryptLocked()
|
||||
@ -139,8 +164,6 @@ func (s *Store) persistLocked() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// encryptLocked seals sensitive fields for the write; decryptLocked runs
|
||||
// right after marshaling so in-memory data stays plaintext.
|
||||
func (s *Store) encryptLocked() {
|
||||
for i := range s.data.Sources {
|
||||
if v, err := s.box.Encrypt(s.data.Sources[i].APIKey); err == nil {
|
||||
@ -181,75 +204,3 @@ func removeString(list []string, s string) []string {
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// ListKeys returns the persisted gateway keys.
|
||||
func (s *Store) ListKeys() []GWKey {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
out := make([]GWKey, len(s.data.Keys))
|
||||
copy(out, s.data.Keys)
|
||||
return out
|
||||
}
|
||||
|
||||
// KeyByValue looks up a gateway key record by its secret value.
|
||||
func (s *Store) KeyByValue(key string) (GWKey, bool) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
for _, k := range s.data.Keys {
|
||||
if k.Key == key {
|
||||
return k, true
|
||||
}
|
||||
}
|
||||
return GWKey{}, false
|
||||
}
|
||||
|
||||
// SaveKey upserts a gateway key record and persists.
|
||||
func (s *Store) SaveKey(k GWKey) error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
for i := range s.data.Keys {
|
||||
if s.data.Keys[i].Key == k.Key {
|
||||
s.data.Keys[i] = k
|
||||
return s.persistLocked()
|
||||
}
|
||||
}
|
||||
s.data.Keys = append(s.data.Keys, k)
|
||||
return s.persistLocked()
|
||||
}
|
||||
|
||||
// DeleteKey removes a gateway key record and persists.
|
||||
func (s *Store) DeleteKey(key string) (bool, error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
kept := s.data.Keys[:0]
|
||||
removed := false
|
||||
for _, k := range s.data.Keys {
|
||||
if k.Key == key {
|
||||
removed = true
|
||||
continue
|
||||
}
|
||||
kept = append(kept, k)
|
||||
}
|
||||
if !removed {
|
||||
return false, nil
|
||||
}
|
||||
s.data.Keys = kept
|
||||
return true, s.persistLocked()
|
||||
}
|
||||
|
||||
// AutoRules returns the persisted AUTO scheduling slots.
|
||||
func (s *Store) AutoRules() []ModelScope {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
out := make([]ModelScope, len(s.data.Auto))
|
||||
copy(out, s.data.Auto)
|
||||
return out
|
||||
}
|
||||
|
||||
// SaveAutoRules persists the AUTO scheduling slots.
|
||||
func (s *Store) SaveAutoRules(entries []ModelScope) error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
s.data.Auto = entries
|
||||
return s.persistLocked()
|
||||
}
|
||||
Reference in New Issue
Block a user