package cmd import ( "context" "errors" "log/slog" nethttp "net/http" "os" "os/signal" "syscall" "time" "github.com/spf13/cobra" "trankilou.fr/lassistanoque/backend/internal/adapter/auth/password" "trankilou.fr/lassistanoque/backend/internal/adapter/database" "trankilou.fr/lassistanoque/backend/internal/adapter/file" "trankilou.fr/lassistanoque/backend/internal/adapter/llm" "trankilou.fr/lassistanoque/backend/internal/adapter/security" "trankilou.fr/lassistanoque/backend/internal/brain" "trankilou.fr/lassistanoque/backend/internal/http" "trankilou.fr/lassistanoque/backend/internal/service/agent" "trankilou.fr/lassistanoque/backend/internal/service/auth" "trankilou.fr/lassistanoque/backend/internal/service/chat" "trankilou.fr/lassistanoque/backend/internal/service/provider" "trankilou.fr/lassistanoque/backend/internal/service/storage" "trankilou.fr/lassistanoque/backend/internal/service/user" ) func init() { rootCmd.AddCommand( newServeCmd(), ) } func newServeCmd() *cobra.Command { var short bool cmd := &cobra.Command{ Use: "serve", Short: "Run server", Run: func(cmd *cobra.Command, args []string) { runServe() }, } cmd.Flags().BoolVarP(&short, "short", "s", false, "show only version number") return cmd } func runServe() { // database db, err := database.GetDatabase() if err != nil { panic(err) } defer db.Close() err = db.Migrate() if err != nil { panic(err) } // storage store, err := file.GetStorageProvider(db.FileRepository()) if err != nil { panic(err) } // Adapters tokenManager := security.NewJwtTokenManager(12*time.Hour, 7*24*time.Hour, "lassistanoque") pwdAuth := password.NewPasswordAuthenticator(tokenManager, db.UserRepository()) llmEngine := llm.NewAnyLLMEngine() repoUser := db.UserRepository() repoProvider := db.ProviderRepository() repoAgent := db.AgentRepository() repoChat := db.ChatRepository() // services authService := auth.NewService( db.SettingsRepository(), db.UserRepository(), map[string]auth.Authenticator{ "password": pwdAuth, }, ) userService := user.NewService(repoUser) storageService := storage.NewService(store) providerService := provider.NewService(repoProvider, repoUser, llmEngine) agentService := agent.NewService(repoAgent, repoUser) chatService := chat.NewService(repoUser, repoAgent, repoProvider, repoChat, llmEngine) // http server httpRouter := http.NewRouter(http.Dependencies{ StorageService: storageService, AuthService: authService, UserService: userService, TokenManager: tokenManager, ProviderService: providerService, AgentService: agentService, ChatService: chatService, }) brainRouter := brain.NewRouter(brain.Dependencies{ ChatRepository: db.ChatRepository(), }) ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) defer stop() // start Brain router go func() { if err := brainRouter.Start(); err != nil && !errors.Is(err, nethttp.ErrServerClosed) { slog.Error("failed to start llm server", "error", err) } }() // start HTTP router go func() { if err := httpRouter.Start(); err != nil && !errors.Is(err, nethttp.ErrServerClosed) { slog.Error("failed to start http server", "error", err) } }() <-ctx.Done() // stop HTTP service ctx, cancel1 := context.WithTimeout(context.Background(), 10*time.Second) defer cancel1() if err := httpRouter.Stop(ctx); err != nil { slog.Error("failed to stop http server", "error", err) } slog.Info("HTTP server stopped gracefully") // stop LLM service ctx, cancel2 := context.WithTimeout(context.Background(), 10*time.Second) defer cancel2() if err := brainRouter.Stop(ctx); err != nil { slog.Error("failed to stop llm server", "error", err) } slog.Info("LLM server stopped gracefully") }