Viel Push und achivements + AGB

This commit is contained in:
2025-09-16 23:41:34 +02:00
parent b2fc63e2d0
commit 5831d1bb91
8 changed files with 934 additions and 49 deletions

View File

@@ -1045,7 +1045,7 @@ const { checkNameAgainstBlacklist, addToBlacklist, removeFromBlacklist, getBlack
// Create new player with RFID and blacklist validation (no auth required for dashboard)
router.post('/v1/public/players/create-with-rfid', async (req, res) => {
const { rfiduid, firstname, lastname, birthdate, supabase_user_id } = req.body;
const { rfiduid, firstname, lastname, birthdate, supabase_user_id, agb_accepted } = req.body;
// Validierung
if (!rfiduid || !firstname || !lastname || !birthdate) {
@@ -1119,10 +1119,10 @@ router.post('/v1/public/players/create-with-rfid', async (req, res) => {
// Spieler in Datenbank einfügen
const result = await pool.query(
`INSERT INTO players (rfiduid, firstname, lastname, birthdate, supabase_user_id, created_at, show_in_leaderboard)
VALUES ($1, $2, $3, $4, $5, $6, $7)
RETURNING id, rfiduid, firstname, lastname, birthdate, created_at`,
[rfiduid, firstname, lastname, birthdate, supabase_user_id || null, new Date(), true]
`INSERT INTO players (rfiduid, firstname, lastname, birthdate, supabase_user_id, created_at, show_in_leaderboard, agb_accepted)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
RETURNING id, rfiduid, firstname, lastname, birthdate, created_at, agb_accepted`,
[rfiduid, firstname, lastname, birthdate, supabase_user_id || null, new Date(), !!agb_accepted, !!agb_accepted]
);
const newPlayer = result.rows[0];
@@ -1824,6 +1824,12 @@ router.delete('/v1/admin/players/:id', requireAdminAuth, async (req, res) => {
try {
// Erst alle zugehörigen Zeiten löschen
await pool.query('DELETE FROM times WHERE player_id = $1', [playerId]);
// Alle zugehörigen Notifications löschen
await pool.query('DELETE FROM sent_notifications WHERE player_id = $1', [playerId]);
// Alle zugehörigen Player Achievements löschen
await pool.query('DELETE FROM player_achievements WHERE player_id = $1', [playerId]);
// Dann den Spieler löschen
const result = await pool.query('DELETE FROM players WHERE id = $1', [playerId]);
@@ -2212,22 +2218,17 @@ router.get('/v1/public/times-with-details', async (req, res) => {
// Get all times with player and location details, ordered by time (fastest first)
// Only show times from players who have opted into leaderboard visibility
// SECURITY: Only return data needed for leaderboard display
const result = await pool.query(`
SELECT
t.id,
EXTRACT(EPOCH FROM t.recorded_time) as recorded_time_seconds,
t.created_at,
json_build_object(
'id', p.id,
'firstname', p.firstname,
'lastname', p.lastname,
'rfiduid', p.rfiduid
'lastname', p.lastname
) as player,
json_build_object(
'id', l.id,
'name', l.name,
'latitude', l.latitude,
'longitude', l.longitude
'name', l.name
) as location
FROM times t
LEFT JOIN players p ON t.player_id = p.id
@@ -2843,6 +2844,23 @@ router.get('/v1/public/best-times', async (req, res) => {
}
});
// ==================== LOGGING ENDPOINT ====================
// Log endpoint for client-side logging
router.post('/v1/public/log', async (req, res) => {
try {
const { level, message, playerId } = req.body;
const timestamp = new Date().toISOString();
console.log(`[${timestamp}] [${level?.toUpperCase() || 'INFO'}] [Player: ${playerId || 'unknown'}] ${message}`);
res.json({ success: true });
} catch (error) {
console.error('Error processing log:', error);
res.status(500).json({ success: false });
}
});
// ==================== PUSH NOTIFICATION ENDPOINTS ====================
// Subscribe to push notifications
@@ -3026,6 +3044,29 @@ router.get('/v1/public/notification-sent/:playerId/:type', async (req, res) => {
const { playerId, type } = req.params;
const { achievementId, locationId } = req.query;
// Validate required parameters
if (!playerId || playerId === 'undefined' || playerId === 'null') {
return res.status(400).json({
success: false,
message: 'Player ID ist erforderlich'
});
}
if (!type) {
return res.status(400).json({
success: false,
message: 'Notification Type ist erforderlich'
});
}
// Validate achievementId if provided
if (achievementId && (achievementId === 'undefined' || achievementId === 'null')) {
return res.status(400).json({
success: false,
message: 'Ungültige Achievement ID'
});
}
const today = new Date().toISOString().split('T')[0];
const result = await pool.query(`
@@ -3058,6 +3099,19 @@ router.post('/v1/public/notification-sent', async (req, res) => {
try {
const { playerId, notificationType, achievementId, locationId } = req.body;
// Log notification details to server console
const logData = {
playerId: playerId,
notificationType: notificationType,
timestamp: new Date().toISOString()
};
// Only add achievementId and locationId if they exist
if (achievementId) logData.achievementId = achievementId;
if (locationId) logData.locationId = locationId;
console.log('📱 Push Notification marked as sent:', logData);
await pool.query(`
INSERT INTO sent_notifications (player_id, notification_type, achievement_id, location_id)
VALUES ($1, $2, $3, $4)