serve

package
v0.0.36 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 1, 2026 License: AGPL-3.0 Imports: 26 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var Command = &cli.Command{
	Name: "serve",
	Flags: []cli.Flag{
		&cli.StringFlag{
			Name:    "port",
			Value:   "8089",
			Usage:   "port to listen on",
			Aliases: []string{"p"},
			Sources: cli.EnvVars("PORT"),
		},
		&cli.StringFlag{
			Name:    "turso-db-url",
			Sources: cli.EnvVars("TURSO_CONNECTION_PATH"),
		},
		&cli.StringFlag{
			Name:    "turso-db-token",
			Sources: cli.EnvVars("TURSO_CONNECTION_TOKEN"),
		},
	},
	Action: func(ctx context.Context, cmd *cli.Command) error {
		gormDB, err := setupDatabase(cmd.String("turso-db-url"), cmd.String("turso-db-token"))
		if err != nil {
			return fmt.Errorf("failed to setup database: %w", err)
		}

		productIDs := map[apiv1.CheckoutProduct]string{
			apiv1.CheckoutProduct_CHECKOUT_PRODUCT_PLUS: os.Getenv("POLAR_PRODUCT_PLUS_ID"),
			apiv1.CheckoutProduct_CHECKOUT_PRODUCT_PRO:  os.Getenv("POLAR_PRODUCT_PRO_ID"),
		}

		apiService, err := api.NewServiceImpl(gormDB, productIDs)
		if err != nil {
			return fmt.Errorf("failed to create api service: %w", err)
		}

		mux := http.NewServeMux()

		apiPath, apiHandler := apiv1connect.NewApiServiceHandler(apiService, connect.WithInterceptors(
			api.NewAuthInterceptor(gormDB),
			validate.NewInterceptor(),
		))

		protocols := new(http.Protocols)
		protocols.SetHTTP1(true)
		protocols.SetUnencryptedHTTP2(true)
		mux.Handle(apiPath, apiHandler)

		webhookPath := "/api/v1/webhooks/polar"
		mux.HandleFunc(webhookPath, api.NewPolarWebhookHandler(apiService))
		slog.Info("serving polar webhook handler", "path", webhookPath)

		geminiProxyConfig := llmProxyConfig{
			Provider:   "gemini",
			BaseURL:    "https://generativelanguage.googleapis.com",
			PathPrefix: "/api/v1/gemini",
			SetupRequest: func(_ *http.Request, targetURL *url.URL, _ *http.Request) error {
				apiKey := os.Getenv("GEMINI_API_KEY")
				if apiKey == "" {
					return fmt.Errorf("missing GEMINI_API_KEY")
				}

				query := targetURL.Query()
				query.Set("key", apiKey)
				targetURL.RawQuery = query.Encode()
				return nil
			},
			ExtractUsage: extractGeminiUsageMetadata,
		}

		openAIProxyConfig := llmProxyConfig{
			Provider:   "openai",
			BaseURL:    "https://api.openai.com",
			PathPrefix: "/api/v1/openai",
			SetupRequest: func(_ *http.Request, _ *url.URL, proxyReq *http.Request) error {
				apiKey := os.Getenv("OPENAI_API_KEY")
				if apiKey == "" {
					return fmt.Errorf("missing OPENAI_API_KEY")
				}

				proxyReq.Header.Set("Authorization", "Bearer "+apiKey)
				return nil
			},
			ExtractUsage: extractOpenAIUsageMetadata,
		}

		anthropicProxyConfig := llmProxyConfig{
			Provider:   "anthropic",
			BaseURL:    "https://api.anthropic.com",
			PathPrefix: "/api/v1/anthropic",
			SetupRequest: func(_ *http.Request, _ *url.URL, proxyReq *http.Request) error {
				apiKey := os.Getenv("ANTHROPIC_API_KEY")
				if apiKey == "" {
					return fmt.Errorf("missing ANTHROPIC_API_KEY")
				}

				version := os.Getenv("ANTHROPIC_VERSION")
				if version == "" {
					version = "2023-06-01"
				}

				proxyReq.Header.Set("x-api-key", apiKey)
				proxyReq.Header.Set("anthropic-version", version)
				if proxyReq.Header.Get("Content-Type") == "" {
					proxyReq.Header.Set("Content-Type", "application/json")
				}
				return nil
			},
			ExtractUsage: extractAnthropicUsageMetadata,
		}

		grokProxyConfig := llmProxyConfig{
			Provider:   "grok",
			BaseURL:    "https://api.x.ai",
			PathPrefix: "/api/v1/grok",
			SetupRequest: func(_ *http.Request, _ *url.URL, proxyReq *http.Request) error {
				apiKey := os.Getenv("GROK_API_KEY")
				if apiKey == "" {
					return fmt.Errorf("missing GROK_API_KEY")
				}

				proxyReq.Header.Set("Authorization", "Bearer "+apiKey)
				return nil
			},
			ExtractUsage: extractGrokUsageMetadata,
		}

		geminiProxyPath := "/api/v1/gemini/"
		mux.HandleFunc(geminiProxyPath, newLLMProxyHandler(gormDB, geminiProxyConfig))
		slog.Info("serving gemini proxy handler", "path", geminiProxyPath)

		openAIProxyPath := "/api/v1/openai/"
		mux.HandleFunc(openAIProxyPath, newLLMProxyHandler(gormDB, openAIProxyConfig))
		slog.Info("serving openai proxy handler", "path", openAIProxyPath)

		anthropicProxyPath := "/api/v1/anthropic/"
		mux.HandleFunc(anthropicProxyPath, newLLMProxyHandler(gormDB, anthropicProxyConfig))
		slog.Info("serving anthropic proxy handler", "path", anthropicProxyPath)

		grokProxyPath := "/api/v1/grok/"
		mux.HandleFunc(grokProxyPath, newLLMProxyHandler(gormDB, grokProxyConfig))
		slog.Info("serving grok proxy handler", "path", grokProxyPath)

		slog.Info("serving rpc handler for api v1 service", "path", apiPath)

		h2Handler := h2c.NewHandler(mux, &http2.Server{})

		server := &http.Server{
			Addr:    ":" + cmd.String("port"),
			Handler: h2Handler,

			ReadHeaderTimeout: 3 * time.Second,
			Protocols:         protocols,
		}

		sigint := make(chan os.Signal, 1)
		signal.Notify(sigint, os.Interrupt)

		go func() {
			slog.Info("serving http server for api v1 service", "addr", ":"+cmd.String("port"))
			if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
				slog.Error("failed to serve engine service", "error", err)
				os.Exit(1)
			}
		}()

		<-sigint
		slog.Info("shutting down engine service")

		shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
		defer cancel()

		if err := server.Shutdown(shutdownCtx); err != nil {
			slog.Error("server forced to shutdown", "error", err)
		}

		slog.Info("engine service shut down")
		return nil
	},
}

Functions

This section is empty.

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL