feat: add user centric features
This commit is contained in:
@@ -46,6 +46,10 @@ type Server struct {
|
||||
shortURLH *handler.ShortURLHandler
|
||||
authH *handler.AuthHandler
|
||||
userH *handler.UserHandler
|
||||
postH *handler.PostHandler
|
||||
bookmarkH *handler.BookmarkHandler
|
||||
collectionH *handler.CollectionHandler
|
||||
profileH *handler.ProfileHandler
|
||||
|
||||
// Auth state (nil when Postgres is unavailable — /auth and /users return 503)
|
||||
authIssuer *auth.Issuer
|
||||
@@ -142,12 +146,24 @@ func (s *Server) initAuth(cfg *config.Config, logger *zap.Logger) {
|
||||
authSvc := service.NewAuthService(userRepo, tokenRepo, issuer, mail, logger, cfg.FrontendURL)
|
||||
userSvc := service.NewUserService(userRepo, r2)
|
||||
|
||||
// Social repositories + services share the same Postgres pool and R2 client.
|
||||
postRepo := repository.NewPostRepo(s.pg)
|
||||
bookmarkRepo := repository.NewBookmarkRepo(s.pg)
|
||||
collectionRepo := repository.NewCollectionRepo(s.pg)
|
||||
postSvc := service.NewPostService(postRepo, r2)
|
||||
bookmarkSvc := service.NewBookmarkService(userRepo, bookmarkRepo)
|
||||
collectionSvc := service.NewCollectionService(userRepo, collectionRepo, postRepo, r2)
|
||||
|
||||
cookieOpts := auth.CookieOptions{Domain: cfg.CookieDomain, Secure: cfg.CookieSecure}
|
||||
|
||||
s.authIssuer = issuer
|
||||
s.userRepo = userRepo
|
||||
s.authH = handler.NewAuthHandler(authSvc, cookieOpts)
|
||||
s.userH = handler.NewUserHandler(userSvc)
|
||||
s.postH = handler.NewPostHandler(postSvc)
|
||||
s.bookmarkH = handler.NewBookmarkHandler(bookmarkSvc)
|
||||
s.collectionH = handler.NewCollectionHandler(collectionSvc, postRepo, issuer, userRepo)
|
||||
s.profileH = handler.NewProfileHandler(userRepo, postRepo, collectionRepo)
|
||||
}
|
||||
|
||||
// initShortener wires Postgres + Redis + the shorturl handler/workers.
|
||||
@@ -471,13 +487,70 @@ func (s *Server) setupRouter() {
|
||||
r.Post("/me/avatar-presign", s.userH.AvatarPresign)
|
||||
})
|
||||
})
|
||||
r.Route("/collections", func(r chi.Router) {
|
||||
// TODO: register collection/bookmark handlers
|
||||
})
|
||||
r.Route("/mentions", func(r chi.Router) {
|
||||
// TODO: register mention handlers
|
||||
|
||||
// --- Social: posts ---
|
||||
r.Route("/posts", func(r chi.Router) {
|
||||
if s.postH == nil {
|
||||
r.HandleFunc("/*", s.authUnavailable)
|
||||
return
|
||||
}
|
||||
// Public — read individual posts without auth.
|
||||
r.Get("/{id}", s.postH.Get)
|
||||
// Protected — create / delete / image presign / "my posts".
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Use(middleware.RequireAuth(s.authIssuer, s.userRepo))
|
||||
r.Post("/", s.postH.Create)
|
||||
r.Delete("/{id}", s.postH.Delete)
|
||||
r.Put("/{id}/images", s.postH.ReplaceImages)
|
||||
r.Get("/mine", s.postH.ListMine)
|
||||
r.Post("/image-presign", s.postH.ImagePresign)
|
||||
})
|
||||
})
|
||||
|
||||
// --- Social: bookmarks (all protected; bookmarks are private by design) ---
|
||||
r.Route("/bookmarks", func(r chi.Router) {
|
||||
if s.bookmarkH == nil {
|
||||
r.HandleFunc("/*", s.authUnavailable)
|
||||
return
|
||||
}
|
||||
r.Use(middleware.RequireAuth(s.authIssuer, s.userRepo))
|
||||
r.Post("/", s.bookmarkH.Create)
|
||||
r.Get("/", s.bookmarkH.List)
|
||||
r.Delete("/{id}", s.bookmarkH.Delete)
|
||||
})
|
||||
|
||||
// --- Social: collections ---
|
||||
// The single-collection GET is the only route that may be hit
|
||||
// anonymously (public collections); everything else needs auth.
|
||||
r.Route("/collections", func(r chi.Router) {
|
||||
if s.collectionH == nil {
|
||||
r.HandleFunc("/*", s.authUnavailable)
|
||||
return
|
||||
}
|
||||
r.Get("/{id}", s.collectionH.Get)
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Use(middleware.RequireAuth(s.authIssuer, s.userRepo))
|
||||
r.Post("/", s.collectionH.Create)
|
||||
r.Get("/", s.collectionH.ListMine)
|
||||
r.Patch("/{id}", s.collectionH.Update)
|
||||
r.Delete("/{id}", s.collectionH.Delete)
|
||||
r.Post("/{id}/items", s.collectionH.AddItem)
|
||||
r.Put("/{id}/items/order", s.collectionH.ReorderItems)
|
||||
r.Delete("/{id}/items/{post_id}", s.collectionH.RemoveItem)
|
||||
r.Get("/containing/{post_id}", s.collectionH.ContainingPost)
|
||||
r.Post("/cover-presign", s.collectionH.CoverPresign)
|
||||
})
|
||||
})
|
||||
|
||||
// --- Public profile + feed ---
|
||||
r.Route("/u", func(r chi.Router) {
|
||||
if s.profileH == nil {
|
||||
r.HandleFunc("/*", s.authUnavailable)
|
||||
return
|
||||
}
|
||||
r.Get("/{handle}", s.profileH.Get)
|
||||
r.Get("/{handle}/feed", s.profileH.Feed)
|
||||
})
|
||||
// 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) {
|
||||
|
||||
Reference in New Issue
Block a user