mirror of
https://github.com/hzrd149/nsite-gateway.git
synced 2025-06-23 12:05:01 +00:00
add nginx cache invalidation
bundle nginx in docker image switch from ndk to nostr-tools
This commit is contained in:
parent
88a9229633
commit
b7b43cff10
5
.changeset/many-lemons-exercise.md
Normal file
5
.changeset/many-lemons-exercise.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"nsite-ts": minor
|
||||||
|
---
|
||||||
|
|
||||||
|
Bundle nginx in docker image
|
5
.changeset/metal-wasps-cry.md
Normal file
5
.changeset/metal-wasps-cry.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"nsite-ts": minor
|
||||||
|
---
|
||||||
|
|
||||||
|
Add NGINX_CACHE_DIR for invalidating nginx cache
|
5
.changeset/popular-plants-beam.md
Normal file
5
.changeset/popular-plants-beam.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"nsite-ts": minor
|
||||||
|
---
|
||||||
|
|
||||||
|
Add SUBSCRIPTION_RELAYS for listening for new events
|
@ -2,8 +2,11 @@
|
|||||||
# can be in-memory, redis:// or sqlite://
|
# can be in-memory, redis:// or sqlite://
|
||||||
CACHE_PATH="in-memory"
|
CACHE_PATH="in-memory"
|
||||||
|
|
||||||
# A list of nostr relays to search
|
# A list of relays to find users relay lists (10002) and blossom servers (10063)
|
||||||
NOSTR_RELAYS=wss://nos.lol,wss://relay.damus.io
|
LOOKUP_RELAYS=wss://user.kindpag.es,wss://purplepag.es
|
||||||
|
|
||||||
|
# A list of nostr relays to listen to for new nsite events
|
||||||
|
SUBSCRIPTION_RELAYS=wss://nos.lol,wss://relay.damus.io
|
||||||
|
|
||||||
# A list of fallback blossom servers
|
# A list of fallback blossom servers
|
||||||
BLOSSOM_SERVERS=https://cdn.satellite.earth
|
BLOSSOM_SERVERS=https://cdn.satellite.earth
|
||||||
@ -12,4 +15,4 @@ BLOSSOM_SERVERS=https://cdn.satellite.earth
|
|||||||
MAX_FILE_SIZE='2 MB'
|
MAX_FILE_SIZE='2 MB'
|
||||||
|
|
||||||
# the hostname or ip of the upstream nginx proxy cache
|
# the hostname or ip of the upstream nginx proxy cache
|
||||||
NGINX_HOST='nginx'
|
NGINX_CACHE_DIR='/var/nginx/cache'
|
||||||
|
26
Dockerfile
26
Dockerfile
@ -5,6 +5,9 @@ ENV PNPM_HOME="/pnpm"
|
|||||||
ENV PATH="$PNPM_HOME:$PATH"
|
ENV PATH="$PNPM_HOME:$PATH"
|
||||||
RUN corepack enable
|
RUN corepack enable
|
||||||
|
|
||||||
|
RUN apk update && apk add --no-cache nginx supervisor
|
||||||
|
COPY supervisord.conf /etc/supervisord.conf
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY package.json .
|
COPY package.json .
|
||||||
COPY pnpm-lock.yaml .
|
COPY pnpm-lock.yaml .
|
||||||
@ -19,12 +22,29 @@ COPY src ./src
|
|||||||
RUN pnpm build
|
RUN pnpm build
|
||||||
|
|
||||||
FROM base AS main
|
FROM base AS main
|
||||||
|
|
||||||
|
# Setup user
|
||||||
|
RUN addgroup -S nsite && adduser -S nsite -G nsite
|
||||||
|
RUN chown -R nsite:nsite /app
|
||||||
|
|
||||||
|
# Setup nginx
|
||||||
|
COPY nginx/nginx.conf /etc/nginx/nginx.conf
|
||||||
|
COPY nginx/default.conf /etc/nginx/conf.d/default.conf
|
||||||
|
|
||||||
|
# setup nsite
|
||||||
COPY --from=prod-deps /app/node_modules /app/node_modules
|
COPY --from=prod-deps /app/node_modules /app/node_modules
|
||||||
COPY --from=build ./app/build ./build
|
COPY --from=build ./app/build ./build
|
||||||
|
|
||||||
COPY ./public ./public
|
COPY ./public ./public
|
||||||
|
|
||||||
EXPOSE 80
|
VOLUME [ "/var/cache/nginx" ]
|
||||||
ENV PORT="80"
|
|
||||||
|
|
||||||
ENTRYPOINT [ "node", "." ]
|
EXPOSE 80 3000
|
||||||
|
ENV NSITE_PORT="3000"
|
||||||
|
ENV NGINX_CACHE_DIR="/var/cache/nginx"
|
||||||
|
|
||||||
|
COPY docker-entrypoint.sh /
|
||||||
|
RUN chmod +x /docker-entrypoint.sh
|
||||||
|
ENTRYPOINT ["/docker-entrypoint.sh"]
|
||||||
|
|
||||||
|
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf"]
|
||||||
|
@ -1,22 +1,17 @@
|
|||||||
version: "3.7"
|
version: "3.7"
|
||||||
|
|
||||||
volumes:
|
|
||||||
cache: {}
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
nginx:
|
|
||||||
image: nginx:alpine
|
|
||||||
ports:
|
|
||||||
- 8080:80
|
|
||||||
volumes:
|
|
||||||
- cache:/var/cache/nginx
|
|
||||||
- ./nginx/nginx.conf:/etc/nginx/nginx.conf
|
|
||||||
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf
|
|
||||||
|
|
||||||
nsite:
|
nsite:
|
||||||
build: .
|
build: .
|
||||||
image: ghcr.io/hzrd149/nsite-ts:master
|
image: ghcr.io/hzrd149/nsite-ts:master
|
||||||
environment:
|
environment:
|
||||||
LOOKUP_RELAYS: wss://user.kindpag.es,wss://purplepag.es
|
LOOKUP_RELAYS: wss://user.kindpag.es,wss://purplepag.es
|
||||||
|
SUBSCRIPTION_RELAYS: wss://nostrue.com/,wss://nos.lol/,wss://relay.damus.io/,wss://purplerelay.com/
|
||||||
|
volumes:
|
||||||
|
- type: tmpfs
|
||||||
|
target: /var/cache/nginx
|
||||||
|
tmpfs:
|
||||||
|
size: 100M
|
||||||
ports:
|
ports:
|
||||||
- 3000:80
|
- 8080:80
|
||||||
|
- 3000:3000
|
||||||
|
5
docker-entrypoint.sh
Executable file
5
docker-entrypoint.sh
Executable file
@ -0,0 +1,5 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
chown -R nginx:nginx /var/cache/nginx
|
||||||
|
|
||||||
|
exec "$@"
|
@ -1,7 +1,7 @@
|
|||||||
server {
|
server {
|
||||||
listen 80;
|
listen 80;
|
||||||
listen [::]:80;
|
listen [::]:80;
|
||||||
server_name nsite-proxy;
|
server_name nsite;
|
||||||
|
|
||||||
location / {
|
location / {
|
||||||
proxy_cache request_cache;
|
proxy_cache request_cache;
|
||||||
@ -17,13 +17,6 @@ server {
|
|||||||
add_header Cache-Control "public, no-transform";
|
add_header Cache-Control "public, no-transform";
|
||||||
|
|
||||||
proxy_set_header Host $host;
|
proxy_set_header Host $host;
|
||||||
proxy_pass http://nsite;
|
proxy_pass http://127.0.0.1:3000;
|
||||||
}
|
}
|
||||||
|
|
||||||
# Manual cache invalidation ( cant use proxy_cache_purge )
|
|
||||||
# location ~ /purge(/.*) {
|
|
||||||
# allow 127.0.0.1;
|
|
||||||
# deny all;
|
|
||||||
# proxy_cache_purge request_cache $scheme$proxy_host$1;
|
|
||||||
# }
|
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
user nginx;
|
user nginx;
|
||||||
worker_processes auto;
|
worker_processes auto;
|
||||||
|
|
||||||
error_log /var/log/nginx/error.log notice;
|
error_log /dev/stderr notice;
|
||||||
pid /var/run/nginx.pid;
|
pid /var/run/nginx.pid;
|
||||||
|
|
||||||
events {
|
events {
|
||||||
@ -19,7 +19,7 @@ http {
|
|||||||
'$status $body_bytes_sent "$http_referer" '
|
'$status $body_bytes_sent "$http_referer" '
|
||||||
'"$http_user_agent" "$http_x_forwarded_for"';
|
'"$http_user_agent" "$http_x_forwarded_for"';
|
||||||
|
|
||||||
access_log /var/log/nginx/access.log main;
|
access_log /dev/stdout main;
|
||||||
|
|
||||||
sendfile on;
|
sendfile on;
|
||||||
#tcp_nopush on;
|
#tcp_nopush on;
|
||||||
|
@ -21,7 +21,6 @@
|
|||||||
"@keyv/redis": "^3.0.1",
|
"@keyv/redis": "^3.0.1",
|
||||||
"@keyv/sqlite": "^4.0.1",
|
"@keyv/sqlite": "^4.0.1",
|
||||||
"@koa/cors": "^5.0.0",
|
"@koa/cors": "^5.0.0",
|
||||||
"@nostr-dev-kit/ndk": "^2.10.0",
|
|
||||||
"blossom-client-sdk": "^1.1.1",
|
"blossom-client-sdk": "^1.1.1",
|
||||||
"dotenv": "^16.4.5",
|
"dotenv": "^16.4.5",
|
||||||
"follow-redirects": "^1.15.6",
|
"follow-redirects": "^1.15.6",
|
||||||
|
113
pnpm-lock.yaml
generated
113
pnpm-lock.yaml
generated
@ -20,9 +20,6 @@ importers:
|
|||||||
'@koa/cors':
|
'@koa/cors':
|
||||||
specifier: ^5.0.0
|
specifier: ^5.0.0
|
||||||
version: 5.0.0
|
version: 5.0.0
|
||||||
'@nostr-dev-kit/ndk':
|
|
||||||
specifier: ^2.10.0
|
|
||||||
version: 2.10.0(typescript@5.6.2)
|
|
||||||
blossom-client-sdk:
|
blossom-client-sdk:
|
||||||
specifier: ^1.1.1
|
specifier: ^1.1.1
|
||||||
version: 1.1.1
|
version: 1.1.1
|
||||||
@ -215,10 +212,6 @@ packages:
|
|||||||
'@noble/curves@1.2.0':
|
'@noble/curves@1.2.0':
|
||||||
resolution: {integrity: sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==}
|
resolution: {integrity: sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==}
|
||||||
|
|
||||||
'@noble/curves@1.6.0':
|
|
||||||
resolution: {integrity: sha512-TlaHRXDehJuRNR9TfZDNQ45mMEd5dwUwmicsafcIX4SsNiqnCHKjE/1alYPd/lDRVhxdhUAlv8uEhMCI5zjIJQ==}
|
|
||||||
engines: {node: ^14.21.3 || >=16}
|
|
||||||
|
|
||||||
'@noble/hashes@1.3.1':
|
'@noble/hashes@1.3.1':
|
||||||
resolution: {integrity: sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==}
|
resolution: {integrity: sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==}
|
||||||
engines: {node: '>= 16'}
|
engines: {node: '>= 16'}
|
||||||
@ -231,9 +224,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-1j6kQFb7QRru7eKN3ZDvRcP13rugwdxZqCjbiAVZfIJwgj2A65UmT4TgARXGlXgnRkORLTDTrO19ZErt7+QXgA==}
|
resolution: {integrity: sha512-1j6kQFb7QRru7eKN3ZDvRcP13rugwdxZqCjbiAVZfIJwgj2A65UmT4TgARXGlXgnRkORLTDTrO19ZErt7+QXgA==}
|
||||||
engines: {node: ^14.21.3 || >=16}
|
engines: {node: ^14.21.3 || >=16}
|
||||||
|
|
||||||
'@noble/secp256k1@2.1.0':
|
|
||||||
resolution: {integrity: sha512-XLEQQNdablO0XZOIniFQimiXsZDNwaYgL96dZwC54Q30imSbAOFf3NKtepc+cXyuZf5Q1HCgbqgZ2UFFuHVcEw==}
|
|
||||||
|
|
||||||
'@nodelib/fs.scandir@2.1.5':
|
'@nodelib/fs.scandir@2.1.5':
|
||||||
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
|
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
|
||||||
engines: {node: '>= 8'}
|
engines: {node: '>= 8'}
|
||||||
@ -246,10 +236,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
|
resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
|
||||||
engines: {node: '>= 8'}
|
engines: {node: '>= 8'}
|
||||||
|
|
||||||
'@nostr-dev-kit/ndk@2.10.0':
|
|
||||||
resolution: {integrity: sha512-TqCAAo6ylORraAXrzRkCGFN2xTMiFbdER8Y8CtUT0HwOpFG/Wn+PBNeDeDmqkl/6LaPdeyXmVwCWj2KcUjIwYA==}
|
|
||||||
engines: {node: '>=16'}
|
|
||||||
|
|
||||||
'@npmcli/fs@1.1.1':
|
'@npmcli/fs@1.1.1':
|
||||||
resolution: {integrity: sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==}
|
resolution: {integrity: sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==}
|
||||||
|
|
||||||
@ -316,9 +302,6 @@ packages:
|
|||||||
'@scure/base@1.1.1':
|
'@scure/base@1.1.1':
|
||||||
resolution: {integrity: sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==}
|
resolution: {integrity: sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==}
|
||||||
|
|
||||||
'@scure/base@1.1.9':
|
|
||||||
resolution: {integrity: sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==}
|
|
||||||
|
|
||||||
'@scure/bip32@1.3.1':
|
'@scure/bip32@1.3.1':
|
||||||
resolution: {integrity: sha512-osvveYtyzdEVbt3OfwwXFr4P2iVBL5u1Q3q4ONBfDY/UpOuXmOlbgwc1xECEboY8wIays8Yt6onaWMUdUbfl0A==}
|
resolution: {integrity: sha512-osvveYtyzdEVbt3OfwwXFr4P2iVBL5u1Q3q4ONBfDY/UpOuXmOlbgwc1xECEboY8wIays8Yt6onaWMUdUbfl0A==}
|
||||||
|
|
||||||
@ -666,10 +649,6 @@ packages:
|
|||||||
cross-spawn@5.1.0:
|
cross-spawn@5.1.0:
|
||||||
resolution: {integrity: sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==}
|
resolution: {integrity: sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==}
|
||||||
|
|
||||||
data-uri-to-buffer@4.0.1:
|
|
||||||
resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==}
|
|
||||||
engines: {node: '>= 12'}
|
|
||||||
|
|
||||||
debug@2.6.9:
|
debug@2.6.9:
|
||||||
resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==}
|
resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@ -794,10 +773,6 @@ packages:
|
|||||||
fastq@1.17.1:
|
fastq@1.17.1:
|
||||||
resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==}
|
resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==}
|
||||||
|
|
||||||
fetch-blob@3.2.0:
|
|
||||||
resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==}
|
|
||||||
engines: {node: ^12.20 || >= 14.13}
|
|
||||||
|
|
||||||
file-uri-to-path@1.0.0:
|
file-uri-to-path@1.0.0:
|
||||||
resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==}
|
resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==}
|
||||||
|
|
||||||
@ -818,10 +793,6 @@ packages:
|
|||||||
debug:
|
debug:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
formdata-polyfill@4.0.10:
|
|
||||||
resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==}
|
|
||||||
engines: {node: '>=12.20.0'}
|
|
||||||
|
|
||||||
fresh@0.5.2:
|
fresh@0.5.2:
|
||||||
resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==}
|
resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==}
|
||||||
engines: {node: '>= 0.6'}
|
engines: {node: '>= 0.6'}
|
||||||
@ -1046,9 +1017,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-j/8tY9j5t+GVMLeioLaxweJiKUayFhlGqNTzf2ZGwL0ZCQijd2RLHK0SLW5Tsko8YyyqCZC2cojIb0/s62qTAg==}
|
resolution: {integrity: sha512-j/8tY9j5t+GVMLeioLaxweJiKUayFhlGqNTzf2ZGwL0ZCQijd2RLHK0SLW5Tsko8YyyqCZC2cojIb0/s62qTAg==}
|
||||||
engines: {node: ^4.8.4 || ^6.10.1 || ^7.10.1 || >= 8.1.4}
|
engines: {node: ^4.8.4 || ^6.10.1 || ^7.10.1 || >= 8.1.4}
|
||||||
|
|
||||||
light-bolt11-decoder@3.1.1:
|
|
||||||
resolution: {integrity: sha512-sLg/KCwYkgsHWkefWd6KqpCHrLFWWaXTOX3cf6yD2hAzL0SLpX+lFcaFK2spkjbgzG6hhijKfORDc9WoUHwX0A==}
|
|
||||||
|
|
||||||
locate-path@5.0.0:
|
locate-path@5.0.0:
|
||||||
resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==}
|
resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
@ -1176,14 +1144,6 @@ packages:
|
|||||||
node-addon-api@7.1.1:
|
node-addon-api@7.1.1:
|
||||||
resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==}
|
resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==}
|
||||||
|
|
||||||
node-domexception@1.0.0:
|
|
||||||
resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==}
|
|
||||||
engines: {node: '>=10.5.0'}
|
|
||||||
|
|
||||||
node-fetch@3.3.2:
|
|
||||||
resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==}
|
|
||||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
|
||||||
|
|
||||||
node-gyp@8.4.1:
|
node-gyp@8.4.1:
|
||||||
resolution: {integrity: sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==}
|
resolution: {integrity: sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==}
|
||||||
engines: {node: '>= 10.12.0'}
|
engines: {node: '>= 10.12.0'}
|
||||||
@ -1539,9 +1499,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==}
|
resolution: {integrity: sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
tseep@1.2.2:
|
|
||||||
resolution: {integrity: sha512-GgPFuNx+08UaYBYmJQmuI86ykYa2PUUtfXAYb4MLRHGunSCp8k9N+dbsR4PK1yk4/zV9q4e4PrNg8ymXqGYaYA==}
|
|
||||||
|
|
||||||
tslib@2.7.0:
|
tslib@2.7.0:
|
||||||
resolution: {integrity: sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==}
|
resolution: {integrity: sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==}
|
||||||
|
|
||||||
@ -1559,9 +1516,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==}
|
resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==}
|
||||||
engines: {node: '>= 0.6'}
|
engines: {node: '>= 0.6'}
|
||||||
|
|
||||||
typescript-lru-cache@2.0.0:
|
|
||||||
resolution: {integrity: sha512-Jp57Qyy8wXeMkdNuZiglE6v2Cypg13eDA1chHwDG6kq51X7gk4K7P7HaDdzZKCxkegXkVHNcPD0n5aW6OZH3aA==}
|
|
||||||
|
|
||||||
typescript@5.6.2:
|
typescript@5.6.2:
|
||||||
resolution: {integrity: sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==}
|
resolution: {integrity: sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==}
|
||||||
engines: {node: '>=14.17'}
|
engines: {node: '>=14.17'}
|
||||||
@ -1583,10 +1537,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==}
|
resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==}
|
||||||
engines: {node: '>= 4.0.0'}
|
engines: {node: '>= 4.0.0'}
|
||||||
|
|
||||||
utf8-buffer@1.0.0:
|
|
||||||
resolution: {integrity: sha512-ueuhzvWnp5JU5CiGSY4WdKbiN/PO2AZ/lpeLiz2l38qwdLy/cW40XobgyuIWucNyum0B33bVB0owjFCeGBSLqg==}
|
|
||||||
engines: {node: '>=8'}
|
|
||||||
|
|
||||||
util-deprecate@1.0.2:
|
util-deprecate@1.0.2:
|
||||||
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
|
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
|
||||||
|
|
||||||
@ -1594,10 +1544,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==}
|
resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==}
|
||||||
engines: {node: '>= 0.8'}
|
engines: {node: '>= 0.8'}
|
||||||
|
|
||||||
web-streams-polyfill@3.3.3:
|
|
||||||
resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==}
|
|
||||||
engines: {node: '>= 8'}
|
|
||||||
|
|
||||||
websocket-polyfill@1.0.0:
|
websocket-polyfill@1.0.0:
|
||||||
resolution: {integrity: sha512-QwfEy8jcOOCVO9su9UP+msEmhZa4a9WSJfePIdCT8GxwVl2Z9toM7nCqFfDDxA/sRmxgf1KNiwL6PXvjJ9qRxw==}
|
resolution: {integrity: sha512-QwfEy8jcOOCVO9su9UP+msEmhZa4a9WSJfePIdCT8GxwVl2Z9toM7nCqFfDDxA/sRmxgf1KNiwL6PXvjJ9qRxw==}
|
||||||
|
|
||||||
@ -1867,18 +1813,12 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@noble/hashes': 1.3.2
|
'@noble/hashes': 1.3.2
|
||||||
|
|
||||||
'@noble/curves@1.6.0':
|
|
||||||
dependencies:
|
|
||||||
'@noble/hashes': 1.5.0
|
|
||||||
|
|
||||||
'@noble/hashes@1.3.1': {}
|
'@noble/hashes@1.3.1': {}
|
||||||
|
|
||||||
'@noble/hashes@1.3.2': {}
|
'@noble/hashes@1.3.2': {}
|
||||||
|
|
||||||
'@noble/hashes@1.5.0': {}
|
'@noble/hashes@1.5.0': {}
|
||||||
|
|
||||||
'@noble/secp256k1@2.1.0': {}
|
|
||||||
|
|
||||||
'@nodelib/fs.scandir@2.1.5':
|
'@nodelib/fs.scandir@2.1.5':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@nodelib/fs.stat': 2.0.5
|
'@nodelib/fs.stat': 2.0.5
|
||||||
@ -1891,26 +1831,6 @@ snapshots:
|
|||||||
'@nodelib/fs.scandir': 2.1.5
|
'@nodelib/fs.scandir': 2.1.5
|
||||||
fastq: 1.17.1
|
fastq: 1.17.1
|
||||||
|
|
||||||
'@nostr-dev-kit/ndk@2.10.0(typescript@5.6.2)':
|
|
||||||
dependencies:
|
|
||||||
'@noble/curves': 1.6.0
|
|
||||||
'@noble/hashes': 1.5.0
|
|
||||||
'@noble/secp256k1': 2.1.0
|
|
||||||
'@scure/base': 1.1.9
|
|
||||||
debug: 4.3.7(supports-color@5.5.0)
|
|
||||||
light-bolt11-decoder: 3.1.1
|
|
||||||
node-fetch: 3.3.2
|
|
||||||
nostr-tools: 2.7.2(typescript@5.6.2)
|
|
||||||
tseep: 1.2.2
|
|
||||||
typescript-lru-cache: 2.0.0
|
|
||||||
utf8-buffer: 1.0.0
|
|
||||||
websocket-polyfill: 1.0.0
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- bufferutil
|
|
||||||
- supports-color
|
|
||||||
- typescript
|
|
||||||
- utf-8-validate
|
|
||||||
|
|
||||||
'@npmcli/fs@1.1.1':
|
'@npmcli/fs@1.1.1':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@gar/promisify': 1.1.3
|
'@gar/promisify': 1.1.3
|
||||||
@ -1960,8 +1880,6 @@ snapshots:
|
|||||||
|
|
||||||
'@scure/base@1.1.1': {}
|
'@scure/base@1.1.1': {}
|
||||||
|
|
||||||
'@scure/base@1.1.9': {}
|
|
||||||
|
|
||||||
'@scure/bip32@1.3.1':
|
'@scure/bip32@1.3.1':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@noble/curves': 1.1.0
|
'@noble/curves': 1.1.0
|
||||||
@ -2360,8 +2278,6 @@ snapshots:
|
|||||||
shebang-command: 1.2.0
|
shebang-command: 1.2.0
|
||||||
which: 1.3.1
|
which: 1.3.1
|
||||||
|
|
||||||
data-uri-to-buffer@4.0.1: {}
|
|
||||||
|
|
||||||
debug@2.6.9:
|
debug@2.6.9:
|
||||||
dependencies:
|
dependencies:
|
||||||
ms: 2.0.0
|
ms: 2.0.0
|
||||||
@ -2457,11 +2373,6 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
reusify: 1.0.4
|
reusify: 1.0.4
|
||||||
|
|
||||||
fetch-blob@3.2.0:
|
|
||||||
dependencies:
|
|
||||||
node-domexception: 1.0.0
|
|
||||||
web-streams-polyfill: 3.3.3
|
|
||||||
|
|
||||||
file-uri-to-path@1.0.0: {}
|
file-uri-to-path@1.0.0: {}
|
||||||
|
|
||||||
fill-range@7.1.1:
|
fill-range@7.1.1:
|
||||||
@ -2475,10 +2386,6 @@ snapshots:
|
|||||||
|
|
||||||
follow-redirects@1.15.9: {}
|
follow-redirects@1.15.9: {}
|
||||||
|
|
||||||
formdata-polyfill@4.0.10:
|
|
||||||
dependencies:
|
|
||||||
fetch-blob: 3.2.0
|
|
||||||
|
|
||||||
fresh@0.5.2: {}
|
fresh@0.5.2: {}
|
||||||
|
|
||||||
fs-constants@1.0.0: {}
|
fs-constants@1.0.0: {}
|
||||||
@ -2766,10 +2673,6 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
light-bolt11-decoder@3.1.1:
|
|
||||||
dependencies:
|
|
||||||
'@scure/base': 1.1.1
|
|
||||||
|
|
||||||
locate-path@5.0.0:
|
locate-path@5.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
p-locate: 4.1.0
|
p-locate: 4.1.0
|
||||||
@ -2908,14 +2811,6 @@ snapshots:
|
|||||||
|
|
||||||
node-addon-api@7.1.1: {}
|
node-addon-api@7.1.1: {}
|
||||||
|
|
||||||
node-domexception@1.0.0: {}
|
|
||||||
|
|
||||||
node-fetch@3.3.2:
|
|
||||||
dependencies:
|
|
||||||
data-uri-to-buffer: 4.0.1
|
|
||||||
fetch-blob: 3.2.0
|
|
||||||
formdata-polyfill: 4.0.10
|
|
||||||
|
|
||||||
node-gyp@8.4.1:
|
node-gyp@8.4.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
env-paths: 2.2.1
|
env-paths: 2.2.1
|
||||||
@ -3295,8 +3190,6 @@ snapshots:
|
|||||||
|
|
||||||
touch@3.1.1: {}
|
touch@3.1.1: {}
|
||||||
|
|
||||||
tseep@1.2.2: {}
|
|
||||||
|
|
||||||
tslib@2.7.0: {}
|
tslib@2.7.0: {}
|
||||||
|
|
||||||
tsscmp@1.0.6: {}
|
tsscmp@1.0.6: {}
|
||||||
@ -3312,8 +3205,6 @@ snapshots:
|
|||||||
media-typer: 0.3.0
|
media-typer: 0.3.0
|
||||||
mime-types: 2.1.35
|
mime-types: 2.1.35
|
||||||
|
|
||||||
typescript-lru-cache@2.0.0: {}
|
|
||||||
|
|
||||||
typescript@5.6.2: {}
|
typescript@5.6.2: {}
|
||||||
|
|
||||||
undefsafe@2.0.5: {}
|
undefsafe@2.0.5: {}
|
||||||
@ -3332,14 +3223,10 @@ snapshots:
|
|||||||
|
|
||||||
universalify@0.1.2: {}
|
universalify@0.1.2: {}
|
||||||
|
|
||||||
utf8-buffer@1.0.0: {}
|
|
||||||
|
|
||||||
util-deprecate@1.0.2: {}
|
util-deprecate@1.0.2: {}
|
||||||
|
|
||||||
vary@1.1.2: {}
|
vary@1.1.2: {}
|
||||||
|
|
||||||
web-streams-polyfill@3.3.3: {}
|
|
||||||
|
|
||||||
websocket-polyfill@1.0.0:
|
websocket-polyfill@1.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
import2: 1.0.3
|
import2: 1.0.3
|
||||||
|
@ -1,16 +1,11 @@
|
|||||||
import { getServersFromServerListEvent, USER_BLOSSOM_SERVER_LIST_KIND } from "blossom-client-sdk";
|
import { getServersFromServerListEvent, USER_BLOSSOM_SERVER_LIST_KIND } from "blossom-client-sdk";
|
||||||
import { NDKRelaySet } from "@nostr-dev-kit/ndk";
|
|
||||||
|
|
||||||
import ndk from "./ndk.js";
|
|
||||||
import { BLOSSOM_SERVERS, MAX_FILE_SIZE } from "./env.js";
|
import { BLOSSOM_SERVERS, MAX_FILE_SIZE } from "./env.js";
|
||||||
import { makeRequestWithAbort } from "./helpers/http.js";
|
import { makeRequestWithAbort } from "./helpers/http.js";
|
||||||
|
import pool from "./nostr.js";
|
||||||
|
|
||||||
export async function getUserBlossomServers(pubkey: string, relays?: string[]) {
|
export async function getUserBlossomServers(pubkey: string, relays: string[]) {
|
||||||
const blossomServersEvent = await ndk.fetchEvent(
|
const blossomServersEvent = await pool.get(relays, { kinds: [USER_BLOSSOM_SERVER_LIST_KIND], authors: [pubkey] });
|
||||||
[{ kinds: [USER_BLOSSOM_SERVER_LIST_KIND], authors: [pubkey] }],
|
|
||||||
{},
|
|
||||||
relays ? NDKRelaySet.fromRelayUrls(relays, ndk, true) : undefined,
|
|
||||||
);
|
|
||||||
|
|
||||||
return blossomServersEvent ? getServersFromServerListEvent(blossomServersEvent).map((u) => u.toString()) : undefined;
|
return blossomServersEvent ? getServersFromServerListEvent(blossomServersEvent).map((u) => u.toString()) : undefined;
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,14 @@ store?.on("error", (err) => {
|
|||||||
|
|
||||||
const opts = store ? { store } : {};
|
const opts = store ? { store } : {};
|
||||||
|
|
||||||
|
/** domain -> pubkey */
|
||||||
|
export const userDomains = new Keyv({
|
||||||
|
...opts,
|
||||||
|
namespace: "domains",
|
||||||
|
// cache domains for an hour
|
||||||
|
ttl: 60 * 60 * 1000,
|
||||||
|
});
|
||||||
|
|
||||||
/** pubkey -> blossom servers */
|
/** pubkey -> blossom servers */
|
||||||
export const userServers = new Keyv({
|
export const userServers = new Keyv({
|
||||||
...opts,
|
...opts,
|
||||||
|
13
src/env.ts
13
src/env.ts
@ -1,13 +1,16 @@
|
|||||||
import "dotenv/config";
|
import "dotenv/config";
|
||||||
import xbytes from "xbytes";
|
import xbytes from "xbytes";
|
||||||
|
|
||||||
const LOOKUP_RELAYS = process.env.LOOKUP_RELAYS?.split(",") ?? ["wss://user.kindpag.es/", "wss://purplepag.es/"];
|
const LOOKUP_RELAYS = process.env.LOOKUP_RELAYS?.split(",").map((u) => u.trim()) ?? [
|
||||||
const NOSTR_RELAYS = process.env.NOSTR_RELAYS?.split(",") ?? [];
|
"wss://user.kindpag.es/",
|
||||||
const BLOSSOM_SERVERS = process.env.BLOSSOM_SERVERS?.split(",") ?? [];
|
"wss://purplepag.es/",
|
||||||
|
];
|
||||||
|
const SUBSCRIPTION_RELAYS = process.env.SUBSCRIPTION_RELAYS?.split(",").map((u) => u.trim()) ?? [];
|
||||||
|
const BLOSSOM_SERVERS = process.env.BLOSSOM_SERVERS?.split(",").map((u) => u.trim()) ?? [];
|
||||||
|
|
||||||
const MAX_FILE_SIZE = process.env.MAX_FILE_SIZE ? xbytes.parseSize(process.env.MAX_FILE_SIZE) : Infinity;
|
const MAX_FILE_SIZE = process.env.MAX_FILE_SIZE ? xbytes.parseSize(process.env.MAX_FILE_SIZE) : Infinity;
|
||||||
|
|
||||||
const NGINX_HOST = process.env.NGINX_HOST;
|
const NGINX_CACHE_DIR = process.env.NGINX_CACHE_DIR;
|
||||||
const CACHE_PATH = process.env.CACHE_PATH;
|
const CACHE_PATH = process.env.CACHE_PATH;
|
||||||
|
|
||||||
export { NOSTR_RELAYS, LOOKUP_RELAYS, BLOSSOM_SERVERS, MAX_FILE_SIZE, NGINX_HOST, CACHE_PATH };
|
export { SUBSCRIPTION_RELAYS, LOOKUP_RELAYS, BLOSSOM_SERVERS, MAX_FILE_SIZE, NGINX_CACHE_DIR, CACHE_PATH };
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import { extname, isAbsolute, join } from "path";
|
import { extname, isAbsolute, join } from "path";
|
||||||
import { NSITE_KIND } from "./const.js";
|
import { NSITE_KIND } from "./const.js";
|
||||||
import ndk from "./ndk.js";
|
import { requestEvents } from "./nostr.js";
|
||||||
import { NDKRelaySet } from "@nostr-dev-kit/ndk";
|
|
||||||
|
|
||||||
export function getSearchPaths(path: string) {
|
export function getSearchPaths(path: string) {
|
||||||
const paths = [path];
|
const paths = [path];
|
||||||
@ -9,11 +8,6 @@ export function getSearchPaths(path: string) {
|
|||||||
// if the path does not have an extension, also look for index.html
|
// if the path does not have an extension, also look for index.html
|
||||||
if (extname(path) === "") paths.push(join(path, "index.html"));
|
if (extname(path) === "") paths.push(join(path, "index.html"));
|
||||||
|
|
||||||
// also look for relative paths
|
|
||||||
for (const p of Array.from(paths)) {
|
|
||||||
if (isAbsolute(p)) paths.push(p.replace(/^\//, ""));
|
|
||||||
}
|
|
||||||
|
|
||||||
return paths.filter((p) => !!p);
|
return paths.filter((p) => !!p);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,13 +23,10 @@ export function parseNsiteEvent(event: { pubkey: string; tags: string[][] }) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getNsiteBlobs(pubkey: string, path: string, relays?: string[]) {
|
export async function getNsiteBlobs(pubkey: string, path: string, relays: string[]) {
|
||||||
const paths = getSearchPaths(path);
|
// NOTE: hack, remove "/" paths since it breaks some relays
|
||||||
const events = await ndk.fetchEvents(
|
const paths = getSearchPaths(path).filter((p) => p !== "/");
|
||||||
{ kinds: [NSITE_KIND], "#d": paths, authors: [pubkey] },
|
const events = await requestEvents(relays, { kinds: [NSITE_KIND], "#d": paths, authors: [pubkey] });
|
||||||
{},
|
|
||||||
relays && NDKRelaySet.fromRelayUrls(relays, ndk, true),
|
|
||||||
);
|
|
||||||
|
|
||||||
return Array.from(events)
|
return Array.from(events)
|
||||||
.map(parseNsiteEvent)
|
.map(parseNsiteEvent)
|
||||||
|
68
src/index.ts
68
src/index.ts
@ -10,11 +10,13 @@ import mime from "mime";
|
|||||||
import morgan from "koa-morgan";
|
import morgan from "koa-morgan";
|
||||||
|
|
||||||
import { resolveNpubFromHostname } from "./helpers/dns.js";
|
import { resolveNpubFromHostname } from "./helpers/dns.js";
|
||||||
import { getNsiteBlobs } from "./events.js";
|
import { getNsiteBlobs, parseNsiteEvent } from "./events.js";
|
||||||
import { downloadFile, getUserBlossomServers } from "./blossom.js";
|
import { downloadFile, getUserBlossomServers } from "./blossom.js";
|
||||||
import { BLOSSOM_SERVERS } from "./env.js";
|
import { BLOSSOM_SERVERS, NGINX_CACHE_DIR, SUBSCRIPTION_RELAYS } from "./env.js";
|
||||||
import { userRelays, userServers } from "./cache.js";
|
import { userDomains, userRelays, userServers } from "./cache.js";
|
||||||
import { getUserOutboxes } from "./ndk.js";
|
import { NSITE_KIND } from "./const.js";
|
||||||
|
import { invalidatePubkeyPath } from "./nginx.js";
|
||||||
|
import pool, { getUserOutboxes, subscribeForEvents } from "./nostr.js";
|
||||||
|
|
||||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
|
|
||||||
@ -45,12 +47,29 @@ app.use(async (ctx, next) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// map pubkeys to folders in sites dir
|
// handle nsite requests
|
||||||
app.use(async (ctx, next) => {
|
app.use(async (ctx, next) => {
|
||||||
const pubkey = (ctx.state.pubkey = await resolveNpubFromHostname(ctx.hostname));
|
let pubkey = await userDomains.get<string | undefined>(ctx.hostname);
|
||||||
|
|
||||||
|
// resolve pubkey if not in cache
|
||||||
|
if (!pubkey) {
|
||||||
|
console.log(`${ctx.hostname}: Resolving`);
|
||||||
|
pubkey = await resolveNpubFromHostname(ctx.hostname);
|
||||||
|
|
||||||
|
if (pubkey) {
|
||||||
|
await userDomains.set(ctx.hostname, pubkey);
|
||||||
|
console.log(`${ctx.hostname}: Found ${pubkey}`);
|
||||||
|
} else {
|
||||||
|
await userDomains.set(ctx.hostname, "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (pubkey) {
|
if (pubkey) {
|
||||||
|
ctx.state.pubkey = pubkey;
|
||||||
|
|
||||||
let relays = await userRelays.get<string[] | undefined>(pubkey);
|
let relays = await userRelays.get<string[] | undefined>(pubkey);
|
||||||
|
|
||||||
|
// fetch relays if not in cache
|
||||||
if (!relays) {
|
if (!relays) {
|
||||||
console.log(`${pubkey}: Fetching relays`);
|
console.log(`${pubkey}: Fetching relays`);
|
||||||
|
|
||||||
@ -69,12 +88,15 @@ app.use(async (ctx, next) => {
|
|||||||
const blobs = await getNsiteBlobs(pubkey, ctx.path, relays);
|
const blobs = await getNsiteBlobs(pubkey, ctx.path, relays);
|
||||||
|
|
||||||
if (blobs.length === 0) {
|
if (blobs.length === 0) {
|
||||||
|
console.log(`${pubkey}: Found 0 events`);
|
||||||
ctx.status = 404;
|
ctx.status = 404;
|
||||||
ctx.body = "Not Found";
|
ctx.body = "Not Found";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let servers = await userServers.get<string[] | undefined>(pubkey);
|
let servers = await userServers.get<string[] | undefined>(pubkey);
|
||||||
|
|
||||||
|
// fetch blossom servers if not in cache
|
||||||
if (!servers) {
|
if (!servers) {
|
||||||
console.log(`${pubkey}: Searching for blossom servers`);
|
console.log(`${pubkey}: Searching for blossom servers`);
|
||||||
servers = await getUserBlossomServers(pubkey, relays);
|
servers = await getUserBlossomServers(pubkey, relays);
|
||||||
@ -88,6 +110,8 @@ app.use(async (ctx, next) => {
|
|||||||
console.log(`${pubkey}: Failed to find servers`);
|
console.log(`${pubkey}: Failed to find servers`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// always fetch from additional servers
|
||||||
servers.push(...BLOSSOM_SERVERS);
|
servers.push(...BLOSSOM_SERVERS);
|
||||||
|
|
||||||
for (const blob of blobs) {
|
for (const blob of blobs) {
|
||||||
@ -121,12 +145,40 @@ try {
|
|||||||
app.use(serve(www));
|
app.use(serve(www));
|
||||||
}
|
}
|
||||||
|
|
||||||
app.listen(process.env.PORT || 3000, () => {
|
app.listen(
|
||||||
console.log("Started on port", process.env.PORT || 3000);
|
{
|
||||||
|
port: process.env.NSITE_PORT || 3000,
|
||||||
|
host: process.env.NSITE_HOST || "0.0.0.0",
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
console.log("Started on port", process.env.PORT || 3000);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// invalidate nginx cache on new events
|
||||||
|
if (NGINX_CACHE_DIR && SUBSCRIPTION_RELAYS.length > 0) {
|
||||||
|
console.log(`Listening for new nsite events`);
|
||||||
|
|
||||||
|
subscribeForEvents(SUBSCRIPTION_RELAYS, async (event) => {
|
||||||
|
try {
|
||||||
|
const nsite = parseNsiteEvent(event);
|
||||||
|
if (nsite) {
|
||||||
|
console.log(`${nsite.pubkey}: Invalidating ${nsite.path}`);
|
||||||
|
await invalidatePubkeyPath(nsite.pubkey, nsite.path);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log(`Failed to invalidate ${event.id}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
process.on("unhandledRejection", (reason, promise) => {
|
||||||
|
console.error("Unhandled Rejection at:", promise, "reason:", reason);
|
||||||
});
|
});
|
||||||
|
|
||||||
async function shutdown() {
|
async function shutdown() {
|
||||||
console.log("Shutting down...");
|
console.log("Shutting down...");
|
||||||
|
pool.destroy();
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
17
src/ndk.ts
17
src/ndk.ts
@ -1,17 +0,0 @@
|
|||||||
import NDK from "@nostr-dev-kit/ndk";
|
|
||||||
import { LOOKUP_RELAYS, NOSTR_RELAYS } from "./env.js";
|
|
||||||
|
|
||||||
const ndk = new NDK({
|
|
||||||
explicitRelayUrls: [...LOOKUP_RELAYS, ...NOSTR_RELAYS],
|
|
||||||
});
|
|
||||||
|
|
||||||
ndk.connect();
|
|
||||||
|
|
||||||
export async function getUserOutboxes(pubkey: string) {
|
|
||||||
const mailboxes = await ndk.fetchEvent({ kinds: [10002], authors: [pubkey] });
|
|
||||||
if (!mailboxes) return;
|
|
||||||
|
|
||||||
return mailboxes.tags.filter((t) => t[0] === "r" && (t[2] === undefined || t[2] === "write")).map((t) => t[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ndk;
|
|
55
src/nginx.ts
55
src/nginx.ts
@ -1,26 +1,37 @@
|
|||||||
import http from "node:http";
|
import pfs from "node:fs/promises";
|
||||||
import { NGINX_HOST } from "./env.js";
|
import crypto from "node:crypto";
|
||||||
|
import { join } from "node:path";
|
||||||
|
|
||||||
export function invalidateCache(host: string, path: string) {
|
import { NGINX_CACHE_DIR } from "./env.js";
|
||||||
if (!NGINX_HOST) return Promise.resolve(false);
|
import { userDomains } from "./cache.js";
|
||||||
|
|
||||||
return new Promise<boolean>((resolve, reject) => {
|
export async function invalidatePubkeyPath(pubkey: string, path: string) {
|
||||||
const req = http.request(
|
const iterator = userDomains.iterator?.(undefined);
|
||||||
{
|
if (!iterator) return;
|
||||||
hostname: NGINX_HOST,
|
|
||||||
method: "GET",
|
|
||||||
port: 80,
|
|
||||||
path,
|
|
||||||
headers: {
|
|
||||||
Host: host,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
(res) => {
|
|
||||||
if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) resolve(true);
|
|
||||||
else reject(new Error("Failed to invalidate"));
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
req.end();
|
const promises: Promise<boolean | undefined>[] = [];
|
||||||
});
|
for await (const [domain, key] of iterator) {
|
||||||
|
if (key === pubkey) {
|
||||||
|
promises.push(invalidateNginxCache(domain, path));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.allSettled(promises);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function invalidateNginxCache(host: string, path: string) {
|
||||||
|
if (!NGINX_CACHE_DIR) return Promise.resolve(false);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const key = `${host}${path}`;
|
||||||
|
const md5 = crypto.createHash("md5").update(key).digest("hex");
|
||||||
|
|
||||||
|
// NOTE: hard coded to cache levels 1:2
|
||||||
|
const cachePath = join(NGINX_CACHE_DIR, md5.slice(-1), md5.slice(-3, -1), md5);
|
||||||
|
await pfs.rm(cachePath);
|
||||||
|
|
||||||
|
console.log(`Invalidated ${key} (${md5})`);
|
||||||
|
} catch (error) {
|
||||||
|
// ignore errors
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
38
src/nostr.ts
Normal file
38
src/nostr.ts
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import { Filter, NostrEvent, SimplePool } from "nostr-tools";
|
||||||
|
import { LOOKUP_RELAYS } from "./env.js";
|
||||||
|
import { NSITE_KIND } from "./const.js";
|
||||||
|
|
||||||
|
const pool = new SimplePool();
|
||||||
|
|
||||||
|
export async function getUserOutboxes(pubkey: string) {
|
||||||
|
const mailboxes = await pool.get(LOOKUP_RELAYS, { kinds: [10002], authors: [pubkey] });
|
||||||
|
if (!mailboxes) return;
|
||||||
|
|
||||||
|
return mailboxes.tags.filter((t) => t[0] === "r" && (t[2] === undefined || t[2] === "write")).map((t) => t[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function subscribeForEvents(relays: string[], onevent: (event: NostrEvent) => any) {
|
||||||
|
return pool.subscribeMany(relays, [{ kinds: [NSITE_KIND], since: Math.round(Date.now() / 1000) - 60 * 60 }], {
|
||||||
|
onevent,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function requestEvents(relays: string[], filter: Filter) {
|
||||||
|
return new Promise<NostrEvent[]>(async (res, rej) => {
|
||||||
|
const events: NostrEvent[] = [];
|
||||||
|
|
||||||
|
await Promise.allSettled(relays.map((url) => pool.ensureRelay(url).catch((e) => {})));
|
||||||
|
|
||||||
|
const sub = pool.subscribeMany(relays, [filter], {
|
||||||
|
onevent: (e) => events.push(e),
|
||||||
|
oneose: () => sub.close(),
|
||||||
|
onclose: (reasons) => {
|
||||||
|
const errs = reasons.filter((r) => r !== "closed by caller");
|
||||||
|
if (errs.length > 0 && events.length === 0) rej(new Error(errs.join(", ")));
|
||||||
|
else res(events);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export default pool;
|
22
supervisord.conf
Normal file
22
supervisord.conf
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
[supervisord]
|
||||||
|
nodaemon=true
|
||||||
|
user=root
|
||||||
|
|
||||||
|
[program:nginx]
|
||||||
|
command=nginx -g "daemon off;"
|
||||||
|
autostart=true
|
||||||
|
autorestart=true
|
||||||
|
stdout_logfile=/dev/stdout
|
||||||
|
stdout_logfile_maxbytes=0
|
||||||
|
stderr_logfile=/dev/stderr
|
||||||
|
stderr_logfile_maxbytes=0
|
||||||
|
|
||||||
|
[program:nsite]
|
||||||
|
command=node /app
|
||||||
|
autostart=true
|
||||||
|
autorestart=true
|
||||||
|
user=root
|
||||||
|
stdout_logfile=/dev/stdout
|
||||||
|
stdout_logfile_maxbytes=0
|
||||||
|
stderr_logfile=/dev/stderr
|
||||||
|
stderr_logfile_maxbytes=0
|
Loading…
x
Reference in New Issue
Block a user