import express from 'express'; import path from 'path'; import { fileURLToPath } from 'url'; import sequelize from './database.js'; import cors from 'cors'; import { User, Log, Club, UserClub, Member, DiaryDate, Participant, Activity, MemberNote, DiaryNote, DiaryTag, MemberDiaryTag, DiaryDateTag, DiaryMemberNote, DiaryMemberTag, PredefinedActivity, PredefinedActivityImage, DiaryDateActivity, DiaryMemberActivity, Match, League, Team, Group, GroupActivity, Tournament, TournamentGroup, TournamentMatch, TournamentResult, TournamentMember, Accident, UserToken, OfficialTournament, OfficialCompetition } from './models/index.js'; import authRoutes from './routes/authRoutes.js'; import clubRoutes from './routes/clubRoutes.js'; import diaryRoutes from './routes/diaryRoutes.js'; import memberRoutes from './routes/memberRoutes.js'; import participantRoutes from './routes/participantRoutes.js'; import activityRoutes from './routes/activityRoutes.js'; import memberNoteRoutes from './routes/memberNoteRoutes.js'; import diaryTagRoutes from './routes/diaryTagRoutes.js'; import diaryNoteRoutes from './routes/diaryNoteRoutes.js'; import diaryMemberRoutes from './routes/diaryMemberRoutes.js'; import predefinedActivityRoutes from './routes/predefinedActivityRoutes.js'; import diaryDateActivityRoutes from './routes/diaryDateActivityRoutes.js'; import diaryMemberActivityRoutes from './routes/diaryMemberActivityRoutes.js'; import matchRoutes from './routes/matchRoutes.js'; import Season from './models/Season.js'; import Location from './models/Location.js'; import groupRoutes from './routes/groupRoutes.js'; import diaryDateTagRoutes from './routes/diaryDateTagRoutes.js'; import sessionRoutes from './routes/sessionRoutes.js'; import tournamentRoutes from './routes/tournamentRoutes.js'; import accidentRoutes from './routes/accidentRoutes.js'; import trainingStatsRoutes from './routes/trainingStatsRoutes.js'; import officialTournamentRoutes from './routes/officialTournamentRoutes.js'; const app = express(); const port = process.env.PORT || 3000; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); app.use(cors()); app.use(express.json()); // Globale Fehlerbehandlung, damit der Server bei unerwarteten Fehlern nicht hart abstürzt process.on('uncaughtException', (err) => { console.error('[uncaughtException]', err); }); process.on('unhandledRejection', (reason, promise) => { console.error('[unhandledRejection]', reason); }); app.use('/api/auth', authRoutes); app.use('/api/clubs', clubRoutes); app.use('/api/clubmembers', memberRoutes); app.use('/api/diary', diaryRoutes); app.use('/api/participants', participantRoutes); app.use('/api/activities', activityRoutes); app.use('/api/membernotes', memberNoteRoutes); app.use('/api/diarynotes', diaryNoteRoutes); app.use('/api/tags', diaryTagRoutes); app.use('/api/diarymember', diaryMemberRoutes); app.use('/api/predefined-activities', predefinedActivityRoutes); app.use('/api/diary-date-activities', diaryDateActivityRoutes); app.use('/api/diary-member-activities', diaryMemberActivityRoutes); app.use('/api/matches', matchRoutes); app.use('/api/group', groupRoutes); app.use('/api/diarydatetags', diaryDateTagRoutes); app.use('/api/session', sessionRoutes); app.use('/api/tournament', tournamentRoutes); app.use('/api/accident', accidentRoutes); app.use('/api/training-stats', trainingStatsRoutes); app.use('/api/official-tournaments', officialTournamentRoutes); app.use(express.static(path.join(__dirname, '../frontend/dist'))); // Catch-All Handler für Frontend-Routen (muss nach den API-Routen stehen) app.get('*', (req, res) => { res.sendFile(path.join(__dirname, '../frontend/dist/index.html')); }); (async () => { try { await sequelize.authenticate(); // Einmalige Migration: deutsche Spaltennamen -> englische const renameColumnIfExists = async (table, from, to, typeSql) => { try { const [rows] = await sequelize.query(`SHOW COLUMNS FROM \`${table}\` LIKE :col`, { replacements: { col: from } }); if (Array.isArray(rows) && rows.length > 0) { await sequelize.query(`ALTER TABLE \`${table}\` CHANGE \`${from}\` \`${to}\` ${typeSql}`); } } catch (e) { console.error(`[migration] Failed to rename ${table}.${from} -> ${to}:`, e.message); } }; // official_competitions await renameColumnIfExists('official_competitions', 'altersklasse_wettbewerb', 'age_class_competition', 'VARCHAR(255) NULL'); await renameColumnIfExists('official_competitions', 'leistungsklasse', 'performance_class', 'VARCHAR(255) NULL'); await renameColumnIfExists('official_competitions', 'startzeit', 'start_time', 'VARCHAR(255) NULL'); await renameColumnIfExists('official_competitions', 'meldeschluss_datum', 'registration_deadline_date', 'VARCHAR(255) NULL'); await renameColumnIfExists('official_competitions', 'meldeschluss_online', 'registration_deadline_online', 'VARCHAR(255) NULL'); await renameColumnIfExists('official_competitions', 'stichtag', 'cutoff_date', 'VARCHAR(255) NULL'); await renameColumnIfExists('official_competitions', 'offen_fuer', 'open_to', 'VARCHAR(255) NULL'); await renameColumnIfExists('official_competitions', 'vorrunde', 'preliminary_round', 'VARCHAR(255) NULL'); await renameColumnIfExists('official_competitions', 'endrunde', 'final_round', 'VARCHAR(255) NULL'); await renameColumnIfExists('official_competitions', 'max_teilnehmer', 'max_participants', 'VARCHAR(255) NULL'); await renameColumnIfExists('official_competitions', 'startgeld', 'entry_fee', 'VARCHAR(255) NULL'); // official_tournaments await renameColumnIfExists('official_tournaments', 'termin', 'event_date', 'VARCHAR(255) NULL'); await renameColumnIfExists('official_tournaments', 'veranstalter', 'organizer', 'VARCHAR(255) NULL'); await renameColumnIfExists('official_tournaments', 'ausrichter', 'host', 'VARCHAR(255) NULL'); await renameColumnIfExists('official_tournaments', 'austragungsorte', 'venues', 'TEXT NULL'); await renameColumnIfExists('official_tournaments', 'konkurrenztypen', 'competition_types', 'TEXT NULL'); await renameColumnIfExists('official_tournaments', 'meldeschluesse', 'registration_deadlines', 'TEXT NULL'); await User.sync(); await Club.sync({ alter: true }); await UserClub.sync({ alter: true }); await Log.sync({ alter: true }); await Member.sync({ alter: true }); await DiaryDate.sync({ alter: true }); await Participant.sync({ alter: true }); await Activity.sync({ alter: true }); await MemberNote.sync({ alter: true }); await DiaryNote.sync({ alter: true }); await DiaryTag.sync({ alter: true }); await MemberDiaryTag.sync({ alter: true }); await DiaryDateTag.sync({ alter: true }); await DiaryMemberTag.sync({ alter: true }); await DiaryMemberNote.sync({ alter: true }); await PredefinedActivity.sync({ alter: true }); await PredefinedActivityImage.sync({ alter: true }); await DiaryDateActivity.sync({ alter: true }); await DiaryMemberActivity.sync({ alter: true }); await OfficialTournament.sync({ alter: true }); await OfficialCompetition.sync({ alter: true }); await Season.sync({ alter: true }); await League.sync({ alter: true }); await Team.sync({ alter: true }); await Location.sync({ alter: true }); await Match.sync({ alter: true }); await Group.sync({ alter: true }); await GroupActivity.sync({ alter: true }); await Tournament.sync({ alter: true }); await TournamentGroup.sync({ alter: true }); await TournamentMember.sync({ alter: true }); await TournamentMatch.sync({ alter: true }); await TournamentResult.sync({ alter: true }); await Accident.sync({ alter: true }); await UserToken.sync({ alter: true }); app.listen(port, () => { console.log(`Server is running on http://localhost:${port}`); }); } catch (err) { console.error('Unable to synchronize the database:', err); } })();