Add image cache and use struct for config (closes #58)

This commit is contained in:
video-prize-ranch 2022-09-30 16:57:18 -04:00
parent f1c058cbfc
commit 4306b3bb81
No known key found for this signature in database
GPG Key ID: D8EAA4C5B12A7281
9 changed files with 116 additions and 39 deletions

View File

@ -3,6 +3,11 @@ PORT=3000
FIBER_PREFORK=false FIBER_PREFORK=false
IMGUR_CLIENT_ID=546c25a59c58ad7 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 # Instance privacy
# For more information, see https://codeberg.org/librarian/librarian/wiki/Instance-privacy # For more information, see https://codeberg.org/librarian/librarian/wiki/Instance-privacy
# Required to be on the instance list. # Required to be on the instance list.

View File

@ -42,7 +42,7 @@ func FetchAlbum(albumID string) (Album, error) {
return cacheData.(Album), nil 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 { if err != nil {
return Album{}, err return Album{}, err
} }
@ -62,7 +62,7 @@ func FetchPosts(albumID string) (Album, error) {
return cacheData.(Album), nil 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 { if err != nil {
return Album{}, err return Album{}, err
} }
@ -82,7 +82,7 @@ func FetchMedia(mediaID string) (Album, error) {
return cacheData.(Album), nil 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 { if err != nil {
return Album{}, err return Album{}, err
} }

View File

@ -33,7 +33,7 @@ func FetchComments(galleryID string) ([]Comment, error) {
return cacheData.([]Comment), nil 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 { if err != nil {
return []Comment{}, nil return []Comment{}, nil
} }

View File

@ -36,7 +36,7 @@ func FetchTag(tag string, sort string, page string) (Tag, error) {
} }
q := req.URL.Query() 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("include", "cover")
q.Add("page", page) q.Add("page", page)

View File

@ -43,7 +43,7 @@ func FetchUser(username string) (User, error) {
return cacheData.(User), nil 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 { if err != nil {
return User{}, err return User{}, err
} }
@ -77,7 +77,7 @@ func FetchSubmissions(username string, sort string, page string) ([]Submission,
return cacheData.([]Submission), nil 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 { if err != nil {
return []Submission{}, err return []Submission{}, err
} }

20
main.go
View File

@ -3,7 +3,11 @@ package main
import ( import (
"flag" "flag"
"fmt" "fmt"
"log"
"net/http" "net/http"
"os"
"path/filepath"
"time"
"codeberg.org/video-prize-ranch/rimgo/pages" "codeberg.org/video-prize-ranch/rimgo/pages"
"codeberg.org/video-prize-ranch/rimgo/static" "codeberg.org/video-prize-ranch/rimgo/static"
@ -24,10 +28,22 @@ func main() {
} }
utils.LoadConfig() 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") engine := handlebars.NewFileSystem(http.FS(views.GetFiles()), ".hbs")
app := fiber.New(fiber.Config{ app := fiber.New(fiber.Config{
Views: engine, Views: engine,
Prefork: utils.Config["fiberPrefork"].(bool), Prefork: utils.Config.FiberPrefork,
UnescapePath: true, UnescapePath: true,
StreamRequestBody: true, StreamRequestBody: true,
ErrorHandler: func(ctx *fiber.Ctx, err error) error { ErrorHandler: func(ctx *fiber.Ctx, err error) error {
@ -90,5 +106,5 @@ func main() {
app.Get("/gallery/:postID", pages.HandlePost) app.Get("/gallery/:postID", pages.HandlePost)
app.Get("/gallery/:postID/embed", pages.HandleEmbed) 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)
} }

View File

@ -1,6 +1,9 @@
package pages package pages
import ( import (
"crypto/sha256"
"encoding/hex"
"io"
"net/http" "net/http"
"os" "os"
"strings" "strings"
@ -37,6 +40,19 @@ func handleMedia(c *fiber.Ctx, url string) error {
url = strings.ReplaceAll(url, ".jpeg", ".webp") 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) req, err := http.NewRequest("GET", url, nil)
if err != nil { if err != nil {
return err return err
@ -63,5 +79,20 @@ func handleMedia(c *fiber.Ctx, url string) error {
c.Set("Content-Range", res.Header.Get("Content-Range")) 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)
}
} }

View File

@ -1,8 +1,23 @@
package utils 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() { func LoadConfig() {
port := "3000" port := "3000"
@ -13,11 +28,6 @@ func LoadConfig() {
port = os.Getenv("RIMGU_PORT") port = os.Getenv("RIMGU_PORT")
} }
fiberPrefork := false
if os.Getenv("FIBER_PREFORK") == "true" {
fiberPrefork = true
}
addr := "0.0.0.0" addr := "0.0.0.0"
if os.Getenv("ADDRESS") != "" { if os.Getenv("ADDRESS") != "" {
addr = os.Getenv("ADDRESS") addr = os.Getenv("ADDRESS")
@ -34,14 +44,29 @@ func LoadConfig() {
imgurId = os.Getenv("RIMGU_IMGUR_CLIENT_ID") imgurId = os.Getenv("RIMGU_IMGUR_CLIENT_ID")
} }
Config = map[string]interface{}{ imageCache := os.Getenv("IMAGE_CACHE") == "true"
"port": port,
"addr": addr, cleanupInterval, err := time.ParseDuration(os.Getenv("IMAGE_CACHE_CLEANUP_INTERVAL"))
"imgurId": imgurId, if err != nil && imageCache {
"fiberPrefork": fiberPrefork, log.Fatal("invalid configuration: invalid duration for IMAGE_CACHE_CLEANUP_INTERVAL")
"privacy": map[string]interface{}{ }
"set": os.Getenv("PRIVACY_NOT_COLLECTED") != "",
"policy": os.Getenv("PRIVACY_POLICY"), 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"), "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"),

View File

@ -14,18 +14,18 @@
<main> <main>
<h1>Instance Privacy</h1> <h1>Instance Privacy</h1>
{{#if config.privacy.policy}} {{#if config.Privacy.policy}}
<p>The instance operator has indicated their instance's privacy practices below. For more information, see the <p>The instance operator has indicated their instance's privacy practices below. For more information, see the
instance operator's <a href="{{config.privacy.policy}}">privacy policy</a>.</p> instance operator's <a href="{{config.Privacy.policy}}">privacy policy</a>.</p>
{{else}} {{else}}
<p>The instance operator has indicated their instance's privacy practices below.</p> <p>The instance operator has indicated their instance's privacy practices below.</p>
{{/if}} {{/if}}
{{#if config.privacy.message}} {{#if config.Privacy.message}}
<p>{{{config.privacy.message}}}</p> <p>{{{config.Privacy.message}}}</p>
{{/if}} {{/if}}
{{#if config.privacy.not_collected}} {{#if config.Privacy.not_collected}}
<div class="badgeWrapper"> <div class="badgeWrapper">
<div class="badge"> <div class="badge">
<span class="material-icons-outlined largeIcon"> <span class="material-icons-outlined largeIcon">
@ -37,7 +37,7 @@
</div> </div>
{{/if}} {{/if}}
{{#unless config.privacy.set}} {{#unless config.Privacy.set}}
<div class="badgeWrapper"> <div class="badgeWrapper">
<div class="badge"> <div class="badge">
<span class="material-icons-outlined largeIcon"> <span class="material-icons-outlined largeIcon">
@ -49,7 +49,7 @@
</div> </div>
</div> </div>
{{else}} {{else}}
{{#unless config.privacy.not_collected}} {{#unless config.Privacy.not_collected}}
<div class="badgeWrapper"> <div class="badgeWrapper">
<div class="badge"> <div class="badge">
<span class="material-icons-outlined largeIcon"> <span class="material-icons-outlined largeIcon">
@ -58,25 +58,25 @@
<h3>Data Collected</h3> <h3>Data Collected</h3>
<p>The following data may be collected:</p> <p>The following data may be collected:</p>
<ul> <ul>
{{#if config.privacy.ip}} {{#if config.Privacy.ip}}
<li> <li>
<span class="material-icons-outlined">password</span> <span class="material-icons-outlined">password</span>
Internet address (IP Address) Internet address (IP Address)
</li> </li>
{{/if}} {{/if}}
{{#if config.privacy.url}} {{#if config.Privacy.url}}
<li> <li>
<span class="material-icons-outlined">link</span> <span class="material-icons-outlined">link</span>
Page viewed (Request URL) Page viewed (Request URL)
</li> </li>
{{/if}} {{/if}}
{{#if config.privacy.device}} {{#if config.Privacy.device}}
<li> <li>
<span class="material-icons-outlined">phonelink</span> <span class="material-icons-outlined">phonelink</span>
Device Type (User agent) Device Type (User agent)
</li> </li>
{{/if}} {{/if}}
{{#if config.privacy.diagnostics}} {{#if config.Privacy.diagnostics}}
<li> <li>
<span class="material-icons-outlined">settings</span> <span class="material-icons-outlined">settings</span>
Diagnostics Diagnostics
@ -119,9 +119,9 @@
<h2 class="addInfo">Additional information</h2> <h2 class="addInfo">Additional information</h2>
<ul> <ul>
<li>Version: {{version}}</li> <li>Version: {{version}}</li>
<li>Country: {{config.privacy.country}}</li> <li>Country: {{config.Privacy.country}}</li>
<li>Provider: {{config.privacy.provider}}</li> <li>Provider: {{config.Privacy.provider}}</li>
{{#if config.privacy.cloudflare}} {{#if config.Privacy.cloudflare}}
<li>Using Cloudflare?: Yes</li> <li>Using Cloudflare?: Yes</li>
{{else}} {{else}}
<li>Using Cloudflare?: No</li> <li>Using Cloudflare?: No</li>