Files
Nip-05-api/internal/http/server.go
Michilis fe2b95258d feat: admin endpoints to reset username sync flags
Add POST /v1/admin/users/{pubkey}/reset-username and
POST /v1/admin/users/reset-usernames to clear manual_username
and last_synced_at so nostr profile sync re-evaluates users.
Includes OpenAPI docs, audit actions, and tests.
2026-05-06 19:31:13 +00:00

105 lines
3.4 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.Post("/users/reset-usernames", adminUsers.ResetAllUsernames)
r.Put("/users/{pubkey}", adminUsers.Update)
r.Delete("/users/{pubkey}", adminUsers.Delete)
r.Post("/users/{pubkey}/extend", adminExtend.Handle)
r.Post("/users/{pubkey}/reset-username", adminUsers.ResetUsername)
})
})
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)
}