feat: edu-search-service migriert, voice-service/geo-service entfernt
All checks were successful
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-school (push) Successful in 28s
CI / test-go-edu-search (push) Successful in 27s
CI / test-python-klausur (push) Successful in 1m45s
CI / test-python-agent-core (push) Successful in 16s
CI / test-nodejs-website (push) Successful in 21s
All checks were successful
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-school (push) Successful in 28s
CI / test-go-edu-search (push) Successful in 27s
CI / test-python-klausur (push) Successful in 1m45s
CI / test-python-agent-core (push) Successful in 16s
CI / test-nodejs-website (push) Successful in 21s
- edu-search-service von breakpilot-pwa nach breakpilot-lehrer kopiert (ohne vendor) - opensearch + edu-search-service in docker-compose.yml hinzugefuegt - voice-service aus docker-compose.yml entfernt (jetzt in breakpilot-core) - geo-service aus docker-compose.yml entfernt (nicht mehr benoetigt) - CI/CD: edu-search-service zu Gitea Actions und Woodpecker hinzugefuegt (Go lint, test mit go mod download, build, SBOM) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
187
edu-search-service/cmd/server/main.go
Normal file
187
edu-search-service/cmd/server/main.go
Normal file
@@ -0,0 +1,187 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/breakpilot/edu-search-service/internal/api/handlers"
|
||||
"github.com/breakpilot/edu-search-service/internal/config"
|
||||
"github.com/breakpilot/edu-search-service/internal/database"
|
||||
"github.com/breakpilot/edu-search-service/internal/indexer"
|
||||
"github.com/breakpilot/edu-search-service/internal/orchestrator"
|
||||
"github.com/breakpilot/edu-search-service/internal/search"
|
||||
"github.com/breakpilot/edu-search-service/internal/staff"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func main() {
|
||||
log.Println("Starting edu-search-service...")
|
||||
|
||||
// Load configuration
|
||||
cfg := config.Load()
|
||||
log.Printf("Configuration loaded: Port=%s, OpenSearch=%s, Index=%s",
|
||||
cfg.Port, cfg.OpenSearchURL, cfg.IndexName)
|
||||
|
||||
// Initialize OpenSearch indexer client
|
||||
indexClient, err := indexer.NewClient(
|
||||
cfg.OpenSearchURL,
|
||||
cfg.OpenSearchUsername,
|
||||
cfg.OpenSearchPassword,
|
||||
cfg.IndexName,
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to create indexer client: %v", err)
|
||||
}
|
||||
|
||||
// Create index if not exists
|
||||
ctx := context.Background()
|
||||
if err := indexClient.CreateIndex(ctx); err != nil {
|
||||
log.Printf("Warning: Could not create index (may already exist): %v", err)
|
||||
}
|
||||
|
||||
// Initialize search service
|
||||
searchService, err := search.NewService(
|
||||
cfg.OpenSearchURL,
|
||||
cfg.OpenSearchUsername,
|
||||
cfg.OpenSearchPassword,
|
||||
cfg.IndexName,
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to create search service: %v", err)
|
||||
}
|
||||
|
||||
// Initialize seed store for admin API
|
||||
if err := handlers.InitSeedStore(cfg.SeedsDir); err != nil {
|
||||
log.Printf("Warning: Could not initialize seed store: %v", err)
|
||||
}
|
||||
|
||||
// Create handler
|
||||
handler := handlers.NewHandler(cfg, searchService, indexClient)
|
||||
|
||||
// Initialize PostgreSQL for Staff/Publications database
|
||||
dbCfg := &database.Config{
|
||||
Host: cfg.DBHost,
|
||||
Port: cfg.DBPort,
|
||||
User: cfg.DBUser,
|
||||
Password: cfg.DBPassword,
|
||||
DBName: cfg.DBName,
|
||||
SSLMode: cfg.DBSSLMode,
|
||||
}
|
||||
|
||||
db, err := database.New(ctx, dbCfg)
|
||||
if err != nil {
|
||||
log.Printf("Warning: Could not connect to PostgreSQL for staff database: %v", err)
|
||||
log.Println("Staff/Publications features will be disabled")
|
||||
} else {
|
||||
defer db.Close()
|
||||
log.Println("Connected to PostgreSQL for staff/publications database")
|
||||
|
||||
// Run migrations
|
||||
if err := db.RunMigrations(ctx); err != nil {
|
||||
log.Printf("Warning: Could not run migrations: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Create repository for Staff handlers (may be nil if DB connection failed)
|
||||
var repo *database.Repository
|
||||
if db != nil {
|
||||
repo = database.NewRepository(db)
|
||||
}
|
||||
|
||||
// Setup Gin router
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
router := gin.New()
|
||||
router.Use(gin.Recovery())
|
||||
router.Use(gin.Logger())
|
||||
|
||||
// CORS middleware
|
||||
router.Use(func(c *gin.Context) {
|
||||
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
c.Writer.Header().Set("Access-Control-Allow-Methods", "GET, POST, OPTIONS")
|
||||
c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
|
||||
|
||||
if c.Request.Method == "OPTIONS" {
|
||||
c.AbortWithStatus(204)
|
||||
return
|
||||
}
|
||||
|
||||
c.Next()
|
||||
})
|
||||
|
||||
// Setup routes
|
||||
handlers.SetupRoutes(router, handler, cfg.APIKey)
|
||||
|
||||
// Setup Staff/Publications routes if database is available
|
||||
if repo != nil {
|
||||
staffHandlers := handlers.NewStaffHandlers(repo, cfg.StaffCrawlerEmail)
|
||||
apiV1 := router.Group("/api/v1")
|
||||
staffHandlers.RegisterRoutes(apiV1)
|
||||
log.Println("Staff/Publications API routes registered")
|
||||
|
||||
// Setup AI Extraction routes for vast.ai integration
|
||||
aiHandlers := handlers.NewAIExtractionHandlers(repo)
|
||||
aiHandlers.RegisterRoutes(apiV1)
|
||||
log.Println("AI Extraction API routes registered")
|
||||
}
|
||||
|
||||
// Setup Orchestrator routes if database is available
|
||||
if db != nil {
|
||||
orchRepo := orchestrator.NewPostgresRepository(db.Pool)
|
||||
|
||||
// Create real crawlers with adapters for orchestrator interface
|
||||
staffCrawler := staff.NewStaffCrawler(repo)
|
||||
staffAdapter := staff.NewOrchestratorAdapter(staffCrawler, repo)
|
||||
pubAdapter := staff.NewPublicationOrchestratorAdapter(repo)
|
||||
|
||||
orch := orchestrator.NewOrchestrator(orchRepo, staffAdapter, pubAdapter)
|
||||
orchHandler := handlers.NewOrchestratorHandler(orch, orchRepo)
|
||||
|
||||
v1 := router.Group("/v1")
|
||||
v1.Use(handlers.AuthMiddleware(cfg.APIKey))
|
||||
handlers.SetupOrchestratorRoutes(v1, orchHandler)
|
||||
log.Println("Orchestrator API routes registered")
|
||||
|
||||
// Setup Audience routes (reuses orchRepo which implements AudienceRepository)
|
||||
audienceHandler := handlers.NewAudienceHandler(orchRepo)
|
||||
handlers.SetupAudienceRoutes(v1, audienceHandler)
|
||||
log.Println("Audience API routes registered")
|
||||
}
|
||||
|
||||
// Create HTTP server
|
||||
srv := &http.Server{
|
||||
Addr: ":" + cfg.Port,
|
||||
Handler: router,
|
||||
ReadTimeout: 10 * time.Second,
|
||||
WriteTimeout: 30 * time.Second,
|
||||
IdleTimeout: 60 * time.Second,
|
||||
}
|
||||
|
||||
// Start server in goroutine
|
||||
go func() {
|
||||
log.Printf("Server listening on port %s", cfg.Port)
|
||||
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||
log.Fatalf("Server error: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
// Graceful shutdown
|
||||
quit := make(chan os.Signal, 1)
|
||||
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
|
||||
<-quit
|
||||
|
||||
log.Println("Shutting down server...")
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
if err := srv.Shutdown(ctx); err != nil {
|
||||
log.Fatalf("Server forced to shutdown: %v", err)
|
||||
}
|
||||
|
||||
log.Println("Server exited")
|
||||
}
|
||||
Reference in New Issue
Block a user