feat: add HTTPS RPC proxy for MetaMask mobile support
Some checks failed
Deploy / deploy (push) Has been cancelled

Nginx SSL reverse proxy (port 8443) in front of Geth using Let's
Encrypt cert via Cloudflare DNS challenge. MetaMask mobile requires
HTTPS for custom RPC URLs.

Also adds AFC token icon served from bridge API static files.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
consultoria-as
2026-02-26 01:34:12 +00:00
parent 14279a878c
commit eac2671529
5 changed files with 209 additions and 0 deletions

View File

@@ -134,6 +134,17 @@ services:
limits:
memory: 1G
rpc-ssl:
image: nginx:alpine
restart: unless-stopped
depends_on:
- geth
volumes:
- ./nginx/rpc-ssl.conf:/etc/nginx/nginx.conf:ro
- certbot_etc:/etc/letsencrypt:ro
ports:
- "8443:8443"
afc-bridge:
build:
context: ../services/afc-bridge
@@ -160,3 +171,6 @@ volumes:
minecraft_ftb_data:
geth_data:
afc_bridge_data:
certbot_etc:
external: true
name: docker_certbot_etc

32
docker/nginx/rpc-ssl.conf Normal file
View File

@@ -0,0 +1,32 @@
events {
worker_connections 256;
}
http {
server {
listen 8443 ssl;
server_name play.consultoria-as.com;
ssl_certificate /etc/letsencrypt/live/play.consultoria-as.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/play.consultoria-as.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
# Geth JSON-RPC proxy
location / {
proxy_pass http://geth:8545;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Content-Type application/json;
# CORS for MetaMask
add_header Access-Control-Allow-Origin * always;
add_header Access-Control-Allow-Methods "POST, GET, OPTIONS" always;
add_header Access-Control-Allow-Headers "Content-Type" always;
if ($request_method = OPTIONS) {
return 204;
}
}
}
}

View File

@@ -6,6 +6,7 @@ COPY package.json package-lock.json* ./
RUN npm install --production
COPY contracts/ ./contracts/
COPY public/ ./public/
COPY src/ ./src/
EXPOSE 3001

View File

