cmd: modernize -fix ./cmd/...

This commit is contained in:
nkanaev
2026-04-27 20:44:24 +01:00
parent 7a5f8a5e41
commit 49c704037b
15 changed files with 53 additions and 75 deletions

View File

@@ -51,7 +51,7 @@ func Template(path string) *template.Template {
return tmpl return tmpl
} }
func Render(path string, writer io.Writer, data interface{}) { func Render(path string, writer io.Writer, data any) {
tmpl := Template(path) tmpl := Template(path)
tmpl.Execute(writer, data) tmpl.Execute(writer, data)
} }

View File

@@ -9,6 +9,7 @@ import (
"fmt" "fmt"
"io" "io"
"regexp" "regexp"
"slices"
"strconv" "strconv"
"strings" "strings"
@@ -225,10 +226,8 @@ func hasRequiredAttributes(tagName string, attributes []string) bool {
for element, attrs := range elements { for element, attrs := range elements {
if tagName == element { if tagName == element {
for _, attribute := range attributes { for _, attribute := range attributes {
for _, attr := range attrs { if slices.Contains(attrs, attribute) {
if attr == attribute { return true
return true
}
} }
} }
@@ -285,13 +284,7 @@ func isValidIframeSource(baseURL, src string) bool {
return true return true
} }
for _, safeDomain := range whitelist { return slices.Contains(whitelist, domain)
if safeDomain == domain {
return true
}
}
return false
} }
func getTagAllowList() map[string][]string { func getTagAllowList() map[string][]string {
@@ -355,13 +348,7 @@ func getTagAllowList() map[string][]string {
} }
func inList(needle string, haystack []string) bool { func inList(needle string, haystack []string) bool {
for _, element := range haystack { return slices.Contains(haystack, needle)
if element == needle {
return true
}
}
return false
} }
func isBlockedTag(tagName string) bool { func isBlockedTag(tagName string) bool {
@@ -371,13 +358,7 @@ func isBlockedTag(tagName string) bool {
"style", "style",
} }
for _, element := range blacklist { return slices.Contains(blacklist, tagName)
if element == tagName {
return true
}
}
return false
} }
/* /*

View File

@@ -2,6 +2,7 @@ package scraper
import ( import (
"net/url" "net/url"
"slices"
"strings" "strings"
"github.com/nkanaev/yarr/src/content/htmlutil" "github.com/nkanaev/yarr/src/content/htmlutil"
@@ -22,10 +23,8 @@ func FindFeeds(body string, base string) map[string]string {
isFeedLink := func(n *html.Node) bool { isFeedLink := func(n *html.Node) bool {
if n.Type == html.ElementNode && n.Data == "link" { if n.Type == html.ElementNode && n.Data == "link" {
t := htmlutil.Attr(n, "type") t := htmlutil.Attr(n, "type")
for _, tt := range linkTypes { if slices.Contains(linkTypes, t) {
if tt == t { return true
return true
}
} }
} }
return false return false

View File

@@ -216,7 +216,7 @@ func TestRSSTitleHTMLTags(t *testing.T) {
`)) `))
have := []string{feed.Items[0].Title, feed.Items[1].Title} have := []string{feed.Items[0].Title, feed.Items[1].Title}
want := []string{"title in p", "very strong title"} want := []string{"title in p", "very strong title"}
for i := 0; i < len(want); i++ { for i := range want {
if want[i] != have[i] { if want[i] != have[i] {
t.Errorf("title doesn't match\nwant: %#v\nhave: %#v\n", want[i], have[i]) t.Errorf("title doesn't match\nwant: %#v\nhave: %#v\n", want[i], have[i])
} }
@@ -241,7 +241,7 @@ func TestRSSIsPermalink(t *testing.T) {
URL: "http://example.com/posts/1", URL: "http://example.com/posts/1",
}, },
} }
for i := 0; i < len(want); i++ { for i := range want {
if !reflect.DeepEqual(want, have) { if !reflect.DeepEqual(want, have) {
t.Errorf("Failed to handle isPermalink\nwant: %#v\nhave: %#v\n", want[i], have[i]) t.Errorf("Failed to handle isPermalink\nwant: %#v\nhave: %#v\n", want[i], have[i])
} }

View File

@@ -46,7 +46,7 @@ func TestSafeXMLReaderPartial1(t *testing.T) {
f = NewSafeXMLReader(f) f = NewSafeXMLReader(f)
buf := make([]byte, 1) buf := make([]byte, 1)
for i := 0; i < len(want); i++ { for i := range want {
n, err := f.Read(buf) n, err := f.Read(buf)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)

View File

@@ -44,7 +44,7 @@ func (m *Middleware) Handler(c *router.Context) {
c.Redirect(rootUrl) c.Redirect(rootUrl)
return return
} else { } else {
c.HTML(http.StatusOK, assets.Template("login.html"), map[string]interface{}{ c.HTML(http.StatusOK, assets.Template("login.html"), map[string]any{
"username": username, "username": username,
"error": "Invalid username/password", "error": "Invalid username/password",
"settings": m.DB.GetSettings(), "settings": m.DB.GetSettings(),
@@ -52,7 +52,7 @@ func (m *Middleware) Handler(c *router.Context) {
return return
} }
} }
c.HTML(http.StatusOK, assets.Template("login.html"), map[string]interface{}{ c.HTML(http.StatusOK, assets.Template("login.html"), map[string]any{
"settings": m.DB.GetSettings(), "settings": m.DB.GetSettings(),
}) })
} }

View File

@@ -53,7 +53,7 @@ type FeverFavicon struct {
Data string `json:"data"` Data string `json:"data"`
} }
func writeFeverJSON(c *router.Context, data map[string]interface{}, lastRefreshed int64) { func writeFeverJSON(c *router.Context, data map[string]any, lastRefreshed int64) {
data["api_version"] = 3 data["api_version"] = 3
data["auth"] = 1 data["auth"] = 1
data["last_refreshed_on_time"] = lastRefreshed data["last_refreshed_on_time"] = lastRefreshed
@@ -78,7 +78,7 @@ func (s *Server) feverAuth(c *router.Context) bool {
if s.Username != "" && s.Password != "" { if s.Username != "" && s.Password != "" {
apiKey := c.Req.FormValue("api_key") apiKey := c.Req.FormValue("api_key")
apiKey = strings.ToLower(apiKey) apiKey = strings.ToLower(apiKey)
md5HashValue := md5.Sum([]byte(fmt.Sprintf("%s:%s", s.Username, s.Password))) md5HashValue := md5.Sum(fmt.Appendf(nil, "%s:%s", s.Username, s.Password))
hexMD5HashValue := fmt.Sprintf("%x", md5HashValue[:]) hexMD5HashValue := fmt.Sprintf("%x", md5HashValue[:])
if !auth.StringsEqual(apiKey, hexMD5HashValue) { if !auth.StringsEqual(apiKey, hexMD5HashValue) {
return false return false
@@ -97,7 +97,7 @@ func formHasValue(values url.Values, value string) bool {
func (s *Server) handleFever(c *router.Context) { func (s *Server) handleFever(c *router.Context) {
c.Req.ParseForm() c.Req.ParseForm()
if !s.feverAuth(c) { if !s.feverAuth(c) {
c.JSON(http.StatusOK, map[string]interface{}{ c.JSON(http.StatusOK, map[string]any{
"api_version": 3, "api_version": 3,
"auth": 0, "auth": 0,
"last_refreshed_on_time": 0, "last_refreshed_on_time": 0,
@@ -123,7 +123,7 @@ func (s *Server) handleFever(c *router.Context) {
case formHasValue(c.Req.Form, "mark"): case formHasValue(c.Req.Form, "mark"):
s.feverMarkHandler(c) s.feverMarkHandler(c)
default: default:
c.JSON(http.StatusOK, map[string]interface{}{ c.JSON(http.StatusOK, map[string]any{
"api_version": 3, "api_version": 3,
"auth": 1, "auth": 1,
"last_refreshed_on_time": getLastRefreshedOnTime(s.db.ListHTTPStates()), "last_refreshed_on_time": getLastRefreshedOnTime(s.db.ListHTTPStates()),
@@ -168,7 +168,7 @@ func (s *Server) feverGroupsHandler(c *router.Context) {
for i, folder := range folders { for i, folder := range folders {
groups[i] = &FeverGroup{ID: folder.Id, Title: folder.Title} groups[i] = &FeverGroup{ID: folder.Id, Title: folder.Title}
} }
writeFeverJSON(c, map[string]interface{}{ writeFeverJSON(c, map[string]any{
"groups": groups, "groups": groups,
"feeds_groups": feedGroups(s.db), "feeds_groups": feedGroups(s.db),
}, getLastRefreshedOnTime(s.db.ListHTTPStates())) }, getLastRefreshedOnTime(s.db.ListHTTPStates()))
@@ -194,7 +194,7 @@ func (s *Server) feverFeedsHandler(c *router.Context) {
LastUpdated: lastUpdated, LastUpdated: lastUpdated,
} }
} }
writeFeverJSON(c, map[string]interface{}{ writeFeverJSON(c, map[string]any{
"feeds": feverFeeds, "feeds": feverFeeds,
"feeds_groups": feedGroups(s.db), "feeds_groups": feedGroups(s.db),
}, getLastRefreshedOnTime(httpStates)) }, getLastRefreshedOnTime(httpStates))
@@ -216,7 +216,7 @@ func (s *Server) feverFaviconsHandler(c *router.Context) {
favicons[i] = &FeverFavicon{ID: feed.Id, Data: data} favicons[i] = &FeverFavicon{ID: feed.Id, Data: data}
} }
writeFeverJSON(c, map[string]interface{}{ writeFeverJSON(c, map[string]any{
"favicons": favicons, "favicons": favicons,
}, getLastRefreshedOnTime(s.db.ListHTTPStates())) }, getLastRefreshedOnTime(s.db.ListHTTPStates()))
} }
@@ -280,15 +280,15 @@ func (s *Server) feverItemsHandler(c *router.Context) {
totalItems := s.db.CountItems(storage.ItemFilter{}) totalItems := s.db.CountItems(storage.ItemFilter{})
writeFeverJSON(c, map[string]interface{}{ writeFeverJSON(c, map[string]any{
"items": feverItems, "items": feverItems,
"total_items": totalItems, "total_items": totalItems,
}, getLastRefreshedOnTime(s.db.ListHTTPStates())) }, getLastRefreshedOnTime(s.db.ListHTTPStates()))
} }
func (s *Server) feverLinksHandler(c *router.Context) { func (s *Server) feverLinksHandler(c *router.Context) {
writeFeverJSON(c, map[string]interface{}{ writeFeverJSON(c, map[string]any{
"links": make([]interface{}, 0), "links": make([]any, 0),
}, getLastRefreshedOnTime(s.db.ListHTTPStates())) }, getLastRefreshedOnTime(s.db.ListHTTPStates()))
} }
@@ -309,7 +309,7 @@ func (s *Server) feverUnreadItemIDsHandler(c *router.Context) {
} }
itemFilter.After = &items[len(items)-1].Id itemFilter.After = &items[len(items)-1].Id
} }
writeFeverJSON(c, map[string]interface{}{ writeFeverJSON(c, map[string]any{
"unread_item_ids": joinInts(itemIds), "unread_item_ids": joinInts(itemIds),
}, getLastRefreshedOnTime(s.db.ListHTTPStates())) }, getLastRefreshedOnTime(s.db.ListHTTPStates()))
} }
@@ -331,7 +331,7 @@ func (s *Server) feverSavedItemIDsHandler(c *router.Context) {
} }
itemFilter.After = &items[len(items)-1].Id itemFilter.After = &items[len(items)-1].Id
} }
writeFeverJSON(c, map[string]interface{}{ writeFeverJSON(c, map[string]any{
"saved_item_ids": joinInts(itemIds), "saved_item_ids": joinInts(itemIds),
}, getLastRefreshedOnTime(s.db.ListHTTPStates())) }, getLastRefreshedOnTime(s.db.ListHTTPStates()))
} }
@@ -386,7 +386,7 @@ func (s *Server) feverMarkHandler(c *router.Context) {
c.Out.WriteHeader(http.StatusBadRequest) c.Out.WriteHeader(http.StatusBadRequest)
return return
} }
c.JSON(http.StatusOK, map[string]interface{}{ c.JSON(http.StatusOK, map[string]any{
"api_version": 3, "api_version": 3,
"auth": 1, "auth": 1,
}) })

View File

@@ -24,7 +24,7 @@ func (c *Context) Next() {
c.chain[c.index](c) c.chain[c.index](c)
} }
func (c *Context) JSON(status int, data interface{}) { func (c *Context) JSON(status int, data any) {
body, err := json.Marshal(data) body, err := json.Marshal(data)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
@@ -35,7 +35,7 @@ func (c *Context) JSON(status int, data interface{}) {
c.Out.Write([]byte("\n")) c.Out.Write([]byte("\n"))
} }
func (c *Context) HTML(status int, tmpl *template.Template, data interface{}) { func (c *Context) HTML(status int, tmpl *template.Template, data any) {
c.Out.Header().Set("Content-Type", "text/html") c.Out.Header().Set("Content-Type", "text/html")
c.Out.WriteHeader(status) c.Out.WriteHeader(status)
tmpl.Execute(c.Out, data) tmpl.Execute(c.Out, data)

View File

@@ -64,7 +64,7 @@ func (s *Server) handler() http.Handler {
} }
func (s *Server) handleIndex(c *router.Context) { func (s *Server) handleIndex(c *router.Context) {
c.HTML(http.StatusOK, assets.Template("index.html"), map[string]interface{}{ c.HTML(http.StatusOK, assets.Template("index.html"), map[string]any{
"settings": s.db.GetSettings(), "settings": s.db.GetSettings(),
"authenticated": s.Username != "" && s.Password != "", "authenticated": s.Username != "" && s.Password != "",
}) })
@@ -82,14 +82,14 @@ func (s *Server) handleStatic(c *router.Context) {
} }
func (s *Server) handleManifest(c *router.Context) { func (s *Server) handleManifest(c *router.Context) {
c.JSON(http.StatusOK, map[string]interface{}{ c.JSON(http.StatusOK, map[string]any{
"$schema": "https://json.schemastore.org/web-manifest-combined.json", "$schema": "https://json.schemastore.org/web-manifest-combined.json",
"name": "yarr!", "name": "yarr!",
"short_name": "yarr", "short_name": "yarr",
"description": "yet another rss reader", "description": "yet another rss reader",
"display": "standalone", "display": "standalone",
"start_url": "/" + strings.TrimPrefix(s.BasePath, "/"), "start_url": "/" + strings.TrimPrefix(s.BasePath, "/"),
"icons": []map[string]interface{}{ "icons": []map[string]any{
{ {
"src": s.BasePath + "/static/graphicarts/favicon.png", "src": s.BasePath + "/static/graphicarts/favicon.png",
"sizes": "64x64", "sizes": "64x64",
@@ -100,7 +100,7 @@ func (s *Server) handleManifest(c *router.Context) {
} }
func (s *Server) handleStatus(c *router.Context) { func (s *Server) handleStatus(c *router.Context) {
c.JSON(http.StatusOK, map[string]interface{}{ c.JSON(http.StatusOK, map[string]any{
"running": s.worker.FeedsPending(), "running": s.worker.FeedsPending(),
"stats": s.db.FeedStats(), "stats": s.db.FeedStats(),
}) })
@@ -239,7 +239,7 @@ func (s *Server) handleFeedList(c *router.Context) {
case len(result.Sources) > 0: case len(result.Sources) > 0:
c.JSON( c.JSON(
http.StatusOK, http.StatusOK,
map[string]interface{}{"status": "multiple", "choice": result.Sources}, map[string]any{"status": "multiple", "choice": result.Sources},
) )
case result.Feed != nil: case result.Feed != nil:
feed := s.db.CreateFeed( feed := s.db.CreateFeed(
@@ -257,7 +257,7 @@ func (s *Server) handleFeedList(c *router.Context) {
} }
s.worker.FindFeedFavicon(*feed) s.worker.FindFeedFavicon(*feed)
c.JSON(http.StatusOK, map[string]interface{}{ c.JSON(http.StatusOK, map[string]any{
"status": "success", "status": "success",
"feed": feed, "feed": feed,
}) })
@@ -279,7 +279,7 @@ func (s *Server) handleFeed(c *router.Context) {
c.Out.WriteHeader(http.StatusBadRequest) c.Out.WriteHeader(http.StatusBadRequest)
return return
} }
body := make(map[string]interface{}) body := make(map[string]any)
if err := json.NewDecoder(c.Req.Body).Decode(&body); err != nil { if err := json.NewDecoder(c.Req.Body).Decode(&body); err != nil {
log.Print(err) log.Print(err)
c.Out.WriteHeader(http.StatusBadRequest) c.Out.WriteHeader(http.StatusBadRequest)
@@ -391,7 +391,7 @@ func (s *Server) handleItemList(c *router.Context) {
items[i].Title = htmlutil.TruncateText(text, 140) items[i].Title = htmlutil.TruncateText(text, 140)
} }
} }
c.JSON(http.StatusOK, map[string]interface{}{ c.JSON(http.StatusOK, map[string]any{
"list": items, "list": items,
"has_more": hasMore, "has_more": hasMore,
}) })
@@ -415,7 +415,7 @@ func (s *Server) handleSettings(c *router.Context) {
if c.Req.Method == "GET" { if c.Req.Method == "GET" {
c.JSON(http.StatusOK, s.db.GetSettings()) c.JSON(http.StatusOK, s.db.GetSettings())
} else if c.Req.Method == "PUT" { } else if c.Req.Method == "PUT" {
settings := make(map[string]interface{}) settings := make(map[string]any)
if err := json.NewDecoder(c.Req.Body).Decode(&settings); err != nil { if err := json.NewDecoder(c.Req.Body).Decode(&settings); err != nil {
c.Out.WriteHeader(http.StatusBadRequest) c.Out.WriteHeader(http.StatusBadRequest)
return return
@@ -472,7 +472,6 @@ func (s *Server) handleOPMLExport(c *router.Context) {
feedsByFolderID := make(map[int64][]*storage.Feed) feedsByFolderID := make(map[int64][]*storage.Feed)
for _, feed := range s.db.ListFeeds() { for _, feed := range s.db.ListFeeds() {
feed := feed
if feed.FolderId == nil { if feed.FolderId == nil {
doc.Feeds = append(doc.Feeds, opml.Feed{ doc.Feeds = append(doc.Feeds, opml.Feed{
Title: feed.Title, Title: feed.Title,

View File

@@ -16,7 +16,7 @@ type Server struct {
Addr string Addr string
db *storage.Storage db *storage.Storage
worker *worker.Worker worker *worker.Worker
cache map[string]interface{} cache map[string]any
cache_mutex *sync.Mutex cache_mutex *sync.Mutex
BasePath string BasePath string
@@ -34,7 +34,7 @@ func NewServer(db *storage.Storage, addr string) *Server {
db: db, db: db,
Addr: addr, Addr: addr,
worker: worker.NewWorker(db), worker: worker.NewWorker(db),
cache: make(map[string]interface{}), cache: make(map[string]any),
cache_mutex: &sync.Mutex{}, cache_mutex: &sync.Mutex{},
} }
} }

View File

@@ -24,7 +24,7 @@ func TestCreateFeedSameLink(t *testing.T) {
t.Fatal("expected feed") t.Fatal("expected feed")
} }
for i := 0; i < 10; i++ { for range 10 {
db.CreateFeed("title", "", "", "http://example2.com/feed.xml", nil) db.CreateFeed("title", "", "", "http://example2.com/feed.xml", nil)
} }

View File

@@ -169,9 +169,9 @@ func (s *Storage) CreateItems(items []Item) bool {
return true return true
} }
func listQueryPredicate(filter ItemFilter, newestFirst bool) (string, []interface{}) { func listQueryPredicate(filter ItemFilter, newestFirst bool) (string, []any) {
cond := make([]string, 0) cond := make([]string, 0)
args := make([]interface{}, 0) args := make([]any, 0)
if filter.FolderID != nil { if filter.FolderID != nil {
cond = append(cond, "i.feed_id in (select id from feeds where folder_id = :folder_id)") cond = append(cond, "i.feed_id in (select id from feeds where folder_id = :folder_id)")
args = append(args, sql.Named("folder_id", *filter.FolderID)) args = append(args, sql.Named("folder_id", *filter.FolderID))

View File

@@ -6,8 +6,8 @@ import (
"log" "log"
) )
func settingsDefaults() map[string]interface{} { func settingsDefaults() map[string]any {
return map[string]interface{}{ return map[string]any{
"filter": "", "filter": "",
"feed": "", "feed": "",
"feed_list_width": 300, "feed_list_width": 300,
@@ -20,7 +20,7 @@ func settingsDefaults() map[string]interface{} {
} }
} }
func (s *Storage) GetSettingsValue(key string) interface{} { func (s *Storage) GetSettingsValue(key string) any {
row := s.db.QueryRow(`select val from settings where key=:key`, sql.Named("key", key)) row := s.db.QueryRow(`select val from settings where key=:key`, sql.Named("key", key))
if row == nil { if row == nil {
return settingsDefaults()[key] return settingsDefaults()[key]
@@ -30,7 +30,7 @@ func (s *Storage) GetSettingsValue(key string) interface{} {
if len(val) == 0 { if len(val) == 0 {
return nil return nil
} }
var valDecoded interface{} var valDecoded any
if err := json.Unmarshal([]byte(val), &valDecoded); err != nil { if err := json.Unmarshal([]byte(val), &valDecoded); err != nil {
log.Print(err) log.Print(err)
return nil return nil
@@ -48,7 +48,7 @@ func (s *Storage) GetSettingsValueInt64(key string) int64 {
return 0 return 0
} }
func (s *Storage) GetSettings() map[string]interface{} { func (s *Storage) GetSettings() map[string]any {
result := settingsDefaults() result := settingsDefaults()
rows, err := s.db.Query(`select key, val from settings;`) rows, err := s.db.Query(`select key, val from settings;`)
if err != nil { if err != nil {
@@ -58,7 +58,7 @@ func (s *Storage) GetSettings() map[string]interface{} {
for rows.Next() { for rows.Next() {
var key string var key string
var val []byte var val []byte
var valDecoded interface{} var valDecoded any
rows.Scan(&key, &val) rows.Scan(&key, &val)
if err = json.Unmarshal([]byte(val), &valDecoded); err != nil { if err = json.Unmarshal([]byte(val), &valDecoded); err != nil {
@@ -70,7 +70,7 @@ func (s *Storage) GetSettings() map[string]interface{} {
return result return result
} }
func (s *Storage) UpdateSettings(kv map[string]interface{}) bool { func (s *Storage) UpdateSettings(kv map[string]any) bool {
defaults := settingsDefaults() defaults := settingsDefaults()
for key, val := range kv { for key, val := range kv {
if defaults[key] == nil { if defaults[key] == nil {

View File

@@ -141,7 +141,6 @@ func findFavicon(siteUrl, feedUrl string) (*[]byte, error) {
func ConvertItems(items []parser.Item, feed storage.Feed) []storage.Item { func ConvertItems(items []parser.Item, feed storage.Feed) []storage.Item {
result := make([]storage.Item, len(items)) result := make([]storage.Item, len(items))
for i, item := range items { for i, item := range items {
item := item
mediaLinks := make(storage.MediaLinks, 0) mediaLinks := make(storage.MediaLinks, 0)
for _, link := range item.MediaLinks { for _, link := range item.MediaLinks {
mediaLinks = append(mediaLinks, storage.MediaLink(link)) mediaLinks = append(mediaLinks, storage.MediaLink(link))

View File

@@ -113,14 +113,14 @@ func (w *Worker) refresher(feeds []storage.Feed) {
srcqueue := make(chan storage.Feed, len(feeds)) srcqueue := make(chan storage.Feed, len(feeds))
dstqueue := make(chan []storage.Item) dstqueue := make(chan []storage.Item)
for i := 0; i < NUM_WORKERS; i++ { for range NUM_WORKERS {
go w.worker(srcqueue, dstqueue) go w.worker(srcqueue, dstqueue)
} }
for _, feed := range feeds { for _, feed := range feeds {
srcqueue <- feed srcqueue <- feed
} }
for i := 0; i < len(feeds); i++ { for range feeds {
items := <-dstqueue items := <-dstqueue
if len(items) > 0 { if len(items) > 0 {
w.db.CreateItems(items) w.db.CreateItems(items)