feat: BreakPilot PWA - Full codebase (clean push without large binaries)
Some checks failed
Tests / Go Tests (push) Has been cancelled
Tests / Python Tests (push) Has been cancelled
Tests / Integration Tests (push) Has been cancelled
Tests / Go Lint (push) Has been cancelled
Tests / Python Lint (push) Has been cancelled
Tests / Security Scan (push) Has been cancelled
Tests / All Checks Passed (push) Has been cancelled
Security Scanning / Secret Scanning (push) Has been cancelled
Security Scanning / Dependency Vulnerability Scan (push) Has been cancelled
Security Scanning / Go Security Scan (push) Has been cancelled
Security Scanning / Python Security Scan (push) Has been cancelled
Security Scanning / Node.js Security Scan (push) Has been cancelled
Security Scanning / Docker Image Security (push) Has been cancelled
Security Scanning / Security Summary (push) Has been cancelled
CI/CD Pipeline / Go Tests (push) Has been cancelled
CI/CD Pipeline / Python Tests (push) Has been cancelled
CI/CD Pipeline / Website Tests (push) Has been cancelled
CI/CD Pipeline / Linting (push) Has been cancelled
CI/CD Pipeline / Security Scan (push) Has been cancelled
CI/CD Pipeline / Docker Build & Push (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Deploy to Staging (push) Has been cancelled
CI/CD Pipeline / Deploy to Production (push) Has been cancelled
CI/CD Pipeline / CI Summary (push) Has been cancelled
ci/woodpecker/manual/build-ci-image Pipeline was successful
ci/woodpecker/manual/main Pipeline failed
Some checks failed
Tests / Go Tests (push) Has been cancelled
Tests / Python Tests (push) Has been cancelled
Tests / Integration Tests (push) Has been cancelled
Tests / Go Lint (push) Has been cancelled
Tests / Python Lint (push) Has been cancelled
Tests / Security Scan (push) Has been cancelled
Tests / All Checks Passed (push) Has been cancelled
Security Scanning / Secret Scanning (push) Has been cancelled
Security Scanning / Dependency Vulnerability Scan (push) Has been cancelled
Security Scanning / Go Security Scan (push) Has been cancelled
Security Scanning / Python Security Scan (push) Has been cancelled
Security Scanning / Node.js Security Scan (push) Has been cancelled
Security Scanning / Docker Image Security (push) Has been cancelled
Security Scanning / Security Summary (push) Has been cancelled
CI/CD Pipeline / Go Tests (push) Has been cancelled
CI/CD Pipeline / Python Tests (push) Has been cancelled
CI/CD Pipeline / Website Tests (push) Has been cancelled
CI/CD Pipeline / Linting (push) Has been cancelled
CI/CD Pipeline / Security Scan (push) Has been cancelled
CI/CD Pipeline / Docker Build & Push (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Deploy to Staging (push) Has been cancelled
CI/CD Pipeline / Deploy to Production (push) Has been cancelled
CI/CD Pipeline / CI Summary (push) Has been cancelled
ci/woodpecker/manual/build-ci-image Pipeline was successful
ci/woodpecker/manual/main Pipeline failed
All services: admin-v2, studio-v2, website, ai-compliance-sdk, consent-service, klausur-service, voice-service, and infrastructure. Large PDFs and compiled binaries excluded via .gitignore.
This commit is contained in:
146
edu-search-service/internal/api/handlers/handlers.go
Normal file
146
edu-search-service/internal/api/handlers/handlers.go
Normal file
@@ -0,0 +1,146 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/breakpilot/edu-search-service/internal/config"
|
||||
"github.com/breakpilot/edu-search-service/internal/indexer"
|
||||
"github.com/breakpilot/edu-search-service/internal/search"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// Handler contains all HTTP handlers
|
||||
type Handler struct {
|
||||
cfg *config.Config
|
||||
searchService *search.Service
|
||||
indexClient *indexer.Client
|
||||
}
|
||||
|
||||
// NewHandler creates a new handler instance
|
||||
func NewHandler(cfg *config.Config, searchService *search.Service, indexClient *indexer.Client) *Handler {
|
||||
return &Handler{
|
||||
cfg: cfg,
|
||||
searchService: searchService,
|
||||
indexClient: indexClient,
|
||||
}
|
||||
}
|
||||
|
||||
// Health returns service health status
|
||||
func (h *Handler) Health(c *gin.Context) {
|
||||
status := "ok"
|
||||
|
||||
// Check OpenSearch health
|
||||
osStatus, err := h.indexClient.Health(c.Request.Context())
|
||||
if err != nil {
|
||||
status = "degraded"
|
||||
osStatus = "unreachable"
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"status": status,
|
||||
"opensearch": osStatus,
|
||||
"service": "edu-search-service",
|
||||
"version": "0.1.0",
|
||||
})
|
||||
}
|
||||
|
||||
// Search handles /v1/search requests
|
||||
func (h *Handler) Search(c *gin.Context) {
|
||||
var req search.SearchRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body", "details": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// Set defaults
|
||||
if req.Limit <= 0 || req.Limit > 100 {
|
||||
req.Limit = 10
|
||||
}
|
||||
if req.Mode == "" {
|
||||
req.Mode = "keyword" // MVP: only BM25
|
||||
}
|
||||
|
||||
// Generate query ID
|
||||
queryID := uuid.New().String()
|
||||
|
||||
// Execute search
|
||||
result, err := h.searchService.Search(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Search failed", "details": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
result.QueryID = queryID
|
||||
c.JSON(http.StatusOK, result)
|
||||
}
|
||||
|
||||
// GetDocument retrieves a single document
|
||||
func (h *Handler) GetDocument(c *gin.Context) {
|
||||
docID := c.Query("doc_id")
|
||||
if docID == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "doc_id parameter required"})
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: Implement document retrieval
|
||||
c.JSON(http.StatusNotImplemented, gin.H{"error": "Not implemented yet"})
|
||||
}
|
||||
|
||||
// AuthMiddleware validates API keys
|
||||
func AuthMiddleware(apiKey string) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
// Skip auth for health endpoint
|
||||
if c.Request.URL.Path == "/v1/health" {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
|
||||
// Check API key
|
||||
authHeader := c.GetHeader("Authorization")
|
||||
if authHeader == "" {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Missing Authorization header"})
|
||||
return
|
||||
}
|
||||
|
||||
// Extract Bearer token
|
||||
if len(authHeader) < 7 || authHeader[:7] != "Bearer " {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Invalid Authorization format"})
|
||||
return
|
||||
}
|
||||
|
||||
token := authHeader[7:]
|
||||
if apiKey != "" && token != apiKey {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Invalid API key"})
|
||||
return
|
||||
}
|
||||
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
// RateLimitMiddleware implements basic rate limiting
|
||||
func RateLimitMiddleware() gin.HandlerFunc {
|
||||
// TODO: Implement proper rate limiting with Redis
|
||||
return func(c *gin.Context) {
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
// SetupRoutes configures all API routes
|
||||
func SetupRoutes(r *gin.Engine, h *Handler, apiKey string) {
|
||||
// Health endpoint (no auth)
|
||||
r.GET("/v1/health", h.Health)
|
||||
|
||||
// API v1 group with auth
|
||||
v1 := r.Group("/v1")
|
||||
v1.Use(AuthMiddleware(apiKey))
|
||||
v1.Use(RateLimitMiddleware())
|
||||
{
|
||||
v1.POST("/search", h.Search)
|
||||
v1.GET("/document", h.GetDocument)
|
||||
|
||||
// Admin routes
|
||||
SetupAdminRoutes(v1, h)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user