add fever doc & fix fever issues

This commit is contained in:
icefed 2023-09-13 22:39:04 +08:00 committed by nkanaev
parent 3278ba4eac
commit 5f82a9e339
3 changed files with 55 additions and 6 deletions

View File

@ -30,6 +30,27 @@ and run [the script](etc/install-linux.sh).
For self-hosting, see `yarr -h` for auth, tls & server configuration flags.
For building from source code, see [build.md](build.md)
## Fever API support
Fever API is a kind of RSS HTTP API interface, because the Fever API definition is not very clear, so the implementation of Fever server and Client may have some compatibility problems.
The Fever API implemented by Yarr is based on the Fever API spec: https://github.com/DigitalDJ/tinytinyrss-fever-plugin/blob/master/fever-api.md.
Here are some Apps that have been tested to work with yarr. Feel free to test other Clients/Apps and update the list here.
> Different apps support different URL/Address formats. Please note whether the URL entered has `http://` scheme and `/` suffix.
| App | Platforms | Config Server URL |
|:------------------------------------------------------------------------- | ---------------- |:--------------------------------------------------- |
| [Reeder](https://reederapp.com/) | MacOS<br>iOS | 127.0.0.1:7070/fever<br>http://127.0.0.1:7070/fever |
| [ReadKit](https://readkit.app/) | MacOS<br>iOS | http://127.0.0.1:7070/fever |
| [Fluent Reader](https://github.com/yang991178/fluent-reader) | MacOS<br>Windows | http://127.0.0.1:7070/fever/ |
| [Unread](https://apps.apple.com/us/app/unread-an-rss-reader/id1363637349) | iOS | http://127.0.0.1:7070/fever |
| [Fiery Feeds](https://voidstern.net/fiery-feeds) | MacOS<br>iOS | http://127.0.0.1:7070/fever |
If you are having trouble using Fever, please open an issue and @icefed, thanks.
## credits
[Feather](http://feathericons.com/) for icons.

View File

@ -54,7 +54,7 @@ type FeverFavicon struct {
}
func writeFeverJSON(c *router.Context, data map[string]interface{}, lastRefreshed int64) {
data["api_version"] = 1
data["api_version"] = 3
data["auth"] = 1
data["last_refreshed_on_time"] = lastRefreshed
c.JSON(http.StatusOK, data)
@ -77,9 +77,10 @@ func getLastRefreshedOnTime(httpStates map[int64]storage.HTTPState) int64 {
func (s *Server) feverAuth(c *router.Context) bool {
if s.Username != "" && s.Password != "" {
apiKey := c.Req.FormValue("api_key")
apiKey = strings.ToLower(apiKey)
md5HashValue := md5.Sum([]byte(fmt.Sprintf("%s:%s", s.Username, s.Password)))
hexMD5HashValue := fmt.Sprintf("%x", md5HashValue[:])
if auth.StringsEqual(apiKey, hexMD5HashValue) {
if !auth.StringsEqual(apiKey, hexMD5HashValue) {
return false
}
}
@ -97,7 +98,7 @@ func (s *Server) handleFever(c *router.Context) {
c.Req.ParseForm()
if !s.feverAuth(c) {
c.JSON(http.StatusOK, map[string]interface{}{
"api_version": 1,
"api_version": 3,
"auth": 0,
"last_refreshed_on_time": 0,
})
@ -123,7 +124,7 @@ func (s *Server) handleFever(c *router.Context) {
s.feverMarkHandler(c)
default:
c.JSON(http.StatusOK, map[string]interface{}{
"api_version": 1,
"api_version": 3,
"auth": 1,
"last_refreshed_on_time": getLastRefreshedOnTime(s.db.ListHTTPStates()),
})
@ -277,8 +278,11 @@ func (s *Server) feverItemsHandler(c *router.Context) {
}
}
totalItems := s.db.CountItems(storage.ItemFilter{})
writeFeverJSON(c, map[string]interface{}{
"items": feverItems,
"total_items": totalItems,
}, getLastRefreshedOnTime(s.db.ListHTTPStates()))
}
@ -382,4 +386,8 @@ func (s *Server) feverMarkHandler(c *router.Context) {
c.Out.WriteHeader(http.StatusBadRequest)
return
}
c.JSON(http.StatusOK, map[string]interface{}{
"api_version": 3,
"auth": 1,
})
}

View File

@ -177,6 +177,23 @@ func listQueryPredicate(filter ItemFilter, newestFirst bool) (string, []interfac
return predicate, args
}
func (s *Storage) CountItems(filter ItemFilter) int {
predicate, args := listQueryPredicate(filter, false)
var count int
query := fmt.Sprintf(`
select count(*)
from items
where %s
`, predicate)
err := s.db.QueryRow(query, args...).Scan(&count)
if err != nil {
log.Print(err)
return 0
}
return count
}
func (s *Storage) ListItems(filter ItemFilter, limit int, newestFirst bool, withContent bool) []Item {
predicate, args := listQueryPredicate(filter, newestFirst)
result := make([]Item, 0, 0)
@ -185,9 +202,12 @@ func (s *Storage) ListItems(filter ItemFilter, limit int, newestFirst bool, with
if !newestFirst {
order = "date asc, id asc"
}
if filter.IDs != nil || filter.SinceID != nil || filter.MaxID != nil {
if filter.IDs != nil || filter.SinceID != nil {
order = "i.id asc"
}
if filter.MaxID != nil {
order = "i.id desc"
}
selectCols := "i.id, i.guid, i.feed_id, i.title, i.link, i.date, i.status, i.image, i.podcast_url"
if withContent {