OpenAI-compatible AI gateway with Vue admin UI, multi-provider egress, ingress key governance, monitoring, and security controls. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,136 @@
|
||||
package sqlite
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/rose_cat707/luminary/internal/auth"
|
||||
"github.com/rose_cat707/luminary/internal/model"
|
||||
"github.com/rose_cat707/luminary/internal/settings"
|
||||
"github.com/rose_cat707/luminary/internal/store/cache"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type Repository struct {
|
||||
db *gorm.DB
|
||||
cache *cache.Store
|
||||
}
|
||||
|
||||
func NewRepository(store *Store, c *cache.Store) *Repository {
|
||||
return &Repository{db: store.DB(), cache: c}
|
||||
}
|
||||
|
||||
func (r *Repository) BootstrapFromDB() error {
|
||||
var providers []model.Provider
|
||||
if err := r.db.Find(&providers).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
r.cache.LoadProviders(providers)
|
||||
|
||||
var keys []model.ProviderKey
|
||||
if err := r.db.Find(&keys).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
r.cache.LoadProviderKeys(keys)
|
||||
|
||||
var models []model.ModelEntry
|
||||
if err := r.db.Find(&models).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
r.cache.LoadModels(models)
|
||||
|
||||
var ingress []model.IngressKey
|
||||
if err := r.db.Find(&ingress).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
r.cache.LoadIngressKeys(ingress)
|
||||
|
||||
var rules []model.IPRule
|
||||
if err := r.db.Find(&rules).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
r.cache.LoadIPRules(rules)
|
||||
|
||||
var routing []model.RoutingRule
|
||||
if err := r.db.Find(&routing).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
r.cache.LoadRoutingRules(routing)
|
||||
r.PruneUsageRecords(model.MaxUsageLogRecords)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Repository) EnsureAdmin() error {
|
||||
var count int64
|
||||
r.db.Model(&model.AdminUser{}).Count(&count)
|
||||
if count > 0 {
|
||||
return nil
|
||||
}
|
||||
username, password := settings.LegacyAdminCredentials()
|
||||
hash, err := auth.HashPassword(password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return r.db.Create(&model.AdminUser{Username: username, PasswordHash: hash}).Error
|
||||
}
|
||||
|
||||
func (r *Repository) FlushUsage(deltas []cache.UsageDelta) {
|
||||
for _, d := range deltas {
|
||||
r.db.Create(&model.UsageRecord{
|
||||
IngressKeyID: d.IngressKeyID,
|
||||
ProviderID: d.ProviderID,
|
||||
ProviderKeyID: d.ProviderKeyID,
|
||||
ClientIP: d.ClientIP,
|
||||
Endpoint: d.Endpoint,
|
||||
Model: d.Model,
|
||||
TokensIn: d.TokensIn,
|
||||
TokensOut: d.TokensOut,
|
||||
Cost: d.Cost,
|
||||
LatencyMs: d.LatencyMs,
|
||||
Status: d.Status,
|
||||
Stream: d.Stream,
|
||||
ErrorMsg: d.ErrorMsg,
|
||||
RequestBody: d.RequestBody,
|
||||
ResponseBody: d.ResponseBody,
|
||||
CreatedAt: time.Now(),
|
||||
})
|
||||
if d.IngressKeyID > 0 {
|
||||
r.db.Model(&model.IngressKey{}).Where("id = ?", d.IngressKeyID).Updates(map[string]any{
|
||||
"budget_used": gorm.Expr("budget_used + ?", d.BudgetUsed),
|
||||
"request_count": gorm.Expr("request_count + ?", d.Requests),
|
||||
"token_count": gorm.Expr("token_count + ?", d.Tokens),
|
||||
})
|
||||
}
|
||||
if d.ProviderKeyID > 0 {
|
||||
r.db.Model(&model.ProviderKey{}).Where("id = ?", d.ProviderKeyID).Updates(map[string]any{
|
||||
"requests_today": gorm.Expr("requests_today + ?", d.Requests),
|
||||
"tokens_today": gorm.Expr("tokens_today + ?", d.Tokens),
|
||||
})
|
||||
}
|
||||
}
|
||||
r.PruneUsageRecords(model.MaxUsageLogRecords)
|
||||
}
|
||||
|
||||
func (r *Repository) RecordRequestLog(rec model.UsageRecord) {
|
||||
if rec.CreatedAt.IsZero() {
|
||||
rec.CreatedAt = time.Now()
|
||||
}
|
||||
r.db.Create(&rec)
|
||||
r.PruneUsageRecords(model.MaxUsageLogRecords)
|
||||
}
|
||||
|
||||
func (r *Repository) PruneUsageRecords(max int) {
|
||||
if max <= 0 {
|
||||
return
|
||||
}
|
||||
var count int64
|
||||
r.db.Model(&model.UsageRecord{}).Count(&count)
|
||||
if count <= int64(max) {
|
||||
return
|
||||
}
|
||||
var keepIDs []uint
|
||||
r.db.Model(&model.UsageRecord{}).Order("created_at desc").Limit(max).Pluck("id", &keepIDs)
|
||||
if len(keepIDs) == 0 {
|
||||
return
|
||||
}
|
||||
r.db.Where("id NOT IN ?", keepIDs).Delete(&model.UsageRecord{})
|
||||
}
|
||||
Reference in New Issue
Block a user