# Datenbank-Integration

## Übersicht

TimeClock v3 verwendet MySQL mit der bestehenden `stechuhr2` Datenbankstruktur. Die Integration erfolgt über das Repository-Pattern für saubere Abstraktion.

## Architektur

```
Controller
    ↓
Service (Business-Logik)
    ↓
Repository (Datenbankzugriff)
    ↓
MySQL Datenbank
```

## Verwendete Tabellen

### Haupttabellen

#### `worklog` - Zeiteinträge
```sql
CREATE TABLE `worklog` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `version` int NOT NULL,
  `user_id` bigint NOT NULL,
  `state` text NOT NULL,          -- JSON mit {action, project, description}
  `tstamp` datetime DEFAULT NULL,
  `relatedTo_id` bigint DEFAULT NULL,  -- Verknüpfung Clock In/Out
  PRIMARY KEY (`id`)
)
```

**Verwendung:**
- Clock In: Neuer Eintrag mit `relatedTo_id = NULL`
- Clock Out: Neuer Eintrag mit `relatedTo_id` verweist auf Clock In
- State: JSON-Format `{"action": "Clock In", "project": "Projektname", "description": "Beschreibung"}`

#### `user` - Benutzer
```sql
CREATE TABLE `user` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `full_name` text NOT NULL,
  `daily_hours` int NOT NULL,
  `week_hours` int NOT NULL,
  ...
)
```

#### `weekly_worktime` - Wochenarbeitszeit
```sql
CREATE TABLE `weekly_worktime` (
  `id` int NOT NULL AUTO_INCREMENT,
  `weekly_work_time` double NOT NULL DEFAULT '40',
  `starting_from` date DEFAULT NULL,
  `ends_at` date DEFAULT NULL,
  `user_id` int DEFAULT NULL,
  ...
)
```

## Konfiguration

### Umgebungsvariablen (`.env`)

```env
# Datenbank-Verbindung
DB_HOST=localhost
DB_PORT=3306
DB_USER=root
DB_PASSWORD=your_password
DB_NAME=stechuhr2
```

### Connection Pool

Die Datenbankverbindung wird über einen Connection Pool verwaltet:

```javascript
// src/config/database.js
const pool = mysql.createPool({
  host: process.env.DB_HOST,
  port: process.env.DB_PORT,
  user: process.env.DB_USER,
  password: process.env.DB_PASSWORD,
  database: process.env.DB_NAME,
  connectionLimit: 10
});
```

## Repository-Pattern

### WorklogRepository

**Verantwortlichkeiten:**
- CRUD-Operationen auf `worklog` Tabelle
- Komplexe Queries (Paare, Statistiken)
- Datenbankzugriff abstrahieren

**Hauptmethoden:**
```javascript
await worklogRepository.findAllByUser(userId)
await worklogRepository.findById(id)
await worklogRepository.create(worklogData)
await worklogRepository.update(id, updateData)
await worklogRepository.delete(id)
await worklogRepository.findPairsByUser(userId)
await worklogRepository.getStatistics(userId)
```

### UserRepository

**Verantwortlichkeiten:**
- Benutzerverwaltung
- Wochenarbeitszeit abrufen
- Benutzereinstellungen

**Hauptmethoden:**
```javascript
await userRepository.findById(userId)
await userRepository.findByEmail(email)
await userRepository.getWeeklyWorktime(userId, date)
await userRepository.getUserSetting(userId, settingId)
```

## Datenfluss

### Timer starten

```
1. Frontend → POST /api/time-entries
   { project: "Website", description: "Feature XY" }

2. Controller → Service.createEntry()

3. Service → WorklogRepository.create()
   {
     user_id: 1,
     state: '{"action":"Clock In","project":"Website","description":"Feature XY"}',
     tstamp: '2025-10-15 10:00:00',
     relatedTo_id: null
   }

4. MySQL → INSERT INTO worklog
   → Gibt neue ID zurück (z.B. 123)

5. Service → Konvertiert zu TimeEntry-Format
   {
     id: 123,
     startTime: '2025-10-15T10:00:00',
     endTime: null,
     project: 'Website',
     description: 'Feature XY',
     duration: null,
     isRunning: true
   }

6. Controller → Frontend
   Status 201, JSON Response
```

### Timer stoppen

```
1. Frontend → PUT /api/time-entries/123
   { endTime: '2025-10-15T12:00:00' }

2. Service → WorklogRepository.create()
   {
     user_id: 1,
     state: '{"action":"Clock Out","project":"Website","description":"Feature XY"}',
     tstamp: '2025-10-15 12:00:00',
     relatedTo_id: 123  // Verweist auf Clock In
   }

3. Service → Berechnet Dauer und gibt Pair zurück
```

