Add REQUEST_PROXY, TOR_PROXY, and I2P_PROXY env options in docker image

Remove CORS_PROXY env option in docker image
This commit is contained in:
hzrd149 2024-04-25 16:59:55 -05:00
parent 686285450c
commit 289fff2f29
25 changed files with 536 additions and 85 deletions

View File

@ -0,0 +1,5 @@
---
"nostrudel": minor
---
Remove CORS_PROXY env option in docker image

View File

@ -0,0 +1,5 @@
---
"nostrudel": minor
---
Add REQUEST_PROXY, TOR_PROXY, and I2P_PROXY env options in docker image

View File

@ -34,7 +34,8 @@ noStrudels docker image has a few options for connecting to other services runni
- `CACHE_RELAY`: if set the client will use the relay to cache all of its events instead of storing them in the browser cache
- `IMAGE_PROXY`: can be set to a local [imageproxy](https://github.com/willnorris/imageproxy) instance so the app can resize profile images
- `CORS_PROXY`: can be set to a local [cors-anywhere](https://github.com/Rob--W/cors-anywhere) instance so the app can proxy http request
- `REQUEST_PROXY`: can be set to a local [cors-anywhere](https://github.com/Rob--W/cors-anywhere) instance so the app can proxy http request
- `PROXY_FIRST`: if this is set to `true` all http requests will go through the request proxy first
You can find a full example of all these services in the [docker-compose.yaml](./docker-compose.yaml)

View File

@ -4,10 +4,14 @@ volumes:
data: {}
services:
cors:
image: ghcr.io/hzrd149/docker-cors-anywhere:0.4.5
environment:
CORSANYWHERE_REQUIRE_HEADERS: "host"
i2p-proxy:
image: purplei2p/i2pd:release-2.51.0
tor-proxy:
image: dockage/tor-privoxy:latest
# cors:
# image: ghcr.io/hzrd149/docker-cors-anywhere:0.4.5
# environment:
# CORSANYWHERE_REQUIRE_HEADERS: "host"
imageproxy:
image: ghcr.io/willnorris/imageproxy:v0.11.2
relay:
@ -19,11 +23,14 @@ services:
image: ghcr.io/hzrd149/nostrudel:latest
depends_on:
- relay
- cors
- tor-proxy
- i2p-proxy
- imageproxy
environment:
CACHE_RELAY: relay:8080
IMAGE_PROXY: imageproxy:8080
CORS_PROXY: cors:8080
TOR_PROXY: tor-proxy:9050
I2P_PROXY: i2p-proxy:4444
REQUEST_PROXY: "true"
ports:
- 8080:80

View File

@ -3,6 +3,41 @@ set -e
PROXY_PASS_BLOCK=""
# start tor if set to true
if [ "$TOR_PROXY" = "true" ]; then
echo "Starting tor socks proxy"
tor &
tor_process=$!
TOR_PROXY="127.0.0.1:9050"
fi
# inject request proxy
if [ -n "$REQUEST_PROXY" ]; then
REQUEST_PROXY_URL="$REQUEST_PROXY"
if [ "$REQUEST_PROXY" = "true" ]; then
REQUEST_PROXY_URL="127.0.0.1:8080"
fi
echo "Request proxy set to $REQUEST_PROXY"
sed -i 's/REQUEST_PROXY = ""/REQUEST_PROXY = "\/request-proxy"/g' /usr/share/nginx/html/index.html
PROXY_PASS_BLOCK="$PROXY_PASS_BLOCK
location /request-proxy/ {
proxy_pass http://$REQUEST_PROXY_URL;
rewrite ^/request-proxy/(.*) /\$1 break;
}
"
if [ -n "$PROXY_FIRST" ]; then
echo "Telling app to use request proxy first"
sed -i 's/PROXY_FIRST = false/PROXY_FIRST = true/g' /usr/share/nginx/html/index.html
fi
else
echo "No request proxy set"
fi
# inject cache relay URL
if [ -n "$CACHE_RELAY" ]; then
echo "Cache relay set to $CACHE_RELAY"
sed -i 's/CACHE_RELAY_ENABLED = false/CACHE_RELAY_ENABLED = true/g' /usr/share/nginx/html/index.html
@ -18,19 +53,7 @@ else
echo "No cache relay set"
fi
if [ -n "$CORS_PROXY" ]; then
echo "CORS proxy set to $CORS_PROXY"
sed -i 's/CORS_PROXY_PATH = ""/CORS_PROXY_PATH = "\/corsproxy"/g' /usr/share/nginx/html/index.html
PROXY_PASS_BLOCK="$PROXY_PASS_BLOCK
location /corsproxy/ {
proxy_pass http://$CORS_PROXY;
rewrite ^/corsproxy/(.*) /\$1 break;
}
"
else
echo "No CORS proxy set"
fi
# inject image proxy URL
if [ -n "$IMAGE_PROXY" ]; then
echo "Image proxy set to $IMAGE_PROXY"
sed -i 's/IMAGE_PROXY_PATH = ""/IMAGE_PROXY_PATH = "\/imageproxy"/g' /usr/share/nginx/html/index.html
@ -99,9 +122,27 @@ echo "$NGINX_CONF" > $CONF_FILE
_term() {
echo "Caught SIGTERM signal!"
# stop node server
if [ "$REQUEST_PROXY" = "true" ]; then
kill -SIGTERM "$node_process" 2>/dev/null
fi
# stop tor if started
if [ "$TOR_PROXY" = "true" ]; then
kill -SIGTERM "$tor_process" 2>/dev/null
fi
# stop nginx
kill -SIGTERM "$nginx_process" 2>/dev/null
}
if [ "$REQUEST_PROXY" = "true" ]; then
echo "Starting local request proxy"
node server/index.js &
node_process=$!
fi
nginx -g 'daemon off;' &
nginx_process=$!

View File

@ -1,5 +1,5 @@
# syntax=docker/dockerfile:1
FROM node:20.11 as builder
FROM node:20-alpine as builder
WORKDIR /app
@ -17,12 +17,33 @@ RUN yarn build
FROM nginx:stable-alpine-slim
EXPOSE 80
# install nodejs
RUN apk add --no-cache libstdc++ wget
RUN wget https://unofficial-builds.nodejs.org/download/release/v20.12.2/node-v20.12.2-linux-x64-musl.tar.gz
RUN tar -xf node-v20.12.2-linux-x64-musl.tar.gz && mv node-v20.12.2-linux-x64-musl node-v20.12.2
RUN rm node-v20.12.2-linux-x64-musl.tar.gz
# install tor
# copied from https://github.com/klemmchr/tor-alpine/blob/master/Dockerfile
RUN echo '@edge https://dl-cdn.alpinelinux.org/alpine/edge/community' >> /etc/apk/repositories && \
apk -U upgrade && \
apk -v add tor@edge torsocks@edge
# remove tmp files
RUN rm -rf /var/cache/apk/* /tmp/* /var/tmp/*
ENV PATH="/node-v20.12.2/bin:$PATH"
WORKDIR /app
COPY --from=builder /app/dist /usr/share/nginx/html
ENV CACHE_RELAY=""
ENV IMAGE_PROXY=""
ENV CORS_PROXY=""
ADD ./docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
RUN chmod a+x /usr/local/bin/docker-entrypoint.sh
# copy server
COPY server/ /app/server/
RUN cd /app/server/ && npm install
ENTRYPOINT "/usr/local/bin/docker-entrypoint.sh"
# setup entrypoint
ADD ./docker-entrypoint.sh docker-entrypoint.sh
RUN chmod a+x docker-entrypoint.sh
ENTRYPOINT "/app/docker-entrypoint.sh"

View File

@ -23,7 +23,8 @@
<script>
window.CACHE_RELAY_ENABLED = false;
window.IMAGE_PROXY_PATH = "";
window.CORS_PROXY_PATH = "";
window.REQUEST_PROXY = "";
window.PROXY_FIRST = false;
</script>
</head>
<body>

View File

@ -92,6 +92,9 @@
"three-spritetext": "^1.8.1",
"three-stdlib": "^2.29.4",
"webln": "^0.3.2",
"workbox-core": "7.0.0",
"workbox-precaching": "7.0.0",
"workbox-routing": "7.0.0",
"yet-another-react-lightbox": "^3.17.3",
"zen-observable": "^0.10.0"
},

52
server/index.js Normal file
View File

@ -0,0 +1,52 @@
var cors_proxy = require("cors-anywhere");
var { PacProxyAgent } = require("pac-proxy-agent");
const { TOR_PROXY, I2P_PROXY } = process.env;
if (TOR_PROXY) console.log("Tor Proxy:", TOR_PROXY);
if (I2P_PROXY) console.log("I2P Proxy:", I2P_PROXY);
const I2pConfig = I2P_PROXY
? `
if (shExpMatch(host, "*.i2p"))
{
return "PROXY ${I2P_PROXY}";
}`.trim()
: "";
const TorConfig = TOR_PROXY
? `
if (shExpMatch(host, "*.onion"))
{
return "SOCKS5 ${TOR_PROXY}";
}`.trim()
: "";
const PACFile = `
// SPDX-License-Identifier: CC0-1.0
function FindProxyForURL(url, host)
{
${I2pConfig}
${TorConfig}
return "DIRECT";
}
`.trim();
const PACURI = "pac+data:application/x-ns-proxy-autoconfig;base64," + btoa(PACFile);
var host = "127.0.0.1";
var port = 8080;
cors_proxy
.createServer({
requireHeader: [],
removeHeaders: ["cookie", "cookie2"],
redirectSameOrigin: true,
httpProxyOptions: {
xfwd: false,
agent: new PacProxyAgent(PACURI),
},
})
.listen(port, host, () => {
console.log("Running HTTP request proxy on " + host + ":" + port);
});

12
server/package.json Normal file
View File

@ -0,0 +1,12 @@
{
"name": "server",
"version": "1.0.0",
"private": true,
"main": "index.js",
"license": "MIT",
"dependencies": {
"cors-anywhere": "^0.4.4",
"pac-proxy-agent": "^7.0.1",
"workbox-precaching": "^7.1.0"
}
}

269
server/yarn.lock Normal file
View File

@ -0,0 +1,269 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
"@tootallnate/quickjs-emscripten@^0.23.0":
version "0.23.0"
resolved "https://registry.yarnpkg.com/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz#db4ecfd499a9765ab24002c3b696d02e6d32a12c"
integrity sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==
agent-base@^7.0.2, agent-base@^7.1.0, agent-base@^7.1.1:
version "7.1.1"
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.1.tgz#bdbded7dfb096b751a2a087eeeb9664725b2e317"
integrity sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==
dependencies:
debug "^4.3.4"
ast-types@^0.13.4:
version "0.13.4"
resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.13.4.tgz#ee0d77b343263965ecc3fb62da16e7222b2b6782"
integrity sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==
dependencies:
tslib "^2.0.1"
basic-ftp@^5.0.2:
version "5.0.5"
resolved "https://registry.yarnpkg.com/basic-ftp/-/basic-ftp-5.0.5.tgz#14a474f5fffecca1f4f406f1c26b18f800225ac0"
integrity sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==
cors-anywhere@^0.4.4:
version "0.4.4"
resolved "https://registry.yarnpkg.com/cors-anywhere/-/cors-anywhere-0.4.4.tgz#98892fcab55f408fff13a63e125135c18dc22ca8"
integrity sha512-8OBFwnzMgR4mNrAeAyOLB2EruS2z7u02of2bOu7i9kKYlZG+niS7CTHLPgEXKWW2NAOJWRry9RRCaL9lJRjNqg==
dependencies:
http-proxy "1.11.1"
proxy-from-env "0.0.1"
data-uri-to-buffer@^6.0.2:
version "6.0.2"
resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz#8a58bb67384b261a38ef18bea1810cb01badd28b"
integrity sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==
debug@4, debug@^4.3.4:
version "4.3.4"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
dependencies:
ms "2.1.2"
degenerator@^5.0.0:
version "5.0.1"
resolved "https://registry.yarnpkg.com/degenerator/-/degenerator-5.0.1.tgz#9403bf297c6dad9a1ece409b37db27954f91f2f5"
integrity sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==
dependencies:
ast-types "^0.13.4"
escodegen "^2.1.0"
esprima "^4.0.1"
escodegen@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-2.1.0.tgz#ba93bbb7a43986d29d6041f99f5262da773e2e17"
integrity sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==
dependencies:
esprima "^4.0.1"
estraverse "^5.2.0"
esutils "^2.0.2"
optionalDependencies:
source-map "~0.6.1"
esprima@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
estraverse@^5.2.0:
version "5.3.0"
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123"
integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==
esutils@^2.0.2:
version "2.0.3"
resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
eventemitter3@1.x.x:
version "1.2.0"
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-1.2.0.tgz#1c86991d816ad1e504750e73874224ecf3bec508"
integrity sha512-DOFqA1MF46fmZl2xtzXR3MPCRsXqgoFqdXcrCVYM3JNnfUeHTm/fh/v/iU7gBFpwkuBmoJPAm5GuhdDfSEJMJA==
fs-extra@^11.2.0:
version "11.2.0"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.2.0.tgz#e70e17dfad64232287d01929399e0ea7c86b0e5b"
integrity sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==
dependencies:
graceful-fs "^4.2.0"
jsonfile "^6.0.1"
universalify "^2.0.0"
get-uri@^6.0.1:
version "6.0.3"
resolved "https://registry.yarnpkg.com/get-uri/-/get-uri-6.0.3.tgz#0d26697bc13cf91092e519aa63aa60ee5b6f385a"
integrity sha512-BzUrJBS9EcUb4cFol8r4W3v1cPsSyajLSthNkz5BxbpDcHN5tIrM10E2eNvfnvBn3DaT3DUgx0OpsBKkaOpanw==
dependencies:
basic-ftp "^5.0.2"
data-uri-to-buffer "^6.0.2"
debug "^4.3.4"
fs-extra "^11.2.0"
graceful-fs@^4.1.6, graceful-fs@^4.2.0:
version "4.2.11"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3"
integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==
http-proxy-agent@^7.0.0:
version "7.0.2"
resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz#9a8b1f246866c028509486585f62b8f2c18c270e"
integrity sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==
dependencies:
agent-base "^7.1.0"
debug "^4.3.4"
http-proxy@1.11.1:
version "1.11.1"
resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.11.1.tgz#71df55757e802d58ea810df2244019dda05ae85d"
integrity sha512-qz7jZarkVG3G6GMq+4VRJPSN4NkIjL4VMTNhKGd8jc25BumeJjWWvnY3A7OkCGa8W1TTxbaK3dcE0ijFalITVA==
dependencies:
eventemitter3 "1.x.x"
requires-port "0.x.x"
https-proxy-agent@^7.0.2:
version "7.0.4"
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz#8e97b841a029ad8ddc8731f26595bad868cb4168"
integrity sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==
dependencies:
agent-base "^7.0.2"
debug "4"
ip-address@^9.0.5:
version "9.0.5"
resolved "https://registry.yarnpkg.com/ip-address/-/ip-address-9.0.5.tgz#117a960819b08780c3bd1f14ef3c1cc1d3f3ea5a"
integrity sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==
dependencies:
jsbn "1.1.0"
sprintf-js "^1.1.3"
jsbn@1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-1.1.0.tgz#b01307cb29b618a1ed26ec79e911f803c4da0040"
integrity sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==
jsonfile@^6.0.1:
version "6.1.0"
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae"
integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==
dependencies:
universalify "^2.0.0"
optionalDependencies:
graceful-fs "^4.1.6"
ms@2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
netmask@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/netmask/-/netmask-2.0.2.tgz#8b01a07644065d536383835823bc52004ebac5e7"
integrity sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==
pac-proxy-agent@^7.0.1:
version "7.0.1"
resolved "https://registry.yarnpkg.com/pac-proxy-agent/-/pac-proxy-agent-7.0.1.tgz#6b9ddc002ec3ff0ba5fdf4a8a21d363bcc612d75"
integrity sha512-ASV8yU4LLKBAjqIPMbrgtaKIvxQri/yh2OpI+S6hVa9JRkUI3Y3NPFbfngDtY7oFtSMD3w31Xns89mDa3Feo5A==
dependencies:
"@tootallnate/quickjs-emscripten" "^0.23.0"
agent-base "^7.0.2"
debug "^4.3.4"
get-uri "^6.0.1"
http-proxy-agent "^7.0.0"
https-proxy-agent "^7.0.2"
pac-resolver "^7.0.0"
socks-proxy-agent "^8.0.2"
pac-resolver@^7.0.0:
version "7.0.1"
resolved "https://registry.yarnpkg.com/pac-resolver/-/pac-resolver-7.0.1.tgz#54675558ea368b64d210fd9c92a640b5f3b8abb6"
integrity sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==
dependencies:
degenerator "^5.0.0"
netmask "^2.0.2"
proxy-from-env@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-0.0.1.tgz#b27c4946e9e6d5dbadb7598a6435d3014c4cfd49"
integrity sha512-B9Hnta3CATuMS0q6kt5hEezOPM+V3dgaRewkFtFoaRQYTVNsHqUvFXmndH06z3QO1ZdDnRELv5vfY6zAj/gG7A==
requires-port@0.x.x:
version "0.0.1"
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-0.0.1.tgz#4b4414411d9df7c855995dd899a8c78a2951c16d"
integrity sha512-AzPDCliPoWDSvEVYRQmpzuPhGGEnPrQz9YiOEvn+UdB9ixBpw+4IOZWtwctmpzySLZTy7ynpn47V14H4yaowtA==
smart-buffer@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae"
integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==
socks-proxy-agent@^8.0.2:
version "8.0.3"
resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-8.0.3.tgz#6b2da3d77364fde6292e810b496cb70440b9b89d"
integrity sha512-VNegTZKhuGq5vSD6XNKlbqWhyt/40CgoEw8XxD6dhnm8Jq9IEa3nIa4HwnM8XOqU0CdB0BwWVXusqiFXfHB3+A==
dependencies:
agent-base "^7.1.1"
debug "^4.3.4"
socks "^2.7.1"
socks@^2.7.1:
version "2.8.3"
resolved "https://registry.yarnpkg.com/socks/-/socks-2.8.3.tgz#1ebd0f09c52ba95a09750afe3f3f9f724a800cb5"
integrity sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==
dependencies:
ip-address "^9.0.5"
smart-buffer "^4.2.0"
source-map@~0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
sprintf-js@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.3.tgz#4914b903a2f8b685d17fdf78a70e917e872e444a"
integrity sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==
tslib@^2.0.1:
version "2.6.2"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae"
integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==
universalify@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d"
integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==
workbox-core@7.1.0:
version "7.1.0"
resolved "https://registry.yarnpkg.com/workbox-core/-/workbox-core-7.1.0.tgz#1867576f994f20d9991b71a7d0b2581af22db170"
integrity sha512-5KB4KOY8rtL31nEF7BfvU7FMzKT4B5TkbYa2tzkS+Peqj0gayMT9SytSFtNzlrvMaWgv6y/yvP9C0IbpFjV30Q==
workbox-precaching@^7.1.0:
version "7.1.0"
resolved "https://registry.yarnpkg.com/workbox-precaching/-/workbox-precaching-7.1.0.tgz#71e27ec2e85661a41b48dec0c92dae707c429eaa"
integrity sha512-LyxzQts+UEpgtmfnolo0hHdNjoB7EoRWcF7EDslt+lQGd0lW4iTvvSe3v5JiIckQSB5KTW5xiCqjFviRKPj1zA==
dependencies:
workbox-core "7.1.0"
workbox-routing "7.1.0"
workbox-strategies "7.1.0"
workbox-routing@7.1.0:
version "7.1.0"
resolved "https://registry.yarnpkg.com/workbox-routing/-/workbox-routing-7.1.0.tgz#c44bda350d1c5eb633ee97a660e64ce5473250c4"
integrity sha512-oOYk+kLriUY2QyHkIilxUlVcFqwduLJB7oRZIENbqPGeBP/3TWHYNNdmGNhz1dvKuw7aqvJ7CQxn27/jprlTdg==
dependencies:
workbox-core "7.1.0"
workbox-strategies@7.1.0:
version "7.1.0"
resolved "https://registry.yarnpkg.com/workbox-strategies/-/workbox-strategies-7.1.0.tgz#a589f2adc0df8f33049c7f4d4cdf4c9556715918"
integrity sha512-/UracPiGhUNehGjRm/tLUQ+9PtWmCbRufWtV0tNrALuf+HZ4F7cmObSEK+E4/Bx1p8Syx2tM+pkIrvtyetdlew==
dependencies:
workbox-core "7.1.0"

View File

@ -28,6 +28,7 @@ export default class RelayPool {
requestRelay(url: string | URL, connect = true) {
url = validateRelayURL(url);
const key = url.toString();
if (!this.relays.has(key)) {
const newRelay = new AbstractRelay(key, { verifyEvent: verifyEventMethod });

View File

@ -40,7 +40,7 @@ export type EmbeddedImageProps = Omit<LinkProps, "children" | "href" | "onClick"
};
function useImageThumbnail(src?: string) {
return (src && buildImageProxyURL(src, "256,fit")) ?? src;
return (src && buildImageProxyURL(src, "512,fit")) ?? src;
}
export const EmbeddedImage = forwardRef<HTMLImageElement, EmbeddedImageProps>(

View File

@ -11,7 +11,8 @@ export function ReloadPrompt(props: Omit<AlertProps, "children" | "status">) {
updateServiceWorker,
} = useRegisterSW({
onRegistered(r) {
console.log("SW Registered: " + r);
console.log("SW Registered");
console.log(r);
if (r) {
setInterval(() => r.update(), intervalMS);
@ -21,7 +22,8 @@ export function ReloadPrompt(props: Omit<AlertProps, "children" | "status">) {
toast({ status: "success", title: "App Installed", duration: 2000, isClosable: true });
},
onRegisterError(error) {
console.log("SW registration error", error);
console.log("SW registration error");
console.log(error);
},
});

View File

@ -1,31 +0,0 @@
import appSettings from "../services/settings/app-settings";
import { convertToUrl } from "./url";
const corsFailedHosts = new Set();
export function createCorsUrl(url: URL | string, corsProxy?: string) {
if (!corsProxy && window.CORS_PROXY_PATH) corsProxy = new URL(window.CORS_PROXY_PATH, location.origin).toString();
if (!corsProxy && appSettings.value.corsProxy) corsProxy = appSettings.value.corsProxy;
if (!corsProxy) return url;
if (corsProxy.includes("<url>")) {
return corsProxy.replace("<url>", "" + url);
} else if (corsProxy.includes("<encoded_url>")) {
return corsProxy.replace("<encoded_url>", encodeURIComponent("" + url));
} else {
return corsProxy.endsWith("/") ? corsProxy + url : corsProxy + "/" + url;
}
}
export function fetchWithCorsFallback(url: URL | string, opts?: RequestInit) {
if (!appSettings.value.corsProxy) return fetch(url, opts);
if (corsFailedHosts.has(convertToUrl(url).host)) {
return fetch(createCorsUrl(url), opts);
}
return fetch(url, opts).catch((e) => {
corsFailedHosts.add(convertToUrl(url).host);
return fetch(createCorsUrl(url), opts);
});
}

View File

@ -1,2 +1,5 @@
import debug from "debug";
if (!localStorage.getItem("debug") && import.meta.env.DEV) debug.enable("noStrudel,noStrudel:*");
export const logger = debug("noStrudel");

44
src/helpers/request.ts Normal file
View File

@ -0,0 +1,44 @@
import appSettings from "../services/settings/app-settings";
import { convertToUrl } from "./url";
const clearNetFailedHosts = new Set();
const proxyFailedHosts = new Set();
export function createRequestProxyUrl(url: URL | string, corsProxy?: string) {
if (!corsProxy && window.REQUEST_PROXY) corsProxy = new URL(window.REQUEST_PROXY, location.origin).toString();
if (!corsProxy && appSettings.value.corsProxy) corsProxy = appSettings.value.corsProxy;
if (!corsProxy) return url;
if (corsProxy.includes("<url>")) {
return corsProxy.replace("<url>", "" + url);
} else if (corsProxy.includes("<encoded_url>")) {
return corsProxy.replace("<encoded_url>", encodeURIComponent("" + url));
} else {
return corsProxy.endsWith("/") ? corsProxy + url : corsProxy + "/" + url;
}
}
export function fetchWithProxy(url: URL | string, opts?: RequestInit) {
if (!appSettings.value.corsProxy && !window.REQUEST_PROXY) return fetch(url, opts);
const u = typeof url === "string" ? convertToUrl(url) : url;
// if its an onion domain try the request proxy first
if ((u.host.endsWith(".onion") || u.host.endsWith(".i2p")) && !proxyFailedHosts.has(u.host)) {
return fetch(createRequestProxyUrl(url), opts).catch((e) => {
proxyFailedHosts.add(u.host);
return fetch(url, opts);
});
}
// if the clear net request has failed. use the proxy
if (clearNetFailedHosts.has(u.host)) {
return fetch(createRequestProxyUrl(url), opts);
}
// try clear net first and fallback to request proxy
return fetch(url, opts).catch((e) => {
clearNetFailedHosts.add(u.host);
return fetch(createRequestProxyUrl(url), opts);
});
}

View File

@ -1,5 +1,5 @@
import { useAsync } from "react-use";
import { fetchWithCorsFallback } from "../helpers/cors";
import { fetchWithProxy } from "../helpers/request";
import type { OgObjectInteral } from "../lib/open-graph-scraper/types";
import useAppSettings from "./use-app-settings";
@ -22,7 +22,7 @@ export default function useOpenGraphData(url: URL) {
try {
const controller = new AbortController();
const res = await fetchWithCorsFallback(url, { signal: controller.signal });
const res = await fetchWithProxy(url, { signal: controller.signal });
const contentType = res.headers.get("content-type");
if (contentType?.includes("text/html")) {

View File

@ -2,7 +2,7 @@ import dayjs from "dayjs";
import db from "./db";
import _throttle from "lodash.throttle";
import { fetchWithCorsFallback } from "../helpers/cors";
import { fetchWithProxy } from "../helpers/request";
import SuperMap from "../classes/super-map";
import Subject from "../classes/subject";
@ -42,7 +42,7 @@ class DnsIdentityService {
const { name, domain } = parseAddress(address);
if (!name || !domain) throw new Error("invalid address " + address);
const json = await fetchWithCorsFallback(`https://${domain}/.well-known/nostr.json?name=${name}`)
const json = await fetchWithProxy(`https://${domain}/.well-known/nostr.json?name=${name}`)
.then((res) => res.json() as Promise<IdentityJson>)
.then((json) => {
// convert all keys in names, and relays to lower case

View File

@ -1,4 +1,4 @@
import { fetchWithCorsFallback } from "../helpers/cors";
import { fetchWithProxy } from "../helpers/request";
import { getLudEndpoint } from "../helpers/lnurl";
type LNURLPMetadata = {
@ -24,9 +24,7 @@ class LNURLMetadataService {
const url = getLudEndpoint(addressOrLNURL);
if (!url) return;
try {
const metadata = await fetchWithCorsFallback(url).then(
(res) => res.json() as Promise<LNURLError | LNURLPMetadata>,
);
const metadata = await fetchWithProxy(url).then((res) => res.json() as Promise<LNURLError | LNURLPMetadata>);
if ((metadata as LNURLPMetadata).tag === "payRequest") {
return metadata as LNURLPMetadata;
}

View File

@ -1,5 +1,5 @@
import db from "./db";
import { fetchWithCorsFallback } from "../helpers/cors";
import { fetchWithProxy } from "../helpers/request";
import { isHexKey } from "../helpers/nip19";
import { validateRelayURL } from "../helpers/relay";
@ -26,7 +26,7 @@ async function fetchInfo(relay: string) {
const url = validateRelayURL(relay);
url.protocol = url.protocol === "ws:" ? "http" : "https";
const infoDoc = await fetchWithCorsFallback(url, { headers: { Accept: "application/nostr+json" } }).then(
const infoDoc = await fetchWithProxy(url, { headers: { Accept: "application/nostr+json" } }).then(
(res) => res.json() as Promise<RelayInformationDocument>,
);

View File

@ -1,5 +1,5 @@
interface Window {
CACHE_RELAY_ENABLED: boolean;
IMAGE_PROXY_PATH: string;
CORS_PROXY_PATH: string;
REQUEST_PROXY: string;
}

View File

@ -17,7 +17,7 @@ import {
import { useFormContext } from "react-hook-form";
import { safeUrl } from "../../helpers/parse";
import { AppSettings } from "../../services/settings/migrations";
import { createCorsUrl } from "../../helpers/cors";
import { createRequestProxyUrl } from "../../helpers/request";
import { SpyIcon } from "../../components/icons";
async function validateInvidiousUrl(url?: string) {
@ -30,12 +30,12 @@ async function validateInvidiousUrl(url?: string) {
}
}
async function validateCorsProxy(url?: string) {
async function validateRequestProxy(url?: string) {
if (!url) return true;
try {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 2000);
const res = await fetch(createCorsUrl("https://example.com", url), { signal: controller.signal });
const res = await fetch(createRequestProxyUrl("https://example.com", url), { signal: controller.signal });
return res.ok || "Cant reach instance";
} catch (e) {
return "Cant reach instance";
@ -124,16 +124,25 @@ export default function PrivacySettings() {
</FormControl>
<FormControl isInvalid={!!formState.errors.corsProxy}>
<FormLabel>CORS Proxy</FormLabel>
<Input
type="url"
placeholder="https://corsproxy.io/?<encoded_url>"
{...register("corsProxy", { validate: validateCorsProxy })}
/>
<FormLabel>Request Proxy</FormLabel>
{window.REQUEST_PROXY ? (
<>
<Input type="url" value={window.REQUEST_PROXY} onChange={() => {}} readOnly isDisabled />
<FormHelperText color="red.500">
This noStrudel version has the request proxy hard coded to <Code>{window.REQUEST_PROXY}</Code>
</FormHelperText>
</>
) : (
<Input
type="url"
placeholder="https://corsproxy.io/?<encoded_url>"
{...register("corsProxy", { validate: validateRequestProxy })}
/>
)}
{formState.errors.corsProxy && <FormErrorMessage>{formState.errors.corsProxy.message}</FormErrorMessage>}
<FormHelperText>
This is used as a fallback ( to bypass CORS restrictions ) when verifying NIP-05 ids and fetching
open-graph metadata.
This is used as a fallback ( to bypass CORS restrictions ) or to make connections to .onion and .i2p
domains
<br />
This can either point to an instance of{" "}
<Link href="https://github.com/Rob--W/cors-anywhere" isExternal color="blue.500">

View File

@ -2,7 +2,7 @@
"compilerOptions": {
"target": "ESNext",
"useDefineForClassFields": true,
"lib": ["DOM", "DOM.Iterable", "ESNext"],
"lib": ["DOM", "DOM.Iterable", "ESNext", "WebWorker"],
"allowJs": false,
"skipLibCheck": true,
"esModuleInterop": true,

View File

@ -13,6 +13,14 @@ export default defineConfig({
react(),
VitePWA({
registerType: "prompt",
// strategies: "injectManifest",
// srcDir: "src",
// filename: "sw.ts",
// devOptions: {
// // NOTE: ESM service workers is not supported by firefox
// type: "module",
// enabled: true,
// },
workbox: {
// This increase the cache limit to 3mB
maximumFileSizeToCacheInBytes: 2097152 * 1.5,