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:
@@ -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({
|
||||
|
||||
@@ -17,10 +17,12 @@
|
||||
<option value="clubInfo">Vereinsinfo (clubInfoDisplay)</option>
|
||||
<option value="regionMeetings">Regionsspielplan (regionMeetingFilter)</option>
|
||||
<option value="url">Direkte URL</option>
|
||||
<option value="preset:httv-home">HTTV Einstieg: https://httv.de</option>
|
||||
<option value="preset:ttde-login">TTDE Login: ttde-id.liga.nu/oauth2/authz/ttde...</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group" v-if="pageType !== 'url'">
|
||||
<div class="form-group" v-if="pageType === 'leaguePage' || pageType === 'clubInfo' || pageType === 'regionMeetings'">
|
||||
<label>Verband</label>
|
||||
<select v-model="association" class="form-select">
|
||||
<option value="HeTTV">HeTTV (Hessen)</option>
|
||||
@@ -30,7 +32,7 @@
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group" v-if="pageType !== 'url' && pageType !== 'clubInfo'">
|
||||
<div class="form-group" v-if="pageType === 'leaguePage' || pageType === 'regionMeetings'">
|
||||
<label>Championship / Saison</label>
|
||||
<input
|
||||
v-model="championship"
|
||||
@@ -50,17 +52,6 @@
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="form-group" v-if="pageType === 'url'">
|
||||
<label>URL-Vorlage</label>
|
||||
<select v-model="directUrl" class="form-select form-input-wide">
|
||||
<option value="">Bitte auswählen...</option>
|
||||
<option value="https://httv.de">HTTV Einstieg: https://httv.de</option>
|
||||
<option value="https://ttde-id.liga.nu/oauth2/authz/ttde?scope=nuLiga&response_type=code&redirect_uri=https%3A%2F%2Fhttv.click-tt.de%2Fcgi-bin%2FWebObjects%2FnuLigaTTDE.woa%2Fwa%2FoAuthLogin&state=nonce%3DVF6WbXUOvTjpsGq9zoZ6oxTH7625JEGH&client_id=XtVpGjXKAhz3BZuu">
|
||||
TTDE Login: ttde-id.liga.nu/oauth2/authz/ttde...
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group" v-if="pageType === 'url'">
|
||||
<label>URL</label>
|
||||
<input
|
||||
@@ -98,6 +89,10 @@
|
||||
import { ref, computed } from 'vue';
|
||||
|
||||
const backendBaseUrl = import.meta.env.VITE_BACKEND || 'http://localhost:3005';
|
||||
const presetUrls = {
|
||||
'preset:httv-home': 'https://httv.de',
|
||||
'preset:ttde-login': 'https://ttde-id.liga.nu/oauth2/authz/ttde?scope=nuLiga&response_type=code&redirect_uri=https%3A%2F%2Fhttv.click-tt.de%2Fcgi-bin%2FWebObjects%2FnuLigaTTDE.woa%2Fwa%2FoAuthLogin&state=nonce%3DVF6WbXUOvTjpsGq9zoZ6oxTH7625JEGH&client_id=XtVpGjXKAhz3BZuu',
|
||||
};
|
||||
|
||||
export default {
|
||||
name: 'ClickTtView',
|
||||
@@ -114,6 +109,9 @@ export default {
|
||||
if (pageType.value === 'url') {
|
||||
return directUrl.value.trim().length > 0;
|
||||
}
|
||||
if (pageType.value.startsWith('preset:')) {
|
||||
return true;
|
||||
}
|
||||
if (pageType.value === 'clubInfo') {
|
||||
return clubId.value.trim().length > 0;
|
||||
}
|
||||
@@ -126,6 +124,8 @@ export default {
|
||||
|
||||
if (pageType.value === 'url') {
|
||||
params.set('url', directUrl.value.trim());
|
||||
} else if (pageType.value.startsWith('preset:')) {
|
||||
params.set('url', presetUrls[pageType.value]);
|
||||
} else {
|
||||
params.set('type', pageType.value);
|
||||
params.set('association', association.value);
|
||||
|
||||
Reference in New Issue
Block a user