updated README
All checks were successful
CI Pipeline / lint-and-test (pull_request) Successful in 1m14s
All checks were successful
CI Pipeline / lint-and-test (pull_request) Successful in 1m14s
This commit is contained in:
parent
5b3f29a98d
commit
a44a68c963
560
README.md
560
README.md
|
|
@ -1,146 +1,297 @@
|
|||
# Kidio Backend
|
||||
|
||||
Das Kidio-Backend ist eine gamifizierte Lösung zur Verwaltung von Bildschirmzeit für Kinder. Die Anwendung basiert auf dem **Ktor**-Framework in Kotlin und kombiniert **PostgreSQL** für die Persistenz mit **Redis** als Caching-Layer für schnelle Zugriffszeiten.
|
||||
Das **Kidio-Backend** ist eine gamifizierte Lösung zur Verwaltung von Bildschirmzeit für Kinder.
|
||||
|
||||
Kinder können durch das Lösen von Lernaufgaben (z. B. Mathe-Aufgaben) zusätzliche Bildschirmzeit erwerben, während das System ihre verbrauchte Bildschirmzeit synchronisiert und bei Ablauf sperrt.
|
||||
Die Anwendung basiert auf dem **Ktor-Framework in Kotlin** und kombiniert **PostgreSQL** für die persistente Datenhaltung mit **Redis** als Caching-Layer für schnelle Zugriffszeiten.
|
||||
|
||||
Kinder können durch das Lösen von Lernaufgaben, beispielsweise Mathematikaufgaben, zusätzliche Bildschirmzeit erwerben. Gleichzeitig synchronisiert das System die verbrauchte Bildschirmzeit und sperrt das Gerät automatisch, sobald keine verfügbare Bildschirmzeit mehr vorhanden ist.
|
||||
|
||||
---
|
||||
|
||||
## Features
|
||||
|
||||
Hier ist eine Übersicht der implementierten Kernfunktionen des Kidio-Backends:
|
||||
|
||||
| Name | Beschreibung |
|
||||
|------|-------------|
|
||||
| **JWT Authentifizierung** | Sichere API-Endpunkte für authentifizierte Benutzer über JSON Web Tokens (JWT). |
|
||||
| **Lernaufgaben (Tasks)** | Abrufen von interaktiven Lernaufgaben mit vordefinierten Antwortmöglichkeiten und Belohnungen (z. B. Minuten). |
|
||||
| Feature | Beschreibung |
|
||||
| :--------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **JWT-Authentifizierung** | Sichere API-Endpunkte für authentifizierte Benutzer über JSON Web Tokens (JWT). |
|
||||
| **Lernaufgaben (Tasks)** | Abrufen von interaktiven Lernaufgaben mit vordefinierten Antwortmöglichkeiten und Belohnungen in Minuten. |
|
||||
| **Antwort-Verifizierung** | Überprüfung der vom Kind ausgewählten Antworten. Bei korrekter Antwort wird die Belohnung direkt auf die verbleibende Bildschirmzeit aufgerechnet. |
|
||||
| **Bildschirmzeit-Synchronisation** | Laufender Datenabgleich der genutzten Bildschirmzeit. Sinkt die Zeit auf oder unter Null, wird der Status automatisch auf `isBlocked = true` gesetzt. |
|
||||
| **Zwei-Ebenen-Speicher** | Datenhaltung in **PostgreSQL** (via JetBrains Exposed ORM) kombiniert mit **Redis-Caching** (via Jedis) für optimale Performance bei der Screentime-Abfrage. |
|
||||
| **Fallback-Mechanismus** | Ist die Redis-Instanz nicht erreichbar, fällt das System automatisch und transparent auf PostgreSQL zurück. |
|
||||
| **Bildschirmzeit-Synchronisation** | Laufender Datenabgleich der verbrauchten Bildschirmzeit. Sinkt die verbleibende Zeit auf oder unter null, wird der Status automatisch auf `isBlocked = true` gesetzt. |
|
||||
| **Zwei-Ebenen-Speicher** | PostgreSQL als persistente Datenbank, kombiniert mit Redis-Caching für schnelle Screentime-Abfragen. |
|
||||
| **Fallback-Mechanismus** | Ist Redis nicht erreichbar, fällt das System automatisch und transparent auf PostgreSQL zurück. |
|
||||
|
||||
---
|
||||
|
||||
## Technologiestack
|
||||
|
||||
- **Framework**: [Ktor](https://ktor.io/) (Kotlin-basiertes asynchrones Web-Framework)
|
||||
- **Datenbank-ORM**: [JetBrains Exposed](https://github.com/JetBrains/Exposed)
|
||||
- **Persistenz**: PostgreSQL
|
||||
- **Caching**: Redis (via Jedis Client)
|
||||
- **Sicherheit & Auth**: JWT (Java JWT von auth0)
|
||||
- **Serialization**: Kotlinx Serialization (JSON)
|
||||
- **Testing**: Ktor Server Testing, JUnit
|
||||
* **Framework:** Ktor
|
||||
* **Programmiersprache:** Kotlin
|
||||
* **Datenbank-ORM:** JetBrains Exposed
|
||||
* **Persistenz:** PostgreSQL
|
||||
* **Caching:** Redis via Jedis
|
||||
* **Authentifizierung:** JWT via Java JWT (Auth0)
|
||||
* **Serialisierung:** Kotlinx Serialization
|
||||
* **Code Quality / Linter:** KtLint
|
||||
* **Testing:** Ktor Server Testing, JUnit
|
||||
* **CI/CD:** Forgejo Actions (Gitea / Act Runner)
|
||||
|
||||
---
|
||||
|
||||
## API-Endpunkte
|
||||
|
||||
Alle Endpunkte befinden sich unter dem Präfix `/api/v1`.
|
||||
Alle API-Endpunkte befinden sich unter dem Präfix:
|
||||
|
||||
```text
|
||||
/api/v1
|
||||
```
|
||||
|
||||
### Öffentliche Endpunkte
|
||||
|
||||
#### 1. Health-Check
|
||||
- **Methode**: `GET`
|
||||
- **Pfad**: `/api/v1/health`
|
||||
- **Beschreibung**: Prüft, ob der Backend-Server ordnungsgemäß läuft.
|
||||
- **Antwort**:
|
||||
```json
|
||||
{
|
||||
|
||||
**Methode:** `GET`
|
||||
|
||||
**Pfad:**
|
||||
|
||||
```text
|
||||
/api/v1/health
|
||||
```
|
||||
|
||||
**Beschreibung:**
|
||||
|
||||
Prüft, ob der Backend-Server ordnungsgemäß läuft.
|
||||
|
||||
**Antwort:**
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "OK",
|
||||
"message": "Kidio Backend läuft!"
|
||||
}
|
||||
```
|
||||
}
|
||||
```
|
||||
|
||||
#### 2. Benutzer-Login (Auth-Token abholen)
|
||||
- **Methode**: `POST`
|
||||
- **Pfad**: `/api/v1/auth/login`
|
||||
- **Request-Body**:
|
||||
```json
|
||||
{
|
||||
"userId": "default_user"
|
||||
}
|
||||
```
|
||||
- **Antwort**:
|
||||
```json
|
||||
{
|
||||
"token": "eyJhbGciOiJIUzI1NiIsIn..."
|
||||
}
|
||||
```
|
||||
---
|
||||
|
||||
### Geschützte Endpunkte (Erfordern JWT im `Authorization: Bearer <Token>` Header)
|
||||
#### 2. Benutzer-Registrierung
|
||||
|
||||
#### 3. Alle verfügbaren Aufgaben abrufen
|
||||
- **Methode**: `GET`
|
||||
- **Pfad**: `/api/v1/tasks`
|
||||
- **Beschreibung**: Liefert eine Liste aller verfügbaren Lernaufgaben.
|
||||
- **Antwort**:
|
||||
```json
|
||||
[
|
||||
**Methode:** `POST`
|
||||
|
||||
**Pfad:**
|
||||
|
||||
```text
|
||||
/api/v1/auth/register
|
||||
```
|
||||
|
||||
**Request-Body:**
|
||||
|
||||
```json
|
||||
{
|
||||
"email": "test@family.de",
|
||||
"passwordPlain": "strengGeheim1",
|
||||
"familyName": "Familie Schmidt",
|
||||
"childAge": 8,
|
||||
"parentPin": "4321"
|
||||
}
|
||||
```
|
||||
|
||||
**Antwort – `201 Created`:**
|
||||
|
||||
```json
|
||||
{
|
||||
"token": "eyJhbGciOiJIUzI1NiIsIn...",
|
||||
"familyName": "Familie Schmidt"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### 3. Benutzer-Login
|
||||
|
||||
**Methode:** `POST`
|
||||
|
||||
**Pfad:**
|
||||
|
||||
```text
|
||||
/api/v1/auth/login
|
||||
```
|
||||
|
||||
**Request-Body:**
|
||||
|
||||
```json
|
||||
{
|
||||
"email": "test@family.de",
|
||||
"passwordPlain": "strengGeheim1"
|
||||
}
|
||||
```
|
||||
|
||||
**Antwort – `200 OK`:**
|
||||
|
||||
```json
|
||||
{
|
||||
"token": "eyJhbGciOiJIUzI1NiIsIn...",
|
||||
"familyName": "Familie Schmidt"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Geschützte Endpunkte
|
||||
|
||||
Die folgenden Endpunkte erfordern ein gültiges JWT im HTTP-Header:
|
||||
|
||||
```http
|
||||
Authorization: Bearer <Token>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### 4. Bildschirmzeit abrufen
|
||||
|
||||
**Methode:** `GET`
|
||||
|
||||
**Pfad:**
|
||||
|
||||
```text
|
||||
/api/v1/screentime
|
||||
```
|
||||
|
||||
**Beschreibung:**
|
||||
|
||||
Liefert die aktuell verbleibende Bildschirmzeit sowie den aktuellen Sperrstatus.
|
||||
|
||||
**Antwort:**
|
||||
|
||||
```json
|
||||
{
|
||||
"remainingSeconds": 1800,
|
||||
"isBlocked": false
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### 5. Bildschirmzeit synchronisieren
|
||||
|
||||
**Methode:** `POST`
|
||||
|
||||
**Pfad:**
|
||||
|
||||
```text
|
||||
/api/v1/screentime/sync
|
||||
```
|
||||
|
||||
**Beschreibung:**
|
||||
|
||||
Zieht die seit dem letzten Abgleich verbrauchten Sekunden von der verfügbaren Bildschirmzeit ab.
|
||||
|
||||
**Request-Body:**
|
||||
|
||||
```json
|
||||
{
|
||||
"userId": "default_user",
|
||||
"usedSecondsSinceLastSync": 200
|
||||
}
|
||||
```
|
||||
|
||||
**Antwort:**
|
||||
|
||||
```json
|
||||
{
|
||||
"remainingSeconds": 1600,
|
||||
"isBlocked": false
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### 6. Alle verfügbaren Aufgaben abrufen
|
||||
|
||||
**Methode:** `GET`
|
||||
|
||||
**Pfad:**
|
||||
|
||||
```text
|
||||
/api/v1/tasks
|
||||
```
|
||||
|
||||
**Beschreibung:**
|
||||
|
||||
Liefert eine Liste aller verfügbaren Lernaufgaben.
|
||||
|
||||
**Antwort:**
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": "t1",
|
||||
"question": "Was ist 12 + 15?",
|
||||
"options": ["25", "27", "30", "22"],
|
||||
"options": [
|
||||
"25",
|
||||
"27",
|
||||
"30",
|
||||
"22"
|
||||
],
|
||||
"rewardMinutes": 10
|
||||
}
|
||||
]
|
||||
```
|
||||
]
|
||||
```
|
||||
|
||||
#### 4. Antwort überprüfen & Belohnung verbuchen
|
||||
- **Methode**: `POST`
|
||||
- **Pfad**: `/api/v1/tasks/verify`
|
||||
- **Request-Body**:
|
||||
```json
|
||||
{
|
||||
---
|
||||
|
||||
#### 7. Antwort überprüfen und Belohnung verbuchen
|
||||
|
||||
**Methode:** `POST`
|
||||
|
||||
**Pfad:**
|
||||
|
||||
```text
|
||||
/api/v1/tasks/verify
|
||||
```
|
||||
|
||||
**Request-Body:**
|
||||
|
||||
```json
|
||||
{
|
||||
"taskId": "t1",
|
||||
"selectedAnswer": "27"
|
||||
}
|
||||
```
|
||||
- **Antwort (Erfolg)**:
|
||||
```json
|
||||
{
|
||||
}
|
||||
```
|
||||
|
||||
**Antwort – korrekte Lösung:**
|
||||
|
||||
```json
|
||||
{
|
||||
"isCorrect": true,
|
||||
"earnedMinutes": 10,
|
||||
"message": "Super gemacht! Du hast 10 Minuten gewonnen."
|
||||
}
|
||||
```
|
||||
- **Antwort (Fehler)**:
|
||||
```json
|
||||
{
|
||||
}
|
||||
```
|
||||
|
||||
**Antwort – falsche Lösung:**
|
||||
|
||||
```json
|
||||
{
|
||||
"isCorrect": false,
|
||||
"earnedMinutes": 0,
|
||||
"message": "Schade, das war leider falsch. Versuche es nochmal!"
|
||||
}
|
||||
```
|
||||
|
||||
#### 5. Bildschirmzeit synchronisieren
|
||||
- **Methode**: `POST`
|
||||
- **Pfad**: `/api/v1/screentime/sync`
|
||||
- **Beschreibung**: Zieht die seit dem letzten Abgleich verbrauchten Sekunden von der verfügbaren Bildschirmzeit ab.
|
||||
- **Request-Body**:
|
||||
```json
|
||||
{
|
||||
"userId": "default_user",
|
||||
"usedSecondsSinceLastSync": 200
|
||||
}
|
||||
```
|
||||
- **Antwort**:
|
||||
```json
|
||||
{
|
||||
"remainingSeconds": 1600,
|
||||
"isBlocked": false
|
||||
}
|
||||
```
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Projektkonfiguration und Umgebungsvariablen
|
||||
|
||||
Das Projekt wird über die Datei `src/main/resources/application.yaml` konfiguriert. Folgende Umgebungsvariablen können zur flexiblen Docker- oder Deployment-Konfiguration übergeben werden:
|
||||
Die Anwendung wird über folgende Konfigurationsdatei konfiguriert:
|
||||
|
||||
```text
|
||||
src/main/resources/application.yaml
|
||||
```
|
||||
|
||||
Für eine flexible Konfiguration in lokalen Entwicklungsumgebungen, Docker-Containern oder Deployments können folgende Umgebungsvariablen gesetzt werden:
|
||||
|
||||
| Variable | Beschreibung | Standardwert |
|
||||
|----------|--------------|--------------|
|
||||
| `DB_HOST` | Hostname der PostgreSQL-Datenbank | `localhost` / aus `application.yaml` |
|
||||
| `DB_PORT` | Port der PostgreSQL-Datenbank | `5432` / aus `application.yaml` |
|
||||
| :------------ | :----------------------------------------- | :------------------------------- |
|
||||
| `DB_HOST` | Hostname der PostgreSQL-Datenbank | `localhost` |
|
||||
| `DB_PORT` | Port der PostgreSQL-Datenbank | `5432` |
|
||||
| `DB_NAME` | Name der PostgreSQL-Datenbank | `kidio_db` |
|
||||
| `DB_USER` | PostgreSQL-Benutzername | `kidio_admin` |
|
||||
| `DB_PASSWORD` | PostgreSQL-Passwort | `learning` |
|
||||
|
|
@ -148,24 +299,221 @@ Das Projekt wird über die Datei `src/main/resources/application.yaml` konfiguri
|
|||
| `REDIS_PORT` | Port der Redis-Instanz | `6379` |
|
||||
| `JWT_SECRET` | Geheimer Schlüssel zur Signierung der JWTs | `super-geheimes-secret-fuer-dev` |
|
||||
|
||||
> **Hinweis:** Die angegebenen Standardwerte sind für die lokale Entwicklung gedacht. Für produktive Umgebungen sollten insbesondere `DB_PASSWORD` und `JWT_SECRET` über sichere Secrets bzw. Umgebungsvariablen gesetzt werden.
|
||||
|
||||
---
|
||||
|
||||
## Lokale Entwicklung, Bauen & Testen
|
||||
## Lokale Entwicklung
|
||||
|
||||
Zur Ausführung des Projekts werden die folgenden Gradle-Tasks bereitgestellt:
|
||||
### Voraussetzungen
|
||||
|
||||
| Task | Beschreibung |
|
||||
|------|-------------|
|
||||
| `./gradlew test` | Führt alle automatisierten Unit- und Integrationstests aus (nutzt die InMemory-Repository-Implementierung). |
|
||||
| `./gradlew build` | Kompiliert das Projekt und baut das Artefakt. |
|
||||
| `./gradlew run` | Startet den Ktor-Entwicklungsserver lokal auf Port `8080`. |
|
||||
Für die lokale Entwicklung werden benötigt:
|
||||
|
||||
* JDK / Java
|
||||
* Kotlin bzw. Gradle Wrapper
|
||||
* PostgreSQL
|
||||
* Redis
|
||||
|
||||
Die konkreten Versionen richten sich nach der im Projekt konfigurierten Gradle- und Kotlin-Version.
|
||||
|
||||
---
|
||||
|
||||
## Bauen und Ausführen
|
||||
|
||||
Das Projekt verwendet den Gradle Wrapper, sodass Gradle nicht global installiert sein muss.
|
||||
|
||||
### Anwendung starten
|
||||
|
||||
```bash
|
||||
# Tests ausführen
|
||||
./gradlew test
|
||||
|
||||
# Server starten
|
||||
./gradlew run
|
||||
```
|
||||
|
||||
Sobald der Server erfolgreich gestartet ist, ist er unter `http://localhost:8080` erreichbar.
|
||||
Der Ktor-Server ist anschließend standardmäßig unter folgender Adresse erreichbar:
|
||||
|
||||
```text
|
||||
http://localhost:8080
|
||||
```
|
||||
|
||||
### Projekt bauen
|
||||
|
||||
```bash
|
||||
./gradlew build
|
||||
```
|
||||
|
||||
### Tests ausführen
|
||||
|
||||
```bash
|
||||
./gradlew test
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Code Quality
|
||||
|
||||
Für die Einhaltung der Kotlin-Coding-Standards wird **KtLint** verwendet.
|
||||
|
||||
### Code automatisch formatieren
|
||||
|
||||
```bash
|
||||
./gradlew ktlintFormat
|
||||
```
|
||||
|
||||
### Code Style prüfen
|
||||
|
||||
```bash
|
||||
./gradlew ktlintCheck
|
||||
```
|
||||
|
||||
Der KtLint-Check wird ebenfalls innerhalb der CI-Pipeline ausgeführt.
|
||||
|
||||
---
|
||||
|
||||
## Empfohlener Workflow vor Commits / Pull Requests
|
||||
|
||||
Vor einem Commit oder Pull Request sollte der folgende Workflow ausgeführt werden:
|
||||
|
||||
```bash
|
||||
# 1. Kotlin-Code automatisch formatieren
|
||||
./gradlew ktlintFormat
|
||||
|
||||
# 2. Code Style überprüfen
|
||||
./gradlew ktlintCheck
|
||||
|
||||
# 3. Tests ausführen
|
||||
./gradlew test
|
||||
|
||||
# 4. Projekt bauen
|
||||
./gradlew build
|
||||
|
||||
# 5. Server lokal starten
|
||||
./gradlew run
|
||||
```
|
||||
|
||||
Der Server ist anschließend unter:
|
||||
|
||||
```text
|
||||
http://localhost:8080
|
||||
```
|
||||
|
||||
erreichbar.
|
||||
|
||||
---
|
||||
|
||||
## CI/CD Pipeline
|
||||
|
||||
Das Repository verwendet **Forgejo Actions** für Continuous Integration.
|
||||
|
||||
Bei jedem Push oder Pull Request auf den `main`-Branch wird automatisch ein Workflow ausgeführt.
|
||||
|
||||
Die Pipeline führt unter anderem folgende Prüfungen durch:
|
||||
|
||||
1. **Code Style**
|
||||
|
||||
```bash
|
||||
./gradlew ktlintCheck
|
||||
```
|
||||
|
||||
2. **Automatisierte Tests**
|
||||
|
||||
```bash
|
||||
./gradlew test
|
||||
```
|
||||
|
||||
Dadurch wird sichergestellt, dass Änderungen den bestehenden Code-Style einhalten und die automatisierten Tests weiterhin erfolgreich ausgeführt werden können.
|
||||
|
||||
---
|
||||
|
||||
## Architektur
|
||||
|
||||
Das Backend besteht im Wesentlichen aus folgenden Komponenten:
|
||||
|
||||
```text
|
||||
┌───────────────────┐
|
||||
│ Client │
|
||||
│ Web / Mobile / │
|
||||
│ Device │
|
||||
└─────────┬─────────┘
|
||||
│
|
||||
│ HTTP / JSON
|
||||
▼
|
||||
┌───────────────────┐
|
||||
│ Ktor │
|
||||
│ REST API │
|
||||
└─────────┬─────────┘
|
||||
│
|
||||
┌────────────┴────────────┐
|
||||
│ │
|
||||
▼ ▼
|
||||
┌─────────────────┐ ┌─────────────────┐
|
||||
│ Redis │ │ PostgreSQL │
|
||||
│ Cache │ │ Persistence │
|
||||
└────────┬────────┘ └────────┬────────┘
|
||||
│ │
|
||||
└────────────┬────────────┘
|
||||
│
|
||||
▼
|
||||
┌───────────────────┐
|
||||
│ Exposed ORM │
|
||||
└───────────────────┘
|
||||
```
|
||||
|
||||
### Redis-Fallback
|
||||
|
||||
Redis wird für schnelle Zugriffe auf häufig benötigte Daten, insbesondere die verbleibende Bildschirmzeit, verwendet.
|
||||
|
||||
Sollte Redis nicht erreichbar sein, greift das Backend automatisch auf PostgreSQL zurück.
|
||||
|
||||
Dadurch bleibt die Anwendung auch bei einem Ausfall des Caches funktionsfähig.
|
||||
|
||||
---
|
||||
|
||||
## Bildschirmzeit-Logik
|
||||
|
||||
Die Bildschirmzeit wird intern in Sekunden verwaltet.
|
||||
|
||||
Beispielsweise:
|
||||
|
||||
```text
|
||||
Start:
|
||||
1800 Sekunden
|
||||
= 30 Minuten
|
||||
|
||||
Verbrauch:
|
||||
200 Sekunden
|
||||
|
||||
Verbleibend:
|
||||
1600 Sekunden
|
||||
= 26 Minuten 40 Sekunden
|
||||
```
|
||||
|
||||
Sobald die verbleibende Bildschirmzeit auf `0` oder darunter fällt, wird der Benutzer automatisch gesperrt:
|
||||
|
||||
```json
|
||||
{
|
||||
"remainingSeconds": 0,
|
||||
"isBlocked": true
|
||||
}
|
||||
```
|
||||
|
||||
Durch das Lösen einer Aufgabe kann zusätzliche Bildschirmzeit erworben werden.
|
||||
|
||||
Beispiel:
|
||||
|
||||
```text
|
||||
Verbleibend: 0 Sekunden
|
||||
Aufgaben-Belohnung: 10 Minuten
|
||||
↓
|
||||
Neue Bildschirmzeit: 600 Sekunden
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Lizenz
|
||||
|
||||
Falls für das Projekt eine bestimmte Lizenz verwendet wird, sollte diese hier ergänzt werden, beispielsweise:
|
||||
|
||||
```text
|
||||
MIT License
|
||||
```
|
||||
|
||||
Eine entsprechende `LICENSE`-Datei sollte sich im Root-Verzeichnis des Repositorys befinden.
|
||||
|
|
|
|||
Loading…
Reference in a new issue