mirror of
https://gitcode.com/JianFeeeee/ModelRouter.git
synced 2026-09-20 08:57:57 +00:00
feat(keys): role-based gateway keys with admin management UI and per-user model scope
This commit is contained in:
@ -45,13 +45,15 @@ type agrRow struct {
|
||||
// Stats collects per-key / per-model / per-source aggregates plus a bounded
|
||||
// ring of raw request records, all guarded by one mutex.
|
||||
type Stats struct {
|
||||
mu sync.Mutex
|
||||
active int64
|
||||
byKey map[string]*Stat
|
||||
byModel map[string]*Stat
|
||||
bySrc map[string]*Stat
|
||||
recs []Req
|
||||
maxRecs int
|
||||
mu sync.Mutex
|
||||
active int64
|
||||
byKey map[string]*Stat
|
||||
byModel map[string]*Stat
|
||||
bySrc map[string]*Stat
|
||||
byKeyModel map[string]map[string]*Stat
|
||||
byKeySrc map[string]map[string]*Stat
|
||||
recs []Req
|
||||
maxRecs int
|
||||
}
|
||||
|
||||
func NewStats(maxRecords int) *Stats {
|
||||
@ -59,10 +61,12 @@ func NewStats(maxRecords int) *Stats {
|
||||
maxRecords = 3000
|
||||
}
|
||||
return &Stats{
|
||||
byKey: map[string]*Stat{},
|
||||
byModel: map[string]*Stat{},
|
||||
bySrc: map[string]*Stat{},
|
||||
maxRecs: maxRecords,
|
||||
byKey: map[string]*Stat{},
|
||||
byModel: map[string]*Stat{},
|
||||
bySrc: map[string]*Stat{},
|
||||
byKeyModel: map[string]map[string]*Stat{},
|
||||
byKeySrc: map[string]map[string]*Stat{},
|
||||
maxRecs: maxRecords,
|
||||
}
|
||||
}
|
||||
|
||||
@ -107,6 +111,18 @@ func (s *Stats) Record(r Req) {
|
||||
inc(s.byKey, r.Key, r)
|
||||
inc(s.byModel, r.Model, r)
|
||||
inc(s.bySrc, r.Source, r)
|
||||
km := s.byKeyModel[r.Key]
|
||||
if km == nil {
|
||||
km = map[string]*Stat{}
|
||||
s.byKeyModel[r.Key] = km
|
||||
}
|
||||
inc(km, r.Model, r)
|
||||
ks := s.byKeySrc[r.Key]
|
||||
if ks == nil {
|
||||
ks = map[string]*Stat{}
|
||||
s.byKeySrc[r.Key] = ks
|
||||
}
|
||||
inc(ks, r.Source, r)
|
||||
s.recs = append(s.recs, r)
|
||||
if len(s.recs) > s.maxRecs {
|
||||
s.recs = s.recs[len(s.recs)-s.maxRecs:]
|
||||
@ -132,8 +148,9 @@ type StatsRow struct {
|
||||
Stat
|
||||
}
|
||||
|
||||
// Snapshot returns the whole dashboard payload.
|
||||
func (s *Stats) Snapshot(limit int) map[string]interface{} {
|
||||
// Snapshot returns the whole dashboard payload; when key != "" the records
|
||||
// and aggregate views are restricted to that gateway key.
|
||||
func (s *Stats) Snapshot(limit int, key string) map[string]interface{} {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
if limit <= 0 {
|
||||
@ -143,8 +160,26 @@ func (s *Stats) Snapshot(limit int) map[string]interface{} {
|
||||
if len(s.recs) > limit {
|
||||
start = len(s.recs) - limit
|
||||
}
|
||||
recs := s.recs[start:]
|
||||
if key != "" {
|
||||
filt := recs[:0]
|
||||
for _, r := range recs {
|
||||
if r.Key == key {
|
||||
filt = append(filt, r)
|
||||
}
|
||||
}
|
||||
recs = filt
|
||||
}
|
||||
var total Stat
|
||||
for _, a := range s.byModel {
|
||||
var byModel, byKey, bySrc map[string]*Stat
|
||||
if key == "" {
|
||||
byKey, byModel, bySrc = s.byKey, s.byModel, s.bySrc
|
||||
} else {
|
||||
byKey = map[string]*Stat{key: s.byKey[key]}
|
||||
byModel = s.byKeyModel[key]
|
||||
bySrc = s.byKeySrc[key]
|
||||
}
|
||||
for _, a := range byModel {
|
||||
total.Reqs += a.Reqs
|
||||
total.OK += a.OK
|
||||
total.Err += a.Err
|
||||
@ -159,9 +194,9 @@ func (s *Stats) Snapshot(limit int) map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"active": s.active,
|
||||
"total": total,
|
||||
"by_key": rows(s.byKey),
|
||||
"by_model": rows(s.byModel),
|
||||
"by_source": rows(s.bySrc),
|
||||
"records": append([]Req(nil), s.recs[start:]...),
|
||||
"by_key": rows(byKey),
|
||||
"by_model": rows(byModel),
|
||||
"by_source": rows(bySrc),
|
||||
"records": append([]Req(nil), recs...),
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user