add support for local image proxy and cors server

This commit is contained in:
hzrd149 2024-01-28 21:15:11 +00:00
parent 7dbdaf1a98
commit 92fe0bb27d
11 changed files with 83 additions and 20 deletions

View File

@ -0,0 +1,5 @@
---
"nostrudel": minor
---
Add support for local image proxy and cors servers

View File

@ -4,17 +4,24 @@ volumes:
data: {}
services:
# cors:
# image: ghcr.io/hzrd149/docker-cors-anywhere:latest
imageproxy:
image: ghcr.io/willnorris/imageproxy:v0.11.2
relay:
image: scsibug/nostr-rs-relay:0.8.13
ports:
- 5000:8080
volumes:
- data:/usr/src/app/db
app:
build: .
image: ghcr.io/hzrd149/nostrudel:latest
depends_on:
- relay
# - cors
- imageproxy
environment:
CACHE_RELAY: relay:8080
IMAGE_PROXY: imageproxy:8080
# CORS_PROXY: cors:8080
ports:
- 8080:80

View File

@ -1,11 +1,12 @@
#!/bin/sh
set -e
CACHE_RELAY_PROXY=""
PROXY_PASS_BLOCK=""
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
CACHE_RELAY_PROXY="
PROXY_PASS_BLOCK="$PROXY_PASS_BLOCK
location /local-relay {
proxy_pass http://$CACHE_RELAY/;
proxy_http_version 1.1;
@ -17,6 +18,32 @@ 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
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
PROXY_PASS_BLOCK="$PROXY_PASS_BLOCK
location /imageproxy/ {
proxy_pass http://$IMAGE_PROXY;
rewrite ^/imageproxy/(.*) /\$1 break;
}
"
else
echo "No Image proxy set"
fi
CONF_FILE="/etc/nginx/conf.d/default.conf"
NGINX_CONF="
server {
@ -24,13 +51,13 @@ server {
server_name localhost;
$PROXY_PASS_BLOCK
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
$CACHE_RELAY_PROXY
# Gzip settings
gzip on;
gzip_disable "msie6";

View File

@ -13,6 +13,8 @@ EXPOSE 80
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

View File

@ -22,6 +22,8 @@
<script>
window.CACHE_RELAY_ENABLED = false;
window.IMAGE_PROXY_PATH = "";
window.CORS_PROXY_PATH = "";
</script>
</head>
<body>

View File

@ -1,7 +1,6 @@
import { MouseEventHandler, MutableRefObject, forwardRef, useCallback, useMemo, useRef } from "react";
import { Image, ImageProps, Link, LinkProps } from "@chakra-ui/react";
import appSettings from "../../services/settings/app-settings";
import { useTrusted } from "../../providers/local/trust";
import { EmbedableContent, defaultGetLocation } from "../../helpers/embeds";
import { getMatchLink } from "../../helpers/regexp";
@ -12,6 +11,7 @@ import { NostrEvent } from "../../types/nostr-event";
import useAppSettings from "../../hooks/use-app-settings";
import { useBreakpointValue } from "../../providers/global/breakpoint-provider";
import useElementBlur from "../../hooks/use-element-blur";
import { buildImageProxyURL } from "../../helpers/image";
export type TrustImageProps = ImageProps;
@ -41,7 +41,7 @@ export type EmbeddedImageProps = Omit<LinkProps, "children" | "href" | "onClick"
};
function useImageThumbnail(src?: string) {
return appSettings.value.imageProxy ? new URL(`/256,fit/${src}`, appSettings.value.imageProxy).toString() : src;
return (src && buildImageProxyURL(src, "256,fit")) ?? src;
}
export const EmbeddedImage = forwardRef<HTMLImageElement, EmbeddedImageProps>(

View File

@ -242,5 +242,5 @@ export const ThingsIcon = Package;
export const TorrentIcon = Magnet;
export const TrackIcon = Recording02;
export const InboxIcon = Download01
export const OutboxIcon = Upload01
export const InboxIcon = Download01;
export const OutboxIcon = Upload01;

View File

@ -1,13 +1,14 @@
import { forwardRef, memo, useMemo } from "react";
import { Avatar, AvatarProps } from "@chakra-ui/react";
import { useUserMetadata } from "../hooks/use-user-metadata";
import { useAsync } from "react-use";
import { useUserMetadata } from "../hooks/use-user-metadata";
import { getIdenticon } from "../helpers/identicon";
import { safeUrl } from "../helpers/parse";
import { getUserDisplayName } from "../helpers/user-metadata";
import useAppSettings from "../hooks/use-app-settings";
import useCurrentAccount from "../hooks/use-current-account";
import { buildImageProxyURL } from "../helpers/image";
export const UserIdenticon = memo(({ pubkey }: { pubkey: string }) => {
const { value: identicon } = useAsync(() => getIdenticon(pubkey), [pubkey]);
@ -15,6 +16,8 @@ export const UserIdenticon = memo(({ pubkey }: { pubkey: string }) => {
return identicon ? <img src={`data:image/svg+xml;base64,${identicon}`} width="100%" /> : null;
});
const RESIZE_PROFILE_SIZE = 96;
export type UserAvatarProps = Omit<AvatarProps, "src"> & {
pubkey: string;
relay?: string;
@ -28,17 +31,16 @@ export const UserAvatar = forwardRef<HTMLDivElement, UserAvatarProps>(({ pubkey,
if (hideUsernames && pubkey !== account?.pubkey) return undefined;
if (metadata?.picture) {
const src = safeUrl(metadata?.picture);
if (!noProxy) {
if (imageProxy && src) {
return new URL(`/96/${src}`, imageProxy).toString();
} else if (proxyUserMedia) {
const last4 = String(pubkey).slice(pubkey.length - 4, pubkey.length);
return `https://media.nostr.band/thumbs/${last4}/${pubkey}-picture-64`;
}
if (src) {
const proxyURL = buildImageProxyURL(src, RESIZE_PROFILE_SIZE);
if (proxyURL) return proxyURL;
} else if (!noProxy && proxyUserMedia) {
const last4 = String(pubkey).slice(pubkey.length - 4, pubkey.length);
return `https://media.nostr.band/thumbs/${last4}/${pubkey}-picture-64`;
}
return src;
}
}, [metadata?.picture, imageProxy, hideUsernames, account]);
}, [metadata?.picture, imageProxy, proxyUserMedia, hideUsernames, account]);
return (
<Avatar

View File

@ -3,7 +3,9 @@ import { convertToUrl } from "./url";
const corsFailedHosts = new Set();
export function createCorsUrl(url: URL | string, corsProxy = appSettings.value.corsProxy) {
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>")) {

View File

@ -1,3 +1,5 @@
import appSettings from "../services/settings/app-settings";
export type ImageSize = { width: number; height: number };
const imageSizeCache = new Map<string, ImageSize>();
@ -17,3 +19,15 @@ export function getImageSize(src: string): Promise<{ width: number; height: numb
image.onerror = () => rej(new Error("Failed to get image size"));
});
}
export function buildImageProxyURL(src: string, size: string | number) {
let url: URL | null = null;
if (window.IMAGE_PROXY_PATH) {
url = new URL(location.origin);
url.pathname = window.IMAGE_PROXY_PATH;
} else if (appSettings.value.imageProxy) url = new URL(appSettings.value.imageProxy);
if (url === null) return;
url.pathname = url.pathname.replace(/\/$/, "") + "/" + size + "/" + src;
return url.toString();
}

View File

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