diff --git a/server.js b/server.js
index bddd008..f7b4a56 100644
--- a/server.js
+++ b/server.js
@@ -11,8 +11,12 @@ if (proxyUrl) {
}
const app = express();
-app.use(express.json({ limit: '10mb' }));
-app.use(express.static(path.join(__dirname, 'public')));
+const PUBLIC_DIR = path.join(__dirname, 'public');
+const UPSTREAM = 'https://student.sechenov.ru';
+
+// ══════════════════════════════════════════════════════════════
+// CLAUDE CHAT (/api/chat)
+// ══════════════════════════════════════════════════════════════
const MODELS = {
sonnet: 'claude-sonnet-4-6',
@@ -71,7 +75,7 @@ async function callClaude(modelId, text, accessToken) {
});
}
-app.post('/api/chat', async (req, res) => {
+app.post('/api/chat', express.json({ limit: '10mb' }), async (req, res) => {
const { text, model = 'sonnet' } = req.body;
if (!text?.trim()) return res.status(400).json({ error: 'text required' });
@@ -86,28 +90,22 @@ app.post('/api/chat', async (req, res) => {
res.flushHeaders();
const heartbeat = setInterval(() => res.write(': ping\n\n'), 15000);
-
const primaryId = MODELS[model] || MODELS.sonnet;
const fallbackId = MODELS.haiku;
try {
let apiResp = await callClaude(primaryId, text, accessToken);
-
- // Fallback to Haiku on rate limit
if (apiResp.status === 429 && primaryId !== fallbackId) {
apiResp = await callClaude(fallbackId, text, accessToken);
}
-
if (!apiResp.ok) {
const errText = await apiResp.text();
res.write(`data: ${JSON.stringify({ error: `${apiResp.status}: ${errText.substring(0, 500)}` })}\n\n`);
return;
}
-
const reader = apiResp.body.getReader();
const decoder = new TextDecoder();
let buf = '';
-
while (true) {
const { done, value } = await reader.read();
if (done) break;
@@ -137,58 +135,168 @@ app.post('/api/chat', async (req, res) => {
}
});
-// ── iframe proxy: fetch student.sechenov.ru, strip X-Frame-Options, rewrite URLs ──
+// ══════════════════════════════════════════════════════════════
+// LEGACY / LOCAL ROUTES
+// ══════════════════════════════════════════════════════════════
+
+app.get(['/old', '/old.html'], (_req, res) => res.sendFile(path.join(PUBLIC_DIR, 'index.html')));
+app.get(['/iframe', '/iframe.html'], (_req, res) => res.sendFile(path.join(PUBLIC_DIR, 'iframe.html')));
+app.use('/assets', express.static(path.join(PUBLIC_DIR, 'assets')));
+
+// Legacy HTML-only proxy used by /iframe.html
app.get('/proxy/*', async (req, res) => {
const upstreamPath = req.url.replace(/^\/proxy/, '') || '/';
- const upstreamUrl = 'https://student.sechenov.ru' + upstreamPath;
-
try {
- const r = await fetch(upstreamUrl, {
+ const r = await fetch(UPSTREAM + upstreamPath, {
headers: {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
'Accept': req.headers.accept || '*/*',
'Accept-Language': req.headers['accept-language'] || 'ru,en;q=0.9',
},
});
-
const ct = r.headers.get('content-type') || 'application/octet-stream';
res.status(r.status);
res.setHeader('Content-Type', ct);
- // strip framing restrictions
res.removeHeader('X-Frame-Options');
res.removeHeader('Content-Security-Policy');
-
if (ct.includes('text/html')) {
let html = await r.text();
html = html
- // navigation → stay inside iframe via our proxy
.replace(/\b(href|action)="\/(?!\/)/g, '$1="/proxy/')
- // assets load directly from upstream (faster, no rewrite needed)
- .replace(/\bsrc="\/(?!\/)/g, 'src="https://student.sechenov.ru/')
- .replace(/url\(["']?\/(?!\/)/g, 'url(https://student.sechenov.ru/')
- // strip framing-break targets
+ .replace(/\bsrc="\/(?!\/)/g, 'src="' + UPSTREAM + '/')
+ .replace(/url\(["']?\/(?!\/)/g, 'url(' + UPSTREAM + '/')
.replace(/\btarget=["'](_top|_parent|_blank)["']/gi, '')
- // neutralize meta CSP/X-Frame
.replace(/]+http-equiv=["']?(Content-Security-Policy|X-Frame-Options)["']?[^>]*>/gi, '')
- // intercept inline JS redirects
.replace(/\b(document|window|top|self)\.location(\.href)?\s*=\s*(['"])\/(?!\/)/g, "document.location.href=$3/proxy/")
- .replace(/\blocation\.href\s*=\s*(['"])\/(?!\/)/g, "location.href=$1/proxy/")
.replace(/\blocation\.replace\(\s*(['"])\/(?!\/)/g, "location.replace($1/proxy/");
- // inject a