feat: add web push and sadhna tracker
This commit is contained in:
@@ -33,6 +33,7 @@ type Server struct {
|
||||
|
||||
// Worker lifecycle
|
||||
workerCancel context.CancelFunc
|
||||
workerCtx context.Context // derived in initShortener; cancelled on Close
|
||||
|
||||
// Handlers
|
||||
bookH *handler.BookHandler
|
||||
@@ -55,6 +56,9 @@ type Server struct {
|
||||
profileH *handler.ProfileHandler
|
||||
relationH *handler.RelationshipHandler
|
||||
notificationH *handler.NotificationHandler
|
||||
sadhnaH *handler.SadhnaHandler
|
||||
pushH *handler.PushHandler
|
||||
reminderH *handler.ReminderHandler
|
||||
feedbackH *handler.FeedbackHandler
|
||||
statsH *handler.StatsHandler
|
||||
|
||||
@@ -202,13 +206,42 @@ func (s *Server) initAuth(cfg *config.Config, logger *zap.Logger) {
|
||||
cfg.SoketiAppID, cfg.SoketiAppKey, cfg.SoketiAppSecret,
|
||||
cfg.SoketiHost, cfg.SoketiPort, cfg.SoketiUseTLS, logger,
|
||||
)
|
||||
notifSvc := service.NewNotificationService(notifRepo, userRepo, mail, rtClient, logger, cfg.FrontendURL)
|
||||
pushRepo := repository.NewPushRepo(s.pg)
|
||||
pushSender := service.NewPushSender(pushRepo, cfg.VAPIDPublicKey, cfg.VAPIDPrivateKey, cfg.VAPIDSubject, logger)
|
||||
s.pushH = handler.NewPushHandler(pushSender, pushRepo)
|
||||
notifSvc := service.NewNotificationService(notifRepo, userRepo, mail, rtClient, pushSender, logger, cfg.FrontendURL)
|
||||
|
||||
// Sādhana reminders: a background worker nudges users (email + push +
|
||||
// websocket) to chant / post a quote at their local time. Shares the
|
||||
// notification seam above so all three channels fire.
|
||||
reminderRepo := repository.NewReminderRepo(s.pg)
|
||||
reminderSvc := service.NewReminderService(reminderRepo, notifSvc, logger, cfg.FrontendURL)
|
||||
s.reminderH = handler.NewReminderHandler(reminderSvc)
|
||||
wctx := s.workerCtx
|
||||
if wctx == nil {
|
||||
wctx = context.Background()
|
||||
}
|
||||
worker.NewReminderWorker(reminderSvc, logger).Run(wctx)
|
||||
|
||||
graphSvc := service.NewSocialGraphService(followRepo, blockRepo, reportRepo, postRepo, userRepo, notifSvc, mail, cfg.FeedbackAdminEmails, logger)
|
||||
s.relationH = handler.NewRelationshipHandler(graphSvc)
|
||||
s.notificationH = handler.NewNotificationHandler(notifSvc)
|
||||
s.postH = handler.NewPostHandler(postSvc, graphSvc)
|
||||
s.profileH = handler.NewProfileHandler(userRepo, postRepo, collectionRepo, graphSvc)
|
||||
|
||||
// Sādhana streak: daily chanting log + the growing-plant state. Reads
|
||||
// the chanting half from sadhna_logs and the posting half from the
|
||||
// existing posts table, so it only needs its own repo plus PostRepo +
|
||||
// UserRepo (for the rounds target).
|
||||
sadhnaRepo := repository.NewSadhnaRepo(s.pg)
|
||||
rewardRepo := repository.NewRewardRepo(s.pg)
|
||||
// Sync the reward catalog from the embedded rewards.json (idempotent).
|
||||
if err := rewardRepo.Seed(context.Background()); err != nil {
|
||||
logger.Warn("sadhna: could not seed reward catalog", zap.Error(err))
|
||||
}
|
||||
sadhnaSvc := service.NewSadhnaService(sadhnaRepo, postRepo, userRepo, rewardRepo, logger)
|
||||
s.sadhnaH = handler.NewSadhnaHandler(sadhnaSvc)
|
||||
|
||||
// Feedback + stats — share Postgres with the rest of /api/v1 and
|
||||
// the existing mailer instance.
|
||||
feedbackRepo := repository.NewFeedbackRepo(s.pg)
|
||||
@@ -224,6 +257,7 @@ func (s *Server) initAuth(cfg *config.Config, logger *zap.Logger) {
|
||||
func (s *Server) initShortener(cfg *config.Config, logger *zap.Logger) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
s.workerCancel = cancel
|
||||
s.workerCtx = ctx
|
||||
|
||||
pg, err := repository.NewPostgres(ctx, cfg.PostgresURI, logger)
|
||||
if err != nil {
|
||||
@@ -740,6 +774,38 @@ func (s *Server) setupRouter() {
|
||||
r.Get("/unread-count", s.notificationH.UnreadCount)
|
||||
r.Post("/read", s.notificationH.MarkAllRead)
|
||||
})
|
||||
|
||||
// --- Web Push --- VAPID key + per-user subscription management.
|
||||
r.Route("/push", func(r chi.Router) {
|
||||
if s.pushH == nil {
|
||||
r.HandleFunc("/*", s.authUnavailable)
|
||||
return
|
||||
}
|
||||
r.Use(middleware.RequireAuth(s.authIssuer, s.userRepo))
|
||||
r.Get("/key", s.pushH.Key)
|
||||
r.Post("/subscribe", s.pushH.Subscribe)
|
||||
r.Post("/unsubscribe", s.pushH.Unsubscribe)
|
||||
})
|
||||
|
||||
// --- Sādhana streak --- daily chanting log + growing-plant state.
|
||||
// All endpoints require a signed-in user.
|
||||
r.Route("/sadhna", func(r chi.Router) {
|
||||
if s.sadhnaH == nil {
|
||||
r.HandleFunc("/*", s.authUnavailable)
|
||||
return
|
||||
}
|
||||
r.Use(middleware.RequireAuth(s.authIssuer, s.userRepo))
|
||||
r.Get("/dashboard", s.sadhnaH.Dashboard)
|
||||
r.Get("/history", s.sadhnaH.History)
|
||||
r.Post("/chant", s.sadhnaH.SaveChant)
|
||||
r.Get("/rewards", s.sadhnaH.Rewards)
|
||||
r.Post("/rewards/ack", s.sadhnaH.AckRewards)
|
||||
if s.reminderH != nil {
|
||||
r.Get("/reminders", s.reminderH.GetPrefs)
|
||||
r.Put("/reminders", s.reminderH.UpdatePrefs)
|
||||
r.Post("/timezone", s.reminderH.SetTimezone)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
s.router = r
|
||||
|
||||
Reference in New Issue
Block a user