@@ -0,0 +1,158 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="512" height="512">
<defs>
<!-- Main gradient - dark teal to cyan -->
<linearGradient id="coinGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#0D1B2A"/>
<stop offset="50%" stop-color="#1B2D45"/>
<stop offset="100%" stop-color="#0D1B2A"/>
</linearGradient>
<!-- Rim gradient -->
<linearGradient id="rimGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#00E5CC"/>
<stop offset="25%" stop-color="#00BFA6"/>
<stop offset="50%" stop-color="#00E5CC"/>
<stop offset="75%" stop-color="#00BFA6"/>
<stop offset="100%" stop-color="#00E5CC"/>
</linearGradient>
<!-- Inner glow gradient -->
<radialGradient id="innerGlow" cx="50%" cy="45%" r="50%">
<stop offset="0%" stop-color="#00E5CC" stop-opacity="0.15"/>
<stop offset="100%" stop-color="#0D1B2A" stop-opacity="0"/>
</radialGradient>
<!-- Phoenix flame gradient -->
<linearGradient id="phoenixGrad" x1="50%" y1="100%" x2="50%" y2="0%">
<stop offset="0%" stop-color="#00BFA6"/>
<stop offset="40%" stop-color="#00E5CC"/>
<stop offset="70%" stop-color="#4DFFD2"/>
<stop offset="100%" stop-color="#AAFFF0"/>
</linearGradient>
<!-- Subtle glow filter -->
<filter id="glow" x="-20%" y="-20%" width="140%" height="140%">
<feGaussianBlur stdDeviation="4" result="blur"/>
<feComposite in="SourceGraphic" in2="blur" operator="over"/>
</filter>
<filter id="softGlow" x="-30%" y="-30%" width="160%" height="160%">
<feGaussianBlur stdDeviation="8" result="blur"/>
<feMerge>
<feMergeNode in="blur"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
<!-- Drop shadow for depth -->
<filter id="coinShadow" x="-10%" y="-10%" width="130%" height="130%">
<feDropShadow dx="0" dy="4" stdDeviation="8" flood-color="#000" flood-opacity="0.4"/>
</filter>
</defs>
<!-- Background circle shadow -->
<circle cx="256" cy="260" r="230" fill="#000" opacity="0.3" filter="url(#coinShadow)"/>
<!-- Outer coin body -->
<circle cx="256" cy="256" r="230" fill="url(#coinGrad)" stroke="url(#rimGrad)" stroke-width="6"/>
<!-- Inner rim -->
<circle cx="256" cy="256" r="210" fill="none" stroke="#00E5CC" stroke-width="1.5" opacity="0.5"/>
<circle cx="256" cy="256" r="205" fill="none" stroke="#00E5CC" stroke-width="0.5" opacity="0.25"/>
<!-- Inner glow -->
<circle cx="256" cy="256" r="210" fill="url(#innerGlow)"/>
<!-- Decorative ring with dashes -->
<circle cx="256" cy="256" r="195" fill="none" stroke="#00E5CC" stroke-width="1" stroke-dasharray="3 8" opacity="0.35"/>
<!-- Central phoenix/afterlife symbol - stylized rising flame/phoenix wing -->
<g transform="translate(256,256)" filter="url(#softGlow)">
<!-- Central rising flame / phoenix shape -->
<path d="
M 0,-95
C 15,-80 25,-55 20,-35
C 18,-20 8,-5 0,10
C -8,-5 -18,-20 -20,-35
C -25,-55 -15,-80 0,-95
Z
" fill="url(#phoenixGrad)" opacity="0.9"/>
<!-- Left wing -->
<path d="
M -5,0
C -20,-15 -50,-40 -65,-55
C -75,-65 -80,-60 -75,-48
C -68,-35 -45,-15 -25,5
C -15,15 -8,12 -5,0
Z
" fill="url(#phoenixGrad)" opacity="0.75"/>
<!-- Right wing -->
<path d="
M 5,0
C 20,-15 50,-40 65,-55
C 75,-65 80,-60 75,-48
C 68,-35 45,-15 25,5
C 15,15 8,12 5,0
Z
" fill="url(#phoenixGrad)" opacity="0.75"/>
<!-- Left outer wing -->
<path d="
M -25,5
C -40,-5 -70,-25 -88,-32
C -98,-36 -100,-30 -92,-22
C -82,-12 -55,5 -35,18
C -25,25 -18,18 -25,5
Z
" fill="#00E5CC" opacity="0.45"/>
<!-- Right outer wing -->
<path d="
M 25,5
C 40,-5 70,-25 88,-32
C 98,-36 100,-30 92,-22
C 82,-12 55,5 35,18
C 25,25 18,18 25,5
Z
" fill="#00E5CC" opacity="0.45"/>
<!-- Small flame tip accent -->
<path d="
M 0,-95
C 5,-105 3,-115 0,-120
C -3,-115 -5,-105 0,-95
Z
" fill="#AAFFF0" opacity="0.8"/>
</g>
<!-- "AFC" text -->
<text x="256" y="330"
font-family="'Orbitron', 'Rajdhani', 'Exo 2', 'Segoe UI', sans-serif"
font-size="52"
font-weight="700"
fill="#00E5CC"
text-anchor="middle"
letter-spacing="14"
filter="url(#glow)">AFC</text>
<!-- "AFTERCOIN" subtitle -->
<text x="256" y="360"
font-family="'Orbitron', 'Rajdhani', 'Exo 2', 'Segoe UI', sans-serif"
font-size="14"
font-weight="400"
fill="#00BFA6"
text-anchor="middle"
letter-spacing="8"
opacity="0.7">AFTERCOIN</text>
<!-- Top decorative dots -->
<circle cx="206" cy="385" r="2" fill="#00E5CC" opacity="0.4"/>
<circle cx="256" cy="390" r="2" fill="#00E5CC" opacity="0.4"/>
<circle cx="306" cy="385" r="2" fill="#00E5CC" opacity="0.4"/>
<!-- Subtle circuit-like lines at bottom -->
<line x1="220" y1="400" x2="292" y2="400" stroke="#00E5CC" stroke-width="0.5" opacity="0.2"/>
</svg>

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

@@ -1,3 +1,4 @@
const path = require("path");
const express = require("express");
const config = require("./config");
const db = require("./db");
@@ -11,6 +12,9 @@ const walletRouter = require("./routes/wallet");
const app = express();
// Serve static files (token icon, etc.)
app.use(express.static(path.join(__dirname, "..", "public")));
// Parse JSON request bodies
app.use(express.json());