aboutsummaryrefslogtreecommitdiffstats
path: root/api/api.go
diff options
context:
space:
mode:
authorAdam Mathes <adam@adammathes.com>2026-02-14 09:42:14 -0800
committerAdam Mathes <adam@adammathes.com>2026-02-14 09:42:14 -0800
commit6fa13a06411048f3217397f4285b3e64e7b9ee58 (patch)
treeaf5ec83884bb45a10d51cf5be3ae895ebf1bcff8 /api/api.go
parent23947045c011e84149bc1b9d48805e57bb0bb3ba (diff)
downloadneko-6fa13a06411048f3217397f4285b3e64e7b9ee58.tar.gz
neko-6fa13a06411048f3217397f4285b3e64e7b9ee58.tar.bz2
neko-6fa13a06411048f3217397f4285b3e64e7b9ee58.zip
feature: implement full OPML and Text import/export (fixing NK-r6nhj0)
Diffstat (limited to 'api/api.go')
-rw-r--r--api/api.go52
1 files changed, 51 insertions, 1 deletions
diff --git a/api/api.go b/api/api.go
index 5e7618d..9ea35c8 100644
--- a/api/api.go
+++ b/api/api.go
@@ -2,6 +2,7 @@ package api
import (
"encoding/json"
+ "fmt"
"log"
"net/http"
"strconv"
@@ -10,6 +11,7 @@ import (
"adammathes.com/neko/config"
"adammathes.com/neko/internal/crawler"
"adammathes.com/neko/internal/exporter"
+ "adammathes.com/neko/internal/importer"
"adammathes.com/neko/models/feed"
"adammathes.com/neko/models/item"
)
@@ -36,6 +38,7 @@ func (s *Server) routes() {
s.HandleFunc("/feed/", s.HandleFeed)
s.HandleFunc("/tag", s.HandleCategory)
s.HandleFunc("/export/", s.HandleExport)
+ s.HandleFunc("/import", s.HandleImport)
s.HandleFunc("/crawl", s.HandleCrawl)
}
@@ -217,10 +220,57 @@ func (s *Server) HandleExport(w http.ResponseWriter, r *http.Request) {
jsonError(w, "format required", http.StatusBadRequest)
return
}
- w.Header().Set("Content-Type", "text/plain") // exporter handles formats internally
+
+ contentType := "text/plain"
+ extension := "txt"
+ switch format {
+ case "opml":
+ contentType = "application/xml"
+ extension = "opml"
+ case "json":
+ contentType = "application/json"
+ extension = "json"
+ case "html":
+ contentType = "text/html"
+ extension = "html"
+ }
+
+ w.Header().Set("Content-Type", contentType)
+ w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=neko_export.%s", extension))
w.Write([]byte(exporter.ExportFeeds(format)))
}
+func (s *Server) HandleImport(w http.ResponseWriter, r *http.Request) {
+ if r.Method != http.MethodPost {
+ jsonError(w, "method not allowed", http.StatusMethodNotAllowed)
+ return
+ }
+
+ format := r.FormValue("format")
+ if format == "" {
+ format = "opml" // default to opml
+ }
+
+ file, _, err := r.FormFile("file")
+ if err != nil {
+ jsonError(w, "file required", http.StatusBadRequest)
+ return
+ }
+ defer file.Close()
+
+ err = importer.ImportFeeds(format, file)
+ if err != nil {
+ log.Println(err)
+ jsonError(w, "import failed: "+err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ // Trigger crawl after import
+ go crawler.Crawl()
+
+ jsonResponse(w, map[string]string{"status": "ok"})
+}
+
func (s *Server) HandleCrawl(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
jsonError(w, "method not allowed", http.StatusMethodNotAllowed)