package rssapi import ( "bytes" "encoding/json" "fmt" "io" "net/http" "net/url" "write-server/internal/config" ) type Feed struct { URL string `json:"url"` FeedTitle string `json:"feedTitle"` SiteURL string `json:"siteUrl,omitempty"` Error string `json:"error,omitempty"` } type FeedHealth struct { URL string `json:"url"` Status int `json:"status"` OK bool `json:"ok"` Latency int `json:"latency,omitempty"` Error string `json:"error,omitempty"` } type Link struct { Name string `json:"name"` URL string `json:"url"` Description string `json:"description,omitempty"` Image string `json:"image,omitempty"` Hidden bool `json:"hidden,omitempty"` RSS string `json:"rss,omitempty"` AddedAt string `json:"addedAt,omitempty"` } func rssFetch(path string, method string, body interface{}) (map[string]interface{}, error) { u := config.RSS_API_BASE + path var req *http.Request var err error if body != nil && (method == "POST" || method == "PUT") { data, _ := json.Marshal(body) req, err = http.NewRequest(method, u, bytes.NewReader(data)) if err != nil { return nil, err } req.Header.Set("Content-Type", "application/json") } else { req, err = http.NewRequest(method, u, nil) if err != nil { return nil, err } } if config.RSS_API_TOKEN != "" { q := req.URL.Query() q.Set("token", config.RSS_API_TOKEN) req.URL.RawQuery = q.Encode() } resp, err := http.DefaultClient.Do(req) if err != nil { return nil, err } defer resp.Body.Close() data, err := io.ReadAll(resp.Body) if err != nil { return nil, err } var result map[string]interface{} if err := json.Unmarshal(data, &result); err != nil { return nil, fmt.Errorf("RSS API error: %d", resp.StatusCode) } return result, nil } func GetFeeds() ([]Feed, error) { result, err := rssFetch("/api/rss/feeds", "GET", nil) if err != nil { return nil, err } var feeds []Feed if raw, ok := result["feeds"]; ok { data, _ := json.Marshal(raw) json.Unmarshal(data, &feeds) } return feeds, nil } func AddFeed(feedURL, feedTitle string) error { _, err := rssFetch("/api/rss/feeds", "POST", map[string]string{ "url": feedURL, "feedTitle": feedTitle, }) return err } func DeleteFeed(feedURL string) error { _, err := rssFetch("/api/rss/feeds?url="+url.QueryEscape(feedURL), "DELETE", nil) return err } func UpdateFeed(oldURL, newURL, feedTitle string) error { _, err := rssFetch("/api/rss/feeds", "PUT", map[string]string{ "oldUrl": oldURL, "url": newURL, "feedTitle": feedTitle, }) return err } func GetLinks(all bool) ([]Link, error) { path := "/api/rss/links" if all { path += "?all=1" } result, err := rssFetch(path, "GET", nil) if err != nil { return nil, err } var links []Link if raw, ok := result["links"]; ok { data, _ := json.Marshal(raw) json.Unmarshal(data, &links) } return links, nil } func AddLink(link Link) error { _, err := rssFetch("/api/rss/links", "POST", link) return err } func UpdateLink(oldURL string, data map[string]interface{}) error { body := map[string]interface{}{"oldUrl": oldURL} for k, v := range data { if k != "oldUrl" { body[k] = v } } _, err := rssFetch("/api/rss/links", "PUT", body) return err } func DeleteLink(linkURL string) error { _, err := rssFetch("/api/rss/links?url="+url.QueryEscape(linkURL), "DELETE", nil) return err } func CheckLinkHealth(targetURL string) (map[string]interface{}, error) { return rssFetch("/api/rss/links?health="+url.QueryEscape(targetURL), "GET", nil) }