247 lines
6.4 KiB
Go
247 lines
6.4 KiB
Go
package feeds
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"strconv"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
// Handler handles HTTP requests for feeds
|
|
type Handler struct {
|
|
service *Service
|
|
}
|
|
|
|
// NewHandler creates a new feed handler
|
|
func NewHandler(service *Service) *Handler {
|
|
return &Handler{service: service}
|
|
}
|
|
|
|
// RegisterRoutes registers the feed routes with the given router group
|
|
func (h *Handler) RegisterRoutes(router *gin.RouterGroup) {
|
|
feeds := router.Group("/feeds")
|
|
{
|
|
feeds.GET("", h.handleListFeeds)
|
|
feeds.POST("", h.handleAddFeed)
|
|
feeds.GET("/:id", h.handleGetFeed)
|
|
feeds.DELETE("/:id", h.handleDeleteFeed)
|
|
feeds.POST("/:id/refresh", h.handleRefreshFeed)
|
|
feeds.POST("/refresh", h.handleRefreshAllFeeds)
|
|
feeds.POST("/import/opml", h.handleImportOPML)
|
|
|
|
// Entry routes
|
|
feeds.GET("/entries", h.handleListEntries)
|
|
feeds.GET("/entries/:id", h.handleGetEntry)
|
|
feeds.POST("/entries/:id/read", h.handleMarkEntryAsRead)
|
|
feeds.POST("/entries/read-all", h.handleMarkAllAsRead)
|
|
feeds.POST("/entries/:id/full-content", h.handleFetchFullContent)
|
|
}
|
|
|
|
// Test endpoint
|
|
router.POST("/test/feeds/reset", h.handleReset)
|
|
}
|
|
|
|
// handleListFeeds handles GET /api/feeds
|
|
func (h *Handler) handleListFeeds(c *gin.Context) {
|
|
feeds, err := h.service.GetFeeds()
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, feeds)
|
|
}
|
|
|
|
// handleAddFeed handles POST /api/feeds
|
|
func (h *Handler) handleAddFeed(c *gin.Context) {
|
|
var req struct {
|
|
URL string `json:"url" binding:"required"`
|
|
}
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
feed, err := h.service.AddFeed(req.URL)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, feed)
|
|
}
|
|
|
|
// handleGetFeed handles GET /api/feeds/:id
|
|
func (h *Handler) handleGetFeed(c *gin.Context) {
|
|
id := c.Param("id")
|
|
feed, err := h.service.GetFeed(id)
|
|
if err != nil {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "Feed not found"})
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, feed)
|
|
}
|
|
|
|
// handleDeleteFeed handles DELETE /api/feeds/:id
|
|
func (h *Handler) handleDeleteFeed(c *gin.Context) {
|
|
id := c.Param("id")
|
|
if err := h.service.DeleteFeed(id); err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
c.Status(http.StatusOK)
|
|
}
|
|
|
|
// handleRefreshFeed handles POST /api/feeds/:id/refresh
|
|
func (h *Handler) handleRefreshFeed(c *gin.Context) {
|
|
id := c.Param("id")
|
|
if err := h.service.RefreshFeed(id); err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
c.Status(http.StatusOK)
|
|
}
|
|
|
|
// handleRefreshAllFeeds handles POST /api/feeds/refresh
|
|
func (h *Handler) handleRefreshAllFeeds(c *gin.Context) {
|
|
if err := h.service.RefreshAllFeeds(); err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
c.Status(http.StatusOK)
|
|
}
|
|
|
|
// handleImportOPML handles POST /api/feeds/import
|
|
func (h *Handler) handleImportOPML(c *gin.Context) {
|
|
file, _, err := c.Request.FormFile("file")
|
|
if err != nil {
|
|
// Check if URL is provided instead
|
|
url := c.PostForm("url")
|
|
if url != "" {
|
|
resp, err := http.Get(url)
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("Failed to download OPML: %v", err)})
|
|
return
|
|
}
|
|
defer func() {
|
|
if err := resp.Body.Close(); err != nil {
|
|
fmt.Printf("Error closing response body: %v\n", err)
|
|
}
|
|
}()
|
|
|
|
feeds, err := h.service.ImportOPML(resp.Body)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, gin.H{"imported": len(feeds)})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "No file or URL provided"})
|
|
return
|
|
}
|
|
defer func() {
|
|
if err := file.Close(); err != nil {
|
|
fmt.Printf("Error closing file: %v\n", err)
|
|
}
|
|
}()
|
|
|
|
feeds, err := h.service.ImportOPML(file)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{"imported": len(feeds)})
|
|
}
|
|
|
|
// handleListEntries handles GET /api/feeds/entries
|
|
func (h *Handler) handleListEntries(c *gin.Context) {
|
|
feedID := c.Query("feedId")
|
|
unreadOnly, _ := strconv.ParseBool(c.Query("unreadOnly"))
|
|
|
|
entries, err := h.service.GetEntries(feedID, unreadOnly)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, entries)
|
|
}
|
|
|
|
// handleGetEntry handles GET /api/feeds/entries/:id
|
|
func (h *Handler) handleGetEntry(c *gin.Context) {
|
|
id := c.Param("id")
|
|
entry, err := h.service.GetEntry(id)
|
|
if err != nil {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "Entry not found"})
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, entry)
|
|
}
|
|
|
|
// handleMarkEntryAsRead handles POST /api/feeds/entries/:id/read
|
|
func (h *Handler) handleMarkEntryAsRead(c *gin.Context) {
|
|
id := c.Param("id")
|
|
if err := h.service.MarkEntryAsRead(id); err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
c.Status(http.StatusOK)
|
|
}
|
|
|
|
// handleFetchFullContent handles POST /api/feeds/entries/:id/full-content
|
|
func (h *Handler) handleFetchFullContent(c *gin.Context) {
|
|
id := c.Param("id")
|
|
if err := h.service.FetchFullContent(id); err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
// Get the updated entry
|
|
entry, err := h.service.GetEntry(id)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, entry)
|
|
}
|
|
|
|
// handleReset handles POST /api/test/feeds/reset
|
|
func (h *Handler) handleReset(c *gin.Context) {
|
|
// Delete all entries
|
|
if err := h.service.db.Exec("DELETE FROM entries").Error; err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
// Delete all feeds
|
|
if err := h.service.db.Exec("DELETE FROM feeds").Error; err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
c.Status(http.StatusOK)
|
|
}
|
|
|
|
func (h *Handler) handleMarkAllAsRead(c *gin.Context) {
|
|
feedID := c.Query("feedId")
|
|
|
|
var err error
|
|
if feedID != "" {
|
|
// Mark all entries as read for a specific feed
|
|
err = h.service.MarkAllEntriesAsRead(feedID)
|
|
} else {
|
|
// Mark all entries as read across all feeds
|
|
err = h.service.MarkAllEntriesAsRead("")
|
|
}
|
|
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{"success": true})
|
|
}
|