4 Commits

Author SHA1 Message Date
video-prize-ranch
2024f7f6cf Add projectsegfau.lt instances to instances.json 2023-08-15 17:05:07 -04:00
orangix
34ecc2a32b User comments support (#131)
#10

Reviewed-on: https://codeberg.org/rimgo/rimgo/pulls/131
Co-authored-by: orangix <uleo8b8g@anonaddy.me>
Co-committed-by: orangix <uleo8b8g@anonaddy.me>
2023-08-15 20:38:14 +00:00
Arya K
e5e1c38058 Update location of Project Segfault instance in README (#130)
Reviewed-on: https://codeberg.org/rimgo/rimgo/pulls/130
Co-authored-by: Arya K <arya@projectsegfau.lt>
Co-committed-by: Arya K <arya@projectsegfau.lt>
2023-08-15 20:30:58 +00:00
video-prize-ranch
493a17385a Remove rimgo.fascinated.cc (closes #125) 2023-08-14 18:59:35 -04:00
9 changed files with 237 additions and 51 deletions

View File

@@ -86,11 +86,10 @@ Open an issue to have your instance listed here! See the rules for the instance
| [imgur.010032.xyz](https://imgur.010032.xyz/) | 🇰🇷 KR | Oracle Cloud | ✅ Data not collected | | | [imgur.010032.xyz](https://imgur.010032.xyz/) | 🇰🇷 KR | Oracle Cloud | ✅ Data not collected | |
| [rimgo.kling.gg](https://rimgo.kling.gg/) | 🇳🇱 NL | RamNode | ✅ Data not collected | | | [rimgo.kling.gg](https://rimgo.kling.gg/) | 🇳🇱 NL | RamNode | ✅ Data not collected | |
| [i.01r.xyz](https://i.01r.xyz/) | 🇺🇸 US | Cloudflare | ✅ Data not collected | | | [i.01r.xyz](https://i.01r.xyz/) | 🇺🇸 US | Cloudflare | ✅ Data not collected | |
| [rimgo.projectsegfau.lt](https://rimgo.projectsegfau.lt/) | 🇱🇺 LU, 🇺🇸 US, 🇮🇳 IN | See below | ✅ Data not collected | | | [rimgo.projectsegfau.lt](https://rimgo.projectsegfau.lt/) | 🇫🇷 FR, 🇺🇸 US, 🇮🇳 IN | See below | ✅ Data not collected | |
| [rimgo.eu.projectsegfau.lt](https://rimgo.projectsegfau.lt/) | 🇱🇺 LU | FranTech Solutions | ✅ Data not collected | | | [rimgo.eu.projectsegfau.lt](https://rimgo.eu.projectsegfau.lt/) | 🇫🇷 FR | Orange S.A. | ✅ Data not collected | |
| [rimgo.us.projectsegfau.lt](https://rimgo.projectsegfau.lt/) | 🇺🇸 US | DigitalOcean | ✅ Data not collected | | | [rimgo.us.projectsegfau.lt](https://rimgo.us.projectsegfau.lt/) | 🇺🇸 US | Racknerd | ✅ Data not collected | |
| [rimgo.in.projectsegfau.lt](https://rimgo.projectsegfau.lt/) | 🇮🇳 IN | Airtel | ✅ Data not collected | | | [rimgo.in.projectsegfau.lt](https://rimgo.in.projectsegfau.lt/) | 🇮🇳 IN | Airtel | ✅ Data not collected | |
| [rimgo.fascinated.cc](https://rimgo.fascinated.cc/) | 🇺🇸 US | Cloudflare | ✅ Data not collected | |
| [rimgo.whateveritworks.org](https://rimgo.whateveritworks.org/) | 🇩🇪 DE | Cloudflare | ✅ Data not collected | | | [rimgo.whateveritworks.org](https://rimgo.whateveritworks.org/) | 🇩🇪 DE | Cloudflare | ✅ Data not collected | |
| [rimgo.nohost.network](https://rimgo.nohost.network/) | 🇲🇽 MX | Telmex | ✅ Data not collected | | | [rimgo.nohost.network](https://rimgo.nohost.network/) | 🇲🇽 MX | Telmex | ✅ Data not collected | |
| [rimgo.catsarch.com](https://rimgo.catsarch.com/) | 🇺🇸 US | Comcast | ✅ Data not collected | Self-hosted, provider is ISP | | [rimgo.catsarch.com](https://rimgo.catsarch.com/) | 🇺🇸 US | Comcast | ✅ Data not collected | Self-hosted, provider is ISP |

View File

@@ -16,14 +16,15 @@ import (
type Comment struct { type Comment struct {
Comments []Comment Comments []Comment
User User User User
Post Submission
Id string Id string
Comment string Comment string
Upvotes int64 Upvotes int64
Downvotes int64 Downvotes int64
Platform string Platform string
CreatedAt string CreatedAt string
RelTime string RelTime string
UpdatedAt string UpdatedAt string
DeletedAt string DeletedAt string
} }
@@ -55,7 +56,7 @@ func (client *Client) FetchComments(galleryID string) ([]Comment, error) {
) )
wg.Wait() wg.Wait()
client.Cache.Set(galleryID + "-comments", comments, cache.DefaultExpiration) client.Cache.Set(galleryID+"-comments", comments, cache.DefaultExpiration)
return comments, nil return comments, nil
} }
@@ -130,13 +131,14 @@ func parseComment(data gjson.Result) Comment {
Username: data.Get("account.username").String(), Username: data.Get("account.username").String(),
Avatar: userAvatar, Avatar: userAvatar,
}, },
Post: parseSubmission(data.Get("post")),
Id: data.Get("id").String(), Id: data.Get("id").String(),
Comment: comment, Comment: comment,
Upvotes: data.Get("upvote_count").Int(), Upvotes: data.Get("upvote_count").Int(),
Downvotes: data.Get("downvote_count").Int(), Downvotes: data.Get("downvote_count").Int(),
Platform: data.Get("platform").String(), Platform: data.Get("platform").String(),
CreatedAt: createdAt, CreatedAt: createdAt,
RelTime: humanize.Time(createdTime), RelTime: humanize.Time(createdTime),
UpdatedAt: updatedAt, UpdatedAt: updatedAt,
DeletedAt: deletedAt, DeletedAt: deletedAt,
} }

View File

@@ -8,6 +8,7 @@ import (
"time" "time"
"codeberg.org/rimgo/rimgo/utils" "codeberg.org/rimgo/rimgo/utils"
"github.com/patrickmn/go-cache"
"github.com/tidwall/gjson" "github.com/tidwall/gjson"
) )
@@ -64,7 +65,7 @@ func (client *Client) FetchUser(username string) (User, error) {
CreatedAt: createdTime.Format("January 2, 2006"), 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 return user, nil
} }
@@ -89,41 +90,7 @@ func (client *Client) FetchSubmissions(username string, sort string, page string
go func() { go func() {
defer wg.Done() defer wg.Done()
coverData := value.Get("images.#(id==\"" + value.Get("cover").String() + "\")") submissions = append(submissions, parseSubmission(value))
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(),
})
}() }()
return true return true
@@ -131,6 +98,102 @@ func (client *Client) FetchSubmissions(username string, sort string, page string
) )
wg.Wait() wg.Wait()
client.Cache.Set(username + "-submissions", submissions, 15*time.Minute) client.Cache.Set(username+"-submissions", submissions, 15*time.Minute)
return submissions, nil 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(),
}
}

View File

@@ -105,11 +105,25 @@
"cloudflare": true "cloudflare": true
}, },
{ {
"url": "https://rimgo.fascinated.cc", "url": "https://rimgo.eu.projectsegfau.lt",
"countries": [
"fr"
],
"cloudflare": false
},
{
"url": "https://rimgo.us.projectsegfau.lt",
"countries": [ "countries": [
"us" "us"
], ],
"cloudflare": true "cloudflare": false
},
{
"url": "https://rimgo.in.projectsegfau.lt",
"countries": [
"in"
],
"cloudflare": false
}, },
{ {
"url": "https://rimgo.whateveritworks.org", "url": "https://rimgo.whateveritworks.org",

View File

@@ -121,8 +121,9 @@ func main() {
app.Get("/a/:postID/embed", pages.HandleEmbed) app.Get("/a/:postID/embed", pages.HandleEmbed)
app.Get("/t/:tag", pages.HandleTag) app.Get("/t/:tag", pages.HandleTag)
app.Get("/t/:tag/:postID", pages.HandlePost) app.Get("/t/:tag/:postID", pages.HandlePost)
app.Get("/user/:userID", pages.HandleUser)
app.Get("/r/:sub/:postID", pages.HandlePost) 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/cover", pages.HandleUserCover)
app.Get("/user/:userID/avatar", pages.HandleUserAvatar) app.Get("/user/:userID/avatar", pages.HandleUserAvatar)
app.Get("/gallery/:postID", pages.HandlePost) app.Get("/gallery/:postID", pages.HandlePost)

View File

@@ -51,7 +51,43 @@ func HandleUser(c *fiber.Ctx) error {
"user": user, "user": user,
"submissions": submissions, "submissions": submissions,
"page": page, "page": page,
"nextPage": pageNumber + 1, "nextPage": pageNumber + 1,
"prevPage": 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,
}) })
} }

