327 Commits

Author SHA1 Message Date
Torsten Schulz (local)
b22e1923cd Fehldende messages hinzugefügt
All checks were successful
Deploy to production / deploy (push) Successful in 2m14s
2026-06-09 12:05:10 +02:00
Torsten Schulz (local)
01c2f230a9 bisaya korrigiert
All checks were successful
Deploy to production / deploy (push) Successful in 32s
2026-06-06 13:31:44 +02:00
Torsten Schulz (local)
973fcaaf9e Bisaya kurs korrekturen
All checks were successful
Deploy to production / deploy (push) Successful in 2m41s
2026-06-06 13:16:04 +02:00
Torsten Schulz (local)
bfec885a1f feat: verbessere initialTotalDue und begrenze tägliche Übungsanzahl auf MAX_DAILY_DUE
All checks were successful
Deploy to production / deploy (push) Successful in 1m59s
2026-06-04 18:52:20 +02:00
Torsten Schulz (local)
64cc360dbd feat: repariere initialTotalDue in SRS-Sitzung bei ungültigen Werten und verbessere Fehlerbehandlung
All checks were successful
Deploy to production / deploy (push) Successful in 2m1s
2026-06-04 18:39:49 +02:00
Torsten Schulz (local)
eca3ea29e5 feat: verbessere SRS-Logik mit Fallback-Mechanismus und Debug-Ausgaben für die Initialisierung
All checks were successful
Deploy to production / deploy (push) Successful in 2m0s
2026-06-04 18:31:01 +02:00
Torsten Schulz (local)
3176a8b07f feat: füge sicheres Backup des alten Frontends hinzu und verbessere Fehlerbehandlung in normalizePool
All checks were successful
Deploy to production / deploy (push) Successful in 1m59s
2026-06-04 18:20:07 +02:00
Torsten Schulz (local)
4754ae37da feat: verbessere die Pool-Aktualisierung, um SRS-Metadaten bei Verwendung eines initialen Pools abzurufen
All checks were successful
Deploy to production / deploy (push) Successful in 1m57s
2026-06-04 17:28:06 +02:00
Torsten Schulz (local)
cb7ea317a3 feat: füge Debug-Ausgaben zur Überwachung der Dialogöffnung und Pool-Aktualisierung hinzu
All checks were successful
Deploy to production / deploy (push) Successful in 2m5s
2026-06-04 17:07:38 +02:00
Torsten Schulz (local)
f0aa097b56 feat: verbessere SRS-Logik zur Abfrage kursweiter fälliger Items ohne SRS-Modus-Bedingung
All checks were successful
Deploy to production / deploy (push) Successful in 2m3s
2026-06-04 17:03:23 +02:00
Torsten Schulz (local)
58f97a5b06 chore: update package versions and improve code readability
All checks were successful
Deploy to production / deploy (push) Successful in 2m1s
- Bump 'tmp' package version from 0.2.5 to 0.2.6 in package.json
- Change version from 3.0.0-pre-alpha.0.1 to 3.0.0-beta-1.0.0 in package-lock.json
- Update various dependencies in package-lock.json:
  - 'brace-expansion' from 5.0.5 to 5.0.6
  - 'dompurify' from 3.3.3 to 3.4.8
  - 'js-cookie' from 3.0.5 to 3.0.8
  - 'lodash' from 4.17.23 to 4.18.1
