mirror of
https://codeberg.org/video-prize-ranch/rimgo.git
synced 2026-02-15 21:16:16 +00:00
Compare commits
24 Commits
v1.3.0
...
permissive
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b78ed44730 | ||
|
|
c208a55f40 | ||
|
|
4441d25d38 | ||
|
|
975ffa0b9c | ||
|
|
7b1314fae3 | ||
|
|
fd704f53e7 | ||
|
|
bf849e1cbc | ||
|
|
cd4a36c9f7 | ||
|
|
04fbc7f5f4 | ||
|
|
189ebeefde | ||
|
|
3f40c25b04 | ||
|
|
33fa04e98d | ||
|
|
e5b87dc924 | ||
|
|
8cb2524924 | ||
|
|
23b66cba47 | ||
|
|
eabb7d9917 | ||
|
|
3b8ad3f360 | ||
|
|
e8ed026962 | ||
|
|
107a45e58b | ||
|
|
751616d967 | ||
|
|
09222a116f | ||
|
|
42174c052d | ||
|
|
c22365100d | ||
|
|
26edb6a385 |
@@ -3,14 +3,14 @@ FROM --platform=$BUILDPLATFORM golang:alpine AS build
|
|||||||
ARG TARGETARCH
|
ARG TARGETARCH
|
||||||
|
|
||||||
WORKDIR /src
|
WORKDIR /src
|
||||||
RUN apk --no-cache add ca-certificates git nodejs npm
|
RUN apk --no-cache add ca-certificates git nodejs pnpm
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
RUN npx @tailwindcss/cli -i static/tailwind.css -o static/app.css -m
|
RUN pnpm install && pnpm run build
|
||||||
RUN go mod download
|
RUN go mod download
|
||||||
RUN GOOS=linux GOARCH=$TARGETARCH CGO_ENABLED=0 go build -ldflags "-X codeberg.org/rimgo/rimgo/pages.VersionInfo=$(date '+%Y-%m-%d')-$(git rev-list --abbrev-commit -1 HEAD)"
|
RUN GOOS=linux GOARCH=$TARGETARCH CGO_ENABLED=0 go build -ldflags "-X codeberg.org/rimgo/rimgo/pages.VersionInfo=$(date '+%Y-%m-%d')-$(git rev-list --abbrev-commit -1 HEAD)"
|
||||||
|
|
||||||
FROM scratch as bin
|
FROM scratch AS bin
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
|
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
|
||||||
|
|||||||
@@ -17,8 +17,6 @@ An alternative frontend for Imgur. Originally based on [rimgu](https://codeberg.
|
|||||||
- [Privacy](#privacy)
|
- [Privacy](#privacy)
|
||||||
- [Usage](#usage)
|
- [Usage](#usage)
|
||||||
- [Instances](#instances)
|
- [Instances](#instances)
|
||||||
- [Clearnet](#clearnet)
|
|
||||||
- [Tor](#tor)
|
|
||||||
- [Contributing](#contributing)
|
- [Contributing](#contributing)
|
||||||
- [License](#license)
|
- [License](#license)
|
||||||
|
|
||||||
@@ -28,7 +26,7 @@ Our new documentation is now available at [https://rimgo.codeberg.page/docs/](ht
|
|||||||
|
|
||||||
- [Install](https://rimgo.codeberg.page/docs/getting-started/install/)
|
- [Install](https://rimgo.codeberg.page/docs/getting-started/install/)
|
||||||
- [Configuration](https://rimgo.codeberg.page/docs/usage/configuration/)
|
- [Configuration](https://rimgo.codeberg.page/docs/usage/configuration/)
|
||||||
- [Redirection](https://rimgo.codeberg.page/docs/usage/configuration/)
|
- [Redirection](https://rimgo.codeberg.page/docs/usage/redirection/)
|
||||||
- [Instance privacy](https://rimgo.codeberg.page/docs/usage/instance-privacy/)
|
- [Instance privacy](https://rimgo.codeberg.page/docs/usage/instance-privacy/)
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
@@ -52,12 +50,12 @@ Tested using [Google PageSpeed Insights](https://pagespeed.web.dev/).
|
|||||||
| Time to Interactive | 1.6s | 23.8s |
|
| Time to Interactive | 1.6s | 23.8s |
|
||||||
|
|
||||||
### Privacy
|
### Privacy
|
||||||
Imgur collects information about your device and uses tracking cookies for advertising, this is mentioned in their [privacy policy](https://imgur.com/privacy/). [Blacklight](https://themarkup.org/blacklight) found 31 trackers and 87 third-party cookies.
|
Imgur collects information about your device and uses tracking cookies for advertising, this is mentioned in their [privacy policy](https://imgur.com/privacy/). [Blacklight](https://themarkup.org/blacklight) found 84 trackers and 264 third-party cookies.
|
||||||
|
|
||||||
See what cookies and trackers Imgur uses and where your data gets sent: https://themarkup.org/blacklight?url=imgur.com
|
See what cookies and trackers Imgur uses and where your data gets sent: https://themarkup.org/blacklight?url=imgur.com
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
Replace imgur.com or i.imgur.com with the instance domain. For i.stack.imgur.com, replace i.stack.imgur.com with the instance domain and add stack/ before the media ID. You can use a browser extension to do this [automatically](#automatically-redirect-links).
|
Replace imgur.com or i.imgur.com with the instance domain. For i.stack.imgur.com, replace i.stack.imgur.com with the instance domain and add stack/ before the media ID.
|
||||||
|
|
||||||
Imgur: `https://imgur.com/gallery/j2sOQkJ` -> `https://rimgo.bcow.xyz/gallery/j2sOQkJ`
|
Imgur: `https://imgur.com/gallery/j2sOQkJ` -> `https://rimgo.bcow.xyz/gallery/j2sOQkJ`
|
||||||
Stack Overflow: `https://i.stack.imgur.com/KnO3v.jpg?s=64&g=1` -> `https://rimgo.bcow.xyz/stack/KnO3v.jpg?s=64&g=1`
|
Stack Overflow: `https://i.stack.imgur.com/KnO3v.jpg?s=64&g=1` -> `https://rimgo.bcow.xyz/stack/KnO3v.jpg?s=64&g=1`
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
version: '3'
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
rimgo:
|
rimgo:
|
||||||
image: codeberg.org/rimgo/rimgo # Official image
|
image: codeberg.org/rimgo/rimgo # Official image
|
||||||
|
|||||||
31
go.mod
31
go.mod
@@ -1,14 +1,10 @@
|
|||||||
module codeberg.org/rimgo/rimgo
|
module codeberg.org/rimgo/rimgo
|
||||||
|
|
||||||
go 1.23
|
go 1.24.0
|
||||||
|
|
||||||
toolchain go1.23.5
|
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/PuerkitoBio/goquery v1.10.1
|
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.6
|
|
||||||
github.com/gofiber/template/handlebars/v2 v2.1.11
|
|
||||||
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
|
||||||
@@ -19,27 +15,14 @@ require (
|
|||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/andybalholm/brotli v1.1.1 // 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/gofiber/template v1.8.3 // indirect
|
|
||||||
github.com/gofiber/utils v1.1.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.17.11 // 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.16 // indirect
|
|
||||||
github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c // indirect
|
|
||||||
github.com/rivo/uniseg v0.4.7 // indirect
|
|
||||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||||
github.com/tidwall/match v1.1.1 // indirect
|
github.com/stretchr/testify v1.10.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.2.5 // indirect
|
golang.org/x/net v0.48.0 // indirect
|
||||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
golang.org/x/sys v0.39.0 // indirect
|
||||||
github.com/valyala/fasthttp v1.58.0 // indirect
|
golang.org/x/text v0.32.0 // indirect
|
||||||
github.com/valyala/tcplisten v1.0.0 // indirect
|
|
||||||
golang.org/x/net v0.34.0 // indirect
|
|
||||||
golang.org/x/sys v0.29.0 // indirect
|
|
||||||
golang.org/x/text v0.21.0 // indirect
|
|
||||||
)
|
)
|
||||||
|
|||||||
54
go.sum
54
go.sum
@@ -1,7 +1,5 @@
|
|||||||
github.com/PuerkitoBio/goquery v1.10.1 h1:Y8JGYUkXWTGRB6Ars3+j3kN0xg1YqqlwvdTV8WTFQcU=
|
github.com/PuerkitoBio/goquery v1.11.0 h1:jZ7pwMQXIITcUXNH83LLk+txlaEy6NVOfTuP43xxfqw=
|
||||||
github.com/PuerkitoBio/goquery v1.10.1/go.mod h1:IYiHrOMps66ag56LEH7QYDDupKXyo5A8qrjIx3ZtujY=
|
github.com/PuerkitoBio/goquery v1.11.0/go.mod h1:wQHgxUOU3JGuj3oD/QFfxUdlzW6xPHfqyHre6VMY4DQ=
|
||||||
github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
|
|
||||||
github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
|
|
||||||
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=
|
||||||
@@ -11,48 +9,25 @@ 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.6 h1:Rfp+ILPiYSvvVuIPvxrBns+HJp8qGLDnLJawAu27XVI=
|
|
||||||
github.com/gofiber/fiber/v2 v2.52.6/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.11 h1:pgPF+DKuIvCl3z/Kj1u6VA/8hLBljH2Rg6LwVFBm7aM=
|
|
||||||
github.com/gofiber/template/handlebars/v2 v2.1.11/go.mod h1:AbKfYOgH+ngxaYXtLzafy4AKLAQ2NrJYbvtWaOX82I4=
|
|
||||||
github.com/gofiber/utils v1.1.0 h1:vdEBpn7AzIUJRhe+CiTOJdUcTg4Q9RK+pEa0KPbLdrM=
|
|
||||||
github.com/gofiber/utils v1.1.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.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
|
|
||||||
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
|
|
||||||
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.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
|
|
||||||
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
|
||||||
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.1.3-0.20240916144458-20a13a1f6b7c h1:dAMKvw0MlJT1GshSTtih8C2gDs04w8dReiOGXrGLNoY=
|
|
||||||
github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c/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/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
|
||||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
|
||||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
|
||||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||||
@@ -65,21 +40,12 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf
|
|||||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
|
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
|
||||||
github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||||
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
|
||||||
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||||
|
github.com/tidwall/match v1.2.0 h1:0pt8FlkOwjN2fPt4bIl4BoNxb98gGHN2ObFEDkrfZnM=
|
||||||
|
github.com/tidwall/match v1.2.0/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||||
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.2.5 h1:WeQg1whrXRFiZusidTQqzETkRpGjFjcIhW6uqWH09po=
|
|
||||||
github.com/tinylib/msgp v1.2.5/go.mod h1:ykjzy2wzgrlvpDCRc4LA8UXy6D8bzMSuAF3WD57Gok0=
|
|
||||||
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.58.0 h1:GGB2dWxSbEprU9j0iMJHgdKYJVDyjrOwF9RE59PbRuE=
|
|
||||||
github.com/valyala/fasthttp v1.58.0/go.mod h1:SYXvHHaFp7QZHGKSHmoMipInhrI5StHrhDTYVEjK/Kw=
|
|
||||||
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
|
|
||||||
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
|
|
||||||
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=
|
||||||
@@ -103,8 +69,8 @@ golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
|||||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||||
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||||
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
|
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
|
||||||
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
|
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
@@ -120,14 +86,13 @@ 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=
|
||||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
|
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
|
||||||
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||||
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
|
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
@@ -145,8 +110,9 @@ golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
|||||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
|
||||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||||
|
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
|
||||||
|
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
|
|||||||
224
main.go
224
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/mailgun/raymond/v2"
|
|
||||||
"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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// 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,110 @@ 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 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[0:len(name)-1])
|
||||||
|
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[0:len(name)-1])
|
||||||
|
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, "."):
|
||||||
|
return pages.HandleMedia(w, r)
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
|
|||||||
10
package.json
10
package.json
@@ -1,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tailwindcss/cli": "^4.0.17",
|
"@tailwindcss/cli": "^4.1.5",
|
||||||
"tailwindcss": "^4.0.17"
|
"tailwindcss": "^4.1.5"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "tailwindcss -i static/tailwind.css -o static/app.css",
|
"build": "tailwindcss -i static/tailwind.css -o static/app.css",
|
||||||
"watch": "tailwindcss -i static/tailwind.css -o static/app.css --watch"
|
"watch": "tailwindcss -i static/tailwind.css -o static/app.css --watch"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,47 +1,61 @@
|
|||||||
package pages
|
package pages
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io"
|
||||||
|
"mime"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
|
||||||
"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") {
|
splitName := strings.SplitN(r.PathValue("component"), ".", 2)
|
||||||
return handleMedia(c, "https://i.stack.imgur.com/"+strings.ReplaceAll(c.Params("baseName"), "stack/", "")+"."+c.Params("extension"))
|
baseName, extension := splitName[0], splitName[1]
|
||||||
|
if strings.HasPrefix(r.URL.Path, "/stack") {
|
||||||
|
return handleMedia(w, r, "https://i.stack.imgur.com/"+strings.ReplaceAll(baseName, "stack/", "")+"."+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)
|
||||||
|
if !utils.Config.RestrictiveCORS {
|
||||||
|
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||||
|
}
|
||||||
|
path := r.URL.Path
|
||||||
|
|
||||||
if os.Getenv("FORCE_WEBP") == "1" && c.Query("no_webp") == "" && c.Accepts("image/webp") == "image/webp" && !strings.HasPrefix(c.Path(), "/stack") {
|
if utils.Config.ForceWebp &&
|
||||||
|
!strings.HasSuffix(path, ".webp") &&
|
||||||
|
r.Header.Get("Sec-Fetch-Dest") == "image" &&
|
||||||
|
r.URL.Query().Get("no_webp") == "" &&
|
||||||
|
utils.Accepts(r, "image/webp") &&
|
||||||
|
!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(path, "/")
|
||||||
|
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)
|
||||||
@@ -51,8 +65,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)
|
||||||
@@ -61,17 +76,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,
|
||||||
})
|
})
|
||||||
|
|||||||
65
pages/rss.go
65
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":
|
||||||
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,
|
||||||
|
|||||||
154
pnpm-lock.yaml
generated
154
pnpm-lock.yaml
generated
@@ -9,11 +9,11 @@ importers:
|
|||||||
.:
|
.:
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@tailwindcss/cli':
|
'@tailwindcss/cli':
|
||||||
specifier: ^4.0.17
|
specifier: ^4.1.5
|
||||||
version: 4.0.17
|
version: 4.1.5
|
||||||
tailwindcss:
|
tailwindcss:
|
||||||
specifier: ^4.0.17
|
specifier: ^4.1.5
|
||||||
version: 4.0.17
|
version: 4.1.5
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
|
|
||||||
@@ -99,81 +99,93 @@ packages:
|
|||||||
resolution: {integrity: sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==}
|
resolution: {integrity: sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==}
|
||||||
engines: {node: '>= 10.0.0'}
|
engines: {node: '>= 10.0.0'}
|
||||||
|
|
||||||
'@tailwindcss/cli@4.0.17':
|
'@tailwindcss/cli@4.1.5':
|
||||||
resolution: {integrity: sha512-Jygu5jjf64vzNXeTr00OhlMzRq+/KwNxJS6eZlgcBpEbXTEmmlr/PSjv1Q9Lk3aTnQc4yNlXkHdWPnlpF+ILUg==}
|
resolution: {integrity: sha512-Kr567rDwDjY1VUnfqh5/+DCpRf4B8lPs5O9flP4kri7n4AM2aubrIxGSh5GN8s+awUKw/U4+6kNlEnZbBNfUeg==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
'@tailwindcss/node@4.0.17':
|
'@tailwindcss/node@4.1.5':
|
||||||
resolution: {integrity: sha512-LIdNwcqyY7578VpofXyqjH6f+3fP4nrz7FBLki5HpzqjYfXdF2m/eW18ZfoKePtDGg90Bvvfpov9d2gy5XVCbg==}
|
resolution: {integrity: sha512-CBhSWo0vLnWhXIvpD0qsPephiaUYfHUX3U9anwDaHZAeuGpTiB3XmsxPAN6qX7bFhipyGBqOa1QYQVVhkOUGxg==}
|
||||||
|
|
||||||
'@tailwindcss/oxide-android-arm64@4.0.17':
|
'@tailwindcss/oxide-android-arm64@4.1.5':
|
||||||
resolution: {integrity: sha512-3RfO0ZK64WAhop+EbHeyxGThyDr/fYhxPzDbEQjD2+v7ZhKTb2svTWy+KK+J1PHATus2/CQGAGp7pHY/8M8ugg==}
|
resolution: {integrity: sha512-LVvM0GirXHED02j7hSECm8l9GGJ1RfgpWCW+DRn5TvSaxVsv28gRtoL4aWKGnXqwvI3zu1GABeDNDVZeDPOQrw==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [android]
|
os: [android]
|
||||||
|
|
||||||
'@tailwindcss/oxide-darwin-arm64@4.0.17':
|
'@tailwindcss/oxide-darwin-arm64@4.1.5':
|
||||||
resolution: {integrity: sha512-e1uayxFQCCDuzTk9s8q7MC5jFN42IY7nzcr5n0Mw/AcUHwD6JaBkXnATkD924ZsHyPDvddnusIEvkgLd2CiREg==}
|
resolution: {integrity: sha512-//TfCA3pNrgnw4rRJOqavW7XUk8gsg9ddi8cwcsWXp99tzdBAZW0WXrD8wDyNbqjW316Pk2hiN/NJx/KWHl8oA==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [darwin]
|
os: [darwin]
|
||||||
|
|
||||||
'@tailwindcss/oxide-darwin-x64@4.0.17':
|
'@tailwindcss/oxide-darwin-x64@4.1.5':
|
||||||
resolution: {integrity: sha512-d6z7HSdOKfXQ0HPlVx1jduUf/YtBuCCtEDIEFeBCzgRRtDsUuRtofPqxIVaSCUTOk5+OfRLonje6n9dF6AH8wQ==}
|
resolution: {integrity: sha512-XQorp3Q6/WzRd9OalgHgaqgEbjP3qjHrlSUb5k1EuS1Z9NE9+BbzSORraO+ecW432cbCN7RVGGL/lSnHxcd+7Q==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [darwin]
|
os: [darwin]
|
||||||
|
|
||||||
'@tailwindcss/oxide-freebsd-x64@4.0.17':
|
'@tailwindcss/oxide-freebsd-x64@4.1.5':
|
||||||
resolution: {integrity: sha512-EjrVa6lx3wzXz3l5MsdOGtYIsRjgs5Mru6lDv4RuiXpguWeOb3UzGJ7vw7PEzcFadKNvNslEQqoAABeMezprxQ==}
|
resolution: {integrity: sha512-bPrLWbxo8gAo97ZmrCbOdtlz/Dkuy8NK97aFbVpkJ2nJ2Jo/rsCbu0TlGx8joCuA3q6vMWTSn01JY46iwG+clg==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [freebsd]
|
os: [freebsd]
|
||||||
|
|
||||||
'@tailwindcss/oxide-linux-arm-gnueabihf@4.0.17':
|
'@tailwindcss/oxide-linux-arm-gnueabihf@4.1.5':
|
||||||
resolution: {integrity: sha512-65zXfCOdi8wuaY0Ye6qMR5LAXokHYtrGvo9t/NmxvSZtCCitXV/gzJ/WP5ksXPhff1SV5rov0S+ZIZU+/4eyCQ==}
|
resolution: {integrity: sha512-1gtQJY9JzMAhgAfvd/ZaVOjh/Ju/nCoAsvOVJenWZfs05wb8zq+GOTnZALWGqKIYEtyNpCzvMk+ocGpxwdvaVg==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [arm]
|
cpu: [arm]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
|
||||||
'@tailwindcss/oxide-linux-arm64-gnu@4.0.17':
|
'@tailwindcss/oxide-linux-arm64-gnu@4.1.5':
|
||||||
resolution: {integrity: sha512-+aaq6hJ8ioTdbJV5IA1WjWgLmun4T7eYLTvJIToiXLHy5JzUERRbIZjAcjgK9qXMwnvuu7rqpxzej+hGoEcG5g==}
|
resolution: {integrity: sha512-dtlaHU2v7MtdxBXoqhxwsWjav7oim7Whc6S9wq/i/uUMTWAzq/gijq1InSgn2yTnh43kR+SFvcSyEF0GCNu1PQ==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
|
||||||
'@tailwindcss/oxide-linux-arm64-musl@4.0.17':
|
'@tailwindcss/oxide-linux-arm64-musl@4.1.5':
|
||||||
resolution: {integrity: sha512-/FhWgZCdUGAeYHYnZKekiOC0aXFiBIoNCA0bwzkICiMYS5Rtx2KxFfMUXQVnl4uZRblG5ypt5vpPhVaXgGk80w==}
|
resolution: {integrity: sha512-fg0F6nAeYcJ3CriqDT1iVrqALMwD37+sLzXs8Rjy8Z1ZHshJoYceodfyUwGJEsQoTyWbliFNRs2wMQNXtT7MVA==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
|
||||||
'@tailwindcss/oxide-linux-x64-gnu@4.0.17':
|
'@tailwindcss/oxide-linux-x64-gnu@4.1.5':
|
||||||
resolution: {integrity: sha512-gELJzOHK6GDoIpm/539Golvk+QWZjxQcbkKq9eB2kzNkOvrP0xc5UPgO9bIMNt1M48mO8ZeNenCMGt6tfkvVBg==}
|
resolution: {integrity: sha512-SO+F2YEIAHa1AITwc8oPwMOWhgorPzzcbhWEb+4oLi953h45FklDmM8dPSZ7hNHpIk9p/SCZKUYn35t5fjGtHA==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
|
||||||
'@tailwindcss/oxide-linux-x64-musl@4.0.17':
|
'@tailwindcss/oxide-linux-x64-musl@4.1.5':
|
||||||
resolution: {integrity: sha512-68NwxcJrZn94IOW4TysMIbYv5AlM6So1luTlbYUDIGnKma1yTFGBRNEJ+SacJ3PZE2rgcTBNRHX1TB4EQ/XEHw==}
|
resolution: {integrity: sha512-6UbBBplywkk/R+PqqioskUeXfKcBht3KU7juTi1UszJLx0KPXUo10v2Ok04iBJIaDPkIFkUOVboXms5Yxvaz+g==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
|
||||||
'@tailwindcss/oxide-win32-arm64-msvc@4.0.17':
|
'@tailwindcss/oxide-wasm32-wasi@4.1.5':
|
||||||
resolution: {integrity: sha512-AkBO8efP2/7wkEXkNlXzRD4f/7WerqKHlc6PWb5v0jGbbm22DFBLbIM19IJQ3b+tNewQZa+WnPOaGm0SmwMNjw==}
|
resolution: {integrity: sha512-hwALf2K9FHuiXTPqmo1KeOb83fTRNbe9r/Ixv9ZNQ/R24yw8Ge1HOWDDgTdtzntIaIUJG5dfXCf4g9AD4RiyhQ==}
|
||||||
|
engines: {node: '>=14.0.0'}
|
||||||
|
cpu: [wasm32]
|
||||||
|
bundledDependencies:
|
||||||
|
- '@napi-rs/wasm-runtime'
|
||||||
|
- '@emnapi/core'
|
||||||
|
- '@emnapi/runtime'
|
||||||
|
- '@tybys/wasm-util'
|
||||||
|
- '@emnapi/wasi-threads'
|
||||||
|
- tslib
|
||||||
|
|
||||||
|
'@tailwindcss/oxide-win32-arm64-msvc@4.1.5':
|
||||||
|
resolution: {integrity: sha512-oDKncffWzaovJbkuR7/OTNFRJQVdiw/n8HnzaCItrNQUeQgjy7oUiYpsm9HUBgpmvmDpSSbGaCa2Evzvk3eFmA==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [win32]
|
os: [win32]
|
||||||
|
|
||||||
'@tailwindcss/oxide-win32-x64-msvc@4.0.17':
|
'@tailwindcss/oxide-win32-x64-msvc@4.1.5':
|
||||||
resolution: {integrity: sha512-7/DTEvXcoWlqX0dAlcN0zlmcEu9xSermuo7VNGX9tJ3nYMdo735SHvbrHDln1+LYfF6NhJ3hjbpbjkMOAGmkDg==}
|
resolution: {integrity: sha512-WiR4dtyrFdbb+ov0LK+7XsFOsG+0xs0PKZKkt41KDn9jYpO7baE3bXiudPVkTqUEwNfiglCygQHl2jklvSBi7Q==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [win32]
|
os: [win32]
|
||||||
|
|
||||||
'@tailwindcss/oxide@4.0.17':
|
'@tailwindcss/oxide@4.1.5':
|
||||||
resolution: {integrity: sha512-B4OaUIRD2uVrULpAD1Yksx2+wNarQr2rQh65nXqaqbLY1jCd8fO+3KLh/+TH4Hzh2NTHQvgxVbPdUDOtLk7vAw==}
|
resolution: {integrity: sha512-1n4br1znquEvyW/QuqMKQZlBen+jxAbvyduU87RS8R3tUSvByAkcaMTkJepNIrTlYhD+U25K4iiCIxE6BGdRYA==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
|
|
||||||
braces@3.0.3:
|
braces@3.0.3:
|
||||||
@@ -185,8 +197,8 @@ packages:
|
|||||||
engines: {node: '>=0.10'}
|
engines: {node: '>=0.10'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
detect-libc@2.0.3:
|
detect-libc@2.0.4:
|
||||||
resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==}
|
resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
enhanced-resolve@5.18.1:
|
enhanced-resolve@5.18.1:
|
||||||
@@ -298,8 +310,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
|
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
|
||||||
engines: {node: '>=8.6'}
|
engines: {node: '>=8.6'}
|
||||||
|
|
||||||
tailwindcss@4.0.17:
|
tailwindcss@4.1.5:
|
||||||
resolution: {integrity: sha512-OErSiGzRa6rLiOvaipsDZvLMSpsBZ4ysB4f0VKGXUrjw2jfkJRd6kjRKV2+ZmTCNvwtvgdDam5D7w6WXsdLJZw==}
|
resolution: {integrity: sha512-nYtSPfWGDiWgCkwQG/m+aX83XCwf62sBgg3bIlNiiOcggnS1x3uVRDAuyelBFL+vJdOPPCGElxv9DjHJjRHiVA==}
|
||||||
|
|
||||||
tapable@2.2.1:
|
tapable@2.2.1:
|
||||||
resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==}
|
resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==}
|
||||||
@@ -371,69 +383,73 @@ snapshots:
|
|||||||
'@parcel/watcher-win32-ia32': 2.5.1
|
'@parcel/watcher-win32-ia32': 2.5.1
|
||||||
'@parcel/watcher-win32-x64': 2.5.1
|
'@parcel/watcher-win32-x64': 2.5.1
|
||||||
|
|
||||||
'@tailwindcss/cli@4.0.17':
|
'@tailwindcss/cli@4.1.5':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@parcel/watcher': 2.5.1
|
'@parcel/watcher': 2.5.1
|
||||||
'@tailwindcss/node': 4.0.17
|
'@tailwindcss/node': 4.1.5
|
||||||
'@tailwindcss/oxide': 4.0.17
|
'@tailwindcss/oxide': 4.1.5
|
||||||
enhanced-resolve: 5.18.1
|
enhanced-resolve: 5.18.1
|
||||||
lightningcss: 1.29.2
|
|
||||||
mri: 1.2.0
|
mri: 1.2.0
|
||||||
picocolors: 1.1.1
|
picocolors: 1.1.1
|
||||||
tailwindcss: 4.0.17
|
tailwindcss: 4.1.5
|
||||||
|
|
||||||
'@tailwindcss/node@4.0.17':
|
'@tailwindcss/node@4.1.5':
|
||||||
dependencies:
|
dependencies:
|
||||||
enhanced-resolve: 5.18.1
|
enhanced-resolve: 5.18.1
|
||||||
jiti: 2.4.2
|
jiti: 2.4.2
|
||||||
tailwindcss: 4.0.17
|
lightningcss: 1.29.2
|
||||||
|
tailwindcss: 4.1.5
|
||||||
|
|
||||||
'@tailwindcss/oxide-android-arm64@4.0.17':
|
'@tailwindcss/oxide-android-arm64@4.1.5':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@tailwindcss/oxide-darwin-arm64@4.0.17':
|
'@tailwindcss/oxide-darwin-arm64@4.1.5':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@tailwindcss/oxide-darwin-x64@4.0.17':
|
'@tailwindcss/oxide-darwin-x64@4.1.5':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@tailwindcss/oxide-freebsd-x64@4.0.17':
|
'@tailwindcss/oxide-freebsd-x64@4.1.5':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@tailwindcss/oxide-linux-arm-gnueabihf@4.0.17':
|
'@tailwindcss/oxide-linux-arm-gnueabihf@4.1.5':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@tailwindcss/oxide-linux-arm64-gnu@4.0.17':
|
'@tailwindcss/oxide-linux-arm64-gnu@4.1.5':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@tailwindcss/oxide-linux-arm64-musl@4.0.17':
|
'@tailwindcss/oxide-linux-arm64-musl@4.1.5':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@tailwindcss/oxide-linux-x64-gnu@4.0.17':
|
'@tailwindcss/oxide-linux-x64-gnu@4.1.5':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@tailwindcss/oxide-linux-x64-musl@4.0.17':
|
'@tailwindcss/oxide-linux-x64-musl@4.1.5':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@tailwindcss/oxide-win32-arm64-msvc@4.0.17':
|
'@tailwindcss/oxide-wasm32-wasi@4.1.5':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@tailwindcss/oxide-win32-x64-msvc@4.0.17':
|
'@tailwindcss/oxide-win32-arm64-msvc@4.1.5':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@tailwindcss/oxide@4.0.17':
|
'@tailwindcss/oxide-win32-x64-msvc@4.1.5':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@tailwindcss/oxide@4.1.5':
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@tailwindcss/oxide-android-arm64': 4.0.17
|
'@tailwindcss/oxide-android-arm64': 4.1.5
|
||||||
'@tailwindcss/oxide-darwin-arm64': 4.0.17
|
'@tailwindcss/oxide-darwin-arm64': 4.1.5
|
||||||
'@tailwindcss/oxide-darwin-x64': 4.0.17
|
'@tailwindcss/oxide-darwin-x64': 4.1.5
|
||||||
'@tailwindcss/oxide-freebsd-x64': 4.0.17
|
'@tailwindcss/oxide-freebsd-x64': 4.1.5
|
||||||
'@tailwindcss/oxide-linux-arm-gnueabihf': 4.0.17
|
'@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.5
|
||||||
'@tailwindcss/oxide-linux-arm64-gnu': 4.0.17
|
'@tailwindcss/oxide-linux-arm64-gnu': 4.1.5
|
||||||
'@tailwindcss/oxide-linux-arm64-musl': 4.0.17
|
'@tailwindcss/oxide-linux-arm64-musl': 4.1.5
|
||||||
'@tailwindcss/oxide-linux-x64-gnu': 4.0.17
|
'@tailwindcss/oxide-linux-x64-gnu': 4.1.5
|
||||||
'@tailwindcss/oxide-linux-x64-musl': 4.0.17
|
'@tailwindcss/oxide-linux-x64-musl': 4.1.5
|
||||||
'@tailwindcss/oxide-win32-arm64-msvc': 4.0.17
|
'@tailwindcss/oxide-wasm32-wasi': 4.1.5
|
||||||
'@tailwindcss/oxide-win32-x64-msvc': 4.0.17
|
'@tailwindcss/oxide-win32-arm64-msvc': 4.1.5
|
||||||
|
'@tailwindcss/oxide-win32-x64-msvc': 4.1.5
|
||||||
|
|
||||||
braces@3.0.3:
|
braces@3.0.3:
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -441,7 +457,7 @@ snapshots:
|
|||||||
|
|
||||||
detect-libc@1.0.3: {}
|
detect-libc@1.0.3: {}
|
||||||
|
|
||||||
detect-libc@2.0.3: {}
|
detect-libc@2.0.4: {}
|
||||||
|
|
||||||
enhanced-resolve@5.18.1:
|
enhanced-resolve@5.18.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -496,7 +512,7 @@ snapshots:
|
|||||||
|
|
||||||
lightningcss@1.29.2:
|
lightningcss@1.29.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
detect-libc: 2.0.3
|
detect-libc: 2.0.4
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
lightningcss-darwin-arm64: 1.29.2
|
lightningcss-darwin-arm64: 1.29.2
|
||||||
lightningcss-darwin-x64: 1.29.2
|
lightningcss-darwin-x64: 1.29.2
|
||||||
@@ -522,7 +538,7 @@ snapshots:
|
|||||||
|
|
||||||
picomatch@2.3.1: {}
|
picomatch@2.3.1: {}
|
||||||
|
|
||||||
tailwindcss@4.0.17: {}
|
tailwindcss@4.1.5: {}
|
||||||
|
|
||||||
tapable@2.2.1: {}
|
tapable@2.2.1: {}
|
||||||
|
|
||||||
|
|||||||
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
|
||||||
|
}
|
||||||
@@ -6,62 +6,53 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type config struct {
|
type config struct {
|
||||||
Port string
|
Port string
|
||||||
Addr string
|
Addr string
|
||||||
ImgurId string
|
ImgurId string
|
||||||
Secure bool
|
ProtocolDetection bool
|
||||||
FiberPrefork bool
|
Secure bool
|
||||||
ImageCache bool
|
ForceWebp bool
|
||||||
CleanupInterval time.Duration
|
RestrictiveCORS bool
|
||||||
CacheDir string
|
ImageCache bool
|
||||||
Privacy map[string]interface{}
|
CleanupInterval time.Duration
|
||||||
|
CacheDir string
|
||||||
|
Privacy map[string]interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
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"),
|
||||||
Secure: os.Getenv("SECURE") == "true",
|
ProtocolDetection: envBool("PROTOCOL_DETECTION"),
|
||||||
FiberPrefork: os.Getenv("FIBER_PREFORK") == "true",
|
Secure: envBool("SECURE"),
|
||||||
|
ForceWebp: envBool("FORCE_WEBP"),
|
||||||
|
RestrictiveCORS: envBool("RESTRICTIVE_CORS"),
|
||||||
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",
|
"cloudflare": envBool("PRIVACY_CLOUDFLARE"),
|
||||||
"not_collected": os.Getenv("PRIVACY_NOT_COLLECTED") == "true",
|
"not_collected": envBool("PRIVACY_NOT_COLLECTED"),
|
||||||
"ip": os.Getenv("PRIVACY_IP") == "true",
|
"ip": envBool("PRIVACY_IP"),
|
||||||
"url": os.Getenv("PRIVACY_URL") == "true",
|
"url": envBool("PRIVACY_URL"),
|
||||||
"device": os.Getenv("PRIVACY_DEVICE") == "true",
|
"device": envBool("PRIVACY_DEVICE"),
|
||||||
"diagnostics": os.Getenv("PRIVACY_DIAGNOSTICS") == "true",
|
"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,11 +1,21 @@
|
|||||||
package utils
|
package utils
|
||||||
|
|
||||||
import "github.com/gofiber/fiber/v2"
|
import "net/http"
|
||||||
|
|
||||||
func GetInstanceUrl(c *fiber.Ctx) string {
|
func GetInstanceProtocol(r *http.Request) string {
|
||||||
proto := "https://"
|
proto := "https"
|
||||||
if !Config.Secure {
|
if !Config.Secure {
|
||||||
proto = "http://"
|
proto = "http"
|
||||||
}
|
}
|
||||||
return proto + c.Hostname()
|
if Config.ProtocolDetection {
|
||||||
|
xproto := r.Header.Get("X-Forwarded-Proto")
|
||||||
|
if xproto != "" {
|
||||||
|
proto = xproto
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return proto
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetInstanceUrl(r *http.Request) string {
|
||||||
|
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")
|
||||||
}
|
}
|
||||||
|
|||||||
16
utils/splitNameExt.go
Normal file
16
utils/splitNameExt.go
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
func SplitNameExt(path string) (name, ext string) {
|
||||||
|
name, ext = path, ""
|
||||||
|
for range 5 {
|
||||||
|
if len(name) == 0 || name[len(name)-1] == '.' || name[len(name)-1] == '/' {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
name = name[:len(name)-1]
|
||||||
|
ext = path[len(name):]
|
||||||
|
}
|
||||||
|
if len(name) == 0 || name[len(name)-1] != '.' {
|
||||||
|
return path, ""
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -7,4 +7,4 @@ var files embed.FS
|
|||||||
|
|
||||||
func GetFiles() embed.FS {
|
func GetFiles() embed.FS {
|
||||||
return files
|
return files
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user