3 Commits

Author SHA1 Message Date
Nazar Kanaev
3ab098db5c update changelog 2023-08-21 10:39:19 +01:00
Nazar Kanaev
6d16e93008 fix sqlite conflict handling for feeds/folders 2023-08-19 21:51:05 +01:00
Nazar Kanaev
98934daee4 update docs 2023-08-15 14:35:42 +01:00
6 changed files with 41 additions and 34 deletions

View File

@@ -1,5 +1,9 @@
# upcoming
- (fix) duplicate articles caused by the same feed addition (thanks to @adaszko)
# v2.4 (2023-08-15)
- (new) ARM build support (thanks to @tillcash & @fenuks)
- (new) auth configuration via param or env variable (thanks to @pierreprinetti)
- (new) web app manifest for an app-like experience on mobile (thanks to @qbit)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 223 KiB

After

Width:  |  Height:  |  Size: 173 KiB

View File

@@ -3,13 +3,13 @@
**yarr** (yet another rss reader) is a web-based feed aggregator which can be used both
as a desktop application and a personal self-hosted server.
It is written in Go with the frontend in Vue.js. The storage is backed by SQLite.
The app is a single binary with an embedded database (SQLite).
![screenshot](etc/promo.png)
## usage
The latest prebuilt binaries for Linux/MacOS/Windows are available
The latest prebuilt binaries for Linux/MacOS/Windows AMD64 are available
[here](https://github.com/nkanaev/yarr/releases/latest).
### macos

View File

@@ -20,20 +20,21 @@ func (s *Storage) CreateFeed(title, description, link, feedLink string, folderId
if title == "" {
title = feedLink
}
result, err := s.db.Exec(`
row := s.db.QueryRow(`
insert into feeds (title, description, link, feed_link, folder_id)
values (?, ?, ?, ?, ?)
on conflict (feed_link) do update set folder_id=?`,
on conflict (feed_link) do update set folder_id = ?
returning id`,
title, description, link, feedLink, folderId,
folderId,
)
if err != nil {
return nil
}
id, idErr := result.LastInsertId()
if idErr != nil {
return nil
}
var id int64
err := row.Scan(&id)
if err != nil {
log.Print(err)
return nil
}
return &Feed{
Id: id,
Title: title,

View File

@@ -17,6 +17,23 @@ func TestCreateFeed(t *testing.T) {
}
}
func TestCreateFeedSameLink(t *testing.T) {
db := testDB()
feed1 := db.CreateFeed("title", "", "", "http://example1.com/feed.xml", nil)
if feed1 == nil || feed1.Id == 0 {
t.Fatal("expected feed")
}
for i := 0; i < 10; i++ {
db.CreateFeed("title", "", "", "http://example2.com/feed.xml", nil)
}
feed2 := db.CreateFeed("title", "", "http://example.com", "http://example1.com/feed.xml", nil)
if feed1.Id != feed2.Id {
t.Fatalf("expected the same feed.\nwant: %#v\nhave: %#v", feed1, feed2)
}
}
func TestReadFeed(t *testing.T) {
db := testDB()
if db.GetFeed(100500) != nil {

View File

@@ -1,7 +1,6 @@
package storage
import (
"fmt"
"log"
)
@@ -13,35 +12,21 @@ type Folder struct {
func (s *Storage) CreateFolder(title string) *Folder {
expanded := true
result, err := s.db.Exec(`
row := s.db.QueryRow(`
insert into folders (title, is_expanded) values (?, ?)
on conflict (title) do nothing`,
on conflict (title) do update set title = ?
returning id`,
title, expanded,
// provide title again so that we can extract row id
title,
)
if err != nil {
fmt.Println(err)
return nil
}
var id int64
err := row.Scan(&id)
var id int64
numrows, err := result.RowsAffected()
if err != nil {
log.Print(err)
return nil
}
if numrows == 1 {
id, err = result.LastInsertId()
if err != nil {
log.Print(err)
return nil
}
} else {
err = s.db.QueryRow(`select id, is_expanded from folders where title=?`, title).Scan(&id, &expanded)
if err != nil {
log.Print(err)
return nil
}
}
return &Folder{Id: id, Title: title, IsExpanded: expanded}
}