feat: add url shortner

This commit is contained in:
Krispire Admin
2026-05-11 16:13:22 +05:30
parent f2889a5679
commit 5e0a484239
17 changed files with 1305 additions and 13 deletions
+78 -4
View File
@@ -1,7 +1,9 @@
package server
import (
"context"
"net/http"
"path/filepath"
"github.com/go-chi/chi/v5"
chiMiddleware "github.com/go-chi/chi/v5/middleware"
@@ -10,6 +12,8 @@ import (
"github.com/prabhupadasays/spsays-bff/internal/handler"
"github.com/prabhupadasays/spsays-bff/internal/middleware"
"github.com/prabhupadasays/spsays-bff/internal/repository"
"github.com/prabhupadasays/spsays-bff/internal/worker"
"github.com/prabhupadasays/spsays-bff/pkg/shorturl"
"go.uber.org/zap"
)
@@ -19,6 +23,11 @@ type Server struct {
logger *zap.Logger
router chi.Router
mongo *repository.MongoDB
pg *repository.Postgres
redis *repository.Redis
// Worker lifecycle
workerCancel context.CancelFunc
// Handlers
bookH *handler.BookHandler
@@ -31,6 +40,7 @@ type Server struct {
otherDocH *handler.OtherDocumentHandler
transcriptionH *handler.TranscriptionHandler
searchH *handler.SearchHandler
shortURLH *handler.ShortURLHandler
}
// New creates a new Server with all routes registered.
@@ -72,10 +82,52 @@ func New(cfg *config.Config, logger *zap.Logger) *Server {
searchH: handler.NewSearchHandler(tsClient),
}
// URL shortener: Postgres (canonical) + Redis (queue + hot cache) + background workers.
// Connection failures degrade gracefully — the BFF stays up; only /share returns 503.
s.initShortener(cfg, logger)
s.setupRouter()
return s
}
// initShortener wires Postgres + Redis + the shorturl handler/workers.
// On connection failure it logs and leaves the components nil — the handler
// short-circuits to 503 so the rest of the BFF keeps working.
func (s *Server) initShortener(cfg *config.Config, logger *zap.Logger) {
ctx, cancel := context.WithCancel(context.Background())
s.workerCancel = cancel
pg, err := repository.NewPostgres(ctx, cfg.PostgresURI, logger)
if err != nil {
logger.Warn("URL shortener: Postgres unavailable — /share endpoints will return 503",
zap.Error(err))
return
}
s.pg = pg
migrationsDir, err := filepath.Abs("migrations")
if err == nil {
if err := pg.RunMigrations(ctx, migrationsDir); err != nil {
logger.Warn("URL shortener: migrations failed", zap.Error(err))
}
}
rd, err := repository.NewRedis(ctx, cfg.RedisURI, logger)
if err != nil {
logger.Warn("URL shortener: Redis unavailable — /share endpoints will return 503",
zap.Error(err))
return
}
s.redis = rd
shortRepo := repository.NewShortURLRepo(pg)
queue := shorturl.NewQueue(rd.Client)
s.shortURLH = handler.NewShortURLHandler(shortRepo, queue, logger)
w := worker.NewShortURLWorker(shortRepo, queue, logger)
w.Run(ctx)
}
// Router returns the chi router.
func (s *Server) Router() http.Handler {
return s.router
@@ -83,10 +135,20 @@ func (s *Server) Router() http.Handler {
// Close shuts down all connections gracefully.
func (s *Server) Close() {
if s.workerCancel != nil {
s.workerCancel()
}
if err := s.mongo.Close(); err != nil {
s.logger.Error("failed to close MongoDB connection", zap.Error(err))
}
// TODO: close PostgreSQL, Redis, etc. connections
if s.pg != nil {
s.pg.Close()
}
if s.redis != nil {
if err := s.redis.Close(); err != nil {
s.logger.Error("failed to close Redis connection", zap.Error(err))
}
}
s.logger.Info("all connections closed")
}
@@ -319,9 +381,21 @@ func (s *Server) setupRouter() {
// TODO: register mention handlers
})
// URL Shortener
r.Route("/s", func(r chi.Router) {
// TODO: register URL shortener handlers
// URL Shortener — Postgres-backed, Redis-queued.
// The handler short-circuits to 503 if Postgres or Redis are unavailable.
r.Route("/share", func(r chi.Router) {
if s.shortURLH != nil {
r.Post("/", s.shortURLH.Create)
r.Get("/{code}", s.shortURLH.Resolve)
} else {
stub := func(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusServiceUnavailable)
w.Write([]byte(`{"success":false,"error":{"code":503,"message":"url shortener unavailable"}}`))
}
r.Post("/", stub)
r.Get("/{code}", stub)
}
})
// Push Notifications