- Reorganized project structure into services/ and apps/ directories for better separation of concerns - Added comprehensive CI/CD pipeline with GitHub Actions for testing and Docker builds - Created .dockerignore file to optimize container builds - Updated Makefile with new targets for each service and application - Added detailed README with architecture overview, setup instructions and development guidelines - Moved cron-analyzer to dedicate
143 lines
4.1 KiB
Go
143 lines
4.1 KiB
Go
package http
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"log"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// CORSMiddleware handles Cross-Origin Resource Sharing
|
|
func CORSMiddleware(next http.Handler) http.Handler {
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
// Set CORS headers
|
|
w.Header().Set("Access-Control-Allow-Origin", "*")
|
|
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
|
|
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Requested-With")
|
|
w.Header().Set("Access-Control-Allow-Credentials", "true")
|
|
w.Header().Set("Access-Control-Max-Age", "86400")
|
|
|
|
// Handle preflight OPTIONS requests
|
|
if r.Method == "OPTIONS" {
|
|
w.WriteHeader(http.StatusOK)
|
|
return
|
|
}
|
|
|
|
next.ServeHTTP(w, r)
|
|
})
|
|
}
|
|
|
|
// LogsMiddleware logs HTTP requests with timing information
|
|
func LogsMiddleware(next http.Handler) http.Handler {
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
start := time.Now()
|
|
next.ServeHTTP(w, r)
|
|
log.Printf("%s %s %s", r.Method, r.RequestURI, time.Since(start))
|
|
})
|
|
}
|
|
|
|
// AuthMiddleware handles JWT token validation, API keys, and basic auth
|
|
func AuthMiddleware(next http.Handler) http.Handler {
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
// Skip auth for swagger endpoints and health checks
|
|
if strings.HasPrefix(r.URL.Path, "/swagger/") || r.URL.Path == "/health" {
|
|
next.ServeHTTP(w, r)
|
|
return
|
|
}
|
|
|
|
// Try Bearer token authentication first
|
|
authHeader := r.Header.Get("Authorization")
|
|
if authHeader != "" {
|
|
if strings.HasPrefix(authHeader, "Bearer ") {
|
|
token := strings.TrimPrefix(authHeader, "Bearer ")
|
|
if token != "" {
|
|
userID, err := validateJWTToken(token)
|
|
if err == nil {
|
|
ctx := context.WithValue(r.Context(), "user_id", userID)
|
|
next.ServeHTTP(w, r.WithContext(ctx))
|
|
return
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Try API Key authentication
|
|
apiKey := r.Header.Get("X-API-Key")
|
|
if apiKey != "" {
|
|
if isValidAPIKey(apiKey) {
|
|
ctx := context.WithValue(r.Context(), "user_id", getUserIDFromAPIKey(apiKey))
|
|
next.ServeHTTP(w, r.WithContext(ctx))
|
|
return
|
|
}
|
|
}
|
|
|
|
// Try Basic authentication
|
|
username, password, ok := r.BasicAuth()
|
|
if ok {
|
|
if isValidCredentials(username, password) {
|
|
ctx := context.WithValue(r.Context(), "username", username)
|
|
next.ServeHTTP(w, r.WithContext(ctx))
|
|
return
|
|
}
|
|
}
|
|
|
|
// Fallback to cookie-based auth (existing behavior)
|
|
token, err := r.Cookie("token")
|
|
if err == nil && token.Value == "123" {
|
|
next.ServeHTTP(w, r)
|
|
return
|
|
}
|
|
|
|
// No valid authentication found
|
|
response := map[string]interface{}{
|
|
"error": "Unauthorized",
|
|
"message": "Valid authentication required. Use Bearer token, API key, or basic auth.",
|
|
"code": http.StatusUnauthorized,
|
|
}
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.WriteHeader(http.StatusUnauthorized)
|
|
json.NewEncoder(w).Encode(response)
|
|
})
|
|
}
|
|
|
|
// Helper functions for authentication validation
|
|
func validateJWTToken(token string) (string, error) {
|
|
// TODO: Implement JWT validation logic with your preferred JWT library
|
|
// For demonstration, accept a demo token
|
|
if token == "valid-jwt-token" {
|
|
return "user-123", nil
|
|
}
|
|
return "", http.ErrAbortHandler
|
|
}
|
|
|
|
func isValidAPIKey(apiKey string) bool {
|
|
// TODO: Implement API key validation logic
|
|
// For demonstration, accept a demo API key
|
|
return apiKey == "demo-api-key-123"
|
|
}
|
|
|
|
func getUserIDFromAPIKey(apiKey string) string {
|
|
// TODO: Implement logic to get user ID from API key
|
|
// For demonstration, return a mock user ID
|
|
return "api-user-123"
|
|
}
|
|
|
|
func isValidCredentials(username, password string) bool {
|
|
// TODO: Implement credential validation logic
|
|
// For demonstration, accept demo credentials
|
|
return username == "admin" && password == "password"
|
|
}
|
|
|
|
// GetUserIDFromContext extracts user ID from request context
|
|
func GetUserIDFromContext(ctx context.Context) (string, bool) {
|
|
userID, ok := ctx.Value("user_id").(string)
|
|
return userID, ok
|
|
}
|
|
|
|
// GetUsernameFromContext extracts username from request context
|
|
func GetUsernameFromContext(ctx context.Context) (string, bool) {
|
|
username, ok := ctx.Value("username").(string)
|
|
return username, ok
|
|
}
|