- Refactor date range selection in CalendarView.vue for better readability
2026-06-04 14:20:56 +02:00
Torsten Schulz (local)
b5dcc6ea8f feat: verbessere SRS-Logik zur Bestimmung des nächsten Fälligkeitsdatums für neu erstellte Elemente und erweitere die API-Anfragen für kursweite fällige Items
All checks were successful
Deploy to production / deploy (push) Successful in 2m6s
2026-06-04 14:09:27 +02:00
Torsten Schulz (local)
4e33c3cb83 feat: aktualisiere SRS-Logik zur Verwendung von konfigurierbaren Zeitfenstern für die Auswahl und Aktualisierung von Elementen
All checks were successful
Deploy to production / deploy (push) Successful in 2m9s
2026-06-04 13:57:13 +02:00
Torsten Schulz (local)
5cc8dde2f1 feat: füge Skripte zur Behebung von SRS-Elementen hinzu, die mit next_due_at gleich oder unmittelbar vor created_at erstellt wurden
All checks were successful
Deploy to production / deploy (push) Successful in 2m11s
2026-06-03 17:35:26 +02:00
Torsten Schulz (local)
bc8d63058a feat: implement neue SRS-Logik zur Berechnung von Intervallen und füge Diagnoseskript für fällige Items hinzu
All checks were successful
Deploy to production / deploy (push) Successful in 2m10s
2026-06-03 17:13:50 +02:00
Torsten Schulz (local)
b5582045a9 feat: verbessere Fehlerbehandlung und optimiere das Laden von Vokabeln im VocabPracticeDialog
All checks were successful
Deploy to production / deploy (push) Successful in 2m5s
2026-06-03 16:50:28 +02:00
Torsten Schulz (local)
8b0aa94715 feat: verbessere SRS-Logik und füge serverseitige Zählung für fällige Items hinzu
Some checks failed
Deploy to production / deploy (push) Failing after 1m50s
2026-06-03 16:39:12 +02:00
Torsten Schulz (local)
a4c053df3e feat: aktualisiere Stile und verbessere das Layout der Kontomenü-Komponente
All checks were successful
Deploy to production / deploy (push) Successful in 2m23s
2026-05-31 18:38:26 +02:00
Torsten Schulz (local)
2c72dee43a feat: erweitere Platzhalter-Titel für Wiederholungs- und Übungslektionen im Bisaya-Kurs
All checks were successful
Deploy to production / deploy (push) Successful in 35s
2026-05-28 15:04:14 +02:00
Torsten Schulz (local)
1a8326ab24 feat: füge Unterstützung für Lagerschaden hinzu und aktualisiere Übersetzungen in mehreren Sprachen
All checks were successful
Deploy to production / deploy (push) Successful in 2m16s
2026-05-27 18:25:40 +02:00
Torsten Schulz (local)
2f858edd5b feat: erweitere Bisaya-Kursdidaktik um Unterstützung für Phase 5 und füge neue Übungen für Lektionen 44 und 45 hinzu
All checks were successful
Deploy to production / deploy (push) Successful in 32s
2026-05-27 18:22:58 +02:00
Torsten Schulz (local)
d10e8746fe feat: aktualisiere Reihenfolge der Skripte in SAFE_SYNC_STEPS für Bisaya-Kurs
All checks were successful
Deploy to production / deploy (push) Successful in 31s
2026-05-27 18:09:07 +02:00
Torsten Schulz (local)
0bde421155 feat: erweitere Vokabeltrainer um Unterstützung für Checkpoint-Lektionen und verbessere Übungsauswahl
All checks were successful
Deploy to production / deploy (push) Successful in 2m1s
2026-05-27 16:53:32 +02:00
Torsten Schulz (local)
871456a6f7 feat: aktualisiere Übersetzungen für "speichern" in mehreren Dateien
All checks were successful
Deploy to production / deploy (push) Successful in 2m1s
2026-05-27 16:38:45 +02:00
Torsten Schulz (local)
6347646a8a feat: füge neue Labels für Trainer-Fortschritt und Versuche in mehreren Sprachen hinzu
All checks were successful
Deploy to production / deploy (push) Successful in 2m5s
2026-05-27 16:30:12 +02:00
Torsten Schulz (local)
b976532b06 feat: füge Fortschrittsanzeige für die Freischaltung von Übungen hinzu
All checks were successful
Deploy to production / deploy (push) Successful in 2m6s
2026-05-27 16:22:13 +02:00
Torsten Schulz (local)
908be3e83b feat: aktualisiere Zahlen und Didaktik im Bisaya-Kurs, ersetze "baynte" durch "kawhaan"
All checks were successful
Deploy to production / deploy (push) Successful in 1m7s
2026-05-27 14:49:44 +02:00
Torsten Schulz (local)
e21949affb feat: erweitere Vokabelkurs mit neuen Zahlen und verbessere Sichtbarkeit der Zahlenreihen
All checks were successful
Deploy to production / deploy (push) Successful in 2m30s
2026-05-27 14:11:55 +02:00
Torsten Schulz (local)
cf007ffa51 chore: deploy pending backend changes [force-deploy]
All checks were successful
Deploy to production / deploy (push) Successful in 2m46s
2026-05-27 14:01:02 +02:00
Torsten Schulz (local)
033ca78dc0 feat: aktualisiere Bedingungen für Backend-Änderungen im Deployment-Skript
All checks were successful
Deploy to production / deploy (push) Successful in 31s
2026-05-27 13:58:52 +02:00
Torsten Schulz (local)
5c108e6508 feat: aktualisiere Zahlen und Sprachaufforderungen in bisaya-Kursplan
All checks were successful
Deploy to production / deploy (push) Successful in 54s
2026-05-27 13:43:15 +02:00
Torsten Schulz (local)
d441b4fa31 Refactor lesson types and update review handling
All checks were successful
Deploy to production / deploy (push) Successful in 2m22s
- Changed lesson type from 'review' to 'weekly_review' in multiple lesson definitions across phase 3 and phase 4 extensions.
- Updated the logic in the vocabService to accommodate 'weekly_review' in various methods, ensuring proper handling of weekly review lessons.
- Modified the VocabLessonView component to recognize 'weekly_review' as a valid lesson type for calculations and access checks.
- Enhanced the didactics update script to include 'weekly_review' in the lesson type checks.
- Adjusted the create scripts to reflect the new lesson type for weekly reviews.
2026-05-27 11:34:03 +02:00
Torsten Schulz (local)
664a7b3530 feat: füge Überprüfung der Platzhalteranzahl in Gap-Fill-Übungen hinzu und aktualisiere Beispieltexte
All checks were successful
Deploy to production / deploy (push) Successful in 33s
2026-05-27 10:43:21 +02:00
Torsten Schulz (local)
2ca008806f feat: normalize questionData for gap-fill exercises and add placeholders if necessary
All checks were successful
Deploy to production / deploy (push) Successful in 2m1s
2026-05-26 16:24:20 +02:00
Torsten Schulz (local)
d808d54dd6 feat: erweitere gap_fill-Übungen, um mehrere Wörter in Referenzen zu unterstützen
All checks were successful
Deploy to production / deploy (push) Successful in 1m58s
2026-05-26 16:09:21 +02:00
Torsten Schulz (local)
52ee406165 feat: füge Unterstützung für temporäre gap_fill-Übungen aus chapterLexemeTraining hinzu
All checks were successful
Deploy to production / deploy (push) Successful in 2m0s
2026-05-26 16:04:36 +02:00
Torsten Schulz (local)
1232edd570 feat: erweitere _extractTrainerVocabsFromExercises um Optionen zur Steuerung von Lückenfüllungen
All checks were successful
Deploy to production / deploy (push) Successful in 1m58s
2026-05-26 15:40:59 +02:00
Torsten Schulz (local)
b1d04812a4 feat: erweitere VocabService um Unterstützung für Grammatikübungen aus vorherigen Lektionen
All checks were successful
Deploy to production / deploy (push) Successful in 2m6s
2026-05-26 15:11:01 +02:00
Torsten Schulz (local)
04ab072dc5 feat: aktualisiere Übungsanzeige, um synthetische Kapitel-MCs auszuschließen
All checks were successful
Deploy to production / deploy (push) Successful in 2m6s
2026-05-26 12:56:50 +02:00
Torsten Schulz (local)
502f1fc04e feat: füge generische Fallback-Übungen hinzu, wenn keine didaktischen Inhalte vorhanden sind
All checks were successful
Deploy to production / deploy (push) Successful in 30s
2026-05-26 11:16:16 +02:00
Torsten Schulz (local)
c44eb93f33 feat: füge Skript hinzu, um 'review: true' in Skripten zu ersetzen und unterstütze einen Dry-Run-Modus
All checks were successful
Deploy to production / deploy (push) Successful in 18s
2026-05-26 11:02:29 +02:00
Torsten Schulz (local)
9b9afac66e feat: füge neue Übungen zur Lektion "Kinder, Spiel & Routine" hinzu, einschließlich Lückenfüllung und Satzreihenfolge
All checks were successful
Deploy to production / deploy (push) Successful in 28s
2026-05-22 15:15:26 +02:00
Torsten Schulz (local)
d371df7ac4 feat: füge Skript zur Reparatur von Kinderlektionen hinzu, das Platzhalterübungen ersetzt
All checks were successful
Deploy to production / deploy (push) Successful in 31s
2026-05-22 11:44:15 +02:00
Torsten Schulz (local)
cee4492dae feat: erweitere VocabLessonView mit Glossar-Optionen und verbessere die Lückentextformatierung
All checks were successful
Deploy to production / deploy (push) Successful in 2m8s
feat: füge Skript hinzu, um doppelte Muster in Lektionen zu identifizieren
feat: implementiere Skript zur Suche nach Übungen anhand von Text
feat: erstelle Skript zur Reparatur von Multiple-Choice-Antworten
feat: implementiere Skript zum Drucken von Lehrmusterinformationen
2026-05-22 09:43:39 +02:00
Torsten Schulz (local)
0732f44c96 fix: entferne redundante Übersetzungen und passe die Sprechaufforderungen an
All checks were successful
Deploy to production / deploy (push) Successful in 33s
2026-05-22 08:26:23 +02:00
Torsten Schulz (local)
1bd47fedb7 feat: füge neue Funktionen zur Analyse und Zählung von Lektionen hinzu, einschließlich der Generierung von Lernkontrollpunkten
All checks were successful
Deploy to production / deploy (push) Successful in 2m3s
2026-05-21 16:33:57 +02:00
Torsten Schulz (local)
8e5b990cf5 feat: erweitere Didaktik-Fragmente und verbessere die Übersetzungen in mehreren Sprachen
All checks were successful
Deploy to production / deploy (push) Successful in 2m6s
2026-05-21 14:45:22 +02:00
Torsten Schulz (local)
fa6164f93f feat(VocabLessonView): verbessere die Lückentextformatierung mit Fallback-Optionen
All checks were successful
Deploy to production / deploy (push) Successful in 1m59s
2026-05-21 13:53:22 +02:00
Torsten Schulz (local)
74b7297c97 feat: Füge Skript hinzu, um problematische Multiple-Choice-Übungen zu identifizieren und zu löschen
All checks were successful
Deploy to production / deploy (push) Successful in 1m57s
2026-05-21 13:47:42 +02:00
Torsten Schulz (local)
38ebeb084d fix(vocabService): skip multi-word learning items in SRS item processing
All checks were successful
Deploy to production / deploy (push) Successful in 1m57s
2026-05-21 13:23:19 +02:00
Torsten Schulz (local)
98ef2f29a0 fix(update-bisaya-didactics): remove speaking prompts from LESSON_DIDACTICS
All checks were successful
Deploy to production / deploy (push) Successful in 28s
2026-05-21 13:16:48 +02:00
Torsten Schulz (local)
a7b51365a0 refactor(NoLoginView): remove unused RandomChatDialog and clean up login panel
All checks were successful
Deploy to production / deploy (push) Successful in 1m53s
2026-05-21 11:24:41 +02:00
Torsten Schulz (local)
4917187517 feat: Füge Übersicht über die Auswirkungen von Liebschaften in mehreren Sprachen hinzu und entferne die Titel-Effekte-Karte aus FamilyView
All checks were successful
Deploy to production / deploy (push) Successful in 1m56s
2026-05-21 11:19:10 +02:00
Torsten Schulz (local)
35549d4c19 fix(falukant): correct computed block syntax in FamilyView.vue
All checks were successful
Deploy to production / deploy (push) Successful in 1m59s
2026-05-21 11:05:45 +02:00
Torsten Schulz (local)
65b5982492 feat: Füge Titel-Effekte und Kosteninformationen für Liebhaber in FamilyView hinzu
All checks were successful
Deploy to production / deploy (push) Successful in 1m53s
2026-05-21 10:52:35 +02:00
Torsten Schulz (local)
504a32a746 backend: accept parenthetical variants in answer checking (use _isEquivalentAnswer)
Some checks failed
Deploy to production / deploy (push) Has been cancelled
2026-05-21 10:51:36 +02:00
Torsten Schulz (local)
4abed5263e frontend: treat trailing parenthetical notes as optional in vocab matching
Some checks failed
Deploy to production / deploy (push) Has been cancelled
2026-05-21 10:50:15 +02:00
Torsten Schulz (local)
ad0ccd0281 feat: Füge Debug-Overlay und Alters-/Geschlechtsanpassungen für 3D-Charaktere hinzu
All checks were successful
Deploy to production / deploy (push) Successful in 1m55s
2026-05-21 09:55:11 +02:00
Torsten Schulz (local)
3df7abe628 feat: Füge Lazy-Loading und zusätzliche Props für Character3D-Komponente hinzu
All checks were successful
Deploy to production / deploy (push) Successful in 1m55s
2026-05-21 08:52:25 +02:00
Torsten Schulz (local)
7b56388bee feat: Importiere SRGBColorSpace aus 'three' und passe Export an
All checks were successful
Deploy to production / deploy (push) Successful in 1m53s
2026-05-21 08:39:46 +02:00
Torsten Schulz (local)
9a50d4df15 feat: Automatische Kameraanpassung für bessere Modellansicht und Import von Hemisphärenlicht
Some checks failed
Deploy to production / deploy (push) Failing after 1m35s
2026-05-21 08:33:53 +02:00
Torsten Schulz (local)
01953b1e18 feat: Anpassung der Kameraposition und Beleuchtung für bessere Darstellung in 3D-Ansichten
All checks were successful
Deploy to production / deploy (push) Successful in 1m53s
2026-05-21 08:28:47 +02:00
Torsten Schulz (local)
a766d47294 feat: Füge ResizeObserver hinzu, um Größenänderungen des Containers zu überwachen und die Darstellung anzupassen
All checks were successful
Deploy to production / deploy (push) Successful in 2m0s
2026-05-21 08:21:18 +02:00
Torsten Schulz (local)
2c47991202 ci: make check-version-against-prod.sh executable
All checks were successful
Deploy to production / deploy (push) Successful in 16s
2026-05-20 16:47:18 +02:00
Torsten Schulz (local)
431dd0bd37 feat: Erweiterung der akzeptierten Antworten im Vokabeltrainer durch Deduplizierung und Variantenaufspaltung
All checks were successful
Deploy to production / deploy (push) Successful in 2m13s
2026-05-20 16:10:57 +02:00
Torsten Schulz (local)
97b4b01b22 feat: Erweiterung der Antwortvarianten für Vokabelübungen zur Unterstützung von Alternativen und Phrasen
All checks were successful
Deploy to production / deploy (push) Successful in 1m58s
2026-05-19 11:24:01 +02:00
Torsten Schulz (local)
ba70c706c8 feat: Erweiterung der Bisaya-Kursinhalte um neue Lektionen und Aktualisierung der Titel in den didaktischen Feldern
All checks were successful
Deploy to production / deploy (push) Successful in 2m11s
2026-05-19 11:04:52 +02:00
Torsten Schulz (local)
3c92e1005a feat: Füge Übungen zu "Gefühle im Alltag" und "Gefühlswortschatz & Reaktionen" hinzu und aktualisiere die Titel für die Sicherheit
All checks were successful
Deploy to production / deploy (push) Successful in 28s
2026-05-19 08:50:44 +02:00
Torsten Schulz (local)
297ffbd4e2 feat: Aktualisiere Bisaya-Kursinhalte mit verbesserten Erklärungen und repariere alte Lektionen zu "Gefühle & Emotionen"
All checks were successful
Deploy to production / deploy (push) Successful in 2m14s
2026-05-19 08:46:09 +02:00
Torsten Schulz (local)
072d578c88 feat: Update home page notices and privacy information in English, Spanish, and French; add public guides and routing for guides
All checks were successful
Deploy to production / deploy (push) Successful in 2m11s
- Changed beta notice to service notice on home page translations for English, Spanish, and French.
- Updated privacy information to reflect transparency and continuous maintenance.
- Added new public guides content with detailed sections for various topics.
- Implemented routing for guide list and individual guide articles.
- Created new components for displaying guides and articles.
2026-05-18 14:37:04 +02:00
Torsten Schulz (local)
e87ed85867 fix(home): show only configured OAuth providers in intro copy
All checks were successful
Deploy to production / deploy (push) Successful in 2m3s
2026-05-15 16:05:08 +02:00
Torsten Schulz (local)
bab326247b feat: OAuth RFC9207 'iss' support; home: collapse intro by default; i18n: add oauth strings
All checks were successful
Deploy to production / deploy (push) Successful in 2m2s
2026-05-15 15:21:03 +02:00
Torsten Schulz (local)
e179dc714b feat(OAuth): add 'iss' parameter to OAuth exchange methods for improved provider handling
All checks were successful
Deploy to production / deploy (push) Successful in 1m57s
2026-05-15 14:58:38 +02:00
Torsten Schulz (local)
52f2d52916 fix(AccountView): update OAuth user ID retrieval to use user ID instead of hashed ID
All checks were successful
Deploy to production / deploy (push) Successful in 2m0s
2026-05-15 14:43:27 +02:00
Torsten Schulz (local)
2d048d4cab feat(OAuth): enhance OAuth user handling and add localized messages
All checks were successful
Deploy to production / deploy (push) Successful in 2m4s
2026-05-15 14:30:56 +02:00
Torsten Schulz (local)
4d3d02d937 feat(i18n): implement backward-compatible alias for security settings
All checks were successful
Deploy to production / deploy (push) Successful in 2m4s
2026-05-15 14:23:09 +02:00
Torsten Schulz (local)
be11fd8d29 Implement feature X to enhance user experience and fix bug Y in module Z
All checks were successful
Deploy to production / deploy (push) Successful in 2m6s
2026-05-15 14:04:49 +02:00
Torsten Schulz (local)
ac57931928 Add OAuth integration for multiple providers and implement user linking
Some checks failed
Deploy to production / deploy (push) Failing after 49s
- Created OAuth credentials setup guide for Google, Microsoft, Keycloak, ORY, and ZITADEL.
- Added migration for oauth_identity table to store OAuth identities linked to users.
- Implemented OAuthIdentity model for managing OAuth identities in the database.
- Developed oauthService to handle OAuth login, user creation, and identity linking.
- Created OAuthCallbackView and OAuthUserCallbackView components for handling OAuth responses in the frontend.
- Added error handling and user feedback during the OAuth process.
2026-05-15 13:59:40 +02:00
Torsten Schulz (local)
464208e30e feat(FalukantService): implement marriage honeymoon pregnancy scheduling logic
All checks were successful
Deploy to production / deploy (push) Successful in 2m5s
- Added a new method to ensure pregnancy scheduling for married couples after the wedding, allowing the system to manage pregnancy due dates effectively.
- Integrated checks for existing pregnancy and relationship flags to prevent duplicate scheduling.
- Updated family retrieval logic to include pregnancy status based on the new scheduling method, enhancing user experience in family management.
2026-05-13 22:07:46 +02:00
Torsten Schulz (local)
b59526b20d feat(Localization, User Management): add missingUserId translations and enhance user ID handling
All checks were successful
Deploy to production / deploy (push) Successful in 2m2s
- Added translations for the "missingUserId" key in multiple languages to improve user feedback when user ID is not available.
- Updated the user ID handling in the store and WorkerSchedulesView component to ensure proper user identification when sending data to the daemon.
- Enhanced error handling to notify users when the user ID is missing, improving overall user experience.
2026-05-08 11:09:34 +02:00
Torsten Schulz (local)
008cd7ae86 feat(Navigation, UserRights, Localization): add worker schedules feature and enhance access control
All checks were successful
Deploy to production / deploy (push) Successful in 1m52s
- Updated navigation structure to include a new section for worker schedules, accessible to specific user roles.
- Introduced a new user right type for 'worker_schedule_read' to manage access permissions effectively.
- Added localization entries for worker schedules in multiple languages, ensuring consistent user experience across the application.
- Created a new route and component for managing worker schedules in the admin panel.
2026-05-08 08:54:17 +02:00
Torsten Schulz (local)
0f7220d0b1 feat(FalukantService): update election filtering to include future dates and enhance eligibility checks
All checks were successful
Deploy to production / deploy (push) Successful in 1m55s
- Modified the election query to filter for upcoming election dates instead of past ones, ensuring only future positions are considered.
- Added checks for prerequisites in election eligibility, allowing positions without prerequisites to be selectable, improving user experience in the application.
2026-05-08 08:30:03 +02:00
Torsten Schulz (local)
0e572f8cbe feat(FalukantService, MessagesDialog): enhance character data handling in notifications
All checks were successful
Deploy to production / deploy (push) Successful in 2m3s
- Added mappings for character titles and genders in the FalukantService to enrich notifications with additional character information.
- Updated the MessagesDialog component to utilize the new title and gender data, improving the display of director names based on their titles and genders.
- Ensured that character names are displayed correctly, enhancing the overall user experience in notifications.
2026-05-08 08:25:36 +02:00
Torsten Schulz (local)
cc89fd4bef feat(VocabPracticeDialog, VocabCourseView): implement event dispatch for hard vocabulary changes
All checks were successful
Deploy to production / deploy (push) Successful in 1m55s
- Added event dispatching for 'yourpart:hardvocab:changed' in both VocabPracticeDialog and VocabCourseView components to notify changes in hard vocabulary items.
- Implemented event handling in VocabCourseView to refresh the hard vocabulary list when the event is triggered, ensuring UI consistency across components.
- Included error handling for environments that do not support CustomEvent, enhancing robustness.
2026-05-07 13:52:49 +02:00
Torsten Schulz (local)
42d0652e48 feat(Director): add autoAdjustIncome feature and enhance director data handling
All checks were successful
Deploy to production / deploy (push) Successful in 1m54s
- Introduced a new boolean field `autoAdjustIncome` in the Director model to manage income adjustments automatically.
- Updated the FalukantService to include `autoAdjustIncome` in director data responses and settings management.
- Enhanced the DirectorInfo component to allow users to toggle the `autoAdjustIncome` setting.
- Updated internationalization files to include translations for the new feature across multiple languages.
2026-05-07 13:29:09 +02:00
Torsten Schulz (local)
ddd8ca49d8 feat(FamilyView): enhance character display and avatar handling
All checks were successful
Deploy to production / deploy (push) Successful in 1m54s
- Refactored character display for spouses, children, and lovers to improve UI consistency and visual appeal.
- Introduced a new method for calculating character avatar styles based on age and gender, enhancing the representation of characters.
- Updated the layout to include compact character media for children and lovers, improving space utilization and readability.
2026-05-07 11:15:23 +02:00
Torsten Schulz (local)
8aeefccc3b feat(FalukantService, FamilyView): enhance marriage satisfaction handling
All checks were successful
Deploy to production / deploy (push) Successful in 1m56s
- Added logic to initialize marriage satisfaction to 100 for newly married couples in the FalukantService.
- Updated the FamilyView component to conditionally display marriage satisfaction only for married relationships, improving clarity in the UI.
- Ensured default values are used when marriage satisfaction is not explicitly set, enhancing user experience.
2026-05-07 09:40:58 +02:00
Torsten Schulz (local)
2c453a4a6b feat(VocabService): enhance logging and error handling in lesson assistant message flow
All checks were successful
Deploy to production / deploy (push) Successful in 2m18s
- Introduced detailed logging throughout the sendLessonAssistantMessage method to track request lifecycle, including start, abort conditions, and response handling.
- Improved error handling for various scenarios such as disabled assistant, unconfigured settings, empty messages, and fetch failures, providing clearer feedback to users.
- Added logging for response parsing and upstream errors to facilitate debugging and improve overall service reliability.
2026-05-07 08:58:22 +02:00
Torsten Schulz (local)
cfab56f63d feat(VocabPracticeDialog): improve SRS handling for incorrect answers
All checks were successful
Deploy to production / deploy (push) Successful in 2m8s
- Introduced logic to reinsert incorrectly answered items into the SRS queue, allowing them to be reviewed again before the session ends.
- Added a constant for the reinsert offset to control the position of requeued items.
- Updated session management to ensure incorrect answers are not marked as completed, enhancing the learning experience.
2026-05-07 08:46:33 +02:00
Torsten Schulz (local)
ab3e8d14e5 feat(AppHeader): improve ad display logic and responsiveness
All checks were successful
Deploy to production / deploy (push) Successful in 2m58s
- Enhanced the AppHeader component to support separate ad slots for mobile and desktop views, improving ad display based on viewport width.
- Introduced a responsive design that adjusts ad dimensions and formats for better visibility across different screen sizes.
- Updated methods to dynamically determine the active ad slot and handle viewport resizing events for optimal ad rendering.
2026-05-06 15:53:58 +02:00
Torsten Schulz (local)
5993f79e7a feat(VocabService, AppHeader): enhance timeout configuration and update ad display settings
All checks were successful
Deploy to production / deploy (push) Successful in 2m21s
- Updated the VocabService to allow configurable timeout for responses, defaulting to 300 seconds if not set or invalid.
- Modified AppHeader component to change ad format to horizontal and adjusted responsive settings for better display consistency.
- Ensured proper height settings for ad elements to maintain layout integrity across different screen sizes.
2026-05-06 15:50:14 +02:00
Torsten Schulz (local)
b1365dccbb feat(MoneyHistoryView): load branch names for enhanced activity display
All checks were successful
Deploy to production / deploy (push) Successful in 2m12s
- Added a new method to load branch names from the API and map them by ID for better display in money history activities.
- Updated the component's data structure to include a mapping of branch names, improving the clarity of tax-related activity translations.
2026-05-06 13:52:44 +02:00
Torsten Schulz (local)
511146da74 feat(FalukantService): add numeric value conversion for money checks
All checks were successful
Deploy to production / deploy (push) Successful in 2m12s
- Introduced a new private method `_toNumericValue` to handle conversion of various input types to numeric values, ensuring proper validation of user money and requirement values.
- Updated `checkMoneyRequirement` method to utilize the new conversion logic, improving accuracy in money requirement checks by handling edge cases for non-finite values.
2026-05-05 08:03:05 +02:00
Torsten Schulz (local)
3365f1dd2a feat(MoneyHistoryView): enhance activity translation for tax and reputation actions
All checks were successful
Deploy to production / deploy (push) Successful in 2m7s
- Added translation logic for activities related to tax from sales branches and reputation actions in the MoneyHistoryView component.
- Updated internationalization files to include new strings for these activities in multiple languages.
2026-04-30 15:41:00 +02:00
Torsten Schulz (local)
c6ffdd10f7 feat(update-frontend): enhance environment file handling and add Google AdSense script
All checks were successful
Deploy to production / deploy (push) Successful in 2m3s
- Updated the script to prioritize the root environment file for builds, falling back to the frontend environment file if necessary.
- Added a Google AdSense script to the frontend HTML for improved monetization capabilities.
2026-04-27 16:36:38 +02:00
Torsten Schulz (local)
530855e26e feat(Moderation): enhance moderation reporting and user feedback
All checks were successful
Deploy to production / deploy (push) Successful in 1m55s
- Added user blocking checks in authentication and reporting processes, returning appropriate error responses.
- Expanded moderation report functionality to include new target types and optional fields for reports.
- Implemented a new API endpoint to retrieve the count of open moderation reports.
- Enhanced frontend components to allow users to report profiles, images, and guestbook entries, with corresponding UI updates.
- Updated internationalization files to include new strings for reporting features in both German and English.
2026-04-27 15:57:02 +02:00
Torsten Schulz (local)
e94ae4350d feat(RandomChatDialog): enhance chat reporting functionality
All checks were successful
Deploy to production / deploy (push) Successful in 2m1s
- Added logic to enable the reporting button based on chat state and message presence.
- Introduced a computed property to determine if the chat can be reported.
- Updated the reporting method to accommodate the last partner in case the current partner is unavailable.
- Improved handling of partner details in the reporting payload for better accuracy.
2026-04-27 15:23:48 +02:00
Torsten Schulz (local)
ff68fb72c4 feat(Chat): implement chat incident reporting feature
All checks were successful
Deploy to production / deploy (push) Successful in 1m57s
- Added reportChatIncident method in ChatController to handle reporting of chat incidents.
- Introduced a new API route for reporting incidents in chatRouter.
- Implemented chatService methods to ensure the chat report table is created and to handle incident data storage.
- Enhanced frontend components to allow users to report incidents in both multi and random chat dialogs.
- Updated internationalization files to include new strings for reporting functionality in multiple languages.
2026-04-27 15:00:52 +02:00
Torsten Schulz (local)
90e1c0496a feat(ModerationReports): enhance moderation reports functionality and UI
All checks were successful
Deploy to production / deploy (push) Successful in 2m3s
- Added topic and forum IDs to moderation report queries for better context.
- Updated admin interface to include links to open reported forum messages.
- Implemented methods to handle opening target messages directly from the moderation reports view.
- Enhanced internationalization support for new UI elements in both German and English.
- Improved scrolling behavior to focus on specific messages when navigating to them.
2026-04-27 14:55:23 +02:00
Torsten Schulz (local)
a02fe1f008 feat(Moderation): implement moderation reports feature
All checks were successful
Deploy to production / deploy (push) Successful in 2m1s
- Added moderationRouter to handle moderation-related API routes.
- Introduced new methods in AdminController for fetching all regions, region types, and creating regions.
- Enhanced adminRouter with routes for moderation reports and status updates.
- Updated navigationController to include moderation reports in the admin menu.
- Implemented frontend components for reporting messages in the forum and managing moderation reports.
- Added internationalization support for moderation-related texts in multiple languages.
2026-04-27 14:52:19 +02:00
Torsten Schulz (local)
7fc9b55b59 feat(FalukantService, CreateBranchDialog): implement dynamic branch cost calculation
All checks were successful
Deploy to production / deploy (push) Successful in 2m8s
- Added new constants and methods in FalukantService to calculate branch action costs based on owned branch types, enhancing the cost management logic.
- Updated the CreateBranchDialog to utilize the new cost calculation, ensuring accurate display of branch creation costs based on user-owned branches.
- Improved the handling of branch type costs, allowing for a more flexible and responsive user experience when creating and upgrading branches.
2026-04-27 14:35:45 +02:00
Torsten Schulz (local)
d854200708 feat(VocabPracticeDialog, VocabCourseView): enhance hard vocabulary management and UI
All checks were successful
Deploy to production / deploy (push) Successful in 2m6s
- Refactored methods in VocabPracticeDialog to improve the handling of hard vocabulary items, including the addition of `findMatchingHardKey` and `removeHardEntriesForItem` for better management of hard vocabulary entries.
- Updated the VocabCourseView to display a new section for hard vocabulary items, allowing users to view and remove difficult words easily.
- Enhanced the UI with new styles for the hard vocabulary list, improving user engagement and accessibility to challenging vocabulary practice.
2026-04-26 23:32:18 +02:00
Torsten Schulz (local)
7fd8e4dda8 fix(VocabPracticeDialog): adjust dialog dimensions for improved layout
All checks were successful
Deploy to production / deploy (push) Successful in 1m54s
- Increased the width from 55em to 64em and height from 32em to 40em to enhance the visual layout of the VocabPracticeDialog component, providing a better user experience.
2026-04-23 14:25:24 +02:00
Torsten Schulz (local)
0e39ca9a0f feat(VocabPracticeDialog): improve vocabulary item normalization and expansion
All checks were successful
Deploy to production / deploy (push) Successful in 1m48s
- Enhanced the normalization process for vocabulary items to better accommodate multiple valid translations, improving the overall accuracy of vocabulary practice.
- Updated methods for expanding vocabulary item alternatives, ensuring a more flexible and effective learning experience for users.
2026-04-23 13:47:21 +02:00
Torsten Schulz (local)
79fe05c630 feat(VocabPracticeDialog): enhance vocabulary item expansion logic
All checks were successful
Deploy to production / deploy (push) Successful in 1m48s
- Added methods to split phrase alternatives and expand pool item alternatives, allowing for better handling of multiple valid translations and maintaining alignment between learning and reference phrases.
- Updated the normalization process to incorporate these new methods, improving the accuracy and flexibility of vocabulary practice sessions.
2026-04-23 13:44:31 +02:00
Torsten Schulz (local)
e3c024d5af feat(VocabPracticeDialog): enhance answer variant expansion logic
All checks were successful
Deploy to production / deploy (push) Successful in 1m51s
- Renamed `expandAnswerVariants` to `expandSingleAnswerVariants` for clarity and added a new `expandAnswerVariants` method to handle full-answer alternatives like "A / B".
- Improved the logic to ensure that phrase alternatives are expanded correctly, enhancing the accuracy of answer variant generation during vocabulary practice.
2026-04-23 13:40:08 +02:00
Torsten Schulz (local)
783dd175e8 feat(VocabCourseView): add hard vocabulary practice button and refresh logic
All checks were successful
Deploy to production / deploy (push) Successful in 1m57s
- Introduced a button to start hard vocabulary practice when applicable, enhancing user engagement with challenging vocabulary.
- Implemented methods to manage and refresh the hard vocabulary list from local storage, ensuring users have access to their difficult words.
- Added event listeners to refresh the hard vocabulary list on window focus, improving the overall user experience.
2026-04-23 13:10:05 +02:00
Torsten Schulz (local)
cab5428d0b feat(VocabLessonView): enhance vocab trainer visibility for cross-chapter hard vocabulary
All checks were successful
Deploy to production / deploy (push) Successful in 1m58s
- Updated the VocabLessonView to display the vocab trainer section when there are cross-chapter hard vocabulary items, improving user engagement and accessibility to training options.
- Adjusted the button rendering logic to ensure that the hard vocabulary trainer is available when applicable, enhancing the overall learning experience.
2026-04-23 10:04:46 +02:00
Torsten Schulz (local)
478a7ffc96 fix(VocabPracticeDialog): adjust success tracking for hard-phase drills
All checks were successful
Deploy to production / deploy (push) Successful in 1m56s
- Updated the success tracking logic to prevent inflating normal success counters during hard-phase drills, ensuring accurate representation of user progress.
- Introduced a condition to separate tracking for hard-phase activities, enhancing the integrity of vocabulary rotation and user experience.
2026-04-22 11:55:40 +02:00
Torsten Schulz (local)
677e4c674e refactor(VocabPracticeDialog): replace hard-coded consecutive correct threshold with constant
All checks were successful
Deploy to production / deploy (push) Successful in 1m56s
- Introduced a constant `HARD_REQUIRED_CONSECUTIVE_CORRECT` to define the required number of consecutive correct answers for hard vocabulary items, improving code maintainability and readability.
- Updated relevant methods to utilize the new constant, ensuring consistent behavior across the component.
2026-04-22 11:51:49 +02:00
Torsten Schulz (local)
d3aad6e7ef feat(localization): add difficult vocabulary marking features in multiple languages
All checks were successful
Deploy to production / deploy (push) Successful in 1m58s
- Introduced new localization strings for marking vocabulary as difficult, including options to save and remove difficult marks.
- Enhanced user experience by providing feedback on the status of difficult vocabulary in Cebuano, Spanish, and French.
- Updated existing localization files to ensure consistency across languages, supporting the recent vocabulary management features.
2026-04-22 11:12:26 +02:00
Torsten Schulz (local)
44b40d5a46 feat(VocabPracticeDialog, localization): add hard vocabulary marking feature
All checks were successful
Deploy to production / deploy (push) Successful in 1m53s
- Introduced functionality to mark vocabulary items as difficult, allowing users to track and manage challenging words during practice sessions.
- Added buttons for marking and unmarking current vocabulary as hard, enhancing user engagement and learning effectiveness.
- Updated the UI to display the count of hard-marked vocabulary items, providing users with better insights into their learning progress.
- Enhanced localization files for German and English to support new vocabulary marking features, ensuring a consistent user experience across languages.
2026-04-21 15:57:21 +02:00
Torsten Schulz (local)
4cc2aace6b feat(DayProduction, FalukantService, VocabLessonView): enhance vocabulary training and production tracking
All checks were successful
Deploy to production / deploy (push) Successful in 1m51s
- Added a new `completionCount` field to the DayProduction model to track the number of completed productions.
- Updated the FalukantService to aggregate completed productions using the new `completionCount` field, improving accuracy in production statistics.
- Introduced new vocabulary training features in VocabLessonView, including options to mark vocabulary as difficult and track remaining hard vocabulary, enhancing user engagement and learning effectiveness.
- Updated localization files for German and English to support new vocabulary training features, ensuring a consistent user experience across languages.
2026-04-21 15:44:44 +02:00
Torsten Schulz (local)
27d42c0a3a fix(AdminService, AdminController): enhance error handling and character deletion logic
All checks were successful
Deploy to production / deploy (push) Successful in 1m44s
- Updated error handling in AdminController to include 'targetnotdead' status for improved response accuracy.
- Modified character deletion logic in AdminService to ensure correct target character is identified, especially when dealing with living characters, enhancing data integrity during cleanup operations.
- Adjusted attributes fetched for characters to include 'health', facilitating better decision-making in deletion processes.
2026-04-20 16:27:08 +02:00
Torsten Schulz (local)
7417736daf refactor(AdminService, EditUserView): streamline character data deletion and enhance confirmation dialog
All checks were successful
Deploy to production / deploy (push) Successful in 1m46s
- Introduced a reusable `deleteIfTableExists` function in AdminService to simplify the deletion of character-related data across multiple tables, improving code maintainability.
- Updated the deletion logic to check for table existence before executing delete queries, enhancing robustness.
- Enhanced the EditUserView by integrating a `ChooseDialog` for user confirmation before proceeding with character death cleanup, improving user experience and interaction clarity.
2026-04-20 16:16:48 +02:00
Torsten Schulz (local)
267711fca6 feat(AdminController, AdminService, AdminRouter, localization): add character death cleanup feature
All checks were successful
Deploy to production / deploy (push) Successful in 1m52s
- Implemented `adminCleanupCharacterDeathArtifacts` method in AdminService to remove stale character-bound data after death/inheritance, including knowledge, debtors prism, and political offices.
- Added corresponding route in AdminRouter for triggering the cleanup process via an API endpoint.
- Enhanced AdminController to handle requests for the new cleanup feature, ensuring proper error handling and response formatting.
- Updated frontend components to include a user interface for initiating the cleanup, with localization support in both English and German for improved user experience.
2026-04-20 15:43:44 +02:00
Torsten Schulz (local)
8ce15441bf feat(VocabPracticeDialog): implement immediate retry feature for incorrect answers
All checks were successful
Deploy to production / deploy (push) Successful in 1m47s
- Added a `pendingRetry` property to manage immediate retries after incorrect answers, enhancing user experience during vocabulary practice.
- Updated logic to handle retries, allowing users to quickly attempt the last question again with the correct direction.
- Ensured that the retry state is reset appropriately after correct answers or when skipping questions, maintaining clarity in the training flow.
2026-04-20 10:02:32 +02:00
Torsten Schulz (local)
1500f01875 feat(VocabPracticeDialog, localization): implement last wrong answer review feature
All checks were successful
Deploy to production / deploy (push) Successful in 1m49s
- Added a new section in VocabPracticeDialog to display the last incorrect answer, including the asked vocabulary, user answer, and correct solutions.
- Updated localization strings in Cebuano, German, English, Spanish, and French to support the new review feature, enhancing user experience across multiple languages.
- Improved UI layout for the review section to provide clearer feedback during vocabulary training sessions.
2026-04-20 09:52:26 +02:00
Torsten Schulz (local)
5f13583e41 feat(VocabLessonView, localization): add vocabulary review feature and update translations
All checks were successful
Deploy to production / deploy (push) Successful in 1m55s
- Implemented a new vocabulary review section in VocabLessonView to display the last incorrect answer, including the asked vocabulary, user answer, and correct answer.
- Added localization strings for the new review feature in Cebuano, German, English, Spanish, and French, enhancing user experience across multiple languages.
- Updated the UI to visually differentiate the review section, improving clarity and usability during vocabulary training sessions.
2026-04-20 09:39:48 +02:00
Torsten Schulz (local)
dee4991be7 feat(VocabLessonView): implement vocab trainer continue timer management
All checks were successful
Deploy to production / deploy (push) Successful in 2m2s
- Added a new timer management feature to the vocab trainer, allowing for better control over the continuation of questions after user answers.
- Introduced `clearVocabTrainerContinueTimer` method to prevent multiple timers from running simultaneously, enhancing the stability of the training session.
- Updated various methods to utilize the new timer management, ensuring a smoother user experience during vocabulary training.
2026-04-20 09:26:02 +02:00
Torsten Schulz (local)
d7fa5f925e feat(VocabService, localization, VocabLessonView): extend assistant wait feedback and timeout duration
All checks were successful
Deploy to production / deploy (push) Successful in 2m2s
- Increased the timeout duration for the language assistant from 30 seconds to 120 seconds to accommodate longer processing times.
- Added new localization strings across multiple languages (Cebuano, German, English, Spanish, French) to provide user feedback during the assistant's wait period, enhancing clarity and user experience.
- Implemented a visual waiting indicator in VocabLessonView to inform users that the assistant is still processing their request, improving overall interaction quality.
2026-04-20 08:58:51 +02:00
Torsten Schulz (local)
e28ed7bdb5 feat(VocabService, VocabPracticeDialog, VocabLessonView): enhance vocabulary training logic and UI feedback
All checks were successful
Deploy to production / deploy (push) Successful in 2m17s
- Introduced methods for improved text analysis and validation in VocabService, including `_wordCount` and `_looksLikeFragmentMismatch`, to better assess learning and reference pairs.
- Updated VocabPracticeDialog to display submitted answers and correct solutions, enhancing user feedback during practice sessions.
- Enhanced VocabLessonView to ensure only trainable vocabulary pairs are processed, improving the quality of vocabulary training.
- Added localization entries for new UI elements in both English and German, ensuring clarity in user interactions.
2026-04-20 08:48:39 +02:00
Torsten Schulz (local)
553f132184 feat(VocabPracticeDialog): add answer variant expansion for improved vocabulary training
All checks were successful
Deploy to production / deploy (push) Successful in 1m55s
- Introduced a new method `expandAnswerVariants` to generate multiple answer variants from a given input, enhancing the flexibility of acceptable answers.
- Improved handling of words with slashes to create diverse answer options, ensuring a richer vocabulary training experience.
- Updated the answer collection logic to utilize the new method, allowing for a broader range of valid responses during practice sessions.
2026-04-20 08:36:11 +02:00
Torsten Schulz (local)
f64c923db6 feat(VocabService, VocabPracticeDialog): enhance text analysis and SRS validation
All checks were successful
Deploy to production / deploy (push) Successful in 1m50s
- Improved text analysis methods in VocabService to better identify instructional-like texts and task-related prompts, enhancing vocabulary training quality.
- Updated VocabPracticeDialog to include a new method for checking instruction-like text, ensuring more accurate filtering of trainable pairs.
- Incremented SRS session storage version to reflect changes in session management and data handling.
- Refactored normalization and validation logic to maintain consistency across vocabulary entries and improve user experience.
2026-04-20 08:30:58 +02:00
Torsten Schulz (local)
71f2ee7c97 feat(seo, routing): add Bisaya learning and German for Bisaya courses
All checks were successful
Deploy to production / deploy (push) Successful in 2m0s
- Introduced new routes for Bisaya learning and German for Bisaya courses, enhancing the marketing section of the application.
- Updated sitemap.xml to include new course URLs for better SEO visibility.
- Added localization entries for both courses in English and German, improving content accessibility for users.
- Enhanced SEO metadata generation for the new courses, ensuring proper indexing and visibility in search engines.
- Updated VocabLandingView to feature links to the new courses, improving user navigation and engagement.
2026-04-20 08:28:00 +02:00
Torsten Schulz (local)
e6c90c219b feat(VocabService): improve SRS pair validation and introduce new text analysis methods
All checks were successful
Deploy to production / deploy (push) Successful in 2m13s
- Refactored SRS pair validation logic by introducing the `_isTrainableSrsPair` method to enhance the criteria for acceptable learning and reference pairs.
- Added `_isInstructionLikeText` method to filter out instructional texts from SRS pairs, improving the quality of vocabulary training.
- Updated various methods to utilize the new validation logic, ensuring consistent handling of vocabulary entries across the service.
- Enhanced the `_courseHasDueSrsItems` and `_extractTrainerVocabsFromLessonDidactics` methods to improve data retrieval and filtering based on the new criteria.
2026-04-20 08:21:18 +02:00
Torsten Schulz (local)
b6d749f781 feat(VocabLessonView): enhance vocab trainer logic and answer normalization
All checks were successful
Deploy to production / deploy (push) Successful in 1m51s
- Refactored vocab trainer logic to improve handling of acceptable answers, allowing for multiple translations and synonyms.
- Introduced a new method to retrieve equivalent vocabulary answers, ensuring mixed pools from different lessons provide valid alternatives.
- Enhanced answer normalization process to prevent duplicates and improve the quality of acceptable answers presented to users.
2026-04-17 17:30:03 +02:00
Torsten Schulz (local)
39dcdd7fb3 feat(FalukantService, OverviewView): enhance church office information retrieval and UI display
All checks were successful
Deploy to production / deploy (push) Successful in 1m51s
- Refactored the `getHighestChurchOfficeInfo` method to utilize a new approach for retrieving the highest church office, improving accuracy and performance.
- Updated the `OverviewView` to format and display the highest political and church office names instead of raw rank values, enhancing user experience and clarity.
- Introduced a new method `formatCertificateHighestOffice` for better localization and presentation of office titles in the UI.
2026-04-17 17:17:17 +02:00
Torsten Schulz (local)
afbea926a2 feat(FalukantService, i18n): enhance household tension handling and localization updates
All checks were successful
Deploy to production / deploy (push) Successful in 1m55s
- Added functionality to refresh and update household tension scores and reasons in the FalukantService, improving user experience by reflecting real-time household dynamics.
- Updated localization strings across multiple languages (Cebuano, German, English, Spanish, French) to include new office rank terminology and improved marriage crisis descriptions, enhancing clarity and context for users.
- Introduced a new office rank tier structure in localization files, providing better categorization and understanding of office ranks in the game.
2026-04-17 17:07:53 +02:00
Torsten Schulz (local)
1dd4d18927 fix(FalukantService): update userHouse query attributes and transaction handling
All checks were successful
Deploy to production / deploy (push) Successful in 1m52s
- Modified the userHouse query to include 'id' in the attributes, improving data retrieval.
- Enhanced transaction handling in state and userHouse updates to ensure atomic operations, preventing potential data inconsistencies.
2026-04-17 16:58:41 +02:00
Torsten Schulz (local)
d2eebf1f94 feat(VocabService, VocabCourseView): enhance SRS item retrieval and UI integration
All checks were successful
Deploy to production / deploy (push) Successful in 1m52s
- Updated VocabService to calculate total due count for spaced repetition system (SRS) items, improving data handling for user queries.
- Modified VocabCourseView to incorporate total due count in the UI, ensuring accurate display of SRS item statistics.
- Enhanced error handling and data validation for SRS due items, improving overall reliability and user experience.
2026-04-17 16:53:56 +02:00
Torsten Schulz (local)
7732498875 feat(MiniCalendarWidget): enhance localization and refactor calendar logic
All checks were successful
Deploy to production / deploy (push) Successful in 1m55s
- Updated the MiniCalendarWidget to utilize localized strings for month names, weekdays, and loading messages, improving user experience across multiple languages.
- Refactored the calendar logic to use constants for month keys and weekday order, ensuring consistency with backend configurations.
- Added new translation entries for the mini widget in Cebuano, German, English, Spanish, and French, enhancing accessibility for users.
2026-04-17 16:44:27 +02:00
Torsten Schulz (local)
14881803df feat(i18n): enhance localization for chat and minigames
All checks were successful
Deploy to production / deploy (push) Successful in 1m51s
- Updated localization strings for chat and minigames across multiple languages, including Cebuano, German, English, Spanish, and French, to improve user experience and clarity.
- Added new translations for participant counts, user selection prompts, and game over messages, ensuring consistency and better engagement in the UI.
- Enhanced existing translations for accuracy and context, particularly in the TaxiGame component and chat functionalities.
2026-04-17 16:38:39 +02:00
Torsten Schulz (local)
3232e42251 feat(VocabPracticeDialog): implement SRS session management and enhance UI feedback
All checks were successful
Deploy to production / deploy (push) Successful in 1m53s
- Added support for spaced repetition system (SRS) session management, allowing users to resume practice sessions with due items.
- Introduced new UI elements to display SRS session status, including total due, completed, and remaining items.
- Enhanced localization for SRS-related messages in multiple languages, improving user experience and clarity.
- Updated methods for saving and loading SRS session data to local storage, ensuring persistence across sessions.
2026-04-17 16:28:19 +02:00
Torsten Schulz (local)
9c121d2dc2 feat(i18n): update Cebuano localization for minigames and enhance UI text
All checks were successful
Deploy to production / deploy (push) Successful in 1m56s
- Added new localized strings for minigames in Cebuano, including loading hints, objectives, and play focus descriptions to improve user experience.
- Updated existing translations for clarity and consistency across various game elements.
- Enhanced the Match3Game component to utilize localized strings, ensuring dynamic text rendering based on user language preferences.
- Included a new entry in .gitignore for the locale audit report to maintain a clean repository.
2026-04-17 16:20:20 +02:00
Torsten Schulz (local)
71d5922409 feat(bisaya-course): refine phase 4 didactics and enhance course content generation
All checks were successful
Deploy to production / deploy (push) Successful in 5m19s
- Corrected grammatical errors and improved the phrasing in the BISAYA_PHASE4_DIDACTICS, ensuring clarity and accuracy in the learning materials.
- Updated the course content generation script to include lessons from phase 5, enhancing the overall structure and flow of the course.
- Introduced a new vocabulary course content synchronization process, improving the integration of vocabulary resources across different modules.
- Enhanced the VocabService to dynamically adjust temperature settings based on the mode, optimizing response generation for different contexts.
- Added new localized titles and vocabulary entries in multiple languages, enriching the learning experience for users.
2026-04-17 16:00:41 +02:00
Torsten Schulz (local)
5c315c477f feat(i18n): add language trainer SEO content and update home page metadata
All checks were successful
Deploy to production / deploy (push) Successful in 5m2s
- Introduced a new section for language trainers in multiple languages (Cebuano, German, English, Spanish, French) to enhance user guidance on beginner courses.
- Updated home page SEO metadata to reflect the addition of language courses, improving search visibility and user engagement.
- Enhanced the NoLoginView component to display the new language trainer information, providing users with clear insights into available resources.
2026-04-17 15:30:12 +02:00
Torsten Schulz (local)
776dea2584 feat(LanguageAssistantView): add preset options for Ollama and OpenAI
All checks were successful
Deploy to production / deploy (push) Successful in 4m31s
- Introduced a new section for language assistant presets, allowing users to quickly apply configurations for Ollama and OpenAI.
- Implemented methods to set the base URL and model for each preset, enhancing user experience with one-click setup.
- Added styling for the preset buttons and hints to improve UI clarity and usability.
2026-04-17 15:24:22 +02:00
Torsten Schulz (local)
4205639de3 feat(bisaya-course): enhance phase 4 didactics with new learning goals and speaking prompts
All checks were successful
Deploy to production / deploy (push) Successful in 1m31s
- Expanded the BISAYA_PHASE4_DIDACTICS by adding new learning goals, core patterns, speaking prompts, and practical tasks to improve language acquisition.
- Updated the course content generation scripts to incorporate the new lesson structures and ensure alignment with the latest didactic updates.
- Enhanced the logic for generating exercises based on lesson types, introducing additional situational and speaking exercises for advanced lessons.
2026-04-17 15:12:21 +02:00
Torsten Schulz (local)
4df8f97a41 feat(bisaya-course): add new 'Ort & Richtung' section with learning goals and practical tasks
All checks were successful
Deploy to production / deploy (push) Successful in 4m46s
- Introduced a new section for 'Ort & Richtung' in the Bisaya didactics, including learning goals, core patterns, grammar focus, speaking prompts, and practical tasks to enhance language learning.
- Updated the lesson didactics to include the new section, ensuring comprehensive coverage of location and direction vocabulary.
2026-04-17 14:51:09 +02:00
Torsten Schulz (local)
f93a0f8b35 feat(VocabLessonView): enhance review lesson logic and vocabulary handling
All checks were successful
Deploy to production / deploy (push) Successful in 4m28s
- Added logic to adjust vocabulary blending and switching conditions based on lesson type and didactic mode, improving the learning experience during review lessons.
- Implemented dynamic thresholds for switching between multiple choice and typing modes based on success rates and lesson context.
- Enhanced vocabulary source selection to incorporate a review share, optimizing the question pool for better engagement during typing sessions.
2026-04-17 14:37:40 +02:00
Torsten Schulz (local)
b3346d4cac fix(update-backend): adjust file permissions and handle environment variables safely
All checks were successful
Deploy to production / deploy (push) Successful in 4m25s
- Updated the permission settings for the environment file to 640, ensuring it is readable only by the owner and the deploy group.
- Modified the sequelize configuration to safely handle missing environment variables, converting them to strings or setting them to undefined to prevent runtime errors.
[force-deploy]
2026-04-17 13:53:13 +02:00
Torsten Schulz (local)
1b9d9664b2 refactor(update-backend): reorder permission settings and update migration message
Some checks failed
Deploy to production / deploy (push) Failing after 3m20s
- Moved permission setting commands to the end of the script for better clarity and execution flow.
- Changed the log message from "Running database synchronization..." to "Running database migrations..." to accurately reflect the operation being performed.
- Ensured that permission settings for backend and data directories are consistently applied after the migration process.
[force-deploy]
2026-04-17 13:45:53 +02:00
Torsten Schulz (local)
9a92940dab chore(package): update version to 3.0.0-beta-1.0.0 in package.json
Some checks failed
Deploy to production / deploy (push) Failing after 3m22s
[force-deploy]
2026-04-17 13:41:00 +02:00
Torsten Schulz (local)
5070785a50 feat(deploy): update deployment workflow and migration paths
Some checks failed
Deploy to production / deploy (push) Has been cancelled
- Modified the deployment workflow to include new migration paths for the backend, ensuring that migrations are correctly referenced in the deployment process.
- Updated the `db:migrate` script in package.json to point to the `migrations-active` directory, enhancing clarity and organization of migration files.
- Adjusted the deployment conditions to account for changes in migration file locations, improving the accuracy of change detection during deployments.
- Removed obsolete migration files to streamline the migration process and prevent confusion.
2026-04-17 13:39:38 +02:00
Torsten Schulz (local)
70c381114b feat(migration): enhance contact_message migration to check for existing columns before adding/removing
Some checks failed
Deploy to production / deploy (push) Failing after 3m14s
- Updated the migration script for the contact_message table to conditionally add or remove columns based on their existence, improving migration safety and preventing errors during deployment.
- This change ensures that the migration can be run multiple times without causing issues if the columns already exist or are missing.
[force-deploy]
2026-04-17 12:01:25 +02:00
Torsten Schulz (local)
de9f2c853d feat(deploy): add force deploy detection to deployment workflow
Some checks failed
Deploy to production / deploy (push) Failing after 3m17s
- Implemented logic to check for a `[force-deploy]` tag in the latest commit message, allowing for a forced deployment regardless of detected changes.
- Updated deployment conditions to include force deploy status, enabling more flexible deployment options based on commit messages.
- Enhanced sync step to trigger on force deploy, ensuring content updates are applied even without file changes.
2026-04-17 11:53:00 +02:00
Torsten Schulz (local)
1e9e247dec feat(update-backend): streamline dependency management and migration process
All checks were successful
Deploy to production / deploy (push) Successful in 1m14s
- Updated the npm install command to remove the production flag, allowing for the installation of all dependencies.
- Added a step to prune development dependencies after running database migrations, optimizing the deployment environment.
2026-04-17 11:42:41 +02:00
Torsten Schulz (local)
7b907ea683 feat(VocabLessonView): improve lesson reset behavior and normalize vocabulary handling
Some checks failed
Deploy to production / deploy (push) Has been cancelled
- Updated lesson reset functionality to ensure users start in the 'learn' tab after resetting progress.
- Enhanced vocabulary normalization by adding a method to strip trailing parenthetical notes, improving text consistency.
- Adjusted normalization logic to conditionally ignore trailing notes based on user input mode, enhancing user experience during vocabulary training.
2026-04-17 11:41:46 +02:00
Torsten Schulz (local)
e96c37aac5 feat(database): add migration and sync-db scripts to deployment process
Some checks failed
Deploy to production / deploy (push) Failing after 3m20s
- Introduced a new npm script for database migrations in package.json.
- Updated update-backend.sh to execute migrations and conditionally run legacy sync-db based on environment variable.
- Enhanced syncDatabase.js to skip initialization routines if APP_INIT_ON_START is not set, promoting best practices for schema changes during deployment.
2026-04-17 11:34:59 +02:00
Torsten Schulz (local)
1f10e7c519 feat(deploy): enhance deployment scripts with skip options for backend and frontend
All checks were successful
Deploy to production / deploy (push) Successful in 1m31s
- Updated `deploy-yourpart-bluegreen.sh` to pass additional arguments for skipping backend or frontend updates.
- Enhanced `update.sh` to handle `--skip-backend` and `--skip-frontend` flags, allowing for more flexible deployment based on changes detected.
- Modified deployment workflow to conditionally execute based on changes in frontend or backend files, improving deployment efficiency.
2026-04-17 11:33:02 +02:00
Torsten Schulz (local)
2461e98fb0 feat(bisaya-course): enhance script execution with direct run detection
All checks were successful
Deploy to production / deploy (push) Successful in 4m25s
- Added functionality to detect if the script is run directly, allowing for conditional execution of the main function.
- Imported `pathToFileURL` from the 'url' module to facilitate the direct run check, improving script usability and flexibility.
2026-04-17 11:23:40 +02:00
Torsten Schulz (local)
3f1b474fdd feat(deploy): add vocab course change detection and sync step
Some checks failed
Deploy to production / deploy (push) Failing after 4m12s
- Implemented a new workflow step to detect changes in vocab course files and conditionally sync content.
- Added a script to check for specific changes in the repository and trigger the sync process if necessary.
- Introduced a new npm script for syncing vocab course content in the backend package.json.
2026-04-17 11:17:59 +02:00
Torsten Schulz (local)
f8f5017436 feat(bisaya-course): enhance lesson structure and didactics integration
All checks were successful
Deploy to production / deploy (push) Successful in 2m52s
- Added an auto-incrementing primary key to the UserHouse model for improved database management.
- Integrated new didactics fragments from the Bisaya course plan, including relationship anchor didactics and lessons for enhanced curriculum depth.
- Updated lesson retrieval logic to utilize planned lesson titles, improving the accuracy of didactics resolution.
- Refactored course content generation scripts to incorporate new didactics, ensuring a comprehensive learning experience for users.
2026-04-17 11:08:12 +02:00
Torsten Schulz (local)
a022b8c174 feat(bisaya-course): add new didactics fragments for everyday conversations
All checks were successful
Deploy to production / deploy (push) Successful in 2m49s
- Introduced new didactics fragments for "Alltagsgespräche - Teil 1" and "Alltagsgespräche - Teil 2," including learning goals, core patterns, speaking prompts, and practical tasks to enhance conversational skills in Bisaya.
- Updated the lesson structure to incorporate these new fragments, improving the overall curriculum and providing learners with more comprehensive resources for everyday interactions.
2026-04-17 10:52:39 +02:00
Torsten Schulz (local)
09a10ff830 feat(bisaya-course, vocabService): expand numerical didactics and enhance lesson progress serialization
All checks were successful
Deploy to production / deploy (push) Successful in 2m54s
- Added new glosses for the numbers sixty, seventy, and eighty in the Bisaya didactics, enriching the numerical curriculum.
- Updated the _serializeLessonProgress method to include an options parameter for suppressing lesson review due notifications based on SRS item status.
- Introduced a new method to check for due SRS items, improving the handling of lesson progress and review scheduling.
2026-04-17 09:30:36 +02:00
Torsten Schulz (local)
8be215761d feat(VocabPracticeDialog, VocabCourseView): implement SRS rating feature and enhance user feedback
All checks were successful
Deploy to production / deploy (push) Successful in 2m48s
- Added SRS rating buttons in VocabPracticeDialog to allow users to rate their confidence after answering vocabulary questions.
- Updated methods to handle SRS ratings and integrated them into the review process, improving spaced repetition feedback.
- Enhanced UI with new styles for SRS rating buttons and updated translations for SRS-related terms in multiple languages.
- Modified VocabCourseView to display appropriate introductory text based on SRS due items, improving user guidance.
2026-04-17 09:27:29 +02:00
Torsten Schulz (local)
e2c1147d75 feat(vocab): implement SRS features and enhance vocabulary management
All checks were successful
Deploy to production / deploy (push) Successful in 2m49s
- Added new endpoints in vocabController for retrieving SRS due items and reviewing SRS items, improving spaced repetition support.
- Updated vocabService to handle SRS item creation and scheduling, ensuring effective tracking of vocabulary exposure.
- Enhanced vocabRouter with new routes for SRS functionalities, facilitating user interaction with spaced repetition features.
- Modified VocabPracticeDialog and VocabCourseView to integrate SRS due items, providing users with timely review opportunities.
- Updated translations and UI elements to reflect new SRS features, enhancing user experience and accessibility.
2026-04-17 09:14:30 +02:00
Torsten Schulz (local)
54a77c2e08 feat(vocabService, VocabPracticeDialog, VocabLessonView): enhance vocabulary handling and exposure tracking
All checks were successful
Deploy to production / deploy (push) Successful in 2m51s
- Updated vocabService to merge extracted vocabularies and improve handling of learning and reference pairs.
- Introduced normalization and exposure tracking in VocabPracticeDialog to ensure diverse and underexposed vocabulary practice.
- Enhanced VocabLessonView with methods to identify underexposed vocabularies and adjust selection logic for improved learning outcomes.
- Implemented new constants for minimum exposure requirements to optimize vocabulary training sessions.
2026-04-17 08:58:50 +02:00
Torsten Schulz (local)
d119869750 feat(bisaya-course): expand numerical didactics with additional glosses
All checks were successful
Deploy to production / deploy (push) Successful in 2m45s
- Added new glosses for numbers in Bisaya, enhancing the didactics for numerical lessons.
- Included translations for numbers 4 to 19, improving the comprehensiveness of the numerical curriculum.
- Updated the lesson structure to support the expanded vocabulary, ensuring a richer learning experience.
2026-04-17 08:21:02 +02:00
Torsten Schulz (local)
712370cad3 feat(bisaya-course): add didactics fragments for "Haus & Familie" lessons
All checks were successful
Deploy to production / deploy (push) Successful in 2m56s
- Introduced comprehensive didactics snippets for the "Haus & Familie" lesson, including learning goals, core patterns, grammar focus, speaking prompts, and practical tasks.
- Updated the lesson retrieval logic to incorporate these new fragments as a fallback for core patterns, enhancing the robustness of lesson content delivery.
- Modified the vocab service to utilize the new didactics fragments, ensuring a seamless integration into the existing lesson structure.
2026-04-16 22:44:57 +02:00
Torsten Schulz (local)
229bdc96bf feat(bisaya-course): update lesson retrieval and didactics processing
All checks were successful
Deploy to production / deploy (push) Successful in 2m56s
- Refactored the lesson retrieval logic to include lesson number and title in the query, improving data handling.
- Updated the didactics processing to utilize a lesson-like object instead of fetching lessons by primary key, enhancing performance.
- Improved error handling and ensured proper closure of the database connection after execution.
2026-04-16 22:36:15 +02:00
Torsten Schulz (local)
c1421db72c feat(bisaya-course): add new exercises for situational role plays and enhance lesson normalization
All checks were successful
Deploy to production / deploy (push) Successful in 3m3s
- Introduced a new section titled "Rollenspiele - echte Situationen" with multiple exercises focusing on real-life scenarios in Bisaya, including multiple-choice, gap-fill, and transformation tasks.
- Implemented a normalization function for lesson titles to improve matching accuracy and facilitate the rebuilding of placeholder lessons.
- Updated the course content generation logic to replace outdated exercises with new ones based on normalized titles, ensuring a more relevant learning experience.
2026-04-16 22:28:10 +02:00
Torsten Schulz (local)
68ac5ec281 feat(bisaya-course): add new lesson and update course creation logic
All checks were successful
Deploy to production / deploy (push) Successful in 2m51s
- Introduced a new lesson titled "Zahlen & Preise" to enhance the numerical curriculum in Bisaya.
- Updated the course creation logic to check for existing "Familien"-Bisaya courses, ensuring idempotency in course creation.
- Enhanced the lesson didactics by mapping legacy titles to current lesson keys, improving data consistency.
- Adjusted the course generation script to limit Bisaya courses to German-speaking learners only, streamlining course offerings.
2026-04-16 22:16:23 +02:00
Torsten Schulz (local)
6dce418728 feat(bisaya-course): enhance numerical lessons with expanded content
All checks were successful
Deploy to production / deploy (push) Successful in 2m51s
- Updated lessons on numbers to include detailed sections for "Zahlen 1–20," "Zahlen: Zehner," "Zahlen: Hunderter," and "Zahlen: Tausender," focusing on foundational numerical concepts in Bisaya.
- Introduced various exercise types such as multiple-choice, gap-fill, and transformation tasks to improve learner engagement and comprehension of numerical expressions.
- Enhanced didactics with clear learning goals, core patterns, grammar focus, speaking prompts, and practical tasks to support effective learning.
- Added review lessons to reinforce key concepts and ensure continuity in learning progression.
2026-04-16 21:55:05 +02:00
Torsten Schulz (local)
b2942c1c9d feat(vocabService): enhance answer validation with comparable variants
All checks were successful
Deploy to production / deploy (push) Successful in 2m45s
- Introduced methods to expand comparable answer variants and improve answer equivalence checks, allowing for more flexible user responses.
- Updated the answer validation logic to utilize the new methods, enhancing the accuracy of response evaluation during exercises.
2026-04-16 16:46:37 +02:00
Torsten Schulz (local)
41b07a8951 fix(bisaya-course): update alternatives for transformation exercise
All checks were successful
Deploy to production / deploy (push) Successful in 2m57s
- Modified the alternatives for a transformation exercise to include additional variations, enhancing the response options for learners.
- Updated the vocabulary service to incorporate both primary and alternative answers, improving the accuracy of answer validation during exercises.
2026-04-16 16:42:48 +02:00
Torsten Schulz (local)
9bc6f32b96 feat(bisaya-course): expand lessons on shopping, prices, and numbers
All checks were successful
Deploy to production / deploy (push) Successful in 2m43s
- Added new lessons titled "Einkaufen & Preise" and "Zahlen & Preise" with various exercises focusing on shopping vocabulary and numerical expressions in Bisaya.
- Introduced multiple exercise types including multiple-choice, gap-fill, and transformation tasks to enhance learner engagement and understanding of practical language use in market contexts.
- Updated didactics to include learning goals, core patterns, grammar focus, speaking prompts, and practical tasks related to shopping dialogues and numerical comprehension.
- Included a review lesson "Woche 2 - Wiederholung" to reinforce key concepts from previous lessons, ensuring comprehensive learning continuity.
- Enhanced vocabulary testing with a new "Woche 2 - Vokabeltest" to assess retention of key terms and phrases.
2026-04-16 16:25:47 +02:00
Torsten Schulz (local)
8e29953a95 feat(bisaya-course): add new lesson on time and date concepts
All checks were successful
Deploy to production / deploy (push) Successful in 2m52s
- Introduced a new lesson titled "Zeit & Datum" with multiple exercises focusing on recognizing and using key time-related vocabulary in Bisaya.
- Added various exercise types including multiple-choice, gap-fill, and transformation tasks to enhance learner engagement and understanding of temporal expressions.
- Updated didactics to include learning goals, core patterns, grammar focus, speaking prompts, and practical tasks related to time markers and simple date questions.
2026-04-16 16:09:10 +02:00
Torsten Schulz (local)
e26a3333c4 fix(vocabLessonReview): improve input normalization by trimming punctuation
All checks were successful
Deploy to production / deploy (push) Successful in 2m47s
- Enhanced the `normalize` method to trim trailing punctuation marks (.,!?;:) from input strings, ensuring cleaner and more consistent user input.
- Added logic to replace multiple spaces with a single space, improving the overall formatting of normalized strings.
2026-04-16 15:23:00 +02:00
Torsten Schulz (local)
44850d5913 feat(bisaya-course): update gap-fill exercise structure and instructions
All checks were successful
Deploy to production / deploy (push) Successful in 2m42s
- Changed exercise type from 'pattern_drill' to 'gap_fill' for improved clarity and engagement.
- Revised instructions to specify the use of the verb "adto" across different tenses, enhancing learner guidance.
- Updated question data format to include gaps for each tense, streamlining the exercise layout.
- Adjusted answer data to reflect the new gap-fill structure, ensuring consistency in exercise responses.
- Enhanced explanation to clarify the focus on tense variations with the verb "adto," improving educational value.
2026-04-16 15:06:17 +02:00
Torsten Schulz (local)
a294a94075 feat(vocabLesson): enhance input handling for gap-fill exercises
All checks were successful
Deploy to production / deploy (push) Successful in 2m45s
- Added functionality to manage Enter key events for gap-fill and single input fields, improving user experience during exercises.
- Introduced methods `onGapInputEnter` and `onSingleInputEnter` to streamline answer checking and navigation between input fields.
- Updated input elements with refs for better focus management, ensuring a smoother interaction flow for users.
2026-04-16 14:55:43 +02:00
Torsten Schulz (local)
cb0e1eb2b1 feat(falukant): refactor character ID resolution for political office checks
All checks were successful
Deploy to production / deploy (push) Successful in 2m46s
- Introduced a new method `resolveCharacterIdForOfficeChecks` to robustly resolve character IDs based on user references, improving the reliability of political office queries.
- Updated `getHighestPoliticalOfficeInfo` and `getHighestOfficeAnyInfo` methods to utilize the new character ID resolution logic, enhancing code clarity and reducing redundancy.
- Ensured that character ID retrieval handles both direct and fallback user references, streamlining the process of fetching political office information.
2026-04-15 15:47:31 +02:00
Torsten Schulz (local)
95ea6336b7 feat(falukant): add political office catalog feature and localization updates
All checks were successful
Deploy to production / deploy (push) Successful in 2m52s
- Implemented the `getPoliticalOfficeCatalog` method in FalukantService to retrieve available political offices and their prerequisites based on user eligibility.
- Updated FalukantController and FalukantRouter to include a new endpoint for accessing the political office catalog.
- Enhanced PoliticsView component to display the list of political offices, including their details and application eligibility.
- Added localization entries for the new offices tab in both German and English, improving user experience and accessibility.
2026-04-15 15:30:24 +02:00
Torsten Schulz (local)
7b4c9a0b1c feat(vocabLesson): add detailed review section for failed chapter exams
All checks were successful
Deploy to production / deploy (push) Successful in 2m53s
- Introduced a new review section that displays questions, user answers, and correct answers for failed chapter exams.
- Added localization entries for the review section in multiple languages, enhancing user experience and understanding of mistakes.
- Implemented logic to format and present failed exam details, improving feedback for learners.
2026-04-15 14:02:34 +02:00
Torsten Schulz (local)
cc791501c9 feat(bisaya-course): enhance lesson content for temporal grammar and exercises
All checks were successful
Deploy to production / deploy (push) Successful in 2m52s
- Updated learning goals to emphasize distinguishing between past, present, and future in everyday sentences.
- Revised core patterns to include specific examples for each tense, improving clarity and context for learners.
- Expanded grammar focus sections to provide detailed explanations of time markers and their usage.
- Introduced new exercises for practicing grammar focus, including multiple-choice questions and transformation tasks, enhancing student engagement with temporal aspects of the language.
- Updated speaking prompts and practical tasks to reflect the new content structure, ensuring a comprehensive learning experience.
2026-04-15 13:25:15 +02:00
Torsten Schulz (local)
feda7d90f2 feat(bisaya-course): enhance grammar focus and drills for lesson content
All checks were successful
Deploy to production / deploy (push) Successful in 2m57s
- Added functions to normalize and build grammar curriculum focus entries, improving the structure and clarity of grammar lessons.
- Implemented logic to merge existing grammar focus with newly defined curriculum focus, ensuring comprehensive coverage of key grammar points.
- Introduced new drills for practicing past and future tense constructions, enhancing student engagement and understanding of temporal aspects in Bisaya.
- Updated the `createBisayaCourseContent` function to incorporate these enhancements, ensuring lessons are enriched with relevant grammar exercises.
2026-04-15 11:52:53 +02:00
Torsten Schulz (local)
ff4fbbfab1 feat(bisaya-course): update exercise content for market-related vocabulary
All checks were successful
Deploy to production / deploy (push) Successful in 2m55s
- Changed the exercise question and answer to focus on market-related scenarios, replacing previous church-related content.
- Updated the explanation to align with the new vocabulary introduced in this lesson, enhancing contextual learning for students.
2026-04-15 11:49:50 +02:00
Torsten Schulz (local)
5d16021557 feat(bisaya-course): implement core pattern extraction and merging for exercises
All checks were successful
Deploy to production / deploy (push) Successful in 2m55s
- Added functions to derive core patterns from various exercise types, including gap-fill, transformation, and multiple-choice.
- Implemented logic to merge derived core patterns with existing lesson patterns, ensuring a minimum count for effective lesson content.
- Enhanced the `createBisayaCourseContent` function to update lessons with merged core patterns and provide detailed logging based on the VOCAB_STRICT_AUDIT environment variable.
2026-04-15 11:12:18 +02:00
Torsten Schulz (local)
3500940d1c feat(bisaya-course): add new lessons for everyday conversations
All checks were successful
Deploy to production / deploy (push) Successful in 3m1s
- Introduced two new lessons: 'Alltagsgespräche - Teil 1' and 'Alltagsgespräche - Teil 2'.
- Each lesson includes learning goals, core patterns, speaking prompts, and practical tasks to enhance conversational skills in everyday contexts.
- Updated the lesson structure to support these additions, ensuring a comprehensive learning experience.
2026-04-15 11:09:14 +02:00
Torsten Schulz (local)
f841a35501 feat(bisaya-course): enhance gap-fill exercise processing and validation
All checks were successful
Deploy to production / deploy (push) Successful in 2m54s
- Added `buildCorePatternGlossLookup` to create a mapping of core patterns for improved hint sanitization.
- Implemented `sanitizeGapFillHintText` to validate and replace hints based on answer word count and gloss availability, generating fixes and warnings as needed.
- Updated `sanitizeExerciseForConsistency` to incorporate new sanitization logic for gap-fill exercises, ensuring consistent exercise data handling.
- Enhanced logging in `createBisayaCourseContent` to provide detailed feedback on exercise processing and validation outcomes.
2026-04-14 15:33:00 +02:00
Torsten Schulz (local)
78da376c5b feat(bisaya-course): add word count validation and exercise audit warnings
Some checks failed
Deploy to production / deploy (push) Has been cancelled
- Introduced a `countWords` function to calculate the number of words in a given text.
- Added `collectExerciseAuditWarnings` function to generate warnings for gap-fill exercises with potentially misleading hints.
- Updated the `createBisayaCourseContent` function to log audit warnings when the VOCAB_STRICT_AUDIT environment variable is set, enhancing the validation process for exercise content.
2026-04-14 15:30:58 +02:00
Torsten Schulz (local)
7e5a46a9bf fix(vocabLesson): improve answer validation logic for gap fill exercises
All checks were successful
Deploy to production / deploy (push) Successful in 2m56s
- Enhanced the answer validation process by introducing checks for native word and answer text, ensuring they are trimmed and compared correctly.
- Added logic to identify likely fragment mismatches based on sentence structure and word count, preventing incorrect answer submissions.
- Updated debug logging to provide clearer insights into the validation process, improving maintainability and debugging capabilities.
2026-04-14 14:57:54 +02:00
Torsten Schulz (local)
deb6f5f36c feat(falukant): add improve lover affection feature and localization updates
All checks were successful
Deploy to production / deploy (push) Successful in 2m49s
- Introduced the `improveLoverAffection` method in FalukantService to enhance relationship dynamics by allowing users to boost affection at a cost.
- Updated FalukantController and FalukantRouter to include the new endpoint for improving lover affection.
- Enhanced FamilyView component to provide a button for users to trigger the affection improvement action.
- Added localization entries for the new feature in multiple languages, ensuring clarity in user interactions regarding affection improvements.
2026-04-14 11:39:42 +02:00
Torsten Schulz (local)
26daf5fed5 feat(falukant): enhance notification handling and localization updates
All checks were successful
Deploy to production / deploy (push) Successful in 2m52s
- Updated the `enrichNotificationsWithCharacterNames` method in FalukantService to include region name enrichment and handle additional character IDs.
- Introduced a new `serializeNotificationForClient` function to format notifications for the client, ensuring all relevant data is included.
- Enhanced the MessagesDialog component to merge notification payloads and extract parameters more effectively, improving the clarity of displayed messages.
- Added new localization entries for director resignation risk and regional festival effects in multiple languages, ensuring comprehensive user notifications.
2026-04-14 08:06:56 +02:00
Torsten Schulz (local)
9deda3147e feat(falukant): add transport cost option to product price calculations
All checks were successful
Deploy to production / deploy (push) Successful in 2m49s
- Updated the `getProductPricesInCitiesBatch` method in FalukantService to accept an options parameter for including transport costs in price calculations.
- Modified the tax calculation logic to conditionally apply transport costs based on the new parameter, enhancing the accuracy of pricing based on regional differences.
- Adjusted the FalukantController to pass the includeTransportCosts option from the request body, improving flexibility in pricing queries.
2026-04-13 16:11:53 +02:00
Torsten Schulz (local)
b50d2a9a93 feat(falukant): refine tax calculation logic to handle political office exemptions
All checks were successful
Deploy to production / deploy (push) Successful in 2m41s
- Updated the tax calculation in FalukantService to ensure no exemptions are applied for political offices, streamlining the cumulative tax query.
- Enhanced the SQL query to accurately compute cumulative tax while excluding exempted ancestor levels based on office, improving overall accuracy in tax assessments.
2026-04-13 16:05:41 +02:00
Torsten Schulz (local)
f92b62e55b feat(falukant): enhance tax calculation and update messaging parameters
All checks were successful
Deploy to production / deploy (push) Successful in 2m52s
- Updated the tax calculation logic in FalukantService to include character-based checks for political offices, improving accuracy in tax assessments.
- Enhanced the MessagesDialog component to incorporate additional parameters such as satisfaction, threshold_percent, director_id, and director_character_id, providing more detailed notifications.
- Added new localization entries for director resignation risk notifications in multiple languages, ensuring users receive clear and contextual information regarding director dynamics.
2026-04-13 15:58:30 +02:00
Torsten Schulz (local)
86e14a875d feat(falukant): add scandalExtraDailyPct field and update related components
All checks were successful
Deploy to production / deploy (push) Successful in 2m59s
- Introduced a new field `scandalExtraDailyPct` in the relationship state model to track additional scandal risk per day, with validation constraints.
- Updated the FalukantService to include the new field in relevant calculations and data handling.
- Enhanced the frontend components, including RevenueSection and FamilyView, to display the scandal risk information and updated price calculations.
- Added localization entries for the new field in multiple languages to ensure clarity for users.
2026-04-13 15:31:47 +02:00
Torsten Schulz (local)
b0624422b8 feat(falukant): update production counting logic and enhance localization
All checks were successful
Deploy to production / deploy (push) Successful in 2m55s
- Modified the production counting logic in FalukantService to count each completed production directly from the falukant_log.production table, simplifying the query structure.
- Added a new localization entry for "scoreHowToRaise" in Cebuano, German, English, Spanish, and French to provide users with clear guidance on how to improve their score through various factors.
- Updated the OverviewView component to display the new score improvement information, enhancing user experience and understanding of scoring dynamics.
2026-04-13 13:59:37 +02:00
Torsten Schulz (local)
162e908c1c feat(falukant): add 'lover_breakup_risk_high' event handling and localization updates
All checks were successful
Deploy to production / deploy (push) Successful in 2m52s
- Introduced a new event type for high breakup risk in relationships, including a sample payload for integration.
- Updated the MessagesDialog component to handle new parameters related to breakup risk.
- Added translations for the new event in Cebuano, German, English, Spanish, and French to enhance user understanding of relationship dynamics.
- Enhanced documentation to reflect the new notification structure for high breakup risk events.
2026-04-13 12:21:24 +02:00
Torsten Schulz (local)
1bce855b3a feat(falukant): enhance marriage actions with production delay handling and UI updates
All checks were successful
Deploy to production / deploy (push) Successful in 2m58s
- Implemented logic in FalukantService to delay running productions when spending time with a spouse, updating the start timestamp accordingly.
- Enhanced the response from the spend time API to include details about delayed productions and household order changes.
- Updated FamilyView component to display the cost of marriage actions and added a help section detailing the effects of actions, including production delays.
- Added new translations for action effects in multiple languages to improve user understanding of the marriage actions.
2026-04-13 12:09:17 +02:00
Torsten Schulz (local)
ba72d4fb74 feat(i18n): update 'acknowledgeHelp' translations and enhance UI interaction
All checks were successful
Deploy to production / deploy (push) Successful in 2m56s
- Added new translations for 'maintenance', 'affection', and 'breakup' in Cebuano, German, English, and Spanish to provide users with comprehensive context regarding relationship dynamics.
- Refactored the FamilyView component to utilize a details element for the 'acknowledgeHelp' section, improving user interaction and readability.
- Enhanced styling for the summary element to improve visibility and user engagement.
2026-04-13 11:41:17 +02:00
Torsten Schulz (local)
e2f4b255ff removed double question
All checks were successful
Deploy to production / deploy (push) Successful in 2m59s
2026-04-13 11:27:06 +02:00
Torsten Schulz (local)
60ef98283f fix(falukant): validate office term length and adjust term end calculation
All checks were successful
Deploy to production / deploy (push) Successful in 2m51s
- Added validation for office term length to ensure it is a positive finite number, throwing an error for invalid values.
- Updated the calculation of the term end date to use the term length in days instead of years, aligning with the expected data format.
2026-04-10 16:08:50 +02:00
Torsten Schulz (local)
c6419c6c34 feat(vocab): implement pagination and localization for vocabulary dictionaries
All checks were successful
Deploy to production / deploy (push) Successful in 3m2s
- Added pagination functionality to the vocabulary dictionary views, allowing users to navigate through results efficiently.
- Introduced a new method `_parseDictionaryPaging` in `VocabService` to handle pagination parameters.
- Updated `getLanguageDictionary` and `getCourseDictionary` methods to return pagination details alongside results.
- Enhanced the `VocabDictionaryView` component with pagination controls and updated UI for better user experience.
- Added localization entries for pagination in Cebuano, German, English, Spanish, and French, ensuring a consistent user experience across languages.
2026-04-10 14:35:50 +02:00
Torsten Schulz (local)
f46c864bbc feat(vocab-lesson): implement pagination for vocabulary overview in lesson view
All checks were successful
Deploy to production / deploy (push) Successful in 2m59s
- Added pagination functionality to the vocabulary overview in the VocabLessonView component, allowing users to navigate through vocabulary items more efficiently.
- Introduced new localization entries for pagination controls in Cebuano, German, English, Spanish, and French, ensuring a consistent user experience across languages.
- Enhanced the UI with buttons for previous and next navigation, improving accessibility and usability of the vocabulary list.
2026-04-10 14:26:01 +02:00
Torsten Schulz (local)
545314e905 feat(i18n, frontend): enhance course planning with optional steps and localization updates
All checks were successful
Deploy to production / deploy (push) Successful in 2m52s
- Added a new section for optional learning steps in the course planning UI, allowing users to engage with additional content when no mandatory tasks are due.
- Updated localization files for Cebuano, German, English, Spanish, and French to reflect changes in course planning instructions and titles, ensuring clarity and consistency across languages.
- Improved pedagogical logic for lesson recommendations, focusing on cognitive load and spaced repetition principles to enhance user learning experience.
2026-04-10 13:33:25 +02:00
Torsten Schulz (local)
d17c8a341d feat(vocab): add language and course dictionary endpoints and UI components
All checks were successful
Deploy to production / deploy (push) Successful in 2m53s
- Implemented `getLanguageDictionary` and `getCourseDictionary` methods in the VocabService to retrieve vocabulary entries filtered by search terms.
- Updated VocabController and vocabRouter to include new routes for accessing language and course dictionaries.
- Enhanced frontend components to navigate to the new dictionary views, including buttons in VocabCourseView and VocabLanguageView.
- Added localization entries for the dictionary feature in multiple languages, ensuring a consistent user experience across the platform.
2026-04-10 13:08:18 +02:00
Torsten Schulz (local)
9582e7b900 feat(i18n, frontend): add 'acknowledgeHelp' translations and UI enhancements
All checks were successful
Deploy to production / deploy (push) Successful in 2m56s
- Introduced 'acknowledgeHelp' translations in Cebuano, German, English, and Spanish to provide users with context about the implications of acknowledging relationships.
- Updated the FamilyView component to display the new 'acknowledgeHelp' information, enhancing user understanding of relationship dynamics.
- Improved styling for the acknowledgment help section to ensure clarity and visibility within the user interface.
2026-04-10 10:29:20 +02:00
Torsten Schulz (local)
7e0821c96b feat(falukant): enhance reputation handling in character checks
All checks were successful
Deploy to production / deploy (push) Successful in 2m52s
- Added the 'reputation' attribute to the character query in FalukantService, improving data retrieval for nobility requirements.
- Refactored the `checkReputationRequirement` method to streamline reputation checks, ensuring it handles undefined or null values more effectively, enhancing reliability in user character evaluations.
2026-04-10 08:45:54 +02:00
Torsten Schulz (local)
3450bac99c feat(falukant): enhance notification handling and parameter extraction in MessagesDialog
All checks were successful
Deploy to production / deploy (push) Successful in 2m51s
- Introduced a new method `mergeNotificationPayload` to consolidate top-level fields with embedded JSON from notifications, improving data handling.
- Updated `formatBody` and `extractParams` methods to utilize the merged payload, ensuring accurate extraction of parameters from notifications.
- Enhanced parameter extraction logic to handle various notification structures, improving clarity and consistency in displayed information.
2026-04-10 08:41:53 +02:00
Torsten Schulz (local)
1dfa49191f feat(vocab-lesson): enhance exercise navigation and randomization logic
All checks were successful
Deploy to production / deploy (push) Successful in 2m57s
- Improved the exercise progression logic to automatically advance only on correct answers, ensuring better user feedback during learning.
- Added a new method to shuffle multiple-choice options display order when distractor replacement is not applicable, enhancing the exercise experience.
- Updated the build process for randomized options to include both randomization from a pool and display order shuffling, improving overall engagement and learning outcomes.
2026-04-09 17:57:51 +02:00
Torsten Schulz (local)
5cbd6d06b1 feat(bisaya-course): refine exercises and instructions for improved learning experience
All checks were successful
Deploy to production / deploy (push) Successful in 2m54s
- Updated exercise titles and instructions to enhance clarity and focus on specific learning objectives.
- Modified question data and answer options to better align with the lesson's vocabulary and context.
- Improved explanations to provide clearer guidance on language usage and learning goals, fostering better comprehension for learners.
2026-04-09 17:49:50 +02:00
Torsten Schulz (local)
2b66f76dd9 feat(bisaya-course): add new exercises to enhance Bisaya language learning
All checks were successful
Deploy to production / deploy (push) Successful in 2m52s
- Introduced multiple-choice and transformation exercises focused on common phrases and vocabulary, including translations and contextual usage.
- Expanded the curriculum with practical tasks that encourage conversational skills and comprehension in various scenarios.
- Enhanced existing exercises with clear instructions and explanations to improve user engagement and learning outcomes.
2026-04-09 17:23:20 +02:00
Torsten Schulz (local)
204b7aed04 feat(bisaya-course): expand 'Haus & Familie' lesson with new vocabulary and exercises
All checks were successful
Deploy to production / deploy (push) Successful in 2m52s
- Added comprehensive learning goals, core patterns, grammar focus, speaking prompts, and practical tasks for the 'Haus & Familie' lesson, enhancing the curriculum for better language acquisition.
- Updated exercise content to include multiple-choice questions and gap-fill tasks related to household vocabulary, improving user engagement and learning outcomes.
- Enhanced localization for the lesson description and vocabulary, ensuring clarity and consistency across the learning platform.
2026-04-09 17:19:02 +02:00
Torsten Schulz (local)
f5a5639e97 feat(seo, i18n): update SEO metadata and enhance localization across multiple languages
All checks were successful
Deploy to production / deploy (push) Successful in 2m47s
- Revised SEO titles and descriptions for improved clarity and relevance, emphasizing the platform's features such as community, chat, forums, and browser games.
- Updated localization files for Cebuano, German, English, Spanish, and French to include new translations and enhance existing content, ensuring a consistent user experience across languages.
- Added a new story teaser in the home view to better highlight the platform's offerings without requiring downloads.
2026-04-09 09:44:53 +02:00
Torsten Schulz (local)
641a2134cb fix(falukant): improve product pricing logic and update localization for office notifications
All checks were successful
Deploy to production / deploy (push) Successful in 2m44s
- Refactored the product pricing logic in `falukantService.js` to ensure accurate regional pricing calculations based on client values and server data.
- Added new notification translations for "office filled" in multiple languages (Cebuano, German, English, Spanish, French) to enhance user experience and clarity in notifications.
2026-04-09 09:21:47 +02:00
Torsten Schulz (local)
1118a691b9 feat(falukant): enhance product pricing and nobility advancement features
All checks were successful
Deploy to production / deploy (push) Successful in 2m52s
- Updated the `getAllProductPricesInRegion` method in `FalukantService` to accept additional parameters for network worth and branch ID, improving pricing calculations based on user branches.
- Enhanced the nobility advancement logic in `NobilityView` to display unmet requirements clearly, providing users with better feedback on advancement conditions.
- Refactored the revenue calculation in `ProductionSection` to utilize a cached product prices object, optimizing performance and reducing redundant API calls.
- Updated localization files to include new translations for attack sources across multiple languages, enhancing the user experience for diverse audiences.
- Removed obsolete C++ worker references and streamlined the retention logic for production logs, ensuring efficient data management.
2026-04-09 09:08:32 +02:00
Torsten Schulz (local)
360bb59a4e feat(user): add certificate production tracking and update localization
All checks were successful
Deploy to production / deploy (push) Successful in 2m50s
- Introduced a new field `certificateProductionsCountSince` in the `FalukantUser` model to track the date from which production logs are counted for certificate requirements.
- Updated the `FalukantService` to utilize the new field for calculating completed productions since the specified date.
- Enhanced the UI to display the count of productions since the last promotion, with corresponding translations added for multiple languages including Cebuano, German, English, Spanish, and French.
- Implemented a method to delete old production logs, ensuring efficient data management while maintaining necessary historical records for certificate calculations.
2026-04-09 08:19:19 +02:00
Torsten Schulz (local)
f7030bbabe feat(i18n): add French language support and enhance localization
All checks were successful
Deploy to production / deploy (push) Successful in 2m48s
- Introduced French as a supported language across the application, updating locale files and adding translations for various components.
- Enhanced language handling logic to accommodate French, ensuring proper detection and fallback mechanisms.
- Updated UI elements to include French language options, improving accessibility for French-speaking users.
- Refactored SEO handling to include French in hreflang links, enhancing search engine indexing for multilingual content.
- Added new scripts for managing French translations and ensuring consistency across language files.
2026-04-07 18:04:03 +02:00
Torsten Schulz (local)
f715c6125d feat(i18n): implement deep merging for locale chunks and enhance Cebuano translations
All checks were successful
Deploy to production / deploy (push) Successful in 2m48s
- Introduced a deep merge function to combine locale chunks, improving the handling of language data for Cebuano.
- Updated Cebuano locale files with comprehensive translations, including new sections for admin, social network, and settings.
- Enhanced existing translations for clarity and consistency across various components, ensuring a better user experience.
- Added new fields in the settings and profile sections to capture more user attributes, improving personalization options.
2026-04-07 16:23:11 +02:00
Torsten Schulz (local)
e9f0f6c133 fix(i18n): update Cebuano language label in AppHeader.vue
All checks were successful
Deploy to production / deploy (push) Successful in 2m53s
- Changed the native label for Cebuano from 'Sinugboanon' to 'Binisaya' in the language options of AppHeader.vue for improved accuracy and user recognition.
2026-04-07 15:59:07 +02:00
Torsten Schulz (local)
ae635b9c16 refactor(i18n): improve language handling and UI updates
All checks were successful
Deploy to production / deploy (push) Successful in 2m49s
- Enhanced language setting logic in App.vue and AppHeader.vue to support reactive updates without page reloads.
- Updated language options in AppHeader.vue to use native labels for improved clarity.
- Introduced a utility function in i18n/index.js to streamline locale updates, ensuring consistent language handling across the application.
2026-04-07 15:52:16 +02:00
Torsten Schulz (local)
c5b8860605 feat(seo): enhance multilingual support and SEO handling
All checks were successful
Deploy to production / deploy (push) Successful in 2m46s
- Added support for multiple languages in the frontend, including English, Spanish, and Cebuano, improving accessibility for a broader audience.
- Implemented hreflang links for better SEO performance, ensuring search engines can correctly index language-specific content.
- Updated SEO metadata handling to utilize internationalization keys, enhancing the clarity and relevance of page titles and descriptions.
- Refactored SEO utility functions to streamline the management of OpenGraph and hreflang attributes, improving maintainability and performance.
2026-04-07 15:43:16 +02:00
Torsten Schulz (local)
ebb2283646 refactor(exercises): standardize answer language handling across exercise scripts
All checks were successful
Deploy to production / deploy (push) Successful in 2m48s
- Introduced a mechanism to infer answer language based on question phrasing in multiple exercise scripts, enhancing consistency in exercise data.
- Updated question formats to clarify the intent of exercises, improving user understanding and engagement.
- Streamlined the code for better maintainability and clarity in exercise generation processes.
2026-04-07 14:32:44 +02:00
Torsten Schulz (local)
160c9dafb2 refactor(vocab): enhance token weight handling in VocabLessonView
All checks were successful
Deploy to production / deploy (push) Successful in 2m45s
- Introduced a new mechanism for calculating token weights based on core patterns and important vocabulary, improving the accuracy of target-gloss pair orientation.
- Replaced the previous hint logic with a more robust token-based scoring system, enhancing the quality of vocabulary item representation.
- Streamlined the code for better maintainability and clarity in vocabulary preparation processes.
2026-04-07 11:32:14 +02:00
Torsten Schulz (local)
ee338c0e49 refactor(vocab): update vocabulary label handling in VocabLessonView
All checks were successful
Deploy to production / deploy (push) Successful in 2m44s
- Replaced computed properties for current vocabulary preparation labels with direct references to new properties, enhancing clarity and maintainability.
- Improved the logic for populating native and target hints, ensuring accurate representation of vocabulary items.
- Removed outdated label swapping logic, streamlining the code and focusing on course-specific language names for better user experience.
2026-04-07 11:22:22 +02:00
Torsten Schulz (local)
fe9322c098 feat(vocab): attach language names to course data in VocabService
All checks were successful
Deploy to production / deploy (push) Successful in 2m49s
- Added a method to attach language names to course rows, enhancing the clarity and localization of vocabulary lessons.
- This update improves the overall user experience by ensuring accurate representation of language names in course data.
2026-04-07 11:13:38 +02:00
Torsten Schulz (local)
3a1d83f20c feat(vocab): add course language name handling in VocabLessonView
All checks were successful
Deploy to production / deploy (push) Successful in 2m48s
- Introduced new properties for course language names to enhance clarity in vocabulary preparation.
- Implemented a method to load course language names from the API, improving localization and user experience.
- Updated label logic to utilize course-specific language names, ensuring accurate representation in vocabulary lessons.
2026-04-07 10:29:28 +02:00
Torsten Schulz (local)
e649236e39 feat(vocab): refine vocabulary label logic in VocabLessonView
All checks were successful
Deploy to production / deploy (push) Successful in 2m48s
- Introduced computed properties for current vocabulary preparation labels, enhancing clarity and localization.
- Updated label handling to dynamically adjust based on the context of target and gloss items, improving user experience.
- Enhanced the logic for determining when to swap labels, ensuring accurate representation of vocabulary items for better learning outcomes.
2026-04-07 10:22:36 +02:00
Torsten Schulz (local)
8f6f06caf0 feat(vocab): enhance vocabulary label handling in VocabLessonView
All checks were successful
Deploy to production / deploy (push) Successful in 2m50s
- Refactored VocabLessonView to utilize computed properties for vocabulary preparation labels, improving localization and maintainability.
- Updated the logic for target and gloss labels to prioritize course-specific language names, enhancing user experience and clarity in vocabulary presentation.
- Improved the orientation logic for target-gloss pairs to ensure accurate representation of vocabulary items, contributing to better learning outcomes.
2026-04-07 10:07:54 +02:00
Torsten Schulz (local)
62503191d4 feat(vocab): improve vocabulary preparation logic in VocabLessonView
All checks were successful
Deploy to production / deploy (push) Successful in 2m44s
- Refactored prepItems method to ensure only valid target-gloss pairs are included, enhancing the quality of vocabulary preparation.
- Introduced a mechanism to limit the output to a maximum of 30 unique pairs, improving the compactness of the preparation round.
- Enhanced handling of core patterns and extracted vocabulary to prioritize high-quality learning materials.
2026-04-07 09:58:33 +02:00
Torsten Schulz (local)
86dfb0d859 feat(vocab): update course today plan localization and UI logic
All checks were successful
Deploy to production / deploy (push) Successful in 2m49s
- Added new localization strings for scenarios with no due reviews in German, English, and Spanish, enhancing user guidance.
- Updated VocabCourseView to conditionally display the appropriate introduction message based on the presence of due reviews, improving clarity in the course flow.
2026-04-07 09:55:01 +02:00
Torsten Schulz (local)
f504a5d597 feat(vocab): enhance feedback acknowledgment and localization in vocabulary lessons
All checks were successful
Deploy to production / deploy (push) Successful in 2m41s
- Added acknowledgment messages for exercise reinforcement in German, English, and Spanish localization files to improve user guidance.
- Updated VocabLessonView to include a feedback acknowledgment button, enhancing user interaction after answering questions.
- Implemented logic to track feedback acknowledgment state, improving the flow of lesson reviews and user experience.
2026-04-07 09:47:45 +02:00
Torsten Schulz (local)
3cc5f63610 feat(vocab): add lesson review functionality and update navigation
All checks were successful
Deploy to production / deploy (push) Successful in 2m46s
- Introduced VocabLessonReviewView for reviewing lessons within the vocabulary course structure.
- Updated routing in socialRoutes.js to include a new path for lesson reviews, ensuring authenticated access.
- Modified VocabCourseView to change the lesson button click behavior to navigate to the review view, enhancing user flow.
- Added a new method to handle lesson review navigation, improving the overall user experience in the vocabulary section.
2026-04-07 09:38:00 +02:00
Torsten Schulz (local)
e17f0cdce0 feat(vocab): enhance vocabulary exercises and localization support
All checks were successful
Deploy to production / deploy (push) Successful in 2m51s
- Updated core patterns in BISAYA_PHASE5_DIDACTICS to include gloss translations for better understanding.
- Refactored vocabulary exercise generation in update-food-care-exercises.js to improve randomization and user engagement.
- Added new exercise types and improved question structures for vocabulary lessons, enhancing the learning experience.
- Enhanced localization files for German, English, and Spanish to support new exercise features and improve user guidance.
- Updated VocabLessonView to incorporate sequential navigation for exercises, providing a more structured learning flow.
2026-04-07 09:09:43 +02:00
Torsten Schulz (local)
d192bcae2d refactor(vocabService, VocabLessonView): unify answer language classification for multiple-choice questions
All checks were successful
Deploy to production / deploy (push) Successful in 2m51s
- Renamed and refactored the method for classifying answer languages in multiple-choice questions from _classifyMcQuestionSide to _resolveMcAnswerSide for clarity and consistency.
- Updated the logic to classify answer languages based on questionData.answerLanguage and answerLanguageId, improving accuracy in determining the correct language context.
- Adjusted VocabLessonView to utilize the new classification method, ensuring alignment with backend changes and enhancing the handling of distractor options based on answer language.
- Enhanced documentation to clarify the expected input and output for the new method, improving maintainability and understanding of the codebase.
2026-04-07 08:34:19 +02:00
Torsten Schulz (local)
07ab648143 feat(political-office): enhance political office benefits and salary computation
All checks were successful
Deploy to production / deploy (push) Successful in 3m6s
- Added a new hierarchyLevel field to PoliticalOfficeType for better categorization of political roles.
- Updated computePoliticalDailySalaryPayout function to incorporate hierarchy level in salary calculations, allowing for more dynamic salary adjustments based on office rank.
- Modified SQL scripts to reflect changes in political office benefits, ensuring compatibility with the new salary structure.
- Enhanced localization files to support updated benefit descriptions and salary formats across multiple languages.
- Improved UI components to display the new salary calculations and benefits accurately in the PoliticsView.
2026-04-02 16:49:18 +02:00
Torsten Schulz (local)
e063df5cbe refactor(PoliticsView): enhance UI layout and styling for political information display
All checks were successful
Deploy to production / deploy (push) Successful in 2m52s
- Improved the layout of the political information section by introducing a structured row format for better readability.
- Updated CSS styles to enhance the visual hierarchy and alignment of labels and values, ensuring a more user-friendly interface.
- Adjusted the display of benefits and term end information to provide clearer context and organization within the PoliticsView component.
2026-04-02 16:09:52 +02:00
Torsten Schulz (local)
56be4b76c0 feat(political-benefits): implement political powers and benefits system
All checks were successful
Deploy to production / deploy (push) Successful in 3m3s
- Added new political powers and benefits functionalities, including reputation ticks, tax jurisdiction management, and appointment capabilities.
- Introduced a new job for periodic reputation updates and created necessary database tables for tracking political benefits.
- Enhanced the FalukantController and services to support new endpoints for managing political powers and appointments.
- Updated localization files to reflect new features and improve user experience across multiple languages.
- Modified the UI to display new political powers and benefits, ensuring accurate representation in the PoliticsView.
2026-04-02 16:00:29 +02:00
Torsten Schulz (local)
5d06d97737 feat(politics): add new political office benefits and enhance database migration
All checks were successful
Deploy to production / deploy (push) Successful in 2m54s
- Introduced new benefits including 'reputation_periodic', 'appoint_politicians', 'set_regional_tax', 'free_lover_slots', 'guard_protection', and 'court_immunity' to the political office system.
- Updated database migration to add and remove the 'last_political_daily_salary_on' column using SQL queries for better performance.
- Enhanced localization files for multiple languages to support new benefits, improving user experience across the application.
- Updated UI components to display new benefits correctly in the PoliticsView, ensuring accurate representation of political office functionalities.
2026-04-02 15:27:05 +02:00
Torsten Schulz (local)
49713d957d feat(dashboard): enhance widget availability and initialization
All checks were successful
Deploy to production / deploy (push) Successful in 3m2s
- Updated `getAvailableWidgets` method in `DashboardService` to merge default widget types with database entries, ensuring immediate visibility of new widgets post-deployment.
- Introduced `DASHBOARD_WIDGET_TYPE_DEFAULTS` in `initializeWidgetTypes` for canonical widget types, facilitating API merging when database entries are absent.
- Modified `StatusBar.vue` to utilize a computed property for quick access children, improving menu item handling and visibility based on user context.
- Enhanced `LoggedInView.vue` to dynamically return localized widget labels, improving user experience with accurate translations.
2026-04-02 15:19:08 +02:00
Torsten Schulz (local)
5fcd55be43 feat(vocab): add dashboard learning summary and related endpoints
All checks were successful
Deploy to production / deploy (push) Successful in 2m52s
- Introduced `getDashboardLearningSummary` method in `VocabService` to provide a compact overview of enrolled courses and current lessons for users.
- Updated `vocabController` to include a new route for the dashboard widget, allowing users to access their learning summary.
- Enhanced `vocabRouter` to route requests for the new dashboard widget endpoint.
- Added localization support for the new dashboard features across multiple languages, improving user engagement and accessibility.
- Updated UI components to integrate the new dashboard widget, ensuring a seamless user experience.
2026-04-02 15:06:50 +02:00
Torsten Schulz (local)
77e6f8d3e8 feat(vocab): enhance vocabService and VocabLessonView for improved exercise handling
All checks were successful
Deploy to production / deploy (push) Successful in 2m58s
- Updated VocabService to include validation for synthetic exercise IDs and added methods for generating multiple-choice exercises based on chapter lexemes.
- Implemented a seeded shuffle function to randomize distractor options in multiple-choice questions, ensuring varied user experiences.
- Modified VocabLessonView to adjust target calculations for lesson goals and unlock attempts, increasing the maximum limits for better flexibility in user progress tracking.
2026-04-02 14:23:21 +02:00
Torsten Schulz (local)
3d2ccd620a feat(localization): enhance course progress and review messaging across multiple languages
All checks were successful
Deploy to production / deploy (push) Successful in 2m52s
- Added new confirmation titles and messages for resetting and marking lessons as complete in admin and user interfaces.
- Expanded course flow and review scheduling messages to improve clarity and user guidance in Cebuano, German, Spanish, and English.
- Introduced a new section in the VocabCourseView to display today's recommended steps for users, enhancing the learning experience.
- Updated localization files to ensure consistent messaging and improved user engagement across all supported languages.
2026-04-02 13:49:59 +02:00
Torsten Schulz (local)
edbf22ac5b extended admin tool for finished lessons
All checks were successful
Deploy to production / deploy (push) Successful in 2m54s
2026-04-02 13:32:13 +02:00
Torsten Schulz (local)
9d663e4f2b feat(vocab): enhance lesson progress tracking and review scheduling
All checks were successful
Deploy to production / deploy (push) Successful in 2m48s
- Updated VocabService to include lessonId and lessonNumber in progress data, improving tracking accuracy.
- Modified getLessonProgress and lastProgressTouch methods to accept lesson parameters, enhancing flexibility in progress retrieval.
- Implemented formatReviewBadgeSchedule method to manage review scheduling notifications, providing clearer user feedback.
- Updated VocabCourseView to reflect changes in lesson progress handling, ensuring accurate display of review statuses and due dates.
- Expanded localization for review scheduling messages across multiple languages, enhancing user experience.
2026-04-02 13:27:24 +02:00
Torsten Schulz (local)
153914d5d2 feat(bisaya-course): add family vocabulary and exercises for relatives
All checks were successful
Deploy to production / deploy (push) Successful in 2m57s
- Introduced new content for the 'Familie - Verwandte & Stieffamilie' lesson, including learning goals, core patterns, grammar focus, speaking prompts, and practical tasks.
- Added multiple-choice and gap-fill exercises related to family vocabulary, enhancing the learning experience for users.
- Updated course content to reflect the new lesson and exercises, ensuring comprehensive coverage of family-related terms in Bisaya.
2026-04-02 09:31:46 +02:00
Torsten Schulz (local)
2272db7f91 feat(admin): add user vocab course management functionality
All checks were successful
Deploy to production / deploy (push) Successful in 2m59s
- Implemented `getUserVocabCourses` and `getVocabCourseForAdmin` methods in `AdminController` to allow admins to retrieve enrolled vocab courses for users and specific course details, respectively.
- Updated `adminRouter` to include new routes for accessing user vocab courses and course details.
- Enhanced `AdminService` with methods to list user-enrolled vocab courses and retrieve course information with lessons, ensuring proper access control.
- Improved `VocabService` to support the new functionalities, including attaching language names to course data.
- Updated UI components in `UsersView` to reflect changes, including error handling and loading states for course retrieval, along with localization updates for new features.
2026-04-02 09:21:52 +02:00
Torsten Schulz (local)
b3c8e8e210 refactor(ui): enhance AppContent layout for improved routing and styling
All checks were successful
Deploy to production / deploy (push) Successful in 3m0s
- Wrapped the router-view in a new div to manage layout and styling more effectively.
- Updated CSS to ensure proper height management and scrolling behavior, allowing for better content display.
- Added comments to clarify the purpose of layout adjustments and ensure maintainability.
2026-04-02 08:56:24 +02:00
Torsten Schulz (local)
02f3e82987 fix(ui): update styles and localization in UsersView and AppShell
All checks were successful
Deploy to production / deploy (push) Successful in 3m1s
- Added styles to ensure the app shell content occupies only the remaining space, preventing overflow issues.
- Updated the header in UsersView to reflect accurate localization for user administration.
2026-04-02 08:48:06 +02:00
Torsten Schulz (local)
c3b2c60362 feat(vocab): implement user vocab lesson progress reset functionality
All checks were successful
Deploy to production / deploy (push) Successful in 2m59s
- Added `resetUserVocabLessonProgress` method in `AdminController` to allow admins to reset a user's progress for a specific vocab lesson.
- Introduced corresponding route in `adminRouter` for the new reset functionality.
- Enhanced `VocabService` with methods to purge lesson progress for users, ensuring that only the specified lesson's progress is affected.
- Updated UI components in `UsersView` to facilitate the selection of courses and lessons for resetting progress, including confirmation dialogs and loading states.
- Added localization support for the new reset functionality across multiple languages.
- Implemented reset functionality in `VocabLessonView` for users to reset their own lesson progress.
2026-04-02 08:25:56 +02:00
Torsten Schulz (local)
13534498fa feat(user): enhance email handling with validation and normalization functions
All checks were successful
Deploy to production / deploy (push) Successful in 2m56s
- Introduced `looksLikePlausibleEmail` to validate email format, ensuring only plausible addresses are processed.
- Added `normalizeEmailCandidate` to standardize email input, returning null for invalid formats.
- Updated `decodeEncryptedBlob` to utilize normalization functions for both UTF-8 and hex formats, improving email decryption reliability.
- Adjusted `SettingsService` to ensure email is set after merging adult access state, maintaining data integrity.
2026-04-02 08:07:25 +02:00
Torsten Schulz (local)
3fb4fb92c6 refactor(ui): update layout styles for AppContent and AccountView components
Some checks failed
Deploy to production / deploy (push) Has been cancelled
- Changed layout from grid to flex in AccountView for improved responsiveness and alignment.
- Adjusted flex properties in AppContent to ensure proper scrolling behavior and height management.
- Enhanced styling for account settings panel to support better content overflow handling and visual consistency.
2026-04-02 08:05:32 +02:00
Torsten Schulz (local)
6d9d69dc10 feat(localization): expand language support and enhance UI for user settings
All checks were successful
Deploy to production / deploy (push) Successful in 3m0s
- Added support for additional UI locales including Cebuano and Spanish, improving accessibility for a broader user base.
- Updated language selection components in the AppHeader and SettingsWidget to reflect new language options, enhancing user experience.
- Enhanced localization of various UI elements across components, ensuring consistent language representation and improved user engagement.
- Implemented logic to synchronize user language preferences with backend settings, providing a seamless experience when changing languages.
2026-04-02 07:54:44 +02:00
Torsten Schulz (local)
ac5d436a36 feat(vocab): implement scheduled review management in VocabService and UI updates
All checks were successful
Deploy to production / deploy (push) Successful in 2m56s
- Added methods in VocabService to handle scheduled review states, including normalization of review dates and management of review stages.
- Enhanced lesson state management to support review scheduling, improving the learning process for users.
- Updated VocabCourseView and VocabLessonView to display review statuses and due dates, providing clearer feedback on lesson progress and review requirements.
- Introduced new UI elements to indicate review status, enhancing user engagement and understanding of lesson timelines.
2026-04-01 16:03:42 +02:00
Torsten Schulz (local)
3ff8e4fc40 refactor(bisaya-course): enhance pedagogy logic for German lessons
All checks were successful
Deploy to production / deploy (push) Successful in 2m57s
- Updated the `getGermanForBisayaLessonPedagogy` function to include lesson titles for improved didactic mode determination, specifically adding support for 'contrast_training'.
- Modified multiple scripts to utilize the updated pedagogy function, ensuring consistent application of the new logic across various course phases.
- Enhanced the VocabService to recognize and handle 'contrast_training' as a didactic mode, improving lesson management and user experience.
- Updated UI components to reflect the new didactic mode, ensuring clarity in lesson presentation.
2026-04-01 15:52:53 +02:00
Torsten Schulz (local)
10fc78e81d feat(falukant): implement score threshold logic and enhance UI feedback for certificate progression
All checks were successful
Deploy to production / deploy (push) Successful in 3m3s
- Added a new function to calculate score thresholds based on certificate levels, improving the logic for determining promotion eligibility.
- Updated the FalukantService to include new properties for score and requirement checks, enhancing the decision-making process for certificate readiness.
- Enhanced the OverviewView component to display detailed hints and states regarding certificate progression, providing users with clearer feedback on their status.
- Localized new strings in multiple languages to support the updated UI elements and hints, improving user experience across different languages.
2026-04-01 15:47:11 +02:00
Torsten Schulz (local)
d39cea2c01 refactor(vocab): unify text normalization across VocabService and VocabPracticeDialog
All checks were successful
Deploy to production / deploy (push) Successful in 3m3s
- Enhanced text normalization methods in VocabService and VocabPracticeDialog to include normalization and improved regex for punctuation and symbols, ensuring consistent text handling.
- This update streamlines the normalization process, contributing to better user input processing and overall application reliability.
2026-04-01 15:24:22 +02:00
Torsten Schulz (local)
d38231b52c refactor(vocab): improve text normalization consistency in VocabService and VocabLessonView
All checks were successful
Deploy to production / deploy (push) Successful in 2m56s
- Enhanced text normalization methods in both VocabService and VocabLessonView to include an additional trim operation, ensuring leading and trailing spaces are removed.
- This change improves the consistency and accuracy of text handling across the application, contributing to better user input processing.
2026-04-01 15:09:15 +02:00
Torsten Schulz (local)
7fee9e12d4 refactor(vocab): standardize text normalization methods in VocabService and VocabLessonView
All checks were successful
Deploy to production / deploy (push) Successful in 3m1s
- Updated text normalization logic in VocabService to use a regex that replaces punctuation with spaces, improving text handling consistency.
- Refactored VocabLessonView to utilize the new normalization method for comparable text, enhancing the accuracy of user input processing.
- Ensured both components now share a unified approach to text normalization, streamlining code and improving maintainability.
2026-04-01 14:56:39 +02:00
Torsten Schulz (local)
0421b2bc00 feat(bisaya-course): expand lesson didactics with new topics and detailed grammar focus
All checks were successful
Deploy to production / deploy (push) Successful in 2m59s
- Added new lesson topics including 'Überlebenssätze - Teil 1', 'Familien-Gespräche', 'Gefühle & Zuneigung', and 'Überlebenssätze - Teil 2' to enhance the curriculum.
- Updated existing lessons with more detailed learning goals, core patterns, grammar focus, speaking prompts, and practical tasks to improve learner engagement and comprehension.
- Refactored core patterns to include glosses for better understanding of vocabulary context.
- Enhanced the structure of lesson didactics to provide a more comprehensive learning experience for users.
2026-04-01 14:30:31 +02:00
Torsten Schulz (local)
09015b4244 feat(vocab): implement repeat queue management in VocabService and VocabLessonView
All checks were successful
Deploy to production / deploy (push) Successful in 2m59s
- Added a new method in VocabService to sanitize and manage a repeat queue for vocabulary items, enhancing the learning process.
- Updated VocabLessonView to incorporate repeat queue functionality, allowing for better tracking of vocabulary that needs review.
- Refactored existing logic to ensure seamless integration of repeat queue features, improving user experience during vocabulary lessons.
2026-04-01 14:06:34 +02:00
Torsten Schulz (local)
a3b820cea0 feat(vocab): enhance lesson state management and persistence in VocabLessonView
All checks were successful
Deploy to production / deploy (push) Successful in 3m20s
- Added a new JSONB field `lessonState` to the VocabCourseProgress model to store detailed lesson state information.
- Implemented methods in VocabService for sanitizing and serializing lesson state, ensuring robust data handling.
- Updated VocabLessonView to manage lesson state persistence, including local storage and server synchronization, improving user experience during vocabulary lessons.
- Introduced mechanisms for exporting and normalizing exercise answers, enhancing the accuracy of saved progress.
2026-04-01 11:16:56 +02:00
Torsten Schulz (local)
84adfeafb4 feat(vocab): add grammar explanations to VocabLessonView and enhance localization
All checks were successful
Deploy to production / deploy (push) Successful in 2m53s
- Introduced new sections for grammar explanations in VocabLessonView, providing users with contextual insights during vocabulary lessons.
- Added localization keys in German, English, and Spanish for the new grammar explanation titles and introductions, improving user guidance across languages.
- Updated styles for the grammar explanation section to enhance visual clarity and user experience.
2026-04-01 10:22:53 +02:00
Torsten Schulz (local)
b8e3732ef8 feat(i18n, vocab): enhance localization and vocabulary preparation in VocabLessonView
Some checks failed
Deploy to production / deploy (push) Has been cancelled
- Added new localization keys in German, English, and Spanish for exercise flow, progress, and learning path instructions, improving user guidance across languages.
- Updated VocabLessonView to incorporate these new keys, enhancing the clarity of vocabulary preparation steps and overall user experience during lessons.
- Refactored the layout to better present the learning path and vocabulary preparation stages, ensuring a more cohesive and informative interface.
2026-04-01 10:21:13 +02:00
Torsten Schulz (local)
8bbfd46ada feat(i18n): add new localization keys for lesson details and deepening sections
All checks were successful
Deploy to production / deploy (push) Successful in 2m46s
- Introduced new localization strings in German, English, and Spanish for lesson detail toggles and deepening sections, enhancing user experience across multiple languages.
- Updated VocabLessonView to incorporate these new localization keys, improving the clarity and accessibility of lesson information for users.
2026-04-01 10:05:37 +02:00
Torsten Schulz (local)
6d13965c76 feat(vocab): integrate fallback core patterns and enhance vocabulary display in VocabLessonView
All checks were successful
Deploy to production / deploy (push) Successful in 2m44s
- Added a new method in VocabService to merge core pattern glosses with fallback patterns, improving vocabulary clarity and consistency.
- Updated VocabLessonView to utilize the merged core patterns, ensuring a comprehensive vocabulary overview for users.
- Refactored vocabulary handling logic to enhance user experience during vocabulary lessons, including improved display of lesson vocabulary.
2026-04-01 09:58:32 +02:00
Torsten Schulz (local)
1328e4983e feat(vocab): enrich core patterns with glosses in VocabService and VocabLessonView
All checks were successful
Deploy to production / deploy (push) Successful in 2m54s
- Implemented a new method in VocabService to enhance core patterns with glosses derived from extracted vocabulary, improving the clarity of vocabulary entries.
- Updated the VocabLessonView to utilize the enriched core patterns, ensuring that glosses are displayed alongside vocabulary targets for better user comprehension.
- Refactored vocabulary preparation logic to integrate glosses seamlessly, enhancing the overall user experience during vocabulary lessons.
2026-04-01 09:40:33 +02:00
Torsten Schulz (local)
02b3636e10 feat(vocab-prep): implement enhanced vocabulary preparation steps in VocabLessonView
All checks were successful
Deploy to production / deploy (push) Successful in 2m58s
- Introduced a structured vocabulary preparation process with two review stages before engaging with the vocabulary trainer.
- Added localization support for new vocabulary preparation messages in German, English, and Spanish, improving accessibility for users.
- Updated the VocabLessonView component to display current progress and next steps during vocabulary preparation, enhancing user guidance.
- Refactored related logic to manage preparation stages and item navigation effectively.
2026-04-01 08:58:36 +02:00
Torsten Schulz (local)
0c89c48e68 feat(bisaya-course): restructure core patterns and enhance vocabulary preparation
All checks were successful
Deploy to production / deploy (push) Successful in 2m48s
- Updated core patterns in various scripts to use an object format with target phrases and glosses, improving clarity and usability for learners.
- Enhanced the VocabService to normalize core pattern entries, ensuring consistent handling of vocabulary data.
- Introduced new vocabulary preparation steps in the VocabLessonView, guiding users through active review processes before engaging with the vocabulary trainer.
- Added localization support for new vocabulary preparation hints and instructions in multiple languages, enhancing user experience across the application.
2026-04-01 08:12:57 +02:00
Torsten Schulz (local)
7e45049e94 refactor(VocabLessonView): combine lesson description and learning goals into a single component
All checks were successful
Deploy to production / deploy (push) Successful in 2m56s
- Merged the lesson description and learning goals sections into a unified block for improved layout and readability.
- Updated styles to enhance the visual presentation of the combined content, ensuring a more cohesive user experience.
2026-04-01 08:06:06 +02:00
Torsten Schulz (local)
1e801b33a5 refactor(AppSectionBar): update localization keys for section labels and titles
All checks were successful
Deploy to production / deploy (push) Successful in 2m58s
- Changed localization keys in AppSectionBar.vue to simplify and standardize the structure by removing the 'general' prefix.
- Updated section labels and title mappings to reflect the new key structure, enhancing consistency across the application.
2026-03-31 18:10:18 +02:00
Torsten Schulz (local)
c6caeefb5f feat(bisaya-course): enhance German course content and localization support
All checks were successful
Deploy to production / deploy (push) Successful in 2m47s
- Updated the create-german-for-bisaya-course-content.js script to improve lesson pattern retrieval by introducing a new function for generating a lesson pattern pool.
- Added new exercises for various topics including 'Wohnung & Nachbarn', 'Besuch empfangen', 'Arzt, Apotheke, Termin', and 'Amt, Dokumente, Anmeldung', enhancing practical language skills for learners.
- Improved localization by integrating translation keys for various UI elements and error messages across multiple components, ensuring a consistent user experience in both German and Bisaya.
- Enhanced the main.js file to recognize Bisaya language preferences in browser settings, improving accessibility for users.
2026-03-31 17:40:03 +02:00
Torsten Schulz (local)
b1990334b9 feat(falukant): enhance relationship state handling and director updates
All checks were successful
Deploy to production / deploy (push) Successful in 2m58s
- Updated FalukantService to return user information alongside relationship state for improved context in relationship management.
- Modified DirectorInfo component to trigger a refresh when a new director is hired, ensuring up-to-date information.
- Added socket event handling for 'directorchanged' in BranchView to manage director updates effectively, enhancing real-time responsiveness in the application.
2026-03-31 12:09:55 +02:00
Torsten Schulz (local)
3187a6e7b0 feat(vocab): add lesson and completed lesson vocab pool endpoints
All checks were successful
Deploy to production / deploy (push) Successful in 3m5s
- Implemented new endpoints in VocabController for retrieving vocab pools based on lessons and completed lessons.
- Updated vocabRouter to include routes for accessing lesson vocab pools and completed lesson vocab pools.
- Enhanced VocabService with methods to extract vocab from exercises and lesson didactics, improving vocabulary retrieval for users.
- Modified VocabPracticeDialog and VocabCourseView components to support new vocab pool functionalities, enhancing user experience in vocabulary practice.
2026-03-31 11:59:48 +02:00
Torsten Schulz (local)
01293b0102 feat(bisaya-course): update German course creation script to support default owner retrieval
All checks were successful
Deploy to production / deploy (push) Successful in 3m12s
- Modified the create-german-for-bisaya-course.js script to allow optional ownerHashedId parameter.
- Implemented a new function to find a default owner ('system' or 'admin') if no ownerHashedId is provided.
- Updated documentation to reflect the change in usage for the script.
2026-03-31 11:42:23 +02:00
Torsten Schulz (local)
0d625f1727 feat(falukant): add age information to lovers in family view
All checks were successful
Deploy to production / deploy (push) Successful in 3m7s
- Updated FalukantService to include age details for partners in relationships.
- Added translations for 'age' in English, German, and Spanish localization files.
- Enhanced FamilyView component to display age information for lovers and candidates, improving user experience.
2026-03-31 11:36:12 +02:00
Torsten Schulz (local)
db0e80a559 feat(falukant): enhance child details with other parent information and birth context
All checks were successful
Deploy to production / deploy (push) Successful in 3m2s
- Updated FalukantService to include father and mother character IDs in child relationships.
- Added logic to retrieve and display other parent details in ChildDetailsDialog and FamilyView components.
- Introduced new translations for 'other parent' and 'birth context' in English, German, and Spanish localization files.
- Enhanced UI to show other parent information and birth context in child detail views.
2026-03-31 10:29:22 +02:00
Torsten Schulz (local)
9b3898e43c refactor(deploy): update deployment workflow for production
All checks were successful
Deploy to production / deploy (push) Successful in 3m1s
- Renamed workflow from "Deploy yourpart (blue-green)" to "Deploy to production" for clarity.
- Removed environment variables for SSH configuration and replaced them with direct references to secrets for improved security.
- Updated SSH connection and deployment script steps to utilize secrets for host, port, and user, enhancing the deployment process.
2026-03-31 10:14:09 +02:00
Torsten Schulz (local)
dba9eb8692 feat(bisaya-course): enhance evening and sleep routines in course content
Some checks failed
Deploy yourpart (blue-green) / deploy (push) Failing after 0s
- Updated the Bisaya course to include new evening greetings and sleep-related phrases in core patterns.
- Added multiple exercises focusing on recognizing and using these phrases, improving practical language skills for learners.
- Expanded lesson didactics to incorporate prompts for evening and sleep routines, enriching the overall learning experience.
2026-03-31 10:13:12 +02:00
Torsten Schulz (local)
734b8f9463 feat(bisaya-course): enhance evening greetings and sleep-related exercises
All checks were successful
Deploy to production / deploy (push) Successful in 2m56s
- Updated core patterns to include evening greetings and sleep phrases in the Bisaya course content.
- Added new exercises focusing on recognizing and using evening greetings and sleep wishes, enhancing practical language use for learners.
- Expanded lesson didactics to incorporate evening and sleep-related conversational prompts, improving the overall learning experience.
2026-03-31 09:39:46 +02:00
Torsten Schulz (local)
6b3aee458a feat(bisaya-course): expand exercises for shopping, neighborhood visits, conflict resolution, and free speaking
All checks were successful
Deploy to production / deploy (push) Successful in 2m57s
- Added new exercises in multiple-choice, gap-fill, and situational response formats for categories including 'Einkaufen vertiefen', 'Nachbarschaft & Besuche', 'Rollenspiel - Konflikt und Hilfe', and 'Freies Sprechen - Alltag ohne Stütze'.
- Each exercise includes detailed instructions, question data, answer data, and explanations to enhance the learning experience for Bisaya language learners.
- Focused on practical scenarios to improve conversational skills and vocabulary retention.
2026-03-31 09:04:24 +02:00
Torsten Schulz (local)
53f0745faf feat(bisaya-course): add new exercises for various everyday scenarios
All checks were successful
Deploy to production / deploy (push) Successful in 2m44s
- Introduced multiple-choice, gap-fill, and situational response exercises across several categories including 'Besuch & Gastfreundschaft', 'Gesundheit im Alltag', 'Unterwegs & Transport', 'Kinder im Alltag', and 'Arzt & Termin'.
- Each category includes detailed instructions, question data, answer data, and explanations to enhance learning experiences.
- Expanded the vocabulary and situational context for learners to practice real-life interactions in Bisaya.
2026-03-31 08:56:36 +02:00
Torsten Schulz (local)
9a78bc7c4b feat(admin): add potential fathers retrieval for character management
All checks were successful
Deploy to production / deploy (push) Successful in 2m47s
- Implemented a new method in AdminService to fetch potential fathers for a given character based on existing relationships.
- Updated AdminController to expose this functionality via a new API endpoint.
- Enhanced adminRouter to include the route for retrieving potential fathers.
- Modified frontend components to allow selection of potential fathers during pregnancy and birth management.
- Updated internationalization files to include new translation keys related to father selection.
2026-03-31 08:50:56 +02:00
Torsten Schulz (local)
ee11a989a0 refactor(update-backend): remove temporary environment directory cleanup
All checks were successful
Deploy to production / deploy (push) Successful in 2m43s
- Eliminated the removal of the temporary environment directory in the update-backend.sh script to retain the environment for potential debugging or further use.
2026-03-31 07:50:19 +02:00
Torsten Schulz (local)
9aad42655e refactor(update-backend): clean up script by removing unnecessary newline
Some checks failed
Deploy to production / deploy (push) Failing after 1m56s
- Removed an extra newline in the update-backend.sh script to improve readability and maintainability.
2026-03-31 07:35:47 +02:00
Torsten Schulz (local)
ca33a29317 refactor(update-backend): streamline .env file handling process
Some checks failed
Deploy to production / deploy (push) Failing after 1m58s
- Replaced the previous method of copying .env files with a direct installation from a specified source path.
- Improved permissions and ownership settings for the .env file to enhance security.
- Added user feedback for successful file transfer and verification steps.
2026-03-30 16:19:00 +02:00
Torsten Schulz (local)
4ebff4dc17 fix(i18n): correct translation directions in socialnetwork.json files
All checks were successful
Deploy to production / deploy (push) Successful in 3m24s
- Updated translation keys in German, English, and Spanish localization files to accurately reflect the intended language direction for vocabulary translation.
- Adjusted the `translateTo` and `translateFrom` keys to ensure consistency across all language files.
2026-03-30 16:03:23 +02:00
Torsten Schulz (local)
3d9bca099c feat(vocab): add vocab distractor pool functionality
All checks were successful
Deploy to production / deploy (push) Successful in 3m9s
- Implemented a new endpoint to retrieve a pool of distractors for vocabulary exercises based on prior lessons.
- Updated the VocabController and VocabRouter to include the new getVocabDistractorPool method.
- Enhanced VocabService to classify questions and gather distractors from previous lessons.
- Modified VocabLessonView to fetch and utilize the distractor pool for multiple-choice exercises, improving the learning experience.
2026-03-30 15:13:10 +02:00
Torsten Schulz (local)
2b83c45e97 feat(family): enhance family view and character pregnancy handling
All checks were successful
Deploy to production / deploy (push) Successful in 2m48s
- Updated the FalukantCharacter model to include a default scope that excludes pregnancy-related fields for compatibility with older databases.
- Implemented a new method in FalukantService to conditionally retrieve pregnancy information based on database schema.
- Enhanced the FamilyView component to display a summary navigation for family relationships, including partners, children, and lovers.
- Updated internationalization files to include new translations for family-related terms and summaries.
2026-03-30 14:36:02 +02:00
Torsten Schulz (local)
f35db4b1a1 feat(update-backend): add .env file handling from live system
All checks were successful
Deploy to production / deploy (push) Successful in 5m35s
- Implemented functionality to copy the .env file from the live system to the backend directory if it exists.
- Added ownership change for the copied .env file to ensure proper permissions.
- Included user feedback for successful or failed .env file transfer.
2026-03-30 14:16:15 +02:00
Torsten Schulz (local)
c52d4b60f9 feat(admin): implement pregnancy and birth management features
Some checks failed
Deploy to production / deploy (push) Failing after 2m6s
- Added new admin functionalities to force pregnancy, clear pregnancy, and trigger birth for characters.
- Introduced corresponding routes and controller methods in adminRouter and adminController.
- Enhanced the FalukantCharacter model to include pregnancy-related fields.
- Created database migration for adding pregnancy columns to the character table.
- Updated frontend views and internationalization files to support new pregnancy and birth management features.
- Improved user feedback and error handling for these new actions.
2026-03-30 13:44:43 +02:00
Torsten Schulz (local)
b2591da428 refactor(update scripts): enhance directory handling and error management
Some checks failed
Deploy to production / deploy (push) Failing after 1m46s
- Updated update-backend.sh and update-frontend.sh to accept a target directory as a parameter, improving flexibility.
- Refactored paths to use the target directory for all operations, ensuring consistency and reducing hardcoded values.
- Added error handling with 'set -euo pipefail' for better script reliability.
- Improved user feedback by displaying the target directory during updates.
2026-03-30 13:19:11 +02:00
Torsten Schulz (local)
5001292616 fix(deploy.yml): update deployment script path for blue-green deployment
Some checks failed
Deploy to production / deploy (push) Failing after 1m55s
- Changed the deployment script path from 'actualize-yourpart.sh' to 'deploy-yourpart-bluegreen.sh' to reflect the new deployment strategy.
2026-03-30 11:48:49 +02:00
Torsten Schulz (local)
8f461d4dba fix(deploy.yml): enhance SSH connection testing for deployment
All checks were successful
Deploy to production / deploy (push) Successful in 1m8s
- Added options to disable strict host key checking and enable batch mode for improved SSH connection reliability during deployment.
2026-03-30 11:40:00 +02:00
Torsten Schulz (local)
ed4c4e1b40 refactor(deploy.yml): improve SSH deployment process
Some checks failed
Deploy to production / deploy (push) Has been cancelled
- Added steps to prepare SSH keys and known hosts for secure connections.
- Implemented a test for the SSH connection to ensure successful deployment.
- Updated the deployment step to use SSH for executing the deployment script on the production server.
2026-03-30 11:35:34 +02:00
Torsten Schulz (local)
4ed9ea39a0 refactor(update.sh): enhance script functionality and user feedback
Some checks failed
Deploy to production / deploy (push) Failing after 1s
- Improved error handling to ensure the script exits on failure.
- Added a check for the current branch to ensure it is 'main' before proceeding.
- Updated git commands for better state management and clarity in user feedback messages.
2026-03-30 11:32:51 +02:00
Torsten Schulz (local)
0749c733a4 refactor(update.sh): improve update script reliability and clarity
Some checks failed
Deploy to production / deploy (push) Failing after 1s
- Changed shebang to use env for better portability.
- Added error handling to ensure the script exits on failure.
- Included a check to confirm the current branch is 'main' before proceeding.
- Updated git commands to fetch and reset to origin/main, ensuring a clean state.
- Enhanced user feedback messages for clarity.
2026-03-30 10:51:57 +02:00
Torsten Schulz (local)
966c73dda9 style(AppSectionBar): enhance layout and styling for improved visual presentation
- Updated .app-section-bar__copy to use flexbox for better alignment and spacing.
- Added gap and flex-wrap properties to improve layout responsiveness.
- Adjusted styles in .app-section-bar__eyebrow for a more cohesive design.
2026-03-30 10:45:49 +02:00
Torsten Schulz (local)
34f22229bb refactor(StatusBar): improve layout and styling for better user experience
- Removed unnecessary label from character name display for a cleaner look.
- Adjusted grid layout and spacing in the StatusBar for enhanced alignment and responsiveness.
- Refined padding and gap settings to optimize visual presentation of status elements.
2026-03-30 10:40:57 +02:00
Torsten Schulz (local)
05b91284fa refactor(StatusBar, PoliticsView): update UI elements and improve layout
- Modified StatusBar component to enhance character name display with a label suffix for clarity.
- Refactored styles in StatusBar for better alignment and spacing, improving overall visual presentation.
- Removed the PoliticsView hero section to streamline the layout and focus on tab content, enhancing user navigation.
2026-03-30 10:36:55 +02:00
Torsten Schulz (local)
c531ae2caf feat(StatusBar, falukantService): enhance character information display and UI structure
- Updated falukantService to include predefined first and last names in character data retrieval.
- Refactored StatusBar component layout to improve organization and visual clarity, introducing sections for identity and stats.
- Enhanced character name handling to prioritize display names, ensuring a more dynamic user experience.
2026-03-30 10:31:59 +02:00
Torsten Schulz (local)
028da5a9f0 feat(MessagesDialog, StatusBar, i18n): enhance character death notifications and UI updates
- Added support for displaying character death notifications with detailed information including age, region, and relationships in both German and English.
- Updated MessagesDialog to format additional parameters such as region, spouses, children, and lovers based on the locale.
- Enhanced StatusBar to show the character's name dynamically, improving user experience.
- Modified i18n files to include new notification messages for character deaths, ensuring accurate translations for both languages.
2026-03-30 10:25:54 +02:00
Torsten Schulz (local)
10d8ee015c feat(VocabLessonView): improve vocab question generation and answer validation
- Introduced a new method to retrieve equivalent vocabulary answers based on user prompts and direction, enhancing the accuracy of answer options.
- Updated the buildChoiceOptions method to handle multiple correct answers and ensure the prompt is excluded from choices.
- Enhanced the current vocab question structure to support multiple acceptable answers, improving user feedback and engagement during exercises.
2026-03-30 08:45:59 +02:00
Torsten Schulz (local)
c9a7619737 feat(VocabLessonView): refine vocab trainer experience and improve user guidance
- Updated vocab trainer descriptions and button labels to better reflect user progress and encourage engagement.
- Enhanced visual indicators for exercise availability and review priorities, clarifying the learning path for users.
- Introduced new methods to calculate trainer targets and review blending, optimizing the learning experience based on user performance.
2026-03-28 23:41:25 +01:00
Torsten Schulz (local)
a2c86247b6 feat(VocabLessonView): enhance exercise access and vocab trainer experience
- Added conditional access to exercises based on user progress and previous vocab completion.
- Introduced visual indicators for exercise availability and review priorities to guide users.
- Updated vocab trainer descriptions and button labels to reflect the user's current state and encourage engagement.
- Implemented methods to manage exercise unlock conditions and improve user feedback on progress.
2026-03-28 13:11:05 +01:00
Torsten Schulz (local)
8a96951b50 feat(vocabService): enhance answer validation for multiple choice exercises
- Improved answer validation logic by expanding multiple choice answer data based on lesson context.
- Added methods to extract indices of correct answers and normalize question prompts for better accuracy.
- Refactored existing answer checking to accommodate new data handling, ensuring robust evaluation of user responses.
2026-03-28 12:46:34 +01:00
Torsten Schulz (local)
71e120bf20 feat(socialnetwork): enhance folder and video management with user visibility options
- Added functionality to manage selected users for adult folders and erotic videos, allowing for more granular visibility control.
- Introduced new endpoints and methods in the SocialNetworkController and SocialNetworkService to handle selected users.
- Updated the frontend components to include input fields for selected users in CreateFolderDialog, EditImageDialog, and EroticPicturesView.
- Enhanced the routing to support fetching erotic folders and videos by username, improving user experience in profile views.
2026-03-27 16:56:45 +01:00
Torsten Schulz (local)
39032570e3 fix(i18n): correct German translations for video upload hints and descriptions
- Updated the German translations in socialnetwork.json for improved accuracy and clarity.
- Enhanced the text in EroticPicturesView.vue and EroticVideosView.vue to ensure proper spelling and grammar in user-facing messages.
2026-03-27 16:29:18 +01:00
Torsten Schulz (local)
9e8f8e8077 refactor(i18n): update video management texts for clarity and consistency
- Revised the introductory texts and hints for video uploads in German, English, and Spanish to enhance user understanding.
- Improved descriptions to better reflect the functionality of the video management features.
- Removed outdated statistics display from the EroticVideosView component to streamline the user interface.
2026-03-27 15:43:15 +01:00
Torsten Schulz (local)
e76be33743 refactor(EroticVideosView): restructure layout and enhance video statistics display
- Updated the layout of the EroticVideosView component to improve organization and user experience.
- Introduced a sidebar for video upload and management, separating it from the video list.
- Added statistics for total, visible, and hidden videos to provide users with better insights.
- Enhanced form elements and labels for clarity and usability during video uploads.
2026-03-27 15:36:00 +01:00
Torsten Schulz (local)
6cbcf9d95f style(DialogWidget): update dialog overlay class for improved modal handling
- Changed the class binding for the dialog overlay to differentiate between modal and non-modal states, enhancing visual clarity.
- Introduced a new class for non-modal dialogs to ensure proper positioning and z-index management.
2026-03-27 15:21:57 +01:00
Torsten Schulz (local)
31a96aaf60 style(DialogWidget): remove backdrop filter for non-modal overlay
- Updated the non-modal dialog overlay to have no backdrop filter, enhancing visual clarity.
- Set the modal property to false in MessagesDialog to ensure proper dialog behavior.
2026-03-27 15:17:18 +01:00
Torsten Schulz (local)
8a1ff52a61 feat(chat): enhance room entry announcement logic in MultiChatDialog.vue
- Introduced a new property `lastAnnouncedRoomName` to track the last announced room, preventing redundant announcements.
- Updated the room entry announcement logic to ensure messages are sent only when entering a new room, improving clarity for users.
2026-03-27 15:10:38 +01:00
Torsten Schulz (local)
84c598bf52 feat(chat): initialize current room name in MultiChatDialog.vue
- Added a new property `currentRoomName` to track the name of the currently selected room.
- Updated the initialization logic to reset `currentRoomName` when rooms are loaded and when the chat is disconnected, ensuring accurate room state management.
2026-03-27 15:03:32 +01:00
Torsten Schulz (local)
291e79c41f fix(chat): improve room announcement logic in MultiChatDialog.vue
- Enhanced the logic for announcing room entries, particularly in adult-only mode, to ensure accurate messaging based on room selection.
- Added checks to prevent redundant announcements and ensure proper synchronization with the selected room.
2026-03-27 14:46:08 +01:00
Torsten Schulz (local)
3b823420e6 feat(chat): implement room synchronization for adult-only mode in MultiChatDialog.vue
- Added logic to handle room synchronization when entering a chat in adult-only mode.
- Introduced a flag to manage ongoing room sync requests, improving user experience during room transitions.
2026-03-27 14:37:49 +01:00
Torsten Schulz (local)
674c4d0b69 refactor(chat): simplify adult-only mode checks in MultiChatDialog.vue
- Removed redundant checks for adult-only mode when opening the chat dialog and connecting the chat socket.
- Streamlined the logic to enhance readability and maintainability of the component.
2026-03-27 14:29:00 +01:00
Torsten Schulz (local)
9f3facbb3f chore(dependencies): update package-lock.json and package.json for dependency versions
- Upgraded @emnapi/runtime to version 1.9.1 and @img/colour to version 1.1.0 in package-lock.json.
- Added new dependencies for ansi-regex and strip-ansi with updated versions in multiple locations.
- Updated lodash types to version 4.17.24 and ansi-escapes to version 7.3.0.
- Introduced overrides for minimatch and tmp in package.json to ensure consistent behavior across environments.
- Refactored MultiChatDialog.vue to improve room selection logic and handle adult-only mode more effectively.
2026-03-27 14:05:29 +01:00
Torsten Schulz (local)
07604cc9fa feat(navigation): enhance adult verification handling and notifications
- Updated navigationController to simplify the eroticChat menu structure.
- Enhanced adminService to notify users of adult verification status changes, including previous status.
- Improved AppNavigation and related components to register and unregister socket listeners for adult verification updates.
- Added localized messages for adult verification notifications in English, German, and Spanish.
- Introduced a verification hint in the EroticAccessView to guide users on document submission.
2026-03-27 13:23:44 +01:00
Torsten Schulz (local)
82223676a6 style(admin): update button styles in AdultVerificationView and EroticModerationView
- Added new styles for buttons in the adult verification filters to enhance appearance and usability.
- Set minimum height, padding, and font size for consistency across both views.
2026-03-27 11:25:56 +01:00
Torsten Schulz (local)
207ef6266a feat(deploy): add adult verification directory creation and permissions setup
- Enhanced deploy-backend.sh and update-backend.sh to create the adult verification directory under /opt/yourpart-data.
- Updated permissions for the new directory to ensure proper access control.
- Refactored file path handling in AdminService and SettingsService to utilize the new directory structure for adult verification files.
2026-03-27 11:24:21 +01:00
Torsten Schulz (local)
02837c7b73 refactor(admin): restructure adult verification and erotic moderation views for improved layout
- Updated the AdultVerificationView and EroticModerationView components to utilize a new layout structure with content scrolling and hidden overflow for better user experience.
- Adjusted styles in styles.scss to support the new layout, ensuring proper height and overflow handling for content sections.
2026-03-27 11:18:42 +01:00
Torsten Schulz (local)
25b658acce feat(admin): enhance adult verification file handling and localization
- Added a new method in AdminService to resolve adult verification file paths, improving file retrieval logic.
- Updated the AdminVerificationView to display a message when the verification document is missing.
- Localized the missing document message in German, English, and Spanish for better user experience.
2026-03-27 11:08:52 +01:00
Torsten Schulz (local)
0f0c102ded fix(admin): add 'adult_verification_request' to user parameter type filtering
- Updated the filtering criteria in the AdminService to include 'adult_verification_request' in the user parameter type query, enhancing data retrieval for adult verification processes.
2026-03-27 10:57:16 +01:00
Torsten Schulz (local)
26eb7b8ce7 feat(admin): add document preview functionality to adult verification
- Implemented a preview section for adult verification documents, allowing users to view images and PDFs inline.
- Added localization support for preview titles and messages in German, English, and Spanish.
- Enhanced the component's state management to handle preview visibility and cleanup.
2026-03-27 10:50:28 +01:00
Torsten Schulz (local)
0dd2bce5d1 fix(settings): streamline settings type creation in settingsService and initialization
- Refactored settingsService to use findOrCreate for settings type, improving efficiency and error handling.
- Added initialization for 'account' settings type in initializeSettings, ensuring all necessary settings are created during setup.
2026-03-27 10:43:44 +01:00
Torsten Schulz (local)
cf6d72385e fix(settings): enhance user parameter handling and add special user parameter types
- Introduced a new method to ensure special user parameter types for adult verification settings, improving data integrity and handling.
- Updated the upsertUserParam method to utilize the new special parameter type handling, ensuring robust user parameter management.
- Updated package dependencies in package.json and package-lock.json for consistency and to address potential vulnerabilities.
2026-03-27 10:38:42 +01:00
Torsten Schulz (local)
1a86061680 fix(dependencies): update @gltf-transform packages and sequelize-cli version
- Upgraded @gltf-transform/cli, core, extensions, and functions to version 4.3.0 for improved functionality and compatibility.
- Updated sequelize-cli to version 6.6.5 to address potential vulnerabilities.
- Added an overrides section in package.json for minimatch to ensure consistent behavior across environments.
- Refactored the EroticAccessView.vue component for improved structure and readability.
2026-03-27 10:23:25 +01:00
Torsten Schulz (local)
e13deb0720 fix(api): improve 404 handling for unknown API routes
- Refactored the 404 response for API routes to ensure it only triggers for paths starting with '/api/', enhancing clarity in error handling.
- Updated package dependencies in package.json and package-lock.json to maintain version consistency and address potential vulnerabilities.
2026-03-27 09:52:24 +01:00
Torsten Schulz (local)
21072139f7 fix(cors): refine CORS handling for OPTIONS requests
- Updated CORS middleware to explicitly handle OPTIONS requests, ensuring proper preflight response and improving API request handling.
2026-03-27 09:49:54 +01:00
Torsten Schulz (local)
1878b2a8c7 fix(router): refine stock and inventory routes for improved clarity
- Updated stock and inventory routes to explicitly define branchId as a required parameter, enhancing API usability and consistency in request handling.
2026-03-27 09:39:51 +01:00
Torsten Schulz (local)
6563ca23c7 fix(router): update stock and inventory routes to support optional branchId parameter
- Modified the stock and inventory routes to allow an optional branchId parameter, improving flexibility in API requests.
2026-03-27 09:24:05 +01:00
Torsten Schulz (local)
085333db29 fix(deploy): update package-lock.json for consistent npm ci deployments
- Removed package-lock.json from .gitignore to ensure it is tracked.
- Added locks for backend, frontend, and repo root to maintain version consistency during deployments with npm ci.
- Updated backend with a new script for lockfile synchronization and added a description note.
2026-03-27 09:20:37 +01:00
Torsten Schulz (local)
17325a5263 fix(deploy): package-lock.json versionieren für npm ci
- Zeile **/package-lock.json aus .gitignore entfernt; Locks zu backend,
  frontend und Repo-Root hinzugefügt, damit Deploy-Skripte mit npm ci
  dieselben Versionen wie package.json installieren.
