Compare commits

..

No commits in common. "main" and "feat/add-new-endpoint-for-sreentime" have entirely different histories.

308 changed files with 2608 additions and 8364 deletions

View file

@ -2,7 +2,7 @@ name: CI Pipeline
on: on:
push: push:
branches: [ "main", "feat/*" ] branches: [ "main" ]
pull_request: pull_request:
branches: [ "main" ] branches: [ "main" ]
@ -12,10 +12,10 @@ jobs:
steps: steps:
- name: Code auschecken - name: Code auschecken
uses: https://github.com/actions/checkout@v4 uses: https://github.com/actions/checkout@v4 # <-- Vollständige URL oder github/ Prefix
- name: Java 21 einrichten - name: Java 21 einrichten
uses: https://github.com/actions/setup-java@v4 uses: https://github.com/actions/setup-java@v4 # <-- Vollständige URL oder github/ Prefix
with: with:
distribution: 'temurin' distribution: 'temurin'
java-version: '21' java-version: '21'
@ -24,10 +24,10 @@ jobs:
run: chmod +x gradlew run: chmod +x gradlew
- name: 1. Code-Style (Linter) prüfen - name: 1. Code-Style (Linter) prüfen
run: ./gradlew ktlintCheck --no-daemon run: ./gradlew ktlintCheck
- name: 2. Tests ausführen - name: 2. Tests ausführen
run: ./gradlew test --no-daemon run: ./gradlew test
- name: 3. Backend bauen - name: 3. Backend bauen
run: ./gradlew build -x test --max-workers=2 --no-daemon -Dorg.gradle.jvmargs="-Xmx1024m" run: ./gradlew build -x test --max-workers=2 -Dorg.gradle.jvmargs="-Xmx1024m"

1
.gitignore vendored
View file

@ -11,4 +11,3 @@ build/
*.iws *.iws
*.ipr *.ipr
out/ out/
build/

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

Binary file not shown.

View file

@ -0,0 +1,2 @@
#Wed Sep 02 15:03:29 CEST 2026
gradle.version=9.5.1

Binary file not shown.

BIN
.gradle/file-system.probe Normal file

Binary file not shown.

View file

10
.idea/.gitignore vendored Normal file
View file

@ -0,0 +1,10 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Ignored default folder with query files
/queries/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

6
.idea/compiler.xml Normal file
View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="21" />
</component>
</project>

12
.idea/dataSources.xml Normal file
View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
<data-source source="LOCAL" name="kidio_db@82.165.11.162" uuid="c8ad70de-d5fc-407a-9165-4f24328fa311">
<driver-ref>postgresql</driver-ref>
<synchronize>true</synchronize>
<jdbc-driver>org.postgresql.Driver</jdbc-driver>
<jdbc-url>jdbc:postgresql://82.165.11.162:5432/kidio_db</jdbc-url>
<working-dir>$ProjectFileDir$</working-dir>
</data-source>
</component>
</project>

16
.idea/gradle.xml Normal file
View file

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
</set>
</option>
</GradleProjectSettings>
</option>
</component>
</project>

7
.idea/kotlinc.xml Normal file
View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="KotlinJpsPluginSettings">
<option name="externalSystemId" value="Gradle" />
<option name="version" value="2.4.0" />
</component>
</project>

7
.idea/misc.xml Normal file
View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" project-jdk-name="homebrew-26" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

6
.idea/vcs.xml Normal file
View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

9
Kidio_backend.iml Normal file
View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

118
README.md
View file

