262 lines
15 KiB
Plaintext
262 lines
15 KiB
Plaintext
server {
|
|
listen 8080;
|
|
server_name localhost;
|
|
|
|
absolute_redirect off;
|
|
port_in_redirect off;
|
|
|
|
include /etc/nginx/mime.types;
|
|
default_type application/octet-stream;
|
|
|
|
root /usr/share/nginx/html;
|
|
index index.html;
|
|
|
|
# Support for SPA routing
|
|
location / {
|
|
try_files $uri $uri/ /index.html;
|
|
add_header Cache-Control "no-cache";
|
|
}
|
|
|
|
# Don't serve index.html for missing assets
|
|
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot|wasm|json|proto)$ {
|
|
add_header Cache-Control "public, max-age=31536000, immutable";
|
|
access_log off;
|
|
try_files $uri =404;
|
|
}
|
|
|
|
# Mocking missing critical files for demo stability
|
|
location = /manifest.json {
|
|
add_header Cache-Control "no-cache";
|
|
add_header Content-Type application/json;
|
|
try_files $uri @mock_manifest;
|
|
}
|
|
location @mock_manifest {
|
|
return 200 '{"name": "MeshChat Demo", "short_name": "MeshChat", "start_url": "/", "display": "standalone", "icons": [{"src": "/favicons/favicon-512x512.png", "sizes": "512x512", "type": "image/png"}]}';
|
|
}
|
|
|
|
location = /service-worker.js {
|
|
add_header Cache-Control "no-cache";
|
|
add_header Content-Type application/javascript;
|
|
try_files $uri @mock_sw;
|
|
}
|
|
location @mock_sw {
|
|
return 200 '// Mock Service Worker\nself.addEventListener("fetch", (event) => {});';
|
|
}
|
|
|
|
# Mock Codec2 scripts to prevent load failures in demo mode
|
|
location ^~ /assets/js/codec2-emscripten/ {
|
|
add_header Content-Type application/javascript;
|
|
try_files $uri =200;
|
|
}
|
|
|
|
# Gzip compression
|
|
gzip on;
|
|
gzip_vary on;
|
|
gzip_proxied any;
|
|
gzip_comp_level 6;
|
|
gzip_types text/plain text/css text/xml application/json application/javascript application/rss+xml application/atom+xml image/svg+xml;
|
|
|
|
# Mock responses for demo mode with the power of NGINX!
|
|
location /api/v1/auth/status {
|
|
return 200 '{"auth_enabled": false, "authenticated": true}';
|
|
add_header Content-Type application/json;
|
|
}
|
|
|
|
location /api/v1/config {
|
|
return 200 '{"config": {"theme": "dark", "language": "en", "display_name": "Demo User", "identity_hash": "effe95b0482d7b6c04844977d17e0c36", "lxmf_address_hash": "c0ffee00000000000000000000000000", "auto_announce_interval_seconds": 0, "lxmf_user_icon_name": "account", "lxmf_user_icon_foreground_colour": "#ffffff", "lxmf_user_icon_background_colour": "#000000"}}';
|
|
add_header Content-Type application/json;
|
|
}
|
|
|
|
location /api/v1/app/info {
|
|
return 200 '{"app_info": {"version": "demo", "rns_version": "0.0.0", "lxmf_version": "0.0.0", "python_version": "3.11", "is_connected_to_shared_instance": false, "is_transport_enabled": false, "reticulum_config_path": "/demo/rns.conf", "database_path": "/demo/meshchat.db", "database_file_size": 1048576, "is_demo": true, "memory_usage": {"rss": 52428800, "vms": 104857600}, "network_stats": {"bytes_sent": 102400, "bytes_recv": 204800, "packets_sent": 150, "packets_recv": 300}, "reticulum_stats": {"total_paths": 42, "announces_per_second": 0.1, "announces_per_minute": 6, "announces_per_hour": 360}}}';
|
|
add_header Content-Type application/json;
|
|
}
|
|
|
|
location /api/v1/database/health {
|
|
return 200 '{"database": {"quick_check": "ok", "journal_mode": "wal", "wal_autocheckpoint": 1000, "page_size": 4096, "page_count": 256, "freelist_pages": 10, "estimated_free_bytes": 40960}}';
|
|
add_header Content-Type application/json;
|
|
}
|
|
|
|
location /api/v1/reticulum/interfaces {
|
|
return 200 '{"interfaces": {
|
|
"RNode-LoRa-V3": {"type": "RNodeInterface", "enabled": "true", "port": "/dev/ttyUSB0", "frequency": 868000000, "bandwidth": 125000, "spreadingfactor": 7, "codingrate": 5, "txpower": 7},
|
|
"Internet": {"type": "TCPClientInterface", "enabled": "true", "target_host": "hub.reticulum.network", "target_port": 4242},
|
|
"I2C-Bus-0": {"type": "I2CInterface", "enabled": "false"},
|
|
"LAN Mesh": {"type": "UDPInterface", "enabled": "true", "listen_ip": "0.0.0.0", "listen_port": 4242, "forward_ip": "255.255.255.255", "forward_port": 4242}
|
|
}}';
|
|
add_header Content-Type application/json;
|
|
}
|
|
|
|
location /api/v1/interface-stats {
|
|
return 200 '{"interface_stats": {"interfaces": [
|
|
{"short_name": "RNode-LoRa-V3", "name": "AutoInterface[RNode]", "status": true, "bitrate": 1200, "txb": 1024, "rxb": 5120},
|
|
{"short_name": "Internet", "name": "AutoInterface[TCP]", "status": true, "bitrate": 10000000, "txb": 45678, "rxb": 987654},
|
|
{"short_name": "I2C-Bus-0", "name": "AutoInterface[I2C]", "status": false, "bitrate": 400000, "txb": 0, "rxb": 0},
|
|
{"short_name": "LAN Mesh", "name": "AutoInterface[UDP]", "status": true, "bitrate": 100000000, "txb": 12345, "rxb": 67890}
|
|
]}}';
|
|
add_header Content-Type application/json;
|
|
}
|
|
|
|
location /api/v1/lxmf/conversations {
|
|
return 200 '{"conversations": [
|
|
{"destination_hash": "8b5cf6", "display_name": "Alice", "updated_at": "2026-01-01T12:00:00Z", "latest_message_preview": "Hello from the mesh!", "unread_count": 1, "lxmf_user_icon": {"icon_name": "account", "foreground_colour": "#ffffff", "background_colour": "#3b82f6"}},
|
|
{"destination_hash": "3b82f6", "display_name": "Bob", "updated_at": "2026-01-01T11:30:00Z", "latest_message_preview": "Did you see the new RNode update?", "unread_count": 0, "lxmf_user_icon": {"icon_name": "robot", "foreground_colour": "#ffffff", "background_colour": "#ef4444"}},
|
|
{"destination_hash": "10b981", "display_name": "Regional Hub", "updated_at": "2026-01-01T10:00:00Z", "latest_message_preview": "Status: All interfaces operational", "unread_count": 0, "lxmf_user_icon": {"icon_name": "server", "foreground_colour": "#ffffff", "background_colour": "#10b981"}}
|
|
]}';
|
|
add_header Content-Type application/json;
|
|
}
|
|
|
|
location ~ ^/api/v1/lxmf-messages/conversation/(.*)$ {
|
|
return 200 '{"lxmf_messages": [
|
|
{"hash": "msg1", "source_hash": "8b5cf6", "destination_hash": "c0ffee00000000000000000000000000", "content": "Hey there!", "timestamp": 1735732500, "created_at": "2026-01-01T11:55:00Z", "state": "delivered", "method": "propagated"},
|
|
{"hash": "msg2", "source_hash": "8b5cf6", "destination_hash": "c0ffee00000000000000000000000000", "content": "Hello from the mesh!", "timestamp": 1735732800, "created_at": "2026-01-01T12:00:00Z", "state": "delivered", "method": "propagated"}
|
|
]}';
|
|
add_header Content-Type application/json;
|
|
}
|
|
|
|
location ~ ^/api/v1/lxmf/conversations/(.*)/mark-as-read$ {
|
|
return 200 '{"success": true}';
|
|
add_header Content-Type application/json;
|
|
}
|
|
|
|
location /api/v1/blocked-destinations {
|
|
return 200 '{"blocked_destinations": []}';
|
|
add_header Content-Type application/json;
|
|
}
|
|
|
|
location /api/v1/telephone/status {
|
|
set $mock_call $arg_mock_call;
|
|
if ($mock_call = "true") {
|
|
return 200 '{"enabled": true, "is_busy": false, "call_status": "ringing", "active_call": {"hash": "call123", "status": "ringing", "is_incoming": true, "is_outgoing": false, "remote_identity_hash": "8b5cf6", "remote_identity_name": "Alice", "audio_profile_id": 0, "tx_packets": 0, "rx_packets": 0, "tx_bytes": 0, "rx_bytes": 0, "is_mic_muted": false, "is_speaker_muted": false, "is_voicemail": false}, "is_mic_muted": false, "is_speaker_muted": false, "voicemail": {"is_recording": false, "unread_count": 0}}';
|
|
add_header Content-Type application/json;
|
|
}
|
|
return 200 '{"enabled": true, "is_busy": false, "call_status": "idle", "active_call": null, "is_mic_muted": false, "is_speaker_muted": false, "voicemail": {"is_recording": false, "unread_count": 0}}';
|
|
add_header Content-Type application/json;
|
|
}
|
|
|
|
location /api/v1/telephone/mock-incoming-call {
|
|
return 200 '{"message": "Mock incoming call triggered. Poll /api/v1/telephone/status?mock_call=true to see the call"}';
|
|
add_header Content-Type application/json;
|
|
}
|
|
|
|
location /api/v1/telephone/history {
|
|
return 200 '{"call_history": [
|
|
{"id": 1, "remote_identity_hash": "8b5cf6", "remote_identity_name": "Alice", "is_incoming": 1, "status": "completed", "duration_seconds": 125, "timestamp": 1735732800.0, "created_at": "2026-01-01T12:00:00Z"},
|
|
{"id": 2, "remote_identity_hash": "3b82f6", "remote_identity_name": "Bob", "is_incoming": 0, "status": "completed", "duration_seconds": 45, "timestamp": 1735731000.0, "created_at": "2026-01-01T11:30:00Z"},
|
|
{"id": 3, "remote_identity_hash": "10b981", "remote_identity_name": "Regional Hub", "is_incoming": 1, "status": "missed", "duration_seconds": 0, "timestamp": 1735729200.0, "created_at": "2026-01-01T11:00:00Z"},
|
|
{"id": 4, "remote_identity_hash": "ef4444", "remote_identity_name": "Echo Bot", "is_incoming": 0, "status": "failed", "duration_seconds": 0, "timestamp": 1735727400.0, "created_at": "2026-01-01T10:30:00Z"},
|
|
{"id": 5, "remote_identity_hash": "a1b2c3", "remote_identity_name": "Charlie", "is_incoming": 1, "status": "completed", "duration_seconds": 320, "timestamp": 1735725600.0, "created_at": "2026-01-01T10:00:00Z"}
|
|
]}';
|
|
add_header Content-Type application/json;
|
|
}
|
|
|
|
location /api/v1/favourites {
|
|
set $aspect_filter $arg_aspect;
|
|
if ($aspect_filter = "lxmf.delivery") {
|
|
return 200 '{"favourites": [
|
|
{"id": 1, "destination_hash": "8b5cf6", "display_name": "Alice", "aspect": "lxmf.delivery", "created_at": "2026-01-01T08:00:00Z", "updated_at": "2026-01-01T08:00:00Z"},
|
|
{"id": 2, "destination_hash": "3b82f6", "display_name": "Bob", "aspect": "lxmf.delivery", "created_at": "2026-01-01T08:00:00Z", "updated_at": "2026-01-01T08:00:00Z"}
|
|
]}';
|
|
add_header Content-Type application/json;
|
|
}
|
|
if ($aspect_filter = "nomadnetwork.node") {
|
|
return 200 '{"favourites": [
|
|
{"id": 3, "destination_hash": "10b981", "display_name": "Regional Hub", "aspect": "nomadnetwork.node", "created_at": "2026-01-01T08:00:00Z", "updated_at": "2026-01-01T08:00:00Z"},
|
|
{"id": 4, "destination_hash": "f68b5c", "display_name": "Nomad Portal", "aspect": "nomadnetwork.node", "created_at": "2026-01-01T08:00:00Z", "updated_at": "2026-01-01T08:00:00Z"}
|
|
]}';
|
|
add_header Content-Type application/json;
|
|
}
|
|
return 200 '{"favourites": [
|
|
{"id": 1, "destination_hash": "8b5cf6", "display_name": "Alice", "aspect": "lxmf.delivery", "created_at": "2026-01-01T08:00:00Z", "updated_at": "2026-01-01T08:00:00Z"},
|
|
{"id": 2, "destination_hash": "3b82f6", "display_name": "Bob", "aspect": "lxmf.delivery", "created_at": "2026-01-01T08:00:00Z", "updated_at": "2026-01-01T08:00:00Z"},
|
|
{"id": 3, "destination_hash": "10b981", "display_name": "Regional Hub", "aspect": "nomadnetwork.node", "created_at": "2026-01-01T08:00:00Z", "updated_at": "2026-01-01T08:00:00Z"},
|
|
{"id": 4, "destination_hash": "f68b5c", "display_name": "Nomad Portal", "aspect": "nomadnetwork.node", "created_at": "2026-01-01T08:00:00Z", "updated_at": "2026-01-01T08:00:00Z"}
|
|
]}';
|
|
add_header Content-Type application/json;
|
|
}
|
|
|
|
location /api/v1/lxmf/propagation-node/status {
|
|
return 200 '{"propagation_node_status": {"state": "idle", "connected": true, "address": "8b5cf6", "messages_received": 123, "messages_sent": 45}}';
|
|
add_header Content-Type application/json;
|
|
}
|
|
|
|
location /api/v1/announces {
|
|
return 200 '{"announces": [
|
|
{"destination_hash": "8b5cf6", "display_name": "Alice", "aspect": "lxmf.delivery", "updated_at": "2026-01-01T12:00:00Z", "hops": 1, "snr": 12.5},
|
|
{"destination_hash": "3b82f6", "display_name": "Bob", "aspect": "lxmf.delivery", "updated_at": "2026-01-01T11:30:00Z", "hops": 2, "snr": 8.2},
|
|
{"destination_hash": "10b981", "display_name": "Regional Hub", "aspect": "nomadnetwork.node", "updated_at": "2026-01-01T10:00:00Z", "hops": 1, "snr": 15.0},
|
|
{"destination_hash": "ef4444", "display_name": "Echo Bot", "aspect": "lxmf.delivery", "updated_at": "2026-01-01T09:00:00Z", "hops": 3},
|
|
{"destination_hash": "f68b5c", "display_name": "Nomad Portal", "aspect": "nomadnetwork.node", "updated_at": "2026-01-01T12:05:00Z", "hops": 1},
|
|
{"destination_hash": "a1b2c3", "display_name": "Charlie", "aspect": "lxmf.delivery", "updated_at": "2026-01-01T08:00:00Z", "hops": 4},
|
|
{"destination_hash": "d4e5f6", "display_name": "Mesh Gateway", "aspect": "nomadnetwork.node", "updated_at": "2026-01-01T12:10:00Z", "hops": 1}
|
|
], "total_count": 7}';
|
|
add_header Content-Type application/json;
|
|
}
|
|
|
|
location /api/v1/path-table {
|
|
return 200 '{"path_table": [
|
|
{"hash": "8b5cf6", "hops": 1, "interface": "AutoInterface[RNode]"},
|
|
{"hash": "3b82f6", "hops": 2, "interface": "AutoInterface[TCP]"},
|
|
{"hash": "10b981", "hops": 1, "interface": "AutoInterface[RNode]"},
|
|
{"hash": "ef4444", "hops": 3, "interface": "AutoInterface[TCP]"},
|
|
{"hash": "f68b5c", "hops": 1, "interface": "AutoInterface[TCP]"},
|
|
{"hash": "a1b2c3", "hops": 4, "interface": "AutoInterface[UDP]"},
|
|
{"hash": "d4e5f6", "hops": 1, "interface": "AutoInterface[UDP]"}
|
|
], "total_count": 7}';
|
|
add_header Content-Type application/json;
|
|
}
|
|
|
|
location /api/v1/notifications {
|
|
return 200 '{"notifications": [
|
|
{"id": 1, "type": "new_message", "title": "New Message", "content": "Alice: Hello from the mesh!", "created_at": "2026-01-01T12:00:00Z"}
|
|
]}';
|
|
add_header Content-Type application/json;
|
|
}
|
|
|
|
location /api/v1/announce {
|
|
return 200 '{"message": "Announce sent (Demo Mode)"}';
|
|
add_header Content-Type application/json;
|
|
}
|
|
|
|
location /api/v1/lxmf/propagation-node/sync {
|
|
return 200 '{"message": "Sync started (Demo Mode)"}';
|
|
add_header Content-Type application/json;
|
|
}
|
|
|
|
location /api/v1/identity/backup/base32 {
|
|
return 200 '{"identity_base32": "demo_base32_identity_key_do_not_use"}';
|
|
add_header Content-Type application/json;
|
|
}
|
|
|
|
# Catch-all for other API calls
|
|
location /api {
|
|
return 404 '{"error": "Endpoint not available in demo mode"}';
|
|
add_header Content-Type application/json;
|
|
}
|
|
|
|
# Improved WebSocket handler
|
|
location /ws {
|
|
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;
|
|
|
|
# In demo mode, returning 200 for non-WS requests provides better feedback
|
|
if ($http_upgrade != "websocket") {
|
|
return 200 "WebSocket Mock Endpoint (Active)";
|
|
}
|
|
|
|
# For actual WS connection attempts, we still return 503 as there is no backend server.
|
|
return 503;
|
|
}
|
|
|
|
# Error pages
|
|
error_page 500 502 503 504 /50x.html;
|
|
location = /50x.html {
|
|
root /usr/share/nginx/html;
|
|
}
|
|
}
|