Rename PROXY_* options to MEDIA_PROXY_*
This commit is contained in:
parent
ed20771194
commit
c2311e316c
14 changed files with 413 additions and 204 deletions
2
Makefile
2
Makefile
|
@ -101,7 +101,7 @@ windows-x86:
|
||||||
@ GOOS=windows GOARCH=386 go build -ldflags=$(LD_FLAGS) -o $(APP)-$@.exe main.go
|
@ GOOS=windows GOARCH=386 go build -ldflags=$(LD_FLAGS) -o $(APP)-$@.exe main.go
|
||||||
|
|
||||||
run:
|
run:
|
||||||
@ LOG_DATE_TIME=1 DEBUG=1 RUN_MIGRATIONS=1 CREATE_ADMIN=1 ADMIN_USERNAME=admin ADMIN_PASSWORD=test123 go run main.go
|
@ LOG_DATE_TIME=1 LOG_LEVEL=debug RUN_MIGRATIONS=1 CREATE_ADMIN=1 ADMIN_USERNAME=admin ADMIN_PASSWORD=test123 go run main.go
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
@ rm -f $(APP)-* $(APP) $(APP)*.rpm $(APP)*.deb $(APP)*.exe
|
@ rm -f $(APP)-* $(APP) $(APP)*.rpm $(APP)*.deb $(APP)*.exe
|
||||||
|
|
|
@ -15,8 +15,8 @@ import (
|
||||||
"miniflux.app/v2/internal/http/request"
|
"miniflux.app/v2/internal/http/request"
|
||||||
"miniflux.app/v2/internal/http/response/json"
|
"miniflux.app/v2/internal/http/response/json"
|
||||||
"miniflux.app/v2/internal/integration"
|
"miniflux.app/v2/internal/integration"
|
||||||
|
"miniflux.app/v2/internal/mediaproxy"
|
||||||
"miniflux.app/v2/internal/model"
|
"miniflux.app/v2/internal/model"
|
||||||
"miniflux.app/v2/internal/proxy"
|
|
||||||
"miniflux.app/v2/internal/reader/processor"
|
"miniflux.app/v2/internal/reader/processor"
|
||||||
"miniflux.app/v2/internal/reader/readingtime"
|
"miniflux.app/v2/internal/reader/readingtime"
|
||||||
"miniflux.app/v2/internal/storage"
|
"miniflux.app/v2/internal/storage"
|
||||||
|
@ -36,14 +36,14 @@ func (h *handler) getEntryFromBuilder(w http.ResponseWriter, r *http.Request, b
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
entry.Content = proxy.AbsoluteProxyRewriter(h.router, r.Host, entry.Content)
|
entry.Content = mediaproxy.RewriteDocumentWithAbsoluteProxyURL(h.router, r.Host, entry.Content)
|
||||||
proxyOption := config.Opts.ProxyOption()
|
proxyOption := config.Opts.MediaProxyMode()
|
||||||
|
|
||||||
for i := range entry.Enclosures {
|
for i := range entry.Enclosures {
|
||||||
if proxyOption == "all" || proxyOption != "none" && !urllib.IsHTTPS(entry.Enclosures[i].URL) {
|
if proxyOption == "all" || proxyOption != "none" && !urllib.IsHTTPS(entry.Enclosures[i].URL) {
|
||||||
for _, mediaType := range config.Opts.ProxyMediaTypes() {
|
for _, mediaType := range config.Opts.MediaProxyResourceTypes() {
|
||||||
if strings.HasPrefix(entry.Enclosures[i].MimeType, mediaType+"/") {
|
if strings.HasPrefix(entry.Enclosures[i].MimeType, mediaType+"/") {
|
||||||
entry.Enclosures[i].URL = proxy.AbsoluteProxifyURL(h.router, r.Host, entry.Enclosures[i].URL)
|
entry.Enclosures[i].URL = mediaproxy.ProxifyAbsoluteURL(h.router, r.Host, entry.Enclosures[i].URL)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -164,7 +164,7 @@ func (h *handler) findEntries(w http.ResponseWriter, r *http.Request, feedID int
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := range entries {
|
for i := range entries {
|
||||||
entries[i].Content = proxy.AbsoluteProxyRewriter(h.router, r.Host, entries[i].Content)
|
entries[i].Content = mediaproxy.RewriteDocumentWithAbsoluteProxyURL(h.router, r.Host, entries[i].Content)
|
||||||
}
|
}
|
||||||
|
|
||||||
json.OK(w, r, &entriesResponse{Total: count, Entries: entries})
|
json.OK(w, r, &entriesResponse{Total: count, Entries: entries})
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
package config // import "miniflux.app/v2/internal/config"
|
package config // import "miniflux.app/v2/internal/config"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
@ -1442,9 +1443,9 @@ func TestPocketConsumerKeyFromUserPrefs(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestProxyOption(t *testing.T) {
|
func TestMediaProxyMode(t *testing.T) {
|
||||||
os.Clearenv()
|
os.Clearenv()
|
||||||
os.Setenv("PROXY_OPTION", "all")
|
os.Setenv("MEDIA_PROXY_MODE", "all")
|
||||||
|
|
||||||
parser := NewParser()
|
parser := NewParser()
|
||||||
opts, err := parser.ParseEnvironmentVariables()
|
opts, err := parser.ParseEnvironmentVariables()
|
||||||
|
@ -1453,14 +1454,14 @@ func TestProxyOption(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
expected := "all"
|
expected := "all"
|
||||||
result := opts.ProxyOption()
|
result := opts.MediaProxyMode()
|
||||||
|
|
||||||
if result != expected {
|
if result != expected {
|
||||||
t.Fatalf(`Unexpected PROXY_OPTION value, got %q instead of %q`, result, expected)
|
t.Fatalf(`Unexpected MEDIA_PROXY_MODE value, got %q instead of %q`, result, expected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDefaultProxyOptionValue(t *testing.T) {
|
func TestDefaultMediaProxyModeValue(t *testing.T) {
|
||||||
os.Clearenv()
|
os.Clearenv()
|
||||||
|
|
||||||
parser := NewParser()
|
parser := NewParser()
|
||||||
|
@ -1469,17 +1470,17 @@ func TestDefaultProxyOptionValue(t *testing.T) {
|
||||||
t.Fatalf(`Parsing failure: %v`, err)
|
t.Fatalf(`Parsing failure: %v`, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
expected := defaultProxyOption
|
expected := defaultMediaProxyMode
|
||||||
result := opts.ProxyOption()
|
result := opts.MediaProxyMode()
|
||||||
|
|
||||||
if result != expected {
|
if result != expected {
|
||||||
t.Fatalf(`Unexpected PROXY_OPTION value, got %q instead of %q`, result, expected)
|
t.Fatalf(`Unexpected MEDIA_PROXY_MODE value, got %q instead of %q`, result, expected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestProxyMediaTypes(t *testing.T) {
|
func TestMediaProxyResourceTypes(t *testing.T) {
|
||||||
os.Clearenv()
|
os.Clearenv()
|
||||||
os.Setenv("PROXY_MEDIA_TYPES", "image,audio")
|
os.Setenv("MEDIA_PROXY_RESOURCE_TYPES", "image,audio")
|
||||||
|
|
||||||
parser := NewParser()
|
parser := NewParser()
|
||||||
opts, err := parser.ParseEnvironmentVariables()
|
opts, err := parser.ParseEnvironmentVariables()
|
||||||
|
@ -1489,25 +1490,25 @@ func TestProxyMediaTypes(t *testing.T) {
|
||||||
|
|
||||||
expected := []string{"audio", "image"}
|
expected := []string{"audio", "image"}
|
||||||
|
|
||||||
if len(expected) != len(opts.ProxyMediaTypes()) {
|
if len(expected) != len(opts.MediaProxyResourceTypes()) {
|
||||||
t.Fatalf(`Unexpected PROXY_MEDIA_TYPES value, got %v instead of %v`, opts.ProxyMediaTypes(), expected)
|
t.Fatalf(`Unexpected MEDIA_PROXY_RESOURCE_TYPES value, got %v instead of %v`, opts.MediaProxyResourceTypes(), expected)
|
||||||
}
|
}
|
||||||
|
|
||||||
resultMap := make(map[string]bool)
|
resultMap := make(map[string]bool)
|
||||||
for _, mediaType := range opts.ProxyMediaTypes() {
|
for _, mediaType := range opts.MediaProxyResourceTypes() {
|
||||||
resultMap[mediaType] = true
|
resultMap[mediaType] = true
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, mediaType := range expected {
|
for _, mediaType := range expected {
|
||||||
if !resultMap[mediaType] {
|
if !resultMap[mediaType] {
|
||||||
t.Fatalf(`Unexpected PROXY_MEDIA_TYPES value, got %v instead of %v`, opts.ProxyMediaTypes(), expected)
|
t.Fatalf(`Unexpected MEDIA_PROXY_RESOURCE_TYPES value, got %v instead of %v`, opts.MediaProxyResourceTypes(), expected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestProxyMediaTypesWithDuplicatedValues(t *testing.T) {
|
func TestMediaProxyResourceTypesWithDuplicatedValues(t *testing.T) {
|
||||||
os.Clearenv()
|
os.Clearenv()
|
||||||
os.Setenv("PROXY_MEDIA_TYPES", "image,audio, image")
|
os.Setenv("MEDIA_PROXY_RESOURCE_TYPES", "image,audio, image")
|
||||||
|
|
||||||
parser := NewParser()
|
parser := NewParser()
|
||||||
opts, err := parser.ParseEnvironmentVariables()
|
opts, err := parser.ParseEnvironmentVariables()
|
||||||
|
@ -1516,23 +1517,119 @@ func TestProxyMediaTypesWithDuplicatedValues(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
expected := []string{"audio", "image"}
|
expected := []string{"audio", "image"}
|
||||||
if len(expected) != len(opts.ProxyMediaTypes()) {
|
if len(expected) != len(opts.MediaProxyResourceTypes()) {
|
||||||
t.Fatalf(`Unexpected PROXY_MEDIA_TYPES value, got %v instead of %v`, opts.ProxyMediaTypes(), expected)
|
t.Fatalf(`Unexpected MEDIA_PROXY_RESOURCE_TYPES value, got %v instead of %v`, opts.MediaProxyResourceTypes(), expected)
|
||||||
}
|
}
|
||||||
|
|
||||||
resultMap := make(map[string]bool)
|
resultMap := make(map[string]bool)
|
||||||
for _, mediaType := range opts.ProxyMediaTypes() {
|
for _, mediaType := range opts.MediaProxyResourceTypes() {
|
||||||
resultMap[mediaType] = true
|
resultMap[mediaType] = true
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, mediaType := range expected {
|
for _, mediaType := range expected {
|
||||||
if !resultMap[mediaType] {
|
if !resultMap[mediaType] {
|
||||||
t.Fatalf(`Unexpected PROXY_MEDIA_TYPES value, got %v instead of %v`, opts.ProxyMediaTypes(), expected)
|
t.Fatalf(`Unexpected MEDIA_PROXY_RESOURCE_TYPES value, got %v instead of %v`, opts.MediaProxyResourceTypes(), expected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestProxyImagesOptionBackwardCompatibility(t *testing.T) {
|
func TestDefaultMediaProxyResourceTypes(t *testing.T) {
|
||||||
|
os.Clearenv()
|
||||||
|
|
||||||
|
parser := NewParser()
|
||||||
|
opts, err := parser.ParseEnvironmentVariables()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf(`Parsing failure: %v`, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
expected := []string{"image"}
|
||||||
|
|
||||||
|
if len(expected) != len(opts.MediaProxyResourceTypes()) {
|
||||||
|
t.Fatalf(`Unexpected MEDIA_PROXY_RESOURCE_TYPES value, got %v instead of %v`, opts.MediaProxyResourceTypes(), expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
resultMap := make(map[string]bool)
|
||||||
|
for _, mediaType := range opts.MediaProxyResourceTypes() {
|
||||||
|
resultMap[mediaType] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, mediaType := range expected {
|
||||||
|
if !resultMap[mediaType] {
|
||||||
|
t.Fatalf(`Unexpected MEDIA_PROXY_RESOURCE_TYPES value, got %v instead of %v`, opts.MediaProxyResourceTypes(), expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMediaProxyHTTPClientTimeout(t *testing.T) {
|
||||||
|
os.Clearenv()
|
||||||
|
os.Setenv("MEDIA_PROXY_HTTP_CLIENT_TIMEOUT", "24")
|
||||||
|
|
||||||
|
parser := NewParser()
|
||||||
|
opts, err := parser.ParseEnvironmentVariables()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf(`Parsing failure: %v`, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
expected := 24
|
||||||
|
result := opts.MediaProxyHTTPClientTimeout()
|
||||||
|
|
||||||
|
if result != expected {
|
||||||
|
t.Fatalf(`Unexpected MEDIA_PROXY_HTTP_CLIENT_TIMEOUT value, got %d instead of %d`, result, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDefaultMediaProxyHTTPClientTimeoutValue(t *testing.T) {
|
||||||
|
os.Clearenv()
|
||||||
|
|
||||||
|
parser := NewParser()
|
||||||
|
opts, err := parser.ParseEnvironmentVariables()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf(`Parsing failure: %v`, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
expected := defaultMediaProxyHTTPClientTimeout
|
||||||
|
result := opts.MediaProxyHTTPClientTimeout()
|
||||||
|
|
||||||
|
if result != expected {
|
||||||
|
t.Fatalf(`Unexpected MEDIA_PROXY_HTTP_CLIENT_TIMEOUT value, got %d instead of %d`, result, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMediaProxyCustomURL(t *testing.T) {
|
||||||
|
os.Clearenv()
|
||||||
|
os.Setenv("MEDIA_PROXY_CUSTOM_URL", "http://example.org/proxy")
|
||||||
|
|
||||||
|
parser := NewParser()
|
||||||
|
opts, err := parser.ParseEnvironmentVariables()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf(`Parsing failure: %v`, err)
|
||||||
|
}
|
||||||
|
expected := "http://example.org/proxy"
|
||||||
|
result := opts.MediaCustomProxyURL()
|
||||||
|
if result != expected {
|
||||||
|
t.Fatalf(`Unexpected MEDIA_PROXY_CUSTOM_URL value, got %q instead of %q`, result, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMediaProxyPrivateKey(t *testing.T) {
|
||||||
|
os.Clearenv()
|
||||||
|
os.Setenv("MEDIA_PROXY_PRIVATE_KEY", "foobar")
|
||||||
|
|
||||||
|
parser := NewParser()
|
||||||
|
opts, err := parser.ParseEnvironmentVariables()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf(`Parsing failure: %v`, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
expected := []byte("foobar")
|
||||||
|
result := opts.MediaProxyPrivateKey()
|
||||||
|
|
||||||
|
if !bytes.Equal(result, expected) {
|
||||||
|
t.Fatalf(`Unexpected MEDIA_PROXY_PRIVATE_KEY value, got %q instead of %q`, result, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestProxyImagesOptionForBackwardCompatibility(t *testing.T) {
|
||||||
os.Clearenv()
|
os.Clearenv()
|
||||||
os.Setenv("PROXY_IMAGES", "all")
|
os.Setenv("PROXY_IMAGES", "all")
|
||||||
|
|
||||||
|
@ -1543,30 +1640,31 @@ func TestProxyImagesOptionBackwardCompatibility(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
expected := []string{"image"}
|
expected := []string{"image"}
|
||||||
if len(expected) != len(opts.ProxyMediaTypes()) {
|
if len(expected) != len(opts.MediaProxyResourceTypes()) {
|
||||||
t.Fatalf(`Unexpected PROXY_MEDIA_TYPES value, got %v instead of %v`, opts.ProxyMediaTypes(), expected)
|
t.Fatalf(`Unexpected PROXY_IMAGES value, got %v instead of %v`, opts.MediaProxyResourceTypes(), expected)
|
||||||
}
|
}
|
||||||
|
|
||||||
resultMap := make(map[string]bool)
|
resultMap := make(map[string]bool)
|
||||||
for _, mediaType := range opts.ProxyMediaTypes() {
|
for _, mediaType := range opts.MediaProxyResourceTypes() {
|
||||||
resultMap[mediaType] = true
|
resultMap[mediaType] = true
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, mediaType := range expected {
|
for _, mediaType := range expected {
|
||||||
if !resultMap[mediaType] {
|
if !resultMap[mediaType] {
|
||||||
t.Fatalf(`Unexpected PROXY_MEDIA_TYPES value, got %v instead of %v`, opts.ProxyMediaTypes(), expected)
|
t.Fatalf(`Unexpected PROXY_IMAGES value, got %v instead of %v`, opts.MediaProxyResourceTypes(), expected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
expectedProxyOption := "all"
|
expectedProxyOption := "all"
|
||||||
result := opts.ProxyOption()
|
result := opts.MediaProxyMode()
|
||||||
if result != expectedProxyOption {
|
if result != expectedProxyOption {
|
||||||
t.Fatalf(`Unexpected PROXY_OPTION value, got %q instead of %q`, result, expectedProxyOption)
|
t.Fatalf(`Unexpected PROXY_OPTION value, got %q instead of %q`, result, expectedProxyOption)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDefaultProxyMediaTypes(t *testing.T) {
|
func TestProxyImageURLForBackwardCompatibility(t *testing.T) {
|
||||||
os.Clearenv()
|
os.Clearenv()
|
||||||
|
os.Setenv("PROXY_IMAGE_URL", "http://example.org/proxy")
|
||||||
|
|
||||||
parser := NewParser()
|
parser := NewParser()
|
||||||
opts, err := parser.ParseEnvironmentVariables()
|
opts, err := parser.ParseEnvironmentVariables()
|
||||||
|
@ -1574,25 +1672,73 @@ func TestDefaultProxyMediaTypes(t *testing.T) {
|
||||||
t.Fatalf(`Parsing failure: %v`, err)
|
t.Fatalf(`Parsing failure: %v`, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
expected := []string{"image"}
|
expected := "http://example.org/proxy"
|
||||||
|
result := opts.MediaCustomProxyURL()
|
||||||
|
if result != expected {
|
||||||
|
t.Fatalf(`Unexpected PROXY_IMAGE_URL value, got %q instead of %q`, result, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if len(expected) != len(opts.ProxyMediaTypes()) {
|
func TestProxyURLOptionForBackwardCompatibility(t *testing.T) {
|
||||||
t.Fatalf(`Unexpected PROXY_MEDIA_TYPES value, got %v instead of %v`, opts.ProxyMediaTypes(), expected)
|
os.Clearenv()
|
||||||
|
os.Setenv("PROXY_URL", "http://example.org/proxy")
|
||||||
|
|
||||||
|
parser := NewParser()
|
||||||
|
opts, err := parser.ParseEnvironmentVariables()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf(`Parsing failure: %v`, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
expected := "http://example.org/proxy"
|
||||||
|
result := opts.MediaCustomProxyURL()
|
||||||
|
if result != expected {
|
||||||
|
t.Fatalf(`Unexpected PROXY_URL value, got %q instead of %q`, result, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestProxyMediaTypesOptionForBackwardCompatibility(t *testing.T) {
|
||||||
|
os.Clearenv()
|
||||||
|
os.Setenv("PROXY_MEDIA_TYPES", "image,audio")
|
||||||
|
|
||||||
|
parser := NewParser()
|
||||||
|
opts, err := parser.ParseEnvironmentVariables()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf(`Parsing failure: %v`, err)
|
||||||
|
}
|
||||||
|
expected := []string{"audio", "image"}
|
||||||
|
if len(expected) != len(opts.MediaProxyResourceTypes()) {
|
||||||
|
t.Fatalf(`Unexpected PROXY_MEDIA_TYPES value, got %v instead of %v`, opts.MediaProxyResourceTypes(), expected)
|
||||||
}
|
}
|
||||||
|
|
||||||
resultMap := make(map[string]bool)
|
resultMap := make(map[string]bool)
|
||||||
for _, mediaType := range opts.ProxyMediaTypes() {
|
for _, mediaType := range opts.MediaProxyResourceTypes() {
|
||||||
resultMap[mediaType] = true
|
resultMap[mediaType] = true
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, mediaType := range expected {
|
for _, mediaType := range expected {
|
||||||
if !resultMap[mediaType] {
|
if !resultMap[mediaType] {
|
||||||
t.Fatalf(`Unexpected PROXY_MEDIA_TYPES value, got %v instead of %v`, opts.ProxyMediaTypes(), expected)
|
t.Fatalf(`Unexpected PROXY_MEDIA_TYPES value, got %v instead of %v`, opts.MediaProxyResourceTypes(), expected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestProxyHTTPClientTimeout(t *testing.T) {
|
func TestProxyOptionForBackwardCompatibility(t *testing.T) {
|
||||||
|
os.Clearenv()
|
||||||
|
os.Setenv("PROXY_OPTION", "all")
|
||||||
|
|
||||||
|
parser := NewParser()
|
||||||
|
opts, err := parser.ParseEnvironmentVariables()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf(`Parsing failure: %v`, err)
|
||||||
|
}
|
||||||
|
expected := "all"
|
||||||
|
result := opts.MediaProxyMode()
|
||||||
|
if result != expected {
|
||||||
|
t.Fatalf(`Unexpected PROXY_OPTION value, got %q instead of %q`, result, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestProxyHTTPClientTimeoutOptionForBackwardCompatibility(t *testing.T) {
|
||||||
os.Clearenv()
|
os.Clearenv()
|
||||||
os.Setenv("PROXY_HTTP_CLIENT_TIMEOUT", "24")
|
os.Setenv("PROXY_HTTP_CLIENT_TIMEOUT", "24")
|
||||||
|
|
||||||
|
@ -1601,29 +1747,26 @@ func TestProxyHTTPClientTimeout(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf(`Parsing failure: %v`, err)
|
t.Fatalf(`Parsing failure: %v`, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
expected := 24
|
expected := 24
|
||||||
result := opts.ProxyHTTPClientTimeout()
|
result := opts.MediaProxyHTTPClientTimeout()
|
||||||
|
|
||||||
if result != expected {
|
if result != expected {
|
||||||
t.Fatalf(`Unexpected PROXY_HTTP_CLIENT_TIMEOUT value, got %d instead of %d`, result, expected)
|
t.Fatalf(`Unexpected PROXY_HTTP_CLIENT_TIMEOUT value, got %d instead of %d`, result, expected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDefaultProxyHTTPClientTimeoutValue(t *testing.T) {
|
func TestProxyPrivateKeyOptionForBackwardCompatibility(t *testing.T) {
|
||||||
os.Clearenv()
|
os.Clearenv()
|
||||||
|
os.Setenv("PROXY_PRIVATE_KEY", "foobar")
|
||||||
|
|
||||||
parser := NewParser()
|
parser := NewParser()
|
||||||
opts, err := parser.ParseEnvironmentVariables()
|
opts, err := parser.ParseEnvironmentVariables()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf(`Parsing failure: %v`, err)
|
t.Fatalf(`Parsing failure: %v`, err)
|
||||||
}
|
}
|
||||||
|
expected := []byte("foobar")
|
||||||
expected := defaultProxyHTTPClientTimeout
|
result := opts.MediaProxyPrivateKey()
|
||||||
result := opts.ProxyHTTPClientTimeout()
|
if !bytes.Equal(result, expected) {
|
||||||
|
t.Fatalf(`Unexpected PROXY_PRIVATE_KEY value, got %q instead of %q`, result, expected)
|
||||||
if result != expected {
|
|
||||||
t.Fatalf(`Unexpected PROXY_HTTP_CLIENT_TIMEOUT value, got %d instead of %d`, result, expected)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -51,10 +51,10 @@ const (
|
||||||
defaultCleanupArchiveUnreadDays = 180
|
defaultCleanupArchiveUnreadDays = 180
|
||||||
defaultCleanupArchiveBatchSize = 10000
|
defaultCleanupArchiveBatchSize = 10000
|
||||||
defaultCleanupRemoveSessionsDays = 30
|
defaultCleanupRemoveSessionsDays = 30
|
||||||
defaultProxyHTTPClientTimeout = 120
|
defaultMediaProxyHTTPClientTimeout = 120
|
||||||
defaultProxyOption = "http-only"
|
defaultMediaProxyMode = "http-only"
|
||||||
defaultProxyMediaTypes = "image"
|
defaultMediaResourceTypes = "image"
|
||||||
defaultProxyUrl = ""
|
defaultMediaProxyURL = ""
|
||||||
defaultFilterEntryMaxAgeDays = 0
|
defaultFilterEntryMaxAgeDays = 0
|
||||||
defaultFetchOdyseeWatchTime = false
|
defaultFetchOdyseeWatchTime = false
|
||||||
defaultFetchYouTubeWatchTime = false
|
defaultFetchYouTubeWatchTime = false
|
||||||
|
@ -136,10 +136,10 @@ type Options struct {
|
||||||
createAdmin bool
|
createAdmin bool
|
||||||
adminUsername string
|
adminUsername string
|
||||||
adminPassword string
|
adminPassword string
|
||||||
proxyHTTPClientTimeout int
|
mediaProxyHTTPClientTimeout int
|
||||||
proxyOption string
|
mediaProxyMode string
|
||||||
proxyMediaTypes []string
|
mediaProxyResourceTypes []string
|
||||||
proxyUrl string
|
mediaProxyCustomURL string
|
||||||
fetchOdyseeWatchTime bool
|
fetchOdyseeWatchTime bool
|
||||||
fetchYouTubeWatchTime bool
|
fetchYouTubeWatchTime bool
|
||||||
filterEntryMaxAgeDays int
|
filterEntryMaxAgeDays int
|
||||||
|
@ -167,7 +167,7 @@ type Options struct {
|
||||||
metricsPassword string
|
metricsPassword string
|
||||||
watchdog bool
|
watchdog bool
|
||||||
invidiousInstance string
|
invidiousInstance string
|
||||||
proxyPrivateKey []byte
|
mediaProxyPrivateKey []byte
|
||||||
webAuthn bool
|
webAuthn bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -211,10 +211,10 @@ func NewOptions() *Options {
|
||||||
pollingParsingErrorLimit: defaultPollingParsingErrorLimit,
|
pollingParsingErrorLimit: defaultPollingParsingErrorLimit,
|
||||||
workerPoolSize: defaultWorkerPoolSize,
|
workerPoolSize: defaultWorkerPoolSize,
|
||||||
createAdmin: defaultCreateAdmin,
|
createAdmin: defaultCreateAdmin,
|
||||||
proxyHTTPClientTimeout: defaultProxyHTTPClientTimeout,
|
mediaProxyHTTPClientTimeout: defaultMediaProxyHTTPClientTimeout,
|
||||||
proxyOption: defaultProxyOption,
|
mediaProxyMode: defaultMediaProxyMode,
|
||||||
proxyMediaTypes: []string{defaultProxyMediaTypes},
|
mediaProxyResourceTypes: []string{defaultMediaResourceTypes},
|
||||||
proxyUrl: defaultProxyUrl,
|
mediaProxyCustomURL: defaultMediaProxyURL,
|
||||||
filterEntryMaxAgeDays: defaultFilterEntryMaxAgeDays,
|
filterEntryMaxAgeDays: defaultFilterEntryMaxAgeDays,
|
||||||
fetchOdyseeWatchTime: defaultFetchOdyseeWatchTime,
|
fetchOdyseeWatchTime: defaultFetchOdyseeWatchTime,
|
||||||
fetchYouTubeWatchTime: defaultFetchYouTubeWatchTime,
|
fetchYouTubeWatchTime: defaultFetchYouTubeWatchTime,
|
||||||
|
@ -242,7 +242,7 @@ func NewOptions() *Options {
|
||||||
metricsPassword: defaultMetricsPassword,
|
metricsPassword: defaultMetricsPassword,
|
||||||
watchdog: defaultWatchdog,
|
watchdog: defaultWatchdog,
|
||||||
invidiousInstance: defaultInvidiousInstance,
|
invidiousInstance: defaultInvidiousInstance,
|
||||||
proxyPrivateKey: crypto.GenerateRandomBytes(16),
|
mediaProxyPrivateKey: crypto.GenerateRandomBytes(16),
|
||||||
webAuthn: defaultWebAuthn,
|
webAuthn: defaultWebAuthn,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -492,24 +492,29 @@ func (o *Options) FetchOdyseeWatchTime() bool {
|
||||||
return o.fetchOdyseeWatchTime
|
return o.fetchOdyseeWatchTime
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProxyOption returns "none" to never proxy, "http-only" to proxy non-HTTPS, "all" to always proxy.
|
// MediaProxyMode returns "none" to never proxy, "http-only" to proxy non-HTTPS, "all" to always proxy.
|
||||||
func (o *Options) ProxyOption() string {
|
func (o *Options) MediaProxyMode() string {
|
||||||
return o.proxyOption
|
return o.mediaProxyMode
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProxyMediaTypes returns a slice of media types to proxy.
|
// MediaProxyResourceTypes returns a slice of resource types to proxy.
|
||||||
func (o *Options) ProxyMediaTypes() []string {
|
func (o *Options) MediaProxyResourceTypes() []string {
|
||||||
return o.proxyMediaTypes
|
return o.mediaProxyResourceTypes
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProxyUrl returns a string of a URL to use to proxy image requests
|
// MediaCustomProxyURL returns the custom proxy URL for medias.
|
||||||
func (o *Options) ProxyUrl() string {
|
func (o *Options) MediaCustomProxyURL() string {
|
||||||
return o.proxyUrl
|
return o.mediaProxyCustomURL
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProxyHTTPClientTimeout returns the time limit in seconds before the proxy HTTP client cancel the request.
|
// MediaProxyHTTPClientTimeout returns the time limit in seconds before the proxy HTTP client cancel the request.
|
||||||
func (o *Options) ProxyHTTPClientTimeout() int {
|
func (o *Options) MediaProxyHTTPClientTimeout() int {
|
||||||
return o.proxyHTTPClientTimeout
|
return o.mediaProxyHTTPClientTimeout
|
||||||
|
}
|
||||||
|
|
||||||
|
// MediaProxyPrivateKey returns the private key used by the media proxy.
|
||||||
|
func (o *Options) MediaProxyPrivateKey() []byte {
|
||||||
|
return o.mediaProxyPrivateKey
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasHTTPService returns true if the HTTP service is enabled.
|
// HasHTTPService returns true if the HTTP service is enabled.
|
||||||
|
@ -605,11 +610,6 @@ func (o *Options) InvidiousInstance() string {
|
||||||
return o.invidiousInstance
|
return o.invidiousInstance
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProxyPrivateKey returns the private key used by the media proxy
|
|
||||||
func (o *Options) ProxyPrivateKey() []byte {
|
|
||||||
return o.proxyPrivateKey
|
|
||||||
}
|
|
||||||
|
|
||||||
// WebAuthn returns true if WebAuthn logins are supported
|
// WebAuthn returns true if WebAuthn logins are supported
|
||||||
func (o *Options) WebAuthn() bool {
|
func (o *Options) WebAuthn() bool {
|
||||||
return o.webAuthn
|
return o.webAuthn
|
||||||
|
@ -680,11 +680,11 @@ func (o *Options) SortedOptions(redactSecret bool) []*Option {
|
||||||
"FORCE_REFRESH_INTERVAL": o.forceRefreshInterval,
|
"FORCE_REFRESH_INTERVAL": o.forceRefreshInterval,
|
||||||
"POLLING_PARSING_ERROR_LIMIT": o.pollingParsingErrorLimit,
|
"POLLING_PARSING_ERROR_LIMIT": o.pollingParsingErrorLimit,
|
||||||
"POLLING_SCHEDULER": o.pollingScheduler,
|
"POLLING_SCHEDULER": o.pollingScheduler,
|
||||||
"PROXY_HTTP_CLIENT_TIMEOUT": o.proxyHTTPClientTimeout,
|
"MEDIA_PROXY_HTTP_CLIENT_TIMEOUT": o.mediaProxyHTTPClientTimeout,
|
||||||
"PROXY_MEDIA_TYPES": o.proxyMediaTypes,
|
"MEDIA_PROXY_RESOURCE_TYPES": o.mediaProxyResourceTypes,
|
||||||
"PROXY_OPTION": o.proxyOption,
|
"MEDIA_PROXY_MODE": o.mediaProxyMode,
|
||||||
"PROXY_PRIVATE_KEY": redactSecretValue(string(o.proxyPrivateKey), redactSecret),
|
"MEDIA_PROXY_PRIVATE_KEY": redactSecretValue(string(o.mediaProxyPrivateKey), redactSecret),
|
||||||
"PROXY_URL": o.proxyUrl,
|
"MEDIA_PROXY_CUSTOM_URL": o.mediaProxyCustomURL,
|
||||||
"ROOT_URL": o.rootURL,
|
"ROOT_URL": o.rootURL,
|
||||||
"RUN_MIGRATIONS": o.runMigrations,
|
"RUN_MIGRATIONS": o.runMigrations,
|
||||||
"SCHEDULER_ENTRY_FREQUENCY_MAX_INTERVAL": o.schedulerEntryFrequencyMaxInterval,
|
"SCHEDULER_ENTRY_FREQUENCY_MAX_INTERVAL": o.schedulerEntryFrequencyMaxInterval,
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"log/slog"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -87,6 +88,7 @@ func (p *Parser) parseLines(lines []string) (err error) {
|
||||||
p.opts.logFormat = parsedValue
|
p.opts.logFormat = parsedValue
|
||||||
}
|
}
|
||||||
case "DEBUG":
|
case "DEBUG":
|
||||||
|
slog.Warn("The DEBUG environment variable is deprecated, use LOG_LEVEL instead")
|
||||||
parsedValue := parseBool(value, defaultDebug)
|
parsedValue := parseBool(value, defaultDebug)
|
||||||
if parsedValue {
|
if parsedValue {
|
||||||
p.opts.logLevel = "debug"
|
p.opts.logLevel = "debug"
|
||||||
|
@ -160,20 +162,41 @@ func (p *Parser) parseLines(lines []string) (err error) {
|
||||||
p.opts.schedulerRoundRobinMinInterval = parseInt(value, defaultSchedulerRoundRobinMinInterval)
|
p.opts.schedulerRoundRobinMinInterval = parseInt(value, defaultSchedulerRoundRobinMinInterval)
|
||||||
case "POLLING_PARSING_ERROR_LIMIT":
|
case "POLLING_PARSING_ERROR_LIMIT":
|
||||||
p.opts.pollingParsingErrorLimit = parseInt(value, defaultPollingParsingErrorLimit)
|
p.opts.pollingParsingErrorLimit = parseInt(value, defaultPollingParsingErrorLimit)
|
||||||
// kept for compatibility purpose
|
|
||||||
case "PROXY_IMAGES":
|
case "PROXY_IMAGES":
|
||||||
p.opts.proxyOption = parseString(value, defaultProxyOption)
|
slog.Warn("The PROXY_IMAGES environment variable is deprecated, use MEDIA_PROXY_MODE instead")
|
||||||
|
p.opts.mediaProxyMode = parseString(value, defaultMediaProxyMode)
|
||||||
case "PROXY_HTTP_CLIENT_TIMEOUT":
|
case "PROXY_HTTP_CLIENT_TIMEOUT":
|
||||||
p.opts.proxyHTTPClientTimeout = parseInt(value, defaultProxyHTTPClientTimeout)
|
slog.Warn("The PROXY_HTTP_CLIENT_TIMEOUT environment variable is deprecated, use MEDIA_PROXY_HTTP_CLIENT_TIMEOUT instead")
|
||||||
|
p.opts.mediaProxyHTTPClientTimeout = parseInt(value, defaultMediaProxyHTTPClientTimeout)
|
||||||
|
case "MEDIA_PROXY_HTTP_CLIENT_TIMEOUT":
|
||||||
|
p.opts.mediaProxyHTTPClientTimeout = parseInt(value, defaultMediaProxyHTTPClientTimeout)
|
||||||
case "PROXY_OPTION":
|
case "PROXY_OPTION":
|
||||||
p.opts.proxyOption = parseString(value, defaultProxyOption)
|
slog.Warn("The PROXY_OPTION environment variable is deprecated, use MEDIA_PROXY_MODE instead")
|
||||||
|
p.opts.mediaProxyMode = parseString(value, defaultMediaProxyMode)
|
||||||
|
case "MEDIA_PROXY_MODE":
|
||||||
|
p.opts.mediaProxyMode = parseString(value, defaultMediaProxyMode)
|
||||||
case "PROXY_MEDIA_TYPES":
|
case "PROXY_MEDIA_TYPES":
|
||||||
p.opts.proxyMediaTypes = parseStringList(value, []string{defaultProxyMediaTypes})
|
slog.Warn("The PROXY_MEDIA_TYPES environment variable is deprecated, use MEDIA_PROXY_RESOURCE_TYPES instead")
|
||||||
// kept for compatibility purpose
|
p.opts.mediaProxyResourceTypes = parseStringList(value, []string{defaultMediaResourceTypes})
|
||||||
|
case "MEDIA_PROXY_RESOURCE_TYPES":
|
||||||
|
p.opts.mediaProxyResourceTypes = parseStringList(value, []string{defaultMediaResourceTypes})
|
||||||
case "PROXY_IMAGE_URL":
|
case "PROXY_IMAGE_URL":
|
||||||
p.opts.proxyUrl = parseString(value, defaultProxyUrl)
|
slog.Warn("The PROXY_IMAGE_URL environment variable is deprecated, use MEDIA_PROXY_CUSTOM_URL instead")
|
||||||
|
p.opts.mediaProxyCustomURL = parseString(value, defaultMediaProxyURL)
|
||||||
case "PROXY_URL":
|
case "PROXY_URL":
|
||||||
p.opts.proxyUrl = parseString(value, defaultProxyUrl)
|
slog.Warn("The PROXY_URL environment variable is deprecated, use MEDIA_PROXY_CUSTOM_URL instead")
|
||||||
|
p.opts.mediaProxyCustomURL = parseString(value, defaultMediaProxyURL)
|
||||||
|
case "PROXY_PRIVATE_KEY":
|
||||||
|
slog.Warn("The PROXY_PRIVATE_KEY environment variable is deprecated, use MEDIA_PROXY_PRIVATE_KEY instead")
|
||||||
|
randomKey := make([]byte, 16)
|
||||||
|
rand.Read(randomKey)
|
||||||
|
p.opts.mediaProxyPrivateKey = parseBytes(value, randomKey)
|
||||||
|
case "MEDIA_PROXY_PRIVATE_KEY":
|
||||||
|
randomKey := make([]byte, 16)
|
||||||
|
rand.Read(randomKey)
|
||||||
|
p.opts.mediaProxyPrivateKey = parseBytes(value, randomKey)
|
||||||
|
case "MEDIA_PROXY_CUSTOM_URL":
|
||||||
|
p.opts.mediaProxyCustomURL = parseString(value, defaultMediaProxyURL)
|
||||||
case "CREATE_ADMIN":
|
case "CREATE_ADMIN":
|
||||||
p.opts.createAdmin = parseBool(value, defaultCreateAdmin)
|
p.opts.createAdmin = parseBool(value, defaultCreateAdmin)
|
||||||
case "ADMIN_USERNAME":
|
case "ADMIN_USERNAME":
|
||||||
|
@ -246,10 +269,6 @@ func (p *Parser) parseLines(lines []string) (err error) {
|
||||||
p.opts.watchdog = parseBool(value, defaultWatchdog)
|
p.opts.watchdog = parseBool(value, defaultWatchdog)
|
||||||
case "INVIDIOUS_INSTANCE":
|
case "INVIDIOUS_INSTANCE":
|
||||||
p.opts.invidiousInstance = parseString(value, defaultInvidiousInstance)
|
p.opts.invidiousInstance = parseString(value, defaultInvidiousInstance)
|
||||||
case "PROXY_PRIVATE_KEY":
|
|
||||||
randomKey := make([]byte, 16)
|
|
||||||
rand.Read(randomKey)
|
|
||||||
p.opts.proxyPrivateKey = parseBytes(value, randomKey)
|
|
||||||
case "WEBAUTHN":
|
case "WEBAUTHN":
|
||||||
p.opts.webAuthn = parseBool(value, defaultWebAuthn)
|
p.opts.webAuthn = parseBool(value, defaultWebAuthn)
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,8 +13,8 @@ import (
|
||||||
"miniflux.app/v2/internal/http/request"
|
"miniflux.app/v2/internal/http/request"
|
||||||
"miniflux.app/v2/internal/http/response/json"
|
"miniflux.app/v2/internal/http/response/json"
|
||||||
"miniflux.app/v2/internal/integration"
|
"miniflux.app/v2/internal/integration"
|
||||||
|
"miniflux.app/v2/internal/mediaproxy"
|
||||||
"miniflux.app/v2/internal/model"
|
"miniflux.app/v2/internal/model"
|
||||||
"miniflux.app/v2/internal/proxy"
|
|
||||||
"miniflux.app/v2/internal/storage"
|
"miniflux.app/v2/internal/storage"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
|
@ -324,7 +324,7 @@ func (h *handler) handleItems(w http.ResponseWriter, r *http.Request) {
|
||||||
FeedID: entry.FeedID,
|
FeedID: entry.FeedID,
|
||||||
Title: entry.Title,
|
Title: entry.Title,
|
||||||
Author: entry.Author,
|
Author: entry.Author,
|
||||||
HTML: proxy.AbsoluteProxyRewriter(h.router, r.Host, entry.Content),
|
HTML: mediaproxy.RewriteDocumentWithAbsoluteProxyURL(h.router, r.Host, entry.Content),
|
||||||
URL: entry.URL,
|
URL: entry.URL,
|
||||||
IsSaved: isSaved,
|
IsSaved: isSaved,
|
||||||
IsRead: isRead,
|
IsRead: isRead,
|
||||||
|
|
|
@ -18,8 +18,8 @@ import (
|
||||||
"miniflux.app/v2/internal/http/response/json"
|
"miniflux.app/v2/internal/http/response/json"
|
||||||
"miniflux.app/v2/internal/http/route"
|
"miniflux.app/v2/internal/http/route"
|
||||||
"miniflux.app/v2/internal/integration"
|
"miniflux.app/v2/internal/integration"
|
||||||
|
"miniflux.app/v2/internal/mediaproxy"
|
||||||
"miniflux.app/v2/internal/model"
|
"miniflux.app/v2/internal/model"
|
||||||
"miniflux.app/v2/internal/proxy"
|
|
||||||
"miniflux.app/v2/internal/reader/fetcher"
|
"miniflux.app/v2/internal/reader/fetcher"
|
||||||
mff "miniflux.app/v2/internal/reader/handler"
|
mff "miniflux.app/v2/internal/reader/handler"
|
||||||
mfs "miniflux.app/v2/internal/reader/subscription"
|
mfs "miniflux.app/v2/internal/reader/subscription"
|
||||||
|
@ -1003,14 +1003,14 @@ func (h *handler) streamItemContentsHandler(w http.ResponseWriter, r *http.Reque
|
||||||
categories = append(categories, userStarred)
|
categories = append(categories, userStarred)
|
||||||
}
|
}
|
||||||
|
|
||||||
entry.Content = proxy.AbsoluteProxyRewriter(h.router, r.Host, entry.Content)
|
entry.Content = mediaproxy.RewriteDocumentWithAbsoluteProxyURL(h.router, r.Host, entry.Content)
|
||||||
proxyOption := config.Opts.ProxyOption()
|
proxyOption := config.Opts.MediaProxyMode()
|
||||||
|
|
||||||
for i := range entry.Enclosures {
|
for i := range entry.Enclosures {
|
||||||
if proxyOption == "all" || proxyOption != "none" && !urllib.IsHTTPS(entry.Enclosures[i].URL) {
|
if proxyOption == "all" || proxyOption != "none" && !urllib.IsHTTPS(entry.Enclosures[i].URL) {
|
||||||
for _, mediaType := range config.Opts.ProxyMediaTypes() {
|
for _, mediaType := range config.Opts.MediaProxyResourceTypes() {
|
||||||
if strings.HasPrefix(entry.Enclosures[i].MimeType, mediaType+"/") {
|
if strings.HasPrefix(entry.Enclosures[i].MimeType, mediaType+"/") {
|
||||||
entry.Enclosures[i].URL = proxy.AbsoluteProxifyURL(h.router, r.Host, entry.Enclosures[i].URL)
|
entry.Enclosures[i].URL = mediaproxy.ProxifyAbsoluteURL(h.router, r.Host, entry.Enclosures[i].URL)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
|
// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
package proxy // import "miniflux.app/v2/internal/proxy"
|
package mediaproxy // import "miniflux.app/v2/internal/mediaproxy"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -29,11 +29,11 @@ func TestProxyFilterWithHttpDefault(t *testing.T) {
|
||||||
r.HandleFunc("/proxy/{encodedDigest}/{encodedURL}", func(w http.ResponseWriter, r *http.Request) {}).Name("proxy")
|
r.HandleFunc("/proxy/{encodedDigest}/{encodedURL}", func(w http.ResponseWriter, r *http.Request) {}).Name("proxy")
|
||||||
|
|
||||||
input := `<p><img src="http://website/folder/image.png" alt="Test"/></p>`
|
input := `<p><img src="http://website/folder/image.png" alt="Test"/></p>`
|
||||||
output := ProxyRewriter(r, input)
|
output := RewriteDocumentWithRelativeProxyURL(r, input)
|
||||||
expected := `<p><img src="/proxy/okK5PsdNY8F082UMQEAbLPeUFfbe2WnNfInNmR9T4WA=/aHR0cDovL3dlYnNpdGUvZm9sZGVyL2ltYWdlLnBuZw==" alt="Test"/></p>`
|
expected := `<p><img src="/proxy/okK5PsdNY8F082UMQEAbLPeUFfbe2WnNfInNmR9T4WA=/aHR0cDovL3dlYnNpdGUvZm9sZGVyL2ltYWdlLnBuZw==" alt="Test"/></p>`
|
||||||
|
|
||||||
if expected != output {
|
if expected != output {
|
||||||
t.Errorf(`Not expected output: got "%s" instead of "%s"`, output, expected)
|
t.Errorf(`Not expected output: got %q instead of %q`, output, expected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,11 +53,11 @@ func TestProxyFilterWithHttpsDefault(t *testing.T) {
|
||||||
r.HandleFunc("/proxy/{encodedDigest}/{encodedURL}", func(w http.ResponseWriter, r *http.Request) {}).Name("proxy")
|
r.HandleFunc("/proxy/{encodedDigest}/{encodedURL}", func(w http.ResponseWriter, r *http.Request) {}).Name("proxy")
|
||||||
|
|
||||||
input := `<p><img src="https://website/folder/image.png" alt="Test"/></p>`
|
input := `<p><img src="https://website/folder/image.png" alt="Test"/></p>`
|
||||||
output := ProxyRewriter(r, input)
|
output := RewriteDocumentWithRelativeProxyURL(r, input)
|
||||||
expected := `<p><img src="https://website/folder/image.png" alt="Test"/></p>`
|
expected := `<p><img src="https://website/folder/image.png" alt="Test"/></p>`
|
||||||
|
|
||||||
if expected != output {
|
if expected != output {
|
||||||
t.Errorf(`Not expected output: got "%s" instead of "%s"`, output, expected)
|
t.Errorf(`Not expected output: got %q instead of %q`, output, expected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,11 +76,11 @@ func TestProxyFilterWithHttpNever(t *testing.T) {
|
||||||
r.HandleFunc("/proxy/{encodedDigest}/{encodedURL}", func(w http.ResponseWriter, r *http.Request) {}).Name("proxy")
|
r.HandleFunc("/proxy/{encodedDigest}/{encodedURL}", func(w http.ResponseWriter, r *http.Request) {}).Name("proxy")
|
||||||
|
|
||||||
input := `<p><img src="http://website/folder/image.png" alt="Test"/></p>`
|
input := `<p><img src="http://website/folder/image.png" alt="Test"/></p>`
|
||||||
output := ProxyRewriter(r, input)
|
output := RewriteDocumentWithRelativeProxyURL(r, input)
|
||||||
expected := input
|
expected := input
|
||||||
|
|
||||||
if expected != output {
|
if expected != output {
|
||||||
t.Errorf(`Not expected output: got "%s" instead of "%s"`, output, expected)
|
t.Errorf(`Not expected output: got %q instead of %q`, output, expected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,11 +99,11 @@ func TestProxyFilterWithHttpsNever(t *testing.T) {
|
||||||
r.HandleFunc("/proxy/{encodedDigest}/{encodedURL}", func(w http.ResponseWriter, r *http.Request) {}).Name("proxy")
|
r.HandleFunc("/proxy/{encodedDigest}/{encodedURL}", func(w http.ResponseWriter, r *http.Request) {}).Name("proxy")
|
||||||
|
|
||||||
input := `<p><img src="https://website/folder/image.png" alt="Test"/></p>`
|
input := `<p><img src="https://website/folder/image.png" alt="Test"/></p>`
|
||||||
output := ProxyRewriter(r, input)
|
output := RewriteDocumentWithRelativeProxyURL(r, input)
|
||||||
expected := input
|
expected := input
|
||||||
|
|
||||||
if expected != output {
|
if expected != output {
|
||||||
t.Errorf(`Not expected output: got "%s" instead of "%s"`, output, expected)
|
t.Errorf(`Not expected output: got %q instead of %q`, output, expected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,11 +124,11 @@ func TestProxyFilterWithHttpAlways(t *testing.T) {
|
||||||
r.HandleFunc("/proxy/{encodedDigest}/{encodedURL}", func(w http.ResponseWriter, r *http.Request) {}).Name("proxy")
|
r.HandleFunc("/proxy/{encodedDigest}/{encodedURL}", func(w http.ResponseWriter, r *http.Request) {}).Name("proxy")
|
||||||
|
|
||||||
input := `<p><img src="http://website/folder/image.png" alt="Test"/></p>`
|
input := `<p><img src="http://website/folder/image.png" alt="Test"/></p>`
|
||||||
output := ProxyRewriter(r, input)
|
output := RewriteDocumentWithRelativeProxyURL(r, input)
|
||||||
expected := `<p><img src="/proxy/okK5PsdNY8F082UMQEAbLPeUFfbe2WnNfInNmR9T4WA=/aHR0cDovL3dlYnNpdGUvZm9sZGVyL2ltYWdlLnBuZw==" alt="Test"/></p>`
|
expected := `<p><img src="/proxy/okK5PsdNY8F082UMQEAbLPeUFfbe2WnNfInNmR9T4WA=/aHR0cDovL3dlYnNpdGUvZm9sZGVyL2ltYWdlLnBuZw==" alt="Test"/></p>`
|
||||||
|
|
||||||
if expected != output {
|
if expected != output {
|
||||||
t.Errorf(`Not expected output: got "%s" instead of "%s"`, output, expected)
|
t.Errorf(`Not expected output: got %q instead of %q`, output, expected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -149,11 +149,11 @@ func TestProxyFilterWithHttpsAlways(t *testing.T) {
|
||||||
r.HandleFunc("/proxy/{encodedDigest}/{encodedURL}", func(w http.ResponseWriter, r *http.Request) {}).Name("proxy")
|
r.HandleFunc("/proxy/{encodedDigest}/{encodedURL}", func(w http.ResponseWriter, r *http.Request) {}).Name("proxy")
|
||||||
|
|
||||||
input := `<p><img src="https://website/folder/image.png" alt="Test"/></p>`
|
input := `<p><img src="https://website/folder/image.png" alt="Test"/></p>`
|
||||||
output := ProxyRewriter(r, input)
|
output := RewriteDocumentWithRelativeProxyURL(r, input)
|
||||||
expected := `<p><img src="/proxy/LdPNR1GBDigeeNp2ArUQRyZsVqT_PWLfHGjYFrrWWIY=/aHR0cHM6Ly93ZWJzaXRlL2ZvbGRlci9pbWFnZS5wbmc=" alt="Test"/></p>`
|
expected := `<p><img src="/proxy/LdPNR1GBDigeeNp2ArUQRyZsVqT_PWLfHGjYFrrWWIY=/aHR0cHM6Ly93ZWJzaXRlL2ZvbGRlci9pbWFnZS5wbmc=" alt="Test"/></p>`
|
||||||
|
|
||||||
if expected != output {
|
if expected != output {
|
||||||
t.Errorf(`Not expected output: got "%s" instead of "%s"`, output, expected)
|
t.Errorf(`Not expected output: got %q instead of %q`, output, expected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -174,11 +174,62 @@ func TestAbsoluteProxyFilterWithHttpsAlways(t *testing.T) {
|
||||||
r.HandleFunc("/proxy/{encodedDigest}/{encodedURL}", func(w http.ResponseWriter, r *http.Request) {}).Name("proxy")
|
r.HandleFunc("/proxy/{encodedDigest}/{encodedURL}", func(w http.ResponseWriter, r *http.Request) {}).Name("proxy")
|
||||||
|
|
||||||
input := `<p><img src="https://website/folder/image.png" alt="Test"/></p>`
|
input := `<p><img src="https://website/folder/image.png" alt="Test"/></p>`
|
||||||
output := AbsoluteProxyRewriter(r, "localhost", input)
|
output := RewriteDocumentWithAbsoluteProxyURL(r, "localhost", input)
|
||||||
expected := `<p><img src="http://localhost/proxy/LdPNR1GBDigeeNp2ArUQRyZsVqT_PWLfHGjYFrrWWIY=/aHR0cHM6Ly93ZWJzaXRlL2ZvbGRlci9pbWFnZS5wbmc=" alt="Test"/></p>`
|
expected := `<p><img src="http://localhost/proxy/LdPNR1GBDigeeNp2ArUQRyZsVqT_PWLfHGjYFrrWWIY=/aHR0cHM6Ly93ZWJzaXRlL2ZvbGRlci9pbWFnZS5wbmc=" alt="Test"/></p>`
|
||||||
|
|
||||||
if expected != output {
|
if expected != output {
|
||||||
t.Errorf(`Not expected output: got "%s" instead of "%s"`, output, expected)
|
t.Errorf(`Not expected output: got %q instead of %q`, output, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAbsoluteProxyFilterWithHttpsScheme(t *testing.T) {
|
||||||
|
os.Clearenv()
|
||||||
|
os.Setenv("PROXY_OPTION", "all")
|
||||||
|
os.Setenv("PROXY_MEDIA_TYPES", "image")
|
||||||
|
os.Setenv("PROXY_PRIVATE_KEY", "test")
|
||||||
|
os.Setenv("HTTPS", "1")
|
||||||
|
|
||||||
|
var err error
|
||||||
|
parser := config.NewParser()
|
||||||
|
config.Opts, err = parser.ParseEnvironmentVariables()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf(`Parsing failure: %v`, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
r := mux.NewRouter()
|
||||||
|
r.HandleFunc("/proxy/{encodedDigest}/{encodedURL}", func(w http.ResponseWriter, r *http.Request) {}).Name("proxy")
|
||||||
|
|
||||||
|
input := `<p><img src="https://website/folder/image.png" alt="Test"/></p>`
|
||||||
|
output := RewriteDocumentWithAbsoluteProxyURL(r, "localhost", input)
|
||||||
|
expected := `<p><img src="https://localhost/proxy/LdPNR1GBDigeeNp2ArUQRyZsVqT_PWLfHGjYFrrWWIY=/aHR0cHM6Ly93ZWJzaXRlL2ZvbGRlci9pbWFnZS5wbmc=" alt="Test"/></p>`
|
||||||
|
|
||||||
|
if expected != output {
|
||||||
|
t.Errorf(`Not expected output: got %q instead of %q`, output, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAbsoluteProxyFilterWithHttpsAlwaysAndAudioTag(t *testing.T) {
|
||||||
|
os.Clearenv()
|
||||||
|
os.Setenv("PROXY_OPTION", "all")
|
||||||
|
os.Setenv("PROXY_MEDIA_TYPES", "audio")
|
||||||
|
os.Setenv("PROXY_PRIVATE_KEY", "test")
|
||||||
|
|
||||||
|
var err error
|
||||||
|
parser := config.NewParser()
|
||||||
|
config.Opts, err = parser.ParseEnvironmentVariables()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf(`Parsing failure: %v`, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
r := mux.NewRouter()
|
||||||
|
r.HandleFunc("/proxy/{encodedDigest}/{encodedURL}", func(w http.ResponseWriter, r *http.Request) {}).Name("proxy")
|
||||||
|
|
||||||
|
input := `<audio src="https://website/folder/audio.mp3"></audio>`
|
||||||
|
output := RewriteDocumentWithAbsoluteProxyURL(r, "localhost", input)
|
||||||
|
expected := `<audio src="http://localhost/proxy/EmBTvmU5B17wGuONkeknkptYopW_Tl6Y6_W8oYbN_Xs=/aHR0cHM6Ly93ZWJzaXRlL2ZvbGRlci9hdWRpby5tcDM="></audio>`
|
||||||
|
|
||||||
|
if expected != output {
|
||||||
|
t.Errorf(`Not expected output: got %q instead of %q`, output, expected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -199,11 +250,11 @@ func TestProxyFilterWithHttpsAlwaysAndCustomProxyServer(t *testing.T) {
|
||||||
r.HandleFunc("/proxy/{encodedDigest}/{encodedURL}", func(w http.ResponseWriter, r *http.Request) {}).Name("proxy")
|
r.HandleFunc("/proxy/{encodedDigest}/{encodedURL}", func(w http.ResponseWriter, r *http.Request) {}).Name("proxy")
|
||||||
|
|
||||||
input := `<p><img src="https://website/folder/image.png" alt="Test"/></p>`
|
input := `<p><img src="https://website/folder/image.png" alt="Test"/></p>`
|
||||||
output := ProxyRewriter(r, input)
|
output := RewriteDocumentWithRelativeProxyURL(r, input)
|
||||||
expected := `<p><img src="https://proxy-example/proxy/aHR0cHM6Ly93ZWJzaXRlL2ZvbGRlci9pbWFnZS5wbmc=" alt="Test"/></p>`
|
expected := `<p><img src="https://proxy-example/proxy/aHR0cHM6Ly93ZWJzaXRlL2ZvbGRlci9pbWFnZS5wbmc=" alt="Test"/></p>`
|
||||||
|
|
||||||
if expected != output {
|
if expected != output {
|
||||||
t.Errorf(`Not expected output: got "%s" instead of "%s"`, output, expected)
|
t.Errorf(`Not expected output: got %q instead of %q`, output, expected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -224,19 +275,19 @@ func TestProxyFilterWithHttpsAlwaysAndIncorrectCustomProxyServer(t *testing.T) {
|
||||||
r.HandleFunc("/proxy/{encodedDigest}/{encodedURL}", func(w http.ResponseWriter, r *http.Request) {}).Name("proxy")
|
r.HandleFunc("/proxy/{encodedDigest}/{encodedURL}", func(w http.ResponseWriter, r *http.Request) {}).Name("proxy")
|
||||||
|
|
||||||
input := `<p><img src="https://website/folder/image.png" alt="Test"/></p>`
|
input := `<p><img src="https://website/folder/image.png" alt="Test"/></p>`
|
||||||
output := ProxyRewriter(r, input)
|
output := RewriteDocumentWithRelativeProxyURL(r, input)
|
||||||
expected := `<p><img src="https://website/folder/image.png" alt="Test"/></p>`
|
expected := `<p><img src="https://website/folder/image.png" alt="Test"/></p>`
|
||||||
|
|
||||||
if expected != output {
|
if expected != output {
|
||||||
t.Errorf(`Not expected output: got "%s" instead of "%s"`, output, expected)
|
t.Errorf(`Not expected output: got %q instead of %q`, output, expected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAbsoluteProxyFilterWithHttpsAlwaysAndCustomProxyServer(t *testing.T) {
|
func TestAbsoluteProxyFilterWithHttpsAlwaysAndCustomProxyServer(t *testing.T) {
|
||||||
os.Clearenv()
|
os.Clearenv()
|
||||||
os.Setenv("PROXY_OPTION", "all")
|
os.Setenv("MEDIA_PROXY_MODE", "all")
|
||||||
os.Setenv("PROXY_MEDIA_TYPES", "image")
|
os.Setenv("MEDIA_PROXY_RESOURCE_TYPES", "image")
|
||||||
os.Setenv("PROXY_URL", "https://proxy-example/proxy")
|
os.Setenv("MEDIA_PROXY_CUSTOM_URL", "https://proxy-example/proxy")
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
parser := config.NewParser()
|
parser := config.NewParser()
|
||||||
|
@ -249,11 +300,11 @@ func TestAbsoluteProxyFilterWithHttpsAlwaysAndCustomProxyServer(t *testing.T) {
|
||||||
r.HandleFunc("/proxy/{encodedDigest}/{encodedURL}", func(w http.ResponseWriter, r *http.Request) {}).Name("proxy")
|
r.HandleFunc("/proxy/{encodedDigest}/{encodedURL}", func(w http.ResponseWriter, r *http.Request) {}).Name("proxy")
|
||||||
|
|
||||||
input := `<p><img src="https://website/folder/image.png" alt="Test"/></p>`
|
input := `<p><img src="https://website/folder/image.png" alt="Test"/></p>`
|
||||||
output := ProxyRewriter(r, input)
|
output := RewriteDocumentWithAbsoluteProxyURL(r, "localhost", input)
|
||||||
expected := `<p><img src="https://proxy-example/proxy/aHR0cHM6Ly93ZWJzaXRlL2ZvbGRlci9pbWFnZS5wbmc=" alt="Test"/></p>`
|
expected := `<p><img src="https://proxy-example/proxy/aHR0cHM6Ly93ZWJzaXRlL2ZvbGRlci9pbWFnZS5wbmc=" alt="Test"/></p>`
|
||||||
|
|
||||||
if expected != output {
|
if expected != output {
|
||||||
t.Errorf(`Not expected output: got "%s" instead of "%s"`, output, expected)
|
t.Errorf(`Not expected output: got %q instead of %q`, output, expected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -273,11 +324,11 @@ func TestProxyFilterWithHttpInvalid(t *testing.T) {
|
||||||
r.HandleFunc("/proxy/{encodedDigest}/{encodedURL}", func(w http.ResponseWriter, r *http.Request) {}).Name("proxy")
|
r.HandleFunc("/proxy/{encodedDigest}/{encodedURL}", func(w http.ResponseWriter, r *http.Request) {}).Name("proxy")
|
||||||
|
|
||||||
input := `<p><img src="http://website/folder/image.png" alt="Test"/></p>`
|
input := `<p><img src="http://website/folder/image.png" alt="Test"/></p>`
|
||||||
output := ProxyRewriter(r, input)
|
output := RewriteDocumentWithRelativeProxyURL(r, input)
|
||||||
expected := `<p><img src="/proxy/okK5PsdNY8F082UMQEAbLPeUFfbe2WnNfInNmR9T4WA=/aHR0cDovL3dlYnNpdGUvZm9sZGVyL2ltYWdlLnBuZw==" alt="Test"/></p>`
|
expected := `<p><img src="/proxy/okK5PsdNY8F082UMQEAbLPeUFfbe2WnNfInNmR9T4WA=/aHR0cDovL3dlYnNpdGUvZm9sZGVyL2ltYWdlLnBuZw==" alt="Test"/></p>`
|
||||||
|
|
||||||
if expected != output {
|
if expected != output {
|
||||||
t.Errorf(`Not expected output: got "%s" instead of "%s"`, output, expected)
|
t.Errorf(`Not expected output: got %q instead of %q`, output, expected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -297,11 +348,11 @@ func TestProxyFilterWithHttpsInvalid(t *testing.T) {
|
||||||
r.HandleFunc("/proxy/{encodedDigest}/{encodedURL}", func(w http.ResponseWriter, r *http.Request) {}).Name("proxy")
|
r.HandleFunc("/proxy/{encodedDigest}/{encodedURL}", func(w http.ResponseWriter, r *http.Request) {}).Name("proxy")
|
||||||
|
|
||||||
input := `<p><img src="https://website/folder/image.png" alt="Test"/></p>`
|
input := `<p><img src="https://website/folder/image.png" alt="Test"/></p>`
|
||||||
output := ProxyRewriter(r, input)
|
output := RewriteDocumentWithRelativeProxyURL(r, input)
|
||||||
expected := `<p><img src="https://website/folder/image.png" alt="Test"/></p>`
|
expected := `<p><img src="https://website/folder/image.png" alt="Test"/></p>`
|
||||||
|
|
||||||
if expected != output {
|
if expected != output {
|
||||||
t.Errorf(`Not expected output: got "%s" instead of "%s"`, output, expected)
|
t.Errorf(`Not expected output: got %q instead of %q`, output, expected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -323,7 +374,7 @@ func TestProxyFilterWithSrcset(t *testing.T) {
|
||||||
|
|
||||||
input := `<p><img src="http://website/folder/image.png" srcset="http://website/folder/image2.png 656w, http://website/folder/image3.png 360w" alt="test"></p>`
|
input := `<p><img src="http://website/folder/image.png" srcset="http://website/folder/image2.png 656w, http://website/folder/image3.png 360w" alt="test"></p>`
|
||||||
expected := `<p><img src="/proxy/okK5PsdNY8F082UMQEAbLPeUFfbe2WnNfInNmR9T4WA=/aHR0cDovL3dlYnNpdGUvZm9sZGVyL2ltYWdlLnBuZw==" srcset="/proxy/aY5Hb4urDnUCly2vTJ7ExQeeaVS-52O7kjUr2v9VrAs=/aHR0cDovL3dlYnNpdGUvZm9sZGVyL2ltYWdlMi5wbmc= 656w, /proxy/QgAmrJWiAud_nNAsz3F8OTxaIofwAiO36EDzH_YfMzo=/aHR0cDovL3dlYnNpdGUvZm9sZGVyL2ltYWdlMy5wbmc= 360w" alt="test"/></p>`
|
expected := `<p><img src="/proxy/okK5PsdNY8F082UMQEAbLPeUFfbe2WnNfInNmR9T4WA=/aHR0cDovL3dlYnNpdGUvZm9sZGVyL2ltYWdlLnBuZw==" srcset="/proxy/aY5Hb4urDnUCly2vTJ7ExQeeaVS-52O7kjUr2v9VrAs=/aHR0cDovL3dlYnNpdGUvZm9sZGVyL2ltYWdlMi5wbmc= 656w, /proxy/QgAmrJWiAud_nNAsz3F8OTxaIofwAiO36EDzH_YfMzo=/aHR0cDovL3dlYnNpdGUvZm9sZGVyL2ltYWdlMy5wbmc= 360w" alt="test"/></p>`
|
||||||
output := ProxyRewriter(r, input)
|
output := RewriteDocumentWithRelativeProxyURL(r, input)
|
||||||
|
|
||||||
if expected != output {
|
if expected != output {
|
||||||
t.Errorf(`Not expected output: got %s`, output)
|
t.Errorf(`Not expected output: got %s`, output)
|
||||||
|
@ -348,7 +399,7 @@ func TestProxyFilterWithEmptySrcset(t *testing.T) {
|
||||||
|
|
||||||
input := `<p><img src="http://website/folder/image.png" srcset="" alt="test"></p>`
|
input := `<p><img src="http://website/folder/image.png" srcset="" alt="test"></p>`
|
||||||
expected := `<p><img src="/proxy/okK5PsdNY8F082UMQEAbLPeUFfbe2WnNfInNmR9T4WA=/aHR0cDovL3dlYnNpdGUvZm9sZGVyL2ltYWdlLnBuZw==" srcset="" alt="test"/></p>`
|
expected := `<p><img src="/proxy/okK5PsdNY8F082UMQEAbLPeUFfbe2WnNfInNmR9T4WA=/aHR0cDovL3dlYnNpdGUvZm9sZGVyL2ltYWdlLnBuZw==" srcset="" alt="test"/></p>`
|
||||||
output := ProxyRewriter(r, input)
|
output := RewriteDocumentWithRelativeProxyURL(r, input)
|
||||||
|
|
||||||
if expected != output {
|
if expected != output {
|
||||||
t.Errorf(`Not expected output: got %s`, output)
|
t.Errorf(`Not expected output: got %s`, output)
|
||||||
|
@ -373,7 +424,7 @@ func TestProxyFilterWithPictureSource(t *testing.T) {
|
||||||
|
|
||||||
input := `<picture><source srcset="http://website/folder/image2.png 656w, http://website/folder/image3.png 360w, https://website/some,image.png 2x"></picture>`
|
input := `<picture><source srcset="http://website/folder/image2.png 656w, http://website/folder/image3.png 360w, https://website/some,image.png 2x"></picture>`
|
||||||
expected := `<picture><source srcset="/proxy/aY5Hb4urDnUCly2vTJ7ExQeeaVS-52O7kjUr2v9VrAs=/aHR0cDovL3dlYnNpdGUvZm9sZGVyL2ltYWdlMi5wbmc= 656w, /proxy/QgAmrJWiAud_nNAsz3F8OTxaIofwAiO36EDzH_YfMzo=/aHR0cDovL3dlYnNpdGUvZm9sZGVyL2ltYWdlMy5wbmc= 360w, /proxy/ZIw0hv8WhSTls5aSqhnFaCXlUrKIqTnBRaY0-NaLnds=/aHR0cHM6Ly93ZWJzaXRlL3NvbWUsaW1hZ2UucG5n 2x"/></picture>`
|
expected := `<picture><source srcset="/proxy/aY5Hb4urDnUCly2vTJ7ExQeeaVS-52O7kjUr2v9VrAs=/aHR0cDovL3dlYnNpdGUvZm9sZGVyL2ltYWdlMi5wbmc= 656w, /proxy/QgAmrJWiAud_nNAsz3F8OTxaIofwAiO36EDzH_YfMzo=/aHR0cDovL3dlYnNpdGUvZm9sZGVyL2ltYWdlMy5wbmc= 360w, /proxy/ZIw0hv8WhSTls5aSqhnFaCXlUrKIqTnBRaY0-NaLnds=/aHR0cHM6Ly93ZWJzaXRlL3NvbWUsaW1hZ2UucG5n 2x"/></picture>`
|
||||||
output := ProxyRewriter(r, input)
|
output := RewriteDocumentWithRelativeProxyURL(r, input)
|
||||||
|
|
||||||
if expected != output {
|
if expected != output {
|
||||||
t.Errorf(`Not expected output: got %s`, output)
|
t.Errorf(`Not expected output: got %s`, output)
|
||||||
|
@ -398,7 +449,7 @@ func TestProxyFilterOnlyNonHTTPWithPictureSource(t *testing.T) {
|
||||||
|
|
||||||
input := `<picture><source srcset="http://website/folder/image2.png 656w, https://website/some,image.png 2x"></picture>`
|
input := `<picture><source srcset="http://website/folder/image2.png 656w, https://website/some,image.png 2x"></picture>`
|
||||||
expected := `<picture><source srcset="/proxy/aY5Hb4urDnUCly2vTJ7ExQeeaVS-52O7kjUr2v9VrAs=/aHR0cDovL3dlYnNpdGUvZm9sZGVyL2ltYWdlMi5wbmc= 656w, https://website/some,image.png 2x"/></picture>`
|
expected := `<picture><source srcset="/proxy/aY5Hb4urDnUCly2vTJ7ExQeeaVS-52O7kjUr2v9VrAs=/aHR0cDovL3dlYnNpdGUvZm9sZGVyL2ltYWdlMi5wbmc= 656w, https://website/some,image.png 2x"/></picture>`
|
||||||
output := ProxyRewriter(r, input)
|
output := RewriteDocumentWithRelativeProxyURL(r, input)
|
||||||
|
|
||||||
if expected != output {
|
if expected != output {
|
||||||
t.Errorf(`Not expected output: got %s`, output)
|
t.Errorf(`Not expected output: got %s`, output)
|
||||||
|
@ -422,7 +473,7 @@ func TestProxyWithImageDataURL(t *testing.T) {
|
||||||
|
|
||||||
input := `<img src="data:image/gif;base64,test">`
|
input := `<img src="data:image/gif;base64,test">`
|
||||||
expected := `<img src="data:image/gif;base64,test"/>`
|
expected := `<img src="data:image/gif;base64,test"/>`
|
||||||
output := ProxyRewriter(r, input)
|
output := RewriteDocumentWithRelativeProxyURL(r, input)
|
||||||
|
|
||||||
if expected != output {
|
if expected != output {
|
||||||
t.Errorf(`Not expected output: got %s`, output)
|
t.Errorf(`Not expected output: got %s`, output)
|
||||||
|
@ -446,7 +497,7 @@ func TestProxyWithImageSourceDataURL(t *testing.T) {
|
||||||
|
|
||||||
input := `<picture><source srcset="data:image/gif;base64,test"/></picture>`
|
input := `<picture><source srcset="data:image/gif;base64,test"/></picture>`
|
||||||
expected := `<picture><source srcset="data:image/gif;base64,test"/></picture>`
|
expected := `<picture><source srcset="data:image/gif;base64,test"/></picture>`
|
||||||
output := ProxyRewriter(r, input)
|
output := RewriteDocumentWithRelativeProxyURL(r, input)
|
||||||
|
|
||||||
if expected != output {
|
if expected != output {
|
||||||
t.Errorf(`Not expected output: got %s`, output)
|
t.Errorf(`Not expected output: got %s`, output)
|
||||||
|
@ -471,7 +522,7 @@ func TestProxyFilterWithVideo(t *testing.T) {
|
||||||
|
|
||||||
input := `<video poster="https://example.com/img.png" src="https://example.com/video.mp4"></video>`
|
input := `<video poster="https://example.com/img.png" src="https://example.com/video.mp4"></video>`
|
||||||
expected := `<video poster="/proxy/aDFfroYL57q5XsojIzATT6OYUCkuVSPXYJQAVrotnLw=/aHR0cHM6Ly9leGFtcGxlLmNvbS9pbWcucG5n" src="/proxy/0y3LR8zlx8S8qJkj1qWFOO6x3a-5yf2gLWjGIJV5yyc=/aHR0cHM6Ly9leGFtcGxlLmNvbS92aWRlby5tcDQ="></video>`
|
expected := `<video poster="/proxy/aDFfroYL57q5XsojIzATT6OYUCkuVSPXYJQAVrotnLw=/aHR0cHM6Ly9leGFtcGxlLmNvbS9pbWcucG5n" src="/proxy/0y3LR8zlx8S8qJkj1qWFOO6x3a-5yf2gLWjGIJV5yyc=/aHR0cHM6Ly9leGFtcGxlLmNvbS92aWRlby5tcDQ="></video>`
|
||||||
output := ProxyRewriter(r, input)
|
output := RewriteDocumentWithRelativeProxyURL(r, input)
|
||||||
|
|
||||||
if expected != output {
|
if expected != output {
|
||||||
t.Errorf(`Not expected output: got %s`, output)
|
t.Errorf(`Not expected output: got %s`, output)
|
||||||
|
@ -496,7 +547,7 @@ func TestProxyFilterVideoPoster(t *testing.T) {
|
||||||
|
|
||||||
input := `<video poster="https://example.com/img.png" src="https://example.com/video.mp4"></video>`
|
input := `<video poster="https://example.com/img.png" src="https://example.com/video.mp4"></video>`
|
||||||
expected := `<video poster="/proxy/aDFfroYL57q5XsojIzATT6OYUCkuVSPXYJQAVrotnLw=/aHR0cHM6Ly9leGFtcGxlLmNvbS9pbWcucG5n" src="https://example.com/video.mp4"></video>`
|
expected := `<video poster="/proxy/aDFfroYL57q5XsojIzATT6OYUCkuVSPXYJQAVrotnLw=/aHR0cHM6Ly9leGFtcGxlLmNvbS9pbWcucG5n" src="https://example.com/video.mp4"></video>`
|
||||||
output := ProxyRewriter(r, input)
|
output := RewriteDocumentWithRelativeProxyURL(r, input)
|
||||||
|
|
||||||
if expected != output {
|
if expected != output {
|
||||||
t.Errorf(`Not expected output: got %s`, output)
|
t.Errorf(`Not expected output: got %s`, output)
|
|
@ -1,7 +1,7 @@
|
||||||
// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
|
// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
package proxy // import "miniflux.app/v2/internal/proxy"
|
package mediaproxy // import "miniflux.app/v2/internal/mediaproxy"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -16,31 +16,29 @@ import (
|
||||||
|
|
||||||
type urlProxyRewriter func(router *mux.Router, url string) string
|
type urlProxyRewriter func(router *mux.Router, url string) string
|
||||||
|
|
||||||
// ProxyRewriter replaces media URLs with internal proxy URLs.
|
func RewriteDocumentWithRelativeProxyURL(router *mux.Router, htmlDocument string) string {
|
||||||
func ProxyRewriter(router *mux.Router, data string) string {
|
return genericProxyRewriter(router, ProxifyRelativeURL, htmlDocument)
|
||||||
return genericProxyRewriter(router, ProxifyURL, data)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// AbsoluteProxyRewriter do the same as ProxyRewriter except it uses absolute URLs.
|
func RewriteDocumentWithAbsoluteProxyURL(router *mux.Router, host, htmlDocument string) string {
|
||||||
func AbsoluteProxyRewriter(router *mux.Router, host, data string) string {
|
|
||||||
proxifyFunction := func(router *mux.Router, url string) string {
|
proxifyFunction := func(router *mux.Router, url string) string {
|
||||||
return AbsoluteProxifyURL(router, host, url)
|
return ProxifyAbsoluteURL(router, host, url)
|
||||||
}
|
}
|
||||||
return genericProxyRewriter(router, proxifyFunction, data)
|
return genericProxyRewriter(router, proxifyFunction, htmlDocument)
|
||||||
}
|
}
|
||||||
|
|
||||||
func genericProxyRewriter(router *mux.Router, proxifyFunction urlProxyRewriter, data string) string {
|
func genericProxyRewriter(router *mux.Router, proxifyFunction urlProxyRewriter, htmlDocument string) string {
|
||||||
proxyOption := config.Opts.ProxyOption()
|
proxyOption := config.Opts.MediaProxyMode()
|
||||||
if proxyOption == "none" {
|
if proxyOption == "none" {
|
||||||
return data
|
return htmlDocument
|
||||||
}
|
}
|
||||||
|
|
||||||
doc, err := goquery.NewDocumentFromReader(strings.NewReader(data))
|
doc, err := goquery.NewDocumentFromReader(strings.NewReader(htmlDocument))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return data
|
return htmlDocument
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, mediaType := range config.Opts.ProxyMediaTypes() {
|
for _, mediaType := range config.Opts.MediaProxyResourceTypes() {
|
||||||
switch mediaType {
|
switch mediaType {
|
||||||
case "image":
|
case "image":
|
||||||
doc.Find("img, picture source").Each(func(i int, img *goquery.Selection) {
|
doc.Find("img, picture source").Each(func(i int, img *goquery.Selection) {
|
||||||
|
@ -91,7 +89,7 @@ func genericProxyRewriter(router *mux.Router, proxifyFunction urlProxyRewriter,
|
||||||
|
|
||||||
output, err := doc.Find("body").First().Html()
|
output, err := doc.Find("body").First().Html()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return data
|
return htmlDocument
|
||||||
}
|
}
|
||||||
|
|
||||||
return output
|
return output
|
|
@ -1,7 +1,7 @@
|
||||||
// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
|
// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
package proxy // import "miniflux.app/v2/internal/proxy"
|
package mediaproxy // import "miniflux.app/v2/internal/mediaproxy"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/hmac"
|
"crypto/hmac"
|
||||||
|
@ -18,33 +18,31 @@ import (
|
||||||
"miniflux.app/v2/internal/config"
|
"miniflux.app/v2/internal/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ProxifyURL generates a relative URL for a proxified resource.
|
func ProxifyRelativeURL(router *mux.Router, mediaURL string) string {
|
||||||
func ProxifyURL(router *mux.Router, mediaURL string) string {
|
|
||||||
if mediaURL == "" {
|
if mediaURL == "" {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
if customProxyURL := config.Opts.ProxyUrl(); customProxyURL != "" {
|
if customProxyURL := config.Opts.MediaCustomProxyURL(); customProxyURL != "" {
|
||||||
return ProxifyURLWithCustomProxy(mediaURL, customProxyURL)
|
return proxifyURLWithCustomProxy(mediaURL, customProxyURL)
|
||||||
}
|
}
|
||||||
|
|
||||||
mac := hmac.New(sha256.New, config.Opts.ProxyPrivateKey())
|
mac := hmac.New(sha256.New, config.Opts.MediaProxyPrivateKey())
|
||||||
mac.Write([]byte(mediaURL))
|
mac.Write([]byte(mediaURL))
|
||||||
digest := mac.Sum(nil)
|
digest := mac.Sum(nil)
|
||||||
return route.Path(router, "proxy", "encodedDigest", base64.URLEncoding.EncodeToString(digest), "encodedURL", base64.URLEncoding.EncodeToString([]byte(mediaURL)))
|
return route.Path(router, "proxy", "encodedDigest", base64.URLEncoding.EncodeToString(digest), "encodedURL", base64.URLEncoding.EncodeToString([]byte(mediaURL)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// AbsoluteProxifyURL generates an absolute URL for a proxified resource.
|
func ProxifyAbsoluteURL(router *mux.Router, host, mediaURL string) string {
|
||||||
func AbsoluteProxifyURL(router *mux.Router, host, mediaURL string) string {
|
|
||||||
if mediaURL == "" {
|
if mediaURL == "" {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
if customProxyURL := config.Opts.ProxyUrl(); customProxyURL != "" {
|
if customProxyURL := config.Opts.MediaCustomProxyURL(); customProxyURL != "" {
|
||||||
return ProxifyURLWithCustomProxy(mediaURL, customProxyURL)
|
return proxifyURLWithCustomProxy(mediaURL, customProxyURL)
|
||||||
}
|
}
|
||||||
|
|
||||||
proxifiedUrl := ProxifyURL(router, mediaURL)
|
proxifiedUrl := ProxifyRelativeURL(router, mediaURL)
|
||||||
scheme := "http"
|
scheme := "http"
|
||||||
if config.Opts.HTTPS {
|
if config.Opts.HTTPS {
|
||||||
scheme = "https"
|
scheme = "https"
|
||||||
|
@ -53,7 +51,7 @@ func AbsoluteProxifyURL(router *mux.Router, host, mediaURL string) string {
|
||||||
return scheme + "://" + host + proxifiedUrl
|
return scheme + "://" + host + proxifiedUrl
|
||||||
}
|
}
|
||||||
|
|
||||||
func ProxifyURLWithCustomProxy(mediaURL, customProxyURL string) string {
|
func proxifyURLWithCustomProxy(mediaURL, customProxyURL string) string {
|
||||||
if customProxyURL == "" {
|
if customProxyURL == "" {
|
||||||
return mediaURL
|
return mediaURL
|
||||||
}
|
}
|
|
@ -16,8 +16,8 @@ import (
|
||||||
"miniflux.app/v2/internal/crypto"
|
"miniflux.app/v2/internal/crypto"
|
||||||
"miniflux.app/v2/internal/http/route"
|
"miniflux.app/v2/internal/http/route"
|
||||||
"miniflux.app/v2/internal/locale"
|
"miniflux.app/v2/internal/locale"
|
||||||
|
"miniflux.app/v2/internal/mediaproxy"
|
||||||
"miniflux.app/v2/internal/model"
|
"miniflux.app/v2/internal/model"
|
||||||
"miniflux.app/v2/internal/proxy"
|
|
||||||
"miniflux.app/v2/internal/timezone"
|
"miniflux.app/v2/internal/timezone"
|
||||||
"miniflux.app/v2/internal/urllib"
|
"miniflux.app/v2/internal/urllib"
|
||||||
|
|
||||||
|
@ -57,19 +57,19 @@ func (f *funcMap) Map() template.FuncMap {
|
||||||
return template.HTML(str)
|
return template.HTML(str)
|
||||||
},
|
},
|
||||||
"proxyFilter": func(data string) string {
|
"proxyFilter": func(data string) string {
|
||||||
return proxy.ProxyRewriter(f.router, data)
|
return mediaproxy.RewriteDocumentWithRelativeProxyURL(f.router, data)
|
||||||
},
|
},
|
||||||
"proxyURL": func(link string) string {
|
"proxyURL": func(link string) string {
|
||||||
proxyOption := config.Opts.ProxyOption()
|
mediaProxyMode := config.Opts.MediaProxyMode()
|
||||||
|
|
||||||
if proxyOption == "all" || (proxyOption != "none" && !urllib.IsHTTPS(link)) {
|
if mediaProxyMode == "all" || (mediaProxyMode != "none" && !urllib.IsHTTPS(link)) {
|
||||||
return proxy.ProxifyURL(f.router, link)
|
return mediaproxy.ProxifyRelativeURL(f.router, link)
|
||||||
}
|
}
|
||||||
|
|
||||||
return link
|
return link
|
||||||
},
|
},
|
||||||
"mustBeProxyfied": func(mediaType string) bool {
|
"mustBeProxyfied": func(mediaType string) bool {
|
||||||
return slices.Contains(config.Opts.ProxyMediaTypes(), mediaType)
|
return slices.Contains(config.Opts.MediaProxyResourceTypes(), mediaType)
|
||||||
},
|
},
|
||||||
"domain": urllib.Domain,
|
"domain": urllib.Domain,
|
||||||
"hasPrefix": strings.HasPrefix,
|
"hasPrefix": strings.HasPrefix,
|
||||||
|
|
|
@ -9,8 +9,8 @@ import (
|
||||||
"miniflux.app/v2/internal/http/request"
|
"miniflux.app/v2/internal/http/request"
|
||||||
"miniflux.app/v2/internal/http/response/json"
|
"miniflux.app/v2/internal/http/response/json"
|
||||||
"miniflux.app/v2/internal/locale"
|
"miniflux.app/v2/internal/locale"
|
||||||
|
"miniflux.app/v2/internal/mediaproxy"
|
||||||
"miniflux.app/v2/internal/model"
|
"miniflux.app/v2/internal/model"
|
||||||
"miniflux.app/v2/internal/proxy"
|
|
||||||
"miniflux.app/v2/internal/reader/processor"
|
"miniflux.app/v2/internal/reader/processor"
|
||||||
"miniflux.app/v2/internal/storage"
|
"miniflux.app/v2/internal/storage"
|
||||||
)
|
)
|
||||||
|
@ -65,5 +65,5 @@ func (h *handler) fetchContent(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
readingTime := locale.NewPrinter(user.Language).Plural("entry.estimated_reading_time", entry.ReadingTime, entry.ReadingTime)
|
readingTime := locale.NewPrinter(user.Language).Plural("entry.estimated_reading_time", entry.ReadingTime, entry.ReadingTime)
|
||||||
|
|
||||||
json.OK(w, r, map[string]string{"content": proxy.ProxyRewriter(h.router, entry.Content), "reading_time": readingTime})
|
json.OK(w, r, map[string]string{"content": mediaproxy.RewriteDocumentWithRelativeProxyURL(h.router, entry.Content), "reading_time": readingTime})
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,7 +46,7 @@ func (h *handler) mediaProxy(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
mac := hmac.New(sha256.New, config.Opts.ProxyPrivateKey())
|
mac := hmac.New(sha256.New, config.Opts.MediaProxyPrivateKey())
|
||||||
mac.Write(decodedURL)
|
mac.Write(decodedURL)
|
||||||
expectedMAC := mac.Sum(nil)
|
expectedMAC := mac.Sum(nil)
|
||||||
|
|
||||||
|
@ -99,9 +99,9 @@ func (h *handler) mediaProxy(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
clt := &http.Client{
|
clt := &http.Client{
|
||||||
Transport: &http.Transport{
|
Transport: &http.Transport{
|
||||||
IdleConnTimeout: time.Duration(config.Opts.ProxyHTTPClientTimeout()) * time.Second,
|
IdleConnTimeout: time.Duration(config.Opts.MediaProxyHTTPClientTimeout()) * time.Second,
|
||||||
},
|
},
|
||||||
Timeout: time.Duration(config.Opts.ProxyHTTPClientTimeout()) * time.Second,
|
Timeout: time.Duration(config.Opts.MediaProxyHTTPClientTimeout()) * time.Second,
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := clt.Do(req)
|
resp, err := clt.Do(req)
|
||||||
|
|
50
miniflux.1
50
miniflux.1
|
@ -345,6 +345,31 @@ Set to 1 to enable maintenance mode\&.
|
||||||
.br
|
.br
|
||||||
Disabled by default\&.
|
Disabled by default\&.
|
||||||
.TP
|
.TP
|
||||||
|
.B MEDIA_PROXY_CUSTOM_URL
|
||||||
|
Sets an external server to proxy media through\&.
|
||||||
|
.br
|
||||||
|
Default is empty, Miniflux does the proxying\&.
|
||||||
|
.TP
|
||||||
|
.B MEDIA_PROXY_HTTP_CLIENT_TIMEOUT
|
||||||
|
Time limit in seconds before the media proxy HTTP client cancel the request\&.
|
||||||
|
.br
|
||||||
|
Default is 120 seconds\&.
|
||||||
|
.TP
|
||||||
|
.B MEDIA_PROXY_RESOURCE_TYPES
|
||||||
|
A comma-separated list of media types to proxify. Supported values are: image, audio, video\&.
|
||||||
|
.br
|
||||||
|
Default is image\&.
|
||||||
|
.TP
|
||||||
|
.B MEDIA_PROXY_MODE
|
||||||
|
Possible values: http-only, all, or none\&.
|
||||||
|
.br
|
||||||
|
Default is http-only\&.
|
||||||
|
.TP
|
||||||
|
.B MEDIA_PROXY_PRIVATE_KEY
|
||||||
|
Set a custom custom private key used to sign proxified media URLs\&.
|
||||||
|
.br
|
||||||
|
By default, a secret is randomly generated during startup\&.
|
||||||
|
.TP
|
||||||
.B METRICS_ALLOWED_NETWORKS
|
.B METRICS_ALLOWED_NETWORKS
|
||||||
List of networks allowed to access the metrics endpoint (comma-separated values)\&.
|
List of networks allowed to access the metrics endpoint (comma-separated values)\&.
|
||||||
.br
|
.br
|
||||||
|
@ -458,31 +483,6 @@ Override LISTEN_ADDR to 0.0.0.0:$PORT\&.
|
||||||
.br
|
.br
|
||||||
Default is empty\&.
|
Default is empty\&.
|
||||||
.TP
|
.TP
|
||||||
.B PROXY_HTTP_CLIENT_TIMEOUT
|
|
||||||
Time limit in seconds before the proxy HTTP client cancel the request\&.
|
|
||||||
.br
|
|
||||||
Default is 120 seconds\&.
|
|
||||||
.TP
|
|
||||||
.B PROXY_MEDIA_TYPES
|
|
||||||
A list of media types to proxify (comma-separated values): image, audio, video\&.
|
|
||||||
.br
|
|
||||||
Default is image only\&.
|
|
||||||
.TP
|
|
||||||
.B PROXY_OPTION
|
|
||||||
Avoids mixed content warnings for external media: http-only, all, or none\&.
|
|
||||||
.br
|
|
||||||
Default is http-only\&.
|
|
||||||
.TP
|
|
||||||
.B PROXY_PRIVATE_KEY
|
|
||||||
Set a custom custom private key used to sign proxified media URL\&.
|
|
||||||
.br
|
|
||||||
Default is randomly generated at startup\&.
|
|
||||||
.TP
|
|
||||||
.B PROXY_URL
|
|
||||||
Sets a server to proxy media through\&.
|
|
||||||
.br
|
|
||||||
Default is empty, miniflux does the proxying\&.
|
|
||||||
.TP
|
|
||||||
.B RUN_MIGRATIONS
|
.B RUN_MIGRATIONS
|
||||||
Set to 1 to run database migrations\&.
|
Set to 1 to run database migrations\&.
|
||||||
.br
|
.br
|
||||||
|
|
Loading…
Reference in a new issue