## Worklog-Paare

Clock In und Clock Out werden als separate Einträge gespeichert, aber logisch als Paar behandelt:

```sql
SELECT 
  w1.id as start_id,
  w1.tstamp as start_time,
  w2.id as end_id,
  w2.tstamp as end_time,
  TIMESTAMPDIFF(SECOND, w1.tstamp, w2.tstamp) as duration
FROM worklog w1
LEFT JOIN worklog w2 ON w2.relatedTo_id = w1.id
WHERE w1.user_id = ? AND w1.relatedTo_id IS NULL
```

**Vorteile:**
- Historische Genauigkeit
- Korrekturen möglich
- Audit-Trail

## State-Format

Der `state` Feld speichert JSON-Daten:

```json
{
  "action": "Clock In|Clock Out",
  "project": "Projektname",
  "description": "Beschreibung der Tätigkeit"
}
```

**Parsing im Service:**
```javascript
let stateData;
try {
  stateData = JSON.parse(worklog.state);
} catch {
  stateData = { project: 'Allgemein', description: '' };
}
```

## Statistiken

### Basis-Statistiken

```sql
SELECT 
  COUNT(DISTINCT w1.id) as total_entries,
  COUNT(DISTINCT w2.id) as completed_entries,
  SUM(TIMESTAMPDIFF(SECOND, w1.tstamp, w2.tstamp)) as total_seconds
FROM worklog w1
LEFT JOIN worklog w2 ON w2.relatedTo_id = w1.id
WHERE w1.user_id = ? AND w1.relatedTo_id IS NULL
```

### Projekt-Statistiken

Werden im Service berechnet durch Gruppierung der Paare nach Projekt.

## Transaktionen

Für zukünftige Erweiterungen können Transaktionen verwendet werden:

```javascript
const connection = await database.getPool().getConnection();
try {
  await connection.beginTransaction();
  
  // Multiple Operationen
  await connection.execute(sql1, params1);
  await connection.execute(sql2, params2);
  
  await connection.commit();
} catch (error) {
  await connection.rollback();
  throw error;
} finally {
  connection.release();
}
```

## Migration von In-Memory

Der Code unterstützt beide Modi:
- **Entwicklung:** In-Memory (schnell, keine DB erforderlich)
- **Produktion:** MySQL (persistent, skalierbar)

Umschalten durch Änderung des Service-Imports.

## Performance-Optimierungen

### Indizes

Wichtige Indizes sind bereits vorhanden:
```sql
KEY `worklog_tstamp_IDX` (`tstamp`)
KEY `worklog_user_id_IDX` (`user_id`, `tstamp`)
```

### Connection Pooling

- Wiederverwendung von Verbindungen
- Limit: 10 gleichzeitige Verbindungen
- Automatisches Reconnect

### Query-Optimierung

- Prepared Statements (SQL Injection Schutz)
- WHERE-Klauseln mit Indizes
- LEFT JOIN statt mehrere Queries

## Fehlerbehandlung

```javascript
try {
  const result = await worklogRepository.create(data);
  return result;
} catch (error) {
  if (error.code === 'ER_DUP_ENTRY') {
    throw new Error('Eintrag existiert bereits');
  }
  if (error.code === 'ER_NO_REFERENCED_ROW_2') {
    throw new Error('Benutzer nicht gefunden');
  }
  throw error;
}
```

## Backup & Restore

### Backup erstellen
```bash
mysqldump -u root -p stechuhr2 > backup_$(date +%Y%m%d).sql
```

### Restore
```bash
mysql -u root -p stechuhr2 < backup_20251015.sql
```

## Sicherheit

✅ **Implemented:**
- Prepared Statements (SQL Injection Schutz)
- Connection Pooling
- Error Handling

🔄 **TODO:**
- Input Validation
- Rate Limiting
- User Authentication
- Row-Level Security

## Monitoring

Logging von Queries (Development):
```javascript
if (process.env.NODE_ENV === 'development') {
  console.log('SQL:', sql);
  console.log('Params:', params);
}
```

## Zukünftige Erweiterungen

1. **Authentifizierung**
   - Verwendung der `auth_info` Tabelle
   - JWT-Token-basiert

2. **Multi-User Support**
   - Benutzer-ID aus Session
   - Row-Level Security

3. **Erweiterte Features**
   - Urlaub (`vacation` Tabelle)
   - Krankmeldungen (`sick` Tabelle)
   - Feiertage (`holiday` Tabelle)

4. **Caching**
   - Redis für häufige Abfragen
   - Cache-Invalidierung

5. **Read Replicas**
   - Master-Slave Setup
   - Read-Write Splitting



