mirror of
https://gitcode.com/JianFeeeee/ModelRouter.git
synced 2026-09-20 00:48:00 +00:00
feat(keys): role-based gateway keys with admin management UI and per-user model scope
This commit is contained in:
@ -4,9 +4,12 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"llmsproxy/internal/config"
|
||||
"llmsproxy/internal/lua"
|
||||
@ -44,12 +47,47 @@ func NewFromConfig(cfg *config.Config) (*Core, error) {
|
||||
return nil, fmt.Errorf("runtime store: %w", err)
|
||||
}
|
||||
c.scheduler = scheduler.New(buildRetries(cfg))
|
||||
if err := c.seedKeys(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := c.rebuildRegistry(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// seedKeys migrates the static config gateway_keys into the runtime store as
|
||||
// admin keys (once), so later UI-created keys can share the same store.
|
||||
func (c *Core) seedKeys() error {
|
||||
existing := map[string]bool{}
|
||||
for _, k := range c.store.ListKeys() {
|
||||
existing[k.Key] = true
|
||||
}
|
||||
changed := false
|
||||
for i, raw := range c.cfg.GatewayKeys {
|
||||
if raw == "" || existing[raw] {
|
||||
continue
|
||||
}
|
||||
name := "admin"
|
||||
if i > 0 {
|
||||
name = fmt.Sprintf("admin-%d", i+1)
|
||||
}
|
||||
if err := c.store.SaveKey(config.GWKey{
|
||||
Key: raw,
|
||||
Role: "admin",
|
||||
Name: name,
|
||||
CreatedAt: time.Now().Unix(),
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
changed = true
|
||||
}
|
||||
if changed {
|
||||
return c.store.Load()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func buildRetries(cfg *config.Config) int {
|
||||
return len(cfg.Sources) // allow fallback across all sources
|
||||
}
|
||||
@ -67,6 +105,60 @@ func (c *Core) GatewayKeys() []string { return c.cfg.GatewayKeys }
|
||||
|
||||
func (c *Core) Listen() string { return c.cfg.Listen }
|
||||
|
||||
// ---- gateway key management (web UI) ----
|
||||
|
||||
// ListKeys returns all gateway keys (admin view).
|
||||
func (c *Core) ListKeys() []config.GWKey { return c.store.ListKeys() }
|
||||
|
||||
// FindKey looks up a gateway key record by its secret value.
|
||||
func (c *Core) FindKey(key string) (config.GWKey, bool) { return c.store.KeyByValue(key) }
|
||||
|
||||
// CreateKey builds a new random gateway key and persists it.
|
||||
func (c *Core) CreateKey(name, role string, models []string, note string) (config.GWKey, error) {
|
||||
key := make([]byte, 16)
|
||||
if _, err := rand.Read(key); err != nil {
|
||||
return config.GWKey{}, err
|
||||
}
|
||||
rec := config.GWKey{
|
||||
Key: "sk-gw-" + hex.EncodeToString(key),
|
||||
Role: role,
|
||||
Name: name,
|
||||
Models: models,
|
||||
Note: note,
|
||||
CreatedAt: time.Now().Unix(),
|
||||
}
|
||||
if rec.Role == "" {
|
||||
rec.Role = "user"
|
||||
}
|
||||
if err := c.store.SaveKey(rec); err != nil {
|
||||
return config.GWKey{}, err
|
||||
}
|
||||
return rec, nil
|
||||
}
|
||||
|
||||
// UpdateKey mutates a key's name/role/model scope and persists it.
|
||||
func (c *Core) UpdateKey(key, name, role string, models []string, note string) (config.GWKey, error) {
|
||||
rec, ok := c.store.KeyByValue(key)
|
||||
if !ok {
|
||||
return config.GWKey{}, fmt.Errorf("key not found")
|
||||
}
|
||||
if name != "" {
|
||||
rec.Name = name
|
||||
}
|
||||
if role == "admin" || role == "user" {
|
||||
rec.Role = role
|
||||
}
|
||||
rec.Models = models
|
||||
rec.Note = note
|
||||
if err := c.store.SaveKey(rec); err != nil {
|
||||
return config.GWKey{}, err
|
||||
}
|
||||
return rec, nil
|
||||
}
|
||||
|
||||
// DeleteKey removes a key record; returns false if it did not exist.
|
||||
func (c *Core) DeleteKey(key string) (bool, error) { return c.store.DeleteKey(key) }
|
||||
|
||||
// Config exposes the underlying configuration (read-only usage).
|
||||
func (c *Core) Config() *config.Config { return c.cfg }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user