feat: image gallery (#2)
/ publish (push) Successful in 4m4s

Reviewed-on: #2
This commit was merged in pull request #2.
This commit is contained in:
2026-06-01 14:07:50 +02:00
parent ee3eba3cd9
commit 763163c24e
30 changed files with 513 additions and 44 deletions
+10 -2
View File
@@ -4,14 +4,22 @@ import "time"
func Default() Cfg {
return Cfg{
Image: &ImageConfig{
Image: &Image{
Concurency: 10,
Quality: 80,
CacheLifetime: time.Hour * 24 * 31,
},
Tracking: &UmamiConfig{
Tracking: &Umami{
ScriptSrc: "https://umami.schreifuchs.ch/script.js",
WebsiteID: "54d8a379-77d5-4c20-b46d-5c9a6c9e39bd",
},
Gallery: &Gallery{
Path: "photos",
},
Cache: &Valkey{
RefreshTime: time.Minute * 10,
LifeTime: time.Hour * 48,
},
LogJSON: true,
}
}
+17 -6
View File
@@ -10,11 +10,13 @@ import (
type Cfg struct {
FileSystem *WebdavConfig `env:", prefix=FILESYSTEM_"`
Cache *ValkeyConfig `env:", prefix=CACHE_"`
Image *ImageConfig `env:", prefix=IMAGE_"`
Tracking *UmamiConfig `env:", prefix=TRACKING_"`
Cache *Valkey `env:", prefix=CACHE_"`
Image *Image `env:", prefix=IMAGE_"`
Tracking *Umami `env:", prefix=TRACKING_"`
Gallery *Gallery `env:", prefix=GALLERY_"`
LogLevel slog.Level `env:"LOG_LEVEL, default=DEBUG"`
LogJSON bool `env:"LOG_JSON"`
}
type WebdavConfig struct {
@@ -23,11 +25,14 @@ type WebdavConfig struct {
Password string `env:"PASSWORD"`
}
type ValkeyConfig struct {
type Valkey struct {
ClientName string `env:"CLIENTNAME"`
Username string `env:"USERNAME"`
Password string `env:"PASSWORD"`
RefreshTime time.Duration `env:"REFRESH_TIME"`
LifeTime time.Duration `env:"LIFE_TIME"`
// InitAddress point to valkey nodes.
// Valkey will connect to them one by one and issue a CLUSTER SLOT command to initialize the cluster client until success.
// If len(InitAddress) == 1 and the address is not running in cluster mode, valkey will fall back to the single client mode.
@@ -36,13 +41,17 @@ type ValkeyConfig struct {
InitAddress []string `env:"ADDRESS"`
}
type ImageConfig struct {
type Image struct {
Concurency int64 `env:"CONCURENCY"`
Quality int `env:"QUALITY"`
CacheLifetime time.Duration `env:"CACHE_LIFETIME"`
}
type UmamiConfig struct {
type Gallery struct {
Path string `env:"PATH"`
}
type Umami struct {
ScriptSrc string `env:"SCRIPT_SRC"`
WebsiteID string `env:"WEBSITE_ID"`
}
@@ -64,5 +73,7 @@ func Read(ctx context.Context) (cfg Cfg, err error) {
return
}
slog.Info("rft", "rft", cfg.Cache.RefreshTime)
return
}
+3 -2
View File
@@ -23,11 +23,11 @@ type CachedClient struct {
}
// NewCachedClient creates a new CachedClient.
func NewCachedClient(impl FS, client valkey.Client, refreshtime time.Duration) *CachedClient {
func NewCachedClient(impl FS, client valkey.Client, refreshtime time.Duration, lifetime time.Duration) *CachedClient {
c := &CachedClient{
impl: impl,
cache: client,
ttl: refreshtime * 5,
ttl: lifetime,
}
go func() {
@@ -42,6 +42,7 @@ func NewCachedClient(impl FS, client valkey.Client, refreshtime time.Duration) *
}
func (c *CachedClient) revalidate(ctx context.Context) {
slog.Info("starting revalidation")
go c.revalidateReadDir(ctx)
go c.revalidateRead(ctx)
go c.revalidateStat(ctx)
-1
View File
@@ -26,7 +26,6 @@ func (c *CachedClient) Read(path string) ([]byte, error) {
if err == nil && len(cached) > 0 {
c.readHits.Add(1)
c.hits.Add(1)
slog.Debug("cache hit", "key", key)
var file readFile
err := json.Unmarshal(cached, &file)
-1
View File
@@ -22,7 +22,6 @@ func (c *CachedClient) ReadDir(path string) ([]os.FileInfo, error) {
if err := json.Unmarshal([]byte(val), &cached); err == nil {
c.readDirHits.Add(1)
c.hits.Add(1)
slog.Debug("cache hit", "key", key)
infos := make([]os.FileInfo, len(cached))
for i, f := range cached {
infos[i] = f
-1
View File
@@ -19,7 +19,6 @@ func (c *CachedClient) Stat(path string) (info os.FileInfo, err error) {
var f File
if err := json.Unmarshal([]byte(val), &f); err == nil {
c.hits.Add(1)
slog.Debug("cache hit", "key", key)
return f, nil
}