Add image cache and use struct for config (closes #58)
This commit is contained in:
parent
f1c058cbfc
commit
4306b3bb81
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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
20
main.go
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
return c.SendStream(res.Body)
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -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,12 +44,27 @@ 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{}{
|
}
|
||||||
|
|
||||||
|
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") != "",
|
"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"),
|
||||||
|
|
|
@ -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>
|
||||||
|
|
Loading…
Reference in New Issue