- backend: Script lockfile:sync und Hinweis in description.

Made-with: Cursor
2026-03-27 09:16:17 +01:00
431 changed files with 75210 additions and 3257 deletions

0
.codex Normal file
View File

View File

@@ -0,0 +1,9 @@
---
description: C++-Worker unter src/ sind obsolet — nicht erweitern oder als Quelle für Spiellogik nutzen
alwaysApply: true
---
# Legacy C++ (`src/`)
- Verzeichnis **`src/`** (C++-Worker, WebSocket-Server): **obsolet**. Keine neuen Features, keine fachlichen Fixes dort planen oder umsetzen, sofern der Nutzer nicht ausdrücklich etwas anderes verlangt.
- Falukant-Hintergrundlogik: **Backend** (`backend/`), **externer Daemon**, **Frontend** — siehe `docs/LEGACY_CPP_WORKERS.md`.

148
.gitea/workflows/deploy.yml Normal file
View File

@@ -0,0 +1,148 @@
name: Deploy to production
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 2
- name: Detect vocab course changes
id: vocab_course_changes
shell: bash
run: |
set -euo pipefail
BASE="${{ gitea.event.before }}"
HEAD="${{ gitea.sha }}"
if [ -z "$BASE" ] || [[ "$BASE" =~ ^0+$ ]] || ! git cat-file -e "$BASE^{commit}" 2>/dev/null; then
BASE="HEAD~1"
fi
git diff --name-only "$BASE" "$HEAD" > changed-files.txt
cat changed-files.txt
COMMIT_MESSAGE="$(git log -1 --pretty=%B "$HEAD" || true)"
COURSE_SCRIPT_PATTERN='^backend/scripts/.*(bisaya|course|didactics|vocab)'
if echo "$COMMIT_MESSAGE" | grep -qi '\[force-deploy\]'; then
echo "force_deploy=true" >> "$GITHUB_OUTPUT"
else
echo "force_deploy=false" >> "$GITHUB_OUTPUT"
fi
if grep -E '^(backend/scripts/.*(bisaya|course|didactics|vocab)|backend/sql/.*vocab|backend/(migrations-active|migrations-archive)/.*vocab|docs/.*(COURSE|VOCAB|BISAYA|GERMAN_FOR_BISAYA))' changed-files.txt; then
echo "changed=true" >> "$GITHUB_OUTPUT"
else
echo "changed=false" >> "$GITHUB_OUTPUT"
fi
if grep -E "$COURSE_SCRIPT_PATTERN" changed-files.txt >/dev/null; then
echo "course_scripts_changed=true" >> "$GITHUB_OUTPUT"
else
echo "course_scripts_changed=false" >> "$GITHUB_OUTPUT"
fi
if grep -E '^frontend/' changed-files.txt >/dev/null; then
echo "frontend_changed=true" >> "$GITHUB_OUTPUT"
else
echo "frontend_changed=false" >> "$GITHUB_OUTPUT"
fi
# Kurs-/Didaktik-Scripts werden vor dem Sync separat uebertragen.
# Fuer sie sind npm ci, Migrationen und ein Backend-Restart nicht noetig.
if grep -E '^backend/' changed-files.txt \
| grep -Ev "$COURSE_SCRIPT_PATTERN" >/dev/null; then
echo "backend_app_changed=true" >> "$GITHUB_OUTPUT"
else
echo "backend_app_changed=false" >> "$GITHUB_OUTPUT"
fi
# App-Code-Änderungen, die einen echten Deploy benötigen
if grep -E '^(frontend/|backend/)' changed-files.txt \
| grep -Ev "$COURSE_SCRIPT_PATTERN" >/dev/null; then
echo "app_changed=true" >> "$GITHUB_OUTPUT"
else
echo "app_changed=false" >> "$GITHUB_OUTPUT"
fi
- name: Prepare SSH
run: |
mkdir -p ~/.ssh
printf "%s" "${{ secrets.PROD_SSH_KEY }}" > ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_ed25519
ssh-keyscan -p "${{ secrets.PROD_PORT }}" "${{ secrets.PROD_HOST }}" >> ~/.ssh/known_hosts
- name: Test SSH connection
run: |
ssh -i ~/.ssh/id_ed25519 \
-o StrictHostKeyChecking=no \
-o BatchMode=yes \
-p "${{ secrets.PROD_PORT }}" \
"${{ secrets.PROD_USER }}@${{ secrets.PROD_HOST }}" \
"echo SSH OK"
- name: Deploy vocab course scripts without app rebuild
if: steps.vocab_course_changes.outputs.course_scripts_changed == 'true' && steps.vocab_course_changes.outputs.backend_app_changed != 'true' && steps.vocab_course_changes.outputs.force_deploy != 'true'
shell: bash
run: |
set -euo pipefail
tar -czf - backend/scripts \
| ssh -i ~/.ssh/id_ed25519 \
-p "${{ secrets.PROD_PORT }}" \
"${{ secrets.PROD_USER }}@${{ secrets.PROD_HOST }}" \
"sudo -n -u yourpart tar -xzf - -C /opt/yourpart"
- name: Run deployment script
if: steps.vocab_course_changes.outputs.app_changed == 'true' || steps.vocab_course_changes.outputs.force_deploy == 'true'
run: |
DEPLOY_FLAGS=""
if [ "${{ steps.vocab_course_changes.outputs.force_deploy }}" = "true" ]; then
DEPLOY_FLAGS=""
elif [ "${{ steps.vocab_course_changes.outputs.backend_app_changed }}" = "true" ] && [ "${{ steps.vocab_course_changes.outputs.frontend_changed }}" != "true" ]; then
DEPLOY_FLAGS="--skip-frontend"
elif [ "${{ steps.vocab_course_changes.outputs.frontend_changed }}" = "true" ] && [ "${{ steps.vocab_course_changes.outputs.backend_app_changed }}" != "true" ]; then
DEPLOY_FLAGS="--skip-backend"
fi
DEPLOY_TARGET="${{ secrets.PROD_DEPLOY_TARGET }}"
if [ -z "$DEPLOY_TARGET" ]; then
DEPLOY_TARGET="/opt/yourpart-green"
fi
echo "Deploy-Flags: ${DEPLOY_FLAGS:-<none>}"
echo "Deploy-Target: $DEPLOY_TARGET"
ssh -i ~/.ssh/id_ed25519 \
-p "${{ secrets.PROD_PORT }}" \
"${{ secrets.PROD_USER }}@${{ secrets.PROD_HOST }}" \
"/home/tsschulz/deploy-yourpart-bluegreen.sh ${DEPLOY_TARGET} ${DEPLOY_FLAGS}"
- name: Skip full deployment (no app changes)
if: steps.vocab_course_changes.outputs.app_changed != 'true' && steps.vocab_course_changes.outputs.force_deploy != 'true'
run: |
echo "Kein Full-Deploy: Es wurden keine Frontend/Backend-App-Dateien geändert."
- name: Sync vocab course content
if: steps.vocab_course_changes.outputs.changed == 'true' || steps.vocab_course_changes.outputs.force_deploy == 'true'
run: |
# Decide whether to actually run the phase3 update on the server.
# By default we run the deploy script in --dry-run mode. To enable the
# actual run set the secret PHASE3_UPDATE=1 in the repo settings.
RUN_FLAG="--dry-run"
if [ "${{ secrets.PHASE3_UPDATE }}" = "1" ]; then
RUN_FLAG=""
fi
ssh -i ~/.ssh/id_ed25519 \
-p "${{ secrets.PROD_PORT }}" \
"${{ secrets.PROD_USER }}@${{ secrets.PROD_HOST }}" \
"cd /opt/yourpart && npm --prefix backend run sync:vocab-courses && if [ -x backend/scripts/deploy-phase3-update.sh ]; then bash backend/scripts/deploy-phase3-update.sh $RUN_FLAG; else echo 'deploy-phase3-update.sh not found or not executable'; fi"

