mirror of
https://github.com/woodchen-ink/webp_server_go.git
synced 2025-07-18 13:42:02 +08:00
* add: cache for remote image requests * update: use default expiration for NoExpiration setting * update: clean code * fix: do not save empty etag * fix: panic during type assertion
238 lines
7.3 KiB
Go
238 lines
7.3 KiB
Go
package config
|
||
|
||
import (
|
||
"encoding/json"
|
||
"flag"
|
||
"os"
|
||
"regexp"
|
||
"runtime"
|
||
"strconv"
|
||
"strings"
|
||
"time"
|
||
|
||
"github.com/patrickmn/go-cache"
|
||
log "github.com/sirupsen/logrus"
|
||
)
|
||
|
||
const (
|
||
TimeDateFormat = "2006-01-02 15:04:05"
|
||
FiberLogFormat = "${ip} - [${time}] ${method} ${url} ${status} ${referer} ${ua}\n"
|
||
WebpMax = 16383
|
||
AvifMax = 65536
|
||
HttpRegexp = `^https?://`
|
||
SampleConfig = `
|
||
{
|
||
"HOST": "127.0.0.1",
|
||
"PORT": "3333",
|
||
"QUALITY": "80",
|
||
"IMG_PATH": "./pics",
|
||
"EXHAUST_PATH": "./exhaust",
|
||
"IMG_MAP": {},
|
||
"ALLOWED_TYPES": ["jpg","png","jpeg","bmp","svg","heic","nef"],
|
||
"ENABLE_AVIF": false,
|
||
"ENABLE_EXTRA_PARAMS": false
|
||
"READ_BUFFER_SIZE": 4096,
|
||
"CONCURRENCY": 262144,
|
||
"DISABLE_KEEPALIVE": false,
|
||
"CACHE_TTL": 259200,
|
||
}`
|
||
)
|
||
|
||
var (
|
||
ConfigPath string
|
||
Jobs int
|
||
DumpSystemd bool
|
||
DumpConfig bool
|
||
ShowVersion bool
|
||
ProxyMode bool
|
||
Prefetch bool
|
||
Config = NewWebPConfig()
|
||
Version = "0.10.6"
|
||
WriteLock = cache.New(5*time.Minute, 10*time.Minute)
|
||
ConvertLock = cache.New(5*time.Minute, 10*time.Minute)
|
||
RemoteRaw = "./remote-raw"
|
||
Metadata = "./metadata"
|
||
LocalHostAlias = "local"
|
||
RemoteCache *cache.Cache
|
||
)
|
||
|
||
type MetaFile struct {
|
||
Id string `json:"id"` // hash of below path️, also json file name id.webp
|
||
Path string `json:"path"` // local: path with width and height, proxy: full url
|
||
Checksum string `json:"checksum"` // hash of original file or hash(etag). Use this to identify changes
|
||
}
|
||
|
||
type WebpConfig struct {
|
||
Host string `json:"HOST"`
|
||
Port string `json:"PORT"`
|
||
ImgPath string `json:"IMG_PATH"`
|
||
Quality int `json:"QUALITY,string"`
|
||
AllowedTypes []string `json:"ALLOWED_TYPES"`
|
||
ImageMap map[string]string `json:"IMG_MAP"`
|
||
ExhaustPath string `json:"EXHAUST_PATH"`
|
||
EnableAVIF bool `json:"ENABLE_AVIF"`
|
||
EnableExtraParams bool `json:"ENABLE_EXTRA_PARAMS"`
|
||
ReadBufferSize int `json:"READ_BUFFER_SIZE"`
|
||
Concurrency int `json:"CONCURRENCY"`
|
||
DisableKeepalive bool `json:"DISABLE_KEEPALIVE"`
|
||
CacheTTL int `json:"CACHE_TTL"`
|
||
}
|
||
|
||
func NewWebPConfig() *WebpConfig {
|
||
return &WebpConfig{
|
||
Host: "0.0.0.0",
|
||
Port: "3333",
|
||
ImgPath: "./pics",
|
||
Quality: 80,
|
||
AllowedTypes: []string{"jpg", "png", "jpeg", "bmp", "svg", "nef", "heic", "webp"},
|
||
ImageMap: map[string]string{},
|
||
ExhaustPath: "./exhaust",
|
||
EnableAVIF: false,
|
||
EnableExtraParams: false,
|
||
ReadBufferSize: 4096,
|
||
Concurrency: 262144,
|
||
DisableKeepalive: false,
|
||
CacheTTL: 259200,
|
||
}
|
||
}
|
||
|
||
func init() {
|
||
flag.StringVar(&ConfigPath, "config", "config.json", "/path/to/config.json. (Default: ./config.json)")
|
||
flag.BoolVar(&Prefetch, "prefetch", false, "Prefetch and convert image to WebP format.")
|
||
flag.IntVar(&Jobs, "jobs", runtime.NumCPU(), "Prefetch thread, default is all.")
|
||
flag.BoolVar(&DumpConfig, "dump-config", false, "Print sample config.json.")
|
||
flag.BoolVar(&ShowVersion, "V", false, "Show version information.")
|
||
}
|
||
|
||
func LoadConfig() {
|
||
jsonObject, err := os.Open(ConfigPath)
|
||
if err != nil {
|
||
log.Fatal(err)
|
||
}
|
||
decoder := json.NewDecoder(jsonObject)
|
||
_ = decoder.Decode(&Config)
|
||
_ = jsonObject.Close()
|
||
switchProxyMode()
|
||
Config.ImageMap = parseImgMap(Config.ImageMap)
|
||
|
||
// Read from ENV for override
|
||
if os.Getenv("WEBP_HOST") != "" {
|
||
Config.Host = os.Getenv("WEBP_HOST")
|
||
}
|
||
if os.Getenv("WEBP_PORT") != "" {
|
||
Config.Port = os.Getenv("WEBP_PORT")
|
||
}
|
||
if os.Getenv("WEBP_IMG_PATH") != "" {
|
||
Config.ImgPath = os.Getenv("WEBP_IMG_PATH")
|
||
}
|
||
if os.Getenv("WEBP_EXHAUST_PATH") != "" {
|
||
Config.ExhaustPath = os.Getenv("WEBP_EXHAUST_PATH")
|
||
}
|
||
if os.Getenv("WEBP_QUALITY") != "" {
|
||
quality, err := strconv.Atoi(os.Getenv("WEBP_QUALITY"))
|
||
if err != nil {
|
||
log.Warnf("WEBP_QUALITY is not a valid integer, using value in config.json %d", Config.Quality)
|
||
} else {
|
||
Config.Quality = quality
|
||
}
|
||
}
|
||
if os.Getenv("WEBP_ALLOWED_TYPES") != "" {
|
||
Config.AllowedTypes = strings.Split(os.Getenv("WEBP_ALLOWED_TYPES"), ",")
|
||
}
|
||
if os.Getenv("WEBP_ENABLE_AVIF") != "" {
|
||
enableAVIF := os.Getenv("WEBP_ENABLE_AVIF")
|
||
if enableAVIF == "true" {
|
||
Config.EnableAVIF = true
|
||
} else if enableAVIF == "false" {
|
||
Config.EnableAVIF = false
|
||
} else {
|
||
log.Warnf("WEBP_ENABLE_AVIF is not a valid boolean, using value in config.json %t", Config.EnableAVIF)
|
||
}
|
||
}
|
||
if os.Getenv("WEBP_ENABLE_EXTRA_PARAMS") != "" {
|
||
enableExtraParams := os.Getenv("WEBP_ENABLE_EXTRA_PARAMS")
|
||
if enableExtraParams == "true" {
|
||
Config.EnableExtraParams = true
|
||
} else if enableExtraParams == "false" {
|
||
Config.EnableExtraParams = false
|
||
} else {
|
||
log.Warnf("WEBP_ENABLE_EXTRA_PARAMS is not a valid boolean, using value in config.json %t", Config.EnableExtraParams)
|
||
}
|
||
}
|
||
if os.Getenv("WEBP_IMG_MAP") != "" {
|
||
// TODO
|
||
}
|
||
if os.Getenv("WEBP_READ_BUFFER_SIZE") != "" {
|
||
readBufferSize, err := strconv.Atoi(os.Getenv("WEBP_READ_BUFFER_SIZE"))
|
||
if err != nil {
|
||
log.Warnf("WEBP_READ_BUFFER_SIZE is not a valid integer, using value in config.json %d", Config.ReadBufferSize)
|
||
} else {
|
||
Config.ReadBufferSize = readBufferSize
|
||
}
|
||
}
|
||
if os.Getenv("WEBP_CONCURRENCY") != "" {
|
||
concurrency, err := strconv.Atoi(os.Getenv("WEBP_CONCURRENCY"))
|
||
if err != nil {
|
||
log.Warnf("WEBP_CONCURRENCY is not a valid integer, using value in config.json %d", Config.Concurrency)
|
||
} else {
|
||
Config.Concurrency = concurrency
|
||
}
|
||
}
|
||
if os.Getenv("WEBP_DISABLE_KEEPALIVE") != "" {
|
||
disableKeepalive := os.Getenv("WEBP_DISABLE_KEEPALIVE")
|
||
if disableKeepalive == "true" {
|
||
Config.DisableKeepalive = true
|
||
} else if disableKeepalive == "false" {
|
||
Config.DisableKeepalive = false
|
||
} else {
|
||
log.Warnf("WEBP_DISABLE_KEEPALIVE is not a valid boolean, using value in config.json %t", Config.DisableKeepalive)
|
||
}
|
||
}
|
||
if os.Getenv("CACHE_TTL") != "" {
|
||
cacheTTL, err := strconv.Atoi(os.Getenv("CACHE_TTL"))
|
||
if err != nil {
|
||
log.Warnf("CACHE_TTL is not a valid integer, using value in config.json %d", Config.CacheTTL)
|
||
} else {
|
||
Config.CacheTTL = cacheTTL
|
||
}
|
||
}
|
||
|
||
if Config.CacheTTL == 0 {
|
||
RemoteCache = cache.New(cache.NoExpiration, 10*time.Minute)
|
||
} else {
|
||
RemoteCache = cache.New(time.Duration(Config.CacheTTL)*time.Minute, 10*time.Minute)
|
||
}
|
||
|
||
log.Debugln("Config init complete")
|
||
log.Debugln("Config", Config)
|
||
}
|
||
|
||
func parseImgMap(imgMap map[string]string) map[string]string {
|
||
var parsedImgMap = map[string]string{}
|
||
httpRegexpMatcher := regexp.MustCompile(HttpRegexp)
|
||
for uriMap, uriMapTarget := range imgMap {
|
||
if httpRegexpMatcher.Match([]byte(uriMap)) || strings.HasPrefix(uriMap, "/") {
|
||
// Valid
|
||
parsedImgMap[uriMap] = uriMapTarget
|
||
} else {
|
||
// Invalid
|
||
log.Warnf("IMG_MAP key '%s' does matches '%s' or starts with '/' - skipped", uriMap, HttpRegexp)
|
||
}
|
||
}
|
||
return parsedImgMap
|
||
}
|
||
|
||
type ExtraParams struct {
|
||
Width int // in px
|
||
Height int // in px
|
||
}
|
||
|
||
func switchProxyMode() {
|
||
matched, _ := regexp.MatchString(HttpRegexp, Config.ImgPath)
|
||
if matched {
|
||
// Enable proxy based on ImgPath should be deprecated in future versions
|
||
log.Warn("Enable proxy based on ImgPath will be deprecated in future versions. Use IMG_MAP config options instead")
|
||
ProxyMode = true
|
||
}
|
||
}
|