Add legal consent and reliable external redirects
This commit is contained in:
@@ -37,11 +37,18 @@ RUN mkdir -p /build \
|
||||
&& cp /src/assets/fonts/Inter-Variable.ttf /build/medmind-inter.ttf \
|
||||
&& cp /src/web/donation-complete.html /build/donation-complete.html \
|
||||
&& cp /src/web/donation-canceled.html /build/donation-canceled.html \
|
||||
&& cp /src/web/terms.html /src/web/privacy.html /src/web/legal-page.css /src/web/legal-page.js /src/web/external-redirect.html /build/ \
|
||||
&& mkdir -p /build/legal \
|
||||
&& cp /src/legal/legal_documents.json /build/legal/legal_documents.json \
|
||||
&& test -s /build/index.html \
|
||||
&& test -s /build/ranaze-loader-icon.png \
|
||||
&& test -s /build/medmind-inter.ttf \
|
||||
&& test -s /build/donation-complete.html \
|
||||
&& test -s /build/donation-canceled.html \
|
||||
&& test -s /build/terms.html \
|
||||
&& test -s /build/privacy.html \
|
||||
&& test -s /build/external-redirect.html \
|
||||
&& test -s /build/legal/legal_documents.json \
|
||||
&& test -s /build/index.wasm \
|
||||
&& test -s /build/index.pck
|
||||
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
<!doctype html>
|
||||
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<title>Opening secure service | RanaZe</title><style>body{font:16px system-ui;background:#081324;color:#f8fafc;display:grid;place-items:center;min-height:100vh;margin:0}.card{max-width:34rem;padding:2rem;border:1px solid #33415d;border-radius:8px}a{color:#38bdf8}</style></head>
|
||||
<body><main class="card"><strong id="title">Opening secure service...</strong><p id="message">This window will continue automatically.</p><p><a id="continue" hidden>Continue</a></p></main>
|
||||
<script>
|
||||
(() => {
|
||||
const parameters = new URLSearchParams(location.search);
|
||||
const key = parameters.get("key") || "";
|
||||
const provider = parameters.get("provider") || "external service";
|
||||
const allowedKey = /^ranaze_[a-z0-9_]{1,80}$/i.test(key);
|
||||
const title = document.getElementById("title");
|
||||
const message = document.getElementById("message");
|
||||
const link = document.getElementById("continue");
|
||||
title.textContent = `Opening ${provider}...`;
|
||||
message.textContent = `This window will continue to ${provider} automatically.`;
|
||||
const redirect = () => {
|
||||
if (!allowedKey) return;
|
||||
const url = localStorage.getItem(key);
|
||||
if (!url || !url.startsWith("https://")) return;
|
||||
link.href = url;
|
||||
link.hidden = false;
|
||||
link.textContent = `Continue to ${provider}`;
|
||||
localStorage.removeItem(key);
|
||||
location.replace(url);
|
||||
};
|
||||
addEventListener("storage", (event) => { if (event.key === key) redirect(); });
|
||||
addEventListener("focus", redirect);
|
||||
setInterval(redirect, 500);
|
||||
redirect();
|
||||
})();
|
||||
</script></body></html>
|
||||
@@ -0,0 +1,19 @@
|
||||
:root { color-scheme: light dark; font-family: Inter, system-ui, sans-serif; }
|
||||
* { box-sizing: border-box; }
|
||||
body { margin: 0; background: #f6f8fc; color: #172033; line-height: 1.65; }
|
||||
main { width: min(880px, calc(100% - 32px)); margin: 32px auto 72px; }
|
||||
header { border-bottom: 1px solid #cbd5e1; margin-bottom: 28px; padding-bottom: 22px; }
|
||||
.brand { color: #2563eb; font-weight: 700; text-decoration: none; }
|
||||
h1 { font-size: clamp(2rem, 5vw, 3rem); line-height: 1.1; margin: 18px 0 8px; }
|
||||
.meta, .summary { color: #526078; }
|
||||
section { margin: 30px 0; }
|
||||
h2 { font-size: 1.25rem; line-height: 1.35; }
|
||||
a { color: #2563eb; }
|
||||
.error { color: #dc2626; font-weight: 600; }
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body { background: #081324; color: #f8fafc; }
|
||||
header { border-color: #33415d; }
|
||||
.meta, .summary { color: #a9b6cc; }
|
||||
a, .brand { color: #38bdf8; }
|
||||
.error { color: #fb7185; }
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
(() => {
|
||||
const key = document.body.dataset.document;
|
||||
const target = document.getElementById("legal-document");
|
||||
const escapeHtml = (value) => String(value).replace(/[&<>"']/g, (character) => ({
|
||||
"&": "&", "<": "<", ">": ">", '"': """, "'": "'"
|
||||
})[character]);
|
||||
|
||||
fetch("/legal/legal_documents.json", { cache: "no-store" })
|
||||
.then((response) => {
|
||||
if (!response.ok) throw new Error(`status ${response.status}`);
|
||||
return response.json();
|
||||
})
|
||||
.then((documents) => {
|
||||
const documentData = documents[key];
|
||||
if (!documentData) throw new Error("document not found");
|
||||
document.title = `${documentData.title} | RanaZe`;
|
||||
const sections = documentData.sections.map((section) => `
|
||||
<section><h2>${escapeHtml(section.heading)}</h2>
|
||||
${section.paragraphs.map((paragraph) => `<p>${escapeHtml(paragraph)}</p>`).join("")}</section>`).join("");
|
||||
target.innerHTML = `
|
||||
<header><a class="brand" href="/">RanaZe: Step 3 Trainer</a>
|
||||
<h1>${escapeHtml(documentData.title)}</h1>
|
||||
<div class="meta">Effective ${escapeHtml(documentData.effectiveDate)} | Version ${escapeHtml(documentData.version)}</div>
|
||||
<p class="summary">${escapeHtml(documentData.summary)}</p></header>${sections}`;
|
||||
})
|
||||
.catch(() => { target.innerHTML = '<p class="error">This document could not be loaded. Contact [email protected].</p>'; });
|
||||
})();
|
||||
@@ -41,6 +41,12 @@ server {
|
||||
try_files $uri =404;
|
||||
}
|
||||
|
||||
location ~ ^/(terms\.html|privacy\.html|legal-page\.(css|js)|legal/legal_documents\.json|external-redirect\.html)$ {
|
||||
add_header Cache-Control "no-cache, no-store, must-revalidate" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
try_files $uri =404;
|
||||
}
|
||||
|
||||
# Godot exports reuse these filenames across releases. Revalidate them so a
|
||||
# newly deployed index never boots an older cached project package.
|
||||
location ~ ^/(index\.(js|pck|wasm)|ranaze-loader-icon\.png|medmind-inter\.ttf)$ {
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
<!doctype html>
|
||||
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<title>Privacy Policy | RanaZe</title><link rel="stylesheet" href="/legal-page.css"></head>
|
||||
<body data-document="privacy"><main id="legal-document"><p>Loading Privacy Policy...</p></main><script src="/legal-page.js"></script></body></html>
|
||||
@@ -0,0 +1,4 @@
|
||||
<!doctype html>
|
||||
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<title>Terms of Service | RanaZe</title><link rel="stylesheet" href="/legal-page.css"></head>
|
||||
<body data-document="terms"><main id="legal-document"><p>Loading Terms of Service...</p></main><script src="/legal-page.js"></script></body></html>
|
||||
Reference in New Issue
Block a user