Start begins listening and serving gRPC requests. It blocks until the server is stopped via signal or Stop().
()
| 225 | // Start begins listening and serving gRPC requests. |
| 226 | // It blocks until the server is stopped via signal or Stop(). |
| 227 | func (s *Server) Start() error { |
| 228 | // Start metrics HTTP server before gRPC (non-blocking) |
| 229 | if s.metricsServer != nil { |
| 230 | s.metricsServer.Start() |
| 231 | } |
| 232 | |
| 233 | // Bind address: localhost for local CLI usage, 0.0.0.0 inside Kubernetes. |
| 234 | // KUBERNETES_SERVICE_HOST is injected by kubelet into every pod — its presence |
| 235 | // means we're running in K8s and must listen on all interfaces for health |
| 236 | // checks and Service routing to work. |
| 237 | bindAddr := os.Getenv("CHATCLI_BIND_ADDRESS") |
| 238 | if bindAddr == "" { |
| 239 | if os.Getenv("KUBERNETES_SERVICE_HOST") != "" { |
| 240 | bindAddr = "0.0.0.0" |
| 241 | } else { |
| 242 | bindAddr = "127.0.0.1" |
| 243 | } |
| 244 | } |
| 245 | addr := fmt.Sprintf("%s:%d", bindAddr, s.config.Port) |
| 246 | lis, err := net.Listen("tcp", addr) |
| 247 | if err != nil { |
| 248 | return fmt.Errorf("%s: %w", i18n.T("server.listen.failed", addr), err) |
| 249 | } |
| 250 | |
| 251 | // Graceful shutdown on SIGINT/SIGTERM |
| 252 | sigChan := make(chan os.Signal, 1) |
| 253 | signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM) |
| 254 | go func() { |
| 255 | sig := <-sigChan |
| 256 | s.logger.Info(i18n.T("server.shutdown.signal"), zap.String("signal", sig.String())) |
| 257 | if s.metricsServer != nil { |
| 258 | s.metricsServer.Stop() |
| 259 | } |
| 260 | s.grpcServer.GracefulStop() |
| 261 | }() |
| 262 | |
| 263 | s.logger.Info(i18n.T("server.starting"), |
| 264 | zap.Int("port", s.config.Port), |
| 265 | zap.String("provider", s.config.Provider), |
| 266 | zap.String("model", s.config.Model), |
| 267 | zap.Bool("auth_enabled", s.config.Token != ""), |
| 268 | zap.Bool("tls_enabled", s.config.TLSCertFile != ""), |
| 269 | zap.Int("metrics_port", s.config.MetricsPort), |
| 270 | ) |
| 271 | |
| 272 | fmt.Println(i18n.T("server.listening", addr)) |
| 273 | if s.config.Token != "" { |
| 274 | fmt.Println(i18n.T("server.auth_enabled")) |
| 275 | } |
| 276 | if s.config.MetricsPort > 0 { |
| 277 | fmt.Println(i18n.T("server.metrics_enabled", s.config.MetricsPort)) |
| 278 | } |
| 279 | |
| 280 | if err := s.grpcServer.Serve(lis); err != nil { |
| 281 | return fmt.Errorf("%s: %w", i18n.T("server.grpc_failed"), err) |
| 282 | } |
| 283 | |
| 284 | s.logger.Info(i18n.T("server.stopped")) |