Commit Diff


commit - 38dff9091688843ea4d52933f27306435c054355
commit + ed95fc8404c32829d0715ad12d6150cb663c049c
blob - e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
blob + 87330582443cec7a37beb0894694a7680aef3d02
--- .gitignore
+++ .gitignore
@@ -0,0 +1 @@
+/rss.db
\ No newline at end of file
blob - 8ef5e3310035605efd81c6ea5d6b857f69f8e6f9
blob + a01942c5a3ee04ca4a83d453267cbe45f2af0f4d
--- cmd/rss/rss.go
+++ cmd/rss/rss.go
@@ -1,10 +1,14 @@
 package main
 
 import (
+	"log"
+
 	"github.com/streatCodes/rss/internal/service"
 )
 
 func main() {
-	_ = service.NewService()
-
+	_, err := service.New("rss.db")
+	if err != nil {
+		log.Fatalf("Error initiating service %s\n", err)
+	}
 }
blob - a0881e7b1d7d6acade066ef3f776676828712d12
blob + d0e504a23f18937db7d92bea2393ef5dea7f23b0
--- go.mod
+++ go.mod
@@ -1,3 +1,7 @@
 module github.com/streatCodes/rss
 
 go 1.24.1
+
+require go.etcd.io/bbolt v1.4.0
+
+require golang.org/x/sys v0.29.0 // indirect
blob - ba5a2e0707e500d09c40d17af9491c793c1a9a11
blob + 569fb214121210f5a197fcb76bb0289a2499044a
--- internal/service/search.go
+++ internal/service/search.go
@@ -2,7 +2,11 @@ package service
 
 import (
 	"html/template"
+	"log"
 	"net/http"
+	"net/url"
+
+	"github.com/streatCodes/rss/rss"
 )
 
 type TemplateData struct {
@@ -21,6 +25,26 @@ func (service *Service) homeHandler(w http.ResponseWri
 func (service *Service) searchHandler(w http.ResponseWriter, r *http.Request) {
 	isHtmx := r.Header.Get("HX-Request")
 	searchResult := r.URL.Query().Get("search")
+
+	log.Printf("Search: %s\n", searchResult)
+
+	//If the search query is a URL then ingest the feed
+	if parsedURL, err := url.ParseRequestURI(searchResult); err == nil {
+		log.Println("Found URL")
+		res, err := http.Get(searchResult)
+		if err != nil {
+			panic("TODO")
+		}
+
+		f, err := rss.Decode(res.Body)
+		if err != nil {
+			panic("TODO")
+		}
+
+		key := []byte(parsedURL.String())
+		service.db.SaveFeed(key, &f.Channel)
+	}
+
 	if isHtmx == "true" {
 		render(w, "results", []string{searchResult})
 	} else {
blob - de3497b969992add0cd7859db4149a5440db6953
blob + c25c607897bf71ad59b2e2c57879c2fea54275f2
--- internal/service/service.go
+++ internal/service/service.go
@@ -3,13 +3,22 @@ package service
 import (
 	"log"
 	"net/http"
+	"time"
+
+	"github.com/streatCodes/rss/internal/db"
+	bolt "go.etcd.io/bbolt"
 )
 
 type Service struct {
+	db *db.DB
 }
 
-func NewService() Service {
-	service := Service{}
+func New(dbPath string) (*Service, error) {
+	db, err := db.New(dbPath, 0600, &bolt.Options{Timeout: 1 * time.Second})
+	if err != nil {
+		return nil, err
+	}
+	service := &Service{db: db}
 
 	mux := http.NewServeMux()
 
@@ -21,5 +30,5 @@ func NewService() Service {
 	log.Printf("Server running at %s", addr)
 	log.Fatal(http.ListenAndServe(addr, mux))
 
-	return service
+	return service, nil
 }
blob - /dev/null
blob + 242b02fed7d0b62a37f7f060050ed7d4a8ecb14b (mode 644)
--- /dev/null
+++ internal/db/db.go
@@ -0,0 +1,25 @@
+package db
+
+import (
+	"os"
+
+	bolt "go.etcd.io/bbolt"
+)
+
+type DB struct {
+	raw *bolt.DB
+}
+
+func New(path string, mode os.FileMode, options *bolt.Options) (*DB, error) {
+	db, err := bolt.Open(path, mode, options)
+	if err != nil {
+		return nil, err
+	}
+
+	err = db.Update(func(tx *bolt.Tx) error {
+		tx.CreateBucketIfNotExists(feedsBucket)
+		return nil
+	})
+
+	return &DB{raw: db}, err
+}
blob - /dev/null
blob + 773e9b46e59c135b9444cd9365bd7193d0b39b57 (mode 644)
--- /dev/null
+++ internal/db/db_test.go
@@ -0,0 +1,47 @@
+package db
+
+import (
+	"os"
+	"path/filepath"
+	"testing"
+	"time"
+
+	"github.com/streatCodes/rss/rss"
+	bolt "go.etcd.io/bbolt"
+)
+
+func TestSaveFeed(t *testing.T) {
+	tempDir := t.TempDir()
+	filePath := filepath.Join(tempDir, "test.db")
+
+	f, err := os.Open("example.rss")
+	if err != nil {
+		t.Error(err)
+	}
+	defer f.Close()
+
+	feed, err := rss.Decode(f)
+	if err != nil {
+		t.Error(err)
+	}
+
+	db, err := New(filePath, 0600, &bolt.Options{Timeout: 1 * time.Second})
+	if err != nil {
+		t.Error(err)
+	}
+
+	feedKey := []byte("test")
+	err = db.SaveFeed(feedKey, &feed.Channel)
+	if err != nil {
+		t.Error(err)
+	}
+
+	retrievedFeed, err := db.GetFeed(feedKey)
+	if err != nil {
+		t.Error(err)
+	}
+
+	if feed.Channel.Title != retrievedFeed.Title {
+		t.Errorf("Retrieved feed title: expected %q, got %q", feed.Channel.Title, retrievedFeed.Title)
+	}
+}
blob - /dev/null
blob + cd7597ce5e6e0a566dd4394a1870fa4f4febebb6 (mode 644)
--- /dev/null
+++ internal/db/example.rss
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<rss version="2.0">
+<channel>
+ <title>RSS Title</title>
+ <description>This is an example of an RSS feed</description>
+ <link>http://www.example.com/main.html</link>
+ <copyright>2020 Example.com All rights reserved</copyright>
+ <lastBuildDate>Mon, 06 Sep 2010 00:01:00 +0000</lastBuildDate>
+ <pubDate>Sun, 06 Sep 2009 16:20:00 +0000</pubDate>
+ <ttl>1800</ttl>
+
+ <item>
+  <title>Example entry</title>
+  <description>Here is some text containing an interesting description.</description>
+  <link>http://www.example.com/blog/post/1</link>
+  <guid isPermaLink="false">7bd204c6-1655-4c27-aeee-53f933c5395f</guid>
+  <pubDate>Sun, 06 Sep 2009 16:20:00 +0000</pubDate>
+ </item>
+
+</channel>
+</rss>
\ No newline at end of file
blob - /dev/null
blob + 1a8dad7d542e277fcd1d8762b09fcbddaa378b5e (mode 644)
--- /dev/null
+++ internal/db/feeds.go
@@ -0,0 +1,46 @@
+package db
+
+import (
+	"encoding/json"
+
+	"github.com/streatCodes/rss/rss"
+	bolt "go.etcd.io/bbolt"
+)
+
+var feedsBucket = []byte("feeds")
+
+func (db *DB) SaveFeed(key []byte, feed *rss.Channel) error {
+	value, err := json.Marshal(feed)
+	if err != nil {
+		return err
+	}
+
+	err = db.raw.Update(func(tx *bolt.Tx) error {
+		bucket := tx.Bucket(feedsBucket)
+		if err := bucket.Put(key, value); err != nil {
+			return err
+		}
+
+		return nil
+	})
+
+	return err
+}
+
+func (db *DB) GetFeed(key []byte) (*rss.Channel, error) {
+	var channelBytes []byte
+	err := db.raw.View(func(tx *bolt.Tx) error {
+		if bucket := tx.Bucket(feedsBucket); bucket != nil {
+			channelBytes = bucket.Get(key)
+		}
+		return nil
+	})
+
+	if err != nil {
+		return nil, err
+	}
+
+	var channel rss.Channel
+	err = json.Unmarshal(channelBytes, &channel)
+	return &channel, err
+}
blob - /dev/null
blob + 27c73bfc28798dc23ad8db4a3c53c477b1ef55f6 (mode 644)
--- /dev/null
+++ go.sum
@@ -0,0 +1,4 @@
+go.etcd.io/bbolt v1.4.0 h1:TU77id3TnN/zKr7CO/uk+fBCwF2jGcMuw2B/FMAzYIk=
+go.etcd.io/bbolt v1.4.0/go.mod h1:AsD+OCi/qPN1giOX1aiLAha3o1U8rAz65bvN4j0sRuk=
+golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
+golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=