chore: sync submodule state (parent ref update)

Made-with: Cursor
This commit is contained in:
defiQUG
2026-03-02 12:14:13 -08:00
parent 43a7b88e2a
commit 041fae1574
223 changed files with 12940 additions and 11756 deletions

View File

@@ -1,5 +1,6 @@
# Production Environment Configuration Template
# Copy this to /home/explorer/explorer-monorepo/.env and fill in values
# See docs/PRODUCTION_CHECKLIST.md for JWT_SECRET and migration steps before going live.
# ============================================
# Database Configuration
@@ -105,6 +106,8 @@ SOUL_MACHINES_API_SECRET=
# ============================================
# Security
# ============================================
# Optional: restrict CORS (default *). Example: https://explorer.d-bis.org
CORS_ALLOWED_ORIGIN=
JWT_SECRET=CHANGE_THIS_JWT_SECRET
ENCRYPTION_KEY=CHANGE_THIS_ENCRYPTION_KEY_32_BYTES

View File

@@ -0,0 +1,16 @@
#!/bin/bash
# Add CSP with unsafe-eval to HTTP location = / in blockscout nginx (for NPM proxy on :80)
set -e
CONFIG=/etc/nginx/sites-available/blockscout
if grep -q "Content-Security-Policy" "$CONFIG" 2>/dev/null; then
echo "CSP already present"
else
# Insert CSP line after add_header Cache-Control in first location = /
sed -i '/location = \/ {/,/try_files \/index.html =404;/{
/add_header Cache-Control "no-store, no-cache, must-revalidate"/a\
add_header Content-Security-Policy "default-src '\''self'\''; script-src '\''self'\'' '\''unsafe-inline'\'' '\''unsafe-eval'\'' https://cdn.jsdelivr.net https://unpkg.com https://cdnjs.cloudflare.com; style-src '\''self'\'' '\''unsafe-inline'\'' https://cdnjs.cloudflare.com; img-src '\''self'\'' data: https:; font-src '\''self'\'' https://cdnjs.cloudflare.com; connect-src '\''self'\'' https://explorer.d-bis.org wss://explorer.d-bis.org https://rpc-http-pub.d-bis.org wss://rpc-ws-pub.d-bis.org http://192.168.11.221:8545 ws://192.168.11.221:8546;" always;
}' "$CONFIG"
echo "Added CSP to HTTP location = /"
fi
nginx -t && systemctl reload nginx
echo "Done"

View File

@@ -0,0 +1,12 @@
# deployment-common
Reusable deployment snippets (nginx, systemd, Cloudflare, fail2ban).
Use as reference or copy into your project.
## Contents
- **nginx-api-location.conf** Generic `location /api/` proxy snippet (upstream host/port to be adjusted).
- **systemd-api-service.example** Example systemd unit for a REST API (env and paths to be adjusted).
- **cloudflare / fail2ban** See parent `../cloudflare/` and `../fail2ban/` for full configs.
When this is a separate repo, add as submodule at `deployment/common`.

View File

@@ -0,0 +1,16 @@
# Generic snippet: proxy /api/ to a backend (Blockscout, Go API, etc.)
# Include in your server block. Replace upstream host/port as needed.
location /api/ {
proxy_pass http://127.0.0.1:4000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 300s;
proxy_connect_timeout 75s;
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS";
add_header Access-Control-Allow-Headers "Content-Type";
}

View File

@@ -0,0 +1,21 @@
# Example systemd unit for a REST API (e.g. explorer API on port 8080)
# Copy to /etc/systemd/system/explorer-api.service and adjust paths/env.
[Unit]
Description=Explorer REST API
After=network.target postgresql.service
[Service]
Type=simple
User=explorer
WorkingDirectory=/opt/explorer
Environment=PORT=8080
Environment=DB_HOST=localhost
Environment=DB_NAME=explorer
Environment=CHAIN_ID=138
ExecStart=/opt/explorer/bin/api-server
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target

View File

