Enhance MyTischtennisUrlController with ratings update and improve apiLogService truncation limits

Added functionality in MyTischtennisUrlController to update (Q)TTR ratings for clubs based on user authentication. Enhanced error handling for ratings updates to provide clearer feedback. Updated apiLogService to increase truncation limits for request and response bodies, accommodating larger API JSON payloads, ensuring better logging accuracy.
This commit is contained in:
Torsten Schulz (local)
2025-11-03 12:03:34 +01:00
parent 23708b99b5
commit 84ff4e126e
3 changed files with 105 additions and 19 deletions

View File

@@ -1,5 +1,6 @@
import myTischtennisUrlParserService from '../services/myTischtennisUrlParserService.js';
import myTischtennisService from '../services/myTischtennisService.js';
import MemberService from '../services/memberService.js';
import autoFetchMatchResultsService from '../services/autoFetchMatchResultsService.js';
import apiLogService from '../services/apiLogService.js';
import ClubTeam from '../models/ClubTeam.js';
@@ -383,13 +384,24 @@ class MyTischtennisUrlController {
// Don't fail the entire request if table update fails
}
// Additionally update (Q)TTR ratings for the club
let ratingsUpdate = null;
try {
// Use already resolved userId instead of authcode to avoid header dependency
const ratingsResult = await MemberService.updateRatingsFromMyTischtennisByUserId(userId, team.clubId);
ratingsUpdate = ratingsResult?.response?.message || `Ratings update status: ${ratingsResult?.status}`;
} catch (ratingsErr) {
ratingsUpdate = 'Ratings update failed: ' + (ratingsErr.message || String(ratingsErr));
}
res.json({
success: true,
message: `${result.fetchedCount} Datensätze abgerufen und verarbeitet`,
data: {
fetchedCount: result.fetchedCount,
teamName: team.name,
tableUpdate: tableUpdateResult
tableUpdate: tableUpdateResult,
ratingsUpdate
}
});
} catch (error) {

View File

@@ -22,8 +22,8 @@ class ApiLogService {
schedulerJobType = null
} = options;
// Truncate long fields
const truncate = (str, maxLen = 10000) => {
// Truncate long fields (raise limits to fit typical API JSON bodies)
const truncate = (str, maxLen = 64000) => {
if (!str) return null;
const strVal = typeof str === 'string' ? str : JSON.stringify(str);
return strVal.length > maxLen ? strVal.substring(0, maxLen) + '... (truncated)' : strVal;
@@ -34,8 +34,8 @@ class ApiLogService {
method,
path,
statusCode,
requestBody: truncate(requestBody),
responseBody: truncate(responseBody),
requestBody: truncate(requestBody, 64000),
responseBody: truncate(responseBody, 64000),
executionTime,
errorMessage: truncate(errorMessage, 5000),
ipAddress,

View File

@@ -1,6 +1,5 @@
import UserClub from "../models/UserClub.js";
import { checkAccess } from "../utils/userUtils.js";
import { getUserByToken } from "../utils/userUtils.js";
import { checkAccess, getUserByToken, hasUserClubAccess } from "../utils/userUtils.js";
import Member from "../models/Member.js";
import path from 'path';
import fs from 'fs';
@@ -141,26 +140,21 @@ class MemberService {
}
}
async updateRatingsFromMyTischtennis(userToken, clubId) {
await checkAccess(userToken, clubId);
const user = await getUserByToken(userToken);
async _updateRatingsInternal(userId, clubId) {
const myTischtennisService = (await import('./myTischtennisService.js')).default;
const myTischtennisClient = (await import('../clients/myTischtennisClient.js')).default;
try {
// 1. myTischtennis-Session abrufen oder Login durchführen
let session;
try {
session = await myTischtennisService.getSession(user.id);
session = await myTischtennisService.getSession(userId);
} catch (sessionError) {
console.log('[updateRatingsFromMyTischtennis] - Session invalid, attempting login...', sessionError.message);
// Versuche automatischen Login mit gespeicherten Credentials
try {
await myTischtennisService.verifyLogin(user.id);
const freshSession = await myTischtennisService.getSession(user.id);
await myTischtennisService.verifyLogin(userId);
const freshSession = await myTischtennisService.getSession(userId);
session = {
cookie: freshSession.cookie,
accessToken: freshSession.accessToken,
@@ -183,7 +177,7 @@ class MemberService {
}
}
const account = await myTischtennisService.getAccount(user.id);
const account = await myTischtennisService.getAccount(userId);
if (!account) {
console.error('[updateRatingsFromMyTischtennis] - No account found!');
@@ -193,7 +187,7 @@ class MemberService {
message: 'Kein myTischtennis-Account gefunden.',
updated: 0,
errors: [],
debug: { userId: user.id }
debug: { userId }
}
};
}
@@ -219,22 +213,80 @@ class MemberService {
};
}
// 2. Ranglisten vom Verein abrufen
// 2. Ranglisten vom Verein abrufen (Logging hinzufügen)
// TTR (aktuell)
try {
await (await import('./apiLogService.js')).default.logRequest({
userId,
method: 'GET',
path: `/rankings/andro-rangliste?club=${account.clubId}&fed=${account.fedNickname}&current=yes`,
statusCode: null,
requestBody: null,
responseBody: null,
executionTime: null,
errorMessage: 'TTR-Rangliste wird abgerufen...',
logType: 'api_request',
schedulerJobType: 'mytischtennis_rankings'
});
} catch {}
const ttrStart = Date.now();
const rankingsCurrent = await myTischtennisClient.getClubRankings(
session.cookie,
account.clubId,
account.fedNickname,
'yes'
);
try {
await (await import('./apiLogService.js')).default.logRequest({
userId,
method: 'GET',
path: `/rankings/andro-rangliste?club=${account.clubId}&fed=${account.fedNickname}&current=yes`,
statusCode: rankingsCurrent.success ? 200 : 500,
requestBody: null,
responseBody: JSON.stringify(rankingsCurrent),
executionTime: Date.now() - ttrStart,
errorMessage: rankingsCurrent.success ? null : 'TTR-Rangliste Fehler',
logType: 'api_request',
schedulerJobType: 'mytischtennis_rankings'
});
} catch {}
// QTTR (Quartalswert)
try {
await (await import('./apiLogService.js')).default.logRequest({
userId,
method: 'GET',
path: `/rankings/andro-rangliste?club=${account.clubId}&fed=${account.fedNickname}&current=no`,
statusCode: null,
requestBody: null,
responseBody: null,
executionTime: null,
errorMessage: 'QTTR-Rangliste wird abgerufen...',
logType: 'api_request',
schedulerJobType: 'mytischtennis_rankings'
});
} catch {}
const qttrStart = Date.now();
const rankingsQuarter = await myTischtennisClient.getClubRankings(
session.cookie,
account.clubId,
account.fedNickname,
'no'
);
try {
await (await import('./apiLogService.js')).default.logRequest({
userId,
method: 'GET',
path: `/rankings/andro-rangliste?club=${account.clubId}&fed=${account.fedNickname}&current=no`,
statusCode: rankingsQuarter.success ? 200 : 500,
requestBody: null,
responseBody: JSON.stringify(rankingsQuarter),
executionTime: Date.now() - qttrStart,
errorMessage: rankingsQuarter.success ? null : 'QTTR-Rangliste Fehler',
logType: 'api_request',
schedulerJobType: 'mytischtennis_rankings'
});
} catch {}
if (!rankingsCurrent.success) {
return {
@@ -367,6 +419,28 @@ class MemberService {
}
}
async updateRatingsFromMyTischtennis(userToken, clubId) {
await checkAccess(userToken, clubId);
const user = await getUserByToken(userToken);
return this._updateRatingsInternal(user.id, clubId);
}
async updateRatingsFromMyTischtennisByUserId(userId, clubId) {
// Zugriff prüfen: User muss Zugriff auf den Club haben
const allowed = await hasUserClubAccess(userId, clubId);
if (!allowed) {
return {
status: 403,
response: {
message: 'noaccess',
updated: 0,
errors: ['noaccess']
}
};
}
return this._updateRatingsInternal(userId, clubId);
}
async rotateMemberImage(userToken, clubId, memberId, direction) {
try {
await checkAccess(userToken, clubId);