feat(logging): add HTTP page fetch logging and enhance click-TT proxy functionality

- Introduced a new logging mechanism for HTTP requests to click-TT/HTTV pages, improving traceability and debugging capabilities.
- Implemented a proxy endpoint for iframe embedding, allowing direct HTML retrieval with enhanced error handling and validation for input URLs.
- Updated the frontend to include a new navigation link for the click-TT feature, accessible to admin users.
- Added a new route for the click-TT view in the router configuration.
This commit is contained in:
Torsten Schulz (local)
2026-03-10 21:27:40 +01:00
parent 055dbf115c
commit 13379d6b24
6 changed files with 477 additions and 3 deletions

View File

@@ -6,11 +6,82 @@
import express from 'express';
import clickTtHttpPageService from '../services/clickTtHttpPageService.js';
import HttpPageFetchLog from '../models/HttpPageFetchLog.js';
import { authenticate } from '../middleware/authenticate.js';
import { Op } from 'sequelize';
import { authenticate } from '../middleware/authMiddleware.js';
const router = express.Router();
/**
* GET /api/clicktt/proxy
* Proxy für iframe-Einbettung liefert HTML direkt (ohne Auth, für iframe src).
* Query: type (leaguePage|clubInfo|regionMeetings), association, championship, clubId
* ODER: url (vollständige URL, nur click-tt.de/httv.de)
*/
router.get('/proxy', async (req, res, next) => {
try {
const { type, association, championship, clubId, url } = req.query;
let targetUrl = null;
let fetchType = 'proxy';
if (url) {
if (!url.includes('click-tt.de') && !url.includes('httv.de')) {
return res.status(400).send('<html><body><h1>Fehler</h1><p>Nur URLs von click-tt.de oder httv.de sind erlaubt.</p></body></html>');
}
targetUrl = url;
fetchType = 'arbitrary';
} else if (type === 'leaguePage') {
targetUrl = clickTtHttpPageService.buildLeaguePageUrl({
association: association || 'HeTTV',
championship: championship || 'HTTV 25/26',
});
fetchType = 'leaguePage';
} else if (type === 'clubInfo' && clubId) {
targetUrl = clickTtHttpPageService.buildClubInfoDisplayUrl({
association: association || 'HeTTV',
clubId,
});
fetchType = 'clubInfoDisplay';
} else if (type === 'regionMeetings') {
targetUrl = clickTtHttpPageService.buildRegionMeetingFilterUrl({
association: association || 'HeTTV',
championship: championship || 'HTTV 25/26',
});
fetchType = 'regionMeetingFilter';
} else {
return res.status(400).send('<html><body><h1>Fehler</h1><p>Parameter type (leaguePage|clubInfo|regionMeetings) oder url erforderlich. Bei clubInfo ist clubId nötig.</p></body></html>');
}
const result = await clickTtHttpPageService.fetchWithLogging({
url: targetUrl,
fetchType,
association: association || null,
championship: championship || null,
clubIdParam: clubId || null,
userId: null,
});
let html = result.body;
// CSP/X-Frame-Options entfernen für iframe-Einbettung
html = (html || '')
.replace(/<meta[^>]*http-equiv=["']content-security-policy["'][^>]*>/gi, '')
.replace(/<meta[^>]*http-equiv=["']x-frame-options["'][^>]*>/gi, '')
.replace(/<meta[^>]*http-equiv=["']x-content-type-options["'][^>]*>/gi, '');
res.set({
'Content-Type': 'text/html; charset=utf-8',
'Access-Control-Allow-Origin': '*',
'X-Frame-Options': 'ALLOWALL',
'Content-Security-Policy': 'frame-ancestors *;',
'Cache-Control': 'no-cache, no-store, must-revalidate',
});
res.send(html);
} catch (error) {
console.error('ClickTT Proxy Fehler:', error);
res.status(500).send(
`<html><body><h1>Fehler beim Laden</h1><p>${String(error.message)}</p></body></html>`
);
}
});
/**
* GET /api/clicktt/league-page
* Ruft die Ligenübersicht ab (leaguePage)