Files
Nip-05-api/internal/http/server.go
Michilis 7a1ceb49c3 Serve Swagger UI from embedded assets instead of unpkg CDN
Bundle swagger-ui-dist@5.32.5 (bundle + CSS) with go:embed and expose
/docs/swagger-ui-bundle.js and /docs/swagger-ui.css so /docs works without
external script loads (fixes timeouts when unpkg is unreachable).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-05 04:25:44 +00:00

103 lines
3.3 KiB
Go

package http
import (
"context"
"net/http"
"time"
"github.com/go-chi/chi/v5"
"github.com/noderunners/nip05api/internal/audit"
"github.com/noderunners/nip05api/internal/config"
"github.com/noderunners/nip05api/internal/db"
"github.com/noderunners/nip05api/internal/dm"
"github.com/noderunners/nip05api/internal/http/docs"
"github.com/noderunners/nip05api/internal/http/handlers"
"github.com/noderunners/nip05api/internal/http/middleware"
"github.com/noderunners/nip05api/internal/invoice"
"github.com/noderunners/nip05api/internal/user"
"github.com/noderunners/nip05api/internal/webhook"
)
type Deps struct {
Cfg *config.Config
DB *db.DB
Users *user.Service
Invoices *invoice.Service
DMs *dm.Service
Hooks *webhook.Service
Audit *audit.Logger
Version string
}
func NewServer(d Deps) *http.Server {
r := chi.NewRouter()
r.Use(middleware.Recoverer)
r.Use(middleware.RealIP)
r.Use(middleware.Logging)
r.Use(middleware.CORS(d.Cfg))
r.Use(middleware.BodyLimit(1 << 20)) // 1 MiB max request body
r.Use(middleware.RateLimit(d.Cfg.RateLimitPerMin))
health := &handlers.Health{DB: d.DB, Version: d.Version}
nostrJSON := &handlers.NostrJSON{Users: d.Users, Relays: d.Cfg.Nostr.Relays}
pricing := &handlers.Pricing{
YearlySats: d.Cfg.Lightning.PriceYearlySats,
LifetimeSats: d.Cfg.Lightning.PriceLifetimeSats,
LightningEnabled: d.Cfg.Lightning.Enabled,
}
users := &handlers.Users{Users: d.Users, GraceDays: d.Cfg.Expiry.GraceDays}
usernames := &handlers.Usernames{Users: d.Users}
invoices := &handlers.Invoices{Service: d.Invoices, LightningEnabled: d.Cfg.Lightning.Enabled}
adminUsers := &handlers.AdminUsers{
Users: d.Users, DMs: d.DMs, Hooks: d.Hooks, Audit: d.Audit,
Domain: d.Cfg.Domain, Frontend: d.Cfg.FrontendURL,
}
adminExtend := &handlers.AdminExtend{
Users: d.Users, DMs: d.DMs, Hooks: d.Hooks, Audit: d.Audit,
Domain: d.Cfg.Domain, Frontend: d.Cfg.FrontendURL,
}
r.Get("/healthz", health.Handle)
r.Get("/.well-known/nostr.json", nostrJSON.Handle)
r.Get("/openapi.json", docs.ServeJSON)
r.Get("/docs", docs.ServeUI)
r.Get("/docs/", docs.ServeUI)
r.Get("/docs/swagger-ui-bundle.js", docs.ServeJS)
r.Get("/docs/swagger-ui.css", docs.ServeCSS)
r.Route("/v1", func(r chi.Router) {
r.Get("/pricing", pricing.Handle)
r.Get("/whitelist/pubkeys", users.ListWhitelistedPubkeys)
r.Get("/users/{pubkey}", users.Get)
r.Get("/usernames/{name}/available", usernames.Available)
if d.Invoices != nil {
r.Post("/invoices", invoices.Create)
r.Get("/invoices/{payment_hash}", invoices.Get)
}
r.Route("/admin", func(r chi.Router) {
r.Use(middleware.AdminAuth(d.Cfg.AdminAPIKey))
r.Post("/users", adminUsers.Add)
r.Get("/users", adminUsers.List)
r.Put("/users/{pubkey}", adminUsers.Update)
r.Delete("/users/{pubkey}", adminUsers.Delete)
r.Post("/users/{pubkey}/extend", adminExtend.Handle)
})
})
return &http.Server{
Addr: d.Cfg.Addr(),
Handler: r,
ReadHeaderTimeout: 10 * time.Second,
ReadTimeout: 30 * time.Second,
WriteTimeout: 30 * time.Second,
IdleTimeout: 60 * time.Second,
}
}
func Shutdown(ctx context.Context, srv *http.Server) error {
shutdownCtx, cancel := context.WithTimeout(ctx, 30*time.Second)
defer cancel()
return srv.Shutdown(shutdownCtx)
}