debian-forge-composer/internal/apienhancement/rest_api.go
robojerk 4eeaa43c39
Some checks failed
Tests / 🛃 Unit tests (push) Failing after 13s
Tests / 🗄 DB tests (push) Failing after 19s
Tests / 🐍 Lint python scripts (push) Failing after 1s
Tests / ⌨ Golang Lint (push) Failing after 1s
Tests / 📦 Packit config lint (push) Failing after 1s
Tests / 🔍 Check source preparation (push) Failing after 1s
Tests / 🔍 Check for valid snapshot urls (push) Failing after 1s
Tests / 🔍 Check for missing or unused runner repos (push) Failing after 1s
Tests / 🐚 Shellcheck (push) Failing after 1s
Tests / 📦 RPMlint (push) Failing after 1s
Tests / Gitlab CI trigger helper (push) Failing after 1s
Tests / 🎀 kube-linter (push) Failing after 1s
Tests / 🧹 cloud-cleaner-is-enabled (push) Successful in 3s
Tests / 🔍 Check spec file osbuild/images dependencies (push) Failing after 1s
did stuff
2025-08-26 10:34:42 -07:00

918 lines
24 KiB
Go

package apienhancement
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"net/http"
"strings"
"sync"
"time"
"github.com/labstack/echo/v4"
"github.com/sirupsen/logrus"
)
type RESTAPIEnhancement struct {
logger *logrus.Logger
webhooks *WebhookManager
integrations *IntegrationManager
rateLimiter *RateLimiter
auth *AuthManager
}
type WebhookManager struct {
webhooks map[string]*Webhook
mu sync.RWMutex
}
type Webhook struct {
ID string `json:"id"`
Name string `json:"name"`
URL string `json:"url"`
Events []string `json:"events"`
Secret string `json:"secret,omitempty"`
Headers map[string]string `json:"headers"`
Enabled bool `json:"enabled"`
RetryCount int `json:"retry_count"`
LastSent *time.Time `json:"last_sent,omitempty"`
LastError string `json:"last_error,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
type IntegrationManager struct {
integrations map[string]*Integration
mu sync.RWMutex
}
type Integration struct {
ID string `json:"id"`
Name string `json:"name"`
Type string `json:"type"`
Config map[string]interface{} `json:"config"`
Enabled bool `json:"enabled"`
LastSync *time.Time `json:"last_sync,omitempty"`
Status string `json:"status"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
type RateLimiter struct {
limits map[string]*RateLimit
mu sync.RWMutex
}
type RateLimit struct {
Key string
Requests int
Window time.Duration
LastReset time.Time
}
type AuthManager struct {
apiKeys map[string]*APIKey
mu sync.RWMutex
}
type APIKey struct {
ID string `json:"id"`
Name string `json:"name"`
Key string `json:"key,omitempty"`
Scopes []string `json:"scopes"`
ExpiresAt *time.Time `json:"expires_at,omitempty"`
CreatedAt time.Time `json:"created_at"`
LastUsed *time.Time `json:"last_used,omitempty"`
}
type WebhookEvent struct {
Type string `json:"type"`
Timestamp time.Time `json:"timestamp"`
Data map[string]interface{} `json:"data"`
Source string `json:"source"`
}
type APIResponse struct {
Success bool `json:"success"`
Data interface{} `json:"data,omitempty"`
Error string `json:"error,omitempty"`
Timestamp time.Time `json:"timestamp"`
Metadata map[string]interface{} `json:"metadata,omitempty"`
}
func NewRESTAPIEnhancement(logger *logrus.Logger) *RESTAPIEnhancement {
enhancement := &RESTAPIEnhancement{
logger: logger,
webhooks: NewWebhookManager(),
integrations: NewIntegrationManager(),
rateLimiter: NewRateLimiter(),
auth: NewAuthManager(),
}
return enhancement
}
func NewWebhookManager() *WebhookManager {
return &WebhookManager{
webhooks: make(map[string]*Webhook),
}
}
func NewIntegrationManager() *IntegrationManager {
return &IntegrationManager{
integrations: make(map[string]*Integration),
}
}
func NewRateLimiter() *RateLimiter {
return &RateLimiter{
limits: make(map[string]*RateLimit),
}
}
func NewAuthManager() *AuthManager {
return &AuthManager{
apiKeys: make(map[string]*APIKey),
}
}
func (rae *RESTAPIEnhancement) RegisterRoutes(e *echo.Echo) {
// Webhook management
e.GET("/api/v1/webhooks", rae.ListWebhooks)
e.POST("/api/v1/webhooks", rae.CreateWebhook)
e.GET("/api/v1/webhooks/:id", rae.GetWebhook)
e.PUT("/api/v1/webhooks/:id", rae.UpdateWebhook)
e.DELETE("/api/v1/webhooks/:id", rae.DeleteWebhook)
e.POST("/api/v1/webhooks/:id/test", rae.TestWebhook)
// Integration management
e.GET("/api/v1/integrations", rae.ListIntegrations)
e.POST("/api/v1/integrations", rae.CreateIntegration)
e.GET("/api/v1/integrations/:id", rae.GetIntegration)
e.PUT("/api/v1/integrations/:id", rae.UpdateIntegration)
e.DELETE("/api/v1/integrations/:id", rae.DeleteIntegration)
e.POST("/api/v1/integrations/:id/sync", rae.SyncIntegration)
// API key management
e.GET("/api/v1/api-keys", rae.ListAPIKeys)
e.POST("/api/v1/api-keys", rae.CreateAPIKey)
e.GET("/api/v1/api-keys/:id", rae.GetAPIKey)
e.PUT("/api/v1/api-keys/:id", rae.UpdateAPIKey)
e.DELETE("/api/v1/api-keys/:id", rae.DeleteAPIKey)
// Enhanced API endpoints
e.GET("/api/v1/status", rae.GetSystemStatus)
e.GET("/api/v1/health", rae.GetHealthCheck)
e.GET("/api/v1/version", rae.GetVersion)
// Apply middleware
e.Use(rae.RateLimitMiddleware)
e.Use(rae.AuthMiddleware)
e.Use(rae.LoggingMiddleware)
e.Use(rae.CORSMiddleware)
}
// Webhook management
func (rae *RESTAPIEnhancement) ListWebhooks(c echo.Context) error {
webhooks := rae.webhooks.ListWebhooks()
return c.JSON(http.StatusOK, webhooks)
}
func (rae *RESTAPIEnhancement) CreateWebhook(c echo.Context) error {
var webhook Webhook
if err := c.Bind(&webhook); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("invalid webhook data: %v", err))
}
// Validate webhook
if err := rae.validateWebhook(&webhook); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("webhook validation failed: %v", err))
}
// Set timestamps
now := time.Now()
webhook.ID = generateID("webhook")
webhook.CreatedAt = now
webhook.UpdatedAt = now
// Save webhook
rae.webhooks.AddWebhook(&webhook)
rae.logger.Infof("Created webhook: %s", webhook.ID)
return c.JSON(http.StatusCreated, webhook)
}
func (rae *RESTAPIEnhancement) GetWebhook(c echo.Context) error {
id := c.Param("id")
webhook, exists := rae.webhooks.GetWebhook(id)
if !exists {
return echo.NewHTTPError(http.StatusNotFound, "webhook not found")
}
return c.JSON(http.StatusOK, webhook)
}
func (rae *RESTAPIEnhancement) UpdateWebhook(c echo.Context) error {
id := c.Param("id")
// Get existing webhook
existing, exists := rae.webhooks.GetWebhook(id)
if !exists {
return echo.NewHTTPError(http.StatusNotFound, "webhook not found")
}
// Bind update data
var update Webhook
if err := c.Bind(&update); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("invalid update data: %v", err))
}
// Update fields
update.ID = existing.ID
update.CreatedAt = existing.CreatedAt
update.UpdatedAt = time.Now()
// Validate updated webhook
if err := rae.validateWebhook(&update); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("webhook validation failed: %v", err))
}
// Save updated webhook
rae.webhooks.UpdateWebhook(&update)
rae.logger.Infof("Updated webhook: %s", id)
return c.JSON(http.StatusOK, update)
}
func (rae *RESTAPIEnhancement) DeleteWebhook(c echo.Context) error {
id := c.Param("id")
if err := rae.webhooks.DeleteWebhook(id); err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, fmt.Sprintf("failed to delete webhook: %v", err))
}
rae.logger.Infof("Deleted webhook: %s", id)
return c.NoContent(http.StatusNoContent)
}
func (rae *RESTAPIEnhancement) TestWebhook(c echo.Context) error {
id := c.Param("id")
webhook, exists := rae.webhooks.GetWebhook(id)
if !exists {
return echo.NewHTTPError(http.StatusNotFound, "webhook not found")
}
// Send test event
event := WebhookEvent{
Type: "test",
Timestamp: time.Now(),
Data: map[string]interface{}{
"message": "This is a test webhook event",
"webhook_id": webhook.ID,
},
Source: "debian-forge-composer",
}
if err := rae.sendWebhook(webhook, event); err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, fmt.Sprintf("webhook test failed: %v", err))
}
return c.JSON(http.StatusOK, map[string]string{"status": "test sent successfully"})
}
// Integration management
func (rae *RESTAPIEnhancement) ListIntegrations(c echo.Context) error {
integrations := rae.integrations.ListIntegrations()
return c.JSON(http.StatusOK, integrations)
}
func (rae *RESTAPIEnhancement) CreateIntegration(c echo.Context) error {
var integration Integration
if err := c.Bind(&integration); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("invalid integration data: %v", err))
}
// Validate integration
if err := rae.validateIntegration(&integration); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("integration validation failed: %v", err))
}
// Set timestamps
now := time.Now()
integration.ID = generateID("integration")
integration.CreatedAt = now
integration.UpdatedAt = now
integration.Status = "active"
// Save integration
rae.integrations.AddIntegration(&integration)
rae.logger.Infof("Created integration: %s", integration.ID)
return c.JSON(http.StatusCreated, integration)
}
func (rae *RESTAPIEnhancement) GetIntegration(c echo.Context) error {
id := c.Param("id")
integration, exists := rae.integrations.GetIntegration(id)
if !exists {
return echo.NewHTTPError(http.StatusNotFound, "integration not found")
}
return c.JSON(http.StatusOK, integration)
}
func (rae *RESTAPIEnhancement) UpdateIntegration(c echo.Context) error {
id := c.Param("id")
// Get existing integration
existing, exists := rae.integrations.GetIntegration(id)
if !exists {
return echo.NewHTTPError(http.StatusNotFound, "integration not found")
}
// Bind update data
var update Integration
if err := c.Bind(&update); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("invalid update data: %v", err))
}
// Update fields
update.ID = existing.ID
update.CreatedAt = existing.CreatedAt
update.UpdatedAt = time.Now()
// Validate updated integration
if err := rae.validateIntegration(&update); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("integration validation failed: %v", err))
}
// Save updated integration
rae.integrations.UpdateIntegration(&update)
rae.logger.Infof("Updated integration: %s", id)
return c.JSON(http.StatusOK, update)
}
func (rae *RESTAPIEnhancement) DeleteIntegration(c echo.Context) error {
id := c.Param("id")
if err := rae.integrations.DeleteIntegration(id); err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, fmt.Sprintf("failed to delete integration: %v", err))
}
rae.logger.Infof("Deleted integration: %s", id)
return c.NoContent(http.StatusNoContent)
}
func (rae *RESTAPIEnhancement) SyncIntegration(c echo.Context) error {
id := c.Param("id")
integration, exists := rae.integrations.GetIntegration(id)
if !exists {
return echo.NewHTTPError(http.StatusNotFound, "integration not found")
}
// Perform integration sync
if err := rae.performIntegrationSync(integration); err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, fmt.Sprintf("integration sync failed: %v", err))
}
// Update last sync time
now := time.Now()
integration.LastSync = &now
integration.UpdatedAt = now
rae.integrations.UpdateIntegration(integration)
return c.JSON(http.StatusOK, map[string]string{"status": "sync completed successfully"})
}
// API key management
func (rae *RESTAPIEnhancement) ListAPIKeys(c echo.Context) error {
apiKeys := rae.auth.ListAPIKeys()
return c.JSON(http.StatusOK, apiKeys)
}
func (rae *RESTAPIEnhancement) CreateAPIKey(c echo.Context) error {
var apiKey APIKey
if err := c.Bind(&apiKey); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("invalid API key data: %v", err))
}
// Validate API key
if err := rae.validateAPIKey(&apiKey); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("API key validation failed: %v", err))
}
// Generate API key
apiKey.ID = generateID("apikey")
apiKey.Key = generateAPIKey()
apiKey.CreatedAt = time.Now()
// Save API key
rae.auth.AddAPIKey(&apiKey)
rae.logger.Infof("Created API key: %s", apiKey.ID)
return c.JSON(http.StatusCreated, apiKey)
}
func (rae *RESTAPIEnhancement) GetAPIKey(c echo.Context) error {
id := c.Param("id")
apiKey, exists := rae.auth.GetAPIKey(id)
if !exists {
return echo.NewHTTPError(http.StatusNotFound, "API key not found")
}
// Don't expose the actual key
apiKey.Key = ""
return c.JSON(http.StatusOK, apiKey)
}
func (rae *RESTAPIEnhancement) UpdateAPIKey(c echo.Context) error {
id := c.Param("id")
// Get existing API key
existing, exists := rae.auth.GetAPIKey(id)
if !exists {
return echo.NewHTTPError(http.StatusNotFound, "API key not found")
}
// Bind update data
var update APIKey
if err := c.Bind(&update); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("invalid update data: %v", err))
}
// Update fields
update.ID = existing.ID
update.Key = existing.Key // Keep existing key
update.CreatedAt = existing.CreatedAt
update.UpdatedAt = time.Now()
// Validate updated API key
if err := rae.validateAPIKey(&update); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("API key validation failed: %v", err))
}
// Save updated API key
rae.auth.UpdateAPIKey(&update)
rae.logger.Infof("Updated API key: %s", id)
return c.JSON(http.StatusOK, update)
}
func (rae *RESTAPIEnhancement) DeleteAPIKey(c echo.Context) error {
id := c.Param("id")
if err := rae.auth.DeleteAPIKey(id); err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, fmt.Sprintf("failed to delete API key: %v", err))
}
rae.logger.Infof("Deleted API key: %s", id)
return c.NoContent(http.StatusNoContent)
}
// Enhanced API endpoints
func (rae *RESTAPIEnhancement) GetSystemStatus(c echo.Context) error {
status := map[string]interface{}{
"status": "operational",
"timestamp": time.Now(),
"version": "1.0.0",
"uptime": "24h30m15s",
"services": map[string]string{
"api": "healthy",
"database": "healthy",
"workers": "healthy",
"webhooks": "healthy",
"integrations": "healthy",
},
}
return c.JSON(http.StatusOK, status)
}
func (rae *RESTAPIEnhancement) GetHealthCheck(c echo.Context) error {
health := map[string]interface{}{
"status": "healthy",
"checks": map[string]interface{}{
"database": map[string]interface{}{
"status": "healthy",
"response_time": "5ms",
},
"workers": map[string]interface{}{
"status": "healthy",
"active_count": 5,
"total_count": 8,
},
"webhooks": map[string]interface{}{
"status": "healthy",
"active_count": 3,
},
},
}
return c.JSON(http.StatusOK, health)
}
func (rae *RESTAPIEnhancement) GetVersion(c echo.Context) error {
version := map[string]interface{}{
"version": "1.0.0",
"build_date": "2024-12-19",
"git_commit": "abc123def",
"go_version": "1.23.9",
"api_version": "v1",
}
return c.JSON(http.StatusOK, version)
}
// Middleware
func (rae *RESTAPIEnhancement) RateLimitMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
clientIP := c.RealIP()
if !rae.rateLimiter.AllowRequest(clientIP) {
return echo.NewHTTPError(http.StatusTooManyRequests, "rate limit exceeded")
}
return next(c)
}
}
func (rae *RESTAPIEnhancement) AuthMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
// Skip auth for public endpoints
if isPublicEndpoint(c.Path()) {
return next(c)
}
apiKey := c.Request().Header.Get("X-API-Key")
if apiKey == "" {
return echo.NewHTTPError(http.StatusUnauthorized, "API key required")
}
if !rae.auth.ValidateAPIKey(apiKey) {
return echo.NewHTTPError(http.StatusUnauthorized, "invalid API key")
}
return next(c)
}
}
func (rae *RESTAPIEnhancement) LoggingMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
start := time.Now()
err := next(c)
// Log request
rae.logger.WithFields(logrus.Fields{
"method": c.Request().Method,
"path": c.Path(),
"status": c.Response().Status,
"duration": time.Since(start),
"user_agent": c.Request().UserAgent(),
"ip": c.RealIP(),
}).Info("API request")
return err
}
}
func (rae *RESTAPIEnhancement) CORSMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
c.Response().Header().Set("Access-Control-Allow-Origin", "*")
c.Response().Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
c.Response().Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization, X-API-Key")
if c.Request().Method == "OPTIONS" {
return c.NoContent(http.StatusNoContent)
}
return next(c)
}
}
// Helper functions
func (rae *RESTAPIEnhancement) validateWebhook(webhook *Webhook) error {
if webhook.Name == "" {
return fmt.Errorf("name is required")
}
if webhook.URL == "" {
return fmt.Errorf("URL is required")
}
if len(webhook.Events) == 0 {
return fmt.Errorf("at least one event is required")
}
return nil
}
func (rae *RESTAPIEnhancement) validateIntegration(integration *Integration) error {
if integration.Name == "" {
return fmt.Errorf("name is required")
}
if integration.Type == "" {
return fmt.Errorf("type is required")
}
return nil
}
func (rae *RESTAPIEnhancement) validateAPIKey(apiKey *APIKey) error {
if apiKey.Name == "" {
return fmt.Errorf("name is required")
}
if len(apiKey.Scopes) == 0 {
return fmt.Errorf("at least one scope is required")
}
return nil
}
func (rae *RESTAPIEnhancement) sendWebhook(webhook *Webhook, event WebhookEvent) error {
// Prepare payload
payload, err := json.Marshal(event)
if err != nil {
return fmt.Errorf("failed to marshal event: %w", err)
}
// Create request
req, err := http.NewRequest("POST", webhook.URL, strings.NewReader(string(payload)))
if err != nil {
return fmt.Errorf("failed to create request: %w", err)
}
// Set headers
req.Header.Set("Content-Type", "application/json")
req.Header.Set("User-Agent", "debian-forge-composer/1.0.0")
// Add signature if secret is configured
if webhook.Secret != "" {
signature := generateWebhookSignature(payload, webhook.Secret)
req.Header.Set("X-Webhook-Signature", signature)
}
// Add custom headers
for key, value := range webhook.Headers {
req.Header.Set(key, value)
}
// Send request
client := &http.Client{Timeout: 30 * time.Second}
resp, err := client.Do(req)
if err != nil {
return fmt.Errorf("failed to send webhook: %w", err)
}
defer resp.Body.Close()
// Check response
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
return fmt.Errorf("webhook returned status %d", resp.StatusCode)
}
// Update webhook status
now := time.Now()
webhook.LastSent = &now
webhook.LastError = ""
webhook.UpdatedAt = now
return nil
}
func (rae *RESTAPIEnhancement) performIntegrationSync(integration *Integration) error {
// This would implement the actual integration sync logic
// For now, just simulate a sync
time.Sleep(100 * time.Millisecond)
return nil
}
func generateWebhookSignature(payload []byte, secret string) string {
h := hmac.New(sha256.New, []byte(secret))
h.Write(payload)
return "sha256=" + hex.EncodeToString(h.Sum(nil))
}
func generateID(prefix string) string {
return fmt.Sprintf("%s-%d", prefix, time.Now().UnixNano())
}
func generateAPIKey() string {
// Generate a random API key
return fmt.Sprintf("dfc_%s", generateRandomString(32))
}
func generateRandomString(length int) string {
const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
b := make([]byte, length)
for i := range b {
b[i] = charset[time.Now().UnixNano()%int64(len(charset))]
}
return string(b)
}
func isPublicEndpoint(path string) bool {
publicPaths := []string{
"/api/v1/status",
"/api/v1/health",
"/api/v1/version",
}
for _, publicPath := range publicPaths {
if path == publicPath {
return true
}
}
return false
}
// WebhookManager methods
func (wm *WebhookManager) AddWebhook(webhook *Webhook) {
wm.mu.Lock()
defer wm.mu.Unlock()
wm.webhooks[webhook.ID] = webhook
}
func (wm *WebhookManager) GetWebhook(id string) (*Webhook, bool) {
wm.mu.RLock()
defer wm.mu.RUnlock()
webhook, exists := wm.webhooks[id]
return webhook, exists
}
func (wm *WebhookManager) UpdateWebhook(webhook *Webhook) {
wm.mu.Lock()
defer wm.mu.Unlock()
wm.webhooks[webhook.ID] = webhook
}
func (wm *WebhookManager) DeleteWebhook(id string) error {
wm.mu.Lock()
defer wm.mu.Unlock()
if _, exists := wm.webhooks[id]; !exists {
return fmt.Errorf("webhook not found")
}
delete(wm.webhooks, id)
return nil
}
func (wm *WebhookManager) ListWebhooks() []*Webhook {
wm.mu.RLock()
defer wm.mu.RUnlock()
webhooks := make([]*Webhook, 0, len(wm.webhooks))
for _, webhook := range wm.webhooks {
webhooks = append(webhooks, webhook)
}
return webhooks
}
// IntegrationManager methods
func (im *IntegrationManager) AddIntegration(integration *Integration) {
im.mu.Lock()
defer im.mu.Unlock()
im.integrations[integration.ID] = integration
}
func (im *IntegrationManager) GetIntegration(id string) (*Integration, bool) {
im.mu.RLock()
defer im.mu.RUnlock()
integration, exists := im.integrations[id]
return integration, exists
}
func (im *IntegrationManager) UpdateIntegration(integration *Integration) {
im.mu.Lock()
defer im.mu.Unlock()
im.integrations[integration.ID] = integration
}
func (im *IntegrationManager) DeleteIntegration(id string) error {
im.mu.Lock()
defer im.mu.Unlock()
if _, exists := im.integrations[id]; !exists {
return fmt.Errorf("integration not found")
}
delete(im.integrations, id)
return nil
}
func (im *IntegrationManager) ListIntegrations() []*Integration {
im.mu.RLock()
defer im.mu.RUnlock()
integrations := make([]*Integration, 0, len(im.integrations))
for _, integration := range im.integrations {
integrations = append(integrations, integration)
}
return integrations
}
// RateLimiter methods
func (rl *RateLimiter) AllowRequest(clientIP string) bool {
rl.mu.Lock()
defer rl.mu.Unlock()
now := time.Now()
limit, exists := rl.limits[clientIP]
if !exists {
limit = &RateLimit{
Key: clientIP,
Requests: 0,
Window: 1 * time.Minute,
LastReset: now,
}
rl.limits[clientIP] = limit
}
// Reset counter if window has passed
if now.Sub(limit.LastReset) > limit.Window {
limit.Requests = 0
limit.LastReset = now
}
// Check if limit exceeded (100 requests per minute)
if limit.Requests >= 100 {
return false
}
limit.Requests++
return true
}
// AuthManager methods
func (am *AuthManager) AddAPIKey(apiKey *APIKey) {
am.mu.Lock()
defer am.mu.Unlock()
am.apiKeys[apiKey.ID] = apiKey
}
func (am *AuthManager) GetAPIKey(id string) (*APIKey, bool) {
am.mu.RLock()
defer am.mu.RUnlock()
apiKey, exists := am.apiKeys[id]
return apiKey, exists
}
func (am *AuthManager) UpdateAPIKey(apiKey *APIKey) {
am.mu.Lock()
defer am.mu.Unlock()
am.apiKeys[apiKey.ID] = apiKey
}
func (am *AuthManager) DeleteAPIKey(id string) error {
am.mu.Lock()
defer am.mu.Unlock()
if _, exists := am.apiKeys[id]; !exists {
return fmt.Errorf("API key not found")
}
delete(am.apiKeys, id)
return nil
}
func (am *AuthManager) ListAPIKeys() []*APIKey {
am.mu.RLock()
defer am.mu.RUnlock()
apiKeys := make([]*APIKey, 0, len(am.apiKeys))
for _, apiKey := range am.apiKeys {
// Don't expose actual keys
apiKey.Key = ""
apiKeys = append(apiKeys, apiKey)
}
return apiKeys
}
func (am *AuthManager) ValidateAPIKey(key string) bool {
am.mu.RLock()
defer am.mu.RUnlock()
for _, apiKey := range am.apiKeys {
if apiKey.Key == key {
// Check if expired
if apiKey.ExpiresAt != nil && time.Now().After(*apiKey.ExpiresAt) {
return false
}
// Update last used
now := time.Now()
apiKey.LastUsed = &now
return true
}
}
return false
}