BDSM Game umgesetzt, Community Features ergänzt
@@ -1,5 +1,5 @@
|
||||
#Mon Mar 02 07:06:09 CET 2026
|
||||
#Tue Mar 03 07:39:34 CET 2026
|
||||
display=\:0
|
||||
host=Mario-Linux
|
||||
process-id=8641
|
||||
process-id=8529
|
||||
user=mario
|
||||
|
||||
137
.metadata/.log
@@ -588,3 +588,140 @@ java.lang.InterruptedException
|
||||
at org.eclipse.jdt.internal.ui.text.java.hover.BestMatchHover.getHoverInfo2(BestMatchHover.java:131)
|
||||
at org.eclipse.jdt.internal.ui.text.java.hover.JavaEditorTextHoverProxy.getHoverInfo2(JavaEditorTextHoverProxy.java:89)
|
||||
at org.eclipse.jface.text.TextViewerHoverManager$1.run(TextViewerHoverManager.java:155)
|
||||
!SESSION 2026-03-02 18:52:12.395 -----------------------------------------------
|
||||
eclipse.buildId=4.38.0.20251204-0849
|
||||
java.version=21.0.9
|
||||
java.vendor=Eclipse Adoptium
|
||||
BootLoader constants: OS=linux, ARCH=x86_64, WS=gtk, NL=de_DE
|
||||
Framework arguments: -product org.eclipse.epp.package.java.product
|
||||
Command-line arguments: -os linux -ws gtk -arch x86_64 -product org.eclipse.epp.package.java.product
|
||||
|
||||
!ENTRY ch.qos.logback.classic 1 0 2026-03-02 18:52:17.211
|
||||
!MESSAGE Activated before the state location was initialized. Retry after the state location is initialized.
|
||||
|
||||
!ENTRY ch.qos.logback.classic 1 0 2026-03-02 18:52:30.891
|
||||
!MESSAGE Logback config file: /home/mario/Workspaces/xxx-thegame/.metadata/.plugins/org.eclipse.m2e.logback/logback.2.7.101.20251017-1242.xml
|
||||
|
||||
!ENTRY org.eclipse.ui 2 0 2026-03-02 18:52:31.083
|
||||
!MESSAGE Warnings while parsing the commands from the 'org.eclipse.ui.commands' and 'org.eclipse.ui.actionDefinitions' extension points.
|
||||
!SUBENTRY 1 org.eclipse.ui 2 0 2026-03-02 18:52:31.083
|
||||
!MESSAGE Commands should really have a category: plug-in='org.springframework.tooling.boot.ls', id='spring.initializr.addStarters', categoryId='org.eclipse.lsp4e.commandCategory'
|
||||
|
||||
!ENTRY org.eclipse.ui 2 0 2026-03-02 18:52:31.215
|
||||
!MESSAGE Warnings while parsing the commands from the 'org.eclipse.ui.commands' and 'org.eclipse.ui.actionDefinitions' extension points.
|
||||
!SUBENTRY 1 org.eclipse.ui 2 0 2026-03-02 18:52:31.215
|
||||
!MESSAGE Commands should really have a category: plug-in='org.springframework.tooling.boot.ls', id='spring.initializr.addStarters', categoryId='org.eclipse.lsp4e.commandCategory'
|
||||
|
||||
!ENTRY org.eclipse.jface 2 0 2026-03-02 19:47:48.567
|
||||
!MESSAGE Keybinding conflicts occurred. They may interfere with normal accelerator operation.
|
||||
!SUBENTRY 1 org.eclipse.jface 2 0 2026-03-02 19:47:48.567
|
||||
!MESSAGE A conflict occurred for CTRL+SHIFT+T:
|
||||
Binding(CTRL+SHIFT+T,
|
||||
ParameterizedCommand(Command(org.eclipse.jdt.ui.navigate.open.type,Open Type,
|
||||
Open a type in a Java editor,
|
||||
Category(org.eclipse.ui.category.navigate,Navigate,null,true),
|
||||
WorkbenchHandlerServiceHandler("org.eclipse.jdt.ui.navigate.open.type"),
|
||||
,,true),null),
|
||||
org.eclipse.ui.defaultAcceleratorConfiguration,
|
||||
org.eclipse.ui.contexts.window,,,system)
|
||||
Binding(CTRL+SHIFT+T,
|
||||
ParameterizedCommand(Command(org.eclipse.lsp4e.symbolInWorkspace,Go to Symbol in Workspace,
|
||||
,
|
||||
Category(org.eclipse.lsp4e.category,Language Servers,null,true),
|
||||
WorkbenchHandlerServiceHandler("org.eclipse.lsp4e.symbolInWorkspace"),
|
||||
,,true),null),
|
||||
org.eclipse.ui.defaultAcceleratorConfiguration,
|
||||
org.eclipse.ui.contexts.window,,,system)
|
||||
|
||||
!ENTRY org.eclipse.debug.core 4 125 2026-03-02 20:36:48.515
|
||||
!MESSAGE Error logged from Debug Core:
|
||||
!STACK 0
|
||||
java.io.IOException: Stream closed
|
||||
at java.base/java.io.BufferedInputStream.getBufIfOpen(BufferedInputStream.java:188)
|
||||
at java.base/java.io.BufferedInputStream.read1(BufferedInputStream.java:343)
|
||||
at java.base/java.io.BufferedInputStream.implRead(BufferedInputStream.java:420)
|
||||
at java.base/java.io.BufferedInputStream.read(BufferedInputStream.java:405)
|
||||
at java.base/java.io.FilterInputStream.read(FilterInputStream.java:95)
|
||||
at org.eclipse.debug.internal.core.OutputStreamMonitor.internalRead(OutputStreamMonitor.java:235)
|
||||
at org.eclipse.debug.internal.core.OutputStreamMonitor.read(OutputStreamMonitor.java:211)
|
||||
at java.base/java.lang.Thread.run(Thread.java:1583)
|
||||
!SESSION 2026-03-03 07:39:25.427 -----------------------------------------------
|
||||
eclipse.buildId=4.38.0.20251204-0849
|
||||
java.version=21.0.9
|
||||
java.vendor=Eclipse Adoptium
|
||||
BootLoader constants: OS=linux, ARCH=x86_64, WS=gtk, NL=de_DE
|
||||
Framework arguments: -product org.eclipse.epp.package.java.product
|
||||
Command-line arguments: -os linux -ws gtk -arch x86_64 -product org.eclipse.epp.package.java.product
|
||||
|
||||
!ENTRY ch.qos.logback.classic 1 0 2026-03-03 07:39:30.215
|
||||
!MESSAGE Activated before the state location was initialized. Retry after the state location is initialized.
|
||||
|
||||
!ENTRY ch.qos.logback.classic 1 0 2026-03-03 07:39:35.273
|
||||
!MESSAGE Logback config file: /home/mario/Workspaces/xxx-thegame/.metadata/.plugins/org.eclipse.m2e.logback/logback.2.7.101.20251017-1242.xml
|
||||
|
||||
!ENTRY org.eclipse.ui 2 0 2026-03-03 07:39:35.456
|
||||
!MESSAGE Warnings while parsing the commands from the 'org.eclipse.ui.commands' and 'org.eclipse.ui.actionDefinitions' extension points.
|
||||
!SUBENTRY 1 org.eclipse.ui 2 0 2026-03-03 07:39:35.456
|
||||
!MESSAGE Commands should really have a category: plug-in='org.springframework.tooling.boot.ls', id='spring.initializr.addStarters', categoryId='org.eclipse.lsp4e.commandCategory'
|
||||
|
||||
!ENTRY org.eclipse.ui 2 0 2026-03-03 07:39:35.586
|
||||
!MESSAGE Warnings while parsing the commands from the 'org.eclipse.ui.commands' and 'org.eclipse.ui.actionDefinitions' extension points.
|
||||
!SUBENTRY 1 org.eclipse.ui 2 0 2026-03-03 07:39:35.586
|
||||
!MESSAGE Commands should really have a category: plug-in='org.springframework.tooling.boot.ls', id='spring.initializr.addStarters', categoryId='org.eclipse.lsp4e.commandCategory'
|
||||
|
||||
!ENTRY org.eclipse.jface 2 0 2026-03-03 07:56:04.643
|
||||
!MESSAGE Keybinding conflicts occurred. They may interfere with normal accelerator operation.
|
||||
!SUBENTRY 1 org.eclipse.jface 2 0 2026-03-03 07:56:04.643
|
||||
!MESSAGE A conflict occurred for CTRL+SHIFT+T:
|
||||
Binding(CTRL+SHIFT+T,
|
||||
ParameterizedCommand(Command(org.eclipse.jdt.ui.navigate.open.type,Open Type,
|
||||
Open a type in a Java editor,
|
||||
Category(org.eclipse.ui.category.navigate,Navigate,null,true),
|
||||
WorkbenchHandlerServiceHandler("org.eclipse.jdt.ui.navigate.open.type"),
|
||||
,,true),null),
|
||||
org.eclipse.ui.defaultAcceleratorConfiguration,
|
||||
org.eclipse.ui.contexts.window,,,system)
|
||||
Binding(CTRL+SHIFT+T,
|
||||
ParameterizedCommand(Command(org.eclipse.lsp4e.symbolInWorkspace,Go to Symbol in Workspace,
|
||||
,
|
||||
Category(org.eclipse.lsp4e.category,Language Servers,null,true),
|
||||
WorkbenchHandlerServiceHandler("org.eclipse.lsp4e.symbolInWorkspace"),
|
||||
,,true),null),
|
||||
org.eclipse.ui.defaultAcceleratorConfiguration,
|
||||
org.eclipse.ui.contexts.window,,,system)
|
||||
|
||||
!ENTRY org.eclipse.lsp4e 2 0 2026-03-03 15:57:21.578
|
||||
!MESSAGE Javadoc unavailable. Failed to obtain it.
|
||||
!STACK 0
|
||||
java.lang.InterruptedException
|
||||
at java.base/java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:386)
|
||||
at java.base/java.util.concurrent.CompletableFuture.get(CompletableFuture.java:2096)
|
||||
at org.eclipse.lsp4e.jdt.LSJavaHoverProvider.getHoverInfo2(LSJavaHoverProvider.java:66)
|
||||
at org.eclipse.jdt.internal.ui.text.java.hover.BestMatchHover.getHoverInfo2(BestMatchHover.java:165)
|
||||
at org.eclipse.jdt.internal.ui.text.java.hover.BestMatchHover.getHoverInfo2(BestMatchHover.java:131)
|
||||
at org.eclipse.jdt.internal.ui.text.java.hover.JavaEditorTextHoverProxy.getHoverInfo2(JavaEditorTextHoverProxy.java:89)
|
||||
at org.eclipse.jface.text.TextViewerHoverManager$1.run(TextViewerHoverManager.java:155)
|
||||
|
||||
!ENTRY org.eclipse.lsp4e 2 0 2026-03-03 15:59:16.948
|
||||
!MESSAGE Javadoc unavailable. Failed to obtain it.
|
||||
!STACK 0
|
||||
java.lang.InterruptedException
|
||||
at java.base/java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:386)
|
||||
at java.base/java.util.concurrent.CompletableFuture.get(CompletableFuture.java:2096)
|
||||
at org.eclipse.lsp4e.jdt.LSJavaHoverProvider.getHoverInfo2(LSJavaHoverProvider.java:66)
|
||||
at org.eclipse.jdt.internal.ui.text.java.hover.BestMatchHover.getHoverInfo2(BestMatchHover.java:165)
|
||||
at org.eclipse.jdt.internal.ui.text.java.hover.BestMatchHover.getHoverInfo2(BestMatchHover.java:131)
|
||||
at org.eclipse.jdt.internal.ui.text.java.hover.JavaEditorTextHoverProxy.getHoverInfo2(JavaEditorTextHoverProxy.java:89)
|
||||
at org.eclipse.jface.text.TextViewerHoverManager$1.run(TextViewerHoverManager.java:155)
|
||||
|
||||
!ENTRY org.eclipse.lsp4e 2 0 2026-03-03 16:03:19.413
|
||||
!MESSAGE Javadoc unavailable. Failed to obtain it.
|
||||
!STACK 0
|
||||
java.lang.InterruptedException
|
||||
at java.base/java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:386)
|
||||
at java.base/java.util.concurrent.CompletableFuture.get(CompletableFuture.java:2096)
|
||||
at org.eclipse.lsp4e.jdt.LSJavaHoverProvider.getHoverInfo2(LSJavaHoverProvider.java:66)
|
||||
at org.eclipse.jdt.internal.ui.text.java.hover.BestMatchHover.getHoverInfo2(BestMatchHover.java:165)
|
||||
at org.eclipse.jdt.internal.ui.text.java.hover.BestMatchHover.getHoverInfo2(BestMatchHover.java:131)
|
||||
at org.eclipse.jdt.internal.ui.text.java.hover.JavaEditorTextHoverProxy.getHoverInfo2(JavaEditorTextHoverProxy.java:89)
|
||||
at org.eclipse.jface.text.TextViewerHoverManager$1.run(TextViewerHoverManager.java:155)
|
||||
|
||||
@@ -1,24 +1,7 @@
|
||||
[ {
|
||||
"version" : "9.5.0-20260301003351+0000",
|
||||
"buildTime" : "20260301003351+0000",
|
||||
"commitId" : "f5df27307ea67cc4fa298aa1a9e1fc01c77458d2",
|
||||
"current" : false,
|
||||
"snapshot" : true,
|
||||
"nightly" : true,
|
||||
"releaseNightly" : false,
|
||||
"activeRc" : false,
|
||||
"rcFor" : "",
|
||||
"milestoneFor" : "",
|
||||
"broken" : false,
|
||||
"downloadUrl" : "https://services.gradle.org/distributions-snapshots/gradle-9.5.0-20260301003351+0000-bin.zip",
|
||||
"checksumUrl" : "https://services.gradle.org/distributions-snapshots/gradle-9.5.0-20260301003351+0000-bin.zip.sha256",
|
||||
"checksum" : "ab9b82bcdaca040e4172b6fa10e3fcd3debaa377315760f6035c29d7837fad30",
|
||||
"wrapperChecksumUrl" : "https://services.gradle.org/distributions-snapshots/gradle-9.5.0-20260301003351+0000-wrapper.jar.sha256",
|
||||
"wrapperChecksum" : "7ef3d73bd95c047814d76ec8324f72deefb96593eb9ce87aa06ecdcdaba7ffe8"
|
||||
}, {
|
||||
"version" : "9.4.0-20260228022640+0000",
|
||||
"buildTime" : "20260228022640+0000",
|
||||
"commitId" : "49c4c929c0d007cf3106e4ab65e91c9591376ac8",
|
||||
"version" : "9.4.0-20260302013914+0000",
|
||||
"buildTime" : "20260302013914+0000",
|
||||
"commitId" : "3c885266535b1a7076dbecff4aac4830bd85a74b",
|
||||
"current" : false,
|
||||
"snapshot" : true,
|
||||
"nightly" : false,
|
||||
@@ -27,11 +10,28 @@
|
||||
"rcFor" : "",
|
||||
"milestoneFor" : "",
|
||||
"broken" : false,
|
||||
"downloadUrl" : "https://services.gradle.org/distributions-snapshots/gradle-9.4.0-20260228022640+0000-bin.zip",
|
||||
"checksumUrl" : "https://services.gradle.org/distributions-snapshots/gradle-9.4.0-20260228022640+0000-bin.zip.sha256",
|
||||
"checksum" : "c8fe5e81a89bb9d72aa318f351af7f8cd6e205cb92708775082ea3b4be6e3e18",
|
||||
"wrapperChecksumUrl" : "https://services.gradle.org/distributions-snapshots/gradle-9.4.0-20260228022640+0000-wrapper.jar.sha256",
|
||||
"downloadUrl" : "https://services.gradle.org/distributions-snapshots/gradle-9.4.0-20260302013914+0000-bin.zip",
|
||||
"checksumUrl" : "https://services.gradle.org/distributions-snapshots/gradle-9.4.0-20260302013914+0000-bin.zip.sha256",
|
||||
"checksum" : "1e225dd94668c5a4d5889c63d8a7e6d48b9750b68cc24d22afe407d56583fc5f",
|
||||
"wrapperChecksumUrl" : "https://services.gradle.org/distributions-snapshots/gradle-9.4.0-20260302013914+0000-wrapper.jar.sha256",
|
||||
"wrapperChecksum" : "55243ef57851f12b070ad14f7f5bb8302daceeebc5bce5ece5fa6edb23e1145c"
|
||||
}, {
|
||||
"version" : "9.5.0-20260302000223+0000",
|
||||
"buildTime" : "20260302000223+0000",
|
||||
"commitId" : "95405f4d8f3894f368fd90697eebd275f3d9a22d",
|
||||
"current" : false,
|
||||
"snapshot" : true,
|
||||
"nightly" : true,
|
||||
"releaseNightly" : false,
|
||||
"activeRc" : false,
|
||||
"rcFor" : "",
|
||||
"milestoneFor" : "",
|
||||
"broken" : false,
|
||||
"downloadUrl" : "https://services.gradle.org/distributions-snapshots/gradle-9.5.0-20260302000223+0000-bin.zip",
|
||||
"checksumUrl" : "https://services.gradle.org/distributions-snapshots/gradle-9.5.0-20260302000223+0000-bin.zip.sha256",
|
||||
"checksum" : "434486e384ac04fbe16ae2bda2f4c9373cfe9f620aff4f5cc9c51f06e6f001ab",
|
||||
"wrapperChecksumUrl" : "https://services.gradle.org/distributions-snapshots/gradle-9.5.0-20260302000223+0000-wrapper.jar.sha256",
|
||||
"wrapperChecksum" : "7ef3d73bd95c047814d76ec8324f72deefb96593eb9ce87aa06ecdcdaba7ffe8"
|
||||
}, {
|
||||
"version" : "9.4.0-rc-2",
|
||||
"buildTime" : "20260227092055+0000",
|
||||
|
||||
@@ -8,4 +8,9 @@
|
||||
<item key="DIALOG_HEIGHT" value="640"/>
|
||||
<item key="DIALOG_FONT_NAME" value="1|Ubuntu Sans|11.0|0|GTK|1|"/>
|
||||
</section>
|
||||
<section name="org.eclipse.debug.ui.SELECT_LAUNCH_SHORTCUT_DIALOG">
|
||||
<item key="DIALOG_WIDTH" value="295"/>
|
||||
<item key="DIALOG_HEIGHT" value="419"/>
|
||||
<item key="DIALOG_FONT_NAME" value="1|Ubuntu Sans|11.0|0|GTK|1|"/>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
@@ -1,61 +1,61 @@
|
||||
INDEX VERSION 1.134+/home/mario/Workspaces/xxx-thegame/.metadata/.plugins/org.eclipse.jdt.core
|
||||
690321491.index
|
||||
2318770678.index
|
||||
1872440599.index
|
||||
2240786275.index
|
||||
4150628576.index
|
||||
2655170954.index
|
||||
4195864863.index
|
||||
2982788279.index
|
||||
2609856074.index
|
||||
2626965509.index
|
||||
2609856074.index
|
||||
2769879155.index
|
||||
4134502745.index
|
||||
2817101718.index
|
||||
4158338144.index
|
||||
519552992.index
|
||||
2181028596.index
|
||||
2503368578.index
|
||||
2181028596.index
|
||||
1453089870.index
|
||||
2593736024.index
|
||||
721517855.index
|
||||
815902026.index
|
||||
3718169413.index
|
||||
721517855.index
|
||||
96642630.index
|
||||
2488355463.index
|
||||
1446719945.index
|
||||
2891161224.index
|
||||
1118739196.index
|
||||
2891161224.index
|
||||
2047888269.index
|
||||
3972616808.index
|
||||
2390245932.index
|
||||
1205982295.index
|
||||
1914043487.index
|
||||
808711116.index
|
||||
3154281632.index
|
||||
2390245932.index
|
||||
3972616808.index
|
||||
808711116.index
|
||||
2191830568.index
|
||||
1653061733.index
|
||||
2586591901.index
|
||||
2609698604.index
|
||||
3882180612.index
|
||||
3758865325.index
|
||||
2070370209.index
|
||||
2332037983.index
|
||||
2070370209.index
|
||||
1732769785.index
|
||||
2838468603.index
|
||||
1436262503.index
|
||||
2668411497.index
|
||||
3662169204.index
|
||||
2927822381.index
|
||||
2398089967.index
|
||||
225562445.index
|
||||
1436262503.index
|
||||
3662169204.index
|
||||
1295630681.index
|
||||
3135354350.index
|
||||
3602551868.index
|
||||
363836152.index
|
||||
504781245.index
|
||||
363836152.index
|
||||
2633787677.index
|
||||
1455171009.index
|
||||
2725629017.index
|
||||
1455171009.index
|
||||
3552156823.index
|
||||
4123041097.index
|
||||
1865797976.index
|
||||
@@ -101,5 +101,5 @@ INDEX VERSION 1.134+/home/mario/Workspaces/xxx-thegame/.metadata/.plugins/org.ec
|
||||
198314732.index
|
||||
1324521365.index
|
||||
1633924572.index
|
||||
2318770678.index
|
||||
690321491.index
|
||||
1256436118.index
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<typeInfoHistroy>
|
||||
<typeInfo handle="=xxxthegame/src\/main\/java=/gradle_scope=/main=/=/gradle_used_by_scope=/main,test=/<de.oaa.xxx.registration{RegistrationController.java[RegistrationController" modifiers="1" timestamp="1772398379804"/>
|
||||
<typeInfo handle="=xxxthegame/src\/main\/java=/gradle_scope=/main=/=/gradle_used_by_scope=/main,test=/<de.oaa.xxx.registration{RegistrationController.java[RegistrationController" modifiers="1" timestamp="1772478938560"/>
|
||||
<typeInfo handle="=xxxthegame/src\/main\/java=/gradle_scope=/main=/=/gradle_used_by_scope=/main,test=/<de.oaa.xxx.session.controller{SperreController.java[SperreController" modifiers="1" timestamp="1772390921746"/>
|
||||
</typeInfoHistroy>
|
||||
|
||||
@@ -2,3 +2,5 @@
|
||||
2026-03-01 18:42:13,387 [Worker-7: Loading available Gradle versions] INFO o.e.b.c.i.u.g.PublishedGradleVersions - Gradle version information cache is up-to-date. Trying to read.
|
||||
2026-03-01 19:35:44,100 [Worker-7: Loading available Gradle versions] INFO o.e.b.c.i.u.g.PublishedGradleVersions - Gradle version information cache is up-to-date. Trying to read.
|
||||
2026-03-02 07:06:13,818 [Worker-6: Loading available Gradle versions] INFO o.e.b.c.i.u.g.PublishedGradleVersions - Gradle version information cache is up-to-date. Trying to read.
|
||||
2026-03-02 18:52:34,442 [Worker-1: Loading available Gradle versions] INFO o.e.b.c.i.u.g.PublishedGradleVersions - Gradle version information cache is out-of-date. Trying to update.
|
||||
2026-03-03 07:39:38,191 [Worker-7: Loading available Gradle versions] INFO o.e.b.c.i.u.g.PublishedGradleVersions - Gradle version information cache is up-to-date. Trying to read.
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
#Mon Mar 02 07:06:09 CET 2026
|
||||
#Tue Mar 03 07:39:34 CET 2026
|
||||
org.eclipse.core.runtime=2
|
||||
org.eclipse.platform=4.38.0.v20251201-0920
|
||||
|
||||
BIN
bilder/Vorlagen/Gemini_Generated_Image_eueluweueluweuel.png
Normal file
|
After Width: | Height: | Size: 1.4 MiB |
BIN
bilder/Vorlagen/Gemini_Generated_Image_swoht3swoht3swoh.png
Normal file
|
After Width: | Height: | Size: 1.3 MiB |
BIN
bilder/Vorlagen/icon.png
Normal file
|
After Width: | Height: | Size: 1015 KiB |
BIN
bilder/Vorlagen/logo.png
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
bilder/Vorlagen/logo_old.png
Normal file
|
After Width: | Height: | Size: 326 KiB |
BIN
bilder/icon.png
Normal file
|
After Width: | Height: | Size: 174 KiB |
BIN
bilder/icon.xcf
Normal file
BIN
bilder/icon_transparent.png
Normal file
|
After Width: | Height: | Size: 149 KiB |
BIN
bilder/icon_transparent.xcf
Normal file
BIN
bilder/logo.png
Normal file
|
After Width: | Height: | Size: 554 KiB |
BIN
bilder/logo.xcf
Normal file
BIN
bilder/logo_transparent.png
Normal file
|
After Width: | Height: | Size: 459 KiB |
BIN
bilder/logo_transparent.xcf
Normal file
BIN
bilder/lvl1.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
bilder/lvl1.xcf
Normal file
BIN
bilder/lvl2.png
Normal file
|
After Width: | Height: | Size: 41 KiB |
BIN
bilder/lvl2.xcf
Normal file
BIN
bilder/lvl3.png
Normal file
|
After Width: | Height: | Size: 59 KiB |
BIN
bilder/lvl3.xcf
Normal file
BIN
bilder/lvl4.png
Normal file
|
After Width: | Height: | Size: 63 KiB |
BIN
bilder/lvl4.xcf
Normal file
BIN
bilder/lvl5.png
Normal file
|
After Width: | Height: | Size: 74 KiB |
BIN
bilder/lvl5.xcf
Normal file
@@ -14,6 +14,7 @@ public class AufgabenGruppe {
|
||||
private List<Aufgabe> aufgaben;
|
||||
private List<Strafe> strafen;
|
||||
private List<Sperre> sperren;
|
||||
private List<Finisher> finisher;
|
||||
private String bild;
|
||||
private long subscriberCount;
|
||||
private boolean subscribed;
|
||||
@@ -45,6 +46,9 @@ public class AufgabenGruppe {
|
||||
public List<Sperre> getSperren() { return sperren; }
|
||||
public void setSperren(List<Sperre> sperren) { this.sperren = sperren; }
|
||||
|
||||
public List<Finisher> getFinisher() { return finisher; }
|
||||
public void setFinisher(List<Finisher> finisher) { this.finisher = finisher; }
|
||||
|
||||
public String getBild() { return bild; }
|
||||
public void setBild(String bild) { this.bild = bild; }
|
||||
|
||||
|
||||
47
xxxthegame/src/main/java/de/oaa/xxx/aufgaben/Finisher.java
Normal file
@@ -0,0 +1,47 @@
|
||||
package de.oaa.xxx.aufgaben;
|
||||
|
||||
import de.oaa.xxx.session.GeschlechtEnum;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
public class Finisher {
|
||||
|
||||
private UUID finisherId;
|
||||
private String kurzText;
|
||||
private String text;
|
||||
private GeschlechtEnum geschlecht;
|
||||
private List<Werkzeug> benoetigtAktiv;
|
||||
private List<Werkzeug> benoetigtPassiv;
|
||||
private List<Toy> benoetigteToys;
|
||||
private UUID gruppeId;
|
||||
|
||||
public UUID getFinisherId() { return finisherId; }
|
||||
public void setFinisherId(UUID finisherId) { this.finisherId = finisherId; }
|
||||
|
||||
public String getKurzText() { return kurzText; }
|
||||
public void setKurzText(String kurzText) { this.kurzText = kurzText; }
|
||||
|
||||
public String getText() { return text; }
|
||||
public void setText(String text) { this.text = text; }
|
||||
|
||||
public GeschlechtEnum getGeschlecht() { return geschlecht; }
|
||||
public void setGeschlecht(GeschlechtEnum geschlecht) { this.geschlecht = geschlecht; }
|
||||
|
||||
public List<Werkzeug> getBenoetigtAktiv() { return benoetigtAktiv; }
|
||||
public void setBenoetigtAktiv(List<Werkzeug> benoetigtAktiv) { this.benoetigtAktiv = benoetigtAktiv; }
|
||||
|
||||
public List<Werkzeug> getBenoetigtPassiv() { return benoetigtPassiv; }
|
||||
public void setBenoetigtPassiv(List<Werkzeug> benoetigtPassiv) { this.benoetigtPassiv = benoetigtPassiv; }
|
||||
|
||||
public List<Toy> getBenoetigteToys() { return benoetigteToys; }
|
||||
public void setBenoetigteToys(List<Toy> benoetigteToys) { this.benoetigteToys = benoetigteToys; }
|
||||
|
||||
public UUID getGruppeId() { return gruppeId; }
|
||||
public void setGruppeId(UUID gruppeId) { this.gruppeId = gruppeId; }
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Finisher[id=" + finisherId + ", kurzText=" + kurzText + ", geschlecht=" + geschlecht + ", gruppeId=" + gruppeId + "]";
|
||||
}
|
||||
}
|
||||
@@ -5,11 +5,13 @@ import de.oaa.xxx.aufgaben.AufgabenGruppeList;
|
||||
import de.oaa.xxx.aufgaben.AufgabenGruppePage;
|
||||
import de.oaa.xxx.aufgaben.entity.AufgabeEntity;
|
||||
import de.oaa.xxx.aufgaben.entity.AufgabenGruppeEntity;
|
||||
import de.oaa.xxx.aufgaben.entity.FinisherEntity;
|
||||
import de.oaa.xxx.aufgaben.entity.SperreEntity;
|
||||
import de.oaa.xxx.aufgaben.entity.StrafeEntity;
|
||||
import de.oaa.xxx.aufgaben.entity.ToyEntity;
|
||||
import de.oaa.xxx.aufgaben.repository.AufgabeRepository;
|
||||
import de.oaa.xxx.aufgaben.repository.AufgabenGruppeRepository;
|
||||
import de.oaa.xxx.aufgaben.repository.FinisherRepository;
|
||||
import de.oaa.xxx.aufgaben.repository.GruppenAboRepository;
|
||||
import de.oaa.xxx.aufgaben.repository.SperreRepository;
|
||||
import de.oaa.xxx.aufgaben.repository.StrafeRepository;
|
||||
@@ -57,6 +59,7 @@ public class AufgabenGruppeController {
|
||||
private final AufgabeRepository aufgabeRepository;
|
||||
private final StrafeRepository strafeRepository;
|
||||
private final SperreRepository sperreRepository;
|
||||
private final FinisherRepository finisherRepository;
|
||||
private final UserRepository userRepository;
|
||||
private final GruppenAboRepository aboRepository;
|
||||
private final ToyRepository toyRepository;
|
||||
@@ -65,6 +68,7 @@ public class AufgabenGruppeController {
|
||||
AufgabeRepository aufgabeRepository,
|
||||
StrafeRepository strafeRepository,
|
||||
SperreRepository sperreRepository,
|
||||
FinisherRepository finisherRepository,
|
||||
UserRepository userRepository,
|
||||
GruppenAboRepository aboRepository,
|
||||
ToyRepository toyRepository) {
|
||||
@@ -72,6 +76,7 @@ public class AufgabenGruppeController {
|
||||
this.aufgabeRepository = aufgabeRepository;
|
||||
this.strafeRepository = strafeRepository;
|
||||
this.sperreRepository = sperreRepository;
|
||||
this.finisherRepository = finisherRepository;
|
||||
this.userRepository = userRepository;
|
||||
this.aboRepository = aboRepository;
|
||||
this.toyRepository = toyRepository;
|
||||
@@ -198,6 +203,7 @@ public class AufgabenGruppeController {
|
||||
source.getAufgaben().forEach(a -> { if (a.getBenoetigteToys() != null) allSourceToys.addAll(a.getBenoetigteToys()); });
|
||||
source.getStrafen().forEach(s -> { if (s.getBenoetigteToys() != null) allSourceToys.addAll(s.getBenoetigteToys()); });
|
||||
source.getSperren().forEach(sp -> { if (sp.getBenoetigteToys() != null) allSourceToys.addAll(sp.getBenoetigteToys()); });
|
||||
source.getFinisher().forEach(f -> { if (f.getBenoetigteToys() != null) allSourceToys.addAll(f.getBenoetigteToys()); });
|
||||
|
||||
Map<UUID, ToyEntity> toyMapping = new HashMap<>();
|
||||
for (ToyEntity sourceToy : allSourceToys) {
|
||||
@@ -274,6 +280,19 @@ public class AufgabenGruppeController {
|
||||
sperreRepository.save(spc);
|
||||
}
|
||||
|
||||
for (FinisherEntity f : source.getFinisher()) {
|
||||
FinisherEntity fc = new FinisherEntity();
|
||||
fc.setFinisherId(UUID.randomUUID());
|
||||
fc.setAufgabenGruppe(copy);
|
||||
fc.setKurzText(f.getKurzText());
|
||||
fc.setText(f.getText());
|
||||
fc.setGeschlecht(f.getGeschlecht());
|
||||
fc.setBenoetigtAktiv(f.getBenoetigtAktiv() != null ? new ArrayList<>(f.getBenoetigtAktiv()) : null);
|
||||
fc.setBenoetigtPassiv(f.getBenoetigtPassiv() != null ? new ArrayList<>(f.getBenoetigtPassiv()) : null);
|
||||
fc.setBenoetigteToys(mapToys(f.getBenoetigteToys(), toyMapping));
|
||||
finisherRepository.save(fc);
|
||||
}
|
||||
|
||||
return ResponseEntity.status(201).build();
|
||||
}
|
||||
|
||||
@@ -298,6 +317,7 @@ public class AufgabenGruppeController {
|
||||
aufgabeRepository.deleteAll(entity.getAufgaben());
|
||||
strafeRepository.deleteAll(entity.getStrafen());
|
||||
sperreRepository.deleteAll(entity.getSperren());
|
||||
finisherRepository.deleteAll(entity.getFinisher());
|
||||
gruppeRepository.delete(entity);
|
||||
return ResponseEntity.accepted().build();
|
||||
} catch (Exception e) {
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
package de.oaa.xxx.aufgaben.controller;
|
||||
|
||||
import de.oaa.xxx.aufgaben.Finisher;
|
||||
import de.oaa.xxx.aufgaben.Toy;
|
||||
import de.oaa.xxx.aufgaben.entity.AufgabenGruppeEntity;
|
||||
import de.oaa.xxx.aufgaben.entity.FinisherEntity;
|
||||
import de.oaa.xxx.aufgaben.entity.ToyEntity;
|
||||
import de.oaa.xxx.aufgaben.repository.AufgabenGruppeRepository;
|
||||
import de.oaa.xxx.aufgaben.repository.FinisherRepository;
|
||||
import de.oaa.xxx.aufgaben.repository.ToyRepository;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/finisher")
|
||||
@Transactional
|
||||
public class FinisherController {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(FinisherController.class);
|
||||
|
||||
private final FinisherRepository finisherRepository;
|
||||
private final AufgabenGruppeRepository gruppeRepository;
|
||||
private final ToyRepository toyRepository;
|
||||
|
||||
public FinisherController(FinisherRepository finisherRepository,
|
||||
AufgabenGruppeRepository gruppeRepository,
|
||||
ToyRepository toyRepository) {
|
||||
this.finisherRepository = finisherRepository;
|
||||
this.gruppeRepository = gruppeRepository;
|
||||
this.toyRepository = toyRepository;
|
||||
}
|
||||
|
||||
@GetMapping("/{finisherId}")
|
||||
public ResponseEntity<Finisher> get(@PathVariable UUID finisherId) {
|
||||
return finisherRepository.findById(finisherId)
|
||||
.map(entity -> ResponseEntity.ok(entity.toFinisher()))
|
||||
.orElse(ResponseEntity.noContent().build());
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public ResponseEntity<Void> create(@RequestBody Finisher finisher) {
|
||||
if (finisher.getKurzText() == null || finisher.getText() == null
|
||||
|| finisher.getGeschlecht() == null || finisher.getGruppeId() == null) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
AufgabenGruppeEntity gruppeEntity = gruppeRepository.findById(finisher.getGruppeId()).orElse(null);
|
||||
if (gruppeEntity == null) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
if (gruppeEntity.getFinisher().size() >= 100) {
|
||||
return ResponseEntity.status(409).build();
|
||||
}
|
||||
List<ToyEntity> toys = resolveToys(finisher.getBenoetigteToys());
|
||||
FinisherEntity entity = FinisherEntity.create(finisher, gruppeEntity, toys);
|
||||
finisherRepository.save(entity);
|
||||
return ResponseEntity.created(
|
||||
ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}").buildAndExpand(entity.getFinisherId()).toUri()
|
||||
).build();
|
||||
}
|
||||
|
||||
@PutMapping("/{finisherId}")
|
||||
public ResponseEntity<Void> update(@PathVariable UUID finisherId, @RequestBody Finisher finisher) {
|
||||
if (finisher.getKurzText() == null || finisher.getText() == null || finisher.getGeschlecht() == null) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
FinisherEntity entity = finisherRepository.findById(finisherId).orElse(null);
|
||||
if (entity == null) return ResponseEntity.notFound().build();
|
||||
entity.setKurzText(finisher.getKurzText());
|
||||
entity.setText(finisher.getText());
|
||||
entity.setGeschlecht(finisher.getGeschlecht());
|
||||
entity.setBenoetigtAktiv(finisher.getBenoetigtAktiv());
|
||||
entity.setBenoetigtPassiv(finisher.getBenoetigtPassiv());
|
||||
entity.setBenoetigteToys(resolveToys(finisher.getBenoetigteToys()));
|
||||
finisherRepository.save(entity);
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
@DeleteMapping
|
||||
public ResponseEntity<Void> delete(@RequestBody Finisher finisher) {
|
||||
try {
|
||||
finisherRepository.findById(finisher.getFinisherId()).ifPresent(finisherRepository::delete);
|
||||
return ResponseEntity.accepted().build();
|
||||
} catch (Exception exception) {
|
||||
LOGGER.error(exception.getMessage(), exception);
|
||||
return ResponseEntity.internalServerError().build();
|
||||
}
|
||||
}
|
||||
|
||||
private List<ToyEntity> resolveToys(List<Toy> toys) {
|
||||
if (toys == null || toys.isEmpty()) return new ArrayList<>();
|
||||
List<UUID> ids = toys.stream()
|
||||
.filter(t -> t.getToyId() != null)
|
||||
.map(Toy::getToyId)
|
||||
.toList();
|
||||
if (ids.isEmpty()) return new ArrayList<>();
|
||||
return toyRepository.findAllById(ids);
|
||||
}
|
||||
}
|
||||
@@ -84,6 +84,12 @@ public class AufgabeEntity {
|
||||
public List<ToyEntity> getBenoetigteToys() { return benoetigteToys; }
|
||||
public void setBenoetigteToys(List<ToyEntity> benoetigteToys) { this.benoetigteToys = benoetigteToys; }
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "AufgabeEntity[id=" + aufgabeId + ", kurzText=" + kurzText + ", level=" + level
|
||||
+ ", sekunden=" + sekundenVon + "-" + sekundenBis + "]";
|
||||
}
|
||||
|
||||
public Aufgabe toAufgabe() {
|
||||
Aufgabe aufgabe = new Aufgabe();
|
||||
aufgabe.setAufgabeId(aufgabeId);
|
||||
|
||||
@@ -39,6 +39,8 @@ public class AufgabenGruppeEntity {
|
||||
private List<StrafeEntity> strafen;
|
||||
@OneToMany(mappedBy = "aufgabenGruppe")
|
||||
private List<SperreEntity> sperren;
|
||||
@OneToMany(mappedBy = "aufgabenGruppe")
|
||||
private List<FinisherEntity> finisher;
|
||||
|
||||
public UUID getGruppenId() { return gruppenId; }
|
||||
public void setGruppenId(UUID gruppenId) { this.gruppenId = gruppenId; }
|
||||
@@ -70,6 +72,15 @@ public class AufgabenGruppeEntity {
|
||||
public List<SperreEntity> getSperren() { return sperren; }
|
||||
public void setSperren(List<SperreEntity> sperren) { this.sperren = sperren; }
|
||||
|
||||
public List<FinisherEntity> getFinisher() { return finisher; }
|
||||
public void setFinisher(List<FinisherEntity> finisher) { this.finisher = finisher; }
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "AufgabenGruppeEntity[gruppenId=" + gruppenId + ", name=" + name + ", userId=" + userId
|
||||
+ ", privat=" + privateGruppe + ", von=" + von + "]";
|
||||
}
|
||||
|
||||
public AufgabenGruppe toAufgabenGruppe() {
|
||||
AufgabenGruppe gruppe = new AufgabenGruppe();
|
||||
gruppe.setGruppenId(gruppenId);
|
||||
@@ -82,6 +93,7 @@ public class AufgabenGruppeEntity {
|
||||
gruppe.setAufgaben(aufgaben.stream().map(AufgabeEntity::toAufgabe).toList());
|
||||
gruppe.setStrafen(strafen.stream().map(StrafeEntity::toStrafe).toList());
|
||||
gruppe.setSperren(sperren.stream().map(SperreEntity::toSperre).toList());
|
||||
gruppe.setFinisher(finisher.stream().map(FinisherEntity::toFinisher).toList());
|
||||
return gruppe;
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,11 @@ public class FavoritEntity {
|
||||
public UUID getAufgabenGruppeId() { return aufgabenGruppeId; }
|
||||
public void setAufgabenGruppeId(UUID aufgabenGruppeId) { this.aufgabenGruppeId = aufgabenGruppeId; }
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "FavoritEntity[favoritId=" + favoritId + ", userId=" + userId + ", gruppeId=" + aufgabenGruppeId + "]";
|
||||
}
|
||||
|
||||
public Favorit toFavorit() {
|
||||
Favorit favorit = new Favorit();
|
||||
favorit.setAufgabenGruppeId(aufgabenGruppeId);
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
package de.oaa.xxx.aufgaben.entity;
|
||||
|
||||
import de.oaa.xxx.aufgaben.Finisher;
|
||||
import de.oaa.xxx.aufgaben.Werkzeug;
|
||||
import de.oaa.xxx.session.GeschlechtEnum;
|
||||
import jakarta.persistence.CascadeType;
|
||||
import jakarta.persistence.CollectionTable;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.ElementCollection;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.EnumType;
|
||||
import jakarta.persistence.Enumerated;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.JoinTable;
|
||||
import jakarta.persistence.ManyToMany;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@Entity
|
||||
@Table(name = "finisher")
|
||||
public class FinisherEntity {
|
||||
|
||||
@Id
|
||||
@Column
|
||||
private UUID finisherId;
|
||||
@Column
|
||||
private String kurzText;
|
||||
@Column(columnDefinition = "TEXT")
|
||||
private String text;
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column
|
||||
private GeschlechtEnum geschlecht;
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "gruppeId")
|
||||
private AufgabenGruppeEntity aufgabenGruppe;
|
||||
@Enumerated(EnumType.STRING)
|
||||
@ElementCollection(targetClass = Werkzeug.class)
|
||||
@CollectionTable(name = "finisher_benoetigtAktiv", joinColumns = @JoinColumn(name = "finisherId"))
|
||||
@Column(name = "werkzeug")
|
||||
private List<Werkzeug> benoetigtAktiv;
|
||||
@Enumerated(EnumType.STRING)
|
||||
@ElementCollection(targetClass = Werkzeug.class)
|
||||
@CollectionTable(name = "finisher_benoetigtPassiv", joinColumns = @JoinColumn(name = "finisherId"))
|
||||
@Column(name = "werkzeug")
|
||||
private List<Werkzeug> benoetigtPassiv;
|
||||
@ManyToMany(cascade = CascadeType.DETACH)
|
||||
@JoinTable(name = "finisherToy", joinColumns = {@JoinColumn(name = "finisherId")}, inverseJoinColumns = {@JoinColumn(name = "toyId")})
|
||||
private List<ToyEntity> benoetigteToys;
|
||||
|
||||
public UUID getFinisherId() { return finisherId; }
|
||||
public void setFinisherId(UUID finisherId) { this.finisherId = finisherId; }
|
||||
|
||||
public String getKurzText() { return kurzText; }
|
||||
public void setKurzText(String kurzText) { this.kurzText = kurzText; }
|
||||
|
||||
public String getText() { return text; }
|
||||
public void setText(String text) { this.text = text; }
|
||||
|
||||
public GeschlechtEnum getGeschlecht() { return geschlecht; }
|
||||
public void setGeschlecht(GeschlechtEnum geschlecht) { this.geschlecht = geschlecht; }
|
||||
|
||||
public AufgabenGruppeEntity getAufgabenGruppe() { return aufgabenGruppe; }
|
||||
public void setAufgabenGruppe(AufgabenGruppeEntity aufgabenGruppe) { this.aufgabenGruppe = aufgabenGruppe; }
|
||||
|
||||
public List<Werkzeug> getBenoetigtAktiv() { return benoetigtAktiv; }
|
||||
public void setBenoetigtAktiv(List<Werkzeug> benoetigtAktiv) { this.benoetigtAktiv = benoetigtAktiv; }
|
||||
|
||||
public List<Werkzeug> getBenoetigtPassiv() { return benoetigtPassiv; }
|
||||
public void setBenoetigtPassiv(List<Werkzeug> benoetigtPassiv) { this.benoetigtPassiv = benoetigtPassiv; }
|
||||
|
||||
public List<ToyEntity> getBenoetigteToys() { return benoetigteToys; }
|
||||
public void setBenoetigteToys(List<ToyEntity> benoetigteToys) { this.benoetigteToys = benoetigteToys; }
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "FinisherEntity[id=" + finisherId + ", kurzText=" + kurzText + ", geschlecht=" + geschlecht + "]";
|
||||
}
|
||||
|
||||
public Finisher toFinisher() {
|
||||
Finisher finisher = new Finisher();
|
||||
finisher.setFinisherId(finisherId);
|
||||
finisher.setKurzText(kurzText);
|
||||
finisher.setText(text);
|
||||
finisher.setGeschlecht(geschlecht);
|
||||
finisher.setBenoetigtAktiv(benoetigtAktiv);
|
||||
finisher.setBenoetigtPassiv(benoetigtPassiv);
|
||||
finisher.setBenoetigteToys(benoetigteToys.stream().map(ToyEntity::toToy).toList());
|
||||
finisher.setGruppeId(aufgabenGruppe.getGruppenId());
|
||||
return finisher;
|
||||
}
|
||||
|
||||
public static FinisherEntity create(Finisher finisher, AufgabenGruppeEntity aufgabenGruppeEntity, List<ToyEntity> toys) {
|
||||
FinisherEntity entity = new FinisherEntity();
|
||||
entity.setFinisherId(UUID.randomUUID());
|
||||
entity.setAufgabenGruppe(aufgabenGruppeEntity);
|
||||
entity.setKurzText(finisher.getKurzText());
|
||||
entity.setText(finisher.getText());
|
||||
entity.setGeschlecht(finisher.getGeschlecht());
|
||||
entity.setBenoetigtAktiv(finisher.getBenoetigtAktiv());
|
||||
entity.setBenoetigtPassiv(finisher.getBenoetigtPassiv());
|
||||
entity.setBenoetigteToys(toys != null ? toys : new ArrayList<>());
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
@@ -32,4 +32,10 @@ public class GruppenAboEntity {
|
||||
|
||||
public AufgabenGruppeEntity getAufgabenGruppe() { return aufgabenGruppe; }
|
||||
public void setAufgabenGruppe(AufgabenGruppeEntity aufgabenGruppe) { this.aufgabenGruppe = aufgabenGruppe; }
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "GruppenAboEntity[aboId=" + aboId + ", userId=" + userId
|
||||
+ ", gruppe=" + (aufgabenGruppe != null ? aufgabenGruppe.getName() : null) + "]";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,6 +76,12 @@ public class SperreEntity {
|
||||
public List<ToyEntity> getBenoetigteToys() { return benoetigteToys; }
|
||||
public void setBenoetigteToys(List<ToyEntity> benoetigteToys) { this.benoetigteToys = benoetigteToys; }
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SperreEntity[id=" + sperreId + ", kurzText=" + kurzText
|
||||
+ ", minuten=" + minutenVon + "-" + minutenBis + ", fuer=" + sperreFuer + "]";
|
||||
}
|
||||
|
||||
public Sperre toSperre() {
|
||||
Sperre sperre = new Sperre();
|
||||
sperre.setSperreId(sperreId);
|
||||
|
||||
@@ -84,6 +84,12 @@ public class StrafeEntity {
|
||||
public List<ToyEntity> getBenoetigteToys() { return benoetigteToys; }
|
||||
public void setBenoetigteToys(List<ToyEntity> benoetigteToys) { this.benoetigteToys = benoetigteToys; }
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "StrafeEntity[id=" + strafeId + ", kurzText=" + kurzText + ", level=" + level
|
||||
+ ", sekunden=" + sekundenVon + "-" + sekundenBis + "]";
|
||||
}
|
||||
|
||||
public Strafe toStrafe() {
|
||||
Strafe strafe = new Strafe();
|
||||
strafe.setStrafeId(strafeId);
|
||||
|
||||
@@ -42,6 +42,11 @@ public class ToyEntity {
|
||||
public byte[] getBild() { return bild; }
|
||||
public void setBild(byte[] bild) { this.bild = bild; }
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ToyEntity[toyId=" + toyId + ", name=" + name + ", userId=" + userId + "]";
|
||||
}
|
||||
|
||||
public Toy toToy() {
|
||||
Toy toy = new Toy();
|
||||
toy.setToyId(toyId);
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
package de.oaa.xxx.aufgaben.repository;
|
||||
|
||||
import de.oaa.xxx.aufgaben.entity.AufgabeEntity;
|
||||
import de.oaa.xxx.aufgaben.entity.AufgabenGruppeEntity;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
public interface AufgabeRepository extends JpaRepository<AufgabeEntity, UUID> {
|
||||
|
||||
List<AufgabeEntity> findByAufgabenGruppeIn(List<AufgabenGruppeEntity> gruppen);
|
||||
}
|
||||
|
||||
@@ -11,4 +11,6 @@ public interface FavoritRepository extends JpaRepository<FavoritEntity, UUID> {
|
||||
List<FavoritEntity> findByUserId(UUID userId);
|
||||
|
||||
List<FavoritEntity> findByUserIdAndAufgabenGruppeId(UUID userId, UUID aufgabenGruppeId);
|
||||
|
||||
void deleteByAufgabenGruppeId(UUID aufgabenGruppeId);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package de.oaa.xxx.aufgaben.repository;
|
||||
|
||||
import de.oaa.xxx.aufgaben.entity.FinisherEntity;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public interface FinisherRepository extends JpaRepository<FinisherEntity, UUID> {
|
||||
}
|
||||
@@ -1,9 +1,13 @@
|
||||
package de.oaa.xxx.aufgaben.repository;
|
||||
|
||||
import de.oaa.xxx.aufgaben.entity.AufgabenGruppeEntity;
|
||||
import de.oaa.xxx.aufgaben.entity.SperreEntity;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
public interface SperreRepository extends JpaRepository<SperreEntity, UUID> {
|
||||
|
||||
List<SperreEntity> findByAufgabenGruppeIn(List<AufgabenGruppeEntity> gruppen);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
package de.oaa.xxx.aufgaben.repository;
|
||||
|
||||
import de.oaa.xxx.aufgaben.entity.AufgabenGruppeEntity;
|
||||
import de.oaa.xxx.aufgaben.entity.StrafeEntity;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
public interface StrafeRepository extends JpaRepository<StrafeEntity, UUID> {
|
||||
|
||||
List<StrafeEntity> findByAufgabenGruppeIn(List<AufgabenGruppeEntity> gruppen);
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
@@ -16,6 +17,8 @@ public interface ToyRepository extends JpaRepository<ToyEntity, UUID> {
|
||||
|
||||
Page<ToyEntity> findByUserId(UUID userId, Pageable pageable);
|
||||
|
||||
List<ToyEntity> findByUserId(UUID userId);
|
||||
|
||||
boolean existsByNameIgnoreCaseAndUserIdIsNull(String name);
|
||||
|
||||
boolean existsByNameIgnoreCaseAndUserId(String name, UUID userId);
|
||||
|
||||
@@ -36,18 +36,40 @@ public class SecurityConfig {
|
||||
.requestMatchers(AntPathRequestMatcher.antMatcher("/toys.html")).authenticated()
|
||||
.requestMatchers(AntPathRequestMatcher.antMatcher("/aufgaben.html")).authenticated()
|
||||
.requestMatchers(AntPathRequestMatcher.antMatcher("/entdecken.html")).authenticated()
|
||||
.requestMatchers(AntPathRequestMatcher.antMatcher("/profile.html")).authenticated()
|
||||
.requestMatchers(AntPathRequestMatcher.antMatcher("/infovanilla.html")).authenticated()
|
||||
.requestMatchers(AntPathRequestMatcher.antMatcher("/infobdsm.html")).authenticated()
|
||||
.requestMatchers(AntPathRequestMatcher.antMatcher("/infochastity.html")).authenticated()
|
||||
.requestMatchers(AntPathRequestMatcher.antMatcher("/sessionvanilla.html")).authenticated()
|
||||
.requestMatchers(AntPathRequestMatcher.antMatcher("/sessionbdsm.html")).authenticated()
|
||||
.requestMatchers(AntPathRequestMatcher.antMatcher("/sessionchastity.html")).authenticated()
|
||||
.requestMatchers(AntPathRequestMatcher.antMatcher("/sessionbdsmtasks.html")).authenticated()
|
||||
.requestMatchers(AntPathRequestMatcher.antMatcher("/sessionbdsmtoys.html")).authenticated()
|
||||
.requestMatchers(AntPathRequestMatcher.antMatcher("/sessionbdsmingame.html")).authenticated()
|
||||
.requestMatchers(AntPathRequestMatcher.antMatcher("/personen-suchen.html")).authenticated()
|
||||
.requestMatchers(AntPathRequestMatcher.antMatcher("/freunde.html")).authenticated()
|
||||
.requestMatchers(AntPathRequestMatcher.antMatcher("/nachrichten.html")).authenticated()
|
||||
.requestMatchers(AntPathRequestMatcher.antMatcher("/benutzer.html")).authenticated()
|
||||
.requestMatchers(AntPathRequestMatcher.antMatcher("/*.html")).permitAll()
|
||||
.requestMatchers(AntPathRequestMatcher.antMatcher("/css/**")).permitAll()
|
||||
.requestMatchers(AntPathRequestMatcher.antMatcher("/js/**")).permitAll()
|
||||
.requestMatchers(AntPathRequestMatcher.antMatcher("/images/**")).permitAll()
|
||||
.requestMatchers(AntPathRequestMatcher.antMatcher("/favicon.ico")).permitAll()
|
||||
.requestMatchers(AntPathRequestMatcher.antMatcher("/*.png")).permitAll()
|
||||
.requestMatchers(AntPathRequestMatcher.antMatcher("/*.jpg")).permitAll()
|
||||
.requestMatchers(AntPathRequestMatcher.antMatcher("/*.svg")).permitAll()
|
||||
.requestMatchers(AntPathRequestMatcher.antMatcher("/*.webp")).permitAll()
|
||||
.requestMatchers(AntPathRequestMatcher.antMatcher(HttpMethod.GET, "/login")).permitAll()
|
||||
.requestMatchers(AntPathRequestMatcher.antMatcher(HttpMethod.GET, "/login/publickey")).permitAll()
|
||||
.requestMatchers(AntPathRequestMatcher.antMatcher(HttpMethod.GET, "/login/logout")).permitAll()
|
||||
.requestMatchers(AntPathRequestMatcher.antMatcher(HttpMethod.POST, "/user")).permitAll()
|
||||
.requestMatchers(AntPathRequestMatcher.antMatcher(HttpMethod.GET, "/registration")).permitAll()
|
||||
.requestMatchers(AntPathRequestMatcher.antMatcher(HttpMethod.POST, "/registration")).permitAll()
|
||||
.requestMatchers(AntPathRequestMatcher.antMatcher(HttpMethod.GET, "/activation")).permitAll()
|
||||
.requestMatchers(AntPathRequestMatcher.antMatcher(HttpMethod.GET, "/activation/**")).permitAll()
|
||||
.requestMatchers(AntPathRequestMatcher.antMatcher(HttpMethod.POST, "/password-reset/request")).permitAll()
|
||||
.requestMatchers(AntPathRequestMatcher.antMatcher(HttpMethod.POST, "/password-reset/confirm")).permitAll()
|
||||
.requestMatchers(AntPathRequestMatcher.antMatcher(HttpMethod.GET, "/email-change/**")).permitAll()
|
||||
.requestMatchers(AntPathRequestMatcher.antMatcher(HttpMethod.POST, "/filler")).permitAll()
|
||||
.anyRequest().authenticated()
|
||||
)
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
package de.oaa.xxx.emailchange;
|
||||
|
||||
import de.oaa.xxx.mail.Email;
|
||||
import de.oaa.xxx.mail.MailService;
|
||||
import de.oaa.xxx.mail.MailTemplateService;
|
||||
import de.oaa.xxx.registration.RegistrationRepository;
|
||||
import de.oaa.xxx.user.UserRepository;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.ResponseCookie;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.Principal;
|
||||
import java.util.UUID;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/email-change")
|
||||
public class EmailChangeController {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(EmailChangeController.class);
|
||||
|
||||
@Value("${app.base-url:http://localhost:8080}")
|
||||
private String baseUrl;
|
||||
|
||||
private final EmailChangeRepository emailChangeRepository;
|
||||
private final UserRepository userRepository;
|
||||
private final RegistrationRepository registrationRepository;
|
||||
private final MailService mailService;
|
||||
private final MailTemplateService mailTemplateService;
|
||||
|
||||
public EmailChangeController(EmailChangeRepository emailChangeRepository,
|
||||
UserRepository userRepository,
|
||||
RegistrationRepository registrationRepository,
|
||||
MailService mailService,
|
||||
MailTemplateService mailTemplateService) {
|
||||
this.emailChangeRepository = emailChangeRepository;
|
||||
this.userRepository = userRepository;
|
||||
this.registrationRepository = registrationRepository;
|
||||
this.mailService = mailService;
|
||||
this.mailTemplateService = mailTemplateService;
|
||||
}
|
||||
|
||||
record EmailChangeRequest(String newEmail) {}
|
||||
|
||||
@PostMapping
|
||||
public ResponseEntity<Void> requestChange(@RequestBody EmailChangeRequest request, Principal principal) {
|
||||
String currentEmail = principal.getName();
|
||||
String newEmail = request.newEmail();
|
||||
|
||||
if (userRepository.findByEmail(newEmail).isPresent()
|
||||
|| registrationRepository.findByEmail(newEmail).isPresent()) {
|
||||
return ResponseEntity.status(409).build();
|
||||
}
|
||||
|
||||
// Remove any pending request for this user
|
||||
emailChangeRepository.findByUserEmail(currentEmail)
|
||||
.ifPresent(emailChangeRepository::delete);
|
||||
|
||||
var user = userRepository.findByEmail(currentEmail);
|
||||
if (user.isEmpty()) return ResponseEntity.status(401).build();
|
||||
|
||||
EmailChangeEntity entity = EmailChangeEntity.create(currentEmail, newEmail);
|
||||
emailChangeRepository.save(entity);
|
||||
|
||||
Email email = new Email();
|
||||
email.setTitel("Bitte bestätige deine neue E-Mail-Adresse");
|
||||
email.setEmailAdresse(newEmail);
|
||||
String confirmLink = baseUrl + "/email-change/" + entity.getTokenId().toString();
|
||||
email.setText(mailTemplateService.buildEmailChangeMail(user.get().getName(), confirmLink, newEmail));
|
||||
|
||||
if (!mailService.send(email)) {
|
||||
emailChangeRepository.delete(entity);
|
||||
return ResponseEntity.internalServerError().build();
|
||||
}
|
||||
|
||||
return ResponseEntity.status(202).build();
|
||||
}
|
||||
|
||||
@GetMapping("/{token}")
|
||||
public void confirm(@PathVariable String token, HttpServletResponse response) throws IOException {
|
||||
UUID tokenId;
|
||||
try {
|
||||
tokenId = UUID.fromString(token);
|
||||
} catch (IllegalArgumentException e) {
|
||||
response.sendRedirect("/login.html");
|
||||
return;
|
||||
}
|
||||
|
||||
var entity = emailChangeRepository.findById(tokenId);
|
||||
if (entity.isEmpty()) {
|
||||
response.sendRedirect("/login.html");
|
||||
return;
|
||||
}
|
||||
|
||||
var user = userRepository.findByEmail(entity.get().getUserEmail());
|
||||
if (user.isPresent()) {
|
||||
user.get().setEmail(entity.get().getNewEmail());
|
||||
userRepository.save(user.get());
|
||||
LOGGER.info("E-Mail geändert von {} zu {}", entity.get().getUserEmail(), entity.get().getNewEmail());
|
||||
}
|
||||
|
||||
emailChangeRepository.delete(entity.get());
|
||||
|
||||
// Clear JWT cookie so user must log in with new email
|
||||
ResponseCookie cookie = ResponseCookie.from("jwt", "")
|
||||
.httpOnly(true)
|
||||
.sameSite("Strict")
|
||||
.path("/")
|
||||
.maxAge(0)
|
||||
.build();
|
||||
response.addHeader(HttpHeaders.SET_COOKIE, cookie.toString());
|
||||
response.sendRedirect("/login.html?emailChanged=1");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package de.oaa.xxx.emailchange;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
@Entity
|
||||
@Table(name = "email_change")
|
||||
public class EmailChangeEntity {
|
||||
|
||||
@Id
|
||||
@Column
|
||||
private UUID tokenId;
|
||||
|
||||
@Column
|
||||
private String userEmail;
|
||||
|
||||
@Column
|
||||
private String newEmail;
|
||||
|
||||
@Column
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
public UUID getTokenId() { return tokenId; }
|
||||
public void setTokenId(UUID tokenId) { this.tokenId = tokenId; }
|
||||
|
||||
public String getUserEmail() { return userEmail; }
|
||||
public void setUserEmail(String userEmail) { this.userEmail = userEmail; }
|
||||
|
||||
public String getNewEmail() { return newEmail; }
|
||||
public void setNewEmail(String newEmail) { this.newEmail = newEmail; }
|
||||
|
||||
public LocalDateTime getCreatedAt() { return createdAt; }
|
||||
public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "EmailChangeEntity[tokenId=" + tokenId + ", userEmail=" + userEmail + ", newEmail=" + newEmail + ", createdAt=" + createdAt + "]";
|
||||
}
|
||||
|
||||
public static EmailChangeEntity create(String userEmail, String newEmail) {
|
||||
EmailChangeEntity entity = new EmailChangeEntity();
|
||||
entity.setTokenId(UUID.randomUUID());
|
||||
entity.setUserEmail(userEmail);
|
||||
entity.setNewEmail(newEmail);
|
||||
entity.setCreatedAt(LocalDateTime.now());
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package de.oaa.xxx.emailchange;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
public interface EmailChangeRepository extends JpaRepository<EmailChangeEntity, UUID> {
|
||||
|
||||
Optional<EmailChangeEntity> findByUserEmail(String userEmail);
|
||||
}
|
||||
@@ -32,6 +32,90 @@ public class MailTemplateService {
|
||||
@Value("${app.theme.color-success:#2ecc71}")
|
||||
private String colorSuccess;
|
||||
|
||||
public String buildEmailChangeMail(String name, String confirmLink, String newEmail) {
|
||||
return """
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<body style="margin:0; padding:2rem; background:%s; font-family:'Segoe UI',Arial,sans-serif; color:%s;">
|
||||
<div style="max-width:460px; margin:0 auto; background:%s; border:1px solid %s; border-radius:12px; padding:2.5rem; box-shadow:0 8px 32px rgba(0,0,0,0.5);">
|
||||
|
||||
<h1 style="color:%s; text-align:center; margin:0 0 1.5rem 0; font-size:1.6rem;">XXX The Game</h1>
|
||||
|
||||
<p style="color:%s; margin:0 0 0.75rem 0;">Moin %s,</p>
|
||||
<p style="color:%s; margin:0 0 0.5rem 0;">Du hast eine Änderung deiner E-Mail-Adresse angefordert.</p>
|
||||
<p style="color:%s; margin:0 0 2rem 0;">Klick auf den Button, um deine neue Adresse <strong style="color:%s;">%s</strong> zu bestätigen:</p>
|
||||
|
||||
<div style="text-align:center; margin:0 0 2rem 0;">
|
||||
<a href="%s"
|
||||
style="display:inline-block; padding:0.75rem 2.5rem; background:%s; color:#ffffff;
|
||||
border-radius:6px; text-decoration:none; font-weight:600; font-size:1rem;">
|
||||
E-Mail-Adresse bestätigen
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<hr style="border:none; border-top:1px solid %s; margin:0 0 1.5rem 0;">
|
||||
|
||||
<p style="color:%s; font-size:0.85em; margin:0;">
|
||||
Falls du diese Änderung nicht angefordert hast, kannst du diese E-Mail einfach ignorieren.
|
||||
</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
""".formatted(
|
||||
colorBg, colorText,
|
||||
colorCard, colorSecondary,
|
||||
colorPrimary,
|
||||
colorText, name,
|
||||
colorText,
|
||||
colorText, colorPrimary, newEmail,
|
||||
confirmLink, colorPrimary,
|
||||
colorSecondary,
|
||||
colorMuted
|
||||
);
|
||||
}
|
||||
|
||||
public String buildPasswordResetMail(String name, String resetLink) {
|
||||
return """
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<body style="margin:0; padding:2rem; background:%s; font-family:'Segoe UI',Arial,sans-serif; color:%s;">
|
||||
<div style="max-width:460px; margin:0 auto; background:%s; border:1px solid %s; border-radius:12px; padding:2.5rem; box-shadow:0 8px 32px rgba(0,0,0,0.5);">
|
||||
|
||||
<h1 style="color:%s; text-align:center; margin:0 0 1.5rem 0; font-size:1.6rem;">XXX The Game</h1>
|
||||
|
||||
<p style="color:%s; margin:0 0 0.75rem 0;">Moin %s,</p>
|
||||
<p style="color:%s; margin:0 0 0.5rem 0;">Du hast eine Anfrage zum Zurücksetzen deines Passworts gestellt.</p>
|
||||
<p style="color:%s; margin:0 0 2rem 0;">Klick auf den Button, um ein neues Passwort zu vergeben:</p>
|
||||
|
||||
<div style="text-align:center; margin:0 0 2rem 0;">
|
||||
<a href="%s"
|
||||
style="display:inline-block; padding:0.75rem 2.5rem; background:%s; color:#ffffff;
|
||||
border-radius:6px; text-decoration:none; font-weight:600; font-size:1rem;">
|
||||
Passwort zurücksetzen
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<hr style="border:none; border-top:1px solid %s; margin:0 0 1.5rem 0;">
|
||||
|
||||
<p style="color:%s; font-size:0.85em; margin:0;">
|
||||
Falls du diese Anfrage nicht gestellt hast, kannst du diese E-Mail einfach ignorieren.
|
||||
</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
""".formatted(
|
||||
colorBg, colorText,
|
||||
colorCard, colorSecondary,
|
||||
colorPrimary,
|
||||
colorText, name,
|
||||
colorText,
|
||||
colorText,
|
||||
resetLink, colorPrimary,
|
||||
colorSecondary,
|
||||
colorMuted
|
||||
);
|
||||
}
|
||||
|
||||
public String buildActivationMail(String name, String activationLink, String activatePageUrl, String uuid) {
|
||||
return """
|
||||
<!DOCTYPE html>
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
package de.oaa.xxx.passwordreset;
|
||||
|
||||
public record PasswordResetConfirm(String token, String passwordHash) {}
|
||||
@@ -0,0 +1,77 @@
|
||||
package de.oaa.xxx.passwordreset;
|
||||
|
||||
import de.oaa.xxx.mail.Email;
|
||||
import de.oaa.xxx.mail.MailService;
|
||||
import de.oaa.xxx.mail.MailTemplateService;
|
||||
import de.oaa.xxx.user.UserRepository;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/password-reset")
|
||||
public class PasswordResetController {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(PasswordResetController.class);
|
||||
|
||||
@Value("${app.base-url:http://localhost:8080}")
|
||||
private String baseUrl;
|
||||
|
||||
private final PasswordResetRepository passwordResetRepository;
|
||||
private final UserRepository userRepository;
|
||||
private final MailService mailService;
|
||||
private final MailTemplateService mailTemplateService;
|
||||
|
||||
public PasswordResetController(PasswordResetRepository passwordResetRepository,
|
||||
UserRepository userRepository,
|
||||
MailService mailService,
|
||||
MailTemplateService mailTemplateService) {
|
||||
this.passwordResetRepository = passwordResetRepository;
|
||||
this.userRepository = userRepository;
|
||||
this.mailService = mailService;
|
||||
this.mailTemplateService = mailTemplateService;
|
||||
}
|
||||
|
||||
@PostMapping("/request")
|
||||
public ResponseEntity<Void> request(@RequestBody PasswordResetRequest request) {
|
||||
userRepository.findByEmail(request.email()).ifPresent(user -> {
|
||||
passwordResetRepository.findByEmail(request.email())
|
||||
.ifPresent(passwordResetRepository::delete);
|
||||
PasswordResetEntity entity = PasswordResetEntity.create(request.email());
|
||||
passwordResetRepository.save(entity);
|
||||
String resetLink = baseUrl + "/reset-password.html?token=" + entity.getTokenId();
|
||||
Email email = new Email();
|
||||
email.setTitel("Passwort zurücksetzen");
|
||||
email.setEmailAdresse(request.email());
|
||||
email.setText(mailTemplateService.buildPasswordResetMail(user.getName(), resetLink));
|
||||
mailService.send(email);
|
||||
LOGGER.info("Passwort-Reset angefordert für: {}", request.email());
|
||||
});
|
||||
return ResponseEntity.status(202).build();
|
||||
}
|
||||
|
||||
@PostMapping("/confirm")
|
||||
public ResponseEntity<Void> confirm(@RequestBody PasswordResetConfirm confirm) {
|
||||
UUID tokenId;
|
||||
try {
|
||||
tokenId = UUID.fromString(confirm.token());
|
||||
} catch (IllegalArgumentException e) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
var entity = passwordResetRepository.findById(tokenId);
|
||||
if (entity.isEmpty()) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
userRepository.findByEmail(entity.get().getEmail()).ifPresent(user -> {
|
||||
user.setPassword(confirm.passwordHash());
|
||||
userRepository.save(user);
|
||||
LOGGER.info("Passwort zurückgesetzt für: {}", entity.get().getEmail());
|
||||
});
|
||||
passwordResetRepository.delete(entity.get());
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package de.oaa.xxx.passwordreset;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
@Entity
|
||||
@Table(name = "password_reset")
|
||||
public class PasswordResetEntity {
|
||||
|
||||
@Id
|
||||
@Column
|
||||
private UUID tokenId;
|
||||
|
||||
@Column(unique = true)
|
||||
private String email;
|
||||
|
||||
@Column
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
public UUID getTokenId() { return tokenId; }
|
||||
public void setTokenId(UUID tokenId) { this.tokenId = tokenId; }
|
||||
|
||||
public String getEmail() { return email; }
|
||||
public void setEmail(String email) { this.email = email; }
|
||||
|
||||
public LocalDateTime getCreatedAt() { return createdAt; }
|
||||
public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PasswordResetEntity[tokenId=" + tokenId + ", email=" + email + ", createdAt=" + createdAt + "]";
|
||||
}
|
||||
|
||||
public static PasswordResetEntity create(String email) {
|
||||
PasswordResetEntity entity = new PasswordResetEntity();
|
||||
entity.setTokenId(UUID.randomUUID());
|
||||
entity.setEmail(email);
|
||||
entity.setCreatedAt(LocalDateTime.now());
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package de.oaa.xxx.passwordreset;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
public interface PasswordResetRepository extends JpaRepository<PasswordResetEntity, UUID> {
|
||||
Optional<PasswordResetEntity> findByEmail(String email);
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
package de.oaa.xxx.passwordreset;
|
||||
|
||||
public record PasswordResetRequest(String email) {}
|
||||
@@ -44,6 +44,11 @@ public class RegistrationController {
|
||||
LOGGER.warn("User mit E-Mail {} bereits vorhanden", registration.getEmail());
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
if (registrationRepository.findByName(registration.getName()).isPresent()
|
||||
|| userRepository.findByName(registration.getName()).isPresent()) {
|
||||
LOGGER.warn("User mit Name {} bereits vorhanden", registration.getName());
|
||||
return ResponseEntity.status(409).build();
|
||||
}
|
||||
RegistrationEntity entity = RegistrationEntity.create(registration);
|
||||
registrationRepository.save(entity);
|
||||
|
||||
|
||||
@@ -8,4 +8,5 @@ import java.util.UUID;
|
||||
public interface RegistrationRepository extends JpaRepository<RegistrationEntity, UUID> {
|
||||
|
||||
Optional<RegistrationEntity> findByEmail(String email);
|
||||
Optional<RegistrationEntity> findByName(String name);
|
||||
}
|
||||
|
||||
@@ -34,4 +34,10 @@ public class AktiveSperre {
|
||||
|
||||
public String getReleaseText() { return releaseText; }
|
||||
public void setReleaseText(String releaseText) { this.releaseText = releaseText; }
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "AktiveSperre[id=" + aktiveSperreId + ", mitspieler=" + (mitspieler != null ? mitspieler.getName() : null)
|
||||
+ ", " + minuten + "min, von=" + startzeit + ", bis=" + endzeit + ", fuer=" + fuer + "]";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ public class AufgabeAnzeige {
|
||||
private String aufgabeText;
|
||||
private Integer timer;
|
||||
private Callback callback;
|
||||
private Integer level;
|
||||
|
||||
public String getNameAktiverMitspieler() { return nameAktiverMitspieler; }
|
||||
public void setNameAktiverMitspieler(String nameAktiverMitspieler) { this.nameAktiverMitspieler = nameAktiverMitspieler; }
|
||||
@@ -18,4 +19,13 @@ public class AufgabeAnzeige {
|
||||
|
||||
public Callback getCallback() { return callback; }
|
||||
public void setCallback(Callback callback) { this.callback = callback; }
|
||||
|
||||
public Integer getLevel() { return level; }
|
||||
public void setLevel(Integer level) { this.level = level; }
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "AufgabeAnzeige[mitspieler=" + nameAktiverMitspieler + ", level=" + level + ", timer=" + timer
|
||||
+ ", callback=" + (callback != null ? callback.getClass().getSimpleName() : null) + "]";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,4 +8,9 @@ public abstract class Callback {
|
||||
|
||||
public UUID getSessionId() { return sessionId; }
|
||||
public void setSessionId(UUID sessionId) { this.sessionId = sessionId; }
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getSimpleName() + "[sessionId=" + sessionId + "]";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,12 @@ public class Mitspieler {
|
||||
return verfuegbareWerkzeuge.contains(werkzeug);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Mitspieler[id=" + id + ", name=" + name + ", geschlecht=" + geschlecht
|
||||
+ ", rollen=" + rollen + ", werkzeuge=" + verfuegbareWerkzeuge + "]";
|
||||
}
|
||||
|
||||
public boolean isPassenderSpielpartner(Mitspieler other) {
|
||||
if (!spieltMit.contains(other.getGeschlecht())) {
|
||||
return false;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package de.oaa.xxx.session;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
public class Session {
|
||||
@@ -10,6 +11,10 @@ public class Session {
|
||||
private Integer wahrscheinlichkeitStrafe;
|
||||
private Integer aufgabenProLevel;
|
||||
private Double zeitfaktorZeitstrafen;
|
||||
private Integer level;
|
||||
private Integer aufgabenAufAktuellemLevel;
|
||||
private LocalDateTime startZeit;
|
||||
private LocalDateTime letzteAktivitaet;
|
||||
|
||||
public UUID getSessionId() { return sessionId; }
|
||||
public void setSessionId(UUID sessionId) { this.sessionId = sessionId; }
|
||||
@@ -28,4 +33,24 @@ public class Session {
|
||||
|
||||
public Double getZeitfaktorZeitstrafen() { return zeitfaktorZeitstrafen; }
|
||||
public void setZeitfaktorZeitstrafen(Double zeitfaktorZeitstrafen) { this.zeitfaktorZeitstrafen = zeitfaktorZeitstrafen; }
|
||||
|
||||
public Integer getLevel() { return level; }
|
||||
public void setLevel(Integer level) { this.level = level; }
|
||||
|
||||
public Integer getAufgabenAufAktuellemLevel() { return aufgabenAufAktuellemLevel; }
|
||||
public void setAufgabenAufAktuellemLevel(Integer aufgabenAufAktuellemLevel) { this.aufgabenAufAktuellemLevel = aufgabenAufAktuellemLevel; }
|
||||
|
||||
public LocalDateTime getStartZeit() { return startZeit; }
|
||||
public void setStartZeit(LocalDateTime startZeit) { this.startZeit = startZeit; }
|
||||
|
||||
public LocalDateTime getLetzteAktivitaet() { return letzteAktivitaet; }
|
||||
public void setLetzteAktivitaet(LocalDateTime letzteAktivitaet) { this.letzteAktivitaet = letzteAktivitaet; }
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Session[sessionId=" + sessionId + ", userId=" + userId
|
||||
+ ", level=" + level + ", aufgaben=" + aufgabenAufAktuellemLevel + "/" + aufgabenProLevel
|
||||
+ ", pStrafe=" + wahrscheinlichkeitStrafe + "%, pSperre=" + wahrscheinlichkeitSperre + "%"
|
||||
+ ", zeitfaktor=" + zeitfaktorZeitstrafen + "]";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
package de.oaa.xxx.session;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import de.oaa.xxx.session.aufgaben.Aufgabe;
|
||||
import de.oaa.xxx.session.aufgaben.AufgabenList;
|
||||
import de.oaa.xxx.session.aufgaben.Sperre;
|
||||
@@ -9,11 +15,6 @@ import de.oaa.xxx.session.entity.SessionEntity;
|
||||
import de.oaa.xxx.session.sperre.SperreCallback;
|
||||
import de.oaa.xxx.session.sperre.SperrenVerlaengernCallback;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class SessionDurchfuehren {
|
||||
|
||||
private final AufgabenList aufgabenList;
|
||||
@@ -23,12 +24,12 @@ public class SessionDurchfuehren {
|
||||
private final Integer wahrscheinlichkeitSperre;
|
||||
private final Integer wahrscheinlichkeitStrafe;
|
||||
|
||||
private final Integer aufgabenProLevel;
|
||||
private Integer level;
|
||||
private Integer aufgabenAufAktuellemLevel;
|
||||
private int aufgabenProLevel;
|
||||
private int level;
|
||||
private int aufgabenAufAktuellemLevel;
|
||||
|
||||
public SessionDurchfuehren(SessionEntity entity) throws Exception {
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
aufgabenList = objectMapper.readValue(entity.getAufgaben(), AufgabenList.class);
|
||||
entity.getMitspieler().forEach(mitspielerEntity -> mitspieler.add(mitspielerEntity.toMitspieler()));
|
||||
entity.getAktiveSperren().forEach(sperreEntity -> aktiveSperren.add(sperreEntity.toSperre(mitspieler)));
|
||||
@@ -36,13 +37,16 @@ public class SessionDurchfuehren {
|
||||
wahrscheinlichkeitSperre = entity.getWahrscheinlichkeitSperre();
|
||||
wahrscheinlichkeitStrafe = entity.getWahrscheinlichkeitStrafe();
|
||||
|
||||
aufgabenProLevel = entity.getAufgabenProLevel() != null ? entity.getAufgabenProLevel() : 5;
|
||||
level = entity.getLevel() != null ? entity.getLevel() : 1;
|
||||
aufgabenAufAktuellemLevel = entity.getAufgabenAufAktuellemLevel() != null ? entity.getAufgabenAufAktuellemLevel() : 0;
|
||||
this.aufgabenProLevel = entity.getAufgabenProLevel() != null ? entity.getAufgabenProLevel() : 5;
|
||||
this.level = entity.getLevel() != null ? entity.getLevel() : 1;
|
||||
this.aufgabenAufAktuellemLevel = entity.getAufgabenAufAktuellemLevel() != null ? entity.getAufgabenAufAktuellemLevel() : 0;
|
||||
}
|
||||
|
||||
public AufgabeAnzeige getNext() {
|
||||
checkLevel();
|
||||
if (level == 6) {
|
||||
return null;
|
||||
}
|
||||
AufgabeAnzeige anzeige = null;
|
||||
int nextInt = new Random().nextInt(1, 100);
|
||||
if (nextInt == 1) {
|
||||
@@ -67,9 +71,34 @@ public class SessionDurchfuehren {
|
||||
}
|
||||
return anzeige;
|
||||
}
|
||||
|
||||
public void backToLvl5() {
|
||||
this.level = 5;
|
||||
this.aufgabenAufAktuellemLevel = 0;
|
||||
}
|
||||
|
||||
public List<AufgabeAnzeige> getFinisher() {
|
||||
var list = new ArrayList<AufgabeAnzeige>();
|
||||
List.of(GeschlechtEnum.WEIBLICH, GeschlechtEnum.DIVERS, GeschlechtEnum.MAENNLICH).forEach(geschlecht -> {
|
||||
mitspieler.stream().filter(m -> geschlecht == m.getGeschlecht()).toList().forEach(cumming -> {
|
||||
var partner = findeMitspielerMitRolle(RolleEnum.AUFGABE_PASSIV, cumming);
|
||||
aufgabenList.getFinisher().stream()
|
||||
.filter(finisher -> geschlecht == finisher.getGeschlecht())
|
||||
.findAny()
|
||||
.ifPresent(aufgabe -> {
|
||||
AufgabeAnzeige anzeige = new AufgabeAnzeige();
|
||||
anzeige.setNameAktiverMitspieler(cumming.getName());
|
||||
anzeige.setAufgabeText(getAnzeigeText(aufgabe.getText(),
|
||||
cumming.getName(), partner != null ? partner.getName() : ""));
|
||||
list.add(anzeige);
|
||||
});
|
||||
});
|
||||
});
|
||||
return list;
|
||||
}
|
||||
|
||||
private void checkLevel() {
|
||||
if (++aufgabenAufAktuellemLevel >= aufgabenProLevel && level < 5) {
|
||||
if (++aufgabenAufAktuellemLevel >= 1 + aufgabenProLevel) {
|
||||
aufgabenAufAktuellemLevel = 0;
|
||||
level++;
|
||||
}
|
||||
@@ -210,4 +239,12 @@ public class SessionDurchfuehren {
|
||||
.toList();
|
||||
return list.isEmpty() ? null : list.get(new Random().nextInt(list.size()));
|
||||
}
|
||||
|
||||
public int getAufgabenAufAktuellemLevel() {
|
||||
return aufgabenAufAktuellemLevel;
|
||||
}
|
||||
|
||||
public int getLevel() {
|
||||
return level;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,6 +45,12 @@ public class Aufgabe {
|
||||
public List<Werkzeug> getBenoetigtPassiv() { return benoetigtPassiv; }
|
||||
public void setBenoetigtPassiv(List<Werkzeug> benoetigtPassiv) { this.benoetigtPassiv = benoetigtPassiv; }
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Aufgabe[id=" + aufgabeId + ", kurzText=" + kurzText + ", level=" + level
|
||||
+ ", sekunden=" + sekundenVon + "-" + sekundenBis + ", gruppeId=" + gruppeId + "]";
|
||||
}
|
||||
|
||||
public boolean isAufgabePassend(int level, Mitspieler aktiv, Mitspieler passiv) {
|
||||
if (level != this.level && level - 1 != this.level) {
|
||||
return false;
|
||||
|
||||
@@ -7,6 +7,7 @@ public class AufgabenList {
|
||||
private List<Aufgabe> aufgaben;
|
||||
private List<Sperre> sperren;
|
||||
private List<Strafe> strafen;
|
||||
private List<Finisher> finisher;
|
||||
|
||||
public List<Aufgabe> getAufgaben() { return aufgaben; }
|
||||
public void setAufgaben(List<Aufgabe> aufgaben) { this.aufgaben = aufgaben; }
|
||||
@@ -17,11 +18,15 @@ public class AufgabenList {
|
||||
public List<Strafe> getStrafen() { return strafen; }
|
||||
public void setStrafen(List<Strafe> strafen) { this.strafen = strafen; }
|
||||
|
||||
public List<Finisher> getFinisher() { return finisher; }
|
||||
public void setFinisher(List<Finisher> finisher) { this.finisher = finisher; }
|
||||
|
||||
public int size() {
|
||||
int size = 0;
|
||||
if (aufgaben != null) size += aufgaben.size();
|
||||
if (sperren != null) size += sperren.size();
|
||||
if (strafen != null) size += strafen.size();
|
||||
if (getFinisher() != null) size += getFinisher().size();
|
||||
return size;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
package de.oaa.xxx.session.aufgaben;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import de.oaa.xxx.session.GeschlechtEnum;
|
||||
import de.oaa.xxx.session.Werkzeug;
|
||||
|
||||
public class Finisher {
|
||||
|
||||
private UUID finisherId;
|
||||
private String kurzText;
|
||||
private String text;
|
||||
private GeschlechtEnum geschlecht;
|
||||
private List<Werkzeug> benoetigtAktiv;
|
||||
private List<Werkzeug> benoetigtPassiv;
|
||||
|
||||
public UUID getFinisherId() { return finisherId; }
|
||||
public void setFinisherId(UUID finisherId) { this.finisherId = finisherId; }
|
||||
|
||||
public String getKurzText() { return kurzText; }
|
||||
public void setKurzText(String kurzText) { this.kurzText = kurzText; }
|
||||
|
||||
public String getText() { return text; }
|
||||
public void setText(String text) { this.text = text; }
|
||||
|
||||
public GeschlechtEnum getGeschlecht() { return geschlecht; }
|
||||
public void setGeschlecht(GeschlechtEnum geschlecht) { this.geschlecht = geschlecht; }
|
||||
|
||||
public List<Werkzeug> getBenoetigtAktiv() { return benoetigtAktiv; }
|
||||
public void setBenoetigtAktiv(List<Werkzeug> benoetigtAktiv) { this.benoetigtAktiv = benoetigtAktiv; }
|
||||
|
||||
public List<Werkzeug> getBenoetigtPassiv() { return benoetigtPassiv; }
|
||||
public void setBenoetigtPassiv(List<Werkzeug> benoetigtPassiv) { this.benoetigtPassiv = benoetigtPassiv; }
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Finisher[id=" + finisherId + ", kurzText=" + kurzText + ", geschlecht=" + geschlecht + "]";
|
||||
}
|
||||
}
|
||||
@@ -41,6 +41,12 @@ public class Sperre {
|
||||
public Integer getMinutenBis() { return minutenBis; }
|
||||
public void setMinutenBis(Integer minutenBis) { this.minutenBis = minutenBis; }
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Sperre[id=" + sperreId + ", kurzText=" + kurzText
|
||||
+ ", minuten=" + minutenVon + "-" + minutenBis + ", fuer=" + sperreFuer + ", gruppeId=" + gruppeId + "]";
|
||||
}
|
||||
|
||||
public boolean isAufgabePassend(Mitspieler passiv) {
|
||||
for (Werkzeug werkzeug : sperreFuer) {
|
||||
if (!passiv.isVerfuegbar(werkzeug)) {
|
||||
|
||||
@@ -8,8 +8,10 @@ import de.oaa.xxx.session.SessionDurchfuehren;
|
||||
import de.oaa.xxx.session.aufgaben.AufgabenList;
|
||||
import de.oaa.xxx.session.entity.MitspielerEntity;
|
||||
import de.oaa.xxx.session.entity.SessionEntity;
|
||||
import de.oaa.xxx.session.repository.AktiveSperreRepository;
|
||||
import de.oaa.xxx.session.repository.MitspielerRepository;
|
||||
import de.oaa.xxx.session.repository.SessionRepository;
|
||||
import de.oaa.xxx.user.UserRepository;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
@@ -26,6 +28,7 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@RestController
|
||||
@@ -37,12 +40,17 @@ public class SessionController {
|
||||
|
||||
private final SessionRepository sessionRepository;
|
||||
private final MitspielerRepository mitspielerRepository;
|
||||
private final AktiveSperreRepository aktiveSperreRepository;
|
||||
private final UserRepository userRepository;
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
public SessionController(SessionRepository sessionRepository, MitspielerRepository mitspielerRepository,
|
||||
AktiveSperreRepository aktiveSperreRepository, UserRepository userRepository,
|
||||
ObjectMapper objectMapper) {
|
||||
this.sessionRepository = sessionRepository;
|
||||
this.mitspielerRepository = mitspielerRepository;
|
||||
this.aktiveSperreRepository = aktiveSperreRepository;
|
||||
this.userRepository = userRepository;
|
||||
this.objectMapper = objectMapper;
|
||||
}
|
||||
|
||||
@@ -62,7 +70,9 @@ public class SessionController {
|
||||
|
||||
@PostMapping
|
||||
public ResponseEntity<Void> create(@RequestBody Session session) {
|
||||
UUID userId = (UUID) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
|
||||
String email = (String) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
|
||||
UUID userId = userRepository.findByEmail(email).map(u -> u.getUserId()).orElse(null);
|
||||
if (userId == null) return ResponseEntity.status(401).build();
|
||||
SessionEntity entity = new SessionEntity();
|
||||
entity.setSessionId(UUID.randomUUID());
|
||||
entity.setUserId(userId);
|
||||
@@ -76,6 +86,10 @@ public class SessionController {
|
||||
entity.setZeitfaktorZeitstrafen(session.getZeitfaktorZeitstrafen() != null ? session.getZeitfaktorZeitstrafen() : 1.0);
|
||||
entity.setLevel(1);
|
||||
sessionRepository.save(entity);
|
||||
LOGGER.debug("Session gestartet [sessionId={}, userId={}, aufgabenProLevel={}, wahrscheinlichkeitStrafe={}%, wahrscheinlichkeitSperre={}%, zeitfaktorZeitstrafen={}]",
|
||||
entity.getSessionId(), entity.getUserId(), entity.getAufgabenProLevel(),
|
||||
entity.getWahrscheinlichkeitStrafe(), entity.getWahrscheinlichkeitSperre(),
|
||||
entity.getZeitfaktorZeitstrafen());
|
||||
return ResponseEntity.created(
|
||||
ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}").buildAndExpand(entity.getSessionId()).toUri()
|
||||
).build();
|
||||
@@ -85,6 +99,8 @@ public class SessionController {
|
||||
public ResponseEntity<Void> deleteSession(@RequestBody Session session) {
|
||||
return sessionRepository.findById(session.getSessionId())
|
||||
.map(entity -> {
|
||||
aktiveSperreRepository.deleteAll(entity.getAktiveSperren());
|
||||
mitspielerRepository.deleteAll(entity.getMitspieler());
|
||||
sessionRepository.delete(entity);
|
||||
return ResponseEntity.accepted().<Void>build();
|
||||
})
|
||||
@@ -119,7 +135,22 @@ public class SessionController {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
session.setLetzteAktivitaet(LocalDateTime.now());
|
||||
AufgabeAnzeige next = new SessionDurchfuehren(session).getNext();
|
||||
SessionDurchfuehren durchfuehren = new SessionDurchfuehren(session);
|
||||
AufgabeAnzeige next = durchfuehren.getNext();
|
||||
session.setLevel(durchfuehren.getLevel());
|
||||
session.setAufgabenAufAktuellemLevel(durchfuehren.getAufgabenAufAktuellemLevel());
|
||||
if (next == null) {
|
||||
return ResponseEntity.noContent().build();
|
||||
}
|
||||
next.setLevel(durchfuehren.getLevel());
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("Neue Aufgabe [sessionId={}, level={}, aufgaben={}/{}, aktiveSperren={}]",
|
||||
sessionId, session.getLevel(), session.getAufgabenAufAktuellemLevel(),
|
||||
session.getAufgabenProLevel(), session.getAktiveSperren().size());
|
||||
session.getAktiveSperren().forEach(s ->
|
||||
LOGGER.debug(" Sperre [mitspieler={}, {}min, ende={}]",
|
||||
s.getMitspieler().getName(), s.getMinuten(), s.getEndzeit()));
|
||||
}
|
||||
return ResponseEntity.ok(next);
|
||||
} catch (Exception exception) {
|
||||
LOGGER.error(exception.getMessage(), exception);
|
||||
@@ -150,14 +181,48 @@ public class SessionController {
|
||||
return ResponseEntity.accepted().build();
|
||||
}
|
||||
|
||||
@GetMapping("/{sessionId}/finisher")
|
||||
public ResponseEntity<List<AufgabeAnzeige>> getFinisher(@PathVariable UUID sessionId) {
|
||||
try {
|
||||
SessionEntity session = sessionRepository.findById(sessionId).orElse(null);
|
||||
if (session == null) return ResponseEntity.badRequest().build();
|
||||
SessionDurchfuehren durchfuehren = new SessionDurchfuehren(session);
|
||||
return ResponseEntity.ok(durchfuehren.getFinisher());
|
||||
} catch (Exception exception) {
|
||||
LOGGER.error(exception.getMessage(), exception);
|
||||
return ResponseEntity.internalServerError().build();
|
||||
}
|
||||
}
|
||||
|
||||
@PostMapping("/{sessionId}/backToLevel5")
|
||||
public ResponseEntity<Void> backToLevel5(@PathVariable UUID sessionId) {
|
||||
try {
|
||||
SessionEntity session = sessionRepository.findById(sessionId).orElse(null);
|
||||
if (session == null) return ResponseEntity.badRequest().build();
|
||||
SessionDurchfuehren durchfuehren = new SessionDurchfuehren(session);
|
||||
durchfuehren.backToLvl5();
|
||||
session.setLevel(durchfuehren.getLevel());
|
||||
session.setAufgabenAufAktuellemLevel(durchfuehren.getAufgabenAufAktuellemLevel());
|
||||
sessionRepository.save(session);
|
||||
return ResponseEntity.accepted().build();
|
||||
} catch (Exception exception) {
|
||||
LOGGER.error(exception.getMessage(), exception);
|
||||
return ResponseEntity.internalServerError().build();
|
||||
}
|
||||
}
|
||||
|
||||
private Session toSession(SessionEntity entity) {
|
||||
Session session = new Session();
|
||||
session.setSessionId(entity.getSessionId());
|
||||
session.setUserId(entity.getUserId());
|
||||
session.setAufgabenProLevel(entity.getAufgabenAufAktuellemLevel());
|
||||
session.setAufgabenProLevel(entity.getAufgabenProLevel());
|
||||
session.setWahrscheinlichkeitSperre(entity.getWahrscheinlichkeitSperre());
|
||||
session.setWahrscheinlichkeitStrafe(entity.getWahrscheinlichkeitStrafe());
|
||||
session.setZeitfaktorZeitstrafen(entity.getZeitfaktorZeitstrafen());
|
||||
session.setLevel(entity.getLevel());
|
||||
session.setAufgabenAufAktuellemLevel(entity.getAufgabenAufAktuellemLevel());
|
||||
session.setStartZeit(entity.getStartZeit());
|
||||
session.setLetzteAktivitaet(entity.getLetzteAktivitaet());
|
||||
return session;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package de.oaa.xxx.session.controller;
|
||||
|
||||
import de.oaa.xxx.session.AktiveSperre;
|
||||
import de.oaa.xxx.session.Mitspieler;
|
||||
import de.oaa.xxx.session.entity.AktiveSperreEntity;
|
||||
import de.oaa.xxx.session.entity.SessionEntity;
|
||||
import de.oaa.xxx.session.repository.AktiveSperreRepository;
|
||||
import de.oaa.xxx.session.repository.MitspielerRepository;
|
||||
import de.oaa.xxx.session.repository.SessionRepository;
|
||||
@@ -21,6 +24,7 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@RestController("sessionSperreController")
|
||||
@RequestMapping("/session/sperre")
|
||||
@@ -70,6 +74,24 @@ public class SperreController {
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/aktive")
|
||||
public ResponseEntity<List<AktiveSperre>> getAktiveSperren(@RequestParam UUID sessionId) {
|
||||
try {
|
||||
SessionEntity session = sessionRepository.findById(sessionId).orElse(null);
|
||||
if (session == null) return ResponseEntity.noContent().build();
|
||||
List<Mitspieler> mitspielerList = session.getMitspieler().stream()
|
||||
.map(m -> m.toMitspieler())
|
||||
.collect(Collectors.toList());
|
||||
List<AktiveSperre> sperren = session.getAktiveSperren().stream()
|
||||
.map(e -> e.toSperre(mitspielerList))
|
||||
.collect(Collectors.toList());
|
||||
return ResponseEntity.ok(sperren);
|
||||
} catch (Exception exception) {
|
||||
LOGGER.error(exception.getMessage(), exception);
|
||||
return ResponseEntity.internalServerError().build();
|
||||
}
|
||||
}
|
||||
|
||||
@PostMapping("/verlaengern")
|
||||
public ResponseEntity<Void> aktiveVerlaengern(@RequestBody SperrenVerlaengernCallback callback) {
|
||||
if (callback == null || callback.getSpielerId() == null || callback.getFaktor() == null) {
|
||||
|
||||
@@ -84,6 +84,12 @@ public class AktiveSperreEntity {
|
||||
return sperre;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "AktiveSperreEntity[id=" + aktiveSperreId + ", mitspieler=" + (mitspieler != null ? mitspieler.getName() : null)
|
||||
+ ", " + minuten + "min, von=" + startzeit + ", bis=" + endzeit + ", fuer=" + fuer + "]";
|
||||
}
|
||||
|
||||
private Mitspieler getMitspielerFromList(List<Mitspieler> mitspielerList, UUID id) {
|
||||
Optional<Mitspieler> first = mitspielerList.stream().filter(m -> m.getId().equals(id)).findFirst();
|
||||
return first.orElse(null);
|
||||
|
||||
@@ -78,6 +78,12 @@ public class MitspielerEntity {
|
||||
public List<AktiveSperreEntity> getAktiveSperren() { return aktiveSperren; }
|
||||
public void setAktiveSperren(List<AktiveSperreEntity> aktiveSperren) { this.aktiveSperren = aktiveSperren; }
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "MitspielerEntity[mitspielerId=" + mitspielerId + ", name=" + name
|
||||
+ ", geschlecht=" + geschlecht + ", rollen=" + rollen + ", werkzeuge=" + werkzeuge + "]";
|
||||
}
|
||||
|
||||
public Mitspieler toMitspieler() {
|
||||
Mitspieler mitspieler = new Mitspieler();
|
||||
mitspieler.setGeschlecht(geschlecht);
|
||||
|
||||
@@ -82,4 +82,12 @@ public class SessionEntity {
|
||||
|
||||
public Double getZeitfaktorZeitstrafen() { return zeitfaktorZeitstrafen; }
|
||||
public void setZeitfaktorZeitstrafen(Double zeitfaktorZeitstrafen) { this.zeitfaktorZeitstrafen = zeitfaktorZeitstrafen; }
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SessionEntity[sessionId=" + sessionId + ", userId=" + userId
|
||||
+ ", level=" + level + ", aufgaben=" + aufgabenAufAktuellemLevel + "/" + aufgabenProLevel
|
||||
+ ", pStrafe=" + wahrscheinlichkeitStrafe + "%, pSperre=" + wahrscheinlichkeitSperre + "%"
|
||||
+ ", zeitfaktor=" + zeitfaktorZeitstrafen + ", start=" + startZeit + "]";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,4 +18,9 @@ public class SperreCallback extends Callback {
|
||||
|
||||
public String getReleaseText() { return releaseText; }
|
||||
public void setReleaseText(String releaseText) { this.releaseText = releaseText; }
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SperreCallback[sessionId=" + getSessionId() + ", sperreId=" + sperreId + ", spielerId=" + spielerId + "]";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,4 +14,9 @@ public class SperrenVerlaengernCallback extends Callback {
|
||||
|
||||
public Integer getFaktor() { return faktor; }
|
||||
public void setFaktor(Integer faktor) { this.faktor = faktor; }
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SperrenVerlaengernCallback[sessionId=" + getSessionId() + ", spielerId=" + spielerId + ", faktor=" + faktor + "]";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
package de.oaa.xxx.social;
|
||||
|
||||
import de.oaa.xxx.social.dto.ProfileImageDto;
|
||||
import de.oaa.xxx.social.entity.ProfileImageEntity;
|
||||
import de.oaa.xxx.social.entity.ProfileImageLikeEntity;
|
||||
import de.oaa.xxx.social.repository.ProfileImageLikeRepository;
|
||||
import de.oaa.xxx.social.repository.ProfileImageRepository;
|
||||
import de.oaa.xxx.user.UserRepository;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.security.Principal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/social/profile-images")
|
||||
public class ProfileImageController {
|
||||
|
||||
private static final int MAX_IMAGES_PER_USER = 20;
|
||||
|
||||
private final ProfileImageRepository profileImageRepository;
|
||||
private final ProfileImageLikeRepository profileImageLikeRepository;
|
||||
private final UserRepository userRepository;
|
||||
|
||||
public ProfileImageController(ProfileImageRepository profileImageRepository,
|
||||
ProfileImageLikeRepository profileImageLikeRepository,
|
||||
UserRepository userRepository) {
|
||||
this.profileImageRepository = profileImageRepository;
|
||||
this.profileImageLikeRepository = profileImageLikeRepository;
|
||||
this.userRepository = userRepository;
|
||||
}
|
||||
|
||||
record UploadRequest(String imageData) {}
|
||||
|
||||
@PostMapping
|
||||
public ResponseEntity<ProfileImageDto> uploadImage(@RequestBody UploadRequest request, Principal principal) {
|
||||
var meOpt = userRepository.findByEmail(principal.getName());
|
||||
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
|
||||
UUID myId = meOpt.get().getUserId();
|
||||
|
||||
if (request.imageData() == null || request.imageData().isBlank()) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
if (profileImageRepository.countByUserId(myId) >= MAX_IMAGES_PER_USER) {
|
||||
return ResponseEntity.status(422).build();
|
||||
}
|
||||
|
||||
ProfileImageEntity entity = new ProfileImageEntity();
|
||||
entity.setImageId(UUID.randomUUID());
|
||||
entity.setUserId(myId);
|
||||
entity.setImageData(request.imageData());
|
||||
entity.setUploadedAt(LocalDateTime.now());
|
||||
profileImageRepository.save(entity);
|
||||
|
||||
return ResponseEntity.status(201).body(toDto(entity, myId));
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public ResponseEntity<List<ProfileImageDto>> getImages(@RequestParam UUID userId, Principal principal) {
|
||||
var meOpt = userRepository.findByEmail(principal.getName());
|
||||
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
|
||||
UUID myId = meOpt.get().getUserId();
|
||||
|
||||
List<ProfileImageEntity> images = profileImageRepository.findByUserIdOrderByUploadedAtDesc(userId);
|
||||
List<ProfileImageDto> dtos = images.stream().map(img -> toDto(img, myId)).toList();
|
||||
return ResponseEntity.ok(dtos);
|
||||
}
|
||||
|
||||
@DeleteMapping("/{imageId}")
|
||||
public ResponseEntity<Void> deleteImage(@PathVariable UUID imageId, Principal principal) {
|
||||
var meOpt = userRepository.findByEmail(principal.getName());
|
||||
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
|
||||
UUID myId = meOpt.get().getUserId();
|
||||
|
||||
var imgOpt = profileImageRepository.findById(imageId);
|
||||
if (imgOpt.isEmpty()) return ResponseEntity.notFound().build();
|
||||
if (!imgOpt.get().getUserId().equals(myId)) return ResponseEntity.status(403).build();
|
||||
|
||||
profileImageLikeRepository.deleteByImageId(imageId);
|
||||
profileImageRepository.delete(imgOpt.get());
|
||||
return ResponseEntity.noContent().build();
|
||||
}
|
||||
|
||||
@PostMapping("/{imageId}/like")
|
||||
public ResponseEntity<Void> toggleLike(@PathVariable UUID imageId, Principal principal) {
|
||||
var meOpt = userRepository.findByEmail(principal.getName());
|
||||
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
|
||||
UUID myId = meOpt.get().getUserId();
|
||||
|
||||
if (profileImageRepository.findById(imageId).isEmpty()) return ResponseEntity.notFound().build();
|
||||
|
||||
var existing = profileImageLikeRepository.findByImageIdAndUserId(imageId, myId);
|
||||
if (existing.isPresent()) {
|
||||
profileImageLikeRepository.delete(existing.get());
|
||||
} else {
|
||||
ProfileImageLikeEntity like = new ProfileImageLikeEntity();
|
||||
like.setLikeId(UUID.randomUUID());
|
||||
like.setImageId(imageId);
|
||||
like.setUserId(myId);
|
||||
like.setLikedAt(LocalDateTime.now());
|
||||
profileImageLikeRepository.save(like);
|
||||
}
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
private ProfileImageDto toDto(ProfileImageEntity entity, UUID myId) {
|
||||
long likeCount = profileImageLikeRepository.countByImageId(entity.getImageId());
|
||||
boolean likedByMe = profileImageLikeRepository.findByImageIdAndUserId(entity.getImageId(), myId).isPresent();
|
||||
return new ProfileImageDto(entity.getImageId(), entity.getUserId(), entity.getImageData(),
|
||||
entity.getUploadedAt(), likeCount, likedByMe);
|
||||
}
|
||||
}
|
||||
273
xxxthegame/src/main/java/de/oaa/xxx/social/SocialController.java
Normal file
@@ -0,0 +1,273 @@
|
||||
package de.oaa.xxx.social;
|
||||
|
||||
import de.oaa.xxx.social.dto.ConversationSummary;
|
||||
import de.oaa.xxx.social.dto.FriendshipDto;
|
||||
import de.oaa.xxx.social.dto.MessageDto;
|
||||
import de.oaa.xxx.social.dto.UserProfile;
|
||||
import de.oaa.xxx.social.entity.FriendshipEntity;
|
||||
import de.oaa.xxx.social.entity.FriendshipEntity.Status;
|
||||
import de.oaa.xxx.social.entity.MessageEntity;
|
||||
import de.oaa.xxx.social.repository.FriendshipRepository;
|
||||
import de.oaa.xxx.social.repository.MessageRepository;
|
||||
import de.oaa.xxx.user.UserEntity;
|
||||
import de.oaa.xxx.user.UserRepository;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.security.Principal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/social")
|
||||
public class SocialController {
|
||||
|
||||
private final UserRepository userRepository;
|
||||
private final FriendshipRepository friendshipRepository;
|
||||
private final MessageRepository messageRepository;
|
||||
|
||||
public SocialController(UserRepository userRepository,
|
||||
FriendshipRepository friendshipRepository,
|
||||
MessageRepository messageRepository) {
|
||||
this.userRepository = userRepository;
|
||||
this.friendshipRepository = friendshipRepository;
|
||||
this.messageRepository = messageRepository;
|
||||
}
|
||||
|
||||
record FriendRequestBody(UUID receiverId) {}
|
||||
record FriendshipActionBody(UUID friendshipId) {}
|
||||
record SendMessageBody(UUID receiverId, String text) {}
|
||||
|
||||
// ── User Profile ──
|
||||
|
||||
@GetMapping("/users/{userId}")
|
||||
public ResponseEntity<UserProfile> getUserProfile(@PathVariable UUID userId, Principal principal) {
|
||||
var meOpt = userRepository.findByEmail(principal.getName());
|
||||
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
|
||||
UUID myId = meOpt.get().getUserId();
|
||||
return userRepository.findById(userId)
|
||||
.map(u -> ResponseEntity.ok(toUserProfileWithStatus(u, myId)))
|
||||
.orElse(ResponseEntity.notFound().build());
|
||||
}
|
||||
|
||||
// ── User Search ──
|
||||
|
||||
@GetMapping("/users/search")
|
||||
public ResponseEntity<List<UserProfile>> searchUsers(@RequestParam String q, Principal principal) {
|
||||
var meOpt = userRepository.findByEmail(principal.getName());
|
||||
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
|
||||
UUID myId = meOpt.get().getUserId();
|
||||
|
||||
List<UserEntity> results = userRepository.findByNameContainingIgnoreCase(q);
|
||||
List<UserProfile> profiles = results.stream()
|
||||
.filter(u -> !u.getUserId().equals(myId))
|
||||
.limit(20)
|
||||
.map(u -> toUserProfileWithStatus(u, myId))
|
||||
.toList();
|
||||
return ResponseEntity.ok(profiles);
|
||||
}
|
||||
|
||||
// ── Friendship ──
|
||||
|
||||
@PostMapping("/friends/request")
|
||||
public ResponseEntity<Void> sendFriendRequest(@RequestBody FriendRequestBody body, Principal principal) {
|
||||
var meOpt = userRepository.findByEmail(principal.getName());
|
||||
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
|
||||
UUID myId = meOpt.get().getUserId();
|
||||
|
||||
if (friendshipRepository.findExisting(myId, body.receiverId()).isPresent()) {
|
||||
return ResponseEntity.status(409).build();
|
||||
}
|
||||
FriendshipEntity f = new FriendshipEntity();
|
||||
f.setFriendshipId(UUID.randomUUID());
|
||||
f.setSenderId(myId);
|
||||
f.setReceiverId(body.receiverId());
|
||||
f.setStatus(Status.PENDING);
|
||||
f.setCreatedAt(LocalDateTime.now());
|
||||
friendshipRepository.save(f);
|
||||
return ResponseEntity.status(201).build();
|
||||
}
|
||||
|
||||
@PostMapping("/friends/accept")
|
||||
public ResponseEntity<Void> acceptFriendRequest(@RequestBody FriendshipActionBody body, Principal principal) {
|
||||
var meOpt = userRepository.findByEmail(principal.getName());
|
||||
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
|
||||
UUID myId = meOpt.get().getUserId();
|
||||
|
||||
var fOpt = friendshipRepository.findById(body.friendshipId());
|
||||
if (fOpt.isEmpty()) return ResponseEntity.notFound().build();
|
||||
FriendshipEntity f = fOpt.get();
|
||||
if (!f.getReceiverId().equals(myId)) return ResponseEntity.status(403).build();
|
||||
|
||||
f.setStatus(Status.ACCEPTED);
|
||||
friendshipRepository.save(f);
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
@DeleteMapping("/friends/reject")
|
||||
public ResponseEntity<Void> rejectOrRemoveFriend(@RequestBody FriendshipActionBody body, Principal principal) {
|
||||
var meOpt = userRepository.findByEmail(principal.getName());
|
||||
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
|
||||
UUID myId = meOpt.get().getUserId();
|
||||
|
||||
var fOpt = friendshipRepository.findById(body.friendshipId());
|
||||
if (fOpt.isEmpty()) return ResponseEntity.notFound().build();
|
||||
FriendshipEntity f = fOpt.get();
|
||||
if (!f.getSenderId().equals(myId) && !f.getReceiverId().equals(myId)) {
|
||||
return ResponseEntity.status(403).build();
|
||||
}
|
||||
friendshipRepository.delete(f);
|
||||
return ResponseEntity.noContent().build();
|
||||
}
|
||||
|
||||
@GetMapping("/friends")
|
||||
public ResponseEntity<List<FriendshipDto>> getFriends(Principal principal) {
|
||||
var meOpt = userRepository.findByEmail(principal.getName());
|
||||
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
|
||||
UUID myId = meOpt.get().getUserId();
|
||||
|
||||
List<FriendshipDto> dtos = friendshipRepository.findFriends(myId, Status.ACCEPTED).stream()
|
||||
.map(f -> {
|
||||
UUID friendId = f.getSenderId().equals(myId) ? f.getReceiverId() : f.getSenderId();
|
||||
return userRepository.findById(friendId)
|
||||
.map(u -> new FriendshipDto(
|
||||
f.getFriendshipId(),
|
||||
new UserProfile(u.getUserId(), u.getName(), u.getProfilePicture(), u.getProfilePictureHq(), "FRIEND"),
|
||||
f.getStatus().name(),
|
||||
f.getCreatedAt()))
|
||||
.orElse(null);
|
||||
})
|
||||
.filter(Objects::nonNull)
|
||||
.toList();
|
||||
return ResponseEntity.ok(dtos);
|
||||
}
|
||||
|
||||
@GetMapping("/friends/pending")
|
||||
public ResponseEntity<List<FriendshipDto>> getPendingRequests(Principal principal) {
|
||||
var meOpt = userRepository.findByEmail(principal.getName());
|
||||
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
|
||||
UUID myId = meOpt.get().getUserId();
|
||||
|
||||
List<FriendshipDto> dtos = friendshipRepository.findByReceiverIdAndStatus(myId, Status.PENDING).stream()
|
||||
.map(f -> userRepository.findById(f.getSenderId())
|
||||
.map(u -> new FriendshipDto(
|
||||
f.getFriendshipId(),
|
||||
new UserProfile(u.getUserId(), u.getName(), u.getProfilePicture(), u.getProfilePictureHq(), "PENDING_RECEIVED"),
|
||||
f.getStatus().name(),
|
||||
f.getCreatedAt()))
|
||||
.orElse(null))
|
||||
.filter(Objects::nonNull)
|
||||
.toList();
|
||||
return ResponseEntity.ok(dtos);
|
||||
}
|
||||
|
||||
@GetMapping("/friends/pending/count")
|
||||
public ResponseEntity<Long> getPendingCount(Principal principal) {
|
||||
var meOpt = userRepository.findByEmail(principal.getName());
|
||||
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
|
||||
UUID myId = meOpt.get().getUserId();
|
||||
return ResponseEntity.ok(friendshipRepository.countByReceiverIdAndStatus(myId, Status.PENDING));
|
||||
}
|
||||
|
||||
// ── Messages ──
|
||||
|
||||
@PostMapping("/messages")
|
||||
public ResponseEntity<Void> sendMessage(@RequestBody SendMessageBody body, Principal principal) {
|
||||
var meOpt = userRepository.findByEmail(principal.getName());
|
||||
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
|
||||
UUID myId = meOpt.get().getUserId();
|
||||
|
||||
if (body.text() == null || body.text().isBlank()) return ResponseEntity.badRequest().build();
|
||||
|
||||
MessageEntity msg = new MessageEntity();
|
||||
msg.setMessageId(UUID.randomUUID());
|
||||
msg.setSenderId(myId);
|
||||
msg.setReceiverId(body.receiverId());
|
||||
msg.setText(body.text().trim());
|
||||
msg.setSentAt(LocalDateTime.now());
|
||||
messageRepository.save(msg);
|
||||
return ResponseEntity.status(201).build();
|
||||
}
|
||||
|
||||
@GetMapping("/messages")
|
||||
public ResponseEntity<List<ConversationSummary>> getConversations(Principal principal) {
|
||||
var meOpt = userRepository.findByEmail(principal.getName());
|
||||
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
|
||||
UUID myId = meOpt.get().getUserId();
|
||||
|
||||
List<MessageEntity> allMessages = messageRepository.findAllByUser(myId);
|
||||
|
||||
// Group by partner, keep most recent message per partner
|
||||
Map<UUID, MessageEntity> latestByPartner = new LinkedHashMap<>();
|
||||
for (MessageEntity m : allMessages) {
|
||||
UUID partnerId = m.getSenderId().equals(myId) ? m.getReceiverId() : m.getSenderId();
|
||||
latestByPartner.putIfAbsent(partnerId, m);
|
||||
}
|
||||
|
||||
List<ConversationSummary> summaries = new ArrayList<>();
|
||||
for (Map.Entry<UUID, MessageEntity> entry : latestByPartner.entrySet()) {
|
||||
UUID partnerId = entry.getKey();
|
||||
MessageEntity lastMsg = entry.getValue();
|
||||
var partnerOpt = userRepository.findById(partnerId);
|
||||
if (partnerOpt.isEmpty()) continue;
|
||||
|
||||
UserProfile partnerProfile = toUserProfileWithStatus(partnerOpt.get(), myId);
|
||||
MessageDto lastMsgDto = toMessageDto(lastMsg);
|
||||
long unreadCount = allMessages.stream()
|
||||
.filter(m -> m.getSenderId().equals(partnerId)
|
||||
&& m.getReceiverId().equals(myId)
|
||||
&& m.getReadAt() == null)
|
||||
.count();
|
||||
summaries.add(new ConversationSummary(partnerProfile, lastMsgDto, unreadCount));
|
||||
}
|
||||
return ResponseEntity.ok(summaries);
|
||||
}
|
||||
|
||||
@GetMapping("/messages/unread/count")
|
||||
public ResponseEntity<Long> getUnreadCount(Principal principal) {
|
||||
var meOpt = userRepository.findByEmail(principal.getName());
|
||||
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
|
||||
UUID myId = meOpt.get().getUserId();
|
||||
return ResponseEntity.ok(messageRepository.countUnread(myId));
|
||||
}
|
||||
|
||||
@GetMapping("/messages/{partnerId}")
|
||||
public ResponseEntity<List<MessageDto>> getConversation(@PathVariable UUID partnerId, Principal principal) {
|
||||
var meOpt = userRepository.findByEmail(principal.getName());
|
||||
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
|
||||
UUID myId = meOpt.get().getUserId();
|
||||
|
||||
List<MessageEntity> messages = messageRepository.findConversation(myId, partnerId, PageRequest.of(0, 50));
|
||||
messageRepository.markAsRead(myId, partnerId, LocalDateTime.now());
|
||||
|
||||
return ResponseEntity.ok(messages.stream().map(this::toMessageDto).toList());
|
||||
}
|
||||
|
||||
// ── Helpers ──
|
||||
|
||||
private UserProfile toUserProfileWithStatus(UserEntity user, UUID myId) {
|
||||
String status = "NONE";
|
||||
var existing = friendshipRepository.findExisting(myId, user.getUserId());
|
||||
if (existing.isPresent()) {
|
||||
FriendshipEntity f = existing.get();
|
||||
if (f.getStatus() == Status.ACCEPTED) {
|
||||
status = "FRIEND";
|
||||
} else if (f.getSenderId().equals(myId)) {
|
||||
status = "PENDING_SENT";
|
||||
} else {
|
||||
status = "PENDING_RECEIVED";
|
||||
}
|
||||
}
|
||||
return new UserProfile(user.getUserId(), user.getName(), user.getProfilePicture(), user.getProfilePictureHq(), status);
|
||||
}
|
||||
|
||||
private MessageDto toMessageDto(MessageEntity m) {
|
||||
String senderName = userRepository.findById(m.getSenderId())
|
||||
.map(UserEntity::getName)
|
||||
.orElse("Unbekannt");
|
||||
return new MessageDto(
|
||||
m.getMessageId(), m.getSenderId(), senderName,
|
||||
m.getReceiverId(), m.getText(), m.getSentAt(), m.getReadAt() != null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
package de.oaa.xxx.social.dto;
|
||||
|
||||
public record ConversationSummary(UserProfile partner, MessageDto lastMessage, long unreadCount) {}
|
||||
@@ -0,0 +1,6 @@
|
||||
package de.oaa.xxx.social.dto;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
public record FriendshipDto(UUID friendshipId, UserProfile user, String status, LocalDateTime createdAt) {}
|
||||
@@ -0,0 +1,6 @@
|
||||
package de.oaa.xxx.social.dto;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
public record MessageDto(UUID messageId, UUID senderId, String senderName, UUID receiverId, String text, LocalDateTime sentAt, boolean read) {}
|
||||
@@ -0,0 +1,7 @@
|
||||
package de.oaa.xxx.social.dto;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
public record ProfileImageDto(UUID imageId, UUID userId, String imageData,
|
||||
LocalDateTime uploadedAt, long likeCount, boolean likedByMe) {}
|
||||
@@ -0,0 +1,5 @@
|
||||
package de.oaa.xxx.social.dto;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public record UserProfile(UUID userId, String name, String profilePicture, String profilePictureHq, String friendStatus) {}
|
||||
@@ -0,0 +1,44 @@
|
||||
package de.oaa.xxx.social.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
@Entity
|
||||
@Table(name = "friendship")
|
||||
public class FriendshipEntity {
|
||||
|
||||
public enum Status { PENDING, ACCEPTED }
|
||||
|
||||
@Id
|
||||
@Column
|
||||
private UUID friendshipId;
|
||||
|
||||
@Column(nullable = false)
|
||||
private UUID senderId;
|
||||
|
||||
@Column(nullable = false)
|
||||
private UUID receiverId;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(nullable = false, length = 10)
|
||||
private Status status;
|
||||
|
||||
@Column(nullable = false)
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
public UUID getFriendshipId() { return friendshipId; }
|
||||
public void setFriendshipId(UUID friendshipId) { this.friendshipId = friendshipId; }
|
||||
|
||||
public UUID getSenderId() { return senderId; }
|
||||
public void setSenderId(UUID senderId) { this.senderId = senderId; }
|
||||
|
||||
public UUID getReceiverId() { return receiverId; }
|
||||
public void setReceiverId(UUID receiverId) { this.receiverId = receiverId; }
|
||||
|
||||
public Status getStatus() { return status; }
|
||||
public void setStatus(Status status) { this.status = status; }
|
||||
|
||||
public LocalDateTime getCreatedAt() { return createdAt; }
|
||||
public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package de.oaa.xxx.social.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
@Entity
|
||||
@Table(name = "message")
|
||||
public class MessageEntity {
|
||||
|
||||
@Id
|
||||
@Column
|
||||
private UUID messageId;
|
||||
|
||||
@Column(nullable = false)
|
||||
private UUID senderId;
|
||||
|
||||
@Column(nullable = false)
|
||||
private UUID receiverId;
|
||||
|
||||
@Column(columnDefinition = "MEDIUMTEXT", nullable = false)
|
||||
private String text;
|
||||
|
||||
@Column(nullable = false)
|
||||
private LocalDateTime sentAt;
|
||||
|
||||
@Column
|
||||
private LocalDateTime readAt;
|
||||
|
||||
public UUID getMessageId() { return messageId; }
|
||||
public void setMessageId(UUID messageId) { this.messageId = messageId; }
|
||||
|
||||
public UUID getSenderId() { return senderId; }
|
||||
public void setSenderId(UUID senderId) { this.senderId = senderId; }
|
||||
|
||||
public UUID getReceiverId() { return receiverId; }
|
||||
public void setReceiverId(UUID receiverId) { this.receiverId = receiverId; }
|
||||
|
||||
public String getText() { return text; }
|
||||
public void setText(String text) { this.text = text; }
|
||||
|
||||
public LocalDateTime getSentAt() { return sentAt; }
|
||||
public void setSentAt(LocalDateTime sentAt) { this.sentAt = sentAt; }
|
||||
|
||||
public LocalDateTime getReadAt() { return readAt; }
|
||||
public void setReadAt(LocalDateTime readAt) { this.readAt = readAt; }
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package de.oaa.xxx.social.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
@Entity
|
||||
@Table(name = "profile_image")
|
||||
public class ProfileImageEntity {
|
||||
|
||||
@Id
|
||||
@Column
|
||||
private UUID imageId;
|
||||
|
||||
@Column(nullable = false)
|
||||
private UUID userId;
|
||||
|
||||
@Column(columnDefinition = "MEDIUMTEXT", nullable = false)
|
||||
private String imageData;
|
||||
|
||||
@Column(nullable = false)
|
||||
private LocalDateTime uploadedAt;
|
||||
|
||||
public UUID getImageId() { return imageId; }
|
||||
public void setImageId(UUID imageId) { this.imageId = imageId; }
|
||||
|
||||
public UUID getUserId() { return userId; }
|
||||
public void setUserId(UUID userId) { this.userId = userId; }
|
||||
|
||||
public String getImageData() { return imageData; }
|
||||
public void setImageData(String imageData) { this.imageData = imageData; }
|
||||
|
||||
public LocalDateTime getUploadedAt() { return uploadedAt; }
|
||||
public void setUploadedAt(LocalDateTime uploadedAt) { this.uploadedAt = uploadedAt; }
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package de.oaa.xxx.social.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
@Entity
|
||||
@Table(name = "profile_image_like")
|
||||
public class ProfileImageLikeEntity {
|
||||
|
||||
@Id
|
||||
@Column
|
||||
private UUID likeId;
|
||||
|
||||
@Column(nullable = false)
|
||||
private UUID imageId;
|
||||
|
||||
@Column(nullable = false)
|
||||
private UUID userId;
|
||||
|
||||
@Column(nullable = false)
|
||||
private LocalDateTime likedAt;
|
||||
|
||||
public UUID getLikeId() { return likeId; }
|
||||
public void setLikeId(UUID likeId) { this.likeId = likeId; }
|
||||
|
||||
public UUID getImageId() { return imageId; }
|
||||
public void setImageId(UUID imageId) { this.imageId = imageId; }
|
||||
|
||||
public UUID getUserId() { return userId; }
|
||||
public void setUserId(UUID userId) { this.userId = userId; }
|
||||
|
||||
public LocalDateTime getLikedAt() { return likedAt; }
|
||||
public void setLikedAt(LocalDateTime likedAt) { this.likedAt = likedAt; }
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package de.oaa.xxx.social.repository;
|
||||
|
||||
import de.oaa.xxx.social.entity.FriendshipEntity;
|
||||
import de.oaa.xxx.social.entity.FriendshipEntity.Status;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
public interface FriendshipRepository extends JpaRepository<FriendshipEntity, UUID> {
|
||||
|
||||
List<FriendshipEntity> findByReceiverIdAndStatus(UUID receiverId, Status status);
|
||||
|
||||
long countByReceiverIdAndStatus(UUID receiverId, Status status);
|
||||
|
||||
@Query("SELECT f FROM FriendshipEntity f WHERE (f.senderId = :userId OR f.receiverId = :userId) AND f.status = :status")
|
||||
List<FriendshipEntity> findFriends(@Param("userId") UUID userId, @Param("status") Status status);
|
||||
|
||||
@Query("SELECT f FROM FriendshipEntity f WHERE (f.senderId = :userA AND f.receiverId = :userB) OR (f.senderId = :userB AND f.receiverId = :userA)")
|
||||
Optional<FriendshipEntity> findExisting(@Param("userA") UUID userA, @Param("userB") UUID userB);
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package de.oaa.xxx.social.repository;
|
||||
|
||||
import de.oaa.xxx.social.entity.MessageEntity;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Modifying;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
public interface MessageRepository extends JpaRepository<MessageEntity, UUID> {
|
||||
|
||||
@Query("SELECT m FROM MessageEntity m WHERE (m.senderId = :userA AND m.receiverId = :userB) OR (m.senderId = :userB AND m.receiverId = :userA) ORDER BY m.sentAt DESC")
|
||||
List<MessageEntity> findConversation(@Param("userA") UUID userA, @Param("userB") UUID userB, Pageable pageable);
|
||||
|
||||
@Query("SELECT m FROM MessageEntity m WHERE m.senderId = :userId OR m.receiverId = :userId ORDER BY m.sentAt DESC")
|
||||
List<MessageEntity> findAllByUser(@Param("userId") UUID userId);
|
||||
|
||||
@Query("SELECT COUNT(m) FROM MessageEntity m WHERE m.receiverId = :userId AND m.readAt IS NULL")
|
||||
long countUnread(@Param("userId") UUID userId);
|
||||
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Query("UPDATE MessageEntity m SET m.readAt = :now WHERE m.senderId = :partnerId AND m.receiverId = :userId AND m.readAt IS NULL")
|
||||
void markAsRead(@Param("userId") UUID userId, @Param("partnerId") UUID partnerId, @Param("now") LocalDateTime now);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package de.oaa.xxx.social.repository;
|
||||
|
||||
import de.oaa.xxx.social.entity.ProfileImageLikeEntity;
|
||||
import jakarta.transaction.Transactional;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Modifying;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
public interface ProfileImageLikeRepository extends JpaRepository<ProfileImageLikeEntity, UUID> {
|
||||
|
||||
Optional<ProfileImageLikeEntity> findByImageIdAndUserId(UUID imageId, UUID userId);
|
||||
|
||||
long countByImageId(UUID imageId);
|
||||
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Query("DELETE FROM ProfileImageLikeEntity l WHERE l.imageId = :imageId")
|
||||
void deleteByImageId(@Param("imageId") UUID imageId);
|
||||
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Query("DELETE FROM ProfileImageLikeEntity l WHERE l.userId = :userId")
|
||||
void deleteByUserId(@Param("userId") UUID userId);
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package de.oaa.xxx.social.repository;
|
||||
|
||||
import de.oaa.xxx.social.entity.ProfileImageEntity;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
public interface ProfileImageRepository extends JpaRepository<ProfileImageEntity, UUID> {
|
||||
|
||||
List<ProfileImageEntity> findByUserIdOrderByUploadedAtDesc(UUID userId);
|
||||
|
||||
long countByUserId(UUID userId);
|
||||
}
|
||||