package main import ( "context" "log" "net/http" "os" "os/signal" "syscall" "time" "github.com/gin-gonic/gin" "monica-go-completion-backend/internal/api" "monica-go-completion-backend/internal/completion" "monica-go-completion-backend/internal/lsp" ) type config struct { Port string GoplsPath string WorkspaceDir string AllowOrigin string } func main() { cfg := loadConfig() lspClient, err := lsp.NewClient(context.Background(), cfg.GoplsPath, cfg.WorkspaceDir) if err != nil { log.Fatalf("create gopls client failed: %v", err) } defer func() { _ = lspClient.Close() }() completionService := completion.NewService(lspClient) router := gin.New() router.Use(gin.Logger()) router.Use(gin.Recovery()) router.Use(corsMiddleware(cfg.AllowOrigin)) api.RegisterRoutes(router, completionService) server := &http.Server{ Addr: ":" + cfg.Port, Handler: router, ReadTimeout: 30 * time.Second, WriteTimeout: 30 * time.Second, IdleTimeout: 60 * time.Second, } go func() { log.Printf("completion backend listening on :%s", cfg.Port) if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed { log.Fatalf("http server failed: %v", err) } }() sigCh := make(chan os.Signal, 1) signal.Notify(sigCh, os.Interrupt, syscall.SIGTERM) <-sigCh shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() if err := server.Shutdown(shutdownCtx); err != nil { log.Printf("http shutdown failed: %v", err) } } func loadConfig() config { cwd, err := os.Getwd() if err != nil { cwd = "." } return config{ Port: getenv("PORT", "8080"), GoplsPath: getenv("GOPLS_PATH", "gopls"), WorkspaceDir: getenv("WORKSPACE_DIR", cwd), AllowOrigin: getenv("CORS_ALLOW_ORIGIN", "*"), } } func getenv(key, fallback string) string { v := os.Getenv(key) if v == "" { return fallback } return v } func corsMiddleware(allowOrigin string) gin.HandlerFunc { return func(c *gin.Context) { c.Header("Access-Control-Allow-Origin", allowOrigin) c.Header("Access-Control-Allow-Methods", "GET,POST,OPTIONS") c.Header("Access-Control-Allow-Headers", "Content-Type,Authorization") if c.Request.Method == http.MethodOptions { c.AbortWithStatus(http.StatusNoContent) return } c.Next() } }