flounder

A simple gemini site builder
Log | Files | Refs | README | LICENSE

commit 4791b3472756756ba175b7d8dc33e842d062c346
parent bb9c710d0ee19b314dc8806a153e99003196859e
Author: alex wennerberg <alex@alexwennerberg.com>
Date:   Thu, 28 Jan 2021 21:33:31 -0800

Remove feeds page, replace with following page

Add sensible limits to feed following code

Diffstat:
Mfeed.go | 79+++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
Mgemfeed.go | 29-----------------------------
Mhttp.go | 45+++++++++++++--------------------------------
Mlog.go | 2+-
Mtemplates/edit_file.html | 3+++
Mtemplates/following.gmi | 4+++-
Mtemplates/my_site.html | 2++
Mtemplates/nav.html | 2+-
8 files changed, 80 insertions(+), 86 deletions(-)

diff --git a/feed.go b/feed.go @@ -3,8 +3,11 @@ package main 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 @@ const followingFile = "following.gmi" 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 @@ func feedsWorker() { 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 @@ func writeAllFeeds(user string) error { 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 { diff --git a/gemfeed.go b/gemfeed.go @@ -123,35 +123,6 @@ func generateFeedFromUser(user string) *Gemfeed { 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 diff --git a/http.go b/http.go @@ -55,7 +55,7 @@ func rootHandler(w http.ResponseWriter, r *http.Request) { return } - user := newGetAuthUser(r) + user := getAuthUser(r) indexFiles, err := getIndexFiles(user.IsAdmin) if err != nil { panic(err) @@ -76,26 +76,8 @@ func rootHandler(w http.ResponseWriter, r *http.Request) { } } -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 editFileHandler(w http.ResponseWriter, r *http.Request) { 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 @@ type AuthUser struct { 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 newGetAuthUser(r *http.Request) AuthUser { } 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 @@ func mySiteHandler(w http.ResponseWriter, r *http.Request) { } 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 @@ func myAccountHandler(w http.ResponseWriter, r *http.Request) { } } // reset auth - user = newGetAuthUser(r) + user = getAuthUser(r) data.Errors = errors data.AuthUser = user data.MyUser.Email = newEmail @@ -344,7 +326,7 @@ func myAccountHandler(w http.ResponseWriter, r *http.Request) { } 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 registerHandler(w http.ResponseWriter, r *http.Request) { } 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 @@ func deleteFileHandler(w http.ResponseWriter, r *http.Request) { } 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 userFile(w http.ResponseWriter, r *http.Request) { } 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 deleteAccountHandler(w http.ResponseWriter, r *http.Request) { } 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 resetPasswordHandler(w http.ResponseWriter, r *http.Request) { } 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 @@ func runHTTPServer() { 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) diff --git a/log.go b/log.go @@ -35,7 +35,7 @@ func logFormatter(writer io.Writer, params handlers.LogFormatterParams) { // 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 diff --git a/templates/edit_file.html b/templates/edit_file.html @@ -4,6 +4,9 @@ <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> diff --git a/templates/following.gmi b/templates/following.gmi @@ -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 diff --git a/templates/my_site.html b/templates/my_site.html @@ -12,6 +12,7 @@ For some help building your site, check out the <a href="https://admin.flounder. <br> <h3>Your files:</h3> {{ define "file" }} +{{ if ne .Name "following.gmi" }} <tr> <div> {{ if gt (len .Children) 0 }} @@ -53,6 +54,7 @@ For some help building your site, check out the <a href="https://admin.flounder. </div> </tr> {{ end }} +{{ end }} <table> {{ range .Files }} {{ template "file" . }} diff --git a/templates/nav.html b/templates/nav.html @@ -1,7 +1,7 @@ <nav> <a href="/">home</a> - <a href="/feed">feed</a> {{ if .AuthUser.LoggedIn }} + <a href="//{{.AuthUser.Username}}.{{.Config.Host}}/following.gmi">following</a> <a href="/my_site">my_site</a> <a href="/me">me</a> {{ if .AuthUser.IsAdmin }}