package db import ( "database/sql" "fmt" "time" "github.com/google/uuid" ) type APIKey struct { ID int64 `json:"id"` Key string `json:"key"` Name string `json:"name"` Email string `json:"email"` Plan string `json:"plan"` RateLimit int `json:"rate_limit"` RequestsToday int `json:"requests_today"` RequestsMonth int `json:"requests_month"` LastRequestAt *time.Time `json:"last_request_at,omitempty"` CreatedAt time.Time `json:"created_at"` ExpiresAt *time.Time `json:"expires_at,omitempty"` Active bool `json:"active"` } type UsageStats struct { TotalRequests int `json:"total_requests"` DailyBreakdown []DailyUsage `json:"daily_breakdown"` TopEndpoints []EndpointStat `json:"top_endpoints"` } type DailyUsage struct { Date string `json:"date"` Requests int `json:"requests"` } type EndpointStat struct { Endpoint string `json:"endpoint"` Requests int `json:"requests"` AvgLatencyMs float64 `json:"avg_latency_ms"` } func planRateLimit(plan string) int { switch plan { case "bronze": return 100 case "gold": return 500 case "platinum": return 2000 default: return 30 } } func (d *DB) CreateAPIKey(name, email, plan string) (*APIKey, error) { key := uuid.New().String() rl := planRateLimit(plan) res, err := d.Conn.Exec( `INSERT INTO api_keys (key, name, email, plan, rate_limit) VALUES (?, ?, ?, ?, ?)`, key, name, email, plan, rl, ) if err != nil { return nil, fmt.Errorf("create api key: %w", err) } id, _ := res.LastInsertId() return &APIKey{ ID: id, Key: key, Name: name, Email: email, Plan: plan, RateLimit: rl, Active: true, CreatedAt: time.Now(), }, nil } func (d *DB) CreateAPIKeyWithValue(key, name, email, plan string) (*APIKey, error) { rl := planRateLimit(plan) res, err := d.Conn.Exec( `INSERT OR IGNORE INTO api_keys (key, name, email, plan, rate_limit) VALUES (?, ?, ?, ?, ?)`, key, name, email, plan, rl, ) if err != nil { return nil, fmt.Errorf("create api key: %w", err) } id, _ := res.LastInsertId() return &APIKey{ ID: id, Key: key, Name: name, Email: email, Plan: plan, RateLimit: rl, Active: true, CreatedAt: time.Now(), }, nil } func (d *DB) GetAPIKey(key string) (*APIKey, error) { row := d.Conn.QueryRow( `SELECT id, key, name, email, plan, rate_limit, requests_today, requests_month, last_request_at, created_at, expires_at, active FROM api_keys WHERE key = ? AND active = 1`, key, ) var ak APIKey var lastReq, expiresAt sql.NullString var activeInt int err := row.Scan(&ak.ID, &ak.Key, &ak.Name, &ak.Email, &ak.Plan, &ak.RateLimit, &ak.RequestsToday, &ak.RequestsMonth, &lastReq, &ak.CreatedAt, &expiresAt, &activeInt) if err != nil { return nil, err } ak.Active = activeInt == 1 if lastReq.Valid { t, _ := time.Parse("2006-01-02 15:04:05", lastReq.String) ak.LastRequestAt = &t } if expiresAt.Valid { t, _ := time.Parse("2006-01-02 15:04:05", expiresAt.String) ak.ExpiresAt = &t } return &ak, nil } func (d *DB) ListAPIKeys() ([]APIKey, error) { rows, err := d.Conn.Query(`SELECT id, key, name, email, plan, rate_limit, requests_today, requests_month, active FROM api_keys`) if err != nil { return nil, err } defer rows.Close() var keys []APIKey for rows.Next() { var ak APIKey var activeInt int if err := rows.Scan(&ak.ID, &ak.Key, &ak.Name, &ak.Email, &ak.Plan, &ak.RateLimit, &ak.RequestsToday, &ak.RequestsMonth, &activeInt); err != nil { continue } ak.Active = activeInt == 1 keys = append(keys, ak) } return keys, nil } func (d *DB) UpdatePlan(keyID int64, plan string) error { rl := planRateLimit(plan) _, err := d.Conn.Exec(`UPDATE api_keys SET plan = ?, rate_limit = ? WHERE id = ?`, plan, rl, keyID) return err } func (d *DB) IncrementUsage(keyID int64, endpoint string, statusCode int, responseTimeMs int, ip string) error { now := time.Now() date := now.Format("2006-01-02") _, err := d.Conn.Exec(`UPDATE api_keys SET requests_today = requests_today + 1, requests_month = requests_month + 1, last_request_at = ? WHERE id = ?`, now, keyID) if err != nil { return err } _, err = d.Conn.Exec(`INSERT INTO usage_log (api_key_id, endpoint, method, status_code, response_time_ms, ip) VALUES (?, ?, 'GET', ?, ?, ?)`, keyID, endpoint, statusCode, responseTimeMs, ip) if err != nil { return err } _, err = d.Conn.Exec(`INSERT INTO usage_daily (api_key_id, date, requests) VALUES (?, ?, 1) ON CONFLICT(api_key_id, date) DO UPDATE SET requests = requests + 1`, keyID, date) return err } func (d *DB) GetUsageStats(keyID int64, from, to string) (*UsageStats, error) { stats := &UsageStats{} // Total d.Conn.QueryRow(`SELECT COALESCE(SUM(requests), 0) FROM usage_daily WHERE api_key_id = ? AND date >= ? AND date <= ?`, keyID, from, to).Scan(&stats.TotalRequests) // Daily rows, err := d.Conn.Query(`SELECT date, requests FROM usage_daily WHERE api_key_id = ? AND date >= ? AND date <= ? ORDER BY date`, keyID, from, to) if err == nil { defer rows.Close() for rows.Next() { var du DailyUsage rows.Scan(&du.Date, &du.Requests) stats.DailyBreakdown = append(stats.DailyBreakdown, du) } } // Top endpoints rows2, err := d.Conn.Query(`SELECT endpoint, COUNT(*) as cnt, AVG(response_time_ms) as avg_ms FROM usage_log WHERE api_key_id = ? AND created_at >= ? AND created_at <= ? GROUP BY endpoint ORDER BY cnt DESC LIMIT 10`, keyID, from, to+" 23:59:59") if err == nil { defer rows2.Close() for rows2.Next() { var es EndpointStat rows2.Scan(&es.Endpoint, &es.Requests, &es.AvgLatencyMs) stats.TopEndpoints = append(stats.TopEndpoints, es) } } return stats, nil } func (d *DB) ResetDailyCounters() error { _, err := d.Conn.Exec(`UPDATE api_keys SET requests_today = 0`) return err } func (d *DB) ResetMonthlyCounters() error { _, err := d.Conn.Exec(`UPDATE api_keys SET requests_month = 0`) return err }