5
.gitignore vendored
View File

@@ -5,7 +5,7 @@
.depbe.sh
node_modules
node_modules/*
**/package-lock.json
# package-lock.json wird versioniert (npm ci im Deploy braucht konsistente Locks zu package.json)
backend/.env
backend/.env.local
backend/images
@@ -17,6 +17,9 @@ frontend/node_modules
frontend/node_modules/*
frontend/dist
frontend/dist/*
frontend/scripts/.i18n-de-fr-cache.json
frontend/scripts/.falukant-fr-smooth-cache.json
frontend/ceb-locale-audit-report.json
frontedtree.txt
backend/dist/
backend/data/model-cache

259
OAUTH_CREDENTIALS_SETUP.md Normal file
View File

@@ -0,0 +1,259 @@
# OAuth Credentials Setup Guide
Anleitung zum Sammeln der OAuth-Credentials für alle 5 Provider.
## Redirect URIs
Für alle Provider benötigst du folgende Redirect URIs (ersetze `www.your-part.de` mit deiner echten Domain):
```
https://www.your-part.de/auth/oauth/callback
https://www.your-part.de/auth/oauth/user/callback
```
Lokal zum Testen:
```
http://localhost:3000/auth/oauth/callback
http://localhost:3000/auth/oauth/user/callback
```
---
## 1. Google
### Credentials besorgen:
1. Öffne [Google Cloud Console](https://console.cloud.google.com/)
2. Erstelle ein neues Projekt oder wähle ein bestehendes
3. Navigiere zu **APIs & Services****Credentials**
4. Klick **+ CREATE CREDENTIALS** → **OAuth 2.0 Client IDs**
5. Wähle **Web application**
6. Füge unter **Authorized redirect URIs** hinzu:
- `https://www.your-part.de/auth/oauth/callback`
- `https://www.your-part.de/auth/oauth/user/callback`
7. Speichern und die **Client ID** und **Client Secret** kopieren
### .env:
```env
OAUTH_GOOGLE_CLIENT_ID=your-client-id.apps.googleusercontent.com
OAUTH_GOOGLE_CLIENT_SECRET=your-client-secret
```
---
## 2. Microsoft Azure
### Credentials besorgen:
1. Öffne [Azure Portal](https://portal.azure.com/)
2. Navigiere zu **Azure Active Directory****App registrations****+ New registration**
3. Gib einen Namen ein (z.B. "YourPart OAuth")
4. Wähle **Accounts in any organizational directory (Any Azure AD directory - Multitenant)**
5. Bei **Redirect URI** wähle **Web** und füge ein:
- `https://www.your-part.de/auth/oauth/callback`
6. Klick **Register**
7. Notiere die **Application (client) ID**
8. Gehe zu **Certificates & secrets****+ New client secret**
9. Erstelle ein Secret und kopiere den **Value** (nicht die ID!)
10. Gehe zu **Token configuration** und stelle sicher, dass die richtigen Claims enthalten sind
### Zusätzliche URI hinzufügen:
1. Gehe zu **Authentication**
2. Unter **Redirect URIs** klick **+ Add URI**
3. Füge hinzu: `https://www.your-part.de/auth/oauth/user/callback`
### .env:
```env
OAUTH_MICROSOFT_CLIENT_ID=your-application-id
OAUTH_MICROSOFT_CLIENT_SECRET=your-client-secret-value
```
---
## 3. Keycloak
Keycloak ist ein Open-Source OIDC Provider. Du kannst ihn selbst hosten oder eine gehostete Lösung nutzen.
### Option A: Selbst gehostet mit Docker
```bash
docker run -d \
-p 8080:8080 \
-e KEYCLOAK_ADMIN=admin \
-e KEYCLOAK_ADMIN_PASSWORD=admin \
quay.io/keycloak/keycloak:latest \
start-dev
```
Dann:
1. Öffne http://localhost:8080
2. Login mit `admin` / `admin`
3. **Realm erstellen**: Oben links Dropdown → **Create realm**
- Name: `yourpart` (oder beliebig)
4. Im neuen Realm: **Clients****Create client**
- Client ID: `yourpart`
- Client Protocol: `openid-connect`
- Access Type: `confidential`
5. Im **Settings** Tab:
- Valid Redirect URIs:
```
https://www.your-part.de/auth/oauth/callback
https://www.your-part.de/auth/oauth/user/callback
```
6. Speichern
7. Gehe zu **Credentials** Tab
- **Client Secret** kopieren
### Option B: Gehosteter Service
- [Keycloak.cloud](https://www.keycloak.cloud/) oder
- [Red Hat Managed Keycloak](https://www.keycloak.org/cloud/)
### .env:
```env
OAUTH_KEYCLOAK_ISSUER=https://your-keycloak-domain/realms/yourpart
OAUTH_KEYCLOAK_CLIENT_ID=yourpart
OAUTH_KEYCLOAK_CLIENT_SECRET=your-client-secret
```
---
## 4. ORY Hydra / ORY Cloud
ORY ist ein moderner OIDC Provider. Am einfachsten ist ORY Cloud.
### Option A: ORY Cloud (empfohlen)
1. Öffne [ORY Cloud Console](https://console.ory.sh/)
2. Registriere dich oder logge dich ein
3. Erstelle ein neues **Project**
4. Gehe zu **Applications**
5. Klick **Create New Application**
6. Gib einen Namen ein (z.B. "YourPart")
7. Unter **Redirect URLs** füge ein:
```
https://www.your-part.de/auth/oauth/callback
https://www.your-part.de/auth/oauth/user/callback
```
8. Speichern
9. Die **Client ID** und **Client Secret** werden angezeigt
10. Finde deine **Issuer URL** in den Project Settings (meist `https://your-project-slug.eu.hydra.cloud`)
### Option B: Selbst gehostet (komplex)
Siehe [ORY Hydra Dokumentation](https://www.ory.sh/hydra/docs/)
### .env:
```env
OAUTH_ORY_ISSUER=https://your-project-slug.eu.hydra.cloud
OAUTH_ORY_CLIENT_ID=your-client-id
OAUTH_ORY_CLIENT_SECRET=your-client-secret
```
---
## 5. ZITADEL
ZITADEL ist ein Zero-Trust Identity Platform als SaaS.
### Credentials besorgen:
1. Öffne [ZITADEL Console](https://zitadel.cloud/)
2. Registriere dich oder logge dich ein
3. Erstelle eine neue **Organization** (oder verwende die existierende)
4. Gehe zu **Projects** → **+ New Project**
5. Gib einen Namen ein (z.B. "YourPart")
6. Gehe zum Projekt → **Applications** → **+ New Application**
7. Wähle **Type: Web**
8. Gib einen Namen ein
9. Bei **Redirect URIs** füge ein:
```
https://www.your-part.de/auth/oauth/callback
https://www.your-part.de/auth/oauth/user/callback
```
10. Speichern
11. Unter **Client Information** kopiere:
- **Client ID**
- **Client Secret** (falls sichtbar, sonst im "CREDENTIALS" Tab generieren)
12. Finde deine **Issuer URL** in den Organization Settings (meist `https://your-instance.zitadel.cloud`)
### .env:
```env
OAUTH_ZITADEL_ISSUER=https://your-instance.zitadel.cloud
OAUTH_ZITADEL_CLIENT_ID=your-client-id
OAUTH_ZITADEL_CLIENT_SECRET=your-client-secret
```
---
## Komplette .env für alle 5 Provider
```env
# Google
OAUTH_GOOGLE_CLIENT_ID=...
OAUTH_GOOGLE_CLIENT_SECRET=...
# Microsoft
OAUTH_MICROSOFT_CLIENT_ID=...
OAUTH_MICROSOFT_CLIENT_SECRET=...
# Keycloak
OAUTH_KEYCLOAK_ISSUER=...
OAUTH_KEYCLOAK_CLIENT_ID=...
OAUTH_KEYCLOAK_CLIENT_SECRET=...
# ORY
OAUTH_ORY_ISSUER=...
OAUTH_ORY_CLIENT_ID=...
OAUTH_ORY_CLIENT_SECRET=...
# ZITADEL
OAUTH_ZITADEL_ISSUER=...
OAUTH_ZITADEL_CLIENT_ID=...
OAUTH_ZITADEL_CLIENT_SECRET=...
```
---
## Schnell-Checkliste
- [ ] Google: Client ID & Secret
- [ ] Microsoft: Application ID & Client Secret
- [ ] Keycloak: Issuer, Client ID, Secret
- [ ] ORY: Issuer, Client ID, Secret
- [ ] ZITADEL: Issuer, Client ID, Secret
- [ ] Alle Redirect URIs in den Providern konfiguriert
- [ ] .env datei aktualisiert
- [ ] Server neu gestartet (`npm restart`)
---
## Testing
Nach der Konfiguration:
1. Frontend öffnen: http://www.your-part.de
2. Auf "Login" oder Provider-Button klicken
3. Jeder verfügbare Provider sollte als Button angezeigt werden
4. Test: Mit jedem Provider einloggen
5. Test: Existender Nutzer → Einstellungen → Authentifizierung hinzufügen
---
## Troubleshooting
### "Invalid redirect URI"
- Stelle sicher, dass die Redirect URIs **exakt** übereinstimmen (inkl. `https://` vs `http://`)
- Beachte Trailing Slashes
### "Invalid client secret"
- Kopiere das Secret neu (nicht die ID)
- Manche Provider verstecken das Secret nach einmaliger Anzeige
### "Discovery endpoint not found"
- Überprüfe die Issuer URL (mit/ohne Trailing Slash)
- Für Keycloak: URL muss auf `/realms/xxx` enden
- Für ORY: URL darf nicht auf `/` enden
### Port-Konflikt lokal
- Keycloak benutzt `8080` → ändere auf: `docker run -p 8081:8080 ...`
- Stelle sicher, dass 3000 (Frontend) und 5000 (Backend) frei sind

3
README.md Normal file
View File

@@ -0,0 +1,3 @@
## zum testen des push
Hinweis: Das Verzeichnis **`src/`** (C++-Worker) ist veraltet; siehe [`docs/LEGACY_CPP_WORKERS.md`](docs/LEGACY_CPP_WORKERS.md).

View File

@@ -3,7 +3,7 @@
This project now supports a per-region sales tax (`tax_percent`) for Falukant.
Migration
- A SQL migration was added: `backend/migrations/20260101000000-add-tax-percent-to-region.cjs`.
- A SQL migration was added: `backend/migrations-archive/20260101000000-add-tax-percent-to-region.cjs`.
- It adds `tax_percent` numeric NOT NULL DEFAULT 7 to `falukant_data.region`.
Runtime configuration

View File

@@ -23,6 +23,7 @@ import vocabRouter from './routers/vocabRouter.js';
import dashboardRouter from './routers/dashboardRouter.js';
import newsRouter from './routers/newsRouter.js';
import calendarRouter from './routers/calendarRouter.js';
import moderationRouter from './routers/moderationRouter.js';
import cors from 'cors';
import './jobs/sessionCleanup.js';
@@ -83,7 +84,12 @@ const corsOptions = {
};
app.use(cors(corsOptions));
app.options('*', cors(corsOptions));
app.use((req, res, next) => {
if (req.method === 'OPTIONS') {
return cors(corsOptions)(req, res, next);
}
return next();
});
app.use(express.json()); // To handle JSON request bodies
app.use('/api/chat', chatRouter);
@@ -100,6 +106,7 @@ app.use('/api/contact', contactRouter);
app.use('/api/socialnetwork', socialnetworkRouter);
app.use('/api/vocab', vocabRouter);
app.use('/api/forum', forumRouter);
app.use('/api/moderation', moderationRouter);
app.use('/api/falukant', falukantRouter);
app.use('/api/friendships', friendshipRouter);
app.use('/api/models', modelsProxyRouter);
@@ -124,6 +131,11 @@ app.get(/^\/(?!api\/).*/, (req, res) => {
});
// Fallback 404 for unknown API routes
app.use('/api/*', (req, res) => res.status(404).send('404 Not Found'));
app.use((req, res, next) => {
if (req.path.startsWith('/api/')) {
return res.status(404).send('404 Not Found');
}
return next();
});
export default app;

