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

@@ -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);