feat(clickTtHttpPageRoutes, ClickTtView): implement proxy navigation script and enhance URL selection

- Added a new function to inject a navigation script into HTML responses, enabling seamless proxy navigation for links and form submissions.
- Updated the ClickTtView component to include new preset URL options for HTTV and TTDE login, improving user experience by providing quick access to common links.
- Adjusted form handling logic to support the new preset URLs, ensuring proper URL management during proxy interactions.
This commit is contained in:
Torsten Schulz (local)
2026-03-10 22:48:54 +01:00
parent 71ac054d48
commit 59d7c3559c
2 changed files with 95 additions and 13 deletions

View File

@@ -159,6 +159,86 @@ function rewriteFormActionsInHtml(html, proxyBaseUrl, pageBaseUrl, sid) {
}
}
function injectProxyNavigationScript(html, proxyBaseUrl, pageBaseUrl, sid) {
if (!html || !proxyBaseUrl || !pageBaseUrl) return html;
const script = `
<script>
(function () {
const PROXY_BASE_URL = ${JSON.stringify(proxyBaseUrl)};
const PAGE_BASE_URL = ${JSON.stringify(pageBaseUrl)};
const SID = ${JSON.stringify(sid || '')};
function normalizeUrl(value, base) {
if (!value) return null;
const trimmed = String(value).trim();
if (!trimmed || trimmed.startsWith('#') || trimmed.startsWith('javascript:')) return null;
try {
return new URL(trimmed, base || PAGE_BASE_URL).href;
} catch {
return null;
}
}
function shouldProxyUrl(value) {
return /(^|\\.)click-tt\\.de$|(^|\\.)httv\\.de$/i.test(new URL(value).hostname);
}
function buildProxyUrl(targetUrl) {
const separator = PROXY_BASE_URL.includes('?') ? '&' : '?';
return PROXY_BASE_URL + separator + 'url=' + encodeURIComponent(targetUrl) + (SID ? '&sid=' + encodeURIComponent(SID) : '');
}
function navigateViaProxy(targetUrl) {
if (!targetUrl) return false;
if (!shouldProxyUrl(targetUrl)) return false;
window.location.assign(buildProxyUrl(targetUrl));
return true;
}
function getSubmitTarget(form, submitter) {
const action = submitter && submitter.getAttribute && submitter.getAttribute('formaction');
if (action) return normalizeUrl(action, PAGE_BASE_URL);
return normalizeUrl(form.getAttribute('action') || PAGE_BASE_URL, PAGE_BASE_URL);
}
document.addEventListener('click', function (event) {
const anchor = event.target && event.target.closest ? event.target.closest('a[href]') : null;
if (!anchor) return;
const targetUrl = normalizeUrl(anchor.getAttribute('href'), PAGE_BASE_URL);
if (!targetUrl || !shouldProxyUrl(targetUrl)) return;
event.preventDefault();
event.stopPropagation();
navigateViaProxy(targetUrl);
}, true);
document.addEventListener('submit', function (event) {
const form = event.target;
if (!form || !form.tagName || form.tagName.toLowerCase() !== 'form') return;
const submitter = event.submitter || null;
const targetUrl = getSubmitTarget(form, submitter);
if (!targetUrl || !shouldProxyUrl(targetUrl)) return;
form.setAttribute('action', buildProxyUrl(targetUrl));
}, true);
const nativeSubmit = HTMLFormElement.prototype.submit;
HTMLFormElement.prototype.submit = function patchedSubmit() {
const targetUrl = getSubmitTarget(this, null);
if (targetUrl && shouldProxyUrl(targetUrl)) {
this.setAttribute('action', buildProxyUrl(targetUrl));
}
return nativeSubmit.apply(this, arguments);
};
})();
</script>`;
if (/<\/body>/i.test(html)) {
return html.replace(/<\/body>/i, `${script}</body>`);
}
return html + script;
}
/**
* GET /api/clicktt/proxy
* Proxy für iframe-Einbettung liefert HTML direkt (ohne Auth, für iframe src).
@@ -248,6 +328,7 @@ router.get('/proxy', async (req, res, next) => {
html = rewriteLinksInHtml(html, proxyBase, effectivePageUrl, sid);
html = rewriteFormActionsInHtml(html, proxyBase, effectivePageUrl, sid);
html = rewriteMetaRefreshInHtml(html, proxyBase, effectivePageUrl, sid);
html = injectProxyNavigationScript(html, proxyBase, effectivePageUrl, sid);
res.set({
'Content-Type': 'text/html; charset=utf-8',
@@ -344,6 +425,7 @@ router.post('/proxy', async (req, res, next) => {
responseBody = rewriteLinksInHtml(responseBody, proxyBase, effectivePageUrl, sid);
responseBody = rewriteFormActionsInHtml(responseBody, proxyBase, effectivePageUrl, sid);
responseBody = rewriteMetaRefreshInHtml(responseBody, proxyBase, effectivePageUrl, sid);
responseBody = injectProxyNavigationScript(responseBody, proxyBase, effectivePageUrl, sid);
}
res.set({