| package main |
|
|
| import ( |
| "log/slog" |
| "net/http" |
| "os" |
|
|
| "github.com/go-chi/chi/v5" |
| "github.com/go-chi/chi/v5/middleware" |
| ) |
|
|
| func main() { |
| port := os.Getenv("PORT") |
| if port == "" { |
| port = "7860" |
| } |
| addr := "0.0.0.0:" + port |
|
|
| slog.Info("Starting OpenMeter Native Mock Server", "address", addr) |
|
|
| r := chi.NewRouter() |
| r.Use(middleware.RequestID) |
| r.Use(middleware.RealIP) |
| r.Use(middleware.Logger) |
| r.Use(middleware.Recoverer) |
|
|
| |
| r.Get("/", func(w http.ResponseWriter, r *http.Request) { |
| http.Redirect(w, r, "/api-docs", http.StatusFound) |
| }) |
|
|
| |
| r.Get("/health", func(w http.ResponseWriter, r *http.Request) { |
| w.Header().Set("Content-Type", "text/plain") |
| w.WriteHeader(http.StatusOK) |
| _, _ = w.Write([]byte("OK")) |
| }) |
|
|
| |
| r.Get("/api/openapi.yaml", func(w http.ResponseWriter, r *http.Request) { |
| w.Header().Set("Content-Type", "text/yaml") |
| |
| http.ServeFile(w, r, "/usr/local/bin/openapi.yaml") |
| }) |
|
|
| |
| r.Get("/api-docs", func(w http.ResponseWriter, r *http.Request) { |
| w.Header().Set("Content-Type", "text/html") |
| w.WriteHeader(http.StatusOK) |
| _, _ = w.Write([]byte(`<!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="utf-8" /> |
| <title>OpenMeter API Documentation (Native Mock Server)</title> |
| <link rel="stylesheet" href="https://unpkg.com/swagger-ui-dist@5/swagger-ui.css" /> |
| <link rel="icon" type="image/png" href="https://unpkg.com/swagger-ui-dist@5/favicon-32x32.png" sizes="32x32" /> |
| </head> |
| <body> |
| <div id="swagger-ui"></div> |
| <script src="https://unpkg.com/swagger-ui-dist@5/swagger-ui-bundle.js"></script> |
| <script> |
| window.onload = () => { |
| window.ui = SwaggerUIBundle({ |
| url: '/api/openapi.yaml', |
| dom_id: '#swagger-ui', |
| deepLinking: true, |
| presets: [ |
| SwaggerUIBundle.presets.apis, |
| SwaggerUIBundle.swaggerInterceptor |
| ] |
| }); |
| }; |
| </script> |
| </body> |
| </html>`)) |
| }) |
|
|
| |
| r.Get("/api/v1/meters", func(w http.ResponseWriter, r *http.Request) { |
| w.Header().Set("Content-Type", "application/json") |
| w.WriteHeader(http.StatusOK) |
| _, _ = w.Write([]byte(`[ |
| { |
| "id": "api_requests_total", |
| "slug": "api_requests_total", |
| "description": "API Requests", |
| "eventType": "request", |
| "aggregation": "COUNT" |
| }, |
| { |
| "id": "tokens_total", |
| "slug": "tokens_total", |
| "description": "AI Token Usage", |
| "eventType": "prompt", |
| "aggregation": "SUM" |
| } |
| ]`)) |
| }) |
|
|
| |
| r.Post("/api/v1/events", func(w http.ResponseWriter, r *http.Request) { |
| w.Header().Set("Content-Type", "application/json") |
| w.WriteHeader(http.StatusAccepted) |
| _, _ = w.Write([]byte(`{"status": "accepted"}`)) |
| }) |
|
|
| |
| r.Get("/api/v1/meters/{meterIdOrSlug}/query", func(w http.ResponseWriter, r *http.Request) { |
| w.Header().Set("Content-Type", "application/json") |
| w.WriteHeader(http.StatusOK) |
| _, _ = w.Write([]byte(`{ |
| "data": [ |
| { |
| "value": 150, |
| "windowStart": "2026-07-07T00:00:00Z", |
| "windowEnd": "2026-07-07T01:00:00Z" |
| } |
| ] |
| }`)) |
| }) |
|
|
| err := http.ListenAndServe(addr, r) |
| if err != nil { |
| slog.Error("Mock server stopped with error", "error", err) |
| os.Exit(1) |
| } |
| } |
|
|