use axum::{ http::header, response::{Html, IntoResponse}, routing::get, Router, }; use crate::state::AppState; pub fn routes() -> Router { Router::new() .route("/", get(web_ui)) .route("/wasm/warzone_wasm.js", get(wasm_js)) .route("/wasm/warzone_wasm_bg.wasm", get(wasm_binary)) .route("/manifest.json", get(pwa_manifest)) .route("/sw.js", get(service_worker)) .route("/icon.svg", get(pwa_icon)) } async fn web_ui() -> Html<&'static str> { Html(WEB_HTML) } async fn wasm_js() -> impl IntoResponse { ( [(header::CONTENT_TYPE, "application/javascript")], include_str!("../../../../wasm-pkg/warzone_wasm.js"), ) } async fn wasm_binary() -> impl IntoResponse { ( [(header::CONTENT_TYPE, "application/wasm")], include_bytes!("../../../../wasm-pkg/warzone_wasm_bg.wasm").as_slice(), ) } async fn pwa_manifest() -> impl IntoResponse { ([(header::CONTENT_TYPE, "application/manifest+json")], r##"{ "name": "Warzone Messenger", "short_name": "Warzone", "description": "End-to-end encrypted messenger", "start_url": "/", "display": "standalone", "background_color": "#0a0a1a", "theme_color": "#0a0a1a", "icons": [{"src": "/icon.svg", "sizes": "any", "type": "image/svg+xml", "purpose": "any maskable"}] }"##) } async fn service_worker() -> impl IntoResponse { ([(header::CONTENT_TYPE, "application/javascript")], r##" const CACHE = 'wz-v14'; const SHELL = ['/', '/wasm/warzone_wasm.js', '/wasm/warzone_wasm_bg.wasm', '/icon.svg', '/manifest.json']; self.addEventListener('install', e => { e.waitUntil(caches.open(CACHE).then(c => c.addAll(SHELL))); self.skipWaiting(); }); self.addEventListener('activate', e => { e.waitUntil(caches.keys().then(keys => Promise.all(keys.filter(k => k !== CACHE).map(k => caches.delete(k))) )); e.waitUntil(clients.claim()); }); self.addEventListener('fetch', e => { const url = new URL(e.request.url); // API calls: network only if (url.pathname.startsWith('/v1/')) return; // WS: skip if (url.protocol === 'ws:' || url.protocol === 'wss:') return; // Network first, cache fallback (ensures updates are picked up immediately) e.respondWith( fetch(e.request).then(resp => { if (resp.ok && SHELL.includes(url.pathname)) { const clone = resp.clone(); caches.open(CACHE).then(c => c.put(e.request, clone)); } return resp; }).catch(() => caches.match(e.request).then(cached => { if (cached) return cached; if (e.request.mode === 'navigate') return caches.match('/'); })) ); }); "##) } async fn pwa_icon() -> impl IntoResponse { ([(header::CONTENT_TYPE, "image/svg+xml")], r##" "##) } const WEB_HTML: &str = r##" Warzone WARZONE end-to-end encrypted messenger Generate Identity Recover Recover Identity Your fingerprint: SAVE YOUR SEED — only way to recover your identity Enter Chat → 📎 ▶ "##;