@ -13,9 +13,8 @@ Kinder können durch das Lösen von Lernaufgaben, beispielsweise Mathematikaufga
| Feature | Beschreibung | | Feature | Beschreibung |
| :--------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | :--------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **JWT-Authentifizierung** | Sichere API-Endpunkte für authentifizierte Benutzer über JSON Web Tokens (JWT). | | **JWT-Authentifizierung** | Sichere API-Endpunkte für authentifizierte Benutzer über JSON Web Tokens (JWT). |
| **Lernaufgaben (Tasks)** | Abrufen von interaktiven Lernaufgaben mit vordefinierten Antwortmöglichkeiten, Filterung nach Fach und Alter sowie Belohnungen in Minuten. | | **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. | | **Antwort-Verifizierung** | Überprüfung der vom Kind ausgewählten Antworten. Bei korrekter Antwort wird die Belohnung direkt auf die verbleibende Bildschirmzeit aufgerechnet. |
| **OER / H5P Ingestion Pipeline** | Automatisierter Live-Crawler und Batch-Import von freien Bildungsinhalten (ZUM.de / H5P) inklusive HTML-Bereinigung und Lizenzattributierung (CC-BY). |
| **Bildschirmzeit-Synchronisation** | Laufender Datenabgleich der verbrauchten Bildschirmzeit. Sinkt die verbleibende Zeit auf oder unter null, wird der Status automatisch auf `isBlocked = true` gesetzt. | | **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. | | **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. | | **Fallback-Mechanismus** | Ist Redis nicht erreichbar, fällt das System automatisch und transparent auf PostgreSQL zurück. |
@ -99,8 +98,7 @@ Prüft, ob der Backend-Server ordnungsgemäß läuft.
```json ```json
{ {
"token": "eyJhbGciOiJIUzI1NiIsIn...", "token": "eyJhbGciOiJIUzI1NiIsIn...",
"userId": "6380776f-88d3-4e9f-a2be-0d9cacae447e", "familyName": "Familie Schmidt"
"message": "Registrierung erfolgreich"
} }
``` ```
@ -130,9 +128,7 @@ Prüft, ob der Backend-Server ordnungsgemäß läuft.
```json ```json
{ {
"token": "eyJhbGciOiJIUzI1NiIsIn...", "token": "eyJhbGciOiJIUzI1NiIsIn...",
"userId": "6380776f-88d3-4e9f-a2be-0d9cacae447e", "familyName": "Familie Schmidt"
"familyName": "Familie Schmidt",
"childAge": 8
} }
``` ```
@ -160,9 +156,9 @@ Authorization: Bearer <Token>
**Beschreibung:** **Beschreibung:**
Liefert die aktuell verbleibende Bildschirmzeit sowie den aktuellen Sperrstatus des authentifizierten Benutzers. Liefert die aktuell verbleibende Bildschirmzeit sowie den aktuellen Sperrstatus.
**Antwort – `200 OK`:** **Antwort:**
```json ```json
{ {
@ -185,28 +181,29 @@ Liefert die aktuell verbleibende Bildschirmzeit sowie den aktuellen Sperrstatus
**Beschreibung:** **Beschreibung:**
Zieht die seit dem letzten Abgleich vom Endgerät verbrauchten Sekunden von der verfügbaren Bildschirmzeit ab. Zieht die seit dem letzten Abgleich verbrauchten Sekunden von der verfügbaren Bildschirmzeit ab.
**Request-Body:** **Request-Body:**
```json ```json
{ {
"usedSecondsSinceLastSync": 30 "userId": "default_user",
"usedSecondsSinceLastSync": 200
} }
``` ```
**Antwort – `200 OK`:** **Antwort:**
```json ```json
{ {
"remainingSeconds": 1770, "remainingSeconds": 1600,
"isBlocked": false "isBlocked": false
} }
``` ```
--- ---
#### 6. Lernaufgaben abrufen (mit Fach- & Altersfilter) #### 6. Alle verfügbaren Aufgaben abrufen
**Methode:** `GET` **Methode:** `GET`
@ -216,42 +213,24 @@ Zieht die seit dem letzten Abgleich vom Endgerät verbrauchten Sekunden von der
/api/v1/tasks /api/v1/tasks
``` ```
**Optionale Query-Parameter:**
| Parameter | Typ | Standardwert | Beschreibung |
| :--- | :--- | :--- | :--- |
| `subject` | String | `null` | Schulfach (z. B. `Mathematik`, `Deutsch`, `Sachunterricht`, `Englisch`, `Biologie`, `Geographie`, `Geschichte`). |
| `age` | Integer | `null` | Zielalter des Kindes (filtert nach `minAge <= age AND maxAge >= age`). |
| `limit` | Integer | `10` | Maximale Anzahl an zurückgegebenen Aufgaben. |
**Beispiel-Anfrage:**
```text
GET /api/v1/tasks?subject=Mathematik&age=8&limit=5
```
**Beschreibung:** **Beschreibung:**
Liefert kuratierte oder aus OER/H5P importierte Lernaufgaben. Die korrekte Lösung (`correctAnswer`) wird zum Schutz vor Manipulationen serverseitig geheim gehalten und niemals an den Client übertragen. Liefert eine Liste aller verfügbaren Lernaufgaben.
**Antwort – `200 OK`:** **Antwort:**
```json ```json
[ [
{ {
"id": "zum-math-001", "id": "t1",
"subject": "Mathematik", "question": "Was ist 12 + 15?",
"minAge": 8,
"maxAge": 9,
"question": "Was ist das Ergebnis von 6 × 8?",
"options": [ "options": [
"42", "25",
"48", "27",
"54", "30",
"56" "22"
], ],
"rewardMinutes": 5, "rewardMinutes": 10
"license": "CC BY-SA 4.0"
} }
] ]
``` ```
@ -272,22 +251,22 @@ Liefert kuratierte oder aus OER/H5P importierte Lernaufgaben. Die korrekte Lösu
```json ```json
{ {
"taskId": "zum-math-001", "taskId": "t1",
"selectedAnswer": "48" "selectedAnswer": "27"
} }
``` ```
**Antwort – korrekte Lösung (`200 OK`):** **Antwort – korrekte Lösung:**
```json ```json
{ {
"isCorrect": true, "isCorrect": true,
"earnedMinutes": 5, "earnedMinutes": 10,
"message": "Super gemacht! Du hast 5 Minuten gewonnen." "message": "Super gemacht! Du hast 10 Minuten gewonnen."
} }
``` ```
**Antwort – falsche Lösung (`200 OK`):** **Antwort – falsche Lösung:**
```json ```json
{ {
@ -299,38 +278,6 @@ Liefert kuratierte oder aus OER/H5P importierte Lernaufgaben. Die korrekte Lösu
--- ---
## H5P / ZUM.de Aufgaben-Import (Standalone CLI)
Das Backend verfügt über ein integriertes CLI-Tool zum automatisierten Importieren und Crawlen interaktiver Lernaufgaben von **ZUM.de / OER (H5P)**.
### Batch-Import ausführen
```bash
# Alle Fächer importieren (z. B. 100 Aufgaben)
./gradlew runIngestion --args="--limit=100"
# Gezielt ein Schulfach importieren (z. B. Mathematik)
./gradlew runIngestion --args="--subject=Mathematik --limit=50"
# Deutsch für Grundschüler importieren
./gradlew runIngestion --args="--subject=Deutsch --limit=30"
```
### CLI-Optionen
| Argument | Standardwert | Beschreibung |
| :--- | :--- | :--- |
| `--subject=<Name>` | *(alle Fächer)* | Filtert Aufgaben nach Fach (z. B. `Mathematik`, `Deutsch`, `Englisch`, `Sachunterricht`). |
| `--limit=<Zahl>` | `50` | Maximale Anzahl an Aufgaben, die im Durchlauf importiert/aktualisiert werden. |
### Funktionsweise der Ingestion-Pipeline
1. **Live-Crawler & Discovery:** Durchsucht `apps.zum.de` nach interaktiven Inhalten (`H5P.MultiChoice`, `H5P.TrueFalse`, `H5P.QuestionSet`).
2. **Textbereinigung:** Entfernt störende HTML-Tags (`<p>`, `<strong>`, `&nbsp;`) für native mobile Darstellung.
3. **Taxonomie- & Alters-Mapping:** Weist Altersgrenzen (`minAge`, `maxAge`) basierend auf Klassenstufen automatisch zu.
4. **Idempotenter Upsert:** Speichert Fragen, Optionen, geheime Lösungen und CC-Lizenzen direkt relational in PostgreSQL (`TasksTable`). Duplikate werden vermieden.
---
## Projektkonfiguration und Umgebungsvariablen ## Projektkonfiguration und Umgebungsvariablen
Die Anwendung wird über folgende Konfigurationsdatei konfiguriert: Die Anwendung wird über folgende Konfigurationsdatei konfiguriert:
@ -570,16 +517,3 @@ MIT License
``` ```
Eine entsprechende `LICENSE`-Datei sollte sich im Root-Verzeichnis des Repositorys befinden. Eine entsprechende `LICENSE`-Datei sollte sich im Root-Verzeichnis des Repositorys befinden.
---
## Kidio Ökosystem
Für Kinder (Der spielerische Teil - "Kid"):
* Kidio Learn: Interaktive Lernspiele (Mathe, Sprachen, Logik), die sich dem Alter und Lernfortschritt des Kindes anpassen.
* Kidio Create: Tools und Plattformen, um eigene Ideen, Kunstwerke oder kleine Projekte zu entwickeln und zu teilen.
* Kidio Media: Ein sicherer Bereich mit kindgerechten Videos, Hörspielen und Büchern (ähnlich wie YouTube Kids).
![img.png](img.png)

View file

@ -11,12 +11,6 @@ version = "1.0.0-SNAPSHOT"
application { application {
mainClass = "io.ktor.server.netty.EngineMain" mainClass = "io.ktor.server.netty.EngineMain"
} }
ktor {
openApi {
enabled = true // Aktiviert die OpenAPI-Code-Generierung
codeInferenceEnabled = true
}
}
kotlin { kotlin {
jvmToolchain(21) jvmToolchain(21)
@ -26,7 +20,7 @@ dependencies {
implementation(ktorLibs.server.config.yaml) implementation(ktorLibs.server.config.yaml)
implementation(ktorLibs.server.core) implementation(ktorLibs.server.core)
implementation(ktorLibs.server.netty) implementation(ktorLibs.server.netty)
implementation("io.ktor:ktor-server-openapi")
// JWT Authentication // JWT Authentication
implementation("io.ktor:ktor-server-auth") implementation("io.ktor:ktor-server-auth")
implementation("io.ktor:ktor-server-auth-jwt") implementation("io.ktor:ktor-server-auth-jwt")
@ -52,11 +46,3 @@ dependencies {
// redis // redis
implementation("redis.clients:jedis:5.1.2") implementation("redis.clients:jedis:5.1.2")
} }
tasks.register<JavaExec>("runIngestion") {
group = "application"
description = "Runs the standalone H5P/ZUM task ingestion CLI tool"
mainClass.set("com.oliver.kidio.backend.ingestion.H5PIngestionCli")
classpath = sourceSets["main"].runtimeClasspath
standardInput = System.`in`
}

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show more