Initial commit: TimeClock v3 - Node.js/Vue.js Zeiterfassung

Features:
- Backend: Node.js/Express mit MySQL/MariaDB
- Frontend: Vue.js 3 mit Composition API
- UTC-Zeithandling für korrekte Zeiterfassung
- Timewish-basierte Überstundenberechnung
- Wochenübersicht mit Urlaubs-/Krankheits-/Feiertagshandling
- Bereinigtes Arbeitsende (Generell/Woche)
- Überstunden-Offset für historische Daten
- Fixed Layout mit scrollbarem Content
- Kompakte UI mit grünem Theme
This commit is contained in:
Torsten Schulz (local)
2025-10-17 14:11:28 +02:00
commit e95bb4cb76
86 changed files with 19530 additions and 0 deletions

110
backend/src/index.js Normal file
View File

@@ -0,0 +1,110 @@
const express = require('express');
const cors = require('cors');
const helmet = require('helmet');
const morgan = require('morgan');
const session = require('express-session');
const passport = require('passport');
require('dotenv').config();
const database = require('./config/database');
const PassportConfig = require('./config/passport');
const unhashRequestIds = require('./middleware/unhashRequest');
const hashResponseIds = require('./middleware/hashResponse');
const app = express();
const PORT = process.env.PORT || 3010;
// Middleware
app.use(helmet());
app.use(cors({
origin: process.env.FRONTEND_URL || 'http://localhost:5010',
credentials: true
}));
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(morgan('dev'));
// Session für Passport (OAuth)
app.use(session({
secret: process.env.SESSION_SECRET || 'session-secret-change-in-production',
resave: false,
saveUninitialized: false,
cookie: {
secure: process.env.NODE_ENV === 'production',
maxAge: 24 * 60 * 60 * 1000 // 24 Stunden
}
}));
// Passport initialisieren
app.use(passport.initialize());
app.use(passport.session());
PassportConfig.initialize();
// Routes
app.get('/api/health', (req, res) => {
res.json({
status: 'ok',
message: 'TimeClock API v3.0.0',
timestamp: new Date().toISOString()
});
});
// Auth routes (öffentlich) - OHNE ID-Hashing
const authRouter = require('./routes/auth');
app.use('/api/auth', authRouter);
// ID-Hashing Middleware (nur für geschützte Routes)
app.use(unhashRequestIds);
app.use(hashResponseIds);
// Time entries routes (geschützt) - MIT ID-Hashing
const timeEntriesRouter = require('./routes/timeEntries');
const { authenticateToken } = require('./middleware/auth');
app.use('/api/time-entries', authenticateToken, timeEntriesRouter);
// Week overview routes (geschützt) - MIT ID-Hashing
const weekOverviewRouter = require('./routes/weekOverview');
app.use('/api/week-overview', authenticateToken, weekOverviewRouter);
// Error handling middleware
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({
error: 'Etwas ist schiefgelaufen!',
message: process.env.NODE_ENV === 'development' ? err.message : undefined
});
});
// 404 handler
app.use((req, res) => {
res.status(404).json({ error: 'Route nicht gefunden' });
});
// Datenbank initialisieren und Server starten
database.initialize()
.then(() => {
app.listen(PORT, () => {
console.log(`🕐 TimeClock Server läuft auf Port ${PORT}`);
console.log(`📍 API verfügbar unter http://localhost:${PORT}/api`);
});
})
.catch(error => {
console.error('❌ Server konnte nicht gestartet werden:', error.message);
process.exit(1);
});
// Graceful Shutdown
process.on('SIGTERM', async () => {
console.log('SIGTERM empfangen, fahre Server herunter...');
await database.close();
process.exit(0);
});
process.on('SIGINT', async () => {
console.log('\nSIGINT empfangen, fahre Server herunter...');
await database.close();
process.exit(0);
});
module.exports = app;