Skip to content

Commit

Permalink
chore(image-proxy): switch to imgproxy service
Browse files Browse the repository at this point in the history
  • Loading branch information
ncarlier committed Nov 1, 2023
1 parent 3811f50 commit 59abebd
Show file tree
Hide file tree
Showing 14 changed files with 75 additions and 43 deletions.
10 changes: 5 additions & 5 deletions docker-compose.dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@ services:
#######################################
# Imagor: Image proxy
#######################################
imagor:
image: shumc/imagor
command: -debug -imagor-auto-webp
imgproxy:
image: darthsim/imgproxy
environment:
IMAGOR_SECRET: ${READFLOW_HASH_SECRET_SALT:-pepper}
IMGPROXY_KEY: ${READFLOW_HASH_SECRET_KEY:-736563726574}
IMGPROXY_SALT: ${READFLOW_HASH_SECRET_SALT:-706570706572}
ports:
- "${IMAGOR_PORT:-8081}:8000"
- "${IMAGOR_PORT:-8081}:8080"
#######################################
# Goenberg: PDF generator
#######################################
Expand Down
10 changes: 5 additions & 5 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ services:
- db-data:/var/lib/postgresql/data

#######################################
# Imagor: Image proxy
# Imgproxy: Image proxy
#######################################
imagor:
image: shumc/imagor
command: -imagor-auto-webp
imgproxy:
image: darthsim/imgproxy
environment:
IMAGOR_SECRET: ${READFLOW_HASH_SECRET_SALT:-pepper}
IMGPROXY_KEY: ${READFLOW_HASH_SECRET_KEY:-736563726574}
IMGPROXY_SALT: ${READFLOW_HASH_SECRET_SALT:-706570706572}
logging:
driver: "json-file"
options:
Expand Down
9 changes: 7 additions & 2 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,18 @@ func NewConfig() *Config {
PublicURL: "http://localhost:8080",
},
Hash: HashConfig{
SecretSalt: "pepper",
SecretKey: hex_string{
Value: []byte("secret"),
},
SecretSalt: hex_string{
Value: []byte("pepper"),
},
},
Avatar: AvatarConfig{
ServiceProvider: "https://robohash.org/{seed}?set=set4&size=48x48",
},
Image: ImageConfig{
ProxySizes: "320x200,768x576",
ProxySizes: "320,768",
},
RateLimiting: RateLimitingConfig{
Notification: RateLimiting{
Expand Down
9 changes: 6 additions & 3 deletions pkg/config/defaults.toml
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,11 @@ directory = "${READFLOW_UI_DIRECTORY}"
public_url = "${READFLOW_UI_PUBLIC_URL}"

[hash]
## Secret salt used by hash algorythms
# Default: "pepper"
## Secret key used by hash algorythms (hex-encoded)
# Default: "736563726574" (aka "secret")
secret_key = "${READFLOW_HASH_SECRET_KEY}"
## Secret salt used by hash algorythms (hex-encoded)
# Default: "706570706572" (aka "pepper")
secret_salt = "${READFLOW_HASH_SECRET_SALT}"

[scraping]
Expand Down Expand Up @@ -111,7 +114,7 @@ service_provider = "${READFLOW_AVATAR_SERVICE_PROVIDER}"
proxy_url = "${READFLOW_IMAGE_PROXY_URL}"
## Image proxy supported sizes
# Comma separated list of image size
# Default: "320x200,768x576"
# Default: "320,768"
sizes = "${READFLOW_IMAGE_PROXY_SIZES}"

[pdf]
Expand Down
5 changes: 3 additions & 2 deletions pkg/config/test/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/ncarlier/readflow/pkg/config"
)
Expand All @@ -18,11 +19,11 @@ func TestDefaultConfig(t *testing.T) {
func TestLaodConfigFromFile(t *testing.T) {
conf := config.NewConfig()
err := conf.LoadFile("test.toml")
assert.Nil(t, err)
require.Nil(t, err)
// Default overide
assert.Equal(t, "localhost:8081", conf.HTTP.ListenAddr)
// Env variable substitution
assert.NotEqual(t, "${USER}", conf.Hash.SecretSalt)
assert.Equal(t, []byte("test"), conf.Hash.SecretSalt.Value)
// Default if empty
assert.Equal(t, "http://localhost:8080", conf.HTTP.PublicURL)
// Sub attribute
Expand Down
4 changes: 2 additions & 2 deletions pkg/config/test/test.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
listen_addr = "localhost:8081"
public_url = "${AN_EMPTY_VARIABLE}"

[hsah]
secret_salt = "${USER}"
[hash]
secret_salt = "74657374"

[integration.sentry]
dsn_url = "https://1..9:[email protected]/1..9"
Expand Down
3 changes: 2 additions & 1 deletion pkg/config/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@ type UIConfig struct {

// HashConfig for hash configuration section
type HashConfig struct {
SecretSalt string `toml:"secret_salt"`
SecretKey hex_string `toml:"secret_key"`
SecretSalt hex_string `toml:"secret_salt"`
}

// ScrapingConfig for scraping configuration section
Expand Down
15 changes: 14 additions & 1 deletion pkg/config/unmarshal.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package config

import "time"
import (
"encoding/hex"
"time"
)

type duration struct {
time.Duration
Expand All @@ -11,3 +14,13 @@ func (d *duration) UnmarshalText(text []byte) error {
d.Duration, err = time.ParseDuration(string(text))
return err
}

type hex_string struct {
Value []byte
}

func (hs *hex_string) UnmarshalText(text []byte) error {
var err error
hs.Value, err = hex.DecodeString(string(text))
return err
}
4 changes: 2 additions & 2 deletions pkg/helper/hashid.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ type HashIDHandler struct {
}

// NewHashIDHandler creates hashid handler
func NewHashIDHandler(salt string) (*HashIDHandler, error) {
func NewHashIDHandler(salt []byte) (*HashIDHandler, error) {
hd := hashids.NewData()
hd.Salt = salt
hd.Salt = string(salt)
provider, err := hashids.NewWithData(hd)
if err != nil {
return nil, err
Expand Down
3 changes: 3 additions & 0 deletions pkg/schema/article/queries.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ func thumbnailsResolver(p graphql.ResolveParams) (interface{}, error) {
if !ok {
return nil, errors.New("thumbnails resolver is expecting an article")
}
if article.Image == nil || *article.Image == "" {
return nil, nil
}
if service.Lookup().GetConfig().Image.ProxyURL == "" {
return nil, nil
}
Expand Down
14 changes: 0 additions & 14 deletions pkg/service/articles.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,8 @@ package service

import (
"context"
"crypto/sha1"
"errors"
"strings"

"github.com/ncarlier/readflow/pkg/helper"
"github.com/ncarlier/readflow/pkg/model"
)

Expand Down Expand Up @@ -95,14 +92,3 @@ func (reg *Registry) CleanHistory(ctx context.Context) (int64, error) {

return nb, nil
}

// GetArticleThumbnail return article thumbnail URL
func (reg *Registry) GetArticleThumbnailHash(article *model.Article, size string) string {
if article.Image == nil || *article.Image == "" {
return ""
}
size += "/top"
path := size + "/" + strings.TrimPrefix(strings.TrimPrefix(*article.Image, "https://"), "http://")
hash := helper.Sign(sha1.New, path, reg.conf.Hash.SecretSalt, 0)
return hash + "/" + size
}
22 changes: 22 additions & 0 deletions pkg/service/articles_thumbnail.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package service

import (
"crypto/hmac"
"crypto/sha256"
"encoding/base64"

"github.com/ncarlier/readflow/pkg/model"
)

// GetArticleThumbnail return article thumbnail URL
func (reg *Registry) GetArticleThumbnailHash(article *model.Article, size string) string {
if article.Image == nil || *article.Image == "" {
return ""
}
path := "/resize:fit:" + size + "/" + base64.StdEncoding.EncodeToString([]byte(*article.Image))

mac := hmac.New(sha256.New, reg.conf.Hash.SecretKey.Value)
mac.Write(reg.conf.Hash.SecretSalt.Value)
mac.Write([]byte(path))
return base64.RawURLEncoding.EncodeToString(mac.Sum(nil))
}
2 changes: 1 addition & 1 deletion pkg/service/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ func Configure(conf config.Config, database db.DB, downloadCache cache.Cache) er
if err != nil {
return err
}
hashid, err := helper.NewHashIDHandler(conf.Hash.SecretSalt)
hashid, err := helper.NewHashIDHandler(conf.Hash.SecretSalt.Value)
if err != nil {
return err
}
Expand Down
8 changes: 3 additions & 5 deletions ui/src/articles/components/ArticleImage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,16 @@ import React, { FC, ImgHTMLAttributes } from 'react'
import { Article, ArticleThumbnail } from '../models'
import { getAPIURL } from '../../helpers'

const imgResToWidth = (res: string) => res.split('x')[0] + 'w'
const getThumbnailURL = (thumbnail: ArticleThumbnail, src: string) => `${getAPIURL()}/img/${thumbnail.hash}/${src.replace(/^https?:\/\//, '')}`

const getThumbnailURL = (thumbnail: ArticleThumbnail, src: string) => `${getAPIURL()}/img/${thumbnail.hash}/resize:fit:${thumbnail.size}/${btoa(src)}`

const getThumbnailAttributes = ({thumbnails, image}: Article) => {
const attrs :ImgHTMLAttributes<HTMLImageElement> = {}
if (!thumbnails || thumbnails.length == 0) {
return attrs
}
const sizes = thumbnails.reverse().map(thumb => thumb.size.split('x')[0] + 'px')
const sizes = thumbnails.reverse().map(thumb => `${thumb.size}px`)
attrs.sizes = `(max-width: ${sizes[0]}) ${sizes.join(', ')}`
attrs.srcSet = thumbnails?.map(thumb => `${getThumbnailURL(thumb, image)} ${imgResToWidth(thumb.size)}`).join(',')
attrs.srcSet = thumbnails?.map(thumb => `${getThumbnailURL(thumb, image)} ${thumb.size}w`).join(',')
return attrs
}

Expand Down

0 comments on commit 59abebd

Please sign in to comment.