feat: iframe proxy test — fetch Sechenov page, strip X-Frame, rewrite URLs
This commit is contained in:
135
public/iframe.html
Normal file
135
public/iframe.html
Normal file
@@ -0,0 +1,135 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<title>Авторизация и регистрация</title>
|
||||
<link rel="icon" type="image/png" href="/assets/images/favicon.png">
|
||||
<style>
|
||||
html, body { margin: 0; padding: 0; height: 100%; overflow: hidden; }
|
||||
#portal {
|
||||
display: block;
|
||||
border: 0;
|
||||
width: 100%;
|
||||
height: calc(100vh - 50px);
|
||||
}
|
||||
/* chat stays fixed at the bottom 50px of viewport */
|
||||
.chat-overlay {
|
||||
position: fixed;
|
||||
left: 0; right: 0; bottom: 0;
|
||||
background: #fff;
|
||||
border-top: 4px solid #003571;
|
||||
z-index: 9999;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
}
|
||||
#chat-output-tiny {
|
||||
position: fixed;
|
||||
left: 0; right: 0;
|
||||
bottom: 50px;
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
padding: 4px 15px;
|
||||
font-size: 10px;
|
||||
color: #c4c4c4;
|
||||
line-height: 1.5;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
background: linear-gradient(to bottom, transparent 0%, rgba(255,255,255,0.92) 30%, #fff 100%);
|
||||
pointer-events: none;
|
||||
}
|
||||
#chat-output-tiny:empty { display: none; }
|
||||
.chat-overlay .inner {
|
||||
max-width: 970px; margin: 0 auto;
|
||||
padding: 8px 15px;
|
||||
}
|
||||
#chat-input {
|
||||
width: 100%;
|
||||
border: none;
|
||||
background: transparent;
|
||||
color: #f2f2f2;
|
||||
font-size: 14px;
|
||||
outline: none;
|
||||
resize: none;
|
||||
height: 28px;
|
||||
max-height: 28px;
|
||||
min-height: 28px;
|
||||
line-height: 1.5;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
caret-color: #c4c4c4;
|
||||
font-family: inherit;
|
||||
}
|
||||
#chat-input::placeholder { color: transparent; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<iframe id="portal" src="/proxy/auth.php"></iframe>
|
||||
|
||||
<div id="chat-output-tiny"></div>
|
||||
|
||||
<div class="chat-overlay">
|
||||
<div class="inner">
|
||||
<textarea id="chat-input" autocomplete="off" spellcheck="false" rows="1"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const inputEl = document.getElementById('chat-input');
|
||||
const outputEl = document.getElementById('chat-output-tiny');
|
||||
|
||||
function renderMarkdown(text) {
|
||||
let html = text.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');
|
||||
html = html.replace(/```[\w]*\n?([\s\S]*?)```/g, (_,c) => `<pre>${c.trim()}</pre>`);
|
||||
html = html.replace(/`([^`]+)`/g, '<code>$1</code>');
|
||||
html = html.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>');
|
||||
return html;
|
||||
}
|
||||
|
||||
let inFlight = false;
|
||||
async function send(text) {
|
||||
if (inFlight || !text.trim()) return;
|
||||
inFlight = true;
|
||||
outputEl.innerHTML = '';
|
||||
let fullText = '';
|
||||
try {
|
||||
const resp = await fetch('/api/chat', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type':'application/json'},
|
||||
body: JSON.stringify({text, model: 'sonnet'}),
|
||||
});
|
||||
const reader = resp.body.getReader();
|
||||
const decoder = new TextDecoder();
|
||||
let buf = '';
|
||||
while (true) {
|
||||
const {done, value} = await reader.read();
|
||||
if (done) break;
|
||||
buf += decoder.decode(value, {stream:true});
|
||||
const parts = buf.split('\n\n');
|
||||
buf = parts.pop();
|
||||
for (const part of parts) {
|
||||
if (!part.startsWith('data: ')) continue;
|
||||
const raw = part.slice(6).trim();
|
||||
if (raw === '[DONE]') continue;
|
||||
try {
|
||||
const obj = JSON.parse(raw);
|
||||
if (obj.text) {
|
||||
fullText += obj.text;
|
||||
outputEl.innerHTML = renderMarkdown(fullText);
|
||||
}
|
||||
} catch(e) {}
|
||||
}
|
||||
}
|
||||
} catch(e) {} finally { inFlight = false; }
|
||||
}
|
||||
|
||||
inputEl.addEventListener('paste', () => {
|
||||
setTimeout(() => {
|
||||
const t = inputEl.value.trim();
|
||||
if (t) send(t);
|
||||
}, 50);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user