diff --git a/.env.example b/.env.example
index 6b60415..f12db21 100644
--- a/.env.example
+++ b/.env.example
@@ -3,6 +3,11 @@ PORT=3000
FIBER_PREFORK=false
IMGUR_CLIENT_ID=546c25a59c58ad7
+# Create IMAGE_CACHE_DIR before enabling image caching
+IMAGE_CACHE=false
+IMAGE_CACHE_DIR=/var/cache/rimgo
+IMAGE_CACHE_CLEANUP_INTERVAL=24h
+
# Instance privacy
# For more information, see https://codeberg.org/librarian/librarian/wiki/Instance-privacy
# Required to be on the instance list.
diff --git a/api/album.go b/api/album.go
index 18903e2..eca7ab3 100644
--- a/api/album.go
+++ b/api/album.go
@@ -42,7 +42,7 @@ func FetchAlbum(albumID string) (Album, error) {
return cacheData.(Album), nil
}
- data, err := utils.GetJSON("https://api.imgur.com/post/v1/albums/" + albumID + "?client_id=" + utils.Config["imgurId"].(string) + "&include=media%2Caccount")
+ data, err := utils.GetJSON("https://api.imgur.com/post/v1/albums/" + albumID + "?client_id=" + utils.Config.ImgurId + "&include=media%2Caccount")
if err != nil {
return Album{}, err
}
@@ -62,7 +62,7 @@ func FetchPosts(albumID string) (Album, error) {
return cacheData.(Album), nil
}
- data, err := utils.GetJSON("https://api.imgur.com/post/v1/posts/" + albumID + "?client_id=" + utils.Config["imgurId"].(string) + "&include=media%2Caccount%2Ctags")
+ data, err := utils.GetJSON("https://api.imgur.com/post/v1/posts/" + albumID + "?client_id=" + utils.Config.ImgurId + "&include=media%2Caccount%2Ctags")
if err != nil {
return Album{}, err
}
@@ -82,7 +82,7 @@ func FetchMedia(mediaID string) (Album, error) {
return cacheData.(Album), nil
}
- data, err := utils.GetJSON("https://api.imgur.com/post/v1/media/" + mediaID + "?client_id=" + utils.Config["imgurId"].(string) + "&include=media%2Caccount")
+ data, err := utils.GetJSON("https://api.imgur.com/post/v1/media/" + mediaID + "?client_id=" + utils.Config.ImgurId + "&include=media%2Caccount")
if err != nil {
return Album{}, err
}
diff --git a/api/comments.go b/api/comments.go
index f6263c4..cf5c785 100644
--- a/api/comments.go
+++ b/api/comments.go
@@ -33,7 +33,7 @@ func FetchComments(galleryID string) ([]Comment, error) {
return cacheData.([]Comment), nil
}
- data, err := utils.GetJSON("https://api.imgur.com/comment/v1/comments?client_id=" + utils.Config["imgurId"].(string) + "&filter[post]=eq:" + galleryID + "&include=account,adconfig&per_page=30&sort=best")
+ data, err := utils.GetJSON("https://api.imgur.com/comment/v1/comments?client_id=" + utils.Config.ImgurId + "&filter[post]=eq:" + galleryID + "&include=account,adconfig&per_page=30&sort=best")
if err != nil {
return []Comment{}, nil
}
diff --git a/api/tag.go b/api/tag.go
index 0e80148..b9b8f6f 100644
--- a/api/tag.go
+++ b/api/tag.go
@@ -36,7 +36,7 @@ func FetchTag(tag string, sort string, page string) (Tag, error) {
}
q := req.URL.Query()
- q.Add("client_id", utils.Config["imgurId"].(string))
+ q.Add("client_id", utils.Config.ImgurId)
q.Add("include", "cover")
q.Add("page", page)
diff --git a/api/user.go b/api/user.go
index 895ea78..979e314 100644
--- a/api/user.go
+++ b/api/user.go
@@ -43,7 +43,7 @@ func FetchUser(username string) (User, error) {
return cacheData.(User), nil
}
- res, err := http.Get("https://api.imgur.com/account/v1/accounts/" + username + "?client_id=" + utils.Config["imgurId"].(string))
+ res, err := http.Get("https://api.imgur.com/account/v1/accounts/" + username + "?client_id=" + utils.Config.ImgurId)
if err != nil {
return User{}, err
}
@@ -77,7 +77,7 @@ func FetchSubmissions(username string, sort string, page string) ([]Submission,
return cacheData.([]Submission), nil
}
- data, err := utils.GetJSON("https://api.imgur.com/3/account/" + username + "/submissions/" + page + "/" + sort + "?album_previews=1&client_id=" + utils.Config["imgurId"].(string))
+ data, err := utils.GetJSON("https://api.imgur.com/3/account/" + username + "/submissions/" + page + "/" + sort + "?album_previews=1&client_id=" + utils.Config.ImgurId)
if err != nil {
return []Submission{}, err
}
diff --git a/main.go b/main.go
index 8860b2f..f6bf3c6 100644
--- a/main.go
+++ b/main.go
@@ -3,7 +3,11 @@ package main
import (
"flag"
"fmt"
+ "log"
"net/http"
+ "os"
+ "path/filepath"
+ "time"
"codeberg.org/video-prize-ranch/rimgo/pages"
"codeberg.org/video-prize-ranch/rimgo/static"
@@ -24,10 +28,22 @@ func main() {
}
utils.LoadConfig()
+ if utils.Config.ImageCache {
+ go func() {
+ for range time.Tick(utils.Config.CleanupInterval) {
+ log.Println("Cache cleaned")
+ files, _ := filepath.Glob(filepath.Join(utils.Config.CacheDir, "*"))
+ for _, file := range files {
+ os.RemoveAll(file)
+ }
+ }
+ }()
+ }
+
engine := handlebars.NewFileSystem(http.FS(views.GetFiles()), ".hbs")
app := fiber.New(fiber.Config{
Views: engine,
- Prefork: utils.Config["fiberPrefork"].(bool),
+ Prefork: utils.Config.FiberPrefork,
UnescapePath: true,
StreamRequestBody: true,
ErrorHandler: func(ctx *fiber.Ctx, err error) error {
@@ -90,5 +106,5 @@ func main() {
app.Get("/gallery/:postID", pages.HandlePost)
app.Get("/gallery/:postID/embed", pages.HandleEmbed)
- app.Listen(utils.Config["addr"].(string) + ":" + utils.Config["port"].(string))
+ app.Listen(utils.Config.Addr + ":" + utils.Config.Port)
}
diff --git a/pages/media.go b/pages/media.go
index c43b971..4a46cab 100644
--- a/pages/media.go
+++ b/pages/media.go
@@ -1,6 +1,9 @@
package pages
import (
+ "crypto/sha256"
+ "encoding/hex"
+ "io"
"net/http"
"os"
"strings"
@@ -37,6 +40,19 @@ func handleMedia(c *fiber.Ctx, url string) error {
url = strings.ReplaceAll(url, ".jpeg", ".webp")
}
+ optionsHash := ""
+ if utils.Config.ImageCache {
+ hasher := sha256.New()
+ hasher.Write([]byte(url))
+ optionsHash = hex.EncodeToString(hasher.Sum(nil))
+
+ image, err := os.ReadFile(utils.Config.CacheDir + "/" + optionsHash)
+ if err == nil {
+ _, err := c.Write(image)
+ return err
+ }
+ }
+
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return err
@@ -63,5 +79,20 @@ func handleMedia(c *fiber.Ctx, url string) error {
c.Set("Content-Range", res.Header.Get("Content-Range"))
}
- return c.SendStream(res.Body)
+ if strings.HasPrefix(res.Header.Get("Content-Type"), "image/") && utils.Config.ImageCache && res.StatusCode == 200 {
+ data, err := io.ReadAll(res.Body)
+ if err != nil {
+ return err
+ }
+
+ err = os.WriteFile(utils.Config.CacheDir + "/" + optionsHash, data, 0644)
+ if err != nil {
+ return err
+ }
+
+ _, err = c.Write(data)
+ return err
+ } else {
+ return c.SendStream(res.Body)
+ }
}
\ No newline at end of file
diff --git a/utils/config.go b/utils/config.go
index e2d7d59..98c14d1 100644
--- a/utils/config.go
+++ b/utils/config.go
@@ -1,8 +1,23 @@
package utils
-import "os"
+import (
+ "log"
+ "os"
+ "time"
+)
-var Config map[string]interface{}
+type config struct {
+ Port string
+ Addr string
+ ImgurId string
+ FiberPrefork bool
+ ImageCache bool
+ CleanupInterval time.Duration
+ CacheDir string
+ Privacy map[string]interface{}
+}
+
+var Config config
func LoadConfig() {
port := "3000"
@@ -13,11 +28,6 @@ func LoadConfig() {
port = os.Getenv("RIMGU_PORT")
}
- fiberPrefork := false
- if os.Getenv("FIBER_PREFORK") == "true" {
- fiberPrefork = true
- }
-
addr := "0.0.0.0"
if os.Getenv("ADDRESS") != "" {
addr = os.Getenv("ADDRESS")
@@ -34,14 +44,29 @@ func LoadConfig() {
imgurId = os.Getenv("RIMGU_IMGUR_CLIENT_ID")
}
- Config = map[string]interface{}{
- "port": port,
- "addr": addr,
- "imgurId": imgurId,
- "fiberPrefork": fiberPrefork,
- "privacy": map[string]interface{}{
- "set": os.Getenv("PRIVACY_NOT_COLLECTED") != "",
- "policy": os.Getenv("PRIVACY_POLICY"),
+ imageCache := os.Getenv("IMAGE_CACHE") == "true"
+
+ cleanupInterval, err := time.ParseDuration(os.Getenv("IMAGE_CACHE_CLEANUP_INTERVAL"))
+ if err != nil && imageCache {
+ log.Fatal("invalid configuration: invalid duration for IMAGE_CACHE_CLEANUP_INTERVAL")
+ }
+
+ cacheDir := os.Getenv("IMAGE_CACHE_DIR")
+ if cacheDir == "" && imageCache {
+ log.Fatal("invalid configuration: no IMAGE_CACHE_DIR")
+ }
+
+ Config = config{
+ Port: port,
+ Addr: addr,
+ ImgurId: imgurId,
+ FiberPrefork: os.Getenv("FIBER_PREFORK") == "true",
+ ImageCache: imageCache,
+ CleanupInterval: cleanupInterval,
+ CacheDir: cacheDir,
+ Privacy: map[string]interface{}{
+ "set": os.Getenv("PRIVACY_NOT_COLLECTED") != "",
+ "policy": os.Getenv("PRIVACY_POLICY"),
"message": os.Getenv("PRIVACY_MESSAGE"),
"country": os.Getenv("PRIVACY_COUNTRY"),
"provider": os.Getenv("PRIVACY_PROVIDER"),
diff --git a/views/privacy.hbs b/views/privacy.hbs
index 3538ea0..cb45faf 100644
--- a/views/privacy.hbs
+++ b/views/privacy.hbs
@@ -14,18 +14,18 @@
Instance Privacy
- {{#if config.privacy.policy}}
+ {{#if config.Privacy.policy}}
The instance operator has indicated their instance's privacy practices below. For more information, see the
- instance operator's privacy policy.
+ instance operator's privacy policy.
{{else}}
The instance operator has indicated their instance's privacy practices below.
{{/if}}
- {{#if config.privacy.message}}
- {{{config.privacy.message}}}
+ {{#if config.Privacy.message}}
+ {{{config.Privacy.message}}}
{{/if}}
- {{#if config.privacy.not_collected}}
+ {{#if config.Privacy.not_collected}}
@@ -37,7 +37,7 @@
{{/if}}
- {{#unless config.privacy.set}}
+ {{#unless config.Privacy.set}}
{{else}}
- {{#unless config.privacy.not_collected}}
+ {{#unless config.Privacy.not_collected}}
@@ -58,25 +58,25 @@
Data Collected
The following data may be collected:
- {{#if config.privacy.ip}}
+ {{#if config.Privacy.ip}}
-
password
Internet address (IP Address)
{{/if}}
- {{#if config.privacy.url}}
+ {{#if config.Privacy.url}}
-
link
Page viewed (Request URL)
{{/if}}
- {{#if config.privacy.device}}
+ {{#if config.Privacy.device}}
-
phonelink
Device Type (User agent)
{{/if}}
- {{#if config.privacy.diagnostics}}
+ {{#if config.Privacy.diagnostics}}
-
settings
Diagnostics
@@ -119,9 +119,9 @@
Additional information
- Version: {{version}}
- - Country: {{config.privacy.country}}
- - Provider: {{config.privacy.provider}}
- {{#if config.privacy.cloudflare}}
+ - Country: {{config.Privacy.country}}
+ - Provider: {{config.Privacy.provider}}
+ {{#if config.Privacy.cloudflare}}
- Using Cloudflare?: Yes
{{else}}
- Using Cloudflare?: No