Refactor backend configuration and enhance logging in services

Updated config.js to ensure .env is loaded correctly from the backend directory. Enhanced MyTischtennisUrlController by removing unnecessary console logs and improving error handling. Updated autoFetchMatchResultsService and autoUpdateRatingsService to return detailed summaries, including counts of fetched or updated items. Improved logging in schedulerService to capture execution details, enhancing monitoring capabilities across scheduled tasks.
This commit is contained in:
Torsten Schulz (local)
2025-10-30 08:14:17 +01:00
parent 89329607dc
commit 3f2b92d886
6 changed files with 81 additions and 112 deletions

View File

@@ -1,13 +1,18 @@
import dotenv from 'dotenv';
import path from 'path';
import { fileURLToPath } from 'url';
dotenv.config();
// Ensure .env is loaded from the backend folder (not dependent on process.cwd())
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
dotenv.config({ path: path.resolve(__dirname, '.env') });
export const development = {
username: process.env.DB_USER || 'root',
password: process.env.DB_PASSWORD || 'hitomisan',
database: process.env.DB_NAME || 'trainingdiary',
host: process.env.DB_HOST,
dialect: process.env.DB_DIALECT,
host: process.env.DB_HOST || 'localhost',
dialect: process.env.DB_DIALECT || 'mysql',
define: {
freezeTableName: true,
underscored: true,

View File

@@ -54,7 +54,6 @@ class MyTischtennisUrlController {
);
}
} catch (error) {
console.error('Error fetching additional team data:', error);
// Continue with parsed data only
}
}
@@ -107,7 +106,6 @@ class MyTischtennisUrlController {
account.accessToken
);
} catch (error) {
console.error('Error fetching team data:', error);
}
}
@@ -245,67 +243,26 @@ class MyTischtennisUrlController {
}
// Get myTischtennis session (similar to memberService.updateRatingsFromMyTischtennis)
console.log('Fetching session for userId:', userId, '(from header:', userIdOrEmail, ')');
let session;
try {
session = await myTischtennisService.getSession(userId);
console.log('Session found:', !!session);
if (session) {
console.log('Session details:', {
hasCookie: !!session.cookie,
hasAccessToken: !!session.accessToken,
expiresAt: session.expiresAt ? new Date(session.expiresAt * 1000).toISOString() : null
});
}
} catch (sessionError) {
console.log('Session invalid, attempting login...', sessionError.message);
console.error('Session error details:', {
message: sessionError.message,
stack: sessionError.stack,
name: sessionError.name
});
// Versuche automatischen Login mit gespeicherten Credentials
try {
console.log('Attempting automatic login for userId:', userId);
// Check if account exists and has password
const accountCheck = await myTischtennisService.getAccount(userId);
if (!accountCheck) {
throw new Error('MyTischtennis-Account nicht gefunden');
}
console.log('Account found:', {
email: accountCheck.email,
hasPassword: !!accountCheck.encryptedPassword,
hasAccessToken: !!accountCheck.accessToken,
hasCookie: !!accountCheck.cookie
});
if (!accountCheck.encryptedPassword) {
throw new Error('Kein Passwort gespeichert. Bitte melden Sie sich in den MyTischtennis-Einstellungen an und speichern Sie Ihr Passwort.');
}
console.log('Calling verifyLogin...');
const verifyResult = await myTischtennisService.verifyLogin(userId);
console.log('verifyLogin result:', verifyResult);
await myTischtennisService.verifyLogin(userId);
session = await myTischtennisService.getSession(userId);
console.log('Automatic login successful, session:', {
hasCookie: !!session?.cookie,
hasAccessToken: !!session?.accessToken
});
} catch (loginError) {
console.error('Automatic login failed - DETAILED ERROR:', {
message: loginError.message,
stack: loginError.stack,
name: loginError.name,
userId: userId,
response: loginError.response?.data,
status: loginError.response?.status
});
const errorMessage = loginError.message || 'Automatischer Login fehlgeschlagen';
throw new HttpError(401, `MyTischtennis-Session abgelaufen und automatischer Login fehlgeschlagen: ${errorMessage}. Bitte melden Sie sich in den MyTischtennis-Einstellungen an.`);
}
@@ -318,13 +275,7 @@ class MyTischtennisUrlController {
throw new HttpError(404, 'MyTischtennis-Account nicht verknüpft. Bitte verknüpfen Sie Ihren Account in den MyTischtennis-Einstellungen.');
}
console.log('Using session:', {
email: account.email,
hasCookie: !!session.cookie,
hasAccessToken: !!session.accessToken,
expiresAt: new Date(session.expiresAt * 1000)
});
// Get team with league and season
const team = await ClubTeam.findByPk(clubTeamId, {
include: [
@@ -345,29 +296,11 @@ class MyTischtennisUrlController {
throw new HttpError(404, `Team mit ID ${clubTeamId} nicht gefunden`);
}
console.log('Team data:', {
id: team.id,
name: team.name,
myTischtennisTeamId: team.myTischtennisTeamId,
hasLeague: !!team.league,
leagueData: team.league ? {
id: team.league.id,
name: team.league.name,
myTischtennisGroupId: team.league.myTischtennisGroupId,
association: team.league.association,
groupname: team.league.groupname,
hasSeason: !!team.league.season
} : null
});
// Verbesserte Validierung mit detaillierten Fehlermeldungen
if (!team.myTischtennisTeamId) {
throw new HttpError(400, `Team "${team.name}" (interne ID: ${team.id}) ist nicht für myTischtennis konfiguriert: myTischtennisTeamId fehlt. Bitte konfigurieren Sie das Team zuerst über die MyTischtennis-URL.`);
}
// Stelle sicher, dass die myTischtennisTeamId auch wirklich verwendet wird
console.log(`Verwende myTischtennisTeamId: ${team.myTischtennisTeamId} (nicht die interne clubTeamId: ${team.id})`);
if (!team.league) {
throw new HttpError(400, 'Team ist keiner Liga zugeordnet. Bitte ordnen Sie das Team einer Liga zu.');
}
@@ -394,11 +327,6 @@ class MyTischtennisUrlController {
// Log the request to myTischtennis BEFORE making the call
// This ensures we always see what WILL BE sent, even if the call fails
const requestStartTime = Date.now();
console.log('=== ABOUT TO FETCH FROM MYTISCHTENNIS ===');
console.log('URL:', myTischtennisUrl);
console.log('myTischtennisTeamId:', team.myTischtennisTeamId);
console.log('clubTeamId:', team.id);
try {
await apiLogService.logRequest({
userId: account.userId,
@@ -423,7 +351,7 @@ class MyTischtennisUrlController {
schedulerJobType: 'mytischtennis_fetch'
});
} catch (logError) {
console.error('Error logging request (non-critical):', logError);
// Silent fail - logging errors shouldn't break the request
}
// Fetch data for this specific team
@@ -445,9 +373,7 @@ class MyTischtennisUrlController {
try {
await autoFetchMatchResultsService.fetchAndUpdateLeagueTable(account.userId, team.league.id);
tableUpdateResult = 'League table updated successfully';
console.log('✓ League table updated for league:', team.league.id);
} catch (error) {
console.error('Error fetching league table data:', error);
tableUpdateResult = 'League table update failed: ' + error.message;
// Don't fail the entire request if table update fails
}
@@ -462,8 +388,6 @@ class MyTischtennisUrlController {
}
});
} catch (error) {
console.error('Error in fetchTeamData:', error);
console.error('Error stack:', error.stack);
// Update log with error information if we got far enough to build the URL
if (typeof myTischtennisUrl !== 'undefined' && account && team) {
@@ -491,7 +415,7 @@ class MyTischtennisUrlController {
schedulerJobType: 'mytischtennis_fetch'
});
} catch (logError) {
console.error('Error logging failed request:', logError);
// Silent fail - logging errors shouldn't break the request
}
}

View File

@@ -54,14 +54,37 @@ class ApiLogService {
*/
async logSchedulerExecution(jobType, success, message, executionTime = null, errorMessage = null) {
try {
// Allow optional counts object in message by accepting message as string or object
const truncate = (str, maxLen = 10000) => {
if (!str) return null;
const strVal = typeof str === 'string' ? str : JSON.stringify(str);
return strVal.length > maxLen ? strVal.substring(0, maxLen) + '... (truncated)' : strVal;
};
// If message is an object with details, keep it; otherwise wrap into an object
let responseObj = null;
try {
if (typeof message === 'object' && message !== null) {
responseObj = message;
} else {
responseObj = { message };
}
} catch (e) {
responseObj = { message: String(message) };
}
// If executionTime or errorMessage present, add to response object for visibility
if (executionTime !== null) responseObj.executionTime = executionTime;
if (errorMessage) responseObj.errorMessage = errorMessage;
await ApiLog.create({
userId: null,
method: 'SCHEDULER',
path: `/scheduler/${jobType}`,
statusCode: success ? 200 : 500,
responseBody: message,
responseBody: truncate(JSON.stringify(responseObj)),
executionTime,
errorMessage,
errorMessage: truncate(errorMessage, 5000),
logType: 'scheduler',
schedulerJobType: jobType
});

View File

@@ -36,14 +36,20 @@ class AutoFetchMatchResultsService {
return;
}
// Process each account
// Process each account and collect summaries
const summaries = [];
for (const account of accounts) {
await this.processAccount(account);
const summary = await this.processAccount(account);
summaries.push({ userId: account.userId, ...summary });
}
devLog('Automatic match results fetch completed');
// Return overall summary including per-account counts
const totalFetched = summaries.reduce((acc, s) => acc + (s.fetchedCount || 0), 0);
return { success: true, totalFetched, summaries };
} catch (error) {
console.error('Error in automatic match results fetch:', error);
throw error;
}
}
@@ -85,13 +91,13 @@ class AutoFetchMatchResultsService {
devLog(`Successfully re-logged in for ${account.email}`);
}
// Perform match results fetch
const fetchResult = await this.fetchMatchResults(account);
fetchedCount = fetchResult.fetchedCount || 0;
// Perform match results fetch
const fetchResult = await this.fetchMatchResults(account);
fetchedCount = fetchResult.fetchedCount || 0;
success = true;
message = `Successfully fetched ${fetchedCount} match results`;
devLog(`Fetched ${fetchedCount} match results for ${account.email}`);
success = true;
message = `Successfully fetched ${fetchedCount} match results`;
devLog(`Fetched ${fetchedCount} match results for ${account.email}`);
} catch (error) {
success = false;
@@ -116,7 +122,10 @@ class AutoFetchMatchResultsService {
}
);
devLog(`Match results fetch for ${account.email}: ${success ? 'SUCCESS' : 'FAILED'} (${executionTime}ms)`);
devLog(`Match results fetch for ${account.email}: ${success ? 'SUCCESS' : 'FAILED'} (${executionTime}ms)`);
// Return a summary for scheduler
return { success, message, fetchedCount, errorDetails, executionTime };
}
/**
@@ -236,14 +245,12 @@ class AutoFetchMatchResultsService {
fetchExecutionTime = Date.now() - fetchStartTime;
responseStatus = playerStatsResponse.status;
if (playerStatsResponse.ok) {
if (playerStatsResponse.ok) {
const playerStatsData = await playerStatsResponse.json();
responseBodyData = playerStatsData;
// Log complete response for debugging
console.log('=== PLAYER STATS RESPONSE START ===');
console.log(JSON.stringify(playerStatsData, null, 2));
console.log('=== PLAYER STATS RESPONSE END ===');
// Avoid dumping full JSON to console; use devLog for compact info
devLog(`Received player stats for team ${team.name} - balancesheet entries: ${Array.isArray(playerStatsData.data?.balancesheet) ? playerStatsData.data.balancesheet.length : 0}`);
const playerCount = await this.processTeamData(team, playerStatsData);
totalProcessed += playerCount;
@@ -824,7 +831,7 @@ class AutoFetchMatchResultsService {
// Fetch table data from MyTischtennis
const tableUrl = `https://www.mytischtennis.de/click-tt/${league.association}/${seasonStr}/ligen/${league.groupname}/gruppe/${league.myTischtennisGroupId}/tabelle/gesamt?_data=routes%2Fclick-tt%2B%2F%24association%2B%2F%24season%2B%2F%24type%2B%2F%24groupname.gruppe.%24urlid%2B%2Ftabelle.%24filter`;
console.log(`[fetchAndUpdateLeagueTable] Fetching table from URL: ${tableUrl}`);
devLog(`[fetchAndUpdateLeagueTable] Fetching table from URL: ${tableUrl}`);
const response = await myTischtennisClient.authenticatedRequest(tableUrl, session.cookie, {
method: 'GET'
});

View File

@@ -29,12 +29,16 @@ class AutoUpdateRatingsService {
return;
}
// Process each account
// Process each account and collect summaries
const summaries = [];
for (const account of accounts) {
await this.processAccount(account);
const summary = await this.processAccount(account);
summaries.push({ userId: account.userId, ...summary });
}
devLog('Automatic rating updates completed');
const totalUpdated = summaries.reduce((acc, s) => acc + (s.updatedCount || 0), 0);
return { success: true, totalUpdated, summaries };
} catch (error) {
console.error('Error in automatic rating updates:', error);
}
@@ -118,6 +122,9 @@ class AutoUpdateRatingsService {
updatedCount,
executionTime
);
// Return summary for scheduler
return { success, message, updatedCount, errorDetails, executionTime };
}
/**

View File

@@ -33,14 +33,16 @@ class SchedulerService {
let errorMessage = null;
try {
await autoUpdateRatingsService.executeAutomaticUpdates();
// Let the service return details including counts if available
const result = await autoUpdateRatingsService.executeAutomaticUpdates();
const executionTime = Date.now() - startTime;
success = true;
message = 'Rating updates completed successfully';
// result may include updatedCount or a summary object
const messageObj = result && typeof result === 'object' ? result : { message: 'Rating updates completed successfully' };
console.log(`[${new Date().toISOString()}] CRON: Rating updates completed successfully`);
// Log to ApiLog
await apiLogService.logSchedulerExecution('rating_updates', true, message, executionTime, null);
// Log to ApiLog with rich details
await apiLogService.logSchedulerExecution('rating_updates', true, messageObj, executionTime, null);
} catch (error) {
const executionTime = Date.now() - startTime;
success = false;
@@ -49,7 +51,7 @@ class SchedulerService {
console.error('Stack trace:', error.stack);
// Log to ApiLog
await apiLogService.logSchedulerExecution('rating_updates', false, 'Rating updates failed', executionTime, errorMessage);
await apiLogService.logSchedulerExecution('rating_updates', false, { message: 'Rating updates failed' }, executionTime, errorMessage);
}
}, {
scheduled: false, // Don't start automatically
@@ -72,14 +74,15 @@ class SchedulerService {
let errorMessage = null;
try {
await autoFetchMatchResultsService.executeAutomaticFetch();
// Execute and capture returned summary (should include counts)
const result = await autoFetchMatchResultsService.executeAutomaticFetch();
const executionTime = Date.now() - startTime;
success = true;
message = 'Match results fetch completed successfully';
const messageObj = result && typeof result === 'object' ? result : { message: 'Match results fetch completed successfully' };
console.log(`[${new Date().toISOString()}] CRON: Match results fetch completed successfully`);
// Log to ApiLog
await apiLogService.logSchedulerExecution('match_results', true, message, executionTime, null);
// Log to ApiLog with rich details (including counts if present)
await apiLogService.logSchedulerExecution('match_results', true, messageObj, executionTime, null);
} catch (error) {
const executionTime = Date.now() - startTime;
success = false;
@@ -88,7 +91,7 @@ class SchedulerService {
console.error('Stack trace:', error.stack);
// Log to ApiLog
await apiLogService.logSchedulerExecution('match_results', false, 'Match results fetch failed', executionTime, errorMessage);
await apiLogService.logSchedulerExecution('match_results', false, { message: 'Match results fetch failed' }, executionTime, errorMessage);
}
}, {
scheduled: false, // Don't start automatically