mirror of
https://github.com/lukaszraczylo/gohoarder.git
synced 2026-07-12 04:50:45 +00:00
refactor: reorganize struct fields, add new handlers and storage backends
- [x] Reorder struct fields across codebase for consistency - [x] Add analytics event handlers and tests - [x] Add authentication API key management handlers and tests - [x] Add pre-warming control handlers and tests - [x] Implement S3 storage backend with tests - [x] Implement SMB/CIFS storage backend with tests - [x] Add CDN middleware tests - [x] Integrate analytics tracking into cache manager - [x] Add S3 and SMB storage initialization in app setup - [x] Add CDN caching to proxy handlers - [x] Remove distributed locking (Redis lock manager) - [x] Remove proxy common package and utilities - [x] Remove standalone HTTP server package - [x] Remove logger middleware - [x] Simplify error handling utilities - [x] Update config with S3 and SMB options - [x] Update cache manager signature to include analytics
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/lukaszraczylo/gohoarder/pkg/auth"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
// GenerateAPIKeyRequest represents a request to generate a new API key
|
||||
type GenerateAPIKeyRequest struct {
|
||||
ExpiresInMin *int `json:"expires_in_min"`
|
||||
Name string `json:"name"`
|
||||
Role string `json:"role"`
|
||||
}
|
||||
|
||||
// handleGenerateAPIKey generates a new API key
|
||||
func (a *App) handleGenerateAPIKey(c *fiber.Ctx) error {
|
||||
c.Set("Content-Type", "application/json")
|
||||
|
||||
var req GenerateAPIKeyRequest
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
|
||||
"error": "invalid JSON in request body",
|
||||
})
|
||||
}
|
||||
|
||||
// Validate request
|
||||
if req.Name == "" {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
|
||||
"error": "name is required",
|
||||
})
|
||||
}
|
||||
|
||||
// Parse role (default to readonly if not specified)
|
||||
var role auth.Role
|
||||
switch req.Role {
|
||||
case "admin":
|
||||
role = auth.RoleAdmin
|
||||
case "readwrite":
|
||||
role = auth.RoleReadWrite
|
||||
case "readonly", "":
|
||||
role = auth.RoleReadOnly
|
||||
default:
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
|
||||
"error": "invalid role, must be 'admin', 'readwrite', or 'readonly'",
|
||||
})
|
||||
}
|
||||
|
||||
// Calculate expiration
|
||||
var expiresIn *time.Duration
|
||||
if req.ExpiresInMin != nil {
|
||||
duration := time.Duration(*req.ExpiresInMin) * time.Minute
|
||||
expiresIn = &duration
|
||||
}
|
||||
|
||||
// Generate key
|
||||
apiKey, rawKey, err := a.authManager.GenerateAPIKey(req.Name, role, expiresIn)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Str("name", req.Name).Msg("Failed to generate API key")
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
||||
"error": "failed to generate API key",
|
||||
})
|
||||
}
|
||||
|
||||
log.Info().
|
||||
Str("key_id", apiKey.ID).
|
||||
Str("name", apiKey.Name).
|
||||
Str("role", string(apiKey.Role)).
|
||||
Msg("API key generated")
|
||||
|
||||
// Return the key info and raw key (only time it's shown!)
|
||||
return c.Status(fiber.StatusCreated).JSON(fiber.Map{
|
||||
"key": rawKey, // IMPORTANT: This is the only time the raw key is shown
|
||||
"key_id": apiKey.ID,
|
||||
"name": apiKey.Name,
|
||||
"role": apiKey.Role,
|
||||
"expires": apiKey.ExpiresAt,
|
||||
"message": "Save this key now! It will not be shown again.",
|
||||
})
|
||||
}
|
||||
|
||||
// handleListAPIKeys lists all API keys
|
||||
func (a *App) handleListAPIKeys(c *fiber.Ctx) error {
|
||||
c.Set("Content-Type", "application/json")
|
||||
|
||||
keys := a.authManager.ListAPIKeys()
|
||||
|
||||
// Convert to response format (excluding hashed keys)
|
||||
response := make([]fiber.Map, len(keys))
|
||||
for i, key := range keys {
|
||||
response[i] = fiber.Map{
|
||||
"id": key.ID,
|
||||
"name": key.Name,
|
||||
"role": key.Role,
|
||||
"created_at": key.CreatedAt,
|
||||
"expires_at": key.ExpiresAt,
|
||||
"last_used_at": key.LastUsedAt,
|
||||
"permissions": key.Permissions,
|
||||
}
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(fiber.Map{
|
||||
"keys": response,
|
||||
"total": len(response),
|
||||
})
|
||||
}
|
||||
|
||||
// handleRevokeAPIKey revokes an API key
|
||||
func (a *App) handleRevokeAPIKey(c *fiber.Ctx) error {
|
||||
c.Set("Content-Type", "application/json")
|
||||
|
||||
keyID := c.Params("key_id")
|
||||
if keyID == "" {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
|
||||
"error": "key_id parameter is required",
|
||||
})
|
||||
}
|
||||
|
||||
err := a.authManager.RevokeAPIKey(keyID)
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Str("key_id", keyID).Msg("Failed to revoke API key")
|
||||
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
|
||||
"error": "API key not found",
|
||||
})
|
||||
}
|
||||
|
||||
log.Info().Str("key_id", keyID).Msg("API key revoked")
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(fiber.Map{
|
||||
"message": "API key revoked successfully",
|
||||
"key_id": keyID,
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user