Compare commits
3 Commits
25058e4bac
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 6710c008c1 | |||
| 0454a06cfc | |||
| 558e0debac |
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 20 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 100 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 62 KiB |
+27
-3
@@ -17,7 +17,16 @@
|
||||
<meta name="twitter:image" content="https://auraajenticai.cloud/logo.png" />
|
||||
<link rel="canonical" href="https://auraajenticai.cloud" />
|
||||
<link rel="icon" type="image/png" href="logo.png" />
|
||||
<link rel="apple-touch-icon" href="logo.png" />
|
||||
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
|
||||
<link rel="icon" type="image/png" sizes="192x192" href="/icon-192.png" />
|
||||
<link rel="icon" type="image/png" sizes="512x512" href="/icon-512.png" />
|
||||
<link rel="manifest" href="/manifest.json" />
|
||||
<meta name="theme-color" content="#080d1a" />
|
||||
<meta name="mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||||
<meta name="apple-mobile-web-app-title" content="Aura Cloud" />
|
||||
|
||||
<meta name="google-site-verification" content="vEK4vy89hAbckU9sUlqn7mZlMxLjNT--CvuLruocMfQ" />
|
||||
|
||||
@@ -340,7 +349,7 @@
|
||||
<script type="text/babel" src="tweaks-panel.jsx?v=9"></script>
|
||||
<script type="text/babel" src="src/icons.jsx?v=9"></script>
|
||||
<script type="text/babel" src="src/data.jsx?v=9"></script>
|
||||
<script type="text/babel" src="src/nav.jsx?v=9"></script>
|
||||
<script type="text/babel" src="src/nav.jsx?v=11"></script>
|
||||
<script type="text/babel" src="src/hero.jsx?v=9"></script>
|
||||
<script type="text/babel" src="src/sections.jsx?v=9"></script>
|
||||
<script type="text/babel" src="src/projects.jsx?v=9"></script>
|
||||
@@ -349,6 +358,21 @@
|
||||
<script type="text/babel" src="src/command-palette.jsx?v=9"></script>
|
||||
<script type="text/babel" src="src/chatbox.jsx?v=9"></script>
|
||||
<script type="text/babel" src="src/pages.jsx?v=9"></script>
|
||||
<script type="text/babel" src="src/app.jsx?v=9"></script>
|
||||
<script type="text/babel" src="src/app.jsx?v=11"></script>
|
||||
|
||||
<!-- PWA Service Worker Registration -->
|
||||
<script>
|
||||
if ('serviceWorker' in navigator) {
|
||||
window.addEventListener('load', function() {
|
||||
navigator.serviceWorker.register('/sw.js', { scope: '/' })
|
||||
.then(function(reg) {
|
||||
console.log('[Aura PWA] Service Worker registered with scope:', reg.scope);
|
||||
})
|
||||
.catch(function(err) {
|
||||
console.warn('[Aura PWA] Service Worker registration failed:', err);
|
||||
});
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
{
|
||||
"name": "Aura Ajentic AI — Enterprise AI & Logistics Platform",
|
||||
"short_name": "Aura Cloud",
|
||||
"description": "Autonomous AI Sales Agents, Courier MCP logistics engine, and real-time COD fraud defense.",
|
||||
"start_url": "/?source=pwa",
|
||||
"scope": "/",
|
||||
"display": "standalone",
|
||||
"orientation": "any",
|
||||
"background_color": "#080d1a",
|
||||
"theme_color": "#080d1a",
|
||||
"lang": "en",
|
||||
"dir": "ltr",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/icon-192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png",
|
||||
"purpose": "any"
|
||||
},
|
||||
{
|
||||
"src": "/icon-maskable-192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable"
|
||||
},
|
||||
{
|
||||
"src": "/icon-512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png",
|
||||
"purpose": "any"
|
||||
},
|
||||
{
|
||||
"src": "/icon-maskable-512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable"
|
||||
}
|
||||
],
|
||||
"shortcuts": [
|
||||
{
|
||||
"name": "Client Portal Login",
|
||||
"short_name": "Client Login",
|
||||
"description": "Sign in to Aura Client Dashboard",
|
||||
"url": "/?action=client-login",
|
||||
"icons": [{ "src": "/icon-192.png", "sizes": "192x192" }]
|
||||
},
|
||||
{
|
||||
"name": "Courier Logistics Hub",
|
||||
"short_name": "Courier Hub",
|
||||
"description": "Steadfast, Pathao & RedX Logistics Automation",
|
||||
"url": "/?action=courier-hub",
|
||||
"icons": [{ "src": "/icon-192.png", "sizes": "192x192" }]
|
||||
},
|
||||
{
|
||||
"name": "AI Agent Studio",
|
||||
"short_name": "AI Studio",
|
||||
"description": "Omnichannel AI Agent Copilot & Inbox",
|
||||
"url": "/?action=ai-studio",
|
||||
"icons": [{ "src": "/icon-192.png", "sizes": "192x192" }]
|
||||
}
|
||||
],
|
||||
"categories": ["business", "productivity", "utilities"],
|
||||
"prefer_related_applications": false
|
||||
}
|
||||
+255
-19
@@ -1,24 +1,42 @@
|
||||
// Top nav — minimal, sticky, blurred — mobile-responsive
|
||||
// Top nav — minimal, sticky, blurred — mobile-responsive + PWA Install & Client Gateway
|
||||
const NAV_LINKS = [
|
||||
{ id: "services", label: "Services", labelBn: "সার্ভিস", url: "#/services" },
|
||||
{ id: "case-studies", label: "Case Studies", labelBn: "কেস স্টাডি", url: "#/case-studies" },
|
||||
{ id: "pricing", label: "Pricing", labelBn: "মূল্য", url: "#/pricing" },
|
||||
{ id: "blog", label: "Blog", labelBn: "ব্লগ", url: "#/blog" },
|
||||
{ id: "contact", label: "Contact", labelBn: "যোগাযোগ", url: "#/contact" },
|
||||
]
|
||||
];
|
||||
|
||||
const NAV_TXT = {
|
||||
en: { search: "Search", getQuote: "Get a Quote", clientLogin: "Client Login", getStarted: "Get Started" },
|
||||
bn: { search: "খুঁজুন", getQuote: "কোট নিন", clientLogin: "ক্লায়েন্ট লগইন", getStarted: "শুরু করুন" },
|
||||
en: {
|
||||
search: "Search",
|
||||
getQuote: "Get a Quote",
|
||||
clientLogin: "Client Login",
|
||||
getStarted: "Get Started",
|
||||
installApp: "Install App",
|
||||
appInstalled: "App Installed",
|
||||
},
|
||||
bn: {
|
||||
search: "খুঁজুন",
|
||||
getQuote: "কোট নিন",
|
||||
clientLogin: "ক্লায়েন্ট লগইন",
|
||||
getStarted: "শুরু করুন",
|
||||
installApp: "অ্যাপ ইনস্টল",
|
||||
appInstalled: "অ্যাপ ইনস্টলড",
|
||||
},
|
||||
};
|
||||
|
||||
// Where a self-serve account should land right after signing up -- the client portal's own
|
||||
// service catalog, not a blank dashboard. aura-auth's /register already reads and honors `next`.
|
||||
// Next redirects ensure clients land seamlessly in the right SaaS portal
|
||||
const REGISTER_URL = "https://auth.auraajenticai.cloud/register?next=" + encodeURIComponent("https://client.auraajenticai.cloud/services");
|
||||
const CLIENT_LOGIN_URL = "https://auth.auraajenticai.cloud/login?next=" + encodeURIComponent("https://client.auraajenticai.cloud");
|
||||
|
||||
const Nav = ({ onCmdK, theme, onToggleTheme, accent, lang, onToggleLang, route }) => {
|
||||
const [scrolled, setScrolled] = React.useState(false);
|
||||
const [menuOpen, setMenuOpen] = React.useState(false);
|
||||
const [installModalOpen, setInstallModalOpen] = React.useState(false);
|
||||
const [deferredPrompt, setDeferredPrompt] = React.useState(null);
|
||||
const [isInstalled, setIsInstalled] = React.useState(false);
|
||||
|
||||
const links = NAV_LINKS;
|
||||
const t = NAV_TXT[lang] || NAV_TXT.en;
|
||||
const bn = lang === "bn";
|
||||
@@ -29,6 +47,49 @@ const Nav = ({ onCmdK, theme, onToggleTheme, accent, lang, onToggleLang, route }
|
||||
return () => window.removeEventListener("scroll", onScroll);
|
||||
}, []);
|
||||
|
||||
// PWA standalone detection & prompt listener
|
||||
React.useEffect(() => {
|
||||
const isStandalone = window.matchMedia("(display-mode: standalone)").matches || window.navigator.standalone === true;
|
||||
if (isStandalone) {
|
||||
setIsInstalled(true);
|
||||
}
|
||||
|
||||
const handleBeforeInstall = (e) => {
|
||||
e.preventDefault();
|
||||
setDeferredPrompt(e);
|
||||
};
|
||||
|
||||
const handleAppInstalled = () => {
|
||||
setIsInstalled(true);
|
||||
setDeferredPrompt(null);
|
||||
};
|
||||
|
||||
window.addEventListener("beforeinstallprompt", handleBeforeInstall);
|
||||
window.addEventListener("appinstalled", handleAppInstalled);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("beforeinstallprompt", handleBeforeInstall);
|
||||
window.removeEventListener("appinstalled", handleAppInstalled);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const handleInstallClick = async () => {
|
||||
if (deferredPrompt) {
|
||||
try {
|
||||
deferredPrompt.prompt();
|
||||
const choice = await deferredPrompt.userChoice;
|
||||
if (choice && choice.outcome === "accepted") {
|
||||
setIsInstalled(true);
|
||||
setDeferredPrompt(null);
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn("PWA install prompt error:", err);
|
||||
}
|
||||
} else {
|
||||
setInstallModalOpen(true);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<nav style={{
|
||||
@@ -138,6 +199,32 @@ const Nav = ({ onCmdK, theme, onToggleTheme, accent, lang, onToggleLang, route }
|
||||
{lang === "en" ? "বাং" : "EN"}
|
||||
</button>
|
||||
|
||||
{/* PWA Install Button (Desktop) */}
|
||||
{!isInstalled && (
|
||||
<button
|
||||
onClick={handleInstallClick}
|
||||
className="nav-install-btn"
|
||||
title={bn ? "Aura Cloud অ্যাপ আপনার ডিভাইসে ইনস্টল করুন" : "Install Aura Cloud App on your device"}
|
||||
style={{
|
||||
display: "inline-flex",
|
||||
alignItems: "center",
|
||||
gap: 6,
|
||||
padding: "6px 11px",
|
||||
background: "linear-gradient(135deg, rgba(124,92,255,0.16), rgba(0,212,255,0.16))",
|
||||
border: "1px solid rgba(124,92,255,0.45)",
|
||||
borderRadius: 8,
|
||||
color: "var(--text)",
|
||||
fontSize: 12.5,
|
||||
fontWeight: 600,
|
||||
cursor: "pointer",
|
||||
whiteSpace: "nowrap",
|
||||
}}
|
||||
>
|
||||
<span>📲</span>
|
||||
<span>{t.installApp}</span>
|
||||
</button>
|
||||
)}
|
||||
|
||||
<a href="mailto:ceo@auraajenticai.cloud?subject=Project Quote" className="nav-quote-btn" style={{
|
||||
padding: "7px 13px",
|
||||
background: "transparent",
|
||||
@@ -152,17 +239,21 @@ const Nav = ({ onCmdK, theme, onToggleTheme, accent, lang, onToggleLang, route }
|
||||
{t.getQuote}
|
||||
</a>
|
||||
|
||||
<a href="https://auth.auraajenticai.cloud/login" className="nav-login-btn" style={{
|
||||
padding: "8px 12px",
|
||||
background: "transparent",
|
||||
border: "1px solid var(--line)",
|
||||
borderRadius: 8,
|
||||
color: "var(--text-dim)",
|
||||
fontSize: 13,
|
||||
fontWeight: 500,
|
||||
textDecoration: "none",
|
||||
whiteSpace: "nowrap",
|
||||
}}>
|
||||
<a
|
||||
href={CLIENT_LOGIN_URL}
|
||||
className="nav-login-btn"
|
||||
style={{
|
||||
padding: "8px 12px",
|
||||
background: "transparent",
|
||||
border: "1px solid var(--line)",
|
||||
borderRadius: 8,
|
||||
color: "var(--text-dim)",
|
||||
fontSize: 13,
|
||||
fontWeight: 500,
|
||||
textDecoration: "none",
|
||||
whiteSpace: "nowrap",
|
||||
}}
|
||||
>
|
||||
{t.clientLogin}
|
||||
</a>
|
||||
|
||||
@@ -220,6 +311,7 @@ const Nav = ({ onCmdK, theme, onToggleTheme, accent, lang, onToggleLang, route }
|
||||
@media (max-width: 640px) {
|
||||
.nav-search-btn { display: none !important; }
|
||||
.nav-quote-btn { display: none !important; }
|
||||
.nav-install-btn { display: none !important; }
|
||||
.nav-badge { display: none !important; }
|
||||
}
|
||||
@media (max-width: 480px) {
|
||||
@@ -246,7 +338,51 @@ const Nav = ({ onCmdK, theme, onToggleTheme, accent, lang, onToggleLang, route }
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: 2,
|
||||
maxHeight: "calc(100vh - 64px)",
|
||||
overflowY: "auto",
|
||||
}}>
|
||||
{/* Mobile PWA Install Banner */}
|
||||
{!isInstalled && (
|
||||
<div style={{
|
||||
padding: "12px 14px",
|
||||
borderRadius: 12,
|
||||
background: "linear-gradient(135deg, rgba(124,92,255,0.18), rgba(0,212,255,0.18))",
|
||||
border: "1px solid rgba(124,92,255,0.35)",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
marginBottom: 10,
|
||||
}}>
|
||||
<div>
|
||||
<div style={{ fontWeight: 600, fontSize: 13, color: "var(--text)" }}>
|
||||
{bn ? "📲 Aura Cloud অ্যাপ" : "📲 Aura Cloud App"}
|
||||
</div>
|
||||
<div style={{ fontSize: 11, color: "var(--text-dim)", marginTop: 2 }}>
|
||||
{bn ? "হোমস্ক্রিনে ১-ট্যাপে ইনস্টল করুন" : "Install to home screen for 1-tap launch"}
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => {
|
||||
setMenuOpen(false);
|
||||
handleInstallClick();
|
||||
}}
|
||||
style={{
|
||||
padding: "6px 12px",
|
||||
background: "var(--text)",
|
||||
color: "var(--bg)",
|
||||
border: "none",
|
||||
borderRadius: 8,
|
||||
fontSize: 12,
|
||||
fontWeight: 600,
|
||||
cursor: "pointer",
|
||||
whiteSpace: "nowrap",
|
||||
}}
|
||||
>
|
||||
{bn ? "ইনস্টল" : "Install"}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{links.map(l => (
|
||||
<a
|
||||
key={l.id}
|
||||
@@ -298,9 +434,9 @@ const Nav = ({ onCmdK, theme, onToggleTheme, accent, lang, onToggleLang, route }
|
||||
fontSize: 13,
|
||||
textDecoration: "none",
|
||||
}}
|
||||
>{t.getQuote}</a>
|
||||
>{t.quote}</a>
|
||||
<a
|
||||
href="https://auth.auraajenticai.cloud/login"
|
||||
href={CLIENT_LOGIN_URL}
|
||||
onClick={() => setMenuOpen(false)}
|
||||
style={{
|
||||
padding: "8px 14px",
|
||||
@@ -329,6 +465,106 @@ const Nav = ({ onCmdK, theme, onToggleTheme, accent, lang, onToggleLang, route }
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* PWA Install Help Modal (iOS & Fallback) */}
|
||||
{installModalOpen && (
|
||||
<div
|
||||
onClick={() => setInstallModalOpen(false)}
|
||||
style={{
|
||||
position: "fixed",
|
||||
inset: 0,
|
||||
zIndex: 100,
|
||||
background: "rgba(0, 0, 0, 0.72)",
|
||||
backdropFilter: "blur(10px)",
|
||||
WebkitBackdropFilter: "blur(10px)",
|
||||
display: "grid",
|
||||
placeItems: "center",
|
||||
padding: 16,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
style={{
|
||||
position: "relative",
|
||||
width: "100%",
|
||||
maxWidth: 440,
|
||||
background: "var(--bg-card, #0c1222)",
|
||||
border: "1px solid rgba(0, 212, 255, 0.35)",
|
||||
borderRadius: 16,
|
||||
padding: "24px 22px",
|
||||
boxShadow: "0 24px 60px rgba(0, 0, 0, 0.6)",
|
||||
}}
|
||||
>
|
||||
<div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 14 }}>
|
||||
<div style={{ display: "flex", alignItems: "center", gap: 8 }}>
|
||||
<span style={{ fontSize: 22 }}>📲</span>
|
||||
<h3 style={{ margin: 0, fontSize: 16, fontWeight: 600, color: "var(--text)" }}>
|
||||
{bn ? "Aura Cloud অ্যাপ ইনস্টল গাইড" : "Install Aura Cloud App"}
|
||||
</h3>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => setInstallModalOpen(false)}
|
||||
style={{ background: "transparent", border: "none", color: "var(--text-dim)", cursor: "pointer", fontSize: 18 }}
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<p style={{ fontSize: 13, color: "var(--text-dim)", lineHeight: 1.5, margin: "0 0 16px" }}>
|
||||
{bn
|
||||
? "Aura Cloud অ্যাপ আপনার মোবাইল বা কম্পিউটারে ইনস্টল করে সরাসরি ১-ট্যাপে ওপেন করুন:"
|
||||
: "Install the Aura Cloud App directly to your device for 1-tap instant launch and offline access:"}
|
||||
</p>
|
||||
|
||||
<div style={{ display: "flex", flexDirection: "column", gap: 10, fontSize: 12.5, color: "var(--text)" }}>
|
||||
<div style={{ padding: "10px 12px", background: "rgba(255,255,255,0.03)", borderRadius: 8, border: "1px solid var(--line)" }}>
|
||||
<strong>🍏 iOS / iPhone (Safari):</strong>
|
||||
<div style={{ color: "var(--text-dim)", marginTop: 4 }}>
|
||||
{bn
|
||||
? "সাফারি ব্রাউজারের নিচে শেয়ার বাটনে (Share ⎋) ট্যাপ করুন, এরপর নিচে স্ক্রল করে 'Add to Home Screen ⊞' সিলেক্ট করুন।"
|
||||
: "Tap the Share icon (⎋) at bottom of Safari, scroll down and tap 'Add to Home Screen ⊞'."}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ padding: "10px 12px", background: "rgba(255,255,255,0.03)", borderRadius: 8, border: "1px solid var(--line)" }}>
|
||||
<strong>🤖 Android (Chrome):</strong>
|
||||
<div style={{ color: "var(--text-dim)", marginTop: 4 }}>
|
||||
{bn
|
||||
? "ব্রাউজারের উপরের ডান কোণায় ৩-ডট মেনুতে (⋮) ট্যাপ করে 'Install app' বা 'Add to Home screen' নির্বাচন করুন।"
|
||||
: "Tap the 3 dots menu (⋮) in Chrome and select 'Install app' or 'Add to Home screen'."}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ padding: "10px 12px", background: "rgba(255,255,255,0.03)", borderRadius: 8, border: "1px solid var(--line)" }}>
|
||||
<strong>💻 Windows / Mac (Chrome & Edge):</strong>
|
||||
<div style={{ color: "var(--text-dim)", marginTop: 4 }}>
|
||||
{bn
|
||||
? "ব্রাউজারের অ্যাড্রেস বারের ডানপাশে ইনস্টল আইকন (⊕) ক্লিক করে ইনস্টল করুন।"
|
||||
: "Click the Install icon (⊕) in the browser address bar to install to desktop."}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={() => setInstallModalOpen(false)}
|
||||
style={{
|
||||
width: "100%",
|
||||
marginTop: 18,
|
||||
padding: "10px",
|
||||
background: "var(--text)",
|
||||
color: "var(--bg)",
|
||||
border: "none",
|
||||
borderRadius: 8,
|
||||
fontWeight: 600,
|
||||
fontSize: 13,
|
||||
cursor: "pointer",
|
||||
}}
|
||||
>
|
||||
{bn ? "ঠিক আছে, বুঝেছি" : "Got it"}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
// Aura Cloud Service Worker — High-Performance Offline & Shell Cache v2.0
|
||||
const CACHE_NAME = 'aura-cloud-pwa-v3';
|
||||
|
||||
const STATIC_PRECACHE = [
|
||||
'/',
|
||||
'/index.html',
|
||||
'/manifest.json',
|
||||
'/logo.png',
|
||||
'/apple-touch-icon.png',
|
||||
'/icon-192.png',
|
||||
'/icon-512.png',
|
||||
'/icon-maskable-192.png',
|
||||
'/icon-maskable-512.png',
|
||||
'/privacy.html',
|
||||
'/terms.html'
|
||||
];
|
||||
|
||||
self.addEventListener('install', (event) => {
|
||||
event.waitUntil(
|
||||
caches.open(CACHE_NAME).then((cache) => {
|
||||
return cache.addAll(STATIC_PRECACHE).catch((err) => {
|
||||
console.warn('[Aura PWA SW] Pre-cache initial warning:', err);
|
||||
});
|
||||
}).then(() => self.skipWaiting())
|
||||
);
|
||||
});
|
||||
|
||||
self.addEventListener('activate', (event) => {
|
||||
event.waitUntil(
|
||||
caches.keys().then((keys) => {
|
||||
return Promise.all(
|
||||
keys.filter((k) => k !== CACHE_NAME).map((k) => caches.delete(k))
|
||||
);
|
||||
}).then(() => self.clients.claim())
|
||||
);
|
||||
});
|
||||
|
||||
self.addEventListener('fetch', (event) => {
|
||||
const req = event.request;
|
||||
const url = new URL(req.url);
|
||||
|
||||
// 1. Bypass external/cross-origin requests (Auth, Analytics, Fonts, APIs)
|
||||
if (url.origin !== self.location.origin) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. Network-first strategy for navigation / HTML routes (instant updates)
|
||||
if (req.mode === 'navigate' || req.headers.get('accept')?.includes('text/html')) {
|
||||
event.respondWith(
|
||||
fetch(req)
|
||||
.then((networkRes) => {
|
||||
if (networkRes && networkRes.ok) {
|
||||
const clone = networkRes.clone();
|
||||
caches.open(CACHE_NAME).then((cache) => cache.put(req, clone));
|
||||
}
|
||||
return networkRes;
|
||||
})
|
||||
.catch(() => {
|
||||
return caches.match(req).then((cached) => cached || caches.match('/index.html'));
|
||||
})
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// 3. Stale-while-revalidate for same-origin static assets
|
||||
event.respondWith(
|
||||
caches.match(req).then((cached) => {
|
||||
const fetchPromise = fetch(req)
|
||||
.then((networkRes) => {
|
||||
if (networkRes && networkRes.ok) {
|
||||
const clone = networkRes.clone();
|
||||
caches.open(CACHE_NAME).then((cache) => cache.put(req, clone));
|
||||
}
|
||||
return networkRes;
|
||||
})
|
||||
.catch(() => null);
|
||||
|
||||
return cached || fetchPromise;
|
||||
})
|
||||
);
|
||||
});
|
||||
+54
-20
@@ -1,6 +1,51 @@
|
||||
{
|
||||
"cleanUrls": false,
|
||||
"trailingSlash": false,
|
||||
"headers": [
|
||||
{
|
||||
"source": "/sw.js",
|
||||
"headers": [
|
||||
{
|
||||
"key": "Cache-Control",
|
||||
"value": "no-cache, no-store, must-revalidate"
|
||||
},
|
||||
{
|
||||
"key": "Service-Worker-Allowed",
|
||||
"value": "/"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"source": "/manifest.json",
|
||||
"headers": [
|
||||
{
|
||||
"key": "Content-Type",
|
||||
"value": "application/manifest+json; charset=utf-8"
|
||||
},
|
||||
{
|
||||
"key": "Cache-Control",
|
||||
"value": "public, max-age=3600"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"source": "/(.*)",
|
||||
"headers": [
|
||||
{
|
||||
"key": "X-Content-Type-Options",
|
||||
"value": "nosniff"
|
||||
},
|
||||
{
|
||||
"key": "X-Frame-Options",
|
||||
"value": "SAMEORIGIN"
|
||||
},
|
||||
{
|
||||
"key": "Referrer-Policy",
|
||||
"value": "strict-origin-when-cross-origin"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"rewrites": [
|
||||
{
|
||||
"source": "/privacy",
|
||||
@@ -19,27 +64,16 @@
|
||||
"destination": "/terms.html"
|
||||
},
|
||||
{
|
||||
"source": "/((?!privacy|terms).*)",
|
||||
"destination": "/index.html"
|
||||
}
|
||||
],
|
||||
"headers": [
|
||||
"source": "/manifest.json",
|
||||
"destination": "/manifest.json"
|
||||
},
|
||||
{
|
||||
"source": "/(.*)",
|
||||
"headers": [
|
||||
{
|
||||
"key": "X-Content-Type-Options",
|
||||
"value": "nosniff"
|
||||
},
|
||||
{
|
||||
"key": "X-Frame-Options",
|
||||
"value": "SAMEORIGIN"
|
||||
},
|
||||
{
|
||||
"key": "Referrer-Policy",
|
||||
"value": "strict-origin-when-cross-origin"
|
||||
}
|
||||
]
|
||||
"source": "/sw.js",
|
||||
"destination": "/sw.js"
|
||||
},
|
||||
{
|
||||
"source": "/((?!privacy|terms|manifest\\.json|sw\\.js|.*\\.(?:png|jpg|jpeg|svg|ico|json|js|css|webmanifest)).*)",
|
||||
"destination": "/index.html"
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user