@@ -1,207 +0,0 @@
# Rate limiting zones
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=general_limit:10m rate=50r/s;
limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
# Upstream servers
upstream explorer_api {
server 127.0.0.1:8080;
keepalive 32;
}
upstream explorer_frontend {
server 127.0.0.1:3000;
keepalive 32;
}
# Redirect HTTP to HTTPS
server {
listen 80;
listen [::]:80;
server_name explorer.d-bis.org www.explorer.d-bis.org;
# Allow Let's Encrypt validation
location /.well-known/acme-challenge/ {
root /var/www/html;
}
# Redirect all other traffic to HTTPS
location / {
return 301 https://$server_name$request_uri;
}
}
# Main HTTPS server
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name explorer.d-bis.org www.explorer.d-bis.org;
# SSL Configuration (Cloudflare handles SSL, but we can add local certs too)
# ssl_certificate /etc/letsencrypt/live/explorer.d-bis.org/fullchain.pem;
# ssl_certificate_key /etc/letsencrypt/live/explorer.d-bis.org/privkey.pem;
# ssl_protocols TLSv1.2 TLSv1.3;
# ssl_ciphers HIGH:!aNULL:!MD5;
# ssl_prefer_server_ciphers on;
# Security headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;
# Content Security Policy (adjust as needed)
# CSP: unsafe-eval required by ethers.js v5 UMD from CDN
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cdn.jsdelivr.net https://unpkg.com https://cdnjs.cloudflare.com; style-src 'self' 'unsafe-inline' https://cdnjs.cloudflare.com; img-src 'self' data: https:; font-src 'self' data: https://cdnjs.cloudflare.com; connect-src 'self' https://api.cloudflare.com https://explorer.d-bis.org wss://explorer.d-bis.org https://rpc-http-pub.d-bis.org wss://rpc-ws-pub.d-bis.org http://192.168.11.221:8545 ws://192.168.11.221:8546;" always;
# Logging
access_log /var/log/nginx/explorer-access.log combined buffer=32k flush=5m;
error_log /var/log/nginx/explorer-error.log warn;
# Client settings
client_max_body_size 10M;
client_body_timeout 60s;
client_header_timeout 60s;
# Gzip compression
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_min_length 1000;
gzip_types
text/plain
text/css
text/xml
text/javascript
application/json
application/javascript
application/xml+rss
application/rss+xml
font/truetype
font/opentype
application/vnd.ms-fontobject
image/svg+xml;
# Brotli compression (if available)
# brotli on;
# brotli_comp_level 6;
# brotli_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
# Frontend
location / {
limit_req zone=general_limit burst=20 nodelay;
limit_conn conn_limit 10;
proxy_pass http://explorer_frontend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;
proxy_cache_bypass $http_upgrade;
proxy_read_timeout 300s;
proxy_connect_timeout 75s;
proxy_send_timeout 300s;
# Buffering
proxy_buffering on;
proxy_buffer_size 4k;
proxy_buffers 8 4k;
proxy_busy_buffers_size 8k;
}
# API endpoints
location /api/ {
limit_req zone=api_limit burst=20 nodelay;
limit_conn conn_limit 5;
proxy_pass http://explorer_api;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header Connection "";
proxy_read_timeout 300s;
proxy_connect_timeout 75s;
proxy_send_timeout 300s;
# Disable buffering for API responses
proxy_buffering off;
# CORS headers (Cloudflare will also add these)
add_header Access-Control-Allow-Origin "*" always;
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS" always;
add_header Access-Control-Allow-Headers "Content-Type, X-API-Key, Authorization" always;
# Handle preflight
if ($request_method = OPTIONS) {
add_header Access-Control-Allow-Origin "*";
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS";
add_header Access-Control-Allow-Headers "Content-Type, X-API-Key, Authorization";
add_header Access-Control-Max-Age 1728000;
add_header Content-Type "text/plain; charset=utf-8";
add_header Content-Length 0;
return 204;
}
}
# WebSocket support
location /ws {
proxy_pass http://explorer_api;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 86400s;
proxy_send_timeout 86400s;
proxy_connect_timeout 75s;
}
# Static files caching
location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf|eot|webp|avif)$ {
expires 1y;
add_header Cache-Control "public, immutable";
add_header X-Content-Type-Options "nosniff";
access_log off;
log_not_found off;
}
# Health check endpoint (internal only)
location /health {
access_log off;
proxy_pass http://explorer_api/health;
proxy_set_header Host $host;
}
# Block access to sensitive files
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
location ~ \.(env|git|gitignore|md|sh)$ {
deny all;
access_log off;
log_not_found off;
}
}