Тестовая страница встраивания в CRM (postMessage auth + проверка в Supabase)
This commit is contained in:
107
public/index.html
Normal file
107
public/index.html
Normal file
@@ -0,0 +1,107 @@
|
||||
<!doctype html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>CRM embed test</title>
|
||||
<style>
|
||||
body { font: 15px/1.5 system-ui, sans-serif; margin: 16px; color: #222; }
|
||||
h1 { font-size: 18px; margin: 0 0 12px; }
|
||||
#status { padding: 8px 12px; border-radius: 6px; background: #fff4d6; }
|
||||
#status.ok { background: #e3f6e3; } #status.err { background: #fde2e2; }
|
||||
dl { display: grid; grid-template-columns: max-content 1fr; gap: 4px 12px; }
|
||||
dt { color: #666; } dd { margin: 0; word-break: break-all; }
|
||||
button { margin-top: 12px; padding: 6px 14px; }
|
||||
#log { margin-top: 16px; font-size: 13px; color: #555; padding-left: 18px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Тест встраивания в CRM Tvorymo</h1>
|
||||
<div id="status">жду авторизацию от CRM</div>
|
||||
<dl id="info" hidden>
|
||||
<dt>Имя</dt><dd id="name"></dd>
|
||||
<dt>Email</dt><dd id="email"></dd>
|
||||
<dt>ID</dt><dd id="uid"></dd>
|
||||
<dt>Origin отправителя</dt><dd id="origin"></dd>
|
||||
<dt>expires_at</dt><dd id="expires"></dd>
|
||||
<dt>Получено токенов</dt><dd id="count">0</dd>
|
||||
</dl>
|
||||
<button id="refresh" hidden>Обновить токен</button>
|
||||
<ol id="log"></ol>
|
||||
<script>
|
||||
(() => {
|
||||
// Сессия только в памяти, токен не выводится и не логируется.
|
||||
let session = null; // { token, expiresAt }
|
||||
let crmSource = null, crmOrigin = null, count = 0;
|
||||
const $ = (id) => document.getElementById(id);
|
||||
const inIframe = window.parent !== window;
|
||||
|
||||
function setStatus(text, cls) { const s = $("status"); s.textContent = text; s.className = cls || ""; }
|
||||
function log(text) {
|
||||
const li = document.createElement("li");
|
||||
li.textContent = new Date().toLocaleTimeString() + " — " + text;
|
||||
$("log").prepend(li);
|
||||
}
|
||||
function fmtTime(v) {
|
||||
if (v == null) return "—";
|
||||
const n = Number(v);
|
||||
const d = new Date(Number.isFinite(n) ? (n < 1e12 ? n * 1000 : n) : v);
|
||||
return isNaN(d) ? String(v) : d.toLocaleString() + " (" + d.toISOString() + ")";
|
||||
}
|
||||
|
||||
async function onAuth(data, ev) {
|
||||
crmSource = ev.source; crmOrigin = ev.origin;
|
||||
$("origin").textContent = ev.origin;
|
||||
const token = data.access_token;
|
||||
if (typeof token !== "string" || !token) { setStatus("crm:auth без access_token", "err"); return; }
|
||||
const changed = session && session.token !== token;
|
||||
const prevExp = session && session.expiresAt;
|
||||
session = { token, expiresAt: data.expires_at };
|
||||
count++;
|
||||
setStatus("проверяю токен в Supabase…");
|
||||
try {
|
||||
const r = await fetch("/api/verify", {
|
||||
method: "POST", headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ access_token: token }),
|
||||
});
|
||||
const res = await r.json();
|
||||
if (!res.ok) { setStatus("Токен не принят: " + res.error, "err"); log("crm:auth от " + ev.origin + " — отклонён"); return; }
|
||||
$("name").textContent = res.name || "имя не задано в Supabase";
|
||||
$("email").textContent = res.email || "—";
|
||||
$("uid").textContent = res.id;
|
||||
$("expires").textContent = fmtTime(data.expires_at != null ? data.expires_at : res.exp);
|
||||
$("count").textContent = String(count);
|
||||
$("info").hidden = false; $("refresh").hidden = false;
|
||||
setStatus(changed ? "Токен сменился, пользователь подтверждён Supabase" : "Пользователь подтверждён Supabase", "ok");
|
||||
log("crm:auth от " + ev.origin + (changed
|
||||
? " — новый токен, expires_at: " + fmtTime(prevExp) + " → " + fmtTime(data.expires_at)
|
||||
: session && count > 1 ? " — тот же токен" : " — принят"));
|
||||
} catch (e) {
|
||||
setStatus("Ошибка проверки токена", "err");
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener("message", (ev) => {
|
||||
const data = ev.data;
|
||||
if (!data || typeof data !== "object") return;
|
||||
// Для теста принимаем от любого origin (решение владельца).
|
||||
if (data.type === "crm:auth") onAuth(data, ev);
|
||||
});
|
||||
|
||||
$("refresh").addEventListener("click", () => {
|
||||
const target = crmSource || window.parent;
|
||||
target.postMessage({ type: "crm:refresh" }, crmOrigin || "*");
|
||||
setStatus("запросил новый токен (crm:refresh)…");
|
||||
log("отправлен crm:refresh → " + (crmOrigin || "*"));
|
||||
});
|
||||
|
||||
if (inIframe) {
|
||||
window.parent.postMessage({ type: "crm:ready", version: 1 }, "*");
|
||||
log("отправлен crm:ready");
|
||||
} else {
|
||||
log("страница открыта не в iframe");
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user