mirror of
https://codeberg.org/video-prize-ranch/rimgo.git
synced 2026-02-14 20:45:51 +00:00
Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e21a9f4856 | ||
|
|
4ffe09bb81 | ||
|
|
d84ca93e0e | ||
|
|
4779d621ef | ||
|
|
3b95e89fa1 | ||
|
|
c208a55f40 | ||
|
|
4441d25d38 | ||
|
|
975ffa0b9c | ||
|
|
7b1314fae3 | ||
|
|
61a312aba0 | ||
|
|
fd704f53e7 | ||
|
|
bf849e1cbc | ||
|
|
cd4a36c9f7 | ||
|
|
04fbc7f5f4 | ||
|
|
189ebeefde | ||
|
|
3f40c25b04 | ||
|
|
33fa04e98d | ||
|
|
e5b87dc924 | ||
|
|
8cb2524924 |
@@ -7,15 +7,15 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Client struct {
|
type Client struct {
|
||||||
ClientID string
|
ClientID string
|
||||||
Cache *cache.Cache
|
Cache *cache.Cache
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewClient(clientId string) (*Client) {
|
func NewClient(clientId string) *Client {
|
||||||
client := Client{
|
client := Client{
|
||||||
ClientID: clientId,
|
ClientID: clientId,
|
||||||
Cache: cache.New(15*time.Minute, 15*time.Minute),
|
Cache: cache.New(15*time.Minute, 15*time.Minute),
|
||||||
}
|
}
|
||||||
|
|
||||||
return &client
|
return &client
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,19 +11,19 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type SearchResult struct {
|
type SearchResult struct {
|
||||||
Id string
|
Id string
|
||||||
Url string
|
Url string
|
||||||
ImageUrl string
|
ImageUrl string
|
||||||
Title string
|
Title string
|
||||||
User string
|
User string
|
||||||
Points string
|
Points string
|
||||||
Views string
|
Views string
|
||||||
RelTime string
|
RelTime string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (client *Client) Search(query string, page string) ([]SearchResult, error) {
|
func (client *Client) Search(query string, page string) ([]SearchResult, error) {
|
||||||
query = url.QueryEscape(query)
|
query = url.QueryEscape(query)
|
||||||
req, err := http.NewRequest("GET", "https://imgur.com/search/all/page/" + page + "?scrolled&q_size_is_mpx=off&qs=list&q=" + query, nil)
|
req, err := http.NewRequest("GET", "https://imgur.com/search/all/page/"+page+"?scrolled&q_size_is_mpx=off&qs=list&q="+query, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []SearchResult{}, err
|
return []SearchResult{}, err
|
||||||
}
|
}
|
||||||
@@ -35,16 +35,16 @@ func (client *Client) Search(query string, page string) ([]SearchResult, error)
|
|||||||
}
|
}
|
||||||
defer res.Body.Close()
|
defer res.Body.Close()
|
||||||
if res.StatusCode != 200 {
|
if res.StatusCode != 200 {
|
||||||
return []SearchResult{}, fmt.Errorf("invalid status code, got %d", res.StatusCode)
|
return []SearchResult{}, fmt.Errorf("invalid status code, got %d", res.StatusCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
doc, err := goquery.NewDocumentFromReader(res.Body)
|
doc, err := goquery.NewDocumentFromReader(res.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []SearchResult{}, err
|
return []SearchResult{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
results := []SearchResult{}
|
results := []SearchResult{}
|
||||||
doc.Find(".post-list").Each(func(i int, s *goquery.Selection) {
|
doc.Find(".post-list").Each(func(i int, s *goquery.Selection) {
|
||||||
url, _ := s.Find("a").Attr("href")
|
url, _ := s.Find("a").Attr("href")
|
||||||
imageUrl, _ := s.Find("img").Attr("src")
|
imageUrl, _ := s.Find("img").Attr("src")
|
||||||
|
|
||||||
@@ -55,18 +55,18 @@ func (client *Client) Search(query string, page string) ([]SearchResult, error)
|
|||||||
views = strings.TrimSuffix(views, " views")
|
views = strings.TrimSuffix(views, " views")
|
||||||
|
|
||||||
result := SearchResult{
|
result := SearchResult{
|
||||||
Id: strings.Split(url, "/")[2],
|
Id: strings.Split(url, "/")[2],
|
||||||
Url: url,
|
Url: url,
|
||||||
ImageUrl: strings.ReplaceAll(imageUrl, "//i.imgur.com", ""),
|
ImageUrl: strings.ReplaceAll(imageUrl, "//i.imgur.com", ""),
|
||||||
Title: s.Find(".search-item-title a").Text(),
|
Title: s.Find(".search-item-title a").Text(),
|
||||||
User: s.Find(".account").Text(),
|
User: s.Find(".account").Text(),
|
||||||
Views: views,
|
Views: views,
|
||||||
Points: points,
|
Points: points,
|
||||||
RelTime: strings.TrimSpace(postInfo[2]),
|
RelTime: strings.TrimSpace(postInfo[2]),
|
||||||
}
|
}
|
||||||
|
|
||||||
results = append(results, result)
|
results = append(results, result)
|
||||||
})
|
})
|
||||||
|
|
||||||
return results, nil
|
return results, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,6 +36,8 @@ func (client *Client) FetchTrending(section, sort, page string) ([]Submission, e
|
|||||||
case "best":
|
case "best":
|
||||||
q.Add("filter[window]", "all")
|
q.Add("filter[window]", "all")
|
||||||
q.Add("sort", "-top")
|
q.Add("sort", "-top")
|
||||||
|
case "random":
|
||||||
|
q.Add("sort", "random")
|
||||||
case "popular":
|
case "popular":
|
||||||
fallthrough
|
fallthrough
|
||||||
default:
|
default:
|
||||||
@@ -51,6 +53,8 @@ func (client *Client) FetchTrending(section, sort, page string) ([]Submission, e
|
|||||||
case "top":
|
case "top":
|
||||||
q.Add("filter[section]", "eq:top")
|
q.Add("filter[section]", "eq:top")
|
||||||
q.Add("filter[window]", "day")
|
q.Add("filter[window]", "day")
|
||||||
|
case "random":
|
||||||
|
q.Add("filter[section]", "eq:random")
|
||||||
default:
|
default:
|
||||||
q.Add("filter[section]", "eq:hot")
|
q.Add("filter[section]", "eq:hot")
|
||||||
section = "hot"
|
section = "hot"
|
||||||
|
|||||||
17
go.mod
17
go.mod
@@ -5,8 +5,6 @@ go 1.24.0
|
|||||||
require (
|
require (
|
||||||
github.com/PuerkitoBio/goquery v1.11.0
|
github.com/PuerkitoBio/goquery v1.11.0
|
||||||
github.com/dustin/go-humanize v1.0.1
|
github.com/dustin/go-humanize v1.0.1
|
||||||
github.com/gofiber/fiber/v2 v2.52.10
|
|
||||||
github.com/gofiber/template/handlebars/v2 v2.1.12
|
|
||||||
github.com/gorilla/feeds v1.2.0
|
github.com/gorilla/feeds v1.2.0
|
||||||
github.com/joho/godotenv v1.5.1
|
github.com/joho/godotenv v1.5.1
|
||||||
github.com/mailgun/raymond/v2 v2.0.48
|
github.com/mailgun/raymond/v2 v2.0.48
|
||||||
@@ -17,26 +15,13 @@ require (
|
|||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/andybalholm/brotli v1.2.0 // indirect
|
|
||||||
github.com/andybalholm/cascadia v1.3.3 // indirect
|
github.com/andybalholm/cascadia v1.3.3 // indirect
|
||||||
github.com/aymerick/douceur v0.2.0 // indirect
|
github.com/aymerick/douceur v0.2.0 // indirect
|
||||||
github.com/clipperhouse/stringish v0.1.1 // indirect
|
|
||||||
github.com/clipperhouse/uax29/v2 v2.3.0 // indirect
|
|
||||||
github.com/gofiber/template v1.8.3 // indirect
|
|
||||||
github.com/gofiber/utils v1.2.0 // indirect
|
|
||||||
github.com/google/uuid v1.6.0 // indirect
|
|
||||||
github.com/gorilla/css v1.0.1 // indirect
|
github.com/gorilla/css v1.0.1 // indirect
|
||||||
github.com/klauspost/compress v1.18.2 // indirect
|
|
||||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
|
||||||
github.com/mattn/go-runewidth v0.0.19 // indirect
|
|
||||||
github.com/philhofer/fwd v1.2.0 // indirect
|
|
||||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||||
|
github.com/stretchr/testify v1.10.0 // indirect
|
||||||
github.com/tidwall/match v1.2.0 // indirect
|
github.com/tidwall/match v1.2.0 // indirect
|
||||||
github.com/tidwall/pretty v1.2.1 // indirect
|
github.com/tidwall/pretty v1.2.1 // indirect
|
||||||
github.com/tinylib/msgp v1.6.3 // indirect
|
|
||||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
|
||||||
github.com/valyala/fasthttp v1.68.0 // indirect
|
|
||||||
golang.org/x/net v0.48.0 // indirect
|
golang.org/x/net v0.48.0 // indirect
|
||||||
golang.org/x/sys v0.39.0 // indirect
|
golang.org/x/sys v0.39.0 // indirect
|
||||||
golang.org/x/text v0.32.0 // indirect
|
golang.org/x/text v0.32.0 // indirect
|
||||||
|
|||||||
35
go.sum
35
go.sum
@@ -1,57 +1,31 @@
|
|||||||
github.com/PuerkitoBio/goquery v1.11.0 h1:jZ7pwMQXIITcUXNH83LLk+txlaEy6NVOfTuP43xxfqw=
|
github.com/PuerkitoBio/goquery v1.11.0 h1:jZ7pwMQXIITcUXNH83LLk+txlaEy6NVOfTuP43xxfqw=
|
||||||
github.com/PuerkitoBio/goquery v1.11.0/go.mod h1:wQHgxUOU3JGuj3oD/QFfxUdlzW6xPHfqyHre6VMY4DQ=
|
github.com/PuerkitoBio/goquery v1.11.0/go.mod h1:wQHgxUOU3JGuj3oD/QFfxUdlzW6xPHfqyHre6VMY4DQ=
|
||||||
github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=
|
|
||||||
github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
|
|
||||||
github.com/andybalholm/cascadia v1.3.3 h1:AG2YHrzJIm4BZ19iwJ/DAua6Btl3IwJX+VI4kktS1LM=
|
github.com/andybalholm/cascadia v1.3.3 h1:AG2YHrzJIm4BZ19iwJ/DAua6Btl3IwJX+VI4kktS1LM=
|
||||||
github.com/andybalholm/cascadia v1.3.3/go.mod h1:xNd9bqTn98Ln4DwST8/nG+H0yuB8Hmgu1YHNnWw0GeA=
|
github.com/andybalholm/cascadia v1.3.3/go.mod h1:xNd9bqTn98Ln4DwST8/nG+H0yuB8Hmgu1YHNnWw0GeA=
|
||||||
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
|
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
|
||||||
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
|
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
|
||||||
github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs=
|
|
||||||
github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA=
|
|
||||||
github.com/clipperhouse/uax29/v2 v2.3.0 h1:SNdx9DVUqMoBuBoW3iLOj4FQv3dN5mDtuqwuhIGpJy4=
|
|
||||||
github.com/clipperhouse/uax29/v2 v2.3.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g=
|
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||||
github.com/gofiber/fiber/v2 v2.52.10 h1:jRHROi2BuNti6NYXmZ6gbNSfT3zj/8c0xy94GOU5elY=
|
|
||||||
github.com/gofiber/fiber/v2 v2.52.10/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw=
|
|
||||||
github.com/gofiber/template v1.8.3 h1:hzHdvMwMo/T2kouz2pPCA0zGiLCeMnoGsQZBTSYgZxc=
|
|
||||||
github.com/gofiber/template v1.8.3/go.mod h1:bs/2n0pSNPOkRa5VJ8zTIvedcI/lEYxzV3+YPXdBvq8=
|
|
||||||
github.com/gofiber/template/handlebars/v2 v2.1.12 h1:uWBMEnhTxVyarRjyj5uDrNg8rVMGzi6fhsL+PAJBvb0=
|
|
||||||
github.com/gofiber/template/handlebars/v2 v2.1.12/go.mod h1:K3h933a8wPFjIrLRUcnIVPTUW867ND6gqpw0zZ3yKpk=
|
|
||||||
github.com/gofiber/utils v1.2.0 h1:NCaqd+Efg3khhN++eeUUTyBz+byIxAsmIjpl8kKOMIc=
|
|
||||||
github.com/gofiber/utils v1.2.0/go.mod h1:poZpsnhBykfnY1Mc0KeEa6mSHrS3dV0+oBWyeQmb2e0=
|
|
||||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
|
||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
|
||||||
github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=
|
github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=
|
||||||
github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=
|
github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=
|
||||||
github.com/gorilla/feeds v1.2.0 h1:O6pBiXJ5JHhPvqy53NsjKOThq+dNFm8+DFrxBEdzSCc=
|
github.com/gorilla/feeds v1.2.0 h1:O6pBiXJ5JHhPvqy53NsjKOThq+dNFm8+DFrxBEdzSCc=
|
||||||
github.com/gorilla/feeds v1.2.0/go.mod h1:WMib8uJP3BbY+X8Szd1rA5Pzhdfh+HCCAYT2z7Fza6Y=
|
github.com/gorilla/feeds v1.2.0/go.mod h1:WMib8uJP3BbY+X8Szd1rA5Pzhdfh+HCCAYT2z7Fza6Y=
|
||||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||||
github.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=
|
|
||||||
github.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
|
|
||||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
github.com/mailgun/raymond/v2 v2.0.48 h1:5dmlB680ZkFG2RN/0lvTAghrSxIESeu9/2aeDqACtjw=
|
github.com/mailgun/raymond/v2 v2.0.48 h1:5dmlB680ZkFG2RN/0lvTAghrSxIESeu9/2aeDqACtjw=
|
||||||
github.com/mailgun/raymond/v2 v2.0.48/go.mod h1:lsgvL50kgt1ylcFJYZiULi5fjPBkkhNfj4KA0W54Z18=
|
github.com/mailgun/raymond/v2 v2.0.48/go.mod h1:lsgvL50kgt1ylcFJYZiULi5fjPBkkhNfj4KA0W54Z18=
|
||||||
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
|
||||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
|
||||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
|
||||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
|
||||||
github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw=
|
|
||||||
github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
|
|
||||||
github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=
|
github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=
|
||||||
github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=
|
github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=
|
||||||
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
||||||
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
||||||
github.com/philhofer/fwd v1.2.0 h1:e6DnBTl7vGY+Gz322/ASL4Gyp1FspeMvx1RNDoToZuM=
|
|
||||||
github.com/philhofer/fwd v1.2.0/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||||
@@ -72,14 +46,6 @@ github.com/tidwall/match v1.2.0/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JT
|
|||||||
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||||
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
|
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
|
||||||
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||||
github.com/tinylib/msgp v1.6.3 h1:bCSxiTz386UTgyT1i0MSCvdbWjVW+8sG3PjkGsZQt4s=
|
|
||||||
github.com/tinylib/msgp v1.6.3/go.mod h1:RSp0LW9oSxFut3KzESt5Voq4GVWyS+PSulT77roAqEA=
|
|
||||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
|
||||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
|
||||||
github.com/valyala/fasthttp v1.68.0 h1:v12Nx16iepr8r9ySOwqI+5RBJ/DqTxhOy1HrHoDFnok=
|
|
||||||
github.com/valyala/fasthttp v1.68.0/go.mod h1:5EXiRfYQAoiO/khu4oU9VISC/eVY6JqmSpPJoHCKsz4=
|
|
||||||
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
|
|
||||||
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
|
|
||||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
gitlab.com/golang-commonmark/linkify v0.0.0-20200225224916-64bca66f6ad3 h1:1Coh5BsUBlXoEJmIEaNzVAWrtg9k7/eJzailMQr1grw=
|
gitlab.com/golang-commonmark/linkify v0.0.0-20200225224916-64bca66f6ad3 h1:1Coh5BsUBlXoEJmIEaNzVAWrtg9k7/eJzailMQr1grw=
|
||||||
gitlab.com/golang-commonmark/linkify v0.0.0-20200225224916-64bca66f6ad3/go.mod h1:Gn+LZmCrhPECMD3SOKlE+BOHwhOYD9j7WT9NUtkCrC8=
|
gitlab.com/golang-commonmark/linkify v0.0.0-20200225224916-64bca66f6ad3/go.mod h1:Gn+LZmCrhPECMD3SOKlE+BOHwhOYD9j7WT9NUtkCrC8=
|
||||||
@@ -120,7 +86,6 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
|||||||
231
main.go
231
main.go
@@ -3,23 +3,37 @@ package main
|
|||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"strings"
|
||||||
|
|
||||||
"codeberg.org/rimgo/rimgo/pages"
|
"codeberg.org/rimgo/rimgo/pages"
|
||||||
|
"codeberg.org/rimgo/rimgo/render"
|
||||||
"codeberg.org/rimgo/rimgo/static"
|
"codeberg.org/rimgo/rimgo/static"
|
||||||
"codeberg.org/rimgo/rimgo/utils"
|
"codeberg.org/rimgo/rimgo/utils"
|
||||||
"codeberg.org/rimgo/rimgo/views"
|
"codeberg.org/rimgo/rimgo/views"
|
||||||
"github.com/gofiber/fiber/v2"
|
|
||||||
"github.com/gofiber/fiber/v2/middleware/cache"
|
|
||||||
"github.com/gofiber/fiber/v2/middleware/filesystem"
|
|
||||||
"github.com/gofiber/fiber/v2/middleware/recover"
|
|
||||||
"github.com/gofiber/template/handlebars/v2"
|
|
||||||
"github.com/joho/godotenv"
|
"github.com/joho/godotenv"
|
||||||
"github.com/mailgun/raymond/v2"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// a handler that returns error if it can't respond
|
||||||
|
type handler func(w http.ResponseWriter, r *http.Request) error
|
||||||
|
|
||||||
|
func wrapHandler(h handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
defer func() {
|
||||||
|
if v := recover(); v != nil {
|
||||||
|
utils.RenderError(w, r, 500, fmt.Sprint(v))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
err := h(w, r)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
utils.RenderError(w, r, 500, err.Error())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
envPath := flag.String("c", ".env", "Path to env file")
|
envPath := flag.String("c", ".env", "Path to env file")
|
||||||
godotenv.Load(*envPath)
|
godotenv.Load(*envPath)
|
||||||
@@ -27,120 +41,117 @@ func main() {
|
|||||||
|
|
||||||
pages.InitializeApiClient()
|
pages.InitializeApiClient()
|
||||||
|
|
||||||
views := http.FS(views.GetFiles())
|
views := views.GetFiles()
|
||||||
if os.Getenv("ENV") == "dev" {
|
static := static.GetFiles()
|
||||||
views = http.Dir("./views")
|
render.Initialize(views)
|
||||||
}
|
|
||||||
engine := handlebars.NewFileSystem(views, ".hbs")
|
|
||||||
|
|
||||||
engine.AddFunc("noteq", func(a interface{}, b interface{}, options *raymond.Options) interface{} {
|
app := http.NewServeMux()
|
||||||
if raymond.Str(a) != raymond.Str(b) {
|
|
||||||
return options.Fn()
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
})
|
|
||||||
|
|
||||||
app := fiber.New(fiber.Config{
|
app.Handle("GET /static/", http.StripPrefix("/static/", http.FileServerFS(static)))
|
||||||
Views: engine,
|
app.Handle("GET /robots.txt", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
Prefork: utils.Config.FiberPrefork,
|
file, _ := static.Open("robots.txt")
|
||||||
UnescapePath: true,
|
defer file.Close()
|
||||||
StreamRequestBody: true,
|
io.Copy(w, file)
|
||||||
ErrorHandler: func(ctx *fiber.Ctx, err error) error {
|
}))
|
||||||
code := fiber.StatusInternalServerError
|
app.Handle("GET /favicon.ico", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
file, _ := static.Open("favicon/favicon.ico")
|
||||||
if e, ok := err.(*fiber.Error); ok {
|
defer file.Close()
|
||||||
code = e.Code
|
io.Copy(w, file)
|
||||||
}
|
|
||||||
|
|
||||||
return utils.RenderError(ctx, code)
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
app.Use(recover.New(recover.Config{
|
|
||||||
EnableStackTrace: true,
|
|
||||||
StackTraceHandler: func(c *fiber.Ctx, e interface{}) {
|
|
||||||
fmt.Println(e)
|
|
||||||
},
|
|
||||||
}))
|
}))
|
||||||
|
|
||||||
if os.Getenv("ENV") == "dev" {
|
if os.Getenv("ENV") == "dev" {
|
||||||
app.Use("/static", filesystem.New(filesystem.Config{
|
app.Handle("GET /errors/429", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
Root: http.Dir("./static"),
|
utils.RenderError(w, r, 429)
|
||||||
}))
|
}))
|
||||||
app.Get("/errors/429", func(c *fiber.Ctx) error {
|
app.Handle("GET /errors/429/img", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
return c.Render("errors/429", nil)
|
w.Header().Set("Location", "/static/img/error-429.png")
|
||||||
})
|
w.WriteHeader(302)
|
||||||
app.Get("/errors/429/img", func(c *fiber.Ctx) error {
|
|
||||||
return c.Redirect("/static/img/error-429.png")
|
|
||||||
})
|
|
||||||
app.Get("/errors/404", func(c *fiber.Ctx) error {
|
|
||||||
return c.Render("errors/404", nil)
|
|
||||||
})
|
|
||||||
app.Get("/errors/404/img", func(c *fiber.Ctx) error {
|
|
||||||
return c.Redirect("/static/img/error-404.png")
|
|
||||||
})
|
|
||||||
app.Get("/errors/error", func(c *fiber.Ctx) error {
|
|
||||||
return c.Render("errors/error", fiber.Map{
|
|
||||||
"err": "Test error",
|
|
||||||
})
|
|
||||||
})
|
|
||||||
app.Get("/errors/error/img", func(c *fiber.Ctx) error {
|
|
||||||
return c.Redirect("/static/img/error-generic.png")
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
app.Use("/static", filesystem.New(filesystem.Config{
|
|
||||||
MaxAge: 2592000,
|
|
||||||
Root: http.FS(static.GetFiles()),
|
|
||||||
}))
|
}))
|
||||||
app.Use(cache.New(cache.Config{
|
app.Handle("GET /errors/404", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
Expiration: 30 * time.Minute,
|
utils.RenderError(w, r, 404)
|
||||||
MaxBytes: 25000000,
|
}))
|
||||||
KeyGenerator: func(c *fiber.Ctx) string {
|
app.Handle("GET /errors/404/img", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
return utils.GetInstanceUrl(c) + c.OriginalURL()
|
w.Header().Set("Location", "/static/img/error-404.png")
|
||||||
},
|
w.WriteHeader(302)
|
||||||
CacheControl: true,
|
}))
|
||||||
StoreResponseHeaders: true,
|
app.Handle("GET /errors/error", wrapHandler(func(w http.ResponseWriter, r *http.Request) error {
|
||||||
|
return fmt.Errorf("Test error")
|
||||||
|
}))
|
||||||
|
app.Handle("GET /errors/panic", wrapHandler(func(w http.ResponseWriter, r *http.Request) error {
|
||||||
|
panic("Test error")
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
app.Handle("GET /{$}", wrapHandler(pages.HandleFrontpage))
|
||||||
app.Get("/robots.txt", func(c *fiber.Ctx) error {
|
app.Handle("GET /a/{postID}", wrapHandler(pages.HandlePost))
|
||||||
file, _ := static.GetFiles().ReadFile("robots.txt")
|
app.Handle("GET /a/{postID}/embed", wrapHandler(pages.HandleEmbed))
|
||||||
_, err := c.Write(file)
|
app.Handle("GET /t/{tag}", wrapHandler(func(w http.ResponseWriter, r *http.Request) error {
|
||||||
|
name, ext := utils.SplitNameExt(r.PathValue("tag"))
|
||||||
|
if ext != "" {
|
||||||
|
r.SetPathValue("tag", name)
|
||||||
|
r.SetPathValue("type", ext)
|
||||||
|
return pages.HandleTagRSS(w, r)
|
||||||
|
}
|
||||||
|
return pages.HandleTag(w, r)
|
||||||
|
}))
|
||||||
|
app.Handle("GET /t/{tag}/{postID}", wrapHandler(pages.HandlePost))
|
||||||
|
app.Handle("GET /r/{sub}/{postID}", wrapHandler(pages.HandlePost))
|
||||||
|
app.Handle("GET /user/{userID}", wrapHandler(func(w http.ResponseWriter, r *http.Request) error {
|
||||||
|
name, ext := utils.SplitNameExt(r.PathValue("userID"))
|
||||||
|
if ext != "" {
|
||||||
|
r.SetPathValue("userID", name)
|
||||||
|
r.SetPathValue("type", ext)
|
||||||
|
return pages.HandleUserRSS(w, r)
|
||||||
|
}
|
||||||
|
return pages.HandleUser(w, r)
|
||||||
|
}))
|
||||||
|
app.Handle("GET /user/{userID}/favorites", wrapHandler(pages.HandleUserFavorites))
|
||||||
|
app.Handle("GET /user/{userID}/comments", wrapHandler(pages.HandleUserComments))
|
||||||
|
app.Handle("GET /user/{userID}/cover", wrapHandler(pages.HandleUserCover))
|
||||||
|
app.Handle("GET /user/{userID}/avatar", wrapHandler(pages.HandleUserAvatar))
|
||||||
|
app.Handle("GET /gallery/{postID}", wrapHandler(pages.HandlePost))
|
||||||
|
app.Handle("GET /gallery/{postID}/embed", wrapHandler(pages.HandleEmbed))
|
||||||
|
app.Handle("GET /{component}", wrapHandler(func(w http.ResponseWriter, r *http.Request) error {
|
||||||
|
component := r.PathValue("component")
|
||||||
|
switch {
|
||||||
|
case component == "about":
|
||||||
|
return pages.HandleAbout(w, r)
|
||||||
|
case component == "privacy":
|
||||||
|
return pages.HandlePrivacy(w, r)
|
||||||
|
case component == "search":
|
||||||
|
return pages.HandleSearch(w, r)
|
||||||
|
case component == "trending":
|
||||||
|
return pages.HandleTrending(w, r)
|
||||||
|
case strings.HasPrefix(component, "trending."):
|
||||||
|
_, ext := utils.SplitNameExt(component)
|
||||||
|
r.SetPathValue("type", ext)
|
||||||
|
return pages.HandleTrendingRSS(w, r)
|
||||||
|
case strings.HasSuffix(component, ".gifv"):
|
||||||
|
r.SetPathValue("postID", component)
|
||||||
|
return pages.HandleGifv(w, r)
|
||||||
|
case strings.Contains(component, "."):
|
||||||
|
baseName, extension := utils.SplitNameExt(r.PathValue("component"))
|
||||||
|
r.SetPathValue("baseName", baseName)
|
||||||
|
r.SetPathValue("extension", extension)
|
||||||
|
switch extension {
|
||||||
|
case ".png", ".gif", ".jpg", ".jpeg", ".webp", ".mp4", ".webm":
|
||||||
|
return pages.HandleMedia(w, r)
|
||||||
|
}
|
||||||
|
fallthrough
|
||||||
|
default:
|
||||||
|
r.SetPathValue("postID", component)
|
||||||
|
return pages.HandlePost(w, r)
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
app.Handle("GET /stack/{component}", wrapHandler(pages.HandleMedia))
|
||||||
|
// matches anything with no more specific route
|
||||||
|
app.Handle("GET /", wrapHandler(func(w http.ResponseWriter, r *http.Request) error {
|
||||||
|
err := render.Render(w, "errors/404", nil)
|
||||||
return err
|
return err
|
||||||
})
|
}))
|
||||||
app.Get("/favicon.ico", func(c *fiber.Ctx) error {
|
|
||||||
file, _ := static.GetFiles().ReadFile("favicon/favicon.ico")
|
|
||||||
_, err := c.Write(file)
|
|
||||||
return err
|
|
||||||
})
|
|
||||||
|
|
||||||
app.Get("/", pages.HandleFrontpage)
|
addr := utils.Config.Addr + ":" + utils.Config.Port
|
||||||
app.Get("/about", pages.HandleAbout)
|
fmt.Println("listening on " + addr)
|
||||||
app.Get("/privacy", pages.HandlePrivacy)
|
err := http.ListenAndServe(addr, app)
|
||||||
app.Get("/search", pages.HandleSearch)
|
|
||||||
app.Get("/trending.:type", pages.HandleTrendingRSS)
|
|
||||||
app.Get("/trending", pages.HandleTrending)
|
|
||||||
app.Get("/a/:postID", pages.HandlePost)
|
|
||||||
app.Get("/a/:postID/embed", pages.HandleEmbed)
|
|
||||||
app.Get("/t/:tag.:type", pages.HandleTagRSS)
|
|
||||||
app.Get("/t/:tag", pages.HandleTag)
|
|
||||||
app.Get("/t/:tag/:postID", pages.HandlePost)
|
|
||||||
app.Get("/r/:sub/:postID", pages.HandlePost)
|
|
||||||
app.Get("/user/:userID.:type", pages.HandleUserRSS)
|
|
||||||
app.Get("/user/:userID", pages.HandleUser)
|
|
||||||
app.Get("/user/:userID/favorites", pages.HandleUserFavorites)
|
|
||||||
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)
|
|
||||||
app.Get("/gallery/:postID/embed", pages.HandleEmbed)
|
|
||||||
app.Get("/:postID.gifv", pages.HandleGifv)
|
|
||||||
app.Get("/:baseName.:extension", pages.HandleMedia)
|
|
||||||
app.Get("/stack/:baseName.:extension", pages.HandleMedia)
|
|
||||||
app.Get("/:postID", pages.HandlePost)
|
|
||||||
app.Get("/:postID/embed", pages.HandleEmbed)
|
|
||||||
|
|
||||||
err := app.Listen(utils.Config.Addr + ":" + utils.Config.Port)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +1,21 @@
|
|||||||
package pages
|
package pages
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"net/http"
|
||||||
|
|
||||||
|
"codeberg.org/rimgo/rimgo/render"
|
||||||
"codeberg.org/rimgo/rimgo/utils"
|
"codeberg.org/rimgo/rimgo/utils"
|
||||||
"github.com/gofiber/fiber/v2"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func HandleAbout(w http.ResponseWriter, r *http.Request) error {
|
||||||
|
utils.SetHeaders(w)
|
||||||
|
w.Header().Set("X-Frame-Options", "DENY")
|
||||||
|
w.Header().Set("Cache-Control", "public,max-age=31557600")
|
||||||
|
w.Header().Set("Content-Security-Policy", "default-src 'none'; frame-ancestors 'none'; base-uri 'none'; form-action 'self'; style-src 'self'; img-src 'self'; manifest-src 'self'; block-all-mixed-content")
|
||||||
|
|
||||||
func HandleAbout(c *fiber.Ctx) error {
|
return render.Render(w, "about", map[string]any{
|
||||||
utils.SetHeaders(c)
|
"proto": r.Proto,
|
||||||
c.Set("X-Frame-Options", "DENY")
|
"domain": r.Host,
|
||||||
c.Set("Cache-Control", "public,max-age=31557600")
|
"force_webp": utils.Config.ForceWebp,
|
||||||
c.Set("Content-Security-Policy", "default-src 'none'; frame-ancestors 'none'; base-uri 'none'; form-action 'self'; style-src 'self'; img-src 'self'; manifest-src 'self'; block-all-mixed-content")
|
|
||||||
|
|
||||||
return c.Render("about", fiber.Map{
|
|
||||||
"proto": c.Protocol(),
|
|
||||||
"domain": c.Hostname(),
|
|
||||||
"force_webp": os.Getenv("FORCE_WEBP"),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,5 +8,5 @@ import (
|
|||||||
var ApiClient *api.Client
|
var ApiClient *api.Client
|
||||||
|
|
||||||
func InitializeApiClient() {
|
func InitializeApiClient() {
|
||||||
ApiClient = api.NewClient(utils.Config.ImgurId)
|
ApiClient = api.NewClient(utils.Config.ImgurId)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,48 +1,49 @@
|
|||||||
package pages
|
package pages
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"codeberg.org/rimgo/rimgo/api"
|
"codeberg.org/rimgo/rimgo/api"
|
||||||
|
"codeberg.org/rimgo/rimgo/render"
|
||||||
"codeberg.org/rimgo/rimgo/utils"
|
"codeberg.org/rimgo/rimgo/utils"
|
||||||
"github.com/gofiber/fiber/v2"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func HandleEmbed(c *fiber.Ctx) error {
|
func HandleEmbed(w http.ResponseWriter, r *http.Request) error {
|
||||||
utils.SetHeaders(c)
|
utils.SetHeaders(w)
|
||||||
c.Set("Cache-Control", "public,max-age=31557600")
|
w.Header().Set("Cache-Control", "public,max-age=31557600")
|
||||||
c.Set("Content-Security-Policy", "default-src 'none'; base-uri 'none'; form-action 'none'; media-src 'self'; style-src 'self'; img-src 'self'; block-all-mixed-content")
|
w.Header().Set("Content-Security-Policy", "default-src 'none'; base-uri 'none'; form-action 'none'; media-src 'self'; style-src 'self'; img-src 'self'; block-all-mixed-content")
|
||||||
|
|
||||||
post, err := api.Album{}, error(nil)
|
post, err := api.Album{}, error(nil)
|
||||||
switch {
|
switch {
|
||||||
case strings.HasPrefix(c.Path(), "/a"):
|
case strings.HasPrefix(r.URL.Path, "/a"):
|
||||||
post, err = ApiClient.FetchAlbum(c.Params("postID"))
|
post, err = ApiClient.FetchAlbum(r.PathValue("postID"))
|
||||||
case strings.HasPrefix(c.Path(), "/gallery"):
|
case strings.HasPrefix(r.URL.Path, "/gallery"):
|
||||||
post, err = ApiClient.FetchPosts(c.Params("postID"))
|
post, err = ApiClient.FetchPosts(r.PathValue("postID"))
|
||||||
default:
|
default:
|
||||||
post, err = ApiClient.FetchMedia(c.Params("postID"))
|
post, err = ApiClient.FetchMedia(r.PathValue("postID"))
|
||||||
}
|
}
|
||||||
if err != nil && err.Error() == "ratelimited by imgur" {
|
if err != nil && err.Error() == "ratelimited by imgur" {
|
||||||
return utils.RenderError(c, 429)
|
return utils.RenderError(w, r, 429)
|
||||||
}
|
}
|
||||||
if err != nil && post.Id == "" && strings.Contains(err.Error(), "404") {
|
if err != nil && post.Id == "" && strings.Contains(err.Error(), "404") {
|
||||||
return utils.RenderError(c, 404)
|
return utils.RenderError(w, r, 404)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.Render("embed", fiber.Map{
|
return render.Render(w, "embed", map[string]any{
|
||||||
"post": post,
|
"post": post,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func HandleGifv(c *fiber.Ctx) error {
|
func HandleGifv(w http.ResponseWriter, r *http.Request) error {
|
||||||
utils.SetHeaders(c)
|
utils.SetHeaders(w)
|
||||||
c.Set("Cache-Control", "public,max-age=31557600")
|
w.Header().Set("Cache-Control", "public,max-age=31557600")
|
||||||
c.Set("Content-Security-Policy", "default-src 'none'; base-uri 'none'; form-action 'none'; media-src 'self'; style-src 'self'; img-src 'self'; block-all-mixed-content")
|
w.Header().Set("Content-Security-Policy", "default-src 'none'; base-uri 'none'; form-action 'none'; media-src 'self'; style-src 'self'; img-src 'self'; block-all-mixed-content")
|
||||||
|
|
||||||
return c.Render("gifv", fiber.Map{
|
return render.Render(w, "gifv", map[string]any{
|
||||||
"id": c.Params("postID"),
|
"id": r.PathValue("postID"),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +1,22 @@
|
|||||||
package pages
|
package pages
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"codeberg.org/rimgo/rimgo/render"
|
||||||
"codeberg.org/rimgo/rimgo/utils"
|
"codeberg.org/rimgo/rimgo/utils"
|
||||||
"github.com/gofiber/fiber/v2"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var VersionInfo string
|
var VersionInfo string
|
||||||
|
|
||||||
func HandleFrontpage(c *fiber.Ctx) error {
|
func HandleFrontpage(w http.ResponseWriter, r *http.Request) error {
|
||||||
utils.SetHeaders(c)
|
utils.SetHeaders(w)
|
||||||
c.Set("X-Frame-Options", "DENY")
|
w.Header().Set("X-Frame-Options", "DENY")
|
||||||
c.Set("Cache-Control", "public,max-age=31557600")
|
w.Header().Set("Cache-Control", "public,max-age=31557600")
|
||||||
c.Set("Content-Security-Policy", "default-src 'none'; frame-ancestors 'none'; base-uri 'none'; form-action 'self'; style-src 'self'; img-src 'self'; manifest-src 'self'; block-all-mixed-content")
|
w.Header().Set("Content-Security-Policy", "default-src 'none'; frame-ancestors 'none'; base-uri 'none'; form-action 'self'; style-src 'self'; img-src 'self'; manifest-src 'self'; block-all-mixed-content")
|
||||||
|
|
||||||
return c.Render("frontpage", fiber.Map{
|
return render.Render(w, "frontpage", map[string]any{
|
||||||
"config": utils.Config,
|
"config": utils.Config,
|
||||||
"version": VersionInfo,
|
"version": VersionInfo,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,54 +1,57 @@
|
|||||||
package pages
|
package pages
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io"
|
||||||
"mime"
|
"mime"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"codeberg.org/rimgo/rimgo/utils"
|
"codeberg.org/rimgo/rimgo/utils"
|
||||||
"github.com/gofiber/fiber/v2"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func HandleMedia(c *fiber.Ctx) error {
|
func HandleMedia(w http.ResponseWriter, r *http.Request) error {
|
||||||
c.Set("Cache-Control", "public,max-age=31557600")
|
w.Header().Set("Cache-Control", "public,max-age=31557600")
|
||||||
c.Set("Content-Security-Policy", "default-src 'none'; style-src 'self'; img-src 'self'")
|
w.Header().Set("Content-Security-Policy", "default-src 'none'; style-src 'self'; img-src 'self'")
|
||||||
if strings.HasPrefix(c.Path(), "/stack") {
|
baseName, extension := r.PathValue("baseName"), r.PathValue("extension")
|
||||||
return handleMedia(c, "https://i.stack.imgur.com/"+strings.ReplaceAll(c.Params("baseName"), "stack/", "")+"."+c.Params("extension"))
|
if strings.HasPrefix(r.URL.Path, "/stack") {
|
||||||
|
return handleMedia(w, r, "https://i.stack.imgur.com/"+baseName+extension)
|
||||||
} else {
|
} else {
|
||||||
return handleMedia(c, "https://i.imgur.com/"+c.Params("baseName")+"."+c.Params("extension"))
|
return handleMedia(w, r, "https://i.imgur.com/"+baseName+extension)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func HandleUserCover(c *fiber.Ctx) error {
|
func HandleUserCover(w http.ResponseWriter, r *http.Request) error {
|
||||||
c.Set("Cache-Control", "public,max-age=604800")
|
w.Header().Set("Cache-Control", "public,max-age=604800")
|
||||||
c.Set("Content-Security-Policy", "default-src 'none'")
|
w.Header().Set("Content-Security-Policy", "default-src 'none'")
|
||||||
return handleMedia(c, "https://imgur.com/user/"+c.Params("userID")+"/cover?maxwidth=2560")
|
return handleMedia(w, r, "https://imgur.com/user/"+r.PathValue("userID")+"/cover?maxwidth=2560")
|
||||||
}
|
}
|
||||||
|
|
||||||
func HandleUserAvatar(c *fiber.Ctx) error {
|
func HandleUserAvatar(w http.ResponseWriter, r *http.Request) error {
|
||||||
c.Set("Cache-Control", "public,max-age=604800")
|
w.Header().Set("Cache-Control", "public,max-age=604800")
|
||||||
c.Set("Content-Security-Policy", "default-src 'none'")
|
w.Header().Set("Content-Security-Policy", "default-src 'none'")
|
||||||
return handleMedia(c, "https://imgur.com/user/"+c.Params("userID")+"/avatar")
|
return handleMedia(w, r, "https://imgur.com/user/"+r.PathValue("userID")+"/avatar")
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleMedia(c *fiber.Ctx, url string) error {
|
func handleMedia(w http.ResponseWriter, r *http.Request, url string) error {
|
||||||
utils.SetHeaders(c)
|
utils.SetHeaders(w)
|
||||||
|
path := r.URL.Path
|
||||||
|
|
||||||
if utils.Config.ForceWebp &&
|
if utils.Config.ForceWebp &&
|
||||||
!strings.HasSuffix(c.Path(), ".webp") &&
|
!strings.HasSuffix(path, ".webp") &&
|
||||||
c.Get("Sec-Fetch-Dest") == "image" &&
|
r.Header.Get("Sec-Fetch-Dest") == "image" &&
|
||||||
c.Query("no_webp") == "" &&
|
r.URL.Query().Get("no_webp") == "" &&
|
||||||
c.Accepts("image/webp") == "image/webp" &&
|
utils.Accepts(r, "image/webp") &&
|
||||||
!strings.HasPrefix(c.Path(), "/stack") {
|
!strings.HasPrefix(path, "/stack") {
|
||||||
url = strings.ReplaceAll(url, ".png", ".webp")
|
url = strings.ReplaceAll(url, ".png", ".webp")
|
||||||
url = strings.ReplaceAll(url, ".jpg", ".webp")
|
url = strings.ReplaceAll(url, ".jpg", ".webp")
|
||||||
url = strings.ReplaceAll(url, ".jpeg", ".webp")
|
url = strings.ReplaceAll(url, ".jpeg", ".webp")
|
||||||
filename := strings.TrimPrefix(c.Path(), "/")
|
filename := strings.TrimPrefix(path, "/")
|
||||||
c.Set("Content-Disposition", mime.FormatMediaType("attachment", map[string]string{"filename*": filename}))
|
w.Header().Set("Content-Disposition", mime.FormatMediaType("attachment", map[string]string{"filename*": filename}))
|
||||||
}
|
}
|
||||||
|
|
||||||
if strings.HasPrefix(c.Path(), "/stack") && strings.Contains(c.OriginalURL(), "?") {
|
queryStr := r.URL.Query().Encode()
|
||||||
url = url + "?" + strings.Split(c.OriginalURL(), "?")[1]
|
if strings.HasPrefix(path, "/stack") && queryStr != "" {
|
||||||
|
url = url + "?" + queryStr
|
||||||
}
|
}
|
||||||
|
|
||||||
req, err := http.NewRequest("GET", url, nil)
|
req, err := http.NewRequest("GET", url, nil)
|
||||||
@@ -58,8 +61,9 @@ func handleMedia(c *fiber.Ctx, url string) error {
|
|||||||
|
|
||||||
utils.SetReqHeaders(req)
|
utils.SetReqHeaders(req)
|
||||||
|
|
||||||
if c.Get("Range") != "" {
|
rng := r.URL.Query().Get("Range")
|
||||||
req.Header.Set("Range", c.Get("Range"))
|
if rng != "" {
|
||||||
|
req.Header.Set("Range", rng)
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := http.DefaultClient.Do(req)
|
res, err := http.DefaultClient.Do(req)
|
||||||
@@ -68,17 +72,18 @@ func handleMedia(c *fiber.Ctx, url string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if res.StatusCode == 404 || strings.Contains(res.Request.URL.String(), "error/404") {
|
if res.StatusCode == 404 || strings.Contains(res.Request.URL.String(), "error/404") {
|
||||||
return utils.RenderError(c, 404)
|
return utils.RenderError(w, r, 404)
|
||||||
} else if res.StatusCode == 429 {
|
} else if res.StatusCode == 429 {
|
||||||
return utils.RenderError(c, 429)
|
return utils.RenderError(w, r, 429)
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Set("Accept-Ranges", "bytes")
|
w.Header().Set("Accept-Ranges", "bytes")
|
||||||
c.Set("Content-Type", res.Header.Get("Content-Type"))
|
w.Header().Set("Content-Type", res.Header.Get("Content-Type"))
|
||||||
c.Set("Content-Length", res.Header.Get("Content-Length"))
|
w.Header().Set("Content-Length", res.Header.Get("Content-Length"))
|
||||||
if res.Header.Get("Content-Range") != "" {
|
if res.Header.Get("Content-Range") != "" {
|
||||||
c.Set("Content-Range", res.Header.Get("Content-Range"))
|
w.Header().Set("Content-Range", res.Header.Get("Content-Range"))
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.SendStream(res.Body)
|
_, err = io.Copy(w, res.Body)
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,12 +3,13 @@ package pages
|
|||||||
import (
|
import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"codeberg.org/rimgo/rimgo/api"
|
"codeberg.org/rimgo/rimgo/api"
|
||||||
|
"codeberg.org/rimgo/rimgo/render"
|
||||||
"codeberg.org/rimgo/rimgo/utils"
|
"codeberg.org/rimgo/rimgo/utils"
|
||||||
"github.com/gofiber/fiber/v2"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Cursed function
|
// Cursed function
|
||||||
@@ -33,31 +34,31 @@ func nextInTag(client *api.Client, tagname, sort, page, I string) string {
|
|||||||
return tag.Posts[i+1].Link
|
return tag.Posts[i+1].Link
|
||||||
}
|
}
|
||||||
|
|
||||||
func HandlePost(c *fiber.Ctx) error {
|
func HandlePost(w http.ResponseWriter, r *http.Request) error {
|
||||||
utils.SetHeaders(c)
|
utils.SetHeaders(w)
|
||||||
c.Set("X-Frame-Options", "DENY")
|
w.Header().Set("X-Frame-Options", "DENY")
|
||||||
|
|
||||||
postId := c.Params("postID")
|
postId := r.PathValue("postID")
|
||||||
if strings.Contains(postId, "-") {
|
if strings.Contains(postId, "-") {
|
||||||
postId = postId[len(postId)-7:]
|
postId = postId[len(postId)-7:]
|
||||||
}
|
}
|
||||||
|
|
||||||
post, err := api.Album{}, error(nil)
|
post, err := api.Album{}, error(nil)
|
||||||
switch {
|
switch {
|
||||||
case strings.HasPrefix(c.Path(), "/a"):
|
case strings.HasPrefix(r.URL.Path, "/a"):
|
||||||
post, err = ApiClient.FetchAlbum(postId)
|
post, err = ApiClient.FetchAlbum(postId)
|
||||||
case strings.HasPrefix(c.Path(), "/gallery"):
|
case strings.HasPrefix(r.URL.Path, "/gallery"):
|
||||||
post, err = ApiClient.FetchPosts(postId)
|
post, err = ApiClient.FetchPosts(postId)
|
||||||
case strings.HasPrefix(c.Path(), "/t"):
|
case strings.HasPrefix(r.URL.Path, "/t"):
|
||||||
post, err = ApiClient.FetchPosts(postId)
|
post, err = ApiClient.FetchPosts(postId)
|
||||||
default:
|
default:
|
||||||
post, err = ApiClient.FetchMedia(postId)
|
post, err = ApiClient.FetchMedia(postId)
|
||||||
}
|
}
|
||||||
if err != nil && err.Error() == "ratelimited by imgur" {
|
if err != nil && err.Error() == "ratelimited by imgur" {
|
||||||
return utils.RenderError(c, 429)
|
return utils.RenderError(w, r, 429)
|
||||||
}
|
}
|
||||||
if err != nil && post.Id == "" && strings.Contains(err.Error(), "404") {
|
if err != nil && post.Id == "" && strings.Contains(err.Error(), "404") {
|
||||||
return utils.RenderError(c, 404)
|
return utils.RenderError(w, r, 404)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -65,13 +66,13 @@ func HandlePost(c *fiber.Ctx) error {
|
|||||||
|
|
||||||
comments := []api.Comment{}
|
comments := []api.Comment{}
|
||||||
if post.SharedWithCommunity {
|
if post.SharedWithCommunity {
|
||||||
c.Set("Cache-Control", "public,max-age=604800")
|
w.Header().Set("Cache-Control", "public,max-age=604800")
|
||||||
comments, err = ApiClient.FetchComments(postId)
|
comments, err = ApiClient.FetchComments(postId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
c.Set("Cache-Control", "public,max-age=31557600")
|
w.Header().Set("Cache-Control", "public,max-age=31557600")
|
||||||
}
|
}
|
||||||
|
|
||||||
nonce := ""
|
nonce := ""
|
||||||
@@ -82,16 +83,16 @@ func HandlePost(c *fiber.Ctx) error {
|
|||||||
nonce = fmt.Sprintf("%x", b)
|
nonce = fmt.Sprintf("%x", b)
|
||||||
csp = csp + " 'nonce-" + nonce + "'"
|
csp = csp + " 'nonce-" + nonce + "'"
|
||||||
}
|
}
|
||||||
c.Set("Content-Security-Policy", csp)
|
w.Header().Set("Content-Security-Policy", csp)
|
||||||
|
|
||||||
var next string
|
var next string
|
||||||
tagParam := strings.Split(c.Query("tag"), ".")
|
tagParam := strings.Split(r.URL.Query().Get("tag"), ".")
|
||||||
if len(tagParam) == 4 {
|
if len(tagParam) == 4 {
|
||||||
tag, sort, page, index := tagParam[0], tagParam[1], tagParam[2], tagParam[3]
|
tag, sort, page, index := tagParam[0], tagParam[1], tagParam[2], tagParam[3]
|
||||||
next = nextInTag(ApiClient, tag, sort, page, index)
|
next = nextInTag(ApiClient, tag, sort, page, index)
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.Render("post", fiber.Map{
|
return render.Render(w, "post", map[string]any{
|
||||||
"post": post,
|
"post": post,
|
||||||
"next": next,
|
"next": next,
|
||||||
"comments": comments,
|
"comments": comments,
|
||||||
|
|||||||
@@ -1,17 +1,18 @@
|
|||||||
package pages
|
package pages
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/gofiber/fiber/v2"
|
"net/http"
|
||||||
|
|
||||||
|
"codeberg.org/rimgo/rimgo/render"
|
||||||
"codeberg.org/rimgo/rimgo/utils"
|
"codeberg.org/rimgo/rimgo/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
func HandlePrivacy(c *fiber.Ctx) error {
|
func HandlePrivacy(w http.ResponseWriter, r *http.Request) error {
|
||||||
utils.SetHeaders(c)
|
utils.SetHeaders(w)
|
||||||
c.Set("X-Frame-Options", "DENY")
|
w.Header().Set("X-Frame-Options", "DENY")
|
||||||
c.Set("Content-Security-Policy", "default-src 'none'; form-action 'self'; style-src 'self'; img-src 'self'; manifest-src 'self'; block-all-mixed-content")
|
w.Header().Set("Content-Security-Policy", "default-src 'none'; form-action 'self'; style-src 'self'; img-src 'self'; manifest-src 'self'; block-all-mixed-content")
|
||||||
|
|
||||||
return c.Render("privacy", fiber.Map{
|
return render.Render(w, "privacy", map[string]any{
|
||||||
"config": utils.Config,
|
"config": utils.Config,
|
||||||
"version": VersionInfo,
|
"version": VersionInfo,
|
||||||
})
|
})
|
||||||
|
|||||||
71
pages/rss.go
71
pages/rss.go
@@ -1,49 +1,54 @@
|
|||||||
package pages
|
package pages
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"mime"
|
||||||
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"codeberg.org/rimgo/rimgo/api"
|
"codeberg.org/rimgo/rimgo/api"
|
||||||
"codeberg.org/rimgo/rimgo/utils"
|
"codeberg.org/rimgo/rimgo/utils"
|
||||||
"github.com/gofiber/fiber/v2"
|
|
||||||
"github.com/gorilla/feeds"
|
"github.com/gorilla/feeds"
|
||||||
)
|
)
|
||||||
|
|
||||||
func HandleTagRSS(c *fiber.Ctx) error {
|
func HandleTagRSS(w http.ResponseWriter, r *http.Request) error {
|
||||||
utils.SetHeaders(c)
|
utils.SetHeaders(w)
|
||||||
|
|
||||||
tag, err := ApiClient.FetchTag(c.Params("tag"), c.Query("sort"), "1")
|
tag, err := ApiClient.FetchTag(r.PathValue("tag"), r.URL.Query().Get("sort"), "1")
|
||||||
if err != nil && err.Error() == "ratelimited by imgur" {
|
if err != nil && err.Error() == "ratelimited by imgur" {
|
||||||
return c.Status(429).SendString("rate limited by imgur")
|
w.WriteHeader(429)
|
||||||
|
_, err := w.Write([]byte("rate limited by imgur"))
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if tag.Display == "" {
|
if tag.Display == "" {
|
||||||
return c.Status(404).SendString("tag not found")
|
w.WriteHeader(404)
|
||||||
|
_, err := w.Write([]byte("tag not found"))
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
instance := utils.GetInstanceUrl(c)
|
instance := utils.GetInstanceUrl(r)
|
||||||
|
|
||||||
feed := &feeds.Feed{
|
feed := &feeds.Feed{
|
||||||
Title: tag.Display + " on Imgur",
|
Title: tag.Display + " on Imgur",
|
||||||
Link: &feeds.Link{Href: instance + "/t/" + c.Params("tag")},
|
Link: &feeds.Link{Href: instance + "/t/" + r.PathValue("tag")},
|
||||||
Created: time.Now(),
|
Created: time.Now(),
|
||||||
}
|
}
|
||||||
|
|
||||||
return handleFeed(c, instance, feed, tag.Posts)
|
return handleFeed(w, r, instance, feed, tag.Posts)
|
||||||
}
|
}
|
||||||
|
|
||||||
func HandleTrendingRSS(c *fiber.Ctx) error {
|
func HandleTrendingRSS(w http.ResponseWriter, r *http.Request) error {
|
||||||
utils.SetHeaders(c)
|
utils.SetHeaders(w)
|
||||||
|
|
||||||
section := c.Query("section")
|
section := r.URL.Query().Get("section")
|
||||||
switch section {
|
switch section {
|
||||||
case "hot", "new", "top":
|
case "hot", "new", "top":
|
||||||
default:
|
default:
|
||||||
section = "hot"
|
section = "hot"
|
||||||
}
|
}
|
||||||
sort := c.Query("sort")
|
sort := r.URL.Query().Get("sort")
|
||||||
switch sort {
|
switch sort {
|
||||||
case "newest", "best", "popular":
|
case "newest", "best", "popular":
|
||||||
default:
|
default:
|
||||||
@@ -55,7 +60,7 @@ func HandleTrendingRSS(c *fiber.Ctx) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
instance := utils.GetInstanceUrl(c)
|
instance := utils.GetInstanceUrl(r)
|
||||||
|
|
||||||
feed := &feeds.Feed{
|
feed := &feeds.Feed{
|
||||||
Title: "Trending on Imgur",
|
Title: "Trending on Imgur",
|
||||||
@@ -63,24 +68,23 @@ func HandleTrendingRSS(c *fiber.Ctx) error {
|
|||||||
Created: time.Now(),
|
Created: time.Now(),
|
||||||
}
|
}
|
||||||
|
|
||||||
return handleFeed(c, instance, feed, results)
|
return handleFeed(w, r, instance, feed, results)
|
||||||
}
|
}
|
||||||
|
|
||||||
func HandleUserRSS(c *fiber.Ctx) error {
|
func HandleUserRSS(w http.ResponseWriter, r *http.Request) error {
|
||||||
utils.SetHeaders(c)
|
utils.SetHeaders(w)
|
||||||
|
|
||||||
user := c.Params("userID")
|
user := r.PathValue("userID")
|
||||||
|
|
||||||
submissions, err := ApiClient.FetchSubmissions(user, "newest", "1")
|
submissions, err := ApiClient.FetchSubmissions(user, "newest", "1")
|
||||||
if err != nil && err.Error() == "ratelimited by imgur" {
|
if err != nil && err.Error() == "ratelimited by imgur" {
|
||||||
c.Status(429)
|
return utils.RenderError(w, r, 429)
|
||||||
return utils.RenderError(c, 429)
|
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
instance := utils.GetInstanceUrl(c)
|
instance := utils.GetInstanceUrl(r)
|
||||||
|
|
||||||
feed := &feeds.Feed{
|
feed := &feeds.Feed{
|
||||||
Title: user + " on Imgur",
|
Title: user + " on Imgur",
|
||||||
@@ -88,10 +92,10 @@ func HandleUserRSS(c *fiber.Ctx) error {
|
|||||||
Created: time.Now(),
|
Created: time.Now(),
|
||||||
}
|
}
|
||||||
|
|
||||||
return handleFeed(c, instance, feed, submissions)
|
return handleFeed(w, r, instance, feed, submissions)
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleFeed(c *fiber.Ctx, instance string, feed *feeds.Feed, posts []api.Submission) error {
|
func handleFeed(w http.ResponseWriter, r *http.Request, instance string, feed *feeds.Feed, posts []api.Submission) error {
|
||||||
feed.Items = []*feeds.Item{}
|
feed.Items = []*feeds.Item{}
|
||||||
|
|
||||||
for _, post := range posts {
|
for _, post := range posts {
|
||||||
@@ -110,27 +114,30 @@ func handleFeed(c *fiber.Ctx, instance string, feed *feeds.Feed, posts []api.Sub
|
|||||||
feed.Items = append(feed.Items, item)
|
feed.Items = append(feed.Items, item)
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Type(c.Params("type"))
|
w.Header().Set("Content-Type", mime.TypeByExtension(r.PathValue("type")))
|
||||||
switch c.Params("type") {
|
switch r.PathValue("type") {
|
||||||
case "atom":
|
case ".atom":
|
||||||
body, err := feed.ToAtom()
|
body, err := feed.ToAtom()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return c.SendString(body)
|
w.Write([]byte(body))
|
||||||
case "json":
|
case ".json":
|
||||||
body, err := feed.ToJSON()
|
body, err := feed.ToJSON()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return c.JSON(body)
|
w.Write([]byte(body))
|
||||||
case "rss":
|
case ".rss":
|
||||||
body, err := feed.ToRss()
|
body, err := feed.ToRss()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return c.SendString(body)
|
w.Write([]byte(body))
|
||||||
default:
|
default:
|
||||||
return c.Status(400).SendString("invalid type")
|
w.Header().Set("Content-Type", "text/plain")
|
||||||
|
w.WriteHeader(400)
|
||||||
|
w.Write([]byte("invalid type"))
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,30 +1,33 @@
|
|||||||
package pages
|
package pages
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
"codeberg.org/rimgo/rimgo/render"
|
||||||
"codeberg.org/rimgo/rimgo/utils"
|
"codeberg.org/rimgo/rimgo/utils"
|
||||||
"github.com/gofiber/fiber/v2"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func HandleSearch(c *fiber.Ctx) error {
|
func HandleSearch(w http.ResponseWriter, r *http.Request) error {
|
||||||
utils.SetHeaders(c)
|
utils.SetHeaders(w)
|
||||||
c.Set("X-Frame-Options", "DENY")
|
w.Header().Set("X-Frame-Options", "DENY")
|
||||||
c.Set("Cache-Control", "public,max-age=604800")
|
w.Header().Set("Cache-Control", "public,max-age=604800")
|
||||||
c.Set("Content-Security-Policy", "default-src 'none'; frame-ancestors 'none'; base-uri 'none'; form-action 'self'; style-src 'unsafe-inline' 'self'; media-src 'self'; img-src 'self'; manifest-src 'self'; block-all-mixed-content")
|
w.Header().Set("Content-Security-Policy", "default-src 'none'; frame-ancestors 'none'; base-uri 'none'; form-action 'self'; style-src 'unsafe-inline' 'self'; media-src 'self'; img-src 'self'; manifest-src 'self'; block-all-mixed-content")
|
||||||
|
|
||||||
query := c.Query("q")
|
query := r.URL.Query().Get("q")
|
||||||
|
|
||||||
if utils.ImgurRe.MatchString(query) {
|
if utils.ImgurRe.MatchString(query) {
|
||||||
return c.Redirect(utils.ImgurRe.ReplaceAllString(query, ""))
|
w.Header().Set("Location", utils.ImgurRe.ReplaceAllString(query, ""))
|
||||||
|
w.WriteHeader(302)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
page := "0"
|
page := r.URL.Query().Get("page")
|
||||||
if c.Query("page") != "" {
|
if page == "" {
|
||||||
page = c.Query("page")
|
page = "0"
|
||||||
}
|
}
|
||||||
|
|
||||||
pageNumber, err := strconv.Atoi(c.Query("page"))
|
pageNumber, err := strconv.Atoi(page)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
pageNumber = 0
|
pageNumber = 0
|
||||||
}
|
}
|
||||||
@@ -34,11 +37,11 @@ func HandleSearch(c *fiber.Ctx) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.Render("search", fiber.Map{
|
return render.Render(w, "search", map[string]any{
|
||||||
"query": query,
|
"query": query,
|
||||||
"results": results,
|
"results": results,
|
||||||
"page": pageNumber,
|
"page": pageNumber,
|
||||||
"nextPage": pageNumber + 1,
|
"nextPage": pageNumber + 1,
|
||||||
"prevPage": pageNumber - 1,
|
"prevPage": pageNumber - 1,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
29
pages/tag.go
29
pages/tag.go
@@ -1,40 +1,41 @@
|
|||||||
package pages
|
package pages
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
"codeberg.org/rimgo/rimgo/render"
|
||||||
"codeberg.org/rimgo/rimgo/utils"
|
"codeberg.org/rimgo/rimgo/utils"
|
||||||
"github.com/gofiber/fiber/v2"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func HandleTag(c *fiber.Ctx) error {
|
func HandleTag(w http.ResponseWriter, r *http.Request) error {
|
||||||
utils.SetHeaders(c)
|
utils.SetHeaders(w)
|
||||||
c.Set("X-Frame-Options", "DENY")
|
w.Header().Set("X-Frame-Options", "DENY")
|
||||||
c.Set("Cache-Control", "public,max-age=604800")
|
w.Header().Set("Cache-Control", "public,max-age=604800")
|
||||||
c.Set("Content-Security-Policy", "default-src 'none'; frame-ancestors 'none'; base-uri 'none'; form-action 'self'; style-src 'unsafe-inline' 'self'; media-src 'self'; img-src 'self'; manifest-src 'self'; block-all-mixed-content")
|
w.Header().Set("Content-Security-Policy", "default-src 'none'; frame-ancestors 'none'; base-uri 'none'; form-action 'self'; style-src 'unsafe-inline' 'self'; media-src 'self'; img-src 'self'; manifest-src 'self'; block-all-mixed-content")
|
||||||
|
|
||||||
page := "1"
|
page := r.URL.Query().Get("page")
|
||||||
if c.Query("page") != "" {
|
if page == "" {
|
||||||
page = c.Query("page")
|
page = "1"
|
||||||
}
|
}
|
||||||
|
|
||||||
pageNumber, err := strconv.Atoi(c.Query("page"))
|
pageNumber, err := strconv.Atoi(page)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
pageNumber = 0
|
pageNumber = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
tag, err := ApiClient.FetchTag(c.Params("tag"), c.Query("sort"), page)
|
tag, err := ApiClient.FetchTag(r.PathValue("tag"), r.URL.Query().Get("sort"), page)
|
||||||
if err != nil && err.Error() == "ratelimited by imgur" {
|
if err != nil && err.Error() == "ratelimited by imgur" {
|
||||||
return utils.RenderError(c, 429)
|
return utils.RenderError(w, r, 429)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if tag.Display == "" {
|
if tag.Display == "" {
|
||||||
return utils.RenderError(c, 404)
|
return utils.RenderError(w, r, 404)
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.Render("tag", fiber.Map{
|
return render.Render(w, "tag", map[string]any{
|
||||||
"tag": tag,
|
"tag": tag,
|
||||||
"page": page,
|
"page": page,
|
||||||
"nextPage": pageNumber + 1,
|
"nextPage": pageNumber + 1,
|
||||||
|
|||||||
@@ -1,35 +1,36 @@
|
|||||||
package pages
|
package pages
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
"codeberg.org/rimgo/rimgo/render"
|
||||||
"codeberg.org/rimgo/rimgo/utils"
|
"codeberg.org/rimgo/rimgo/utils"
|
||||||
"github.com/gofiber/fiber/v2"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func HandleTrending(c *fiber.Ctx) error {
|
func HandleTrending(w http.ResponseWriter, r *http.Request) error {
|
||||||
utils.SetHeaders(c)
|
utils.SetHeaders(w)
|
||||||
c.Set("X-Frame-Options", "DENY")
|
w.Header().Set("X-Frame-Options", "DENY")
|
||||||
c.Set("Cache-Control", "public,max-age=604800")
|
w.Header().Set("Cache-Control", "public,max-age=604800")
|
||||||
c.Set("Content-Security-Policy", "default-src 'none'; frame-ancestors 'none'; base-uri 'none'; form-action 'self'; style-src 'unsafe-inline' 'self'; media-src 'self'; img-src 'self'; manifest-src 'self'; block-all-mixed-content")
|
w.Header().Set("Content-Security-Policy", "default-src 'none'; frame-ancestors 'none'; base-uri 'none'; form-action 'self'; style-src 'unsafe-inline' 'self'; media-src 'self'; img-src 'self'; manifest-src 'self'; block-all-mixed-content")
|
||||||
|
|
||||||
page := "1"
|
page := r.URL.Query().Get("page")
|
||||||
if c.Query("page") != "" {
|
if page == "" {
|
||||||
page = c.Query("page")
|
page = "1"
|
||||||
}
|
}
|
||||||
|
|
||||||
pageNumber, err := strconv.Atoi(c.Query("page"))
|
pageNumber, err := strconv.Atoi(page)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
pageNumber = 1
|
pageNumber = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
section := c.Query("section")
|
section := r.URL.Query().Get("section")
|
||||||
switch section {
|
switch section {
|
||||||
case "hot", "new", "top":
|
case "hot", "new", "top", "random":
|
||||||
default:
|
default:
|
||||||
section = "hot"
|
section = "hot"
|
||||||
}
|
}
|
||||||
sort := c.Query("sort")
|
sort := r.URL.Query().Get("sort")
|
||||||
switch sort {
|
switch sort {
|
||||||
case "newest", "best", "popular":
|
case "newest", "best", "popular":
|
||||||
default:
|
default:
|
||||||
@@ -41,12 +42,12 @@ func HandleTrending(c *fiber.Ctx) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.Render("trending", fiber.Map{
|
return render.Render(w, "trending", map[string]any{
|
||||||
"results": results,
|
"results": results,
|
||||||
"section": section,
|
"section": section,
|
||||||
"sort": sort,
|
"sort": sort,
|
||||||
"page": pageNumber,
|
"page": pageNumber,
|
||||||
"nextPage": pageNumber + 1,
|
"nextPage": pageNumber + 1,
|
||||||
"prevPage": pageNumber - 1,
|
"prevPage": pageNumber - 1,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,49 +1,49 @@
|
|||||||
package pages
|
package pages
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
"codeberg.org/rimgo/rimgo/render"
|
||||||
"codeberg.org/rimgo/rimgo/utils"
|
"codeberg.org/rimgo/rimgo/utils"
|
||||||
"github.com/gofiber/fiber/v2"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func HandleUser(c *fiber.Ctx) error {
|
func HandleUser(w http.ResponseWriter, r *http.Request) error {
|
||||||
utils.SetHeaders(c)
|
utils.SetHeaders(w)
|
||||||
c.Set("X-Frame-Options", "DENY")
|
w.Header().Set("X-Frame-Options", "DENY")
|
||||||
c.Set("Cache-Control", "public,max-age=604800")
|
w.Header().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")
|
w.Header().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")
|
||||||
|
|
||||||
page := "0"
|
page := r.URL.Query().Get("page")
|
||||||
if c.Query("page") != "" {
|
if page == "" {
|
||||||
page = c.Query("page")
|
page = "0"
|
||||||
}
|
}
|
||||||
|
|
||||||
pageNumber, err := strconv.Atoi(c.Query("page"))
|
pageNumber, err := strconv.Atoi(page)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
pageNumber = 0
|
pageNumber = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err := ApiClient.FetchUser(c.Params("userID"))
|
user, err := ApiClient.FetchUser(r.PathValue("userID"))
|
||||||
if err != nil && err.Error() == "ratelimited by imgur" {
|
if err != nil && err.Error() == "ratelimited by imgur" {
|
||||||
return utils.RenderError(c, 429)
|
return utils.RenderError(w, r, 429)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if user.Username == "" {
|
if user.Username == "" {
|
||||||
return utils.RenderError(c, 404)
|
return utils.RenderError(w, r, 404)
|
||||||
}
|
}
|
||||||
|
|
||||||
submissions, err := ApiClient.FetchSubmissions(c.Params("userID"), "newest", page)
|
submissions, err := ApiClient.FetchSubmissions(r.PathValue("userID"), "newest", page)
|
||||||
if err != nil && err.Error() == "ratelimited by imgur" {
|
if err != nil && err.Error() == "ratelimited by imgur" {
|
||||||
c.Status(429)
|
return utils.RenderError(w, r, 429)
|
||||||
return utils.RenderError(c, 429)
|
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.Render("user", fiber.Map{
|
return render.Render(w, "user", map[string]any{
|
||||||
"user": user,
|
"user": user,
|
||||||
"submissions": submissions,
|
"submissions": submissions,
|
||||||
"page": page,
|
"page": page,
|
||||||
@@ -52,74 +52,73 @@ func HandleUser(c *fiber.Ctx) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func HandleUserComments(c *fiber.Ctx) error {
|
func HandleUserComments(w http.ResponseWriter, r *http.Request) error {
|
||||||
utils.SetHeaders(c)
|
utils.SetHeaders(w)
|
||||||
c.Set("X-Frame-Options", "DENY")
|
w.Header().Set("X-Frame-Options", "DENY")
|
||||||
c.Set("Cache-Control", "public,max-age=604800")
|
w.Header().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")
|
w.Header().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"))
|
user, err := ApiClient.FetchUser(r.PathValue("userID"))
|
||||||
if err != nil && err.Error() == "ratelimited by imgur" {
|
if err != nil && err.Error() == "ratelimited by imgur" {
|
||||||
return utils.RenderError(c, 429)
|
return utils.RenderError(w, r, 429)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if user.Username == "" {
|
if user.Username == "" {
|
||||||
return utils.RenderError(c, 404)
|
return utils.RenderError(w, r, 404)
|
||||||
}
|
}
|
||||||
|
|
||||||
comments, err := ApiClient.FetchUserComments(c.Params("userID"))
|
comments, err := ApiClient.FetchUserComments(r.PathValue("userID"))
|
||||||
if err != nil && err.Error() == "ratelimited by imgur" {
|
if err != nil && err.Error() == "ratelimited by imgur" {
|
||||||
c.Status(429)
|
return utils.RenderError(w, r, 429)
|
||||||
return utils.RenderError(c, 429)
|
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.Render("userComments", fiber.Map{
|
return render.Render(w, "userComments", map[string]any{
|
||||||
"user": user,
|
"user": user,
|
||||||
"comments": comments,
|
"comments": comments,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func HandleUserFavorites(c *fiber.Ctx) error {
|
func HandleUserFavorites(w http.ResponseWriter, r *http.Request) error {
|
||||||
utils.SetHeaders(c)
|
utils.SetHeaders(w)
|
||||||
c.Set("X-Frame-Options", "DENY")
|
w.Header().Set("X-Frame-Options", "DENY")
|
||||||
c.Set("Cache-Control", "public,max-age=604800")
|
w.Header().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")
|
w.Header().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")
|
||||||
|
|
||||||
page := "0"
|
page := r.URL.Query().Get("page")
|
||||||
if c.Query("page") != "" {
|
if page == "" {
|
||||||
page = c.Query("page")
|
page = "0"
|
||||||
}
|
}
|
||||||
|
|
||||||
pageNumber, err := strconv.Atoi(c.Query("page"))
|
pageNumber, err := strconv.Atoi(page)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
pageNumber = 0
|
pageNumber = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err := ApiClient.FetchUser(c.Params("userID"))
|
user, err := ApiClient.FetchUser(r.PathValue("userID"))
|
||||||
if err != nil && err.Error() == "ratelimited by imgur" {
|
if err != nil && err.Error() == "ratelimited by imgur" {
|
||||||
return utils.RenderError(c, 429)
|
return utils.RenderError(w, r, 429)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if user.Username == "" {
|
if user.Username == "" {
|
||||||
return utils.RenderError(c, 404)
|
return utils.RenderError(w, r, 404)
|
||||||
}
|
}
|
||||||
|
|
||||||
favorites, err := ApiClient.FetchUserFavorites(c.Params("userID"), "newest", page)
|
favorites, err := ApiClient.FetchUserFavorites(r.PathValue("userID"), "newest", page)
|
||||||
if err != nil && err.Error() == "ratelimited by imgur" {
|
if err != nil && err.Error() == "ratelimited by imgur" {
|
||||||
return utils.RenderError(c, 429)
|
return utils.RenderError(w, r, 429)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.Render("userFavorites", fiber.Map{
|
return render.Render(w, "userFavorites", map[string]any{
|
||||||
"user": user,
|
"user": user,
|
||||||
"favorites": favorites,
|
"favorites": favorites,
|
||||||
"page": page,
|
"page": page,
|
||||||
|
|||||||
17
render/helpers.go
Normal file
17
render/helpers.go
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package render
|
||||||
|
|
||||||
|
import "github.com/mailgun/raymond/v2"
|
||||||
|
|
||||||
|
func (r *renderer) registerHelpers() {
|
||||||
|
funcmap := map[string]any{
|
||||||
|
"noteq": noteq,
|
||||||
|
}
|
||||||
|
raymond.RegisterHelpers(funcmap)
|
||||||
|
}
|
||||||
|
|
||||||
|
func noteq(a, b any, options *raymond.Options) any {
|
||||||
|
if raymond.Str(a) != raymond.Str(b) {
|
||||||
|
return options.Fn()
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
76
render/render.go
Normal file
76
render/render.go
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
// stolen from gofiber/template but simpler
|
||||||
|
package render
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/fs"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/mailgun/raymond/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
var Renderer *renderer
|
||||||
|
|
||||||
|
func Render(out io.Writer, name string, bind map[string]any) error {
|
||||||
|
return Renderer.Render(out, name, bind)
|
||||||
|
}
|
||||||
|
|
||||||
|
type renderer struct {
|
||||||
|
templates map[string]*raymond.Template
|
||||||
|
}
|
||||||
|
|
||||||
|
const ext = ".hbs"
|
||||||
|
|
||||||
|
func Initialize(views fs.FS) {
|
||||||
|
r := new(renderer)
|
||||||
|
r.templates = make(map[string]*raymond.Template)
|
||||||
|
r.registerHelpers()
|
||||||
|
fs.WalkDir(views, ".", func(path string, d fs.DirEntry, err error) error {
|
||||||
|
if err != nil || d.IsDir() {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
name, hasExt := strings.CutSuffix(path, ext)
|
||||||
|
if !hasExt {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
path = filepath.ToSlash(path)
|
||||||
|
file, err := views.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
buf, err := io.ReadAll(file)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
tmpl, err := raymond.Parse(string(buf))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
r.templates[name] = tmpl
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
for j := range r.templates {
|
||||||
|
for n, template := range r.templates {
|
||||||
|
r.templates[j].RegisterPartialTemplate(n, template)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Renderer = r
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *renderer) Render(out io.Writer, name string, bind map[string]any) error {
|
||||||
|
tmpl := r.templates[name]
|
||||||
|
if tmpl == nil {
|
||||||
|
return fmt.Errorf("render: template %s does not exist", name)
|
||||||
|
}
|
||||||
|
parsed, err := tmpl.Exec(bind)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("render: %w", err)
|
||||||
|
}
|
||||||
|
if _, err = out.Write([]byte(parsed)); err != nil {
|
||||||
|
return fmt.Errorf("render: %w", err)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
@@ -7,4 +7,4 @@ var files embed.FS
|
|||||||
|
|
||||||
func GetFiles() embed.FS {
|
func GetFiles() embed.FS {
|
||||||
return files
|
return files
|
||||||
}
|
}
|
||||||
|
|||||||
22
utils/accepts.go
Normal file
22
utils/accepts.go
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Accepts(r *http.Request, format string) bool {
|
||||||
|
format = strings.ToLower(format)
|
||||||
|
group := strings.Split(format, "/")[0] + "/*"
|
||||||
|
header := r.Header.Get("Accept")
|
||||||
|
if header == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, mime := range strings.Split(header, ",") {
|
||||||
|
mime = strings.ToLower(strings.TrimSpace(strings.SplitN(mime, ";", 2)[0]))
|
||||||
|
if mime == "*/*" || mime == format || mime == group {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
@@ -11,7 +11,6 @@ type config struct {
|
|||||||
ImgurId string
|
ImgurId string
|
||||||
ProtocolDetection bool
|
ProtocolDetection bool
|
||||||
Secure bool
|
Secure bool
|
||||||
FiberPrefork bool
|
|
||||||
ForceWebp bool
|
ForceWebp bool
|
||||||
ImageCache bool
|
ImageCache bool
|
||||||
CleanupInterval time.Duration
|
CleanupInterval time.Duration
|
||||||
@@ -21,51 +20,37 @@ type config struct {
|
|||||||
|
|
||||||
var Config config
|
var Config config
|
||||||
|
|
||||||
|
func envString(name, def string) string {
|
||||||
|
env := os.Getenv(name)
|
||||||
|
if env != "" {
|
||||||
|
return env
|
||||||
|
}
|
||||||
|
return def
|
||||||
|
}
|
||||||
|
func envBool(name string) bool {
|
||||||
|
return os.Getenv(name) == "true" || os.Getenv(name) == "1"
|
||||||
|
}
|
||||||
|
|
||||||
func LoadConfig() {
|
func LoadConfig() {
|
||||||
port := "3000"
|
|
||||||
if os.Getenv("PORT") != "" {
|
|
||||||
port = os.Getenv("PORT")
|
|
||||||
}
|
|
||||||
if os.Getenv("RIMGU_PORT") != "" {
|
|
||||||
port = os.Getenv("RIMGU_PORT")
|
|
||||||
}
|
|
||||||
|
|
||||||
addr := "0.0.0.0"
|
|
||||||
if os.Getenv("ADDRESS") != "" {
|
|
||||||
addr = os.Getenv("ADDRESS")
|
|
||||||
}
|
|
||||||
if os.Getenv("RIMGU_ADDRESS") != "" {
|
|
||||||
addr = os.Getenv("RIMGU_ADDRESS")
|
|
||||||
}
|
|
||||||
|
|
||||||
imgurId := "546c25a59c58ad7"
|
|
||||||
if os.Getenv("IMGUR_CLIENT_ID") != "" {
|
|
||||||
imgurId = os.Getenv("IMGUR_CLIENT_ID")
|
|
||||||
}
|
|
||||||
if os.Getenv("RIMGU_IMGUR_CLIENT_ID") != "" {
|
|
||||||
imgurId = os.Getenv("RIMGU_IMGUR_CLIENT_ID")
|
|
||||||
}
|
|
||||||
|
|
||||||
Config = config{
|
Config = config{
|
||||||
Port: port,
|
Port: envString("PORT", "3000"),
|
||||||
Addr: addr,
|
Addr: envString("ADDR", "0.0.0.0"),
|
||||||
ImgurId: imgurId,
|
ImgurId: envString("IMGUR_CLIENT_ID", "546c25a59c58ad7"),
|
||||||
ProtocolDetection: os.Getenv("PROTOCOL_DETECTION") == "true" || os.Getenv("PROTOCOL_DETECTION") == "1",
|
ProtocolDetection: envBool("PROTOCOL_DETECTION"),
|
||||||
Secure: os.Getenv("SECURE") == "true" || os.Getenv("SECURE") == "1",
|
Secure: envBool("SECURE"),
|
||||||
FiberPrefork: os.Getenv("FIBER_PREFORK") == "true" || os.Getenv("FIBER_PREFORK") == "1",
|
ForceWebp: envBool("FORCE_WEBP"),
|
||||||
ForceWebp: os.Getenv("FORCE_WEBP") == "true" || os.Getenv("FORCE_WEBP") == "1",
|
|
||||||
Privacy: map[string]interface{}{
|
Privacy: map[string]interface{}{
|
||||||
"set": os.Getenv("PRIVACY_NOT_COLLECTED") != "",
|
"set": os.Getenv("PRIVACY_NOT_COLLECTED") != "",
|
||||||
"policy": os.Getenv("PRIVACY_POLICY"),
|
"policy": os.Getenv("PRIVACY_POLICY"),
|
||||||
"message": os.Getenv("PRIVACY_MESSAGE"),
|
"message": os.Getenv("PRIVACY_MESSAGE"),
|
||||||
"country": os.Getenv("PRIVACY_COUNTRY"),
|
"country": os.Getenv("PRIVACY_COUNTRY"),
|
||||||
"provider": os.Getenv("PRIVACY_PROVIDER"),
|
"provider": os.Getenv("PRIVACY_PROVIDER"),
|
||||||
"cloudflare": os.Getenv("PRIVACY_CLOUDFLARE") == "true" || os.Getenv("PRIVACY_CLOUDFLARE") == "1",
|
"cloudflare": envBool("PRIVACY_CLOUDFLARE"),
|
||||||
"not_collected": os.Getenv("PRIVACY_NOT_COLLECTED") == "true" || os.Getenv("PRIVACY_NOT_COLLECTED") == "1",
|
"not_collected": envBool("PRIVACY_NOT_COLLECTED"),
|
||||||
"ip": os.Getenv("PRIVACY_IP") == "true" || os.Getenv("PRIVACY_IP") == "1",
|
"ip": envBool("PRIVACY_IP"),
|
||||||
"url": os.Getenv("PRIVACY_URL") == "true" || os.Getenv("PRIVACY_URL") == "1",
|
"url": envBool("PRIVACY_URL"),
|
||||||
"device": os.Getenv("PRIVACY_DEVICE") == "true" || os.Getenv("PRIVACY_DEVICE") == "1",
|
"device": envBool("PRIVACY_DEVICE"),
|
||||||
"diagnostics": os.Getenv("PRIVACY_DIAGNOSTICS") == "true" || os.Getenv("PRIVACY_DIAGNOSTICS") == "1",
|
"diagnostics": envBool("PRIVACY_DIAGNOSTICS"),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +1,43 @@
|
|||||||
package utils
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
|
||||||
|
|
||||||
|
"codeberg.org/rimgo/rimgo/render"
|
||||||
"codeberg.org/rimgo/rimgo/static"
|
"codeberg.org/rimgo/rimgo/static"
|
||||||
"github.com/gofiber/fiber/v2"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func RenderError(c *fiber.Ctx, code int) error {
|
func RenderError(w http.ResponseWriter, r *http.Request, code int, str ...string) (err error) {
|
||||||
if !strings.Contains(c.Get("Accept"), "html") && c.Params("extension") != "" {
|
if len(str) != 1 {
|
||||||
codeStr := "generic"
|
str = []string{""}
|
||||||
if code != 0 {
|
}
|
||||||
codeStr = strconv.Itoa(code)
|
codeStr := "generic"
|
||||||
}
|
if code == 0 {
|
||||||
img, _ := static.GetFiles().ReadFile("img/error-" + codeStr + ".png")
|
code = 500
|
||||||
c.Set("Content-Type", "image/png")
|
}
|
||||||
return c.Status(code).Send(img)
|
if code != 500 {
|
||||||
|
codeStr = strconv.Itoa(code)
|
||||||
|
}
|
||||||
|
if !Accepts(r, "text/html") && r.PathValue("extension") != "" {
|
||||||
|
w.Header().Set("Content-Type", "image/png")
|
||||||
|
w.WriteHeader(code)
|
||||||
|
file, _ := static.GetFiles().Open("img/error-" + codeStr + ".png")
|
||||||
|
defer file.Close()
|
||||||
|
_, err = io.Copy(w, file)
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
return c.Status(code).Render("errors/" + strconv.Itoa(code), fiber.Map{
|
w.WriteHeader(code)
|
||||||
"path": c.Path(),
|
err = render.Render(w, "errors/"+codeStr, map[string]any{
|
||||||
|
"path": r.URL.Path,
|
||||||
|
"err": str[0],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
if err != nil {
|
||||||
|
// don't panic or return error, it will loop
|
||||||
|
fmt.Println("error in RenderError: " + err.Error())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,4 +9,4 @@ func FormatDate(date string) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return time.Format("Jan 2, 2006 3:04 PM"), nil
|
return time.Format("Jan 2, 2006 3:04 PM"), nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,21 @@
|
|||||||
package utils
|
package utils
|
||||||
|
|
||||||
import "github.com/gofiber/fiber/v2"
|
import "net/http"
|
||||||
|
|
||||||
func GetInstanceProtocol(c *fiber.Ctx) string {
|
func GetInstanceProtocol(r *http.Request) string {
|
||||||
proto := "https"
|
proto := "https"
|
||||||
if !Config.Secure {
|
if !Config.Secure {
|
||||||
proto = "http"
|
proto = "http"
|
||||||
}
|
}
|
||||||
if Config.ProtocolDetection {
|
if Config.ProtocolDetection {
|
||||||
proto = c.Get("X-Forwarded-Proto", proto)
|
xproto := r.Header.Get("X-Forwarded-Proto")
|
||||||
|
if xproto != "" {
|
||||||
|
proto = xproto
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return proto
|
return proto
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetInstanceUrl(c *fiber.Ctx) string {
|
func GetInstanceUrl(r *http.Request) string {
|
||||||
return GetInstanceProtocol(c) + "://" + c.Hostname()
|
return GetInstanceProtocol(r) + "://" + r.Host
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,4 +2,4 @@ package utils
|
|||||||
|
|
||||||
import "regexp"
|
import "regexp"
|
||||||
|
|
||||||
var ImgurRe = regexp.MustCompile(`https?://(i\.)?imgur\.com`)
|
var ImgurRe = regexp.MustCompile(`https?://(i\.)?imgur\.com`)
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ func GetJSON(url string) (gjson.Result, error) {
|
|||||||
return gjson.Result{}, err
|
return gjson.Result{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (res.StatusCode) {
|
switch res.StatusCode {
|
||||||
case 200:
|
case 200:
|
||||||
return gjson.Parse(string(body)), nil
|
return gjson.Parse(string(body)), nil
|
||||||
case 429:
|
case 429:
|
||||||
@@ -43,4 +43,4 @@ func GetJSON(url string) (gjson.Result, error) {
|
|||||||
default:
|
default:
|
||||||
return gjson.Result{}, fmt.Errorf("received status %s, expected 200 OK.\n%s", res.Status, string(body))
|
return gjson.Result{}, fmt.Errorf("received status %s, expected 200 OK.\n%s", res.Status, string(body))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,16 +2,14 @@ package utils
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func SetHeaders(c *fiber.Ctx) {
|
func SetHeaders(w http.ResponseWriter) {
|
||||||
c.Set("Referrer-Policy", "no-referrer")
|
w.Header().Set("Referrer-Policy", "no-referrer")
|
||||||
c.Set("X-Content-Type-Options", "nosniff")
|
w.Header().Set("X-Content-Type-Options", "nosniff")
|
||||||
c.Set("X-Robots-Tag", "noindex, noimageindex, nofollow")
|
w.Header().Set("X-Robots-Tag", "noindex, noimageindex, nofollow")
|
||||||
c.Set("Strict-Transport-Security", "max-age=31557600")
|
w.Header().Set("Strict-Transport-Security", "max-age=31557600")
|
||||||
c.Set("Permissions-Policy", "accelerometer=(), ambient-light-sensor=(), autoplay=(), battery=(), camera=(), cross-origin-isolated=(), display-capture=(), document-domain=(), encrypted-media=(), execution-while-not-rendered=(), execution-while-out-of-viewport=(), fullscreen=(self), geolocation=(), gyroscope=(), interest-cohort=(), magnetometer=(), microphone=(), midi=(), navigation-override=(), payment=(), picture-in-picture=(), publickey-credentials-get=(), screen-wake-lock=(), sync-xhr=(), usb=(), web-share=(), xr-spatial-tracking=()")
|
w.Header().Set("Permissions-Policy", "accelerometer=(), ambient-light-sensor=(), autoplay=(), battery=(), camera=(), cross-origin-isolated=(), display-capture=(), document-domain=(), encrypted-media=(), execution-while-not-rendered=(), execution-while-out-of-viewport=(), fullscreen=(self), geolocation=(), gyroscope=(), interest-cohort=(), magnetometer=(), microphone=(), midi=(), navigation-override=(), payment=(), picture-in-picture=(), publickey-credentials-get=(), screen-wake-lock=(), sync-xhr=(), usb=(), web-share=(), xr-spatial-tracking=()")
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetReqHeaders(req *http.Request) {
|
func SetReqHeaders(req *http.Request) {
|
||||||
@@ -25,4 +23,4 @@ func SetReqHeaders(req *http.Request) {
|
|||||||
req.Header.Set("Sec-Fetch-Mode", "cors")
|
req.Header.Set("Sec-Fetch-Mode", "cors")
|
||||||
req.Header.Set("Sec-Fetch-Site", "same-site")
|
req.Header.Set("Sec-Fetch-Site", "same-site")
|
||||||
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; rv:102.0) Gecko/20100101 Firefox/102.0")
|
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; rv:102.0) Gecko/20100101 Firefox/102.0")
|
||||||
}
|
}
|
||||||
|
|||||||
8
utils/splitNameExt.go
Normal file
8
utils/splitNameExt.go
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import "path/filepath"
|
||||||
|
|
||||||
|
func SplitNameExt(path string) (name, ext string) {
|
||||||
|
ext = filepath.Ext(path)
|
||||||
|
return path[:len(path)-len(ext)], ext
|
||||||
|
}
|
||||||
@@ -7,4 +7,4 @@ var files embed.FS
|
|||||||
|
|
||||||
func GetFiles() embed.FS {
|
func GetFiles() embed.FS {
|
||||||
return files
|
return files
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,20 +30,35 @@
|
|||||||
<a href="?section=hot&sort={{sort}}"><b>Hot</b></a>
|
<a href="?section=hot&sort={{sort}}"><b>Hot</b></a>
|
||||||
<a href="?section=new&sort={{sort}}">New</a>
|
<a href="?section=new&sort={{sort}}">New</a>
|
||||||
<a href="?section=top&sort={{sort}}">Top</a>
|
<a href="?section=top&sort={{sort}}">Top</a>
|
||||||
|
<a href="?section=random&sort=random">Random</a>
|
||||||
{{/equal}}
|
{{/equal}}
|
||||||
{{#equal section "new"}}
|
{{#equal section "new"}}
|
||||||
<a href="?section=hot&sort={{sort}}">Hot</a>
|
<a href="?section=hot&sort={{sort}}">Hot</a>
|
||||||
<a href="?section=new&sort={{sort}}"><b>New</b></a>
|
<a href="?section=new&sort={{sort}}"><b>New</b></a>
|
||||||
<a href="?section=top&sort={{sort}}">Top</a>
|
<a href="?section=top&sort={{sort}}">Top</a>
|
||||||
|
<a href="?section=random&sort=random">Random</a>
|
||||||
{{/equal}}
|
{{/equal}}
|
||||||
{{#equal section "top"}}
|
{{#equal section "top"}}
|
||||||
<a href="?section=hot&sort={{sort}}">Hot</a>
|
<a href="?section=hot&sort={{sort}}">Hot</a>
|
||||||
<a href="?section=new&sort={{sort}}">New</a>
|
<a href="?section=new&sort={{sort}}">New</a>
|
||||||
<a href="?section=top&sort={{sort}}"><b>Top</b></a>
|
<a href="?section=top&sort={{sort}}"><b>Top</b></a>
|
||||||
|
<a href="?section=random&sort=random">Random</a>
|
||||||
|
{{/equal}}
|
||||||
|
{{#equal section "random"}}
|
||||||
|
<a href="?section=hot&sort={{sort}}">Hot</a>
|
||||||
|
<a href="?section=new&sort={{sort}}">New</a>
|
||||||
|
<a href="?section=top&sort={{sort}}">Top</a>
|
||||||
|
<a href="?section=random&sort=random"><b>Random</b></a>
|
||||||
{{/equal}}
|
{{/equal}}
|
||||||
</div>
|
</div>
|
||||||
<hr class="sm:hidden my-2" />
|
<hr class="sm:hidden my-2" />
|
||||||
<div class="flex flex-col sm:items-end">
|
<div class="flex flex-col sm:items-end">
|
||||||
|
{{#equal section "random"}}
|
||||||
|
<a href="?section=hot&sort=popular">Popular</a>
|
||||||
|
<a href="?section=hot&sort=newest">Newest</a>
|
||||||
|
<a href="?section=hot&sort=best">Best</a>
|
||||||
|
{{/equal}}
|
||||||
|
{{#noteq section "random"}}
|
||||||
{{#equal sort "popular"}}
|
{{#equal sort "popular"}}
|
||||||
<a href="?section={{section}}&sort=popular"><b>Popular</b></a>
|
<a href="?section={{section}}&sort=popular"><b>Popular</b></a>
|
||||||
<a href="?section={{section}}&sort=newest">Newest</a>
|
<a href="?section={{section}}&sort=newest">Newest</a>
|
||||||
@@ -59,6 +74,7 @@
|
|||||||
<a href="?section={{section}}&sort=newest">Newest</a>
|
<a href="?section={{section}}&sort=newest">Newest</a>
|
||||||
<a href="?section={{section}}&sort=best"><b>Best</b></a>
|
<a href="?section={{section}}&sort=best"><b>Best</b></a>
|
||||||
{{/equal}}
|
{{/equal}}
|
||||||
|
{{/noteq}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|||||||
Reference in New Issue
Block a user