diff --git a/api/comments.go b/api/comments.go index 3e9c31d..3243b94 100644 --- a/api/comments.go +++ b/api/comments.go @@ -16,14 +16,15 @@ import ( type Comment struct { Comments []Comment - User User + User User + Post Submission Id string Comment string Upvotes int64 Downvotes int64 Platform string CreatedAt string - RelTime string + RelTime string UpdatedAt string DeletedAt string } @@ -55,7 +56,7 @@ func (client *Client) FetchComments(galleryID string) ([]Comment, error) { ) wg.Wait() - client.Cache.Set(galleryID + "-comments", comments, cache.DefaultExpiration) + client.Cache.Set(galleryID+"-comments", comments, cache.DefaultExpiration) return comments, nil } @@ -130,13 +131,14 @@ func parseComment(data gjson.Result) Comment { Username: data.Get("account.username").String(), Avatar: userAvatar, }, + Post: parseSubmission(data.Get("post")), Id: data.Get("id").String(), Comment: comment, Upvotes: data.Get("upvote_count").Int(), Downvotes: data.Get("downvote_count").Int(), Platform: data.Get("platform").String(), CreatedAt: createdAt, - RelTime: humanize.Time(createdTime), + RelTime: humanize.Time(createdTime), UpdatedAt: updatedAt, DeletedAt: deletedAt, } diff --git a/api/user.go b/api/user.go index 3af2026..25dca50 100644 --- a/api/user.go +++ b/api/user.go @@ -8,6 +8,7 @@ import ( "time" "codeberg.org/rimgo/rimgo/utils" + "github.com/patrickmn/go-cache" "github.com/tidwall/gjson" ) @@ -64,7 +65,7 @@ func (client *Client) FetchUser(username string) (User, error) { CreatedAt: createdTime.Format("January 2, 2006"), } - client.Cache.Set(username + "-user", user, 1*time.Hour) + client.Cache.Set(username+"-user", user, 1*time.Hour) return user, nil } @@ -89,41 +90,7 @@ func (client *Client) FetchSubmissions(username string, sort string, page string go func() { defer wg.Done() - coverData := value.Get("images.#(id==\"" + value.Get("cover").String() + "\")") - cover := Media{ - Id: value.Get("id").String(), - Description: value.Get("description").String(), - Type: strings.Split(value.Get("type").String(), "/")[0], - Url: strings.ReplaceAll(value.Get("link").String(), "https://i.imgur.com", ""), - } - if coverData.Exists() { - cover = Media{ - Id: coverData.Get("id").String(), - Description: coverData.Get("description").String(), - Type: strings.Split(coverData.Get("type").String(), "/")[0], - Url: strings.ReplaceAll(coverData.Get("link").String(), "https://i.imgur.com", ""), - } - } - - id := value.Get("id").String() - - link := "/a/" + id - if value.Get("in_gallery").Bool() { - link = "/gallery/" + id - } - - submissions = append(submissions, Submission{ - Id: id, - Link: link, - Title: value.Get("title").String(), - Cover: cover, - Points: value.Get("points").Int(), - Upvotes: value.Get("ups").Int(), - Downvotes: value.Get("downs").Int(), - Comments: value.Get("comment_count").Int(), - Views: value.Get("views").Int(), - IsAlbum: value.Get("is_album").Bool(), - }) + submissions = append(submissions, parseSubmission(value)) }() return true @@ -131,6 +98,102 @@ func (client *Client) FetchSubmissions(username string, sort string, page string ) wg.Wait() - client.Cache.Set(username + "-submissions", submissions, 15*time.Minute) + client.Cache.Set(username+"-submissions", submissions, 15*time.Minute) return submissions, nil } + +func (client *Client) FetchUserComments(username string) ([]Comment, error) { + cacheData, found := client.Cache.Get(username + "-usercomments") + if found { + return cacheData.([]Comment), nil + } + + req, err := http.NewRequest("GET", "https://api.imgur.com/comment/v1/comments", nil) + if err != nil { + return []Comment{}, err + } + utils.SetReqHeaders(req) + + q := req.URL.Query() + q.Add("client_id", client.ClientID) + q.Add("filter[account]", "eq:"+username) + q.Add("include", "account,post") + q.Add("sort", "new") + + req.URL.RawQuery = q.Encode() + + res, err := http.DefaultClient.Do(req) + if err != nil { + return []Comment{}, err + } + + body, err := io.ReadAll(res.Body) + if err != nil { + return []Comment{}, err + } + + data := gjson.Parse(string(body)) + + comments := make([]Comment, 0) + data.Get("data").ForEach( + func(key, value gjson.Result) bool { + comments = append(comments, parseComment(value)) + return true + }, + ) + + client.Cache.Set(username+"-usercomments", comments, cache.DefaultExpiration) + return comments, nil +} + +func parseSubmission(value gjson.Result) Submission { + var cover Media + c := value.Get("cover") + coverData := value.Get("images.#(id==\"" + c.String() + "\")") + switch { + case c.Type == gjson.String && coverData.Exists(): + cover = Media{ + Id: coverData.Get("id").String(), + Description: coverData.Get("description").String(), + Type: strings.Split(coverData.Get("type").String(), "/")[0], + Url: strings.ReplaceAll(coverData.Get("link").String(), "https://i.imgur.com", ""), + } + // This case is when fetching comments + case c.Type != gjson.Null: + cover = Media{ + Id: c.Get("id").String(), + Url: strings.ReplaceAll(c.Get("url").String(), "https://i.imgur.com", ""), + } + // Replace with thumbnails here because it's easier. + if strings.HasSuffix(cover.Url, ".mp4") { + cover.Url = cover.Url[:len(cover.Url)-3] + "webp" + } + default: + cover = Media{ + Id: value.Get("id").String(), + Description: value.Get("description").String(), + Type: strings.Split(value.Get("type").String(), "/")[0], + Url: strings.ReplaceAll(value.Get("link").String(), "https://i.imgur.com", ""), + } + } + + id := value.Get("id").String() + + link := "/a/" + id + if value.Get("in_gallery").Bool() { + link = "/gallery/" + id + } + + return Submission{ + Id: id, + Link: link, + Title: value.Get("title").String(), + Cover: cover, + Points: value.Get("points").Int(), + Upvotes: value.Get("ups").Int(), + Downvotes: value.Get("downs").Int(), + Comments: value.Get("comment_count").Int(), + Views: value.Get("views").Int(), + IsAlbum: value.Get("is_album").Bool(), + } +} diff --git a/main.go b/main.go index 0a1cd52..881c6dc 100644 --- a/main.go +++ b/main.go @@ -121,8 +121,9 @@ func main() { app.Get("/a/:postID/embed", pages.HandleEmbed) app.Get("/t/:tag", pages.HandleTag) app.Get("/t/:tag/:postID", pages.HandlePost) - app.Get("/user/:userID", pages.HandleUser) app.Get("/r/:sub/:postID", pages.HandlePost) + app.Get("/user/:userID", pages.HandleUser) + app.Get("/user/:userID/comments", pages.HandleUserComments) app.Get("/user/:userID/cover", pages.HandleUserCover) app.Get("/user/:userID/avatar", pages.HandleUserAvatar) app.Get("/gallery/:postID", pages.HandlePost) diff --git a/pages/user.go b/pages/user.go index f38d50a..e8460c4 100644 --- a/pages/user.go +++ b/pages/user.go @@ -51,7 +51,43 @@ func HandleUser(c *fiber.Ctx) error { "user": user, "submissions": submissions, "page": page, - "nextPage": pageNumber + 1, - "prevPage": pageNumber - 1, + "nextPage": pageNumber + 1, + "prevPage": pageNumber - 1, + }) +} + +func HandleUserComments(c *fiber.Ctx) error { + utils.SetHeaders(c) + c.Set("X-Frame-Options", "DENY") + c.Set("Cache-Control", "public,max-age=604800") + c.Set("Content-Security-Policy", "default-src 'none'; frame-ancestors 'none'; base-uri 'none'; form-action 'self'; media-src 'self'; style-src 'unsafe-inline' 'self'; img-src 'self'; manifest-src 'self'; block-all-mixed-content") + + user, err := ApiClient.FetchUser(c.Params("userID")) + if err != nil && err.Error() == "ratelimited by imgur" { + return c.Status(429).Render("errors/429", fiber.Map{ + "path": c.Path(), + }) + } + if err != nil { + return err + } + if user.Username == "" { + return c.Status(404).Render("errors/404", nil) + } + + comments, err := ApiClient.FetchUserComments(c.Params("userID")) + if err != nil && err.Error() == "ratelimited by imgur" { + c.Status(429) + return c.Render("errors/429", fiber.Map{ + "path": c.Path(), + }) + } + if err != nil { + return err + } + + return c.Render("userComments", fiber.Map{ + "user": user, + "comments": comments, }) } diff --git a/views/partials/contextComment.hbs b/views/partials/contextComment.hbs new file mode 100644 index 0000000..f559e48 --- /dev/null +++ b/views/partials/contextComment.hbs @@ -0,0 +1,22 @@ + +
+ +
+
+

{{{this.Comment}}}

+
+
+ {{this.RelTime}} + {{#if this.DeletedAt}} + (deleted {{this.DeletedAt}}) + {{/if}} + | + Likes + {{this.Upvotes}} + Dislikes + {{this.Downvotes}} +
+
+
+
+
\ No newline at end of file diff --git a/views/user.hbs b/views/user.hbs index 0243e1b..2213f59 100644 --- a/views/user.hbs +++ b/views/user.hbs @@ -21,6 +21,11 @@

{{user.Username}}

{{user.Points}} pts · {{user.CreatedAt}}

+
+
+ Submissions + Comments +

{{user.Bio}}

diff --git a/views/userComments.hbs b/views/userComments.hbs new file mode 100644 index 0000000..b0f223e --- /dev/null +++ b/views/userComments.hbs @@ -0,0 +1,44 @@ + + + + + {{user.Username}}'s comments - rimgo + + {{> partials/head }} + + + + {{> partials/nav }} + +
+ {{> partials/searchBar }} +
+ +
+
+ +
+

{{user.Username}}

+

{{user.Points}} pts · {{user.CreatedAt}}

+
+
+ +
+

{{user.Bio}}

+
+ +
+
+ {{#each comments}} + {{> partials/contextComment }} + {{/each}} +
+
+ + {{> partials/footer }} + + + \ No newline at end of file