View File

@@ -0,0 +1,22 @@
<a href="{{Post.Link}}">
<div class="sm:grid gap-4 w-full" style="grid-template-columns: 120px 1fr;">
<img class="object-cover block w-full h-[300px] sm:w-[120px] sm:h-[140px] rounded-lg rounded-b-none sm:rounded-b-lg" src="{{this.Post.Cover.Url}}" alt="">
<div class="flex flex-col gap-2 bg-slate-600 p-4 rounded-lg rounded-t-none sm:rounded-t-lg w-full">
<div class="flex flex-col h-full">
<p>{{{this.Comment}}}</p>
<div class="flex-grow"></div>
<div class="flex gap-2">
<span title="{{this.CreatedAt}}">{{this.RelTime}}</span>
{{#if this.DeletedAt}}
<span class="text-md">(deleted {{this.DeletedAt}})</span>
{{/if}}
|
<img class="invert icon" src="/static/icons/PhArrowFatUp.svg" alt="Likes" width="24px" height="24px">
{{this.Upvotes}}
<img class="invert icon" src="/static/icons/PhArrowFatDown.svg" alt="Dislikes" width="24px" height="24px">
{{this.Downvotes}}
</div>
</div>
</div>
</div>
</a>

View File

@@ -21,6 +21,11 @@
<h2 class="font-bold text-2xl">{{user.Username}}</h2> <h2 class="font-bold text-2xl">{{user.Username}}</h2>
<p>{{user.Points}} pts · {{user.CreatedAt}}</p> <p>{{user.Points}} pts · {{user.CreatedAt}}</p>
</div> </div>
<hr class="sm:border-0 flex-grow">
<div class="flex flex-col sm:items-end">
<a href="/user/{{user.Username}}"><b>Submissions</b></a>
<a href="/user/{{user.Username}}/comments">Comments</a>
</div>
</div> </div>
<p class="mt-2">{{user.Bio}}</p> <p class="mt-2">{{user.Bio}}</p>
</header> </header>

44
views/userComments.hbs Normal file
View File

@@ -0,0 +1,44 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>{{user.Username}}'s comments - rimgo</title>
{{> partials/head }}
</head>
<body class="font-sans text-lg bg-slate-800 text-white">
{{> partials/nav }}
<section class="my-4 w-full flex flex-col items-center">
{{> partials/searchBar }}
</section>
<header class="p-4 rounded-xl text-white mb-4" style="background-image: url('{{user.Cover}}');">
<div class="flex flex-col sm:flex-row items-center gap-2">
<img class="rounded-full" src="{{user.Avatar}}" width="72" height="72">
<div class="items-center sm:items-start text-center sm:text-left">
<h2 class="font-bold text-2xl">{{user.Username}}</h2>
<p>{{user.Points}} pts · {{user.CreatedAt}}</p>
</div>
<hr class="sm:border-0 flex-grow">
<div class="flex flex-col items-center sm:items-end">
<a href="/user/{{user.Username}}">Submissions</a>
<a href="/user/{{user.Username}}/comments"><b>Comments</b></a>
</div>
</div>
<p class="mt-2">{{user.Bio}}</p>
</header>
<main>
<div class="comments flex flex-col gap-4">
{{#each comments}}
{{> partials/contextComment }}
{{/each}}
</div>
</main>
{{> partials/footer }}
</body>
</html>