Implement login page proxy and CAPTCHA handling in MyTischtennisClient and Controller. Enhance login process with CAPTCHA token extraction and error handling. Update frontend to support iframe-based login and improve user experience with loading indicators.

This commit is contained in:
Torsten Schulz (local)
2025-11-23 15:18:53 +01:00
parent b74cb30cf6
commit f7a799ea7f
15 changed files with 702 additions and 284 deletions

View File

@@ -78,6 +78,9 @@ class AutoFetchMatchResultsService {
const loginResult = await myTischtennisClient.login(account.email, password);
if (!loginResult.success) {
if (loginResult.requiresCaptcha) {
throw new Error(`Re-login failed: CAPTCHA erforderlich. Bitte loggen Sie sich einmal direkt auf mytischtennis.de ein, um das CAPTCHA zu lösen.`);
}
throw new Error(`Re-login failed: ${loginResult.error}`);
}

View File

@@ -70,6 +70,9 @@ class AutoUpdateRatingsService {
const loginResult = await myTischtennisClient.login(account.email, password);
if (!loginResult.success) {
if (loginResult.requiresCaptcha) {
throw new Error(`Re-login failed: CAPTCHA erforderlich. Bitte loggen Sie sich einmal direkt auf mytischtennis.de ein, um das CAPTCHA zu lösen.`);
}
throw new Error(`Re-login failed: ${loginResult.error}`);
}

View File

@@ -59,7 +59,12 @@ class MyTischtennisService {
// Login-Versuch bei myTischtennis
loginResult = await myTischtennisClient.login(email, password);
if (!loginResult.success) {
throw new HttpError(loginResult.error || 'myTischtennis-Login fehlgeschlagen. Bitte überprüfen Sie Ihre Zugangsdaten.', 401);
const statusCode = loginResult.requiresCaptcha ? 400 : 401;
const errorMessage = loginResult.error || 'myTischtennis-Login fehlgeschlagen. Bitte überprüfen Sie Ihre Zugangsdaten.';
if (loginResult.requiresCaptcha) {
throw new HttpError({ code: 'ERROR_MYTISCHTENNIS_CAPTCHA_REQUIRED', params: { message: errorMessage } }, statusCode);
}
throw new HttpError(errorMessage, statusCode);
}
}
@@ -220,7 +225,7 @@ class MyTischtennisService {
// Login-Versuch mit Passwort
console.log('[myTischtennisService.verifyLogin] Attempting login for user:', account.email);
const loginResult = await myTischtennisClient.login(account.email, password);
console.log('[myTischtennisService.verifyLogin] Login result:', { success: loginResult.success, error: loginResult.error });
console.log('[myTischtennisService.verifyLogin] Login result:', { success: loginResult.success, error: loginResult.error, requiresCaptcha: loginResult.requiresCaptcha });
if (loginResult.success) {
account.lastLoginSuccess = now;
@@ -256,10 +261,16 @@ class MyTischtennisService {
await account.save(); // Save lastLoginAttempt
const errorMessage = loginResult.error || 'myTischtennis-Login fehlgeschlagen';
// Verwende den Status-Code vom myTischtennisClient, falls vorhanden, sonst 401
const statusCode = loginResult.status && loginResult.status >= 400 && loginResult.status < 600
? loginResult.status
: 401;
console.error('[myTischtennisService.verifyLogin] Login failed:', errorMessage, `(Status: ${statusCode})`);
// Wenn CAPTCHA erforderlich ist, verwende 400 statt 401
const statusCode = loginResult.requiresCaptcha
? 400
: (loginResult.status && loginResult.status >= 400 && loginResult.status < 600
? loginResult.status
: 401);
console.error('[myTischtennisService.verifyLogin] Login failed:', errorMessage, `(Status: ${statusCode})`, loginResult.requiresCaptcha ? '(CAPTCHA erforderlich)' : '');
if (loginResult.requiresCaptcha) {
throw new HttpError({ code: 'ERROR_MYTISCHTENNIS_CAPTCHA_REQUIRED', params: { message: errorMessage } }, statusCode);
}
throw new HttpError(errorMessage, statusCode);
}
}

View File

@@ -21,95 +21,43 @@ class SchedulerService {
devLog('Starting scheduler service...');
// Schedule automatic rating updates at 6:00 AM daily
// HINWEIS: Automatische MyTischtennis-Abrufe wurden deaktiviert
// Die folgenden Jobs werden nicht mehr ausgeführt:
// - Rating Updates (6:00 AM)
// - Match Results Fetch (6:30 AM)
// Erstelle Dummy-Jobs, damit getStatus() weiterhin funktioniert
const ratingUpdateJob = cron.schedule('0 6 * * *', async () => {
const startTime = Date.now();
devLog(`[${new Date().toISOString()}] CRON: Executing scheduled rating updates...`);
let errorMessage = null;
try {
// Let the service return details including counts if available
const result = await autoUpdateRatingsService.executeAutomaticUpdates();
const executionTime = Date.now() - startTime;
// result may include updatedCount or a summary object
const messageObj = result && typeof result === 'object' ? result : { message: 'Rating updates completed successfully' };
// Log to ApiLog with rich details
await apiLogService.logSchedulerExecution('rating_updates', true, messageObj, executionTime, null);
devLog('Scheduled rating updates completed successfully');
} catch (error) {
const executionTime = Date.now() - startTime;
errorMessage = error.message;
console.error(`[${new Date().toISOString()}] CRON ERROR in scheduled rating updates:`, error);
console.error('Stack trace:', error.stack);
// Log to ApiLog
await apiLogService.logSchedulerExecution('rating_updates', false, { message: 'Rating updates failed' }, executionTime, errorMessage);
}
devLog('[DISABLED] Rating updates job would run here (deactivated)');
}, {
scheduled: false, // Don't start automatically
scheduled: false,
timezone: 'Europe/Berlin'
});
this.jobs.set('ratingUpdates', ratingUpdateJob);
ratingUpdateJob.start();
devLog('Rating update job scheduled and started');
// Schedule automatic match results fetching at 6:30 AM daily
const matchResultsJob = cron.schedule('30 6 * * *', async () => {
const startTime = Date.now();
devLog(`[${new Date().toISOString()}] CRON: Executing scheduled match results fetch...`);
let errorMessage = null;
try {
// Execute and capture returned summary (should include counts)
const result = await autoFetchMatchResultsService.executeAutomaticFetch();
const executionTime = Date.now() - startTime;
const messageObj = result && typeof result === 'object' ? result : { message: 'Match results fetch completed successfully' };
// Log to ApiLog with rich details (including counts if present)
await apiLogService.logSchedulerExecution('match_results', true, messageObj, executionTime, null);
devLog('Scheduled match results fetch completed successfully');
} catch (error) {
const executionTime = Date.now() - startTime;
errorMessage = error.message;
console.error(`[${new Date().toISOString()}] CRON ERROR in scheduled match results fetch:`, error);
console.error('Stack trace:', error.stack);
// Log to ApiLog
await apiLogService.logSchedulerExecution('match_results', false, { message: 'Match results fetch failed' }, executionTime, errorMessage);
}
devLog('[DISABLED] Match results fetch job would run here (deactivated)');
}, {
scheduled: false, // Don't start automatically
scheduled: false,
timezone: 'Europe/Berlin'
});
// Jobs werden NICHT gestartet (deaktiviert)
this.jobs.set('ratingUpdates', ratingUpdateJob);
this.jobs.set('matchResults', matchResultsJob);
matchResultsJob.start();
devLog('Match results fetch job scheduled and started');
devLog('MyTischtennis automatic fetch jobs are DISABLED');
this.isRunning = true;
const now = new Date();
const tomorrow6AM = new Date(now);
tomorrow6AM.setDate(tomorrow6AM.getDate() + 1);
tomorrow6AM.setHours(6, 0, 0, 0);
const tomorrow630AM = new Date(now);
tomorrow630AM.setDate(tomorrow630AM.getDate() + 1);
tomorrow630AM.setHours(6, 30, 0, 0);
devLog('[Scheduler] ===== SCHEDULER SERVICE STARTED =====');
devLog(`[Scheduler] Server time: ${now.toISOString()}`);
devLog(`[Scheduler] Timezone: Europe/Berlin`);
devLog(`[Scheduler] Rating updates: Next execution at ${tomorrow6AM.toISOString()} (6:00 AM Berlin time)`);
devLog(`[Scheduler] Match results fetch: Next execution at ${tomorrow630AM.toISOString()} (6:30 AM Berlin time)`);
devLog(`[Scheduler] MyTischtennis automatic fetch jobs: DISABLED`);
devLog('[Scheduler] =====================================');
devLog('Scheduler service started successfully');
devLog('Rating updates scheduled for 6:00 AM daily (Europe/Berlin timezone)');
devLog('Match results fetch scheduled for 6:30 AM daily (Europe/Berlin timezone)');
devLog('MyTischtennis automatic fetch jobs are DISABLED');
}
/**
@@ -146,30 +94,26 @@ class SchedulerService {
/**
* Manually trigger rating updates (for testing)
* HINWEIS: Deaktiviert - automatische MyTischtennis-Abrufe sind nicht mehr verfügbar
*/
async triggerRatingUpdates() {
devLog('Manually triggering rating updates...');
try {
await autoUpdateRatingsService.executeAutomaticUpdates();
return { success: true, message: 'Rating updates completed successfully' };
} catch (error) {
console.error('Error in manual rating updates:', error);
return { success: false, message: error.message };
}
devLog('[DISABLED] Manual rating updates trigger called (deactivated)');
return {
success: false,
message: 'Automatische MyTischtennis-Abrufe wurden deaktiviert'
};
}
/**
* Manually trigger match results fetch (for testing)
* HINWEIS: Deaktiviert - automatische MyTischtennis-Abrufe sind nicht mehr verfügbar
*/
async triggerMatchResultsFetch() {
devLog('Manually triggering match results fetch...');
try {
await autoFetchMatchResultsService.executeAutomaticFetch();
return { success: true, message: 'Match results fetch completed successfully' };
} catch (error) {
console.error('Error in manual match results fetch:', error);
return { success: false, message: error.message };
}
devLog('[DISABLED] Manual match results fetch trigger called (deactivated)');
return {
success: false,
message: 'Automatische MyTischtennis-Abrufe wurden deaktiviert'
};
}
/**