Remove feeds page, replace with following page Add sensible limits to feed following code
@@ -3,8 +3,11 @@
import ( "bufio" "fmt" + "git.sr.ht/~adnano/go-gemini" "github.com/mmcdole/gofeed" "log" + "net/http" + "net/url" "os" "path" "sort"@@ -19,7 +22,6 @@
func feedsWorker() { log.Println("Starting feeds worker") for { - time.Sleep(time.Hour * 1) users, err := getActiveUserNames() if err != nil { // Handle error somehow@@ -29,40 +31,69 @@ }
for _, user := range users { writeAllFeeds(user) } + time.Sleep(time.Hour * 1) } } func writeAllFeeds(user string) error { // Open file file, err := os.Open(path.Join(getUserDirectory(user), followingPath)) - if err != nil { - if os.IsNotExist(err) { - // TODO - return nil - } - return err - } log.Println("Writing feeds for user " + user) defer file.Close() feedData := []*gofeed.Feed{} - scanner := bufio.NewScanner(file) - for scanner.Scan() { - feedURL := scanner.Text() - // TODO if scheme is gemini and filetype is gemini... gemtext - // TODO if scheme is gemini and filetype is xml/rss... fetch data and parse - // TODO rate limit etc - fp := gofeed.NewParser() - feed, err := fp.ParseURL(feedURL) - if err != nil { - log.Println("Error getting feed " + feedURL) - } else { + if err == nil { + scanner := bufio.NewScanner(file) + count := 1 + for scanner.Scan() { + if count > 100 { // max number of lines + break + } + count = count + 1 + feedURL := scanner.Text() + parsed, err := url.Parse(feedURL) + var feed *gofeed.Feed + fp := gofeed.NewParser() + if err != nil { + log.Println("Invalid url " + feedURL) + } + if parsed.Scheme == "gemini" { + client := gemini.Client{ + Timeout: 10 * time.Second, + } + res, err := client.Get(feedURL) + defer res.Body.Close() + if err != nil { + log.Println(err) + continue + } + if err != nil { + log.Println(err) + continue + } + feed, err = fp.Parse(res.Body) + if err != nil { + log.Println(err) + continue + } + } else { + // TODO if scheme is gemini and filetype is gemini... gemtext + // TODO rate limit etc + fp.Client = &http.Client{ + Timeout: 10 * time.Second, + } + feed, err = fp.ParseURL(feedURL) + if err != nil { + log.Println("Error getting feed " + feedURL) + continue + } + } log.Println("Got feed data from " + feedURL) feedData = append(feedData, feed) } - } - if err := scanner.Err(); err != nil { - return err + if err := scanner.Err(); err != nil { + return err + } } // Aggregate and sort by date type feedPlusItem struct {@@ -89,6 +120,10 @@ }
sort.Slice(data.FeedItems, func(i, j int) bool { return data.FeedItems[i].FeedItem.UpdatedParsed.After(*data.FeedItems[j].FeedItem.UpdatedParsed) }) + maxItems := 100 + if len(data.FeedItems) > maxItems { + data.FeedItems = data.FeedItems[:maxItems] + } outputf, err := os.OpenFile(path.Join(getUserDirectory(user), followingFile), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755) if err != nil {
@@ -123,35 +123,6 @@ })
return &feed } -// TODO definitely cache this function -// TODO include generateFeedFromFolder for "gemfeed" folders -func getAllGemfeedEntries() ([]FeedEntry, []Gemfeed, error) { - maxItems := 50 - var feedEntries []FeedEntry - var feeds []Gemfeed - users, err := getActiveUserNames() - if err != nil { - return nil, nil, err - } else { - for _, user := range users { - fe := generateFeedFromUser(user) - if len(fe.Entries) > 0 { - feeds = append(feeds, *fe.Entries[0].Feed) - for _, e := range fe.Entries { - feedEntries = append(feedEntries, e) - } - } - } - } - sort.Slice(feedEntries, func(i, j int) bool { - return feedEntries[i].Date.After(feedEntries[j].Date) - }) - if len(feedEntries) > maxItems { - return feedEntries[:maxItems], feeds, nil - } - return feedEntries, feeds, nil -} - var GemfeedRegex = regexp.MustCompile(`=>\s*(\S+)\s([0-9]{4}-[0-9]{2}-[0-9]{2})\s?-?\s?(.*)`) // Parsed Gemfeed text Returns error if not a gemfeed
@@ -55,7 +55,7 @@ http.ServeFile(w, r, fileName) // TODO better error handling
return } - user := newGetAuthUser(r) + user := getAuthUser(r) indexFiles, err := getIndexFiles(user.IsAdmin) if err != nil { panic(err)@@ -76,26 +76,8 @@ panic(err)
} } -func feedHandler(w http.ResponseWriter, r *http.Request) { - user := newGetAuthUser(r) - feedEntries, feeds, err := getAllGemfeedEntries() - if err != nil { - panic(err) - } - data := struct { - Config Config - FeedEntries []FeedEntry - Feeds []Gemfeed - AuthUser AuthUser - }{c, feedEntries, feeds, user} - err = t.ExecuteTemplate(w, "feed.html", data) - if err != nil { - panic(err) - } -} - func editFileHandler(w http.ResponseWriter, r *http.Request) { - user := newGetAuthUser(r) + user := getAuthUser(r) if !user.LoggedIn { renderDefaultError(w, http.StatusForbidden) return@@ -196,7 +178,7 @@ }
func uploadFilesHandler(w http.ResponseWriter, r *http.Request) { if r.Method == "POST" { - user := newGetAuthUser(r) + user := getAuthUser(r) if !user.LoggedIn { renderDefaultError(w, http.StatusForbidden) return@@ -241,7 +223,7 @@ IsAdmin bool
ImpersonatingUser string // used if impersonating } -func newGetAuthUser(r *http.Request) AuthUser { +func getAuthUser(r *http.Request) AuthUser { session, _ := SessionStore.Get(r, "cookie-session") user, ok := session.Values["auth_user"].(string) impers, _ := session.Values["impersonating_user"].(string)@@ -255,7 +237,7 @@ }
} func mySiteHandler(w http.ResponseWriter, r *http.Request) { - user := newGetAuthUser(r) + user := getAuthUser(r) if !user.LoggedIn { renderDefaultError(w, http.StatusForbidden) return@@ -274,7 +256,7 @@ _ = t.ExecuteTemplate(w, "my_site.html", data)
} func myAccountHandler(w http.ResponseWriter, r *http.Request) { - user := newGetAuthUser(r) + user := getAuthUser(r) authUser := user.Username if !user.LoggedIn { renderDefaultError(w, http.StatusForbidden)@@ -334,7 +316,7 @@ session.Save(r, w)
} } // reset auth - user = newGetAuthUser(r) + user = getAuthUser(r) data.Errors = errors data.AuthUser = user data.MyUser.Email = newEmail@@ -344,7 +326,7 @@ }
} func archiveHandler(w http.ResponseWriter, r *http.Request) { - authUser := newGetAuthUser(r) + authUser := getAuthUser(r) if !authUser.LoggedIn { renderDefaultError(w, http.StatusForbidden) return@@ -492,7 +474,7 @@ }
} func deleteFileHandler(w http.ResponseWriter, r *http.Request) { - user := newGetAuthUser(r) + user := getAuthUser(r) if !user.LoggedIn { renderDefaultError(w, http.StatusForbidden) return@@ -505,7 +487,7 @@ http.Redirect(w, r, "/my_site", http.StatusSeeOther)
} func adminHandler(w http.ResponseWriter, r *http.Request) { - user := newGetAuthUser(r) + user := getAuthUser(r) if !user.IsAdmin { renderDefaultError(w, http.StatusForbidden) return@@ -634,7 +616,7 @@ }
} func deleteAccountHandler(w http.ResponseWriter, r *http.Request) { - user := newGetAuthUser(r) + user := getAuthUser(r) if r.Method == "POST" { r.ParseForm() validate := r.Form.Get("validate-delete")@@ -653,7 +635,7 @@ }
} func resetPasswordHandler(w http.ResponseWriter, r *http.Request) { - user := newGetAuthUser(r) + user := getAuthUser(r) data := struct { Config Config AuthUser AuthUser@@ -699,7 +681,7 @@ }
} func adminUserHandler(w http.ResponseWriter, r *http.Request) { - user := newGetAuthUser(r) + user := getAuthUser(r) if r.Method == "POST" { if !user.IsAdmin { renderDefaultError(w, http.StatusForbidden)@@ -790,7 +772,6 @@ hostname := s[0]
port := c.HttpPort serveMux.HandleFunc(hostname+"/", rootHandler) - serveMux.HandleFunc(hostname+"/feed", feedHandler) serveMux.HandleFunc(hostname+"/my_site", mySiteHandler) serveMux.HandleFunc(hostname+"/me", myAccountHandler) serveMux.HandleFunc(hostname+"/my_site/flounder-archive.zip", archiveHandler)
@@ -35,7 +35,7 @@ // buildCommonLogLine builds a log entry for req in Apache Common Log Format.
// ts is the timestamp with which the entry should be logged. // status and size are used to provide the response HTTP status and size. func buildCommonLogLine(req *http.Request, url url.URL, ts time.Time, status int, size int) []byte { - user := newGetAuthUser(req) + user := getAuthUser(req) username := "-" if user.Username != "" { username = user.Username
@@ -4,6 +4,9 @@ <form id="edit-form" action="/edit/{{.FileName}}" method="POST">
<label for="rename">Rename:</label> <input type="text" value="{{.FileName}}" id="rename" name="rename"> {{ if .IsText }} + {{ if eq .FileName "following.txt" }} + <p><em>Add URLs here of feeds you would like to follow. They will be available at <a href="//{{.AuthUser.Username}}.{{.Host}}/following.gmi">following.gmi</a>. For more information, see <a href="https://admin.flounder.online/following-pages.gmi">Following Feeds</a></em></p> + {{ end}} {{ if .IsGemini }} <p> <em>For help with the Gemini markup format, see the <a href="https://admin.flounder.online/gemini_text_guide.gmi">Gemtext Guide</a></em>
@@ -1,7 +1,9 @@
# {{.User}}'s Following A collection of feeds that {{.User}} is following -=> following.txt All feeds +=> following.txt {{ range .FeedItems }} => {{.FeedItem.Link}} {{.Date}} {{.Feed.Title}} -- {{.FeedItem.Title}}{{ end }} +For more information about setting up this page, see: +=> //admin.flounder.online/following-feeds.gmi
@@ -12,6 +12,7 @@ <br>
<br> <h3>Your files:</h3> {{ define "file" }} +{{ if ne .Name "following.gmi" }} <tr> <div> {{ if gt (len .Children) 0 }}@@ -52,6 +53,7 @@ {{ end }}
</td> </div> </tr> +{{ end }} {{ end }} <table> {{ range .Files }}