View File

@@ -0,0 +1,33 @@
const path = require('path');
const dotenv = require('dotenv');
const envPath = process.env.SEQUELIZE_ENV_FILE
? path.resolve(process.cwd(), process.env.SEQUELIZE_ENV_FILE)
: path.resolve(process.cwd(), '.env');
dotenv.config({ path: envPath });
const dialectOptions = {};
if (process.env.DB_SSL === '1' || process.env.PGSSLMODE === 'require') {
dialectOptions.ssl = process.env.DB_SSL_REJECT_UNAUTHORIZED === '0'
? { rejectUnauthorized: false }
: true;
}
// pg/SCRAM: password muss ein String sein; bei fehlender .env sonst undefined-Fallen vermeiden
const shared = {
username: process.env.DB_USER != null ? String(process.env.DB_USER) : undefined,
password: process.env.DB_PASS != null ? String(process.env.DB_PASS) : '',
database: process.env.DB_NAME != null ? String(process.env.DB_NAME) : undefined,
host: process.env.DB_HOST || '127.0.0.1',
port: Number.parseInt(process.env.DB_PORT || '5432', 10),
dialect: 'postgres',
logging: false,
dialectOptions
};
module.exports = {
development: shared,
test: shared,
production: shared
};

View File

@@ -13,6 +13,11 @@ class AdminController {
this.searchUser = this.searchUser.bind(this);
this.getFalukantUserById = this.getFalukantUserById.bind(this);
this.changeFalukantUser = this.changeFalukantUser.bind(this);
this.adminForceFalukantPregnancy = this.adminForceFalukantPregnancy.bind(this);
this.adminClearFalukantPregnancy = this.adminClearFalukantPregnancy.bind(this);
this.adminForceFalukantBirth = this.adminForceFalukantBirth.bind(this);
this.adminCleanupCharacterDeathArtifacts = this.adminCleanupCharacterDeathArtifacts.bind(this);
this.adminGetPotentialFathersForCharacter = this.adminGetPotentialFathersForCharacter.bind(this);
this.getFalukantUserBranches = this.getFalukantUserBranches.bind(this);
this.updateFalukantStock = this.updateFalukantStock.bind(this);
this.addFalukantStock = this.addFalukantStock.bind(this);
@@ -29,6 +34,10 @@ class AdminController {
this.getUser = this.getUser.bind(this);
this.getUsers = this.getUsers.bind(this);
this.updateUser = this.updateUser.bind(this);
this.resetUserVocabLessonProgress = this.resetUserVocabLessonProgress.bind(this);
this.markUserVocabLessonsCompleteThrough = this.markUserVocabLessonsCompleteThrough.bind(this);
this.getUserVocabCourses = this.getUserVocabCourses.bind(this);
this.getVocabCourseForAdmin = this.getVocabCourseForAdmin.bind(this);
this.getAdultVerificationRequests = this.getAdultVerificationRequests.bind(this);
this.setAdultVerificationStatus = this.setAdultVerificationStatus.bind(this);
this.getAdultVerificationDocument = this.getAdultVerificationDocument.bind(this);
@@ -45,6 +54,9 @@ class AdminController {
// Statistics
this.getUserStatistics = this.getUserStatistics.bind(this);
this.getFalukantRegions = this.getFalukantRegions.bind(this);
this.getFalukantAllRegions = this.getFalukantAllRegions.bind(this);
this.getFalukantRegionTypes = this.getFalukantRegionTypes.bind(this);
this.createFalukantRegion = this.createFalukantRegion.bind(this);
this.updateFalukantRegionMap = this.updateFalukantRegionMap.bind(this);
this.getRegionDistances = this.getRegionDistances.bind(this);
this.upsertRegionDistance = this.upsertRegionDistance.bind(this);
@@ -125,6 +137,77 @@ class AdminController {
}
}
async resetUserVocabLessonProgress(req, res) {
const schema = Joi.object({
lessonId: Joi.number().integer().positive().required()
});
const { error, value } = schema.validate(req.body || {});
if (error) {
return res.status(400).json({ error: error.details[0].message });
}
try {
const { userid: requester } = req.headers;
const { id } = req.params;
const result = await AdminService.adminResetUserVocabLessonProgress(requester, id, value.lessonId);
res.status(200).json(result);
} catch (err) {
const status = err.message === 'noaccess' ? 403 : (err.message === 'lessonnotfound' ? 404 : 500);
res.status(status).json({ error: err.message });
}
}
async markUserVocabLessonsCompleteThrough(req, res) {
const schema = Joi.object({
courseId: Joi.number().integer().positive().required(),
throughLessonNumber: Joi.number().integer().positive().required()
});
const { error, value } = schema.validate(req.body || {});
if (error) {
return res.status(400).json({ error: error.details[0].message });
}
try {
const { userid: requester } = req.headers;
const { id } = req.params;
const result = await AdminService.adminMarkUserVocabLessonsCompleteThrough(
requester,
id,
value.courseId,
value.throughLessonNumber
);
res.status(200).json(result);
} catch (err) {
let status = 500;
if (err.message === 'noaccess') status = 403;
else if (err.message === 'notenrolled') status = 403;
else if (err.message === 'badrequest') status = 400;
res.status(status).json({ error: err.message });
}
}
async getUserVocabCourses(req, res) {
try {
const { userid: requester } = req.headers;
const { id } = req.params;
const result = await AdminService.adminListUserEnrolledVocabCourses(requester, id);
res.status(200).json(result);
} catch (err) {
const status = err.message === 'noaccess' ? 403 : (err.message === 'notfound' ? 404 : 500);
res.status(status).json({ error: err.message });
}
}
async getVocabCourseForAdmin(req, res) {
try {
const { userid: requester } = req.headers;
const { courseId } = req.params;
const result = await AdminService.adminGetVocabCourseWithLessons(requester, courseId);
res.status(200).json(result);
} catch (err) {
const status = err.message === 'noaccess' ? 403 : (err.message === 'coursenotfound' ? 404 : 500);
res.status(status).json({ error: err.message });
}
}
async getAdultVerificationRequests(req, res) {
try {
const { userid: requester } = req.headers;
@@ -372,6 +455,77 @@ class AdminController {
}
}
async adminForceFalukantPregnancy(req, res) {
try {
const { userid: userId } = req.headers;
const { characterId, fatherCharacterId, dueInDays } = req.body;
const response = await AdminService.adminForceFalukantPregnancy(userId, characterId, {
fatherCharacterId,
dueInDays,
});
res.status(200).json(response);
} catch (error) {
console.log(error);
res.status(400).json({ error: error.message });
}
}
async adminGetPotentialFathersForCharacter(req, res) {
try {
const { userid: userId } = req.headers;
const { characterId } = req.params;
const response = await AdminService.adminGetPotentialFathersForCharacter(userId, characterId);
res.status(200).json(response);
} catch (error) {
console.log(error);
res.status(400).json({ error: error.message });
}
}
async adminClearFalukantPregnancy(req, res) {
try {
const { userid: userId } = req.headers;
const { characterId } = req.body;
const response = await AdminService.adminClearFalukantPregnancy(userId, characterId);
res.status(200).json(response);
} catch (error) {
console.log(error);
res.status(400).json({ error: error.message });
}
}
async adminForceFalukantBirth(req, res) {
try {
const { userid: userId } = req.headers;
const { motherCharacterId, fatherCharacterId, birthContext, legitimacy, gender } = req.body;
const response = await AdminService.adminForceFalukantBirth(userId, motherCharacterId, {
fatherCharacterId,
birthContext,
legitimacy,
gender,
});
res.status(200).json(response);
} catch (error) {
console.log(error);
res.status(400).json({ error: error.message });
}
}
async adminCleanupCharacterDeathArtifacts(req, res) {
try {
const { userid: userId } = req.headers;
const { characterId } = req.params;
const response = await AdminService.adminCleanupCharacterDeathArtifacts(userId, characterId);
res.status(200).json(response);
} catch (error) {
console.log(error);
const status = error.message === 'noaccess'
? 403
: (['invalidCharacter', 'notfound', 'targetnotdead'].includes(error.message) ? 400 : 500);
res.status(status).json({ error: error.message });
}
}
async getFalukantUserBranches(req, res) {
try {
const { userid: userId } = req.headers;
@@ -432,6 +586,42 @@ class AdminController {
}
}
async getFalukantAllRegions(req, res) {
try {
const { userid: userId } = req.headers;
const regions = await AdminService.getFalukantAllRegions(userId);
res.status(200).json(regions);
} catch (error) {
console.log(error);
const status = error.message === 'noaccess' ? 403 : 500;
res.status(status).json({ error: error.message });
}
}
async getFalukantRegionTypes(req, res) {
try {
const { userid: userId } = req.headers;
const types = await AdminService.getFalukantRegionTypes(userId);
res.status(200).json(types);
} catch (error) {
console.log(error);
const status = error.message === 'noaccess' ? 403 : 500;
res.status(status).json({ error: error.message });
}
}
async createFalukantRegion(req, res) {
try {
const { userid: userId } = req.headers;
const created = await AdminService.createFalukantRegion(userId, req.body || {});
res.status(200).json(created);
} catch (error) {
console.log(error);
const status = error.message === 'noaccess' ? 403 : 400;
res.status(status).json({ error: error.message });
}
}
async updateFalukantRegionMap(req, res) {
try {
const { userid: userId } = req.headers;

View File

@@ -1,4 +1,5 @@
import * as userService from '../services/authService.js';
import * as oauthService from '../services/oauthService.js';
class AuthController {
constructor() {
@@ -7,6 +8,13 @@ class AuthController {
this.forgotPassword = this.forgotPassword.bind(this);
this.activateAccount = this.activateAccount.bind(this);
this.logout = this.logout.bind(this);
this.oauthProviders = this.oauthProviders.bind(this);
this.oauthStart = this.oauthStart.bind(this);
this.oauthExchange = this.oauthExchange.bind(this);
this.oauthUserIdentities = this.oauthUserIdentities.bind(this);
this.oauthUserStart = this.oauthUserStart.bind(this);
this.oauthUserExchange = this.oauthUserExchange.bind(this);
this.oauthUserRemove = this.oauthUserRemove.bind(this);
}
async register(req, res) {
@@ -29,6 +37,8 @@ class AuthController {
} catch (error) {
if (error.message === 'credentialsinvalid') {
res.status(404).json({ error: error.message });
} else if (error.message === 'userblocked') {
res.status(403).json({ error: error.message });
} else {
res.status(500).json({ error: error.message });
}
@@ -41,6 +51,134 @@ class AuthController {
res.status(200).json({ result: 'loggedout' });
}
async oauthProviders(req, res) {
try {
const providers = await oauthService.getOAuthProviders();
res.status(200).json({ providers });
} catch (error) {
res.status(500).json({ error: error.message });
}
}
async oauthStart(req, res) {
const { provider } = req.params;
try {
const redirectTo = await oauthService.startOAuthLogin({ providerSlug: provider });
res.redirect(302, redirectTo.toString());
} catch (error) {
const status = error.message === 'providernotconfigured' ? 503 : 500;
res.status(status).json({ error: error.message });
}
}
async oauthExchange(req, res) {
const { code, state, iss } = req.body;
try {
const result = await oauthService.exchangeOAuthLogin({ code, state, iss });
res.status(200).json(result);
} catch (error) {
const knownErrors = new Set([
'oauthcodemissing',
'oauthstatemissing',
'oauthsubjectmissing',
'providernotconfigured',
'oauthidentityconflict'
]);
const status = knownErrors.has(error.message) ? 400 : 500;
res.status(status).json({ error: error.message });
}
}
async oauthUserIdentities(req, res) {
const { userid: hashedUserId } = req.headers;
try {
const User = (await import('../models/community/user.js')).default;
const user = await User.findOne({ where: { hashedId: hashedUserId } });
if (!user) {
return res.status(404).json({ error: 'usernotfound' });
}
const identities = await oauthService.getUserOAuthIdentities(user.id);
res.status(200).json({ identities });
} catch (error) {
res.status(500).json({ error: error.message });
}
}
async oauthUserStart(req, res) {
const hashedUserId = req.headers.userid || req.query.userid;
const { provider } = req.params;
try {
const User = (await import('../models/community/user.js')).default;
const user = await User.findOne({ where: { hashedId: hashedUserId } });
if (!user) {
return res.status(404).json({ error: 'usernotfound' });
}
const redirectTo = await oauthService.startOAuthLoginForUser({
userId: user.id,
providerSlug: provider
});
res.redirect(302, redirectTo.toString());
} catch (error) {
const status = error.message === 'providernotconfigured' ? 503 : 500;
res.status(status).json({ error: error.message });
}
}
async oauthUserExchange(req, res) {
const hashedUserId = req.headers.userid || req.query.userid;
const { code, state, iss } = req.body;
try {
const User = (await import('../models/community/user.js')).default;
const user = await User.findOne({ where: { hashedId: hashedUserId } });
if (!user) {
return res.status(404).json({ error: 'usernotfound' });
}
const result = await oauthService.exchangeOAuthLoginForUser({
userId: user.id,
code,
state,
iss
});
res.status(200).json(result);
} catch (error) {
const knownErrors = new Set([
'oauthcodemissing',
'oauthstatemissing',
'oauthsubjectmissing',
'providernotconfigured',
'oauthuseridmismatch',
'oauthidentityalreadylinked'
]);
const status = knownErrors.has(error.message) ? 400 : 500;
res.status(status).json({ error: error.message });
}
}
async oauthUserRemove(req, res) {
const { userid: hashedUserId } = req.headers;
const { identityId } = req.params;
try {
const User = (await import('../models/community/user.js')).default;
const user = await User.findOne({ where: { hashedId: hashedUserId } });
if (!user) {
return res.status(404).json({ error: 'usernotfound' });
}
const result = await oauthService.removeOAuthIdentity({
userId: user.id,
identityId: parseInt(identityId, 10)
});
res.status(200).json(result);
} catch (error) {
const status = error.message === 'forbidden' ? 403 : 500;
res.status(status).json({ error: error.message });
}
}
async forgotPassword(req, res) {
const { email } = req.body;
try {

View File

@@ -16,6 +16,7 @@ class ChatController {
this.getRoomCreateOptions = this.getRoomCreateOptions.bind(this);
this.getOwnRooms = this.getOwnRooms.bind(this);
this.deleteOwnRoom = this.deleteOwnRoom.bind(this);
this.reportChatIncident = this.reportChatIncident.bind(this);
}
async getMessages(req, res) {
@@ -215,6 +216,32 @@ class ChatController {
res.status(status).json({ error: error.message });
}
}
async reportChatIncident(req, res) {
const schema = Joi.object({
context: Joi.string().valid('random_chat', 'multi_chat', 'one_to_one').required(),
reporterHashedId: Joi.string().allow('', null),
reporterRandomId: Joi.string().allow('', null),
reporterUsername: Joi.string().allow('', null),
offenderHashedId: Joi.string().allow('', null),
offenderRandomId: Joi.string().allow('', null),
offenderUsername: Joi.string().allow('', null),
incidentAt: Joi.date().iso().required(),
chatHistory: Joi.array().min(1).required(),
metadata: Joi.object().unknown(true).optional()
});
const { error, value } = schema.validate(req.body || {});
if (error) {
return res.status(400).json({ error: error.details[0].message });
}
try {
const result = await chatService.reportChatIncident(value);
return res.status(201).json(result);
} catch (err) {
console.error('Error in reportChatIncident:', err);
return res.status(400).json({ error: err.message });
}
}
}
export default ChatController;

View File

@@ -1,4 +1,5 @@
import FalukantService from '../services/falukantService.js';
import politicalPowersService from '../services/falukantPoliticalPowersService.js';
function extractHashedUserId(req) {
return req.headers?.userid;
@@ -118,6 +119,8 @@ class FalukantController {
});
this.setLoverMaintenance = this._wrapWithUser((userId, req) =>
this.service.setLoverMaintenance(userId, req.params.relationshipId, req.body?.maintenanceLevel), { blockInDebtorsPrison: true });
this.improveLoverAffection = this._wrapWithUser((userId, req) =>
this.service.improveLoverAffection(userId, req.params.relationshipId), { blockInDebtorsPrison: true });
this.createLoverRelationship = this._wrapWithUser((userId, req) =>
this.service.createLoverRelationship(userId, req.body?.targetCharacterId, req.body?.loverRole), { successStatus: 201, blockInDebtorsPrison: true });
this.spendTimeWithSpouse = this._wrapWithUser((userId) =>
@@ -207,11 +210,29 @@ class FalukantController {
}, { blockInDebtorsPrison: true });
this.getPoliticsOverview = this._wrapWithUser((userId) => this.service.getPoliticsOverview(userId));
this.getPoliticalOfficeCatalog = this._wrapWithUser((userId) => this.service.getPoliticalOfficeCatalog(userId));
this.getOpenPolitics = this._wrapWithUser((userId) => this.service.getOpenPolitics(userId));
this.getElections = this._wrapWithUser((userId) => this.service.getElections(userId));
this.vote = this._wrapWithUser((userId, req) => this.service.vote(userId, req.body.votes), { blockInDebtorsPrison: true });
this.applyForElections = this._wrapWithUser((userId, req) => this.service.applyForElections(userId, req.body.electionIds), { blockInDebtorsPrison: true });
this.getPoliticalMyPowers = this._wrapWithUser((userId) => politicalPowersService.getMyPowers(userId));
this.getPoliticalTaxJurisdiction = this._wrapWithUser((userId) => politicalPowersService.getTaxJurisdiction(userId));
this.setPoliticalRegionTax = this._wrapWithUser((userId, req) =>
politicalPowersService.setRegionTax(userId, parseInt(req.params.regionId, 10), req.body?.percent), { blockInDebtorsPrison: true });
this.getPoliticalRegionTaxHistory = this._wrapWithUser((userId, req) =>
politicalPowersService.getRegionTaxHistory(userId, parseInt(req.params.regionId, 10), parseInt(req.query.limit || '5', 10)));
this.getPoliticalAppointableOffices = this._wrapWithUser((userId) => politicalPowersService.getAppointableOffices(userId));
this.createPoliticalAppointment = this._wrapWithUser(
(userId, req) =>
politicalPowersService.createAppointment(userId, {
targetCharacterId: req.body?.targetCharacterId,
officeTypeId: req.body?.officeTypeId,
regionId: req.body?.regionId
}),
{ successStatus: 201, blockInDebtorsPrison: true }
);
this.getRegions = this._wrapWithUser((userId) => this.service.getRegions(userId));
this.getBranchTaxes = this._wrapWithUser((userId, req) => this.service.getBranchTaxes(userId, req.params.branchId));
this.getProductPriceInRegion = this._wrapWithUser((userId, req) => {
@@ -227,7 +248,9 @@ class FalukantController {
if (Number.isNaN(regionId)) {
throw new Error('regionId is required');
}
return this.service.getAllProductPricesInRegion(userId, regionId);
const networkWorth = req.query.networkWorth === '1' || req.query.networkWorth === 'true';
const branchId = req.query.branchId != null ? parseInt(req.query.branchId, 10) : null;
return this.service.getAllProductPricesInRegion(userId, regionId, { networkWorth, branchId });
});
this.getProductPricesInCities = this._wrapWithUser((userId, req) => {
const productId = parseInt(req.query.productId, 10);
@@ -242,11 +265,17 @@ class FalukantController {
const body = req.body || {};
const items = Array.isArray(body.items) ? body.items : [];
const currentRegionId = body.currentRegionId != null ? parseInt(body.currentRegionId, 10) : null;
const includeTransportCosts = body.includeTransportCosts === true || body.includeTransportCosts === 'true';
const valid = items.map(i => ({
productId: parseInt(i.productId, 10),
currentPrice: parseFloat(i.currentPrice)
})).filter(i => !Number.isNaN(i.productId) && !Number.isNaN(i.currentPrice));
return this.service.getProductPricesInCitiesBatch(userId, valid, Number.isNaN(currentRegionId) ? null : currentRegionId);
return this.service.getProductPricesInCitiesBatch(
userId,
valid,
Number.isNaN(currentRegionId) ? null : currentRegionId,
{ includeTransportCosts }
);
});
this.renovate = this._wrapWithUser((userId, req) => this.service.renovate(userId, req.body.element), { blockInDebtorsPrison: true });
this.renovateAll = this._wrapWithUser((userId) => this.service.renovateAll(userId), { blockInDebtorsPrison: true });

View File

@@ -0,0 +1,79 @@
import Joi from 'joi';
import moderationService from '../services/moderationService.js';
const moderationController = {
async createReport(req, res) {
const allowedTargetTypes = [
'forum_message',
'gallery_image',
'guestbook_entry',
'one_to_one_message',
'diary_entry',
'user_profile',
'blog',
'blog_post'
];
const schema = Joi.object({
targetType: Joi.string().valid(...allowedTargetTypes).required(),
targetId: Joi.number().integer().min(1).optional(),
targetRef: Joi.string().trim().max(255).allow('').optional(),
reason: Joi.string().trim().min(3).max(120).required(),
details: Joi.string().allow('').max(2000).optional()
}).or('targetId', 'targetRef');
const { error, value } = schema.validate(req.body || {});
if (error) {
return res.status(400).json({ error: error.details[0].message });
}
try {
const { userid: userId } = req.headers;
const result = await moderationService.createReport(userId, value);
return res.status(201).json(result);
} catch (err) {
console.error('Error in createReport:', err);
return res.status(400).json({ error: err.message });
}
},
async listReports(req, res) {
try {
const { userid: userId } = req.headers;
const result = await moderationService.listReports(userId, req.query || {});
return res.status(200).json(result);
} catch (err) {
console.error('Error in listReports:', err);
return res.status(400).json({ error: err.message });
}
},
async updateReportStatus(req, res) {
const schema = Joi.object({
status: Joi.string().valid('open', 'in_review', 'resolved', 'rejected').required(),
reviewerNote: Joi.string().allow('').max(2000).optional()
});
const { error, value } = schema.validate(req.body || {});
if (error) {
return res.status(400).json({ error: error.details[0].message });
}
try {
const { userid: userId } = req.headers;
const result = await moderationService.updateReportStatus(userId, req.params.reportId, value);
return res.status(200).json(result);
} catch (err) {
console.error('Error in updateReportStatus:', err);
return res.status(400).json({ error: err.message });
}
},
async getOpenReportCount(req, res) {
try {
const { userid: userId } = req.headers;
const result = await moderationService.getOpenReportCount(userId);
return res.status(200).json(result);
} catch (err) {
console.error('Error in getOpenReportCount:', err);
return res.status(400).json({ error: err.message });
}
}
};
export default moderationController;

View File

@@ -96,9 +96,7 @@ const menuStructure = {
},
eroticChat: {
visible: ["over18"],
action: "openEroticChat",
view: "window",
class: "eroticChatWindow"
action: "openEroticChat"
}
}
},
@@ -280,6 +278,10 @@ const menuStructure = {
visible: ["mainadmin", "forum"],
path: "/admin/forum"
},
moderationReports: {
visible: ["mainadmin", "forum"],
path: "/admin/moderation/reports"
},
chatrooms: {
visible: ["mainadmin", "chatrooms"],
path: "/admin/chatrooms"
@@ -293,7 +295,7 @@ const menuStructure = {
path: "/admin/interests"
},
falukant: {
visible: ["mainadmin", "falukant"],
visible: ["mainadmin", "falukant", "worker_schedule_read"],
children: {
logentries: {
visible: ["mainadmin", "falukant"],
@@ -315,6 +317,10 @@ const menuStructure = {
visible: ["mainadmin", "falukant"],
path: "/admin/falukant/create-npc"
},
workerSchedules: {
visible: ["mainadmin", "worker_schedule_read"],
path: "/admin/falukant/worker-schedules"
},
}
},
minigames: {

View File

@@ -16,13 +16,16 @@ class SocialNetworkController {
this.getFoldersByUsername = this.getFoldersByUsername.bind(this);
this.deleteFolder = this.deleteFolder.bind(this);
this.getAdultFolders = this.getAdultFolders.bind(this);
this.getAdultFoldersByUsername = this.getAdultFoldersByUsername.bind(this);
this.createAdultFolder = this.createAdultFolder.bind(this);
this.getAdultFolderImageList = this.getAdultFolderImageList.bind(this);
this.uploadAdultImage = this.uploadAdultImage.bind(this);
this.getAdultImageByHash = this.getAdultImageByHash.bind(this);
this.changeAdultImage = this.changeAdultImage.bind(this);
this.listEroticVideos = this.listEroticVideos.bind(this);
this.getEroticVideosByUsername = this.getEroticVideosByUsername.bind(this);
this.uploadEroticVideo = this.uploadEroticVideo.bind(this);
this.changeEroticVideo = this.changeEroticVideo.bind(this);
this.getEroticVideoByHash = this.getEroticVideoByHash.bind(this);
this.reportEroticContent = this.reportEroticContent.bind(this);
this.createGuestbookEntry = this.createGuestbookEntry.bind(this);
@@ -157,8 +160,8 @@ class SocialNetworkController {
try {
const userId = req.headers.userid;
const { imageId } = req.params;
const { title, visibilities } = req.body;
const folderId = await this.socialNetworkService.changeImage(userId, imageId, title, visibilities);
const { title, visibilities, selectedUsers } = req.body;
const folderId = await this.socialNetworkService.changeImage(userId, imageId, title, visibilities, selectedUsers);
console.log('--->', folderId);
res.status(201).json(await this.socialNetworkService.getFolderImageList(userId, folderId));
} catch (error) {
@@ -208,6 +211,21 @@ class SocialNetworkController {
}
}
async getAdultFoldersByUsername(req, res) {
try {
const requestingUserId = req.headers.userid;
const { username } = req.params;
const folders = await this.socialNetworkService.getAdultFoldersByUsername(username, requestingUserId);
if (!folders) {
return res.status(404).json({ error: 'No folders found or access denied.' });
}
res.status(200).json(folders);
} catch (error) {
console.error('Error in getAdultFoldersByUsername:', error);
res.status(error.status || 500).json({ error: error.message });
}
}
async createAdultFolder(req, res) {
try {
const userId = req.headers.userid;
@@ -267,8 +285,8 @@ class SocialNetworkController {
try {
const userId = req.headers.userid;
const { imageId } = req.params;
const { title, visibilities } = req.body;
const folderId = await this.socialNetworkService.changeAdultImage(userId, imageId, title, visibilities);
const { title, visibilities, selectedUsers } = req.body;
const folderId = await this.socialNetworkService.changeAdultImage(userId, imageId, title, visibilities, selectedUsers);
res.status(201).json(await this.socialNetworkService.getAdultFolderImageList(userId, folderId));
} catch (error) {
console.error('Error in changeAdultImage:', error);
@@ -287,6 +305,18 @@ class SocialNetworkController {
}
}
async getEroticVideosByUsername(req, res) {
try {
const userId = req.headers.userid;
const { username } = req.params;
const videos = await this.socialNetworkService.getEroticVideosByUsername(username, userId);
res.status(200).json(videos);
} catch (error) {
console.error('Error in getEroticVideosByUsername:', error);
res.status(error.status || 500).json({ error: error.message });
}
}
async uploadEroticVideo(req, res) {
try {
const userId = req.headers.userid;
@@ -300,6 +330,18 @@ class SocialNetworkController {
}
}
async changeEroticVideo(req, res) {
try {
const userId = req.headers.userid;
const { videoId } = req.params;
const updatedVideo = await this.socialNetworkService.changeEroticVideo(userId, videoId, req.body);
res.status(200).json(updatedVideo);
} catch (error) {
console.error('Error in changeEroticVideo:', error);
res.status(error.status || 500).json({ error: error.message });
}
}
async getEroticVideoByHash(req, res) {
try {
const userId = req.headers.userid;

View File

@@ -18,15 +18,35 @@ class VocabController {
this.createChapter = this._wrapWithUser((userId, req) => this.service.createChapter(userId, req.params.languageId, req.body), { successStatus: 201 });
this.listLanguageVocabs = this._wrapWithUser((userId, req) => this.service.listLanguageVocabs(userId, req.params.languageId));
this.searchVocabs = this._wrapWithUser((userId, req) => this.service.searchVocabs(userId, req.params.languageId, req.query));
this.getLanguageDictionary = this._wrapWithUser((userId, req) =>
this.service.getLanguageDictionary(userId, req.params.languageId, req.query)
);
this.getChapter = this._wrapWithUser((userId, req) => this.service.getChapter(userId, req.params.chapterId));
this.listChapterVocabs = this._wrapWithUser((userId, req) => this.service.listChapterVocabs(userId, req.params.chapterId));
this.addVocabToChapter = this._wrapWithUser((userId, req) => this.service.addVocabToChapter(userId, req.params.chapterId, req.body), { successStatus: 201 });
this.getLessonVocabPool = this._wrapWithUser((userId, req) => this.service.getLessonVocabPool(userId, req.params.lessonId));
// Courses
this.createCourse = this._wrapWithUser((userId, req) => this.service.createCourse(userId, req.body), { successStatus: 201 });
this.getCourses = this._wrapWithUser((userId, req) => this.service.getCourses(userId, req.query));
this.getCourse = this._wrapWithUser((userId, req) => this.service.getCourse(userId, req.params.courseId));
this.getCompletedLessonVocabPool = this._wrapWithUser((userId, req) =>
this.service.getCompletedLessonVocabPool(userId, req.params.courseId, req.query.untilLessonId)
);
this.getCourseDictionary = this._wrapWithUser((userId, req) =>
this.service.getCourseDictionary(userId, req.params.courseId, req.query)
);
this.getVocabDistractorPool = this._wrapWithUser((userId, req) =>
this.service.getVocabDistractorPool(userId, req.params.courseId, req.query.beforeLessonId)
);
this.getCourseSrsDue = this._wrapWithUser((userId, req) =>
this.service.getCourseSrsDue(userId, req.params.courseId, req.query)
);
this.reviewSrsItem = this._wrapWithUser((userId, req) =>
this.service.reviewSrsItem(userId, req.body),
{ successStatus: 201 }
);
this.getCourseByShareCode = this._wrapWithUser((userId, req) => this.service.getCourseByShareCode(userId, req.body.shareCode));
this.updateCourse = this._wrapWithUser((userId, req) => this.service.updateCourse(userId, req.params.courseId, req.body));
this.deleteCourse = this._wrapWithUser((userId, req) => this.service.deleteCourse(userId, req.params.courseId));
@@ -41,10 +61,12 @@ class VocabController {
this.enrollInCourse = this._wrapWithUser((userId, req) => this.service.enrollInCourse(userId, req.params.courseId), { successStatus: 201 });
this.unenrollFromCourse = this._wrapWithUser((userId, req) => this.service.unenrollFromCourse(userId, req.params.courseId));
this.getMyCourses = this._wrapWithUser((userId) => this.service.getMyCourses(userId));
this.getDashboardLearningSummary = this._wrapWithUser((userId) => this.service.getDashboardLearningSummary(userId));
// Progress
this.getCourseProgress = this._wrapWithUser((userId, req) => this.service.getCourseProgress(userId, req.params.courseId));
this.updateLessonProgress = this._wrapWithUser((userId, req) => this.service.updateLessonProgress(userId, req.params.lessonId, req.body));
this.resetLessonProgress = this._wrapWithUser((userId, req) => this.service.resetMyLessonProgress(userId, req.params.lessonId));
// Grammar Exercises
this.getExerciseTypes = this._wrapWithUser((userId) => this.service.getExerciseTypes());
@@ -77,4 +99,3 @@ class VocabController {
}
export default VocabController;

View File

@@ -0,0 +1,42 @@
{
"24": true,
"25": true,
"26": true,
"27": true,
"28": true,
"29": true,
"30": true,
"31": true,
"32": true,
"33": true,
"34": true,
"35": true,
"36": true,
"37": true,
"38": true,
"39": true,
"40": true,
"41": true,
"42": true,
"43": true,
"44": true,
"45": true,
"46": true,
"47": true,
"48": true,
"49": true,
"50": true,
"51": true,
"52": true,
"53": true,
"54": true,
"55": true,
"56": true,
"57": true,
"58": true,
"59": true,
"60": true,
"61": true,
"62": true,
"63": true
}

View File

@@ -19,3 +19,20 @@ DB_PASS=
#
# Optional (Defaults siehe utils/sequelize.js)
# DB_CONNECT_TIMEOUT_MS=30000
# OAuth / OpenID Connect
# FRONTEND_URL muss auf die öffentliche Frontend-URL zeigen, damit der Provider nach dem Login
# korrekt auf den Callback im SPA zurückspringen kann.
# OAUTH_GOOGLE_CLIENT_ID=
# OAUTH_GOOGLE_CLIENT_SECRET=
# OAUTH_MICROSOFT_CLIENT_ID=
# OAUTH_MICROSOFT_CLIENT_SECRET=
# OAUTH_KEYCLOAK_ISSUER=
# OAUTH_KEYCLOAK_CLIENT_ID=
# OAUTH_KEYCLOAK_CLIENT_SECRET=
# OAUTH_ORY_ISSUER=
# OAUTH_ORY_CLIENT_ID=
# OAUTH_ORY_CLIENT_SECRET=
# OAUTH_ZITADEL_ISSUER=
# OAUTH_ZITADEL_CLIENT_ID=
# OAUTH_ZITADEL_CLIENT_SECRET=

View File

@@ -0,0 +1,94 @@
/**
* Periodischer Job: reputation_periodic für politische Amtsinhaber.
* Aufruf: systemd-Timer oder FALUKANT_POLITICAL_REPUTATION_JOB=1 (siehe server.js).
*/
import PoliticalOffice from '../models/falukant/data/political_office.js';
import PoliticalOfficeType from '../models/falukant/type/political_office_type.js';
import PoliticalOfficeBenefit from '../models/falukant/predefine/political_office_benefit.js';
import PoliticalOfficeBenefitType from '../models/falukant/type/political_office_benefit_type.js';
import PoliticalBenefitLastTick from '../models/falukant/data/political_benefit_last_tick.js';
import FalukantCharacter from '../models/falukant/data/character.js';
import FalukantUser from '../models/falukant/data/user.js';
import User from '../models/community/user.js';
import { sequelize } from '../utils/sequelize.js';
import { notifyUser } from '../utils/socket.js';
export async function runPoliticalReputationTicks() {
const offices = await PoliticalOffice.findAll({
include: [{ model: PoliticalOfficeType, as: 'type', attributes: ['id', 'name'] }]
});
const toNotify = new Set();
let ticks = 0;
for (const po of offices) {
const characterId = po.characterId;
const benefitRows = await PoliticalOfficeBenefit.findAll({
where: { officeTypeId: po.officeTypeId },
include: [
{
model: PoliticalOfficeBenefitType,
as: 'benefitDefinition',
attributes: ['tr'],
required: true,
where: { tr: 'reputation_periodic' }
}
]
});
for (const br of benefitRows) {
const v = br.value && typeof br.value === 'object' ? br.value : {};
const intervalDays = Math.max(1, Number(v.intervalDays ?? v.everyDays ?? 7));
const gain = Math.max(1, Number(v.gain ?? 1));
const [tickRow, created] = await PoliticalBenefitLastTick.findOrCreate({
where: {
characterId,
politicalOfficeBenefitId: br.id
},
defaults: {
characterId,
politicalOfficeBenefitId: br.id,
lastTickAt: new Date(po.createdAt),
ticksCount: 0
}
});
const baseMs = new Date(tickRow.lastTickAt).getTime();
const daysSince = Math.floor((Date.now() - baseMs) / 86400000);
if (daysSince < intervalDays) continue;
await sequelize.transaction(async (t) => {
const ch = await FalukantCharacter.findByPk(characterId, { transaction: t });
if (!ch) return;
const nextRep = Math.min(100, (ch.reputation ?? 0) + gain);
await ch.update({ reputation: nextRep }, { transaction: t });
await PoliticalBenefitLastTick.update(
{
lastTickAt: new Date(),
ticksCount: (tickRow.ticksCount || 0) + 1
},
{ where: { id: tickRow.id }, transaction: t }
);
});
ticks += 1;
toNotify.add(characterId);
console.info(
`[PoliticalBenefits] reputation_tick characterId=${characterId} benefitId=${br.id} gain=${gain}`
);
}
}
for (const characterId of toNotify) {
const ch = await FalukantCharacter.findByPk(characterId, { attributes: ['userId'] });
if (!ch?.userId) continue;
const fu = await FalukantUser.findOne({
where: { id: ch.userId },
include: [{ model: User, as: 'user', attributes: ['hashedId'] }]
});
const hid = fu?.user?.hashedId;
if (hid) notifyUser(hid, 'falukantUpdateStatus', {});
}
return { processedOffices: offices.length, ticksApplied: ticks };
}

View File

@@ -10,6 +10,9 @@ export const authenticate = async (req, res, next) => {
if (!user) {
return res.status(401).json({ error: 'Unauthorized: Invalid credentials' });
}
if (!user.active) {
return res.status(403).json({ error: 'Unauthorized: User blocked' });
}
try {
await updateUserTimestamp(user.id);
} catch (error) {

View File

View File

@@ -0,0 +1,70 @@
'use strict';
/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.createTable(
{ schema: 'community', tableName: 'oauth_identity' },
{
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true,
allowNull: false,
},
user_id: {
type: Sequelize.INTEGER,
allowNull: false,
references: {
model: { schema: 'community', tableName: 'user' },
key: 'id',
},
onUpdate: 'CASCADE',
onDelete: 'CASCADE',
},
provider: {
type: Sequelize.STRING(64),
allowNull: false,
},
issuer: {
type: Sequelize.TEXT,
allowNull: false,
},
subject: {
type: Sequelize.TEXT,
allowNull: false,
},
email: {
type: Sequelize.TEXT,
allowNull: true,
},
created_at: {
type: Sequelize.DATE,
allowNull: false,
defaultValue: Sequelize.literal('CURRENT_TIMESTAMP'),
},
updated_at: {
type: Sequelize.DATE,
allowNull: false,
defaultValue: Sequelize.literal('CURRENT_TIMESTAMP'),
},
}
);
await queryInterface.addIndex(
{ schema: 'community', tableName: 'oauth_identity' },
['provider', 'subject'],
{ unique: true, name: 'oauth_identity_provider_subject_uniq' }
);
await queryInterface.addIndex(
{ schema: 'community', tableName: 'oauth_identity' },
['user_id'],
{ name: 'oauth_identity_user_idx' }
);
},
async down(queryInterface) {
await queryInterface.dropTable({ schema: 'community', tableName: 'oauth_identity' });
},
};

View File

@@ -0,0 +1,45 @@
'use strict';
module.exports = {
up: async (queryInterface, Sequelize) => {
const table = { tableName: 'contact_message', schema: 'service' };
const columns = await queryInterface.describeTable(table);
if (!columns.answer) {
await queryInterface.addColumn(table, 'answer', {
type: Sequelize.TEXT,
allowNull: true
});
}
if (!columns.answered_at) {
await queryInterface.addColumn(table, 'answered_at', {
type: Sequelize.DATE,
allowNull: true
});
}
if (!columns.is_answered) {
await queryInterface.addColumn(table, 'is_answered', {
type: Sequelize.BOOLEAN,
allowNull: false,
defaultValue: false
});
}
},
down: async (queryInterface, Sequelize) => {
const table = { tableName: 'contact_message', schema: 'service' };
const columns = await queryInterface.describeTable(table);
if (columns.answer) {
await queryInterface.removeColumn(table, 'answer');
}
if (columns.answered_at) {
await queryInterface.removeColumn(table, 'answered_at');
}
if (columns.is_answered) {
await queryInterface.removeColumn(table, 'is_answered');
}
}
};

View File

@@ -0,0 +1,89 @@
'use strict';
/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.createTable(
{ schema: 'community', tableName: 'erotic_video_image_visibility' },
{
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true,
allowNull: false,
},
erotic_video_id: {
type: Sequelize.INTEGER,
allowNull: false,
references: {
model: { schema: 'community', tableName: 'erotic_video' },
key: 'id',
},
onUpdate: 'CASCADE',
onDelete: 'CASCADE',
},
visibility_type_id: {
type: Sequelize.INTEGER,
allowNull: false,
references: {
model: { schema: 'type', tableName: 'image_visibility' },
key: 'id',
},
onUpdate: 'CASCADE',
onDelete: 'CASCADE',
},
}
);
await queryInterface.createTable(
{ schema: 'community', tableName: 'erotic_video_visibility_user' },
{
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true,
allowNull: false,
},
erotic_video_id: {
type: Sequelize.INTEGER,
allowNull: false,
references: {
model: { schema: 'community', tableName: 'erotic_video' },
key: 'id',
},
onUpdate: 'CASCADE',
onDelete: 'CASCADE',
},
user_id: {
type: Sequelize.INTEGER,
allowNull: false,
references: {
model: { schema: 'community', tableName: 'user' },
key: 'id',
},
onUpdate: 'CASCADE',
onDelete: 'CASCADE',
},
}
);
await queryInterface.sequelize.query(`
INSERT INTO community.erotic_video_image_visibility (erotic_video_id, visibility_type_id)
SELECT ev.id, iv.id
FROM community.erotic_video ev
CROSS JOIN type.image_visibility iv
WHERE iv.description = 'adults'
AND NOT EXISTS (
SELECT 1
FROM community.erotic_video_image_visibility eviv
WHERE eviv.erotic_video_id = ev.id
AND eviv.visibility_type_id = iv.id
)
`);
},
async down(queryInterface) {
await queryInterface.dropTable({ schema: 'community', tableName: 'erotic_video_visibility_user' });
await queryInterface.dropTable({ schema: 'community', tableName: 'erotic_video_image_visibility' });
},
};

View File

@@ -0,0 +1,36 @@
"use strict";
/** Schwangerschaft (Admin / Spiel): erwarteter Geburtstermin + optionaler Vater-Charakter */
module.exports = {
async up(queryInterface) {
await queryInterface.sequelize.query(`
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_schema = 'falukant_data' AND table_name = 'character' AND column_name = 'pregnancy_due_at'
) THEN
ALTER TABLE falukant_data."character"
ADD COLUMN pregnancy_due_at TIMESTAMPTZ NULL;
END IF;
IF NOT EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_schema = 'falukant_data' AND table_name = 'character' AND column_name = 'pregnancy_father_character_id'
) THEN
ALTER TABLE falukant_data."character"
ADD COLUMN pregnancy_father_character_id INTEGER NULL
REFERENCES falukant_data."character"(id) ON DELETE SET NULL;
END IF;
END$$;
`);
},
async down(queryInterface) {
await queryInterface.sequelize.query(`
ALTER TABLE falukant_data."character" DROP COLUMN IF EXISTS pregnancy_father_character_id;
`);
await queryInterface.sequelize.query(`
ALTER TABLE falukant_data."character" DROP COLUMN IF EXISTS pregnancy_due_at;
`);
},
};

View File

@@ -0,0 +1,44 @@
'use strict';
module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.sequelize.query(`
ALTER TABLE community.vocab_course_lesson
ADD COLUMN IF NOT EXISTS didactic_mode TEXT,
ADD COLUMN IF NOT EXISTS phase_label TEXT,
ADD COLUMN IF NOT EXISTS block_number INTEGER,
ADD COLUMN IF NOT EXISTS difficulty_weight INTEGER,
ADD COLUMN IF NOT EXISTS new_unit_target INTEGER,
ADD COLUMN IF NOT EXISTS review_weight INTEGER,
ADD COLUMN IF NOT EXISTS is_intensive_review BOOLEAN NOT NULL DEFAULT FALSE;
COMMENT ON COLUMN community.vocab_course_lesson.didactic_mode IS
'Didaktischer Modus der Lektion, z.B. core_input, guided_dialogue, intensive_review oder checkpoint.';
COMMENT ON COLUMN community.vocab_course_lesson.phase_label IS
'Übergeordnete Lernphase, z.B. quickstart, daily_life oder stabilization.';
COMMENT ON COLUMN community.vocab_course_lesson.block_number IS
'Inhaltlicher Block für Konsolidierungs- und Wiederholungswellen.';
COMMENT ON COLUMN community.vocab_course_lesson.difficulty_weight IS
'Grobe relative Schwierigkeit der Lektion von leicht bis schwer.';
COMMENT ON COLUMN community.vocab_course_lesson.new_unit_target IS
'Empfohlene Zahl neuer Spracheinheiten in dieser Lektion.';
COMMENT ON COLUMN community.vocab_course_lesson.review_weight IS
'Wie stark Wiederholung in dieser Lektion dominieren soll, typischerweise 0 bis 100.';
COMMENT ON COLUMN community.vocab_course_lesson.is_intensive_review IS
'Markiert Lektionen, die als intensive Wiederholungsphase gedacht sind.';
`);
},
async down(queryInterface, Sequelize) {
await queryInterface.sequelize.query(`
ALTER TABLE community.vocab_course_lesson
DROP COLUMN IF EXISTS is_intensive_review,
DROP COLUMN IF EXISTS review_weight,
DROP COLUMN IF EXISTS new_unit_target,
DROP COLUMN IF EXISTS difficulty_weight,
DROP COLUMN IF EXISTS block_number,
DROP COLUMN IF EXISTS phase_label,
DROP COLUMN IF EXISTS didactic_mode;
`);
}
};

View File

@@ -0,0 +1,20 @@
'use strict';
module.exports = {
async up(queryInterface) {
await queryInterface.sequelize.query(`
ALTER TABLE community.vocab_course_progress
ADD COLUMN IF NOT EXISTS lesson_state JSONB NOT NULL DEFAULT '{}'::jsonb;
COMMENT ON COLUMN community.vocab_course_progress.lesson_state IS
'Persistierter UI- und Lernzustand pro Nutzer und Lektion fuer Resume im Sprachkurs.';
`);
},
async down(queryInterface) {
await queryInterface.sequelize.query(`
ALTER TABLE community.vocab_course_progress
DROP COLUMN IF EXISTS lesson_state;
`);
}
};

View File

@@ -0,0 +1,72 @@
'use strict';
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.sequelize.query(`
ALTER TABLE falukant_data.falukant_user
ADD COLUMN IF NOT EXISTS last_political_daily_salary_on date NULL;
`);
await queryInterface.sequelize.query(`
DO $$
BEGIN
IF EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_schema = 'falukant_predefine'
AND table_name = 'political_office_benefit'
AND column_name = 'political_office_id'
) AND NOT EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_schema = 'falukant_predefine'
AND table_name = 'political_office_benefit'
AND column_name = 'office_type_id'
) THEN
ALTER TABLE falukant_predefine.political_office_benefit
RENAME COLUMN political_office_id TO office_type_id;
ELSIF EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_schema = 'falukant_predefine'
AND table_name = 'political_office_benefit'
AND column_name = 'political_office_id'
) AND EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_schema = 'falukant_predefine'
AND table_name = 'political_office_benefit'
AND column_name = 'office_type_id'
) THEN
UPDATE falukant_predefine.political_office_benefit
SET office_type_id = COALESCE(office_type_id, political_office_id);
ALTER TABLE falukant_predefine.political_office_benefit
DROP COLUMN political_office_id;
END IF;
END $$;
`);
},
down: async (queryInterface, Sequelize) => {
await queryInterface.sequelize.query(`
ALTER TABLE falukant_data.falukant_user
DROP COLUMN IF EXISTS last_political_daily_salary_on;
`);
await queryInterface.sequelize.query(`
DO $$
BEGIN
IF EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_schema = 'falukant_predefine'
AND table_name = 'political_office_benefit'
AND column_name = 'office_type_id'
) AND NOT EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_schema = 'falukant_predefine'
AND table_name = 'political_office_benefit'
AND column_name = 'political_office_id'
) THEN
ALTER TABLE falukant_predefine.political_office_benefit
RENAME COLUMN office_type_id TO political_office_id;
END IF;
END $$;
`);
}
};

View File

@@ -0,0 +1,30 @@
'use strict';
/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up(queryInterface) {
await queryInterface.sequelize.query(`
INSERT INTO type.user_param_value (user_param_type_id, value, order_id)
SELECT upt.id, 'fr', COALESCE(
(SELECT MAX(v.order_id) FROM type.user_param_value v WHERE v.user_param_type_id = upt.id),
0
) + 1
FROM type.user_param upt
WHERE upt.description = 'language'
AND NOT EXISTS (
SELECT 1 FROM type.user_param_value x
WHERE x.user_param_type_id = upt.id AND x.value = 'fr'
);
`);
},
async down(queryInterface) {
await queryInterface.sequelize.query(`
DELETE FROM type.user_param_value v
USING type.user_param upt
WHERE v.user_param_type_id = upt.id
AND upt.description = 'language'
AND v.value = 'fr';
`);
},
};

View File

@@ -0,0 +1,22 @@
/* eslint-disable */
'use strict';
module.exports = {
async up(queryInterface) {
await queryInterface.sequelize.query(`
ALTER TABLE falukant_data.falukant_user
ADD COLUMN IF NOT EXISTS certificate_productions_count_since TIMESTAMPTZ;
`);
await queryInterface.sequelize.query(`
COMMENT ON COLUMN falukant_data.falukant_user.certificate_productions_count_since IS
'Daemon/UI: Zählt nur falukant_log.production-Zeilen mit COALESCE(production_timestamp, production_date::timestamp) >= diesem Wert; bei Stufenänderung (Aufstieg/Bankrott/Erbfolge) auf NOW() (YpDaemon QUERY_UPDATE_FALUKANT_USER_CERTIFICATE). NULL = alle passenden Log-Zeilen bis zur ersten Stufenänderung nach Migration. Kein Löschen der Logs zum Reset.';
`);
},
async down(queryInterface) {
await queryInterface.sequelize.query(`
ALTER TABLE falukant_data.falukant_user
DROP COLUMN IF EXISTS certificate_productions_count_since;
`);
}
};

View File

@@ -0,0 +1,61 @@
'use strict';
/** @param {import('sequelize').QueryInterface} queryInterface */
module.exports = {
async up(queryInterface) {
await queryInterface.sequelize.query(`
CREATE TABLE IF NOT EXISTS falukant_data.political_benefit_last_tick (
id serial PRIMARY KEY,
character_id integer NOT NULL
REFERENCES falukant_data."character"(id) ON DELETE CASCADE,
political_office_benefit_id integer NOT NULL
REFERENCES falukant_predefine.political_office_benefit(id) ON DELETE CASCADE,
last_tick_at timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP,
ticks_count integer NOT NULL DEFAULT 0,
CONSTRAINT political_benefit_last_tick_unique UNIQUE (character_id, political_office_benefit_id)
);
CREATE INDEX IF NOT EXISTS political_benefit_last_tick_character_idx
ON falukant_data.political_benefit_last_tick (character_id);
`);
await queryInterface.sequelize.query(`
CREATE TABLE IF NOT EXISTS falukant_data.region_tax_history (
id serial PRIMARY KEY,
region_id integer NOT NULL REFERENCES falukant_data.region(id) ON DELETE CASCADE,
old_tax_percent numeric(12,4) NOT NULL,
new_tax_percent numeric(12,4) NOT NULL,
setter_character_id integer NOT NULL REFERENCES falukant_data."character"(id) ON DELETE CASCADE,
political_office_id integer NULL REFERENCES falukant_data.political_office(id) ON DELETE SET NULL,
created_at timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX IF NOT EXISTS region_tax_history_region_idx
ON falukant_data.region_tax_history (region_id, created_at DESC);
`);
await queryInterface.sequelize.query(`
CREATE TABLE IF NOT EXISTS falukant_data.political_appointment (
id serial PRIMARY KEY,
appointer_character_id integer NOT NULL REFERENCES falukant_data."character"(id) ON DELETE CASCADE,
target_character_id integer NOT NULL REFERENCES falukant_data."character"(id) ON DELETE CASCADE,
office_type_id integer NOT NULL REFERENCES falukant_type.political_office_type(id) ON DELETE CASCADE,
region_id integer NOT NULL REFERENCES falukant_data.region(id) ON DELETE CASCADE,
status varchar(32) NOT NULL DEFAULT 'completed',
created_at timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP,
expires_at timestamptz NULL,
completed_political_office_id integer NULL REFERENCES falukant_data.political_office(id) ON DELETE SET NULL
);
CREATE INDEX IF NOT EXISTS political_appointment_appointer_idx
ON falukant_data.political_appointment (appointer_character_id, created_at DESC);
CREATE INDEX IF NOT EXISTS political_appointment_target_idx
ON falukant_data.political_appointment (target_character_id);
`);
},
async down(queryInterface) {
await queryInterface.sequelize.query(`
DROP TABLE IF EXISTS falukant_data.political_appointment;
DROP TABLE IF EXISTS falukant_data.region_tax_history;
DROP TABLE IF EXISTS falukant_data.political_benefit_last_tick;
`);
}
};

View File

@@ -0,0 +1,50 @@
'use strict';
/** Stufe pro politischem Amt (Tageshonorar: base + perRank × hierarchy_level). */
module.exports = {
async up(queryInterface) {
await queryInterface.sequelize.query(`
ALTER TABLE falukant_type.political_office_type
ADD COLUMN IF NOT EXISTS hierarchy_level INTEGER NOT NULL DEFAULT 1;
`);
await queryInterface.sequelize.query(`
UPDATE falukant_type.political_office_type AS pot
SET hierarchy_level = sub.lvl
FROM (VALUES
('assessor', 1),
('councillor', 1),
('council', 2),
('beadle', 2),
('town-clerk', 2),
('mayor', 3),
('master-builder', 2),
('village-major', 2),
('judge', 3),
('bailif', 3),
('taxman', 2),
('sheriff', 3),
('consultant', 3),
('treasurer', 4),
('hangman', 2),
('territorial-council', 3),
('territorial-council-speaker', 4),
('ruler-consultant', 4),
('state-administrator', 4),
('super-state-administrator', 5),
('governor', 5),
('ministry-helper', 4),
('minister', 5),
('chancellor', 6)
) AS sub(name, lvl)
WHERE pot.name = sub.name;
`);
},
async down(queryInterface) {
await queryInterface.sequelize.query(`
ALTER TABLE falukant_type.political_office_type
DROP COLUMN IF EXISTS hierarchy_level;
`);
}
};

View File

@@ -0,0 +1,32 @@
/* eslint-disable */
module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.sequelize.query(`
ALTER TABLE falukant_data.relationship_state
ADD COLUMN IF NOT EXISTS scandal_extra_daily_pct double precision NOT NULL DEFAULT 0;
`);
await queryInterface.sequelize.query(`
ALTER TABLE falukant_data.relationship_state
DROP CONSTRAINT IF EXISTS relationship_state_scandal_extra_daily_pct_chk;
`);
await queryInterface.sequelize.query(`
ALTER TABLE falukant_data.relationship_state
ADD CONSTRAINT relationship_state_scandal_extra_daily_pct_chk
CHECK (scandal_extra_daily_pct >= 0 AND scandal_extra_daily_pct <= 100);
`);
},
async down(queryInterface, Sequelize) {
await queryInterface.sequelize.query(`
ALTER TABLE falukant_data.relationship_state
DROP CONSTRAINT IF EXISTS relationship_state_scandal_extra_daily_pct_chk;
`);
await queryInterface.sequelize.query(`
ALTER TABLE falukant_data.relationship_state
DROP COLUMN IF EXISTS scandal_extra_daily_pct;
`);
},
};

View File

@@ -0,0 +1,54 @@
'use strict';
module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.sequelize.query(`
ALTER TABLE falukant_data.relationship_state
ADD COLUMN IF NOT EXISTS marriage_satisfaction integer;
`);
await queryInterface.sequelize.query(`
UPDATE falukant_data.relationship_state
SET marriage_satisfaction = 55
WHERE marriage_satisfaction IS NULL;
`);
await queryInterface.sequelize.query(`
ALTER TABLE falukant_data.relationship_state
ALTER COLUMN marriage_satisfaction SET DEFAULT 55;
`);
await queryInterface.sequelize.query(`
ALTER TABLE falukant_data.relationship_state
ALTER COLUMN marriage_satisfaction SET NOT NULL;
`);
await queryInterface.sequelize.query(`
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1
FROM pg_constraint
WHERE conname = 'relationship_state_marriage_satisfaction_check'
AND connamespace = 'falukant_data'::regnamespace
) THEN
ALTER TABLE falukant_data.relationship_state
ADD CONSTRAINT relationship_state_marriage_satisfaction_check
CHECK (marriage_satisfaction >= 0 AND marriage_satisfaction <= 100);
END IF;
END $$;
`);
},
async down(queryInterface, Sequelize) {
await queryInterface.sequelize.query(`
ALTER TABLE falukant_data.relationship_state
DROP CONSTRAINT IF EXISTS relationship_state_marriage_satisfaction_check;
`);
await queryInterface.sequelize.query(`
ALTER TABLE falukant_data.relationship_state
DROP COLUMN IF EXISTS marriage_satisfaction;
`);
}
};

View File

@@ -0,0 +1,49 @@
'use strict';
module.exports = {
async up(queryInterface) {
await queryInterface.sequelize.query(`
CREATE TABLE IF NOT EXISTS community.vocab_srs_item (
id SERIAL PRIMARY KEY,
user_id INTEGER NOT NULL REFERENCES community."user"(id) ON DELETE CASCADE,
course_id INTEGER NOT NULL REFERENCES community.vocab_course(id) ON DELETE CASCADE,
lesson_id INTEGER NULL REFERENCES community.vocab_course_lesson(id) ON DELETE SET NULL,
item_key VARCHAR(80) NOT NULL,
learning TEXT NOT NULL,
reference TEXT NOT NULL,
direction VARCHAR(8) NOT NULL DEFAULT 'BOTH',
stage INTEGER NOT NULL DEFAULT 0,
interval_days INTEGER NOT NULL DEFAULT 0,
last_reviewed_at TIMESTAMP WITH TIME ZONE NULL,
next_due_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
correct_count INTEGER NOT NULL DEFAULT 0,
wrong_count INTEGER NOT NULL DEFAULT 0,
lapse_count INTEGER NOT NULL DEFAULT 0,
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
CONSTRAINT vocab_srs_item_user_key_unique UNIQUE (user_id, item_key)
);
CREATE INDEX IF NOT EXISTS idx_vocab_srs_item_due
ON community.vocab_srs_item (user_id, course_id, next_due_at);
CREATE INDEX IF NOT EXISTS idx_vocab_srs_item_lesson
ON community.vocab_srs_item (user_id, course_id, lesson_id);
COMMENT ON TABLE community.vocab_srs_item IS
'Nutzerbezogener SRS-Fortschritt pro Vokabel/Phrase aus Sprachkursen.';
COMMENT ON COLUMN community.vocab_srs_item.item_key IS
'Stabiler deterministischer Schlüssel aus Kurs, Lektion und normalisiertem Begriffspaar.';
COMMENT ON COLUMN community.vocab_srs_item.stage IS
'SRS-Stufe. Höhere Stufen bedeuten längere Wiederholungsintervalle.';
COMMENT ON COLUMN community.vocab_srs_item.next_due_at IS
'Zeitpunkt, zu dem das Item wieder fällig ist.';
`);
},
async down(queryInterface) {
await queryInterface.sequelize.query(`
DROP TABLE IF EXISTS community.vocab_srs_item;
`);
}
};

View File

@@ -1,43 +0,0 @@
'use strict';
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.addColumn({
tableName: 'contact_message',
schema: 'service'
}, 'answer', {
type: Sequelize.TEXT,
allowNull: true
});
await queryInterface.addColumn({
tableName: 'contact_message',
schema: 'service'
}, 'answered_at', {
type: Sequelize.DATE,
allowNull: true
});
await queryInterface.addColumn({
tableName: 'contact_message',
schema: 'service'
}, 'is_answered', {
type: Sequelize.BOOLEAN,
allowNull: false,
defaultValue: false
});
},
down: async (queryInterface, Sequelize) => {
await queryInterface.removeColumn({
tableName: 'contact_message',
schema: 'service'
}, 'answer');
await queryInterface.removeColumn({
tableName: 'contact_message',
schema: 'service'
}, 'answered_at');
await queryInterface.removeColumn({
tableName: 'contact_message',
schema: 'service'
}, 'is_answered');
}
};

View File

@@ -0,0 +1,27 @@
# Backend-Migrationen (Sequelize)
## Aktive Migrationen
Neue Schema-Änderungen: nur noch Dateien unter **`migrations-active/`**. Ausführung z.B.:
`npm run db:migrate` (siehe `backend/package.json`, Pfad `migrations-active`).
## Archiv
Ältere, bereits auf den Umgebungen ausgerollte Migrationen liegen unter **`migrations-archive/`** und werden vom Sequelize-CLI **nicht** mehr ausgeführt.
Vor dem ersten Deploy nach dieser Aufteilung: fehlende Einträge in `"SequelizeMeta"` für die archivierten Dateinamen per SQL ergänzen, siehe **`sql/baseline-sequelize-meta-migrations-archive.sql`** (idempotent).
## Sonstiges in diesem Ordner
SQL-Hilfsdateien und ältere Notizen verbleiben hier (werden nicht vom CLI geladen).
## Falukant: Zertifikat und Produktionszählung
| Datei (Archiv) | Inhalt |
|--------|--------|
| `migrations-archive/20260402140000-add-certificate-productions-count-since.cjs` | Spalte `falukant_data.falukant_user.certificate_productions_count_since` (`TIMESTAMPTZ`, nullable) inkl. Kommentar. Setzt die DB-Grundlage dafür, dass Daemon, Backend und UI dieselbe Periode für „abgeschlossene Produktionen“ nutzen (Filter mit `COALESCE(production_timestamp, production_date::timestamp)` ab diesem Zeitpunkt; `NULL` = bisherige Historie). |
Eine parallele SQL-Migration im Daemon-Repository (z.B. `014_falukant_certificate_productions_count_since.sql`) kann dieselbe Spalte anlegen, wenn das Deployment dort getrennt ist Schema doppelt anlegen vermeiden.
Details zur Zähl- und Retention-Logik: `docs/FALUKANT_PRODUCTION_CERTIFICATE.md`.

View File

@@ -6,6 +6,7 @@ import Room from './chat/room.js';
import User from './community/user.js';
import UserParam from './community/user_param.js';
import UserDashboard from './community/user_dashboard.js';
import OAuthIdentity from './community/oauth_identity.js';
import UserParamType from './type/user_param.js';
import UserRightType from './type/user_right.js';
import UserRight from './community/user_right.js';
@@ -25,6 +26,8 @@ import ImageVisibilityUser from './community/image_visibility_user.js';
import FolderImageVisibility from './community/folder_image_visibility.js';
import ImageImageVisibility from './community/image_image_visibility.js';
import FolderVisibilityUser from './community/folder_visibility_user.js';
import EroticVideoImageVisibility from './community/erotic_video_image_visibility.js';
import EroticVideoVisibilityUser from './community/erotic_video_visibility_user.js';
import GuestbookEntry from './community/guestbook.js';
import Forum from './forum/forum.js';
import Title from './forum/title.js';
@@ -92,6 +95,9 @@ import Candidate from './falukant/data/candidate.js';
import Vote from './falukant/data/vote.js';
import PoliticalOfficeType from './falukant/type/political_office_type.js';
import PoliticalOffice from './falukant/data/political_office.js';
import PoliticalBenefitLastTick from './falukant/data/political_benefit_last_tick.js';
import RegionTaxHistory from './falukant/data/region_tax_history.js';
import PoliticalAppointment from './falukant/data/political_appointment.js';
import PoliticalOfficeBenefit from './falukant/predefine/political_office_benefit.js';
import PoliticalOfficeBenefitType from './falukant/type/political_office_benefit_type.js';
import PoliticalOfficeRequirement from './falukant/predefine/political_office_prerequisite.js';
@@ -121,6 +127,7 @@ import VocabCourseProgress from './community/vocab_course_progress.js';
import VocabGrammarExerciseType from './community/vocab_grammar_exercise_type.js';
import VocabGrammarExercise from './community/vocab_grammar_exercise.js';
import VocabGrammarExerciseProgress from './community/vocab_grammar_exercise_progress.js';
import VocabSrsItem from './community/vocab_srs_item.js';
import CalendarEvent from './community/calendar_event.js';
import Campaign from './match3/campaign.js';
import Match3Level from './match3/level.js';
@@ -176,6 +183,9 @@ export default function setupAssociations() {
User.hasOne(UserDashboard, { foreignKey: 'userId', as: 'dashboard' });
UserDashboard.belongsTo(User, { foreignKey: 'userId', as: 'user' });
User.hasMany(OAuthIdentity, { foreignKey: 'userId', as: 'oauthIdentities' });
OAuthIdentity.belongsTo(User, { foreignKey: 'userId', as: 'user' });
UserParamValue.belongsTo(UserParamType, { foreignKey: 'userParamTypeId', as: 'user_param_value_type' });
UserParamType.hasMany(UserParamValue, { foreignKey: 'userParamTypeId', as: 'user_param_type_value' });
@@ -242,6 +252,17 @@ export default function setupAssociations() {
otherKey: 'imageId'
});
EroticVideo.belongsToMany(ImageVisibilityType, {
through: EroticVideoImageVisibility,
foreignKey: 'eroticVideoId',
otherKey: 'visibilityTypeId'
});
ImageVisibilityType.belongsToMany(EroticVideo, {
through: EroticVideoImageVisibility,
foreignKey: 'visibilityTypeId',
otherKey: 'eroticVideoId'
});
Folder.belongsToMany(ImageVisibilityUser, {
through: FolderVisibilityUser,
foreignKey: 'folderId',
@@ -253,6 +274,19 @@ export default function setupAssociations() {
otherKey: 'folderId'
});
EroticVideo.belongsToMany(User, {
through: EroticVideoVisibilityUser,
foreignKey: 'eroticVideoId',
otherKey: 'userId',
as: 'selectedVisibilityUsers'
});
User.belongsToMany(EroticVideo, {
through: EroticVideoVisibilityUser,
foreignKey: 'userId',
otherKey: 'eroticVideoId',
as: 'visibleEroticVideos'
});
// Guestbook related associations
User.hasMany(GuestbookEntry, { foreignKey: 'recipientId', as: 'receivedEntries' });
User.hasMany(GuestbookEntry, { foreignKey: 'senderId', as: 'sentEntries' });
@@ -353,6 +387,8 @@ export default function setupAssociations() {
FalukantCharacter.belongsTo(RegionData, { foreignKey: 'regionId', as: 'region' });
RegionData.hasMany(FalukantCharacter, { foreignKey: 'regionId', as: 'charactersInRegion' });
FalukantCharacter.belongsTo(FalukantCharacter, { foreignKey: 'pregnancyFatherCharacterId', as: 'pregnancyFather' });
FalukantStock.belongsTo(FalukantStockType, { foreignKey: 'stockTypeId', as: 'stockType' });
FalukantStockType.hasMany(FalukantStock, { foreignKey: 'stockTypeId', as: 'stocks' });
@@ -764,6 +800,48 @@ export default function setupAssociations() {
as: 'heldOffice'
});
PoliticalBenefitLastTick.belongsTo(FalukantCharacter, {
foreignKey: 'characterId',
as: 'character'
});
FalukantCharacter.hasMany(PoliticalBenefitLastTick, {
foreignKey: 'characterId',
as: 'politicalBenefitTicks'
});
PoliticalBenefitLastTick.belongsTo(PoliticalOfficeBenefit, {
foreignKey: 'politicalOfficeBenefitId',
as: 'officeBenefit'
});
RegionTaxHistory.belongsTo(RegionData, { foreignKey: 'regionId', as: 'region' });
RegionData.hasMany(RegionTaxHistory, { foreignKey: 'regionId', as: 'taxHistory' });
RegionTaxHistory.belongsTo(FalukantCharacter, {
foreignKey: 'setterCharacterId',
as: 'setterCharacter'
});
RegionTaxHistory.belongsTo(PoliticalOffice, {
foreignKey: 'politicalOfficeId',
as: 'sourceOffice'
});
PoliticalAppointment.belongsTo(FalukantCharacter, {
foreignKey: 'appointerCharacterId',
as: 'appointer'
});
PoliticalAppointment.belongsTo(FalukantCharacter, {
foreignKey: 'targetCharacterId',
as: 'targetCharacter'
});
PoliticalAppointment.belongsTo(PoliticalOfficeType, {
foreignKey: 'officeTypeId',
as: 'officeType'
});
PoliticalAppointment.belongsTo(RegionData, { foreignKey: 'regionId', as: 'region' });
PoliticalAppointment.belongsTo(PoliticalOffice, {
foreignKey: 'completedPoliticalOfficeId',
as: 'completedOffice'
});
// elections
Election.belongsTo(PoliticalOfficeType, {
foreignKey: 'officeTypeId',
@@ -1104,6 +1182,13 @@ export default function setupAssociations() {
VocabGrammarExerciseProgress.belongsTo(VocabGrammarExercise, { foreignKey: 'exerciseId', as: 'exercise' });
VocabGrammarExercise.hasMany(VocabGrammarExerciseProgress, { foreignKey: 'exerciseId', as: 'progress' });
VocabSrsItem.belongsTo(User, { foreignKey: 'userId', as: 'user' });
User.hasMany(VocabSrsItem, { foreignKey: 'userId', as: 'vocabSrsItems' });
VocabSrsItem.belongsTo(VocabCourse, { foreignKey: 'courseId', as: 'course' });
VocabCourse.hasMany(VocabSrsItem, { foreignKey: 'courseId', as: 'srsItems' });
VocabSrsItem.belongsTo(VocabCourseLesson, { foreignKey: 'lessonId', as: 'lesson' });
VocabCourseLesson.hasMany(VocabSrsItem, { foreignKey: 'lessonId', as: 'srsItems' });
// Calendar associations
CalendarEvent.belongsTo(User, { foreignKey: 'userId', as: 'user' });
User.hasMany(CalendarEvent, { foreignKey: 'userId', as: 'calendarEvents' });

View File

@@ -0,0 +1,26 @@
import { sequelize } from '../../utils/sequelize.js';
import { DataTypes } from 'sequelize';
const EroticVideoImageVisibility = sequelize.define('erotic_video_image_visibility', {
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
allowNull: false
},
eroticVideoId: {
type: DataTypes.INTEGER,
allowNull: false
},
visibilityTypeId: {
type: DataTypes.INTEGER,
allowNull: false
}
}, {
tableName: 'erotic_video_image_visibility',
timestamps: false,
underscored: true,
schema: 'community'
});
export default EroticVideoImageVisibility;

View File

@@ -0,0 +1,26 @@
import { sequelize } from '../../utils/sequelize.js';
import { DataTypes } from 'sequelize';
const EroticVideoVisibilityUser = sequelize.define('erotic_video_visibility_user', {
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
allowNull: false
},
eroticVideoId: {
type: DataTypes.INTEGER,
allowNull: false
},
userId: {
type: DataTypes.INTEGER,
allowNull: false
}
}, {
tableName: 'erotic_video_visibility_user',
timestamps: false,
underscored: true,
schema: 'community'
});
export default EroticVideoVisibilityUser;

View File

@@ -0,0 +1,54 @@
import { sequelize } from '../../utils/sequelize.js';
import { DataTypes } from 'sequelize';
const OAuthIdentity = sequelize.define('oauth_identity', {
userId: {
type: DataTypes.INTEGER,
allowNull: false,
field: 'user_id'
},
provider: {
type: DataTypes.STRING(64),
allowNull: false
},
issuer: {
type: DataTypes.TEXT,
allowNull: false
},
subject: {
type: DataTypes.TEXT,
allowNull: false
},
email: {
type: DataTypes.TEXT,
allowNull: true
},
createdAt: {
type: DataTypes.DATE,
allowNull: false,
field: 'created_at',
defaultValue: DataTypes.NOW
},
updatedAt: {
type: DataTypes.DATE,
allowNull: false,
field: 'updated_at',
defaultValue: DataTypes.NOW
}
}, {
tableName: 'oauth_identity',
schema: 'community',
underscored: true,
timestamps: false,
indexes: [
{
unique: true,
fields: ['provider', 'subject']
},
{
fields: ['user_id']
}
]
});
export default OAuthIdentity;

View File

@@ -8,6 +8,26 @@ function encodeEncryptedValueToBlob(value) {
return Buffer.from(encrypted, 'utf8');
}
/** Nur echte Adressen zurückgeben — verhindert Anzeige von Base64-/Key-artigem Müll bei fehlender Entschlüsselung. */
function looksLikePlausibleEmail(s) {
if (typeof s !== 'string') {
return false;
}
const t = s.trim();
if (!t || t.length > 254) {
return false;
}
return /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/i.test(t);
}
function normalizeEmailCandidate(s) {
if (!s || typeof s !== 'string') {
return null;
}
const t = s.trim();
return looksLikePlausibleEmail(t) ? t : null;
}
function decodeEncryptedBlob(value) {
if (!value) {
return null;
@@ -16,8 +36,9 @@ function decodeEncryptedBlob(value) {
try {
const encryptedUtf8 = value.toString('utf8');
const decryptedUtf8 = decrypt(encryptedUtf8);
if (decryptedUtf8) {
return decryptedUtf8;
const fromUtf8 = normalizeEmailCandidate(decryptedUtf8);
if (fromUtf8) {
return fromUtf8;
}
} catch (error) {
console.warn('Email utf8 decryption failed, trying legacy hex format:', error.message);
@@ -26,15 +47,16 @@ function decodeEncryptedBlob(value) {
try {
const encryptedHex = value.toString('hex');
const decryptedHex = decrypt(encryptedHex);
if (decryptedHex) {
return decryptedHex;
const fromHex = normalizeEmailCandidate(decryptedHex);
if (fromHex) {
return fromHex;
}
} catch (error) {
console.warn('Email legacy hex decryption failed:', error.message);
}
try {
return value.toString('utf8');
return normalizeEmailCandidate(value.toString('utf8'));
} catch (error) {
console.warn('Email could not be read as plain text:', error.message);
return null;

View File

@@ -48,6 +48,42 @@ VocabCourseLesson.init({
defaultValue: 'vocab',
field: 'lesson_type'
},
didacticMode: {
type: DataTypes.TEXT,
allowNull: true,
field: 'didactic_mode'
},
phaseLabel: {
type: DataTypes.TEXT,
allowNull: true,
field: 'phase_label'
},
blockNumber: {
type: DataTypes.INTEGER,
allowNull: true,
field: 'block_number'
},
difficultyWeight: {
type: DataTypes.INTEGER,
allowNull: true,
field: 'difficulty_weight'
},
newUnitTarget: {
type: DataTypes.INTEGER,
allowNull: true,
field: 'new_unit_target'
},
reviewWeight: {
type: DataTypes.INTEGER,
allowNull: true,
field: 'review_weight'
},
isIntensiveReview: {
type: DataTypes.BOOLEAN,
allowNull: false,
defaultValue: false,
field: 'is_intensive_review'
},
audioUrl: {
type: DataTypes.TEXT,
allowNull: true,

View File

@@ -34,6 +34,12 @@ VocabCourseProgress.init({
allowNull: false,
defaultValue: 0
},
lessonState: {
type: DataTypes.JSONB,
allowNull: false,
defaultValue: {},
field: 'lesson_state'
},
lastAccessedAt: {
type: DataTypes.DATE,
allowNull: true,

View File

@@ -0,0 +1,94 @@
import { Model, DataTypes } from 'sequelize';
import { sequelize } from '../../utils/sequelize.js';
class VocabSrsItem extends Model {}
VocabSrsItem.init({
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
userId: {
type: DataTypes.INTEGER,
allowNull: false,
field: 'user_id'
},
courseId: {
type: DataTypes.INTEGER,
allowNull: false,
field: 'course_id'
},
lessonId: {
type: DataTypes.INTEGER,
allowNull: true,
field: 'lesson_id'
},
itemKey: {
type: DataTypes.STRING(80),
allowNull: false,
field: 'item_key'
},
learning: {
type: DataTypes.TEXT,
allowNull: false
},
reference: {
type: DataTypes.TEXT,
allowNull: false
},
direction: {
type: DataTypes.STRING(8),
allowNull: false,
defaultValue: 'BOTH'
},
stage: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: 0
},
intervalDays: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: 0,
field: 'interval_days'
},
lastReviewedAt: {
type: DataTypes.DATE,
allowNull: true,
field: 'last_reviewed_at'
},
nextDueAt: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: DataTypes.NOW,
field: 'next_due_at'
},
correctCount: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: 0,
field: 'correct_count'
},
wrongCount: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: 0,
field: 'wrong_count'
},
lapseCount: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: 0,
field: 'lapse_count'
}
}, {
sequelize,
modelName: 'VocabSrsItem',
tableName: 'vocab_srs_item',
schema: 'community',
timestamps: true,
underscored: true
});
export default VocabSrsItem;

View File

@@ -45,6 +45,14 @@ FalukantCharacter.init(
min: 0,
max: 100
}
},
pregnancyDueAt: {
type: DataTypes.DATE,
allowNull: true,
},
pregnancyFatherCharacterId: {
type: DataTypes.INTEGER,
allowNull: true,
}
},
{
@@ -53,7 +61,12 @@ FalukantCharacter.init(
tableName: 'character',
schema: 'falukant_data',
timestamps: true,
underscored: true}
underscored: true,
// Spalten erst nach Migration 20260330000000; ohne Exclude würde SELECT/INSERT auf alten DBs fehlschlagen
defaultScope: {
attributes: { exclude: ['pregnancyDueAt', 'pregnancyFatherCharacterId'] },
},
}
);
export default FalukantCharacter;

View File

@@ -33,6 +33,10 @@ Director.init({
type: DataTypes.BOOLEAN,
allowNull: false,
defaultValue: true},
autoAdjustIncome: {
type: DataTypes.BOOLEAN,
allowNull: false,
defaultValue: false},
lastSalaryPayout: {
type: DataTypes.DATE,
allowNull: false,

View File

@@ -0,0 +1,56 @@
import { Model, DataTypes } from 'sequelize';
import { sequelize } from '../../../utils/sequelize.js';
class PoliticalAppointment extends Model {}
PoliticalAppointment.init(
{
appointerCharacterId: {
type: DataTypes.INTEGER,
allowNull: false,
field: 'appointer_character_id'
},
targetCharacterId: {
type: DataTypes.INTEGER,
allowNull: false,
field: 'target_character_id'
},
officeTypeId: {
type: DataTypes.INTEGER,
allowNull: false,
field: 'office_type_id'
},
regionId: {
type: DataTypes.INTEGER,
allowNull: false,
field: 'region_id'
},
status: {
type: DataTypes.STRING(32),
allowNull: false,
defaultValue: 'completed'
},
expiresAt: {
type: DataTypes.DATE,
allowNull: true,
field: 'expires_at'
},
completedPoliticalOfficeId: {
type: DataTypes.INTEGER,
allowNull: true,
field: 'completed_political_office_id'
},
},
{
sequelize,
modelName: 'PoliticalAppointment',
tableName: 'political_appointment',
schema: 'falukant_data',
timestamps: true,
createdAt: 'created_at',
updatedAt: false,
underscored: true
}
);
export default PoliticalAppointment;

View File

@@ -0,0 +1,40 @@
import { Model, DataTypes } from 'sequelize';
import { sequelize } from '../../../utils/sequelize.js';
class PoliticalBenefitLastTick extends Model {}
PoliticalBenefitLastTick.init(
{
characterId: {
type: DataTypes.INTEGER,
allowNull: false,
field: 'character_id'
},
politicalOfficeBenefitId: {
type: DataTypes.INTEGER,
allowNull: false,
field: 'political_office_benefit_id'
},
lastTickAt: {
type: DataTypes.DATE,
allowNull: false,
field: 'last_tick_at'
},
ticksCount: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: 0,
field: 'ticks_count'
}
},
{
sequelize,
modelName: 'PoliticalBenefitLastTick',
tableName: 'political_benefit_last_tick',
schema: 'falukant_data',
timestamps: false,
underscored: true
}
);
export default PoliticalBenefitLastTick;

View File

@@ -0,0 +1,46 @@
import { Model, DataTypes } from 'sequelize';
import { sequelize } from '../../../utils/sequelize.js';
class RegionTaxHistory extends Model {}
RegionTaxHistory.init(
{
regionId: {
type: DataTypes.INTEGER,
allowNull: false,
field: 'region_id'
},
oldTaxPercent: {
type: DataTypes.DECIMAL(12, 4),
allowNull: false,
field: 'old_tax_percent'
},
newTaxPercent: {
type: DataTypes.DECIMAL(12, 4),
allowNull: false,
field: 'new_tax_percent'
},
setterCharacterId: {
type: DataTypes.INTEGER,
allowNull: false,
field: 'setter_character_id'
},
politicalOfficeId: {
type: DataTypes.INTEGER,
allowNull: true,
field: 'political_office_id'
}
},
{
sequelize,
modelName: 'RegionTaxHistory',
tableName: 'region_tax_history',
schema: 'falukant_data',
timestamps: true,
createdAt: 'created_at',
updatedAt: false,
underscored: true
}
);
export default RegionTaxHistory;

View File

@@ -88,6 +88,16 @@ RelationshipState.init(
min: 0,
},
},
scandalExtraDailyPct: {
type: DataTypes.FLOAT,
allowNull: false,
defaultValue: 0,
validate: {
min: 0,
max: 100,
},
field: 'scandal_extra_daily_pct',
},
monthsUnderfunded: {
type: DataTypes.INTEGER,
allowNull: false,

View File

@@ -29,6 +29,10 @@ FalukantUser.init({
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: 1},
certificateProductionsCountSince: {
type: DataTypes.DATE,
allowNull: true
},
mainBranchRegionId: {
type: DataTypes.INTEGER,
allowNull: true
@@ -36,6 +40,11 @@ FalukantUser.init({
lastNobilityAdvanceAt: {
type: DataTypes.DATE,
allowNull: true
},
lastPoliticalDailySalaryOn: {
type: DataTypes.DATEONLY,
allowNull: true,
field: 'last_political_daily_salary_on'
}
}, {
sequelize,

View File

@@ -4,6 +4,11 @@ import { sequelize } from '../../../utils/sequelize.js';
class UserHouse extends Model { }
UserHouse.init({
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
roofCondition: {
type: DataTypes.INTEGER,
allowNull: false,

View File

@@ -23,7 +23,11 @@ DayProduction.init({
productionDate: {
type: DataTypes.DATEONLY,
allowNull: false,
defaultValue: sequelize.literal('CURRENT_DATE')}
defaultValue: sequelize.literal('CURRENT_DATE')},
completionCount: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: 1}
}, {
sequelize,
modelName: 'DayProduction',

View File

@@ -1,7 +1,6 @@
// falukant/predefine/political_office_benefit.js
import { Model, DataTypes } from 'sequelize';
import { sequelize } from '../../../utils/sequelize.js';
import PoliticalOfficeBenefitType from '../type/political_office_benefit_type.js';
class PoliticalOfficeBenefit extends Model {}
@@ -9,31 +8,29 @@ PoliticalOfficeBenefit.init({
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true},
politicalOfficeId: {
autoIncrement: true
},
officeTypeId: {
type: DataTypes.INTEGER,
allowNull: false},
allowNull: false,
field: 'office_type_id'
},
benefitTypeId: {
type: DataTypes.INTEGER,
allowNull: false},
allowNull: false,
field: 'benefit_type_id'
},
value: {
type: DataTypes.JSONB,
allowNull: false}}, {
allowNull: false
}
}, {
sequelize,
modelName: 'PoliticalOfficeBenefit',
tableName: 'political_office_benefit',
schema: 'falukant_predefine',
timestamps: false,
underscored: true});
// Association
PoliticalOfficeBenefit.belongsTo(PoliticalOfficeBenefitType, {
foreignKey: 'benefit_type_id',
as: 'benefitType'
});
PoliticalOfficeBenefitType.hasMany(PoliticalOfficeBenefit, {
foreignKey: 'benefit_type_id',
as: 'benefits'
underscored: true
});
export default PoliticalOfficeBenefit;

View File

@@ -20,7 +20,15 @@ PoliticalOfficeType.init({
termLength: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: 0}}, {
defaultValue: 0},
/** Stufe für Tageshonorar (base + perRank × level) und Sortierung; 1 = niedrigstes politisches Level */
hierarchyLevel: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: 1,
field: 'hierarchy_level'
}
}, {
sequelize,
modelName: 'PoliticalOfficeType',
tableName: 'political_office_type',

View File

@@ -8,6 +8,7 @@ import WidgetType from './type/widget_type.js';
import User from './community/user.js';
import UserParam from './community/user_param.js';
import UserDashboard from './community/user_dashboard.js';
import OAuthIdentity from './community/oauth_identity.js';
import Login from './logs/login.js';
import UserRight from './community/user_right.js';
import InterestType from './type/interest.js';
@@ -25,6 +26,8 @@ import ImageVisibilityUser from './community/image_visibility_user.js';
import FolderImageVisibility from './community/folder_image_visibility.js';
import ImageImageVisibility from './community/image_image_visibility.js';
import FolderVisibilityUser from './community/folder_visibility_user.js';
import EroticVideoImageVisibility from './community/erotic_video_image_visibility.js';
import EroticVideoVisibilityUser from './community/erotic_video_visibility_user.js';
import GuestbookEntry from './community/guestbook.js';
import DiaryHistory from './community/diary_history.js';
import Diary from './community/diary.js';
@@ -114,6 +117,9 @@ import PoliticalOfficeRequirement from './falukant/predefine/political_office_pr
import PoliticalOfficeBenefitType from './falukant/type/political_office_benefit_type.js';
import PoliticalOfficeBenefit from './falukant/predefine/political_office_benefit.js';
import PoliticalOffice from './falukant/data/political_office.js';
import PoliticalBenefitLastTick from './falukant/data/political_benefit_last_tick.js';
import RegionTaxHistory from './falukant/data/region_tax_history.js';
import PoliticalAppointment from './falukant/data/political_appointment.js';
import Election from './falukant/data/election.js';
import Candidate from './falukant/data/candidate.js';
import Vote from './falukant/data/vote.js';
@@ -151,6 +157,7 @@ import VocabCourseProgress from './community/vocab_course_progress.js';
import VocabGrammarExerciseType from './community/vocab_grammar_exercise_type.js';
import VocabGrammarExercise from './community/vocab_grammar_exercise.js';
import VocabGrammarExerciseProgress from './community/vocab_grammar_exercise_progress.js';
import VocabSrsItem from './community/vocab_srs_item.js';
import CalendarEvent from './community/calendar_event.js';
const models = {
@@ -162,6 +169,7 @@ const models = {
User,
UserParam,
UserDashboard,
OAuthIdentity,
Login,
UserRight,
InterestType,
@@ -179,6 +187,8 @@ const models = {
FolderImageVisibility,
ImageImageVisibility,
FolderVisibilityUser,
EroticVideoImageVisibility,
EroticVideoVisibilityUser,
GuestbookEntry,
DiaryHistory,
Diary,
@@ -258,6 +268,9 @@ const models = {
PoliticalOfficeBenefitType,
PoliticalOfficeBenefit,
PoliticalOffice,
PoliticalBenefitLastTick,
RegionTaxHistory,
PoliticalAppointment,
Election,
Candidate,
Vote,
@@ -308,6 +321,7 @@ const models = {
VocabGrammarExerciseType,
VocabGrammarExercise,
VocabGrammarExerciseProgress,
VocabSrsItem,
// Calendar
CalendarEvent,

View File

@@ -127,26 +127,29 @@ export async function createTriggers() {
const updateMoney = `
CREATE OR REPLACE FUNCTION falukant_data.update_money(
p_falukant_user_id integer,
p_money_change numeric,
p_activity text,
p_changed_by integer DEFAULT NULL
p_money_change numeric,
p_activity text,
p_changed_by integer DEFAULT NULL::integer
)
RETURNS void
LANGUAGE plpgsql
AS $function$
DECLARE
v_money_before numeric(10,2);
v_money_after numeric(10,2);
v_money_before numeric(14,2);
v_money_after numeric(14,2);
v_moneyflow_id bigint;
BEGIN
SELECT money
INTO v_money_before
FROM falukant_data.falukant_user
WHERE id = p_falukant_user_id;
INTO v_money_before
FROM falukant_data.falukant_user
WHERE id = p_falukant_user_id;
IF NOT FOUND THEN
RAISE EXCEPTION 'FalukantUser mit ID % nicht gefunden', p_falukant_user_id;
END IF;
v_money_after := v_money_before + p_money_change;
INSERT INTO falukant_log.moneyflow (
falukant_user_id,
activity,
@@ -160,22 +163,24 @@ export async function createTriggers() {
p_falukant_user_id,
p_activity,
v_money_before,
NULL, -- Wird gleich aktualisiert
NULL, -- wird gleich aktualisiert
p_money_change,
p_changed_by,
NOW()
)
RETURNING id INTO v_moneyflow_id;
UPDATE falukant_data.falukant_user
SET money = v_money_after
WHERE id = p_falukant_user_id;
SET money = v_money_after
WHERE id = p_falukant_user_id;
UPDATE falukant_log.moneyflow
SET money_after = (
SELECT money
FROM falukant_data.falukant_user
SET money_after = (
SELECT money
FROM falukant_data.falukant_user
WHERE id = p_falukant_user_id
)
WHERE id = v_moneyflow_id;
)
WHERE id = v_moneyflow_id;
END;
$function$;
`;

5712
backend/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
{
"name": "backend",
"version": "1.0.0",
"description": "",
"description": "Nach Änderungen an dependencies: npm install ausführen und package-lock.json committen (npm ci im Deploy).",
"type": "module",
"main": "index.js",
"scripts": {
@@ -9,17 +9,21 @@
"dev": "NODE_ENV=development node server.js",
"start-daemon": "node daemonServer.js",
"sync-db": "node sync-database.js",
"db:migrate": "sequelize-cli db:migrate --config config/sequelize-cli.cjs --migrations-path migrations-active --env production",
"sync-tables": "node sync-tables-only.js",
"check-connections": "node check-connections.js",
"cleanup-connections": "node cleanup-connections.js",
"diag:town-worth": "QUIET_ENV_LOGS=1 DOTENV_CONFIG_QUIET=1 node scripts/falukant-town-product-worth-stats.mjs",
"diag:moneyflow": "QUIET_ENV_LOGS=1 DOTENV_CONFIG_QUIET=1 node scripts/falukant-moneyflow-report.mjs",
"sync:vocab-courses": "node scripts/sync-vocab-course-content.js",
"lockfile:sync": "npm install --package-lock-only",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"@gltf-transform/cli": "^4.3.0",
"amqplib": "^0.10.9",
"bcryptjs": "^3.0.3",
"connect-redis": "^9.0.0",
@@ -35,17 +39,23 @@
"multer": "^2.1.1",
"mysql2": "^3.20.0",
"nodemailer": "^8.0.3",
"openid-client": "^6.8.4",
"pg": "^8.20.0",
"pg-hstore": "^2.3.4",
"redis": "^5.11.0",
"sequelize": "^6.37.8",
"sharp": "^0.34.5",
"socket.io": "^4.8.3",
"uuid": "^13.0.0",
"ws": "^8.20.0",
"@gltf-transform/cli": "^4.3.0"
"uuid": "^14.0.0",
"ws": "^8.20.0"
},
"devDependencies": {
"sequelize-cli": "^6.6.5"
},
"overrides": {
"minimatch": "10.2.4",
"sequelize": {
"uuid": "14.0.0"
}
}
}

View File

@@ -2,6 +2,7 @@
import { Router } from 'express';
import { authenticate } from '../middleware/authMiddleware.js';
import AdminController from '../controllers/adminController.js';
import moderationController from '../controllers/moderationController.js';
const router = Router();
const adminController = new AdminController();
@@ -25,6 +26,14 @@ router.put('/users/:id/adult-verification', authenticate, adminController.setAdu
router.get('/users/erotic-moderation', authenticate, adminController.getEroticModerationReports);
router.get('/users/erotic-moderation/preview/:type/:targetId', authenticate, adminController.getEroticModerationPreview);
router.put('/users/erotic-moderation/:id', authenticate, adminController.applyEroticModerationAction);
router.get('/users/:id/vocab-courses', authenticate, adminController.getUserVocabCourses);
router.post('/users/:id/vocab-lesson-progress/reset', authenticate, adminController.resetUserVocabLessonProgress);
router.post(
'/users/:id/vocab-lesson-progress/mark-complete-through',
authenticate,
adminController.markUserVocabLessonsCompleteThrough
);
router.get('/vocab/courses/:courseId', authenticate, adminController.getVocabCourseForAdmin);
router.get('/users/:id', authenticate, adminController.getUser);
router.put('/users/:id', authenticate, adminController.updateUser);
@@ -43,15 +52,26 @@ router.post('/contacts/answer', authenticate, adminController.answerContact);
router.post('/falukant/searchuser', authenticate, adminController.searchUser);
router.get('/falukant/getuser/:id', authenticate, adminController.getFalukantUserById);
router.post('/falukant/edituser', authenticate, adminController.changeFalukantUser);
router.get('/falukant/character/:characterId/potential-fathers', authenticate, adminController.adminGetPotentialFathersForCharacter);
router.post('/falukant/character/force-pregnancy', authenticate, adminController.adminForceFalukantPregnancy);
router.post('/falukant/character/clear-pregnancy', authenticate, adminController.adminClearFalukantPregnancy);
router.post('/falukant/character/force-birth', authenticate, adminController.adminForceFalukantBirth);
router.post('/falukant/character/:characterId/death-cleanup', authenticate, adminController.adminCleanupCharacterDeathArtifacts);
router.get('/falukant/branches/:falukantUserId', authenticate, adminController.getFalukantUserBranches);
router.put('/falukant/stock/:stockId', authenticate, adminController.updateFalukantStock);
router.post('/falukant/stock', authenticate, adminController.addFalukantStock);
router.get('/falukant/stock-types', authenticate, adminController.getFalukantStockTypes);
router.get('/falukant/region-types', authenticate, adminController.getFalukantRegionTypes);
router.get('/falukant/regions', authenticate, adminController.getFalukantRegions);
router.get('/falukant/regions/all', authenticate, adminController.getFalukantAllRegions);
router.post('/falukant/regions', authenticate, adminController.createFalukantRegion);
router.put('/falukant/regions/:id/map', authenticate, adminController.updateFalukantRegionMap);
router.get('/falukant/region-distances', authenticate, adminController.getRegionDistances);
router.post('/falukant/region-distances', authenticate, adminController.upsertRegionDistance);
router.delete('/falukant/region-distances/:id', authenticate, adminController.deleteRegionDistance);
router.get('/moderation/reports', authenticate, moderationController.listReports);
router.get('/moderation/reports/open-count', authenticate, moderationController.getOpenReportCount);
router.post('/moderation/reports/:reportId/status', authenticate, moderationController.updateReportStatus);
router.post('/falukant/npcs/create', authenticate, adminController.createNPCs);
router.get('/falukant/npcs/status/:jobId', authenticate, adminController.getNPCsCreationStatus);
router.get('/falukant/titles', authenticate, adminController.getTitlesOfNobility);

View File

@@ -9,8 +9,15 @@ router.post('/register', authController.register);
router.post('/login', authController.login);
router.post('/forgot-password', authController.forgotPassword);
router.post('/activate', authController.activateAccount);
router.get('/oauth/providers', authController.oauthProviders);
router.get('/oauth/:provider/start', authController.oauthStart);
router.post('/oauth/exchange', authController.oauthExchange);
// Geschützte Routen (Authentifizierung erforderlich)
router.get('/logout', authController.logout);
router.get('/oauth/user/identities', authController.oauthUserIdentities);
router.get('/oauth/user/:provider/start', authController.oauthUserStart);
router.post('/oauth/user/exchange', authController.oauthUserExchange);
router.delete('/oauth/user/:identityId', authController.oauthUserRemove);
export default router;

View File

@@ -18,5 +18,6 @@ router.get('/rooms', authenticate, chatController.getRoomList);
router.get('/room-create-options', authenticate, chatController.getRoomCreateOptions);
router.get('/my-rooms', authenticate, chatController.getOwnRooms);
router.delete('/my-rooms/:id', authenticate, chatController.deleteOwnRoom);
router.post('/report', chatController.reportChatIncident);
export default router;

View File

@@ -22,10 +22,12 @@ router.post('/production', falukantController.createProduction);
router.get('/production/:branchId', falukantController.getProduction);
router.get('/stocktypes', falukantController.getStockTypes);
router.get('/stockoverview', falukantController.getStockOverview);
router.get('/stock/?:branchId', falukantController.getStock);
router.get('/stock', falukantController.getStock);
router.get('/stock/:branchId', falukantController.getStock);
router.post('/stock', falukantController.createStock);
router.get('/products', falukantController.getProducts);
router.get('/inventory/?:branchId', falukantController.getInventory);
router.get('/inventory', falukantController.getInventory);
router.get('/inventory/:branchId', falukantController.getInventory);
router.post('/sell/all', falukantController.sellAllProducts);
router.post('/sell', falukantController.sellProduct);
router.post('/moneyhistory', falukantController.moneyHistory);
@@ -52,6 +54,7 @@ router.post('/family/marriage/spend-time', falukantController.spendTimeWithSpous
router.post('/family/marriage/gift', falukantController.giftToSpouse);
router.post('/family/marriage/reconcile', falukantController.reconcileMarriage);
router.post('/family/lover/:relationshipId/maintenance', falukantController.setLoverMaintenance);
router.post('/family/lover/:relationshipId/improve-affection', falukantController.improveLoverAffection);
router.post('/family/lover/:relationshipId/acknowledge', falukantController.acknowledgeLover);
router.post('/family/lover/:relationshipId/end', falukantController.endLoverRelationship);
router.get('/heirs/potential', falukantController.getPotentialHeirs);
@@ -93,6 +96,13 @@ router.post('/nobility', falukantController.advanceNobility);
router.get('/health', falukantController.getHealth);
router.post('/health', falukantController.healthActivity);
router.get('/politics/overview', falukantController.getPoliticsOverview);
router.get('/politics/offices', falukantController.getPoliticalOfficeCatalog);
router.get('/politics/my-powers', falukantController.getPoliticalMyPowers);
router.get('/politics/tax-jurisdiction', falukantController.getPoliticalTaxJurisdiction);
router.put('/politics/region/:regionId/tax', falukantController.setPoliticalRegionTax);
router.get('/politics/region/:regionId/tax-history', falukantController.getPoliticalRegionTaxHistory);
router.get('/politics/appointable-offices', falukantController.getPoliticalAppointableOffices);
router.post('/politics/appointments', falukantController.createPoliticalAppointment);
router.get('/politics/open', falukantController.getOpenPolitics);
router.post('/politics/open', falukantController.applyForElections);
router.get('/politics/elections', falukantController.getElections);

View File

@@ -0,0 +1,10 @@
import express from 'express';
import { authenticate } from '../middleware/authMiddleware.js';
import moderationController from '../controllers/moderationController.js';
const router = express.Router();
router.use(authenticate);
router.post('/reports', moderationController.createReport);
export default router;

View File

@@ -17,12 +17,15 @@ router.get('/folder/:folderId', socialNetworkController.getFolderImageList);
router.post('/images', upload.single('image'), socialNetworkController.uploadImage);
router.post('/erotic/folders/:folderId', socialNetworkController.createAdultFolder);
router.get('/erotic/folders', socialNetworkController.getAdultFolders);
router.get('/profile/erotic/folders/:username', socialNetworkController.getAdultFoldersByUsername);
router.get('/profile/erotic/videos/:username', socialNetworkController.getEroticVideosByUsername);
router.get('/erotic/folder/:folderId', socialNetworkController.getAdultFolderImageList);
router.post('/erotic/images', upload.single('image'), socialNetworkController.uploadAdultImage);
router.put('/erotic/images/:imageId', socialNetworkController.changeAdultImage);
router.get('/erotic/image/:hash', socialNetworkController.getAdultImageByHash);
router.get('/erotic/videos', socialNetworkController.listEroticVideos);
router.post('/erotic/videos', upload.single('video'), socialNetworkController.uploadEroticVideo);
router.put('/erotic/videos/:videoId', socialNetworkController.changeEroticVideo);
router.get('/erotic/video/:hash', socialNetworkController.getEroticVideoByHash);
router.post('/erotic/report', socialNetworkController.reportEroticContent);
router.get('/images/:imageId', socialNetworkController.getImage);

View File

@@ -7,6 +7,8 @@ const vocabController = new VocabController();
router.use(authenticate);
router.get('/dashboard-widget', vocabController.getDashboardLearningSummary);
router.get('/languages', vocabController.listLanguages);
router.get('/languages/all', vocabController.listAllLanguages);
router.post('/languages', vocabController.createLanguage);
@@ -18,19 +20,26 @@ router.get('/languages/:languageId/chapters', vocabController.listChapters);
router.post('/languages/:languageId/chapters', vocabController.createChapter);
router.get('/languages/:languageId/vocabs', vocabController.listLanguageVocabs);
router.get('/languages/:languageId/search', vocabController.searchVocabs);
router.get('/languages/:languageId/dictionary', vocabController.getLanguageDictionary);
router.get('/chapters/:chapterId', vocabController.getChapter);
router.get('/chapters/:chapterId/vocabs', vocabController.listChapterVocabs);
router.post('/chapters/:chapterId/vocabs', vocabController.addVocabToChapter);
router.get('/lessons/:lessonId/vocab-pool', vocabController.getLessonVocabPool);
// Courses
router.post('/courses', vocabController.createCourse);
router.get('/courses', vocabController.getCourses);
router.get('/courses/my', vocabController.getMyCourses);
router.post('/courses/find-by-code', vocabController.getCourseByShareCode);
router.get('/courses/:courseId/completed-lesson-vocabs', vocabController.getCompletedLessonVocabPool);
router.get('/courses/:courseId/dictionary', vocabController.getCourseDictionary);
router.get('/courses/:courseId/distractor-pool', vocabController.getVocabDistractorPool);
router.get('/courses/:courseId/srs/due', vocabController.getCourseSrsDue);
router.get('/courses/:courseId', vocabController.getCourse);
router.put('/courses/:courseId', vocabController.updateCourse);
router.delete('/courses/:courseId', vocabController.deleteCourse);
router.post('/srs/review', vocabController.reviewSrsItem);
// Lessons
router.post('/courses/:courseId/lessons', vocabController.addLessonToCourse);
@@ -45,6 +54,7 @@ router.delete('/courses/:courseId/enroll', vocabController.unenrollFromCourse);
router.get('/courses/:courseId/progress', vocabController.getCourseProgress);
router.get('/lessons/:lessonId', vocabController.getLesson);
router.put('/lessons/:lessonId/progress', vocabController.updateLessonProgress);
router.delete('/lessons/:lessonId/progress', vocabController.resetLessonProgress);
// Grammar Exercises
router.get('/grammar/exercise-types', vocabController.getExerciseTypes);
@@ -58,4 +68,3 @@ router.put('/grammar-exercises/:exerciseId', vocabController.updateGrammarExerci
router.delete('/grammar-exercises/:exerciseId', vocabController.deleteGrammarExercise);
export default router;

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