Compare commits

3 Commits

175 changed files with 5909 additions and 3526 deletions

View File

@@ -59,7 +59,21 @@
"Bash(mkdir -p /home/mario/Workspaces/xxx-thegame/xxxthegame/src/main/java/de/oaa/xxx/games/common/repository)", "Bash(mkdir -p /home/mario/Workspaces/xxx-thegame/xxxthegame/src/main/java/de/oaa/xxx/games/common/repository)",
"Bash(mkdir -p /home/mario/Workspaces/xxx-thegame/xxxthegame/src/main/java/de/oaa/xxx/games/common/aufgaben)", "Bash(mkdir -p /home/mario/Workspaces/xxx-thegame/xxxthegame/src/main/java/de/oaa/xxx/games/common/aufgaben)",
"Bash(mkdir -p /home/mario/Workspaces/xxx-thegame/xxxthegame/src/main/java/de/oaa/xxx/games/vanilla/entity)", "Bash(mkdir -p /home/mario/Workspaces/xxx-thegame/xxxthegame/src/main/java/de/oaa/xxx/games/vanilla/entity)",
"Bash(mkdir -p /home/mario/Workspaces/xxx-thegame/xxxthegame/src/main/java/de/oaa/xxx/games/vanilla/repository)" "Bash(mkdir -p /home/mario/Workspaces/xxx-thegame/xxxthegame/src/main/java/de/oaa/xxx/games/vanilla/repository)",
"Bash(grep -l \"script>\" /home/mario/Workspaces/xxx-thegame/xxxthegame/src/main/resources/static/*.html)",
"Bash(./gradlew compileJava -q)",
"Bash(do)",
"Bash(echo \"=== $f ===\")",
"Read(//home/mario/Workspaces/xxx-thegame/.claude/worktrees/agent-a211ce07/**)",
"Bash(./gradlew :xxxthegame:compileJava -q)",
"Bash(uniq done:*)",
"Bash(mv aufgaben.html games/bdsm/)",
"Bash(mv bdsm.html games/bdsm/)",
"Bash(mv bdsm-einladung.html games/bdsm/)",
"Bash(mv bdsmingame.html games/bdsm/)",
"Bash(mv bdsmplayers.html games/bdsm/)",
"Bash(perl -pi -e 's|\\\\.requestMatchers\\\\\\(\"\"/\\\\*\\\\.html\"\"\\\\\\)\\\\.permitAll\\\\\\(\\\\\\)|.requestMatchers\\(\"\"/*.html\"\"\\).permitAll\\(\\)\\\\n .requestMatchers\\(\"\"/**/*.html\"\"\\).permitAll\\(\\)|' /home/mario/Workspaces/xxx-thegame/xxxthegame/src/main/java/de/oaa/xxx/config/SecurityConfig.java)",
"Bash(./gradlew compileJava -x test)"
] ]
} }
} }

Submodule .claude/worktrees/agent-a211ce07 added at e262a1b46b

View File

@@ -1,5 +1,5 @@
#Fri Mar 27 07:46:21 CET 2026 #Tue Mar 31 20:02:52 CEST 2026
display=\:0 display=\:0
host=mario-mint host=mario-mint
process-id=5726 process-id=9888
user=mario user=mario

View File

@@ -1172,3 +1172,384 @@ java.lang.NullPointerException: Cannot invoke "org.eclipse.jdt.internal.compiler
at org.eclipse.jdt.internal.ui.util.SelectionUtil.logException(SelectionUtil.java:157) at org.eclipse.jdt.internal.ui.util.SelectionUtil.logException(SelectionUtil.java:157)
at org.eclipse.jdt.internal.ui.text.java.hover.AbstractJavaEditorTextHover.getJavaElementsAt(AbstractJavaEditorTextHover.java:124) at org.eclipse.jdt.internal.ui.text.java.hover.AbstractJavaEditorTextHover.getJavaElementsAt(AbstractJavaEditorTextHover.java:124)
... 14 more ... 14 more
!ENTRY org.springframework.tooling.boot.ls 1 0 2026-03-27 12:09:57.934
!MESSAGE DelegatingStreamConnectionProvider - Stopping Boot LS
!SESSION 2026-03-29 16:28:05.532 -----------------------------------------------
eclipse.buildId=4.39.0.20260305-0817
java.version=21.0.6
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 -clean -product org.eclipse.epp.package.java.product
!ENTRY ch.qos.logback.classic 1 0 2026-03-29 16:28:07.076
!MESSAGE Activated before the state location was initialized. Retry after the state location is initialized.
!ENTRY ch.qos.logback.classic 1 0 2026-03-29 16:28:10.040
!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-29 16:28:10.201
!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-29 16:28:10.201
!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-29 16:28:10.332
!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-29 16:28:10.332
!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-29 17:27:01.590
!MESSAGE Keybinding conflicts occurred. They may interfere with normal accelerator operation.
!SUBENTRY 1 org.eclipse.jface 2 0 2026-03-29 17:27:01.590
!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.jdt.ui 4 10001 2026-03-29 17:41:39.966
!MESSAGE Internal Error
!STACK 1
Java Model Exception: Error in Java Model (code 969): toMitspielerBdsm(VanillaMitspieler) {key=Lde/oaa/xxx/games/vanilla/VanillaGameDurchfuehren;.toMitspielerBdsm(Lde/oaa/xxx/games/vanilla/VanillaMitspieler;)Lde/oaa/xxx/games/bdsm/BdsmMitspieler;} [in VanillaGameDurchfuehren [in [Working copy] VanillaGameDurchfuehren.java [in de.oaa.xxx.games.vanilla [in src/main/java [in xxxthegame]]]]] does not exist
at org.eclipse.jdt.internal.core.JavaElement.newNotPresentException(JavaElement.java:548)
at org.eclipse.jdt.internal.core.JavaElement.openWhenClosed(JavaElement.java:591)
at org.eclipse.jdt.internal.core.JavaElement.getElementInfo(JavaElement.java:294)
at org.eclipse.jdt.internal.core.JavaElement.getElementInfo(JavaElement.java:280)
at org.eclipse.jdt.internal.core.Member.getFlags(Member.java:193)
at org.eclipse.jdt.internal.corext.util.JdtFlags.isPrivate(JdtFlags.java:157)
at org.eclipse.jdt.internal.ui.javaeditor.JavaElementHyperlinkSuperImplementationDetector.isOverriddenMethod(JavaElementHyperlinkSuperImplementationDetector.java:57)
at org.eclipse.jdt.internal.ui.javaeditor.JavaElementHyperlinkSuperImplementationDetector.addHyperlinks(JavaElementHyperlinkSuperImplementationDetector.java:44)
at org.eclipse.jdt.internal.ui.javaeditor.JavaElementHyperlinkDetector.detectHyperlinksCached(JavaElementHyperlinkDetector.java:139)
at org.eclipse.jdt.internal.ui.javaeditor.JavaElementHyperlinkDetector.lambda$0(JavaElementHyperlinkDetector.java:84)
at org.eclipse.jdt.internal.core.JavaModelManager.cacheZipFiles(JavaModelManager.java:5709)
at org.eclipse.jdt.internal.core.JavaModelManager.callReadOnly(JavaModelManager.java:5698)
at org.eclipse.jdt.core.JavaCore.callReadOnly(JavaCore.java:6211)
at org.eclipse.jdt.internal.ui.javaeditor.JavaElementHyperlinkDetector.detectHyperlinks(JavaElementHyperlinkDetector.java:84)
at org.eclipse.ui.texteditor.HyperlinkDetectorRegistry$HyperlinkDetectorDelegate.detectHyperlinks(HyperlinkDetectorRegistry.java:83)
at org.eclipse.jface.text.hyperlink.HyperlinkManager.findHyperlinks(HyperlinkManager.java:294)
at org.eclipse.jface.text.hyperlink.HyperlinkManager.findHyperlinks(HyperlinkManager.java:264)
at org.eclipse.jface.text.hyperlink.HyperlinkManager.mouseMove(HyperlinkManager.java:474)
at org.eclipse.swt.widgets.TypedListener.handleEvent(TypedListener.java:254)
at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:91)
at org.eclipse.swt.widgets.Display.sendEvent(Display.java:5845)
at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1656)
at org.eclipse.swt.widgets.Display.runDeferredEvents(Display.java:5060)
at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:4497)
at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$5.run(PartRenderingEngine.java:1160)
at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339)
at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:1051)
at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:153)
at org.eclipse.ui.internal.Workbench.lambda$3(Workbench.java:684)
at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339)
at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:583)
at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:173)
at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:185)
at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:219)
at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:149)
at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:115)
at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:467)
at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:298)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:615)
at org.eclipse.equinox.launcher.Main.basicRun(Main.java:563)
at org.eclipse.equinox.launcher.Main.run(Main.java:1415)
at org.eclipse.equinox.launcher.Main.main(Main.java:1387)
!SUBENTRY 1 org.eclipse.jdt.core 4 969 2026-03-29 17:41:39.967
!MESSAGE toMitspielerBdsm(VanillaMitspieler) {key=Lde/oaa/xxx/games/vanilla/VanillaGameDurchfuehren;.toMitspielerBdsm(Lde/oaa/xxx/games/vanilla/VanillaMitspieler;)Lde/oaa/xxx/games/bdsm/BdsmMitspieler;} [in VanillaGameDurchfuehren [in [Working copy] VanillaGameDurchfuehren.java [in de.oaa.xxx.games.vanilla [in src/main/java [in xxxthegame]]]]] does not exist
!ENTRY org.eclipse.jdt.ui 4 10001 2026-03-29 17:41:39.968
!MESSAGE Internal Error
!STACK 1
Java Model Exception: Error in Java Model (code 969): toMitspielerBdsm(VanillaMitspieler) {key=Lde/oaa/xxx/games/vanilla/VanillaGameDurchfuehren;.toMitspielerBdsm(Lde/oaa/xxx/games/vanilla/VanillaMitspieler;)Lde/oaa/xxx/games/bdsm/BdsmMitspieler;} [in VanillaGameDurchfuehren [in [Working copy] VanillaGameDurchfuehren.java [in de.oaa.xxx.games.vanilla [in src/main/java [in xxxthegame]]]]] does not exist
at org.eclipse.jdt.internal.core.JavaElement.newNotPresentException(JavaElement.java:548)
at org.eclipse.jdt.internal.core.JavaElement.openWhenClosed(JavaElement.java:591)
at org.eclipse.jdt.internal.core.JavaElement.getElementInfo(JavaElement.java:294)
at org.eclipse.jdt.internal.core.JavaElement.getElementInfo(JavaElement.java:280)
at org.eclipse.jdt.internal.core.SourceMethod.getReturnType(SourceMethod.java:223)
at org.eclipse.jdt.internal.ui.javaeditor.JavaElementHyperlinkReturnTypeDetector.addHyperlinks(JavaElementHyperlinkReturnTypeDetector.java:43)
at org.eclipse.jdt.internal.ui.javaeditor.JavaElementHyperlinkDetector.detectHyperlinksCached(JavaElementHyperlinkDetector.java:139)
at org.eclipse.jdt.internal.ui.javaeditor.JavaElementHyperlinkDetector.lambda$0(JavaElementHyperlinkDetector.java:84)
at org.eclipse.jdt.internal.core.JavaModelManager.cacheZipFiles(JavaModelManager.java:5709)
at org.eclipse.jdt.internal.core.JavaModelManager.callReadOnly(JavaModelManager.java:5698)
at org.eclipse.jdt.core.JavaCore.callReadOnly(JavaCore.java:6211)
at org.eclipse.jdt.internal.ui.javaeditor.JavaElementHyperlinkDetector.detectHyperlinks(JavaElementHyperlinkDetector.java:84)
at org.eclipse.ui.texteditor.HyperlinkDetectorRegistry$HyperlinkDetectorDelegate.detectHyperlinks(HyperlinkDetectorRegistry.java:83)
at org.eclipse.jface.text.hyperlink.HyperlinkManager.findHyperlinks(HyperlinkManager.java:294)
at org.eclipse.jface.text.hyperlink.HyperlinkManager.findHyperlinks(HyperlinkManager.java:264)
at org.eclipse.jface.text.hyperlink.HyperlinkManager.mouseMove(HyperlinkManager.java:474)
at org.eclipse.swt.widgets.TypedListener.handleEvent(TypedListener.java:254)
at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:91)
at org.eclipse.swt.widgets.Display.sendEvent(Display.java:5845)
at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1656)
at org.eclipse.swt.widgets.Display.runDeferredEvents(Display.java:5060)
at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:4497)
at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$5.run(PartRenderingEngine.java:1160)
at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339)
at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:1051)
at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:153)
at org.eclipse.ui.internal.Workbench.lambda$3(Workbench.java:684)
at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339)
at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:583)
at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:173)
at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:185)
at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:219)
at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:149)
at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:115)
at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:467)
at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:298)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:615)
at org.eclipse.equinox.launcher.Main.basicRun(Main.java:563)
at org.eclipse.equinox.launcher.Main.run(Main.java:1415)
at org.eclipse.equinox.launcher.Main.main(Main.java:1387)
!SUBENTRY 1 org.eclipse.jdt.core 4 969 2026-03-29 17:41:39.969
!MESSAGE toMitspielerBdsm(VanillaMitspieler) {key=Lde/oaa/xxx/games/vanilla/VanillaGameDurchfuehren;.toMitspielerBdsm(Lde/oaa/xxx/games/vanilla/VanillaMitspieler;)Lde/oaa/xxx/games/bdsm/BdsmMitspieler;} [in VanillaGameDurchfuehren [in [Working copy] VanillaGameDurchfuehren.java [in de.oaa.xxx.games.vanilla [in src/main/java [in xxxthegame]]]]] does not exist
!ENTRY org.eclipse.debug.core 4 125 2026-03-29 21:26:35.374
!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)
!ENTRY org.springframework.tooling.boot.ls 1 0 2026-03-29 22:58:21.587
!MESSAGE DelegatingStreamConnectionProvider - Stopping Boot LS
!SESSION 2026-03-30 07:32:31.775 -----------------------------------------------
eclipse.buildId=4.39.0.20260305-0817
java.version=21.0.6
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 -clean -product org.eclipse.epp.package.java.product
!ENTRY ch.qos.logback.classic 1 0 2026-03-30 07:32:34.763
!MESSAGE Activated before the state location was initialized. Retry after the state location is initialized.
!ENTRY ch.qos.logback.classic 1 0 2026-03-30 07:33:02.086
!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-30 07:33:02.235
!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-30 07:33:02.235
!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-30 07:33:02.395
!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-30 07:33:02.395
!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-30 22:51:45.208
!MESSAGE Keybinding conflicts occurred. They may interfere with normal accelerator operation.
!SUBENTRY 1 org.eclipse.jface 2 0 2026-03-30 22:51:45.208
!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.springframework.tooling.boot.ls 1 0 2026-03-30 22:56:58.269
!MESSAGE DelegatingStreamConnectionProvider - Stopping Boot LS
!SESSION 2026-03-31 08:24:30.017 -----------------------------------------------
eclipse.buildId=4.39.0.20260305-0817
java.version=21.0.6
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 -clean -product org.eclipse.epp.package.java.product
!ENTRY ch.qos.logback.classic 1 0 2026-03-31 08:24:31.551
!MESSAGE Activated before the state location was initialized. Retry after the state location is initialized.
!ENTRY ch.qos.logback.classic 1 0 2026-03-31 08:24:34.912
!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-31 08:24:35.071
!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-31 08:24:35.071
!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-31 08:24:35.221
!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-31 08:24:35.221
!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-31 09:50:47.663
!MESSAGE Keybinding conflicts occurred. They may interfere with normal accelerator operation.
!SUBENTRY 1 org.eclipse.jface 2 0 2026-03-31 09:50:47.663
!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.springframework.tooling.boot.ls 1 0 2026-03-31 10:19:59.888
!MESSAGE DelegatingStreamConnectionProvider - Stopping Boot LS
!SESSION 2026-03-31 11:33:04.112 -----------------------------------------------
eclipse.buildId=4.39.0.20260305-0817
java.version=21.0.6
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 -clean -product org.eclipse.epp.package.java.product
!ENTRY ch.qos.logback.classic 1 0 2026-03-31 11:33:05.636
!MESSAGE Activated before the state location was initialized. Retry after the state location is initialized.
!ENTRY ch.qos.logback.classic 1 0 2026-03-31 11:33:15.062
!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-31 11:33:15.221
!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-31 11:33:15.221
!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-31 11:33:15.366
!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-31 11:33:15.366
!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-31 11:35:22.321
!MESSAGE Keybinding conflicts occurred. They may interfere with normal accelerator operation.
!SUBENTRY 1 org.eclipse.jface 2 0 2026-03-31 11:35:22.321
!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.springframework.tooling.boot.ls 1 0 2026-03-31 15:25:37.605
!MESSAGE DelegatingStreamConnectionProvider - Stopping Boot LS
!SESSION 2026-03-31 20:02:49.357 -----------------------------------------------
eclipse.buildId=4.39.0.20260305-0817
java.version=21.0.6
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 -clean -product org.eclipse.epp.package.java.product
!ENTRY ch.qos.logback.classic 1 0 2026-03-31 20:02:50.906
!MESSAGE Activated before the state location was initialized. Retry after the state location is initialized.
!ENTRY ch.qos.logback.classic 1 0 2026-03-31 20:02:53.313
!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-31 20:02:53.439
!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-31 20:02:53.440
!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-31 20:02:53.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-31 20:02:53.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-31 20:48:38.922
!MESSAGE Keybinding conflicts occurred. They may interfere with normal accelerator operation.
!SUBENTRY 1 org.eclipse.jface 2 0 2026-03-31 20:48:38.922
!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-31 21:38:55.972
!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.jface 2 0 2026-03-31 22:11:43.432
!MESSAGE Keybinding conflicts occurred. They may interfere with normal accelerator operation.
!SUBENTRY 1 org.eclipse.jface 2 0 2026-03-31 22:11:43.432
!MESSAGE A conflict occurred for CTRL+R:
Binding(CTRL+R,
ParameterizedCommand(Command(org.eclipse.debug.ui.commands.RunToLine,Run to Line,
Resume and break when execution reaches the current line,
Category(org.eclipse.debug.ui.category.run,Run/Debug,Run/Debug command category,true),
WorkbenchHandlerServiceHandler("org.eclipse.debug.ui.commands.RunToLine"),
,,true),null),
org.eclipse.ui.defaultAcceleratorConfiguration,
org.eclipse.debug.ui.debugging,,,system)
Binding(CTRL+R,
ParameterizedCommand(Command(org.springframework.ide.eclipse.boot.restart.commands.restart,Trigger Restart,
Restart Spring Boot Application,
Category(org.eclipse.debug.ui.category.run,Run/Debug,Run/Debug command category,true),
WorkbenchHandlerServiceHandler("org.springframework.ide.eclipse.boot.restart.commands.restart"),
,,true),null),
org.eclipse.ui.defaultAcceleratorConfiguration,
org.eclipse.debug.ui.console,,,system)

View File

@@ -1,7 +1,7 @@
[ { [ {
"version" : "9.5.0-20260326015913+0000", "version" : "9.5.0-20260331054436+0000",
"buildTime" : "20260326015913+0000", "buildTime" : "20260331054436+0000",
"commitId" : "b62b56136fe3f28a01c3e35f77694c3d5af75916", "commitId" : "04cdc7917382feb3229f13b035ea48f106ad01f1",
"current" : false, "current" : false,
"snapshot" : true, "snapshot" : true,
"nightly" : false, "nightly" : false,
@@ -10,15 +10,15 @@
"rcFor" : "", "rcFor" : "",
"milestoneFor" : "", "milestoneFor" : "",
"broken" : false, "broken" : false,
"downloadUrl" : "https://services.gradle.org/distributions-snapshots/gradle-9.5.0-20260326015913+0000-bin.zip", "downloadUrl" : "https://services.gradle.org/distributions-snapshots/gradle-9.5.0-20260331054436+0000-bin.zip",
"checksumUrl" : "https://services.gradle.org/distributions-snapshots/gradle-9.5.0-20260326015913+0000-bin.zip.sha256", "checksumUrl" : "https://services.gradle.org/distributions-snapshots/gradle-9.5.0-20260331054436+0000-bin.zip.sha256",
"checksum" : "ace6a98f3a565a82cd108c6a115f64837cd5bb8e95d563c6e2dfb884ea3a8fe5", "checksum" : "4ab20ff318524006769da0e39fa7cf8f355a5ca54ea213dd2edadd9019d95649",
"wrapperChecksumUrl" : "https://services.gradle.org/distributions-snapshots/gradle-9.5.0-20260326015913+0000-wrapper.jar.sha256", "wrapperChecksumUrl" : "https://services.gradle.org/distributions-snapshots/gradle-9.5.0-20260331054436+0000-wrapper.jar.sha256",
"wrapperChecksum" : "497c8c2a7e5031f6aa847f88104aa80a93532ec32ee17bdb8d1d2f67a194a9c7" "wrapperChecksum" : "497c8c2a7e5031f6aa847f88104aa80a93532ec32ee17bdb8d1d2f67a194a9c7"
}, { }, {
"version" : "9.6.0-20260326003843+0000", "version" : "9.6.0-20260331012943+0000",
"buildTime" : "20260326003843+0000", "buildTime" : "20260331012943+0000",
"commitId" : "f6b5714b236ea05298517d966a339045da81a5ee", "commitId" : "6921c9df28f41760c3a348e57a9bf332d093742e",
"current" : false, "current" : false,
"snapshot" : true, "snapshot" : true,
"nightly" : true, "nightly" : true,
@@ -27,11 +27,28 @@
"rcFor" : "", "rcFor" : "",
"milestoneFor" : "", "milestoneFor" : "",
"broken" : false, "broken" : false,
"downloadUrl" : "https://services.gradle.org/distributions-snapshots/gradle-9.6.0-20260326003843+0000-bin.zip", "downloadUrl" : "https://services.gradle.org/distributions-snapshots/gradle-9.6.0-20260331012943+0000-bin.zip",
"checksumUrl" : "https://services.gradle.org/distributions-snapshots/gradle-9.6.0-20260326003843+0000-bin.zip.sha256", "checksumUrl" : "https://services.gradle.org/distributions-snapshots/gradle-9.6.0-20260331012943+0000-bin.zip.sha256",
"checksum" : "9d70bda347d4cdbc4fc8ce8550d53ce5d1b2add847f4720e8543ff6c74c322b8", "checksum" : "83f35f9ee38851b1835842ba93b9846fc43be77792c2cae138570c5f262039e0",
"wrapperChecksumUrl" : "https://services.gradle.org/distributions-snapshots/gradle-9.6.0-20260326003843+0000-wrapper.jar.sha256", "wrapperChecksumUrl" : "https://services.gradle.org/distributions-snapshots/gradle-9.6.0-20260331012943+0000-wrapper.jar.sha256",
"wrapperChecksum" : "f307680272dffdb8e636f1169adfbf693513005c80aa06e8d381f20390a06e6a" "wrapperChecksum" : "497c8c2a7e5031f6aa847f88104aa80a93532ec32ee17bdb8d1d2f67a194a9c7"
}, {
"version" : "9.5.0-rc-1",
"buildTime" : "20260330120715+0000",
"commitId" : "6a1704c113de068f7e9a6744245c7eb4bc5091d0",
"current" : false,
"snapshot" : false,
"nightly" : false,
"releaseNightly" : false,
"activeRc" : true,
"rcFor" : "9.5.0",
"milestoneFor" : "",
"broken" : false,
"downloadUrl" : "https://services.gradle.org/distributions/gradle-9.5.0-rc-1-bin.zip",
"checksumUrl" : "https://services.gradle.org/distributions/gradle-9.5.0-rc-1-bin.zip.sha256",
"checksum" : "66d79b10eb939c954bf1ac3be9d9cde985301b56058d49542286c35782ae1e74",
"wrapperChecksumUrl" : "https://services.gradle.org/distributions/gradle-9.5.0-rc-1-wrapper.jar.sha256",
"wrapperChecksum" : "497c8c2a7e5031f6aa847f88104aa80a93532ec32ee17bdb8d1d2f67a194a9c7"
}, { }, {
"version" : "9.4.1", "version" : "9.4.1",
"buildTime" : "20260319084628+0000", "buildTime" : "20260319084628+0000",

File diff suppressed because one or more lines are too long

View File

@@ -1,20 +1,20 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<typeInfoHistroy> <typeInfoHistroy>
<typeInfo handle="=xxxthegame/src\/main\/java=/gradle_scope=/main=/=/gradle_used_by_scope=/main,test=/&lt;de.oaa.xxx.aufgaben{DefaultFiller.java[DefaultFiller" modifiers="1" timestamp="1772437686926"/> <typeInfo handle="=xxxthegame/src\/main\/java=/gradle_scope=/main=/=/gradle_used_by_scope=/main,test=/&lt;de.oaa.xxx.user{UserRepository.java[UserRepository" modifiers="513" timestamp="1774814915722"/>
<typeInfo handle="=xxxthegame/src\/main\/java=/gradle_scope=/main=/=/gradle_used_by_scope=/main,test=/&lt;de.oaa.xxx.aufgaben.controller{FillerController.java[FillerController" modifiers="1" timestamp="1772385528555"/> <typeInfo handle="=xxxthegame/src\/main\/java=/gradle_scope=/main=/=/gradle_used_by_scope=/main,test=/&lt;de.oaa.xxx.games.chastity.ttlock{TTLockUserConfigEntity.java[TTLockUserConfigEntity" modifiers="1" timestamp="1774814915717"/>
<typeInfo handle="=xxxthegame/src\/main\/java=/gradle_scope=/main=/=/gradle_used_by_scope=/main,test=/&lt;de.oaa.xxx.user{UserRepository.java[UserRepository" modifiers="513" timestamp="1774016609131"/> <typeInfo handle="=xxxthegame/src\/main\/java=/gradle_scope=/main=/=/gradle_used_by_scope=/main,test=/&lt;de.oaa.xxx.games.chastity.keyholder{KeyholderNotificationEntity.java[KeyholderNotificationEntity" modifiers="1" timestamp="1774814915717"/>
<typeInfo handle="=xxxthegame/src\/main\/java=/gradle_scope=/main=/=/gradle_used_by_scope=/main,test=/&lt;de.oaa.xxx.games.chastity.ttlock{TTLockUserConfigEntity.java[TTLockUserConfigEntity" modifiers="1" timestamp="1774425822887"/> <typeInfo handle="=xxxthegame/src\/main\/java=/gradle_scope=/main=/=/gradle_used_by_scope=/main,test=/&lt;de.oaa.xxx.games.chastity.cardlock{CardLockEntity.java[CardLockEntity" modifiers="1" timestamp="1774814915718"/>
<typeInfo handle="=xxxthegame/src\/main\/java=/gradle_scope=/main=/=/gradle_used_by_scope=/main,test=/&lt;de.oaa.xxx.games.chastity.keyholder{KeyholderNotificationEntity.java[KeyholderNotificationEntity" modifiers="1" timestamp="1774386563354"/> <typeInfo handle="=xxxthegame/src\/main\/java=/gradle_scope=/main=/=/gradle_used_by_scope=/main,test=/&lt;de.oaa.xxx.user{UserController.java[UserController" modifiers="1" timestamp="1774814915722"/>
<typeInfo handle="=xxxthegame/src\/main\/java=/gradle_scope=/main=/=/gradle_used_by_scope=/main,test=/&lt;de.oaa.xxx.games.chastity.cardlock{CardLockEntity.java[CardLockEntity" modifiers="1" timestamp="1774171624571"/> <typeInfo handle="=xxxthegame/src\/main\/java=/gradle_scope=/main=/=/gradle_used_by_scope=/main,test=/&lt;de.oaa.xxx.games.chastity.ttlock{TTLockService.java[TTLockService" modifiers="1" timestamp="1774814915716"/>
<typeInfo handle="=xxxthegame/src\/main\/java=/gradle_scope=/main=/=/gradle_used_by_scope=/main,test=/&lt;de.oaa.xxx.user{UserController.java[UserController" modifiers="1" timestamp="1774551025161"/> <typeInfo handle="=xxxthegame/src\/main\/java=/gradle_scope=/main=/=/gradle_used_by_scope=/main,test=/&lt;de.oaa.xxx.games.chastity.timelock{TimeLockController.java[TimeLockController" modifiers="1" timestamp="1774900567883"/>
<typeInfo handle="=xxxthegame/src\/main\/java=/gradle_scope=/main=/=/gradle_used_by_scope=/main,test=/&lt;de.oaa.xxx.games.chastity.ttlock{TTLockService.java[TTLockService" modifiers="1" timestamp="1774375173709"/> <typeInfo handle="=xxxthegame/src\/main\/java=/gradle_scope=/main=/=/gradle_used_by_scope=/main,test=/&lt;de.oaa.xxx.games.chastity.common{BaseLockService.java[BaseLockService" modifiers="1025" timestamp="1774814915716"/>
<typeInfo handle="=xxxthegame/src\/main\/java=/gradle_scope=/main=/=/gradle_used_by_scope=/main,test=/&lt;de.oaa.xxx.games.chastity.timelock{TimeLockController.java[TimeLockController" modifiers="1" timestamp="1774558039992"/> <typeInfo handle="=xxxthegame/src\/main\/java=/gradle_scope=/main=/=/gradle_used_by_scope=/main,test=/&lt;de.oaa.xxx.games.chastity.ttlock{TTLockCallback.java[TTLockCallback" modifiers="1" timestamp="1774814915716"/>
<typeInfo handle="=xxxthegame/src\/main\/java=/gradle_scope=/main=/=/gradle_used_by_scope=/main,test=/&lt;de.oaa.xxx.games.chastity.common{BaseLockService.java[BaseLockService" modifiers="1025" timestamp="1774551269600"/> <typeInfo handle="=xxxthegame/src\/main\/java=/gradle_scope=/main=/=/gradle_used_by_scope=/main,test=/&lt;de.oaa.xxx.games.chastity.lockcontroll{TTLockControl.java[TTLockControl" modifiers="1" timestamp="1774814915717"/>
<typeInfo handle="=xxxthegame/src\/main\/java=/gradle_scope=/main=/=/gradle_used_by_scope=/main,test=/&lt;de.oaa.xxx.games.chastity.ttlock{TTLockCallback.java[TTLockCallback" modifiers="1" timestamp="1774387007874"/> <typeInfo handle="=xxxthegame/src\/main\/java=/gradle_scope=/main=/=/gradle_used_by_scope=/main,test=/&lt;de.oaa.xxx.games.chastity.cardlock{CardLockController.java[CardLockController" modifiers="1" timestamp="1774903113531"/>
<typeInfo handle="=xxxthegame/src\/main\/java=/gradle_scope=/main=/=/gradle_used_by_scope=/main,test=/&lt;de.oaa.xxx.games.chastity.lockcontroll{TTLockControl.java[TTLockControl" modifiers="1" timestamp="1774383035013"/> <typeInfo handle="=xxxthegame/src\/main\/java=/gradle_scope=/main=/=/gradle_used_by_scope=/main,test=/&lt;de.oaa.xxx.games.chastity.common{BaseLockEntity.java[BaseLockEntity" modifiers="1" timestamp="1774814915716"/>
<typeInfo handle="=xxxthegame/src\/main\/java=/gradle_scope=/main=/=/gradle_used_by_scope=/main,test=/&lt;de.oaa.xxx.games.chastity.cardlock{CardLockController.java[CardLockController" modifiers="1" timestamp="1774550581083"/> <typeInfo handle="=xxxthegame/src\/main\/java=/gradle_scope=/main=/=/gradle_used_by_scope=/main,test=/&lt;de.oaa.xxx.games.chastity.cardlock{CardLockService.java[CardLockService" modifiers="1" timestamp="1774814915718"/>
<typeInfo handle="=xxxthegame/src\/main\/java=/gradle_scope=/main=/=/gradle_used_by_scope=/main,test=/&lt;de.oaa.xxx.games.chastity.common{BaseLockEntity.java[BaseLockEntity" modifiers="1" timestamp="1774476260239"/> <typeInfo handle="=xxxthegame/src\/main\/java=/gradle_scope=/main=/=/gradle_used_by_scope=/main,test=/&lt;de.oaa.xxx.games.chastity.timelock{TimeLockService.java[TimeLockService" modifiers="1" timestamp="1774814915716"/>
<typeInfo handle="=xxxthegame/src\/main\/java=/gradle_scope=/main=/=/gradle_used_by_scope=/main,test=/&lt;de.oaa.xxx.games.chastity.cardlock{CardLockService.java[CardLockService" modifiers="1" timestamp="1774551407143"/> <typeInfo handle="=xxxthegame/src\/main\/java=/gradle_scope=/main=/=/gradle_used_by_scope=/main,test=/&lt;de.oaa.xxx.games.common.aufgaben{Aufgabe.java[Aufgabe" modifiers="1" timestamp="1774814915721"/>
<typeInfo handle="=xxxthegame/src\/main\/java=/gradle_scope=/main=/=/gradle_used_by_scope=/main,test=/&lt;de.oaa.xxx.games.chastity.timelock{TimeLockService.java[TimeLockService" modifiers="1" timestamp="1774551447849"/> <typeInfo handle="=xxxthegame/src\/main\/java=/gradle_scope=/main=/=/gradle_used_by_scope=/main,test=/&lt;de.oaa.xxx.games.common.aufgaben{DefaultFiller.java[DefaultFiller" modifiers="1" timestamp="1774814915721"/>
<typeInfo handle="=xxxthegame/src\/main\/java=/gradle_scope=/main=/=/gradle_used_by_scope=/main,test=/&lt;de.oaa.xxx.aufgaben.controller{AboController.java[AboController" modifiers="1" timestamp="1773400404000"/> <typeInfo handle="=xxxthegame/src\/main\/java=/gradle_scope=/main=/=/gradle_used_by_scope=/main,test=/&lt;de.oaa.xxx.mail{MailService.java[MailService" modifiers="1" timestamp="1774814915713"/>
</typeInfoHistroy> </typeInfoHistroy>

View File

@@ -32,4 +32,7 @@
<fullyQualifiedTypeName name="de.oaa.xxx.games.chastity.cardlock.CumCard"/> <fullyQualifiedTypeName name="de.oaa.xxx.games.chastity.cardlock.CumCard"/>
<fullyQualifiedTypeName name="de.oaa.xxx.games.chastity.cardlock.CumInCageCard"/> <fullyQualifiedTypeName name="de.oaa.xxx.games.chastity.cardlock.CumInCageCard"/>
<fullyQualifiedTypeName name="de.oaa.xxx.games.chastity.unlock.TempOpeningReason"/> <fullyQualifiedTypeName name="de.oaa.xxx.games.chastity.unlock.TempOpeningReason"/>
<fullyQualifiedTypeName name="de.oaa.xxx.games.vanilla.VanillaMitspieler"/>
<fullyQualifiedTypeName name="de.oaa.xxx.games.common.aufgaben.CommonMitspieler"/>
<fullyQualifiedTypeName name="java.util.Comparator"/>
</qualifiedTypeNameHistroy> </qualifiedTypeNameHistroy>

View File

@@ -65,6 +65,7 @@
<item key="updateTextualMatches" value="false"/> <item key="updateTextualMatches" value="false"/>
<item key="updateQualifiedNames" value="false"/> <item key="updateQualifiedNames" value="false"/>
<item key="patterns" value="*"/> <item key="patterns" value="*"/>
<item key="renameSubpackages" value="false"/>
</section> </section>
<section name="org.eclipse.jdt.internal.ui.typehierarchy.QuickHierarchy"> <section name="org.eclipse.jdt.internal.ui.typehierarchy.QuickHierarchy">
<item key="org.eclipse.jdt.internal.ui.typehierarchy.HierarchyInformationControlDIALOG_WIDTH" value="400"/> <item key="org.eclipse.jdt.internal.ui.typehierarchy.HierarchyInformationControlDIALOG_WIDTH" value="400"/>
@@ -76,4 +77,11 @@
<item key="width" value="600"/> <item key="width" value="600"/>
<item key="height" value="400"/> <item key="height" value="400"/>
</section> </section>
<section name="NewInterfaceCreationWizard.dialogBounds">
<item key="DIALOG_X_ORIGIN" value="974"/>
<item key="DIALOG_Y_ORIGIN" value="312"/>
<item key="DIALOG_WIDTH" value="613"/>
<item key="DIALOG_HEIGHT" value="559"/>
<item key="DIALOG_FONT_NAME" value="1|Ubuntu|10.0|0|GTK|1|"/>
</section>
</section> </section>

View File

@@ -14,3 +14,8 @@
2026-03-26 11:31:40,355 [Worker-8: 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-26 11:31:40,355 [Worker-8: 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-26 16:50:11,098 [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-26 16:50:11,098 [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-27 07:46:24,300 [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-27 07:46:24,300 [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-29 16:28:13,219 [Worker-2: 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-30 07:33:05,316 [Worker-5: 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-31 08:24:38,073 [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-31 11:33:17,509 [Worker-8: 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-31 20:02:56,538 [Worker-2: Loading available Gradle versions] INFO o.e.b.c.i.u.g.PublishedGradleVersions - Gradle version information cache is up-to-date. Trying to read.

View File

@@ -1,3 +1,3 @@
#Fri Mar 27 07:46:21 CET 2026 #Tue Mar 31 20:02:52 CEST 2026
org.eclipse.core.runtime=2 org.eclipse.core.runtime=2
org.eclipse.platform=4.39.0.v20260226-0420 org.eclipse.platform=4.39.0.v20260226-0420

Binary file not shown.

After

Width:  |  Height:  |  Size: 302 KiB

BIN
bilder/dunno.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
bilder/negative.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
bilder/neutral.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
bilder/positiv.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

BIN
bilder/verynegative.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
bilder/verypositiv.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

21
xxxthegame/deploy.sh Executable file
View File

@@ -0,0 +1,21 @@
#!/bin/bash
# Konfiguration
REMOTE_CONTEXT="proxmox-remote"
IMAGE_NAME="xxx-sphere"
TAG="latest"
echo "--- 1. Gradle Build: Erstelle Docker Image lokal ---"
# Dieser Befehl baut die Jar UND das Docker Image direkt in deinem lokalen Docker
./gradlew bootBuildImage --imageName=$IMAGE_NAME:$TAG
echo "--- 2. Transfer: Image zum Proxmox-Server schieben ---"
# Wir 'pipen' das Image direkt über SSH auf den Zielserver
docker save $IMAGE_NAME:$TAG | docker --context $REMOTE_CONTEXT load
echo "--- 3. Remote Deployment: Starten auf Proxmox ---"
# Wir führen Docker Compose direkt im Remote-Kontext aus
# --force-recreate stellt sicher, dass die App mit dem neuen Image neu startet
docker --context $REMOTE_CONTEXT compose up -d --force-recreate
echo "--- Fertig! Die App läuft auf dem Proxmox-Server ---"

View File

@@ -0,0 +1,32 @@
services:
db:
image: mysql:8.0
container_name: mysql-db
restart: always
environment:
MYSQL_DATABASE: xxx_sphere
MYSQL_ROOT_PASSWORD: xxxsphere123!
ports:
- "3306:3306" # <--- Jetzt steht es korrekt alleine!
volumes:
# Format: [Pfad auf dem Proxmox-Host]:[Pfad im Container]
- /mnt/pve_nas/.mysql_data:/var/lib/mysql
app:
image: xxx-sphere:latest
container_name: spring-boot-app
depends_on:
- db
ports:
- "8080:8080"
environment:
# Wir biegen localhost auf den Service-Namen 'db' um
- SPRING_DATASOURCE_URL=jdbc:mysql://db:3306/xxx_sphere?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC
# Hier injizieren wir die Werte für deine Platzhalter
- DB_USER=root
- DB_PASSWORD=xxxsphere123!
# Wartet kurz, bis die DB wirklich bereit ist (optional, aber empfohlen)
restart: on-failure
volumes:
mysql_data:

View File

@@ -46,6 +46,7 @@ import de.oaa.xxx.subscription.UserSubscriptionEntity;
import de.oaa.xxx.subscription.UserSubscriptionRepository; import de.oaa.xxx.subscription.UserSubscriptionRepository;
import de.oaa.xxx.user.UserEntity; import de.oaa.xxx.user.UserEntity;
import de.oaa.xxx.user.UserRepository; import de.oaa.xxx.user.UserRepository;
import de.oaa.xxx.user.UserService;
@RestController @RestController
@RequestMapping("/admin") @RequestMapping("/admin")
@@ -54,6 +55,7 @@ public class AdminController {
private final AdminRepository adminRepository; private final AdminRepository adminRepository;
private final UserRepository userRepository; private final UserRepository userRepository;
private final UserService userService;
private final MeldungRepository meldungRepository; private final MeldungRepository meldungRepository;
private final FeedbackRepository feedbackRepository; private final FeedbackRepository feedbackRepository;
private final SupportUserService supportUserService; private final SupportUserService supportUserService;
@@ -68,6 +70,7 @@ public class AdminController {
private final UserSubscriptionRepository userSubscriptionRepository; private final UserSubscriptionRepository userSubscriptionRepository;
public AdminController(AdminRepository adminRepository, UserRepository userRepository, public AdminController(AdminRepository adminRepository, UserRepository userRepository,
UserService userService,
MeldungRepository meldungRepository, MeldungRepository meldungRepository,
FeedbackRepository feedbackRepository, FeedbackRepository feedbackRepository,
SupportUserService supportUserService, SupportUserService supportUserService,
@@ -82,6 +85,7 @@ public class AdminController {
UserSubscriptionRepository userSubscriptionRepository) { UserSubscriptionRepository userSubscriptionRepository) {
this.adminRepository = adminRepository; this.adminRepository = adminRepository;
this.userRepository = userRepository; this.userRepository = userRepository;
this.userService = userService;
this.meldungRepository = meldungRepository; this.meldungRepository = meldungRepository;
this.feedbackRepository = feedbackRepository; this.feedbackRepository = feedbackRepository;
this.supportUserService = supportUserService; this.supportUserService = supportUserService;
@@ -129,7 +133,7 @@ public class AdminController {
// ── Hilfsmethoden ──────────────────────────────────────────────────────── // ── Hilfsmethoden ────────────────────────────────────────────────────────
private AdminEntity requireAdmin(Principal principal) { private AdminEntity requireAdmin(Principal principal) {
var user = userRepository.findByEmail(principal.getName()).orElseThrow(); var user = userService.requireUser(principal);
return adminRepository.findByUserId(user.getUserId()) return adminRepository.findByUserId(user.getUserId())
.orElseThrow(() -> new org.springframework.web.server.ResponseStatusException( .orElseThrow(() -> new org.springframework.web.server.ResponseStatusException(
org.springframework.http.HttpStatus.FORBIDDEN, "Kein Admin")); org.springframework.http.HttpStatus.FORBIDDEN, "Kein Admin"));
@@ -160,8 +164,7 @@ public class AdminController {
@GetMapping("/me") @GetMapping("/me")
public ResponseEntity<AdminDto> me(Principal principal) { public ResponseEntity<AdminDto> me(Principal principal) {
var user = userRepository.findByEmail(principal.getName()).orElse(null); var user = userService.requireUser(principal);
if (user == null) return ResponseEntity.status(403).build();
return adminRepository.findByUserId(user.getUserId()) return adminRepository.findByUserId(user.getUserId())
.map(a -> ResponseEntity.ok(toDto(a))) .map(a -> ResponseEntity.ok(toDto(a)))
.orElse(ResponseEntity.status(403).build()); .orElse(ResponseEntity.status(403).build());
@@ -185,7 +188,7 @@ public class AdminController {
@RequestBody StatusRequest body, @RequestBody StatusRequest body,
Principal principal) { Principal principal) {
requireAdmin(principal); requireAdmin(principal);
var user = userRepository.findByEmail(principal.getName()).orElseThrow(); var user = userService.requireUser(principal);
MeldungEntity meldung = meldungRepository.findById(id) MeldungEntity meldung = meldungRepository.findById(id)
.orElseThrow(() -> new org.springframework.web.server.ResponseStatusException( .orElseThrow(() -> new org.springframework.web.server.ResponseStatusException(
org.springframework.http.HttpStatus.NOT_FOUND)); org.springframework.http.HttpStatus.NOT_FOUND));
@@ -251,6 +254,40 @@ public class AdminController {
return ResponseEntity.noContent().build(); return ResponseEntity.noContent().build();
} }
// ── Item verschieben ─────────────────────────────────────────────────────
@PutMapping("/aufgabengruppen/items/{kind}/{itemId}/move")
public ResponseEntity<Void> moveItem(
@PathVariable("kind") String kind,
@PathVariable("itemId") UUID itemId,
@RequestParam("targetGruppeId") UUID targetGruppeId,
Principal principal) {
requireAdmin(principal);
AufgabenGruppeEntity targetGruppe = aufgabenGruppeRepository.findById(targetGruppeId)
.orElseThrow(() -> new org.springframework.web.server.ResponseStatusException(
org.springframework.http.HttpStatus.NOT_FOUND, "Zielgruppe nicht gefunden"));
switch (kind) {
case "aufgabe" -> aufgabeRepository.findById(itemId).ifPresent(e -> {
e.setAufgabenGruppe(targetGruppe);
aufgabeRepository.save(e);
});
case "strafe" -> strafeRepository.findById(itemId).ifPresent(e -> {
e.setAufgabenGruppe(targetGruppe);
strafeRepository.save(e);
});
case "zeitstrafe" -> sperreRepository.findById(itemId).ifPresent(e -> {
e.setAufgabenGruppe(targetGruppe);
sperreRepository.save(e);
});
case "finisher" -> finisherRepository.findById(itemId).ifPresent(e -> {
e.setAufgabenGruppe(targetGruppe);
finisherRepository.save(e);
});
default -> { return ResponseEntity.badRequest().build(); }
}
return ResponseEntity.noContent().build();
}
// ── Toys ───────────────────────────────────────────────────────────────── // ── Toys ─────────────────────────────────────────────────────────────────
@GetMapping("/toys") @GetMapping("/toys")
@@ -357,7 +394,7 @@ public class AdminController {
@DeleteMapping("/admins/{id}") @DeleteMapping("/admins/{id}")
public ResponseEntity<Void> deleteAdmin(@PathVariable("id") UUID id, Principal principal) { public ResponseEntity<Void> deleteAdmin(@PathVariable("id") UUID id, Principal principal) {
var requestingUser = userRepository.findByEmail(principal.getName()).orElseThrow(); var requestingUser = userService.requireUser(principal);
requireSuperAdmin(principal); requireSuperAdmin(principal);
AdminEntity entity = adminRepository.findById(id) AdminEntity entity = adminRepository.findById(id)
.orElseThrow(() -> new org.springframework.web.server.ResponseStatusException( .orElseThrow(() -> new org.springframework.web.server.ResponseStatusException(

View File

@@ -37,47 +37,46 @@ public class SecurityConfig {
.requestMatchers("/error").permitAll() .requestMatchers("/error").permitAll()
.requestMatchers("/api").permitAll() .requestMatchers("/api").permitAll()
.requestMatchers("/userhome.html").authenticated() .requestMatchers("/userhome.html").authenticated()
.requestMatchers("/toys.html").authenticated() .requestMatchers("/games/chastity/toys.html").authenticated()
.requestMatchers("/aufgaben.html").authenticated() .requestMatchers("/games/bdsm/aufgaben.html").authenticated()
.requestMatchers("/entdecken.html").authenticated() .requestMatchers("/games/chastity/entdecken.html").authenticated()
.requestMatchers("/profile.html").authenticated() .requestMatchers("/konto/profile.html").authenticated()
.requestMatchers("/infovanilla.html").authenticated() .requestMatchers("/games/vanilla/infovanilla.html").authenticated()
.requestMatchers("/infobdsm.html").authenticated() .requestMatchers("/games/bdsm/infobdsm.html").authenticated()
.requestMatchers("/infochastity.html").authenticated() .requestMatchers("/games/chastity/infochastity.html").authenticated()
.requestMatchers("/sessionvanilla.html").authenticated() .requestMatchers("/games/vanilla/sessionvanilla.html").authenticated()
.requestMatchers("/sessionbdsm.html").authenticated() .requestMatchers("/sessionbdsm.html").authenticated()
.requestMatchers("/sessionchastity.html").authenticated() .requestMatchers("/games/chastity/sessionchastity.html").authenticated()
.requestMatchers("/neulock.html").authenticated() .requestMatchers("/games/chastity/neulock.html").authenticated()
.requestMatchers("/activelock.html").authenticated() .requestMatchers("/games/chastity/activelock.html").authenticated()
.requestMatchers("/sessionbdsmtasks.html").authenticated()
.requestMatchers("/sessionbdsmtoys.html").authenticated() .requestMatchers("/sessionbdsmtoys.html").authenticated()
.requestMatchers("/sessionbdsmingame.html").authenticated() .requestMatchers("/sessionbdsmingame.html").authenticated()
.requestMatchers("/neubdsm.html").authenticated() .requestMatchers("/games/bdsm/neubdsm.html").authenticated()
.requestMatchers("/bdsmingame.html").authenticated() .requestMatchers("/games/bdsm/bdsmingame.html").authenticated()
.requestMatchers("/bdsmwarten.html").authenticated() .requestMatchers("/community/personen-suchen.html").authenticated()
.requestMatchers("/personen-suchen.html").authenticated() .requestMatchers("/community/freunde.html").authenticated()
.requestMatchers("/freunde.html").authenticated() .requestMatchers("/community/nachrichten.html").authenticated()
.requestMatchers("/nachrichten.html").authenticated() .requestMatchers("/community/benutzer.html").authenticated()
.requestMatchers("/benutzer.html").authenticated() .requestMatchers("/community/gruppen.html").authenticated()
.requestMatchers("/gruppen.html").authenticated() .requestMatchers("/community/gruppe.html").authenticated()
.requestMatchers("/gruppe.html").authenticated() .requestMatchers("/community/feed.html").authenticated()
.requestMatchers("/feed.html").authenticated() .requestMatchers("/admin/admin.html").authenticated()
.requestMatchers("/admin.html").authenticated() .requestMatchers("/games/chastity/communityvotes.html").authenticated()
.requestMatchers("/communityvotes.html").authenticated() .requestMatchers("/games/chastity/keyholder.html").authenticated()
.requestMatchers("/keyholder.html").authenticated() .requestMatchers("/games/chastity/keyholder-finden.html").authenticated()
.requestMatchers("/keyholder-finden.html").authenticated() .requestMatchers("/games/chastity/meine-locks.html").authenticated()
.requestMatchers("/meine-locks.html").authenticated() .requestMatchers("/games/chastity/entdecken-vorlagen.html").authenticated()
.requestMatchers("/entdecken-vorlagen.html").authenticated() .requestMatchers("/games/chastity/unlock-history.html").authenticated()
.requestMatchers("/unlock-history.html").authenticated() .requestMatchers("/games/common/einladungen.html").authenticated()
.requestMatchers("/einladungen.html").authenticated() .requestMatchers("/games/chastity/joinlock.html").authenticated()
.requestMatchers("/joinlock.html").authenticated() .requestMatchers("/community/benachrichtigungen.html").authenticated()
.requestMatchers("/benachrichtigungen.html").authenticated() .requestMatchers("/community/abonnements.html").authenticated()
.requestMatchers("/abonnements.html").authenticated()
.requestMatchers("/gruppen/**").authenticated() .requestMatchers("/gruppen/**").authenticated()
.requestMatchers("/feed/**").authenticated() .requestMatchers("/feed/**").authenticated()
.requestMatchers("/notifications/**").authenticated() .requestMatchers("/notifications/**").authenticated()
.requestMatchers("/events/**").authenticated() .requestMatchers("/events/**").authenticated()
.requestMatchers("/*.html").permitAll() .requestMatchers("/*.html").permitAll()
.requestMatchers("/**/*.html").permitAll()
.requestMatchers("/help/*.html").permitAll() .requestMatchers("/help/*.html").permitAll()
.requestMatchers("/css/**").permitAll() .requestMatchers("/css/**").permitAll()
.requestMatchers("/js/**").permitAll() .requestMatchers("/js/**").permitAll()

View File

@@ -17,12 +17,14 @@ import org.springframework.web.bind.annotation.*;
import java.io.IOException; import java.io.IOException;
import java.security.Principal; import java.security.Principal;
import java.util.UUID; import java.util.UUID;
import java.util.regex.Pattern;
@RestController @RestController
@RequestMapping("/email-change") @RequestMapping("/email-change")
public class EmailChangeController { public class EmailChangeController {
private static final Logger LOGGER = LoggerFactory.getLogger(EmailChangeController.class); private static final Logger LOGGER = LoggerFactory.getLogger(EmailChangeController.class);
private static final Pattern EMAIL_PATTERN = Pattern.compile("^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$");
@Value("${app.base-url:http://localhost:8080}") @Value("${app.base-url:http://localhost:8080}")
private String baseUrl; private String baseUrl;
@@ -52,6 +54,10 @@ public class EmailChangeController {
String currentEmail = principal.getName(); String currentEmail = principal.getName();
String newEmail = request.newEmail(); String newEmail = request.newEmail();
if (newEmail == null || newEmail.isBlank() || !EMAIL_PATTERN.matcher(newEmail).matches()) {
return ResponseEntity.badRequest().build();
}
if (userRepository.findByEmail(newEmail).isPresent() if (userRepository.findByEmail(newEmail).isPresent()
|| registrationRepository.findByEmail(newEmail).isPresent()) { || registrationRepository.findByEmail(newEmail).isPresent()) {
return ResponseEntity.status(409).build(); return ResponseEntity.status(409).build();

View File

@@ -23,7 +23,6 @@ import org.springframework.web.bind.annotation.RestController;
import de.oaa.xxx.feed.dto.FeedItemDto; import de.oaa.xxx.feed.dto.FeedItemDto;
import de.oaa.xxx.feed.dto.FeedPostRequest; import de.oaa.xxx.feed.dto.FeedPostRequest;
import de.oaa.xxx.feed.entity.FeedPostEntity; import de.oaa.xxx.feed.entity.FeedPostEntity;
import de.oaa.xxx.feed.entity.FeedPostLikeEntity;
import de.oaa.xxx.feed.entity.FeedPostOptionEntity; import de.oaa.xxx.feed.entity.FeedPostOptionEntity;
import de.oaa.xxx.feed.entity.FeedPostVoteEntity; import de.oaa.xxx.feed.entity.FeedPostVoteEntity;
import de.oaa.xxx.feed.repository.FeedPostLikeRepository; import de.oaa.xxx.feed.repository.FeedPostLikeRepository;
@@ -40,11 +39,13 @@ import de.oaa.xxx.gruppe.repository.GruppenbeitragRepository;
import de.oaa.xxx.gruppe.repository.GruppenmitgliedRepository; import de.oaa.xxx.gruppe.repository.GruppenmitgliedRepository;
import de.oaa.xxx.gruppe.repository.UmfrageOptionRepository; import de.oaa.xxx.gruppe.repository.UmfrageOptionRepository;
import de.oaa.xxx.gruppe.repository.UmfrageStimmeRepository; import de.oaa.xxx.gruppe.repository.UmfrageStimmeRepository;
import de.oaa.xxx.social.LikeService;
import de.oaa.xxx.social.entity.FriendshipEntity; import de.oaa.xxx.social.entity.FriendshipEntity;
import de.oaa.xxx.social.repository.FriendshipRepository; import de.oaa.xxx.social.repository.FriendshipRepository;
import de.oaa.xxx.social.repository.KommentarRepository; import de.oaa.xxx.social.repository.KommentarRepository;
import de.oaa.xxx.user.UserEntity; import de.oaa.xxx.user.UserEntity;
import de.oaa.xxx.user.UserRepository; import de.oaa.xxx.user.UserRepository;
import de.oaa.xxx.user.UserService;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -68,6 +69,8 @@ public class FeedController {
private final GruppeRepository gruppeRepository; private final GruppeRepository gruppeRepository;
private final KommentarRepository kommentarRepository; private final KommentarRepository kommentarRepository;
private final UserRepository userRepository; private final UserRepository userRepository;
private final UserService userService;
private final LikeService likeService;
public FeedController(FeedPostRepository feedPostRepository, public FeedController(FeedPostRepository feedPostRepository,
FeedPostLikeRepository feedPostLikeRepository, FeedPostLikeRepository feedPostLikeRepository,
@@ -81,7 +84,9 @@ public class FeedController {
GruppenbeitragLikeRepository gruppenbeitragLikeRepository, GruppenbeitragLikeRepository gruppenbeitragLikeRepository,
GruppeRepository gruppeRepository, GruppeRepository gruppeRepository,
KommentarRepository kommentarRepository, KommentarRepository kommentarRepository,
UserRepository userRepository) { UserRepository userRepository,
UserService userService,
LikeService likeService) {
this.feedPostRepository = feedPostRepository; this.feedPostRepository = feedPostRepository;
this.feedPostLikeRepository = feedPostLikeRepository; this.feedPostLikeRepository = feedPostLikeRepository;
this.feedPostOptionRepository = feedPostOptionRepository; this.feedPostOptionRepository = feedPostOptionRepository;
@@ -95,6 +100,8 @@ public class FeedController {
this.gruppeRepository = gruppeRepository; this.gruppeRepository = gruppeRepository;
this.kommentarRepository = kommentarRepository; this.kommentarRepository = kommentarRepository;
this.userRepository = userRepository; this.userRepository = userRepository;
this.userService = userService;
this.likeService = likeService;
} }
record FeedPage(List<FeedItemDto> posts, boolean hasMore) {} record FeedPage(List<FeedItemDto> posts, boolean hasMore) {}
@@ -253,19 +260,7 @@ public class FeedController {
if (myId == null) return ResponseEntity.status(401).build(); if (myId == null) return ResponseEntity.status(401).build();
if (feedPostRepository.findById(id).isEmpty()) return ResponseEntity.notFound().build(); if (feedPostRepository.findById(id).isEmpty()) return ResponseEntity.notFound().build();
var existing = feedPostLikeRepository.findByPostIdAndUserId(id, myId); likeService.toggleFeedPostLike(id, myId);
if (existing.isPresent()) {
feedPostLikeRepository.delete(existing.get());
LOGGER.debug("User {} hat Like auf Feed-Post {} entfernt", myId, id);
} else {
FeedPostLikeEntity like = new FeedPostLikeEntity();
like.setLikeId(UUID.randomUUID());
like.setPostId(id);
like.setUserId(myId);
like.setLikedAt(LocalDateTime.now());
feedPostLikeRepository.save(like);
LOGGER.debug("User {} hat Feed-Post {} geliked", myId, id);
}
return ResponseEntity.ok().build(); return ResponseEntity.ok().build();
} }
@@ -337,9 +332,7 @@ public class FeedController {
private UUID resolveMyId(Principal principal) { private UUID resolveMyId(Principal principal) {
if (principal == null) return null; if (principal == null) return null;
return userRepository.findByEmail(principal.getName()) return userService.requireUser(principal).getUserId();
.map(UserEntity::getUserId)
.orElse(null);
} }
private FeedItemDto toFeedItemDtoFromPost(FeedPostEntity p, UUID myId) { private FeedItemDto toFeedItemDtoFromPost(FeedPostEntity p, UUID myId) {

View File

@@ -13,7 +13,7 @@ import lombok.Setter;
public class AktiveSperre { public class AktiveSperre {
private UUID aktiveSperreId; private UUID aktiveSperreId;
private Mitspieler mitspieler; private BdsmMitspieler mitspieler;
private Integer minuten; private Integer minuten;
private LocalDateTime startzeit; private LocalDateTime startzeit;
private LocalDateTime endzeit; private LocalDateTime endzeit;

View File

@@ -19,7 +19,7 @@ import de.oaa.xxx.games.bdsm.sperre.SperrenVerlaengernCallback;
public class BdsmGameDurchfuehren { public class BdsmGameDurchfuehren {
private final AufgabenList aufgabenList; private final AufgabenList aufgabenList;
private final List<Mitspieler> mitspieler = new ArrayList<>(); private final List<BdsmMitspieler> mitspieler = new ArrayList<>();
private final List<AktiveSperre> aktiveSperren = new ArrayList<>(); private final List<AktiveSperre> aktiveSperren = new ArrayList<>();
private final Integer wahrscheinlichkeitSperre; private final Integer wahrscheinlichkeitSperre;
@@ -74,8 +74,8 @@ public class BdsmGameDurchfuehren {
} }
// Echtes Fallback: nur wenn wirklich keine Kategorie eine Aufgabe liefert // Echtes Fallback: nur wenn wirklich keine Kategorie eine Aufgabe liefert
Mitspieler aktiv = findeMitspielerMitRolle(RolleEnum.AUFGABE_AKTIV); BdsmMitspieler aktiv = findeMitspielerMitRolle(RolleEnum.AUFGABE_AKTIV);
Mitspieler passiv = findeMitspielerMitRolle(RolleEnum.AUFGABE_PASSIV, aktiv); BdsmMitspieler passiv = findeMitspielerMitRolle(RolleEnum.AUFGABE_PASSIV, aktiv);
String text = "Ups, da ist etwas schief gelaufen. Keine potenzielle Aufgabe gefunden. Entweder seid ihr inzwischen so gut weggesperrt, dass wirklich keine Aufgaben mehr zur Verfügung stehen, oder uns ist ein Fehler unterlaufen. {AKTIV} und {PASSIV} überbrücken die Zeit mit ein wenig Petting."; String text = "Ups, da ist etwas schief gelaufen. Keine potenzielle Aufgabe gefunden. Entweder seid ihr inzwischen so gut weggesperrt, dass wirklich keine Aufgaben mehr zur Verfügung stehen, oder uns ist ein Fehler unterlaufen. {AKTIV} und {PASSIV} überbrücken die Zeit mit ein wenig Petting.";
AufgabeAnzeige anzeige = new AufgabeAnzeige(); AufgabeAnzeige anzeige = new AufgabeAnzeige();
anzeige.setNameAktiverMitspieler(aktiv != null ? aktiv.getName() : ""); anzeige.setNameAktiverMitspieler(aktiv != null ? aktiv.getName() : "");
@@ -124,7 +124,7 @@ public class BdsmGameDurchfuehren {
} }
} }
private void setMitspielerInfo(AufgabeAnzeige anzeige, Mitspieler aktiv) { private void setMitspielerInfo(AufgabeAnzeige anzeige, BdsmMitspieler aktiv) {
if (aktiv != null) { if (aktiv != null) {
anzeige.setMitspielerId(aktiv.getId()); anzeige.setMitspielerId(aktiv.getId());
anzeige.setEigenesGeraet(aktiv.isEigenesGeraet()); anzeige.setEigenesGeraet(aktiv.isEigenesGeraet());
@@ -132,9 +132,9 @@ public class BdsmGameDurchfuehren {
} }
private AufgabeAnzeige findUltimativeStrafe() { private AufgabeAnzeige findUltimativeStrafe() {
Mitspieler aktiv = findeMitspielerMitRolle(RolleEnum.BESTRAFUNG_AKTIV); BdsmMitspieler aktiv = findeMitspielerMitRolle(RolleEnum.BESTRAFUNG_AKTIV);
if (aktiv != null) { if (aktiv != null) {
Mitspieler passiv = findeMitspielerMitRolle(RolleEnum.BESTRAFUNG_PASSIV, aktiv); BdsmMitspieler passiv = findeMitspielerMitRolle(RolleEnum.BESTRAFUNG_PASSIV, aktiv);
if (passiv != null) { if (passiv != null) {
String text = "{AKTIV}, verschnüre {PASSIV} fachmännisch inkl. KG, Plugs, Knebel, Augenbinde und was dir sonst einfällt. Nutze die Ruhe für was auch immer du möchtest."; String text = "{AKTIV}, verschnüre {PASSIV} fachmännisch inkl. KG, Plugs, Knebel, Augenbinde und was dir sonst einfällt. Nutze die Ruhe für was auch immer du möchtest.";
AufgabeAnzeige anzeige = new AufgabeAnzeige(); AufgabeAnzeige anzeige = new AufgabeAnzeige();
@@ -151,8 +151,8 @@ public class BdsmGameDurchfuehren {
private AufgabeAnzeige findSperreVerlaengern() { private AufgabeAnzeige findSperreVerlaengern() {
if (!aktiveSperren.isEmpty()) { if (!aktiveSperren.isEmpty()) {
AktiveSperre sperre = aktiveSperren.get(new Random().nextInt(aktiveSperren.size())); AktiveSperre sperre = aktiveSperren.get(new Random().nextInt(aktiveSperren.size()));
Mitspieler passiv = sperre.getMitspieler(); BdsmMitspieler passiv = sperre.getMitspieler();
Mitspieler aktiv = findeMitspielerMitRolle(RolleEnum.BESTRAFUNG_AKTIV, passiv); BdsmMitspieler aktiv = findeMitspielerMitRolle(RolleEnum.BESTRAFUNG_AKTIV, passiv);
if (aktiv != null) { if (aktiv != null) {
String text = "{AKTIV}, du entscheidest. Sollen alle bestehenden Zeitstrafen von {PASSIV} verlängert werden...?"; String text = "{AKTIV}, du entscheidest. Sollen alle bestehenden Zeitstrafen von {PASSIV} verlängert werden...?";
AufgabeAnzeige anzeige = new AufgabeAnzeige(); AufgabeAnzeige anzeige = new AufgabeAnzeige();
@@ -170,9 +170,9 @@ public class BdsmGameDurchfuehren {
} }
private AufgabeAnzeige findeAufgabe() { private AufgabeAnzeige findeAufgabe() {
Mitspieler aktiv = findeMitspielerMitRolle(RolleEnum.AUFGABE_AKTIV); BdsmMitspieler aktiv = findeMitspielerMitRolle(RolleEnum.AUFGABE_AKTIV);
if (aktiv != null) { if (aktiv != null) {
Mitspieler passiv = findeMitspielerMitRolle(RolleEnum.AUFGABE_PASSIV, aktiv); BdsmMitspieler passiv = findeMitspielerMitRolle(RolleEnum.AUFGABE_PASSIV, aktiv);
if (passiv != null) { if (passiv != null) {
List<Aufgabe> list = aufgabenList.getAufgaben().stream() List<Aufgabe> list = aufgabenList.getAufgaben().stream()
.filter(aufgabe -> aufgabe.isAufgabePassend(level, aktiv, passiv)) .filter(aufgabe -> aufgabe.isAufgabePassend(level, aktiv, passiv))
@@ -198,9 +198,9 @@ public class BdsmGameDurchfuehren {
} }
private AufgabeAnzeige findeStrafe() { private AufgabeAnzeige findeStrafe() {
Mitspieler aktiv = findeMitspielerMitRolle(RolleEnum.BESTRAFUNG_AKTIV); BdsmMitspieler aktiv = findeMitspielerMitRolle(RolleEnum.BESTRAFUNG_AKTIV);
if (aktiv != null) { if (aktiv != null) {
Mitspieler passiv = findeMitspielerMitRolle(RolleEnum.BESTRAFUNG_PASSIV, aktiv); BdsmMitspieler passiv = findeMitspielerMitRolle(RolleEnum.BESTRAFUNG_PASSIV, aktiv);
if (passiv != null) { if (passiv != null) {
List<Strafe> list = aufgabenList.getStrafen().stream() List<Strafe> list = aufgabenList.getStrafen().stream()
.filter(strafe -> strafe.isAufgabePassend(level, aktiv, passiv)) .filter(strafe -> strafe.isAufgabePassend(level, aktiv, passiv))
@@ -226,9 +226,9 @@ public class BdsmGameDurchfuehren {
} }
private AufgabeAnzeige findeSperre() { private AufgabeAnzeige findeSperre() {
Mitspieler aktiv = findeMitspielerMitRolle(RolleEnum.BESTRAFUNG_AKTIV); BdsmMitspieler aktiv = findeMitspielerMitRolle(RolleEnum.BESTRAFUNG_AKTIV);
if (aktiv != null) { if (aktiv != null) {
Mitspieler passiv = findeMitspielerMitRolle(RolleEnum.BESTRAFUNG_PASSIV, aktiv); BdsmMitspieler passiv = findeMitspielerMitRolle(RolleEnum.BESTRAFUNG_PASSIV, aktiv);
if (passiv != null) { if (passiv != null) {
List<Sperre> list = aufgabenList.getSperren().stream() List<Sperre> list = aufgabenList.getSperren().stream()
.filter(sperre -> sperre.isAufgabePassend(passiv)) .filter(sperre -> sperre.isAufgabePassend(passiv))
@@ -255,16 +255,16 @@ public class BdsmGameDurchfuehren {
return textMitPlatzhaltern.replace("{AKTIV}", nameAktiv).replace("{PASSIV}", namePassiv); return textMitPlatzhaltern.replace("{AKTIV}", nameAktiv).replace("{PASSIV}", namePassiv);
} }
private Mitspieler findeMitspielerMitRolle(RolleEnum rolle) { private BdsmMitspieler findeMitspielerMitRolle(RolleEnum rolle) {
List<Mitspieler> list = mitspieler.stream() List<BdsmMitspieler> list = mitspieler.stream()
.filter(m -> m.getRollen().contains(rolle)) .filter(m -> m.getRollen().contains(rolle))
.toList(); .toList();
return list.isEmpty() ? null : list.get(new Random().nextInt(list.size())); return list.isEmpty() ? null : list.get(new Random().nextInt(list.size()));
} }
private Mitspieler findeMitspielerMitRolle(RolleEnum rolle, Mitspieler gegenspieler) { private BdsmMitspieler findeMitspielerMitRolle(RolleEnum rolle, BdsmMitspieler gegenspieler) {
if (gegenspieler == null) return findeMitspielerMitRolle(rolle); if (gegenspieler == null) return findeMitspielerMitRolle(rolle);
List<Mitspieler> list = mitspieler.stream() List<BdsmMitspieler> list = mitspieler.stream()
.filter(m -> m != gegenspieler) .filter(m -> m != gegenspieler)
.filter(m -> m.isPassenderSpielpartner(gegenspieler)) .filter(m -> m.isPassenderSpielpartner(gegenspieler))
.filter(m -> m.getRollen().contains(rolle)) .filter(m -> m.getRollen().contains(rolle))

View File

@@ -162,7 +162,7 @@ public class BdsmGameService {
userRepository.findById(keyholderUserId).ifPresent(keyholder -> userRepository.findById(keyholderUserId).ifPresent(keyholder ->
systemMessageService.send(keyholderUserId, lockeeUserId, systemMessageService.send(keyholderUserId, lockeeUserId,
keyholder.getName() + " hat nach dem BDSM Game ein Chastity Lock auf dich gesetzt.", keyholder.getName() + " hat nach dem BDSM Game ein Chastity Lock auf dich gesetzt.",
"/activelock.html", MessageCause.GAME_STATE)); "/games/chastity/activelock.html", MessageCause.GAME_STATE));
} }
// Spielabschluss-Logik (History + XP + Cleanup) // Spielabschluss-Logik (History + XP + Cleanup)

View File

@@ -3,13 +3,14 @@ package de.oaa.xxx.games.bdsm;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import de.oaa.xxx.games.common.aufgaben.CommonMitspieler;
import de.oaa.xxx.games.common.aufgaben.Werkzeug; import de.oaa.xxx.games.common.aufgaben.Werkzeug;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
@Getter @Getter
@Setter @Setter
public class Mitspieler { public class BdsmMitspieler implements CommonMitspieler {
private UUID id; private UUID id;
private UUID userId; private UUID userId;
@@ -31,7 +32,7 @@ public class Mitspieler {
+ ", rollen=" + rollen + ", werkzeuge=" + verfuegbareWerkzeuge + "]"; + ", rollen=" + rollen + ", werkzeuge=" + verfuegbareWerkzeuge + "]";
} }
public boolean isPassenderSpielpartner(Mitspieler other) { public boolean isPassenderSpielpartner(BdsmMitspieler other) {
if (!spieltMit.contains(other.getGeschlecht())) { if (!spieltMit.contains(other.getGeschlecht())) {
return false; return false;
} }

View File

@@ -7,7 +7,7 @@ import de.oaa.xxx.games.common.entity.GruppenAboEntity;
import de.oaa.xxx.games.common.repository.AufgabenGruppeRepository; import de.oaa.xxx.games.common.repository.AufgabenGruppeRepository;
import de.oaa.xxx.games.common.repository.GruppenAboRepository; import de.oaa.xxx.games.common.repository.GruppenAboRepository;
import de.oaa.xxx.user.UserEntity; import de.oaa.xxx.user.UserEntity;
import de.oaa.xxx.user.UserRepository; import de.oaa.xxx.user.UserService;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.DeleteMapping;
@@ -37,14 +37,14 @@ public class AboController {
private final GruppenAboRepository aboRepository; private final GruppenAboRepository aboRepository;
private final AufgabenGruppeRepository gruppeRepository; private final AufgabenGruppeRepository gruppeRepository;
private final UserRepository userRepository; private final UserService userService;
public AboController(GruppenAboRepository aboRepository, public AboController(GruppenAboRepository aboRepository,
AufgabenGruppeRepository gruppeRepository, AufgabenGruppeRepository gruppeRepository,
UserRepository userRepository) { UserService userService) {
this.aboRepository = aboRepository; this.aboRepository = aboRepository;
this.gruppeRepository = gruppeRepository; this.gruppeRepository = gruppeRepository;
this.userRepository = userRepository; this.userService = userService;
} }
// ── Abonnierte Gruppen laden ── // ── Abonnierte Gruppen laden ──
@@ -54,8 +54,7 @@ public class AboController {
@RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "" + DEFAULT_PAGE_SIZE) int size, @RequestParam(defaultValue = "" + DEFAULT_PAGE_SIZE) int size,
Principal principal) { Principal principal) {
UserEntity user = resolveUser(principal); UserEntity user = userService.requireUser(principal);
if (user == null) return ResponseEntity.status(401).build();
List<AufgabenGruppe> dtos = aboRepository.findByUserId(user.getUserId()).stream() List<AufgabenGruppe> dtos = aboRepository.findByUserId(user.getUserId()).stream()
.map(GruppenAboEntity::getAufgabenGruppe) .map(GruppenAboEntity::getAufgabenGruppe)
@@ -75,8 +74,7 @@ public class AboController {
@RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "" + DISCOVER_PAGE_SIZE) int size, @RequestParam(defaultValue = "" + DISCOVER_PAGE_SIZE) int size,
Principal principal) { Principal principal) {
UserEntity user = resolveUser(principal); UserEntity user = userService.requireUser(principal);
if (user == null) return ResponseEntity.status(401).build();
String namePattern = name != null && !name.isBlank() ? "%" + name.trim() + "%" : null; String namePattern = name != null && !name.isBlank() ? "%" + name.trim() + "%" : null;
@@ -94,8 +92,7 @@ public class AboController {
@PostMapping("/{gruppenId}") @PostMapping("/{gruppenId}")
public ResponseEntity<Void> subscribe(@PathVariable UUID gruppenId, Principal principal) { public ResponseEntity<Void> subscribe(@PathVariable UUID gruppenId, Principal principal) {
UserEntity user = resolveUser(principal); UserEntity user = userService.requireUser(principal);
if (user == null) return ResponseEntity.status(401).build();
AufgabenGruppeEntity gruppe = gruppeRepository.findById(gruppenId).orElse(null); AufgabenGruppeEntity gruppe = gruppeRepository.findById(gruppenId).orElse(null);
if (gruppe == null || gruppe.isPrivateGruppe() || user.getUserId().equals(gruppe.getUserId())) { if (gruppe == null || gruppe.isPrivateGruppe() || user.getUserId().equals(gruppe.getUserId())) {
@@ -117,8 +114,7 @@ public class AboController {
@DeleteMapping("/{gruppenId}") @DeleteMapping("/{gruppenId}")
public ResponseEntity<Void> unsubscribe(@PathVariable UUID gruppenId, Principal principal) { public ResponseEntity<Void> unsubscribe(@PathVariable UUID gruppenId, Principal principal) {
UserEntity user = resolveUser(principal); UserEntity user = userService.requireUser(principal);
if (user == null) return ResponseEntity.status(401).build();
AufgabenGruppeEntity gruppe = gruppeRepository.findById(gruppenId).orElse(null); AufgabenGruppeEntity gruppe = gruppeRepository.findById(gruppenId).orElse(null);
if (gruppe == null) return ResponseEntity.noContent().build(); if (gruppe == null) return ResponseEntity.noContent().build();
@@ -149,7 +145,4 @@ public class AboController {
return result; return result;
} }
private UserEntity resolveUser(Principal principal) {
return userRepository.findByEmail(principal.getName()).orElse(null);
}
} }

View File

@@ -9,7 +9,7 @@ import de.oaa.xxx.games.common.repository.AufgabeRepository;
import de.oaa.xxx.games.common.repository.AufgabenGruppeRepository; import de.oaa.xxx.games.common.repository.AufgabenGruppeRepository;
import de.oaa.xxx.games.common.repository.ToyRepository; import de.oaa.xxx.games.common.repository.ToyRepository;
import de.oaa.xxx.subscription.SubscriptionLimitService; import de.oaa.xxx.subscription.SubscriptionLimitService;
import de.oaa.xxx.user.UserRepository; import de.oaa.xxx.user.UserService;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
@@ -39,18 +39,18 @@ public class AufgabeController {
private final AufgabeRepository aufgabeRepository; private final AufgabeRepository aufgabeRepository;
private final AufgabenGruppeRepository gruppeRepository; private final AufgabenGruppeRepository gruppeRepository;
private final ToyRepository toyRepository; private final ToyRepository toyRepository;
private final UserRepository userRepository; private final UserService userService;
private final SubscriptionLimitService limitService; private final SubscriptionLimitService limitService;
public AufgabeController(AufgabeRepository aufgabeRepository, public AufgabeController(AufgabeRepository aufgabeRepository,
AufgabenGruppeRepository gruppeRepository, AufgabenGruppeRepository gruppeRepository,
ToyRepository toyRepository, ToyRepository toyRepository,
UserRepository userRepository, UserService userService,
SubscriptionLimitService limitService) { SubscriptionLimitService limitService) {
this.aufgabeRepository = aufgabeRepository; this.aufgabeRepository = aufgabeRepository;
this.gruppeRepository = gruppeRepository; this.gruppeRepository = gruppeRepository;
this.toyRepository = toyRepository; this.toyRepository = toyRepository;
this.userRepository = userRepository; this.userService = userService;
this.limitService = limitService; this.limitService = limitService;
} }
@@ -70,9 +70,7 @@ public class AufgabeController {
if (gruppeEntity == null) { if (gruppeEntity == null) {
return ResponseEntity.badRequest().build(); return ResponseEntity.badRequest().build();
} }
var ownerOpt = userRepository.findByEmail(principal.getName()); int limit = limitService.maxTasksPerGroup(userService.requireUser(principal).getUserId());
int limit = ownerOpt.map(u -> limitService.maxTasksPerGroup(u.getUserId()))
.orElse(SubscriptionLimitService.STANDARD_MAX_TASKS_PER_GROUP);
if (gruppeEntity.getAufgaben().size() >= limit) { if (gruppeEntity.getAufgaben().size() >= limit) {
return ResponseEntity.status(409).build(); return ResponseEntity.status(409).build();
} }

View File

@@ -10,7 +10,6 @@ import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
@@ -36,7 +35,7 @@ import de.oaa.xxx.games.common.repository.SperreRepository;
import de.oaa.xxx.games.common.repository.StrafeRepository; import de.oaa.xxx.games.common.repository.StrafeRepository;
import de.oaa.xxx.subscription.SubscriptionLimitService; import de.oaa.xxx.subscription.SubscriptionLimitService;
import de.oaa.xxx.user.UserEntity; import de.oaa.xxx.user.UserEntity;
import de.oaa.xxx.user.UserRepository; import de.oaa.xxx.user.UserService;
@RestController @RestController
@RequestMapping("/gruppe") @RequestMapping("/gruppe")
@@ -51,29 +50,29 @@ public class AufgabenGruppeController {
private final StrafeRepository strafeRepository; private final StrafeRepository strafeRepository;
private final SperreRepository sperreRepository; private final SperreRepository sperreRepository;
private final FinisherRepository finisherRepository; private final FinisherRepository finisherRepository;
private final UserRepository userRepository;
private final GruppenAboRepository aboRepository; private final GruppenAboRepository aboRepository;
private final AufgabenGruppeService aufgabenGruppeService; private final AufgabenGruppeService aufgabenGruppeService;
private final SubscriptionLimitService limitService; private final SubscriptionLimitService limitService;
private final UserService userService;
public AufgabenGruppeController(AufgabenGruppeRepository gruppeRepository, public AufgabenGruppeController(AufgabenGruppeRepository gruppeRepository,
AufgabeRepository aufgabeRepository, AufgabeRepository aufgabeRepository,
StrafeRepository strafeRepository, StrafeRepository strafeRepository,
SperreRepository sperreRepository, SperreRepository sperreRepository,
FinisherRepository finisherRepository, FinisherRepository finisherRepository,
UserRepository userRepository,
GruppenAboRepository aboRepository, GruppenAboRepository aboRepository,
AufgabenGruppeService aufgabenGruppeService, AufgabenGruppeService aufgabenGruppeService,
SubscriptionLimitService limitService) { SubscriptionLimitService limitService,
UserService userService) {
this.gruppeRepository = gruppeRepository; this.gruppeRepository = gruppeRepository;
this.aufgabeRepository = aufgabeRepository; this.aufgabeRepository = aufgabeRepository;
this.strafeRepository = strafeRepository; this.strafeRepository = strafeRepository;
this.sperreRepository = sperreRepository; this.sperreRepository = sperreRepository;
this.finisherRepository = finisherRepository; this.finisherRepository = finisherRepository;
this.userRepository = userRepository;
this.aboRepository = aboRepository; this.aboRepository = aboRepository;
this.aufgabenGruppeService = aufgabenGruppeService; this.aufgabenGruppeService = aufgabenGruppeService;
this.limitService = limitService; this.limitService = limitService;
this.userService = userService;
} }
// ── Paginierte Listen ── // ── Paginierte Listen ──
@@ -102,8 +101,8 @@ public class AufgabenGruppeController {
// ── Bestehende Endpunkte ── // ── Bestehende Endpunkte ──
@GetMapping("/all") @GetMapping("/all")
public ResponseEntity<AufgabenGruppeList> getAll(@RequestParam(required = false) String search) { public ResponseEntity<AufgabenGruppeList> getAll(@RequestParam(required = false) String search, Principal principal) {
UUID userId = (UUID) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); UUID userId = userService.requireUser(principal).getUserId();
String searchPattern = search != null ? "%" + search + "%" : null; String searchPattern = search != null ? "%" + search + "%" : null;
AufgabenGruppeList list = new AufgabenGruppeList(); AufgabenGruppeList list = new AufgabenGruppeList();
list.setGruppen(gruppeRepository.listWithUserAndSearch(userId, searchPattern, PageRequest.of(0, 500)) list.setGruppen(gruppeRepository.listWithUserAndSearch(userId, searchPattern, PageRequest.of(0, 500))
@@ -235,7 +234,8 @@ public class AufgabenGruppeController {
// ── Hilfsmethoden ── // ── Hilfsmethoden ──
private UserEntity resolveUser(Principal principal) { private UserEntity resolveUser(Principal principal) {
return userRepository.findByEmail(principal.getName()).orElse(null); if (principal == null) return null;
return userService.requireUser(principal);
} }
private AufgabenGruppePage toGruppePage(Page<AufgabenGruppeEntity> page) { private AufgabenGruppePage toGruppePage(Page<AufgabenGruppeEntity> page) {

View File

@@ -23,9 +23,9 @@ import de.oaa.xxx.games.bdsm.entity.BdsmEinladungEntity;
import de.oaa.xxx.games.bdsm.entity.BdsmEinladungEntity.Status; import de.oaa.xxx.games.bdsm.entity.BdsmEinladungEntity.Status;
import de.oaa.xxx.games.bdsm.repository.BdsmEinladungRepository; import de.oaa.xxx.games.bdsm.repository.BdsmEinladungRepository;
import de.oaa.xxx.social.SystemMessageService; import de.oaa.xxx.social.SystemMessageService;
import de.oaa.xxx.social.entity.MessageCause;
import de.oaa.xxx.social.repository.FriendshipRepository; import de.oaa.xxx.social.repository.FriendshipRepository;
import de.oaa.xxx.user.UserRepository; import de.oaa.xxx.user.UserRepository;
import de.oaa.xxx.user.UserService;
@RestController @RestController
@RequestMapping("/bdsm/einladung") @RequestMapping("/bdsm/einladung")
@@ -36,15 +36,18 @@ public class BdsmEinladungController {
private final UserRepository userRepository; private final UserRepository userRepository;
private final FriendshipRepository friendshipRepository; private final FriendshipRepository friendshipRepository;
private final SystemMessageService systemMessageService; private final SystemMessageService systemMessageService;
private final UserService userService;
public BdsmEinladungController(BdsmEinladungRepository einladungRepository, public BdsmEinladungController(BdsmEinladungRepository einladungRepository,
UserRepository userRepository, UserRepository userRepository,
FriendshipRepository friendshipRepository, FriendshipRepository friendshipRepository,
SystemMessageService systemMessageService) { SystemMessageService systemMessageService,
UserService userService) {
this.einladungRepository = einladungRepository; this.einladungRepository = einladungRepository;
this.userRepository = userRepository; this.userRepository = userRepository;
this.friendshipRepository = friendshipRepository; this.friendshipRepository = friendshipRepository;
this.systemMessageService = systemMessageService; this.systemMessageService = systemMessageService;
this.userService = userService;
} }
record EinladungRequest(UUID setupId, int slotIndex, UUID inviteeId) {} record EinladungRequest(UUID setupId, int slotIndex, UUID inviteeId) {}
@@ -52,8 +55,7 @@ public class BdsmEinladungController {
record SpielerDatenRequest(String spielerDatenJson) {} record SpielerDatenRequest(String spielerDatenJson) {}
private UUID currentUserId(Principal principal) { private UUID currentUserId(Principal principal) {
return userRepository.findByEmail(principal.getName()) return userService.requireUser(principal).getUserId();
.map(u -> u.getUserId()).orElse(null);
} }
@PostMapping @PostMapping
@@ -94,13 +96,7 @@ public class BdsmEinladungController {
entity.setCreatedAt(LocalDateTime.now()); entity.setCreatedAt(LocalDateTime.now());
einladungRepository.save(entity); einladungRepository.save(entity);
String inviterName = userRepository.findById(inviterId).map(u -> u.getName()).orElse("Jemand"); systemMessageService.pushInvitationUpdate(req.inviteeId());
systemMessageService.send(
inviterId, req.inviteeId(),
inviterName + " hat dich zum BDSM Game eingeladen.",
"/einladungen.html",
MessageCause.INVITATION
);
Map<String, Object> result = new LinkedHashMap<>(); Map<String, Object> result = new LinkedHashMap<>();
result.put("einladungId", entity.getEinladungId()); result.put("einladungId", entity.getEinladungId());
@@ -115,10 +111,7 @@ public class BdsmEinladungController {
if (e == null) return ResponseEntity.notFound().build(); if (e == null) return ResponseEntity.notFound().build();
if (!e.getInviterId().equals(userId)) return ResponseEntity.status(403).build(); if (!e.getInviterId().equals(userId)) return ResponseEntity.status(403).build();
e.setStatus(Status.CANCELLED); e.setStatus(Status.CANCELLED);
String inviterName = userRepository.findById(userId).map(u -> u.getName()).orElse("Jemand"); systemMessageService.pushInvitationUpdate(e.getInviteeId());
systemMessageService.send(userId, e.getInviteeId(),
inviterName + " hat die BDSM-Spieleinladung zurückgezogen.",
"/einladungen.html", MessageCause.INVITATION);
return ResponseEntity.accepted().build(); return ResponseEntity.accepted().build();
} }
@@ -141,6 +134,13 @@ public class BdsmEinladungController {
.orElse(ResponseEntity.noContent().build()); .orElse(ResponseEntity.noContent().build());
} }
@GetMapping("/pending/count")
public ResponseEntity<Integer> getPendingCount(Principal principal) {
UUID userId = currentUserId(principal);
if (userId == null) return ResponseEntity.status(401).build();
return ResponseEntity.ok(einladungRepository.findByInviteeIdAndStatus(userId, Status.PENDING).size());
}
@GetMapping("/pending") @GetMapping("/pending")
public ResponseEntity<List<Map<String, Object>>> getPending(Principal principal) { public ResponseEntity<List<Map<String, Object>>> getPending(Principal principal) {
UUID userId = currentUserId(principal); UUID userId = currentUserId(principal);

View File

@@ -15,7 +15,6 @@ import java.util.UUID;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
@@ -36,7 +35,7 @@ import de.oaa.xxx.games.bdsm.BdsmGame;
import de.oaa.xxx.games.bdsm.BdsmGameDurchfuehren; import de.oaa.xxx.games.bdsm.BdsmGameDurchfuehren;
import de.oaa.xxx.games.bdsm.BdsmGameService; import de.oaa.xxx.games.bdsm.BdsmGameService;
import de.oaa.xxx.games.bdsm.GeschlechtEnum; import de.oaa.xxx.games.bdsm.GeschlechtEnum;
import de.oaa.xxx.games.bdsm.Mitspieler; import de.oaa.xxx.games.bdsm.BdsmMitspieler;
import de.oaa.xxx.games.common.aufgaben.AufgabenList; import de.oaa.xxx.games.common.aufgaben.AufgabenList;
import de.oaa.xxx.games.common.aufgaben.Werkzeug; import de.oaa.xxx.games.common.aufgaben.Werkzeug;
import de.oaa.xxx.games.bdsm.entity.AktiveSperreEntity; import de.oaa.xxx.games.bdsm.entity.AktiveSperreEntity;
@@ -55,6 +54,7 @@ import de.oaa.xxx.games.chastity.cardlock.CardlockRepository;
import de.oaa.xxx.social.SystemMessageService; import de.oaa.xxx.social.SystemMessageService;
import de.oaa.xxx.social.entity.MessageCause; import de.oaa.xxx.social.entity.MessageCause;
import de.oaa.xxx.user.UserRepository; import de.oaa.xxx.user.UserRepository;
import de.oaa.xxx.user.UserService;
@RestController @RestController
@RequestMapping("/bdsm") @RequestMapping("/bdsm")
@@ -77,12 +77,13 @@ public class BdsmGameController {
private final SystemMessageService systemMessageService; private final SystemMessageService systemMessageService;
private final CardlockRepository cardlockRepository; private final CardlockRepository cardlockRepository;
private final BdsmGameService bdsmGameService; private final BdsmGameService bdsmGameService;
private final UserService userService;
public BdsmGameController(BdsmGameRepository sessionRepository, MitspielerRepository mitspielerRepository, public BdsmGameController(BdsmGameRepository sessionRepository, MitspielerRepository mitspielerRepository,
AktiveSperreRepository aktiveSperreRepository, UserRepository userRepository, AktiveSperreRepository aktiveSperreRepository, UserRepository userRepository,
BdsmEinladungRepository einladungRepository, ObjectMapper objectMapper, BdsmEinladungRepository einladungRepository, ObjectMapper objectMapper,
SystemMessageService systemMessageService, CardlockRepository cardlockRepository, SystemMessageService systemMessageService, CardlockRepository cardlockRepository,
BdsmGameService bdsmGameService) { BdsmGameService bdsmGameService, UserService userService) {
this.sessionRepository = sessionRepository; this.sessionRepository = sessionRepository;
this.mitspielerRepository = mitspielerRepository; this.mitspielerRepository = mitspielerRepository;
this.aktiveSperreRepository = aktiveSperreRepository; this.aktiveSperreRepository = aktiveSperreRepository;
@@ -92,6 +93,7 @@ public class BdsmGameController {
this.systemMessageService = systemMessageService; this.systemMessageService = systemMessageService;
this.cardlockRepository = cardlockRepository; this.cardlockRepository = cardlockRepository;
this.bdsmGameService = bdsmGameService; this.bdsmGameService = bdsmGameService;
this.userService = userService;
} }
@GetMapping("/{sessionId}") @GetMapping("/{sessionId}")
@@ -109,10 +111,8 @@ public class BdsmGameController {
} }
@PostMapping @PostMapping
public ResponseEntity<Void> create(@RequestBody BdsmGame session) { public ResponseEntity<Void> create(@RequestBody BdsmGame session, Principal principal) {
String email = (String) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); UUID userId = userService.requireUser(principal).getUserId();
UUID userId = userRepository.findByEmail(email).map(u -> u.getUserId()).orElse(null);
if (userId == null) return ResponseEntity.status(401).build();
var existingOpt = sessionRepository.findByUserId(userId); var existingOpt = sessionRepository.findByUserId(userId);
if (existingOpt.isPresent()) { if (existingOpt.isPresent()) {
BdsmGameEntity existing = existingOpt.get(); BdsmGameEntity existing = existingOpt.get();
@@ -175,8 +175,7 @@ public class BdsmGameController {
@DeleteMapping("/{sessionId}/verlassen") @DeleteMapping("/{sessionId}/verlassen")
public ResponseEntity<Void> verlasseSpiel(@PathVariable UUID sessionId, Principal principal) { public ResponseEntity<Void> verlasseSpiel(@PathVariable UUID sessionId, Principal principal) {
UUID userId = userRepository.findByEmail(principal.getName()).map(u -> u.getUserId()).orElse(null); UUID userId = userService.requireUser(principal).getUserId();
if (userId == null) return ResponseEntity.status(401).build();
BdsmGameEntity session = sessionRepository.findById(sessionId).orElse(null); BdsmGameEntity session = sessionRepository.findById(sessionId).orElse(null);
if (session == null) return ResponseEntity.notFound().build(); if (session == null) return ResponseEntity.notFound().build();
@@ -259,7 +258,7 @@ public class BdsmGameController {
} }
@PostMapping("/{sessionId}/mitspieler") @PostMapping("/{sessionId}/mitspieler")
public ResponseEntity<Void> addMitspieler(@RequestBody Mitspieler mitspieler, @PathVariable UUID sessionId) { public ResponseEntity<Void> addMitspieler(@RequestBody BdsmMitspieler mitspieler, @PathVariable UUID sessionId) {
if (mitspieler.getName() == null || mitspieler.getGeschlecht() == null || mitspieler.getRollen() == null if (mitspieler.getName() == null || mitspieler.getGeschlecht() == null || mitspieler.getRollen() == null
|| mitspieler.getRollen().isEmpty() || mitspieler.getSpieltMit() == null || mitspieler.getSpieltMit().isEmpty() || mitspieler.getRollen().isEmpty() || mitspieler.getSpieltMit() == null || mitspieler.getSpieltMit().isEmpty()
|| mitspieler.getVerfuegbareWerkzeuge() == null || mitspieler.getVerfuegbareWerkzeuge().isEmpty()) { || mitspieler.getVerfuegbareWerkzeuge() == null || mitspieler.getVerfuegbareWerkzeuge().isEmpty()) {
@@ -345,8 +344,7 @@ public class BdsmGameController {
@GetMapping("/{sessionId}/mitspieler/me") @GetMapping("/{sessionId}/mitspieler/me")
public ResponseEntity<Map<String, Object>> getMeinMitspieler(@PathVariable UUID sessionId, Principal principal) { public ResponseEntity<Map<String, Object>> getMeinMitspieler(@PathVariable UUID sessionId, Principal principal) {
UUID userId = userRepository.findByEmail(principal.getName()).map(u -> u.getUserId()).orElse(null); UUID userId = userService.requireUser(principal).getUserId();
if (userId == null) return ResponseEntity.status(401).build();
BdsmGameEntity session = sessionRepository.findById(sessionId).orElse(null); BdsmGameEntity session = sessionRepository.findById(sessionId).orElse(null);
if (session == null) return ResponseEntity.notFound().build(); if (session == null) return ResponseEntity.notFound().build();
return session.getMitspieler().stream() return session.getMitspieler().stream()

View File

@@ -2,7 +2,7 @@ package de.oaa.xxx.games.bdsm.controller;
import de.oaa.xxx.games.bdsm.entity.BdsmSetupDraftEntity; import de.oaa.xxx.games.bdsm.entity.BdsmSetupDraftEntity;
import de.oaa.xxx.games.bdsm.repository.BdsmSetupDraftRepository; import de.oaa.xxx.games.bdsm.repository.BdsmSetupDraftRepository;
import de.oaa.xxx.user.UserRepository; import de.oaa.xxx.user.UserService;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
@@ -19,25 +19,20 @@ import java.util.UUID;
public class BdsmSetupDraftController { public class BdsmSetupDraftController {
private final BdsmSetupDraftRepository draftRepository; private final BdsmSetupDraftRepository draftRepository;
private final UserRepository userRepository; private final UserService userService;
public BdsmSetupDraftController(BdsmSetupDraftRepository draftRepository, UserRepository userRepository) { public BdsmSetupDraftController(BdsmSetupDraftRepository draftRepository, UserService userService) {
this.draftRepository = draftRepository; this.draftRepository = draftRepository;
this.userRepository = userRepository; this.userService = userService;
} }
record DraftRequest(String setupId, String settingsJson, String setupJson, String gruppenJson) {} record DraftRequest(String setupId, String settingsJson, String setupJson, String gruppenJson) {}
private UUID currentUserId(Principal principal) {
return userRepository.findByEmail(principal.getName()).map(u -> u.getUserId()).orElse(null);
}
@GetMapping @GetMapping
public ResponseEntity<Map<String, Object>> getDraft( public ResponseEntity<Map<String, Object>> getDraft(
@RequestParam(required = false) String setupId, @RequestParam(required = false) String setupId,
Principal principal) { Principal principal) {
UUID userId = currentUserId(principal); UUID userId = userService.requireUser(principal).getUserId();
if (userId == null) return ResponseEntity.status(401).build();
var lookup = (setupId != null && !setupId.isBlank()) var lookup = (setupId != null && !setupId.isBlank())
? draftRepository.findBySetupId(setupId) ? draftRepository.findBySetupId(setupId)
: draftRepository.findByUserId(userId); : draftRepository.findByUserId(userId);
@@ -55,8 +50,7 @@ public class BdsmSetupDraftController {
@PutMapping @PutMapping
public ResponseEntity<Void> saveDraft(@RequestBody DraftRequest req, Principal principal) { public ResponseEntity<Void> saveDraft(@RequestBody DraftRequest req, Principal principal) {
UUID userId = currentUserId(principal); UUID userId = userService.requireUser(principal).getUserId();
if (userId == null) return ResponseEntity.status(401).build();
BdsmSetupDraftEntity d = draftRepository.findByUserId(userId) BdsmSetupDraftEntity d = draftRepository.findByUserId(userId)
.orElseGet(() -> { BdsmSetupDraftEntity n = new BdsmSetupDraftEntity(); n.setUserId(userId); return n; }); .orElseGet(() -> { BdsmSetupDraftEntity n = new BdsmSetupDraftEntity(); n.setUserId(userId); return n; });
if (req.setupId() != null) d.setSetupId(req.setupId()); if (req.setupId() != null) d.setSetupId(req.setupId());
@@ -70,8 +64,7 @@ public class BdsmSetupDraftController {
@DeleteMapping @DeleteMapping
public ResponseEntity<Void> deleteDraft(Principal principal) { public ResponseEntity<Void> deleteDraft(Principal principal) {
UUID userId = currentUserId(principal); UUID userId = userService.requireUser(principal).getUserId();
if (userId == null) return ResponseEntity.status(401).build();
draftRepository.findByUserId(userId).ifPresent(draftRepository::delete); draftRepository.findByUserId(userId).ifPresent(draftRepository::delete);
return ResponseEntity.accepted().build(); return ResponseEntity.accepted().build();
} }

View File

@@ -4,11 +4,11 @@ import de.oaa.xxx.games.common.aufgaben.Favorit;
import de.oaa.xxx.games.common.aufgaben.FavoritList; import de.oaa.xxx.games.common.aufgaben.FavoritList;
import de.oaa.xxx.games.common.entity.FavoritEntity; import de.oaa.xxx.games.common.entity.FavoritEntity;
import de.oaa.xxx.games.common.repository.FavoritRepository; import de.oaa.xxx.games.common.repository.FavoritRepository;
import de.oaa.xxx.user.UserService;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
@@ -19,6 +19,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder; import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
import java.security.Principal;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
@@ -30,9 +31,11 @@ public class FavoritController {
private static final Logger LOGGER = LoggerFactory.getLogger(FavoritController.class); private static final Logger LOGGER = LoggerFactory.getLogger(FavoritController.class);
private final FavoritRepository favoritRepository; private final FavoritRepository favoritRepository;
private final UserService userService;
public FavoritController(FavoritRepository favoritRepository) { public FavoritController(FavoritRepository favoritRepository, UserService userService) {
this.favoritRepository = favoritRepository; this.favoritRepository = favoritRepository;
this.userService = userService;
} }
@GetMapping("/{favoritId}") @GetMapping("/{favoritId}")
@@ -43,8 +46,8 @@ public class FavoritController {
} }
@GetMapping @GetMapping
public ResponseEntity<FavoritList> all() { public ResponseEntity<FavoritList> all(Principal principal) {
UUID userId = (UUID) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); UUID userId = userService.requireUser(principal).getUserId();
List<FavoritEntity> entities = favoritRepository.findByUserId(userId); List<FavoritEntity> entities = favoritRepository.findByUserId(userId);
FavoritList result = new FavoritList(); FavoritList result = new FavoritList();
result.setFavoriten(entities.stream().map(FavoritEntity::toFavorit).toList()); result.setFavoriten(entities.stream().map(FavoritEntity::toFavorit).toList());
@@ -52,8 +55,8 @@ public class FavoritController {
} }
@PostMapping @PostMapping
public ResponseEntity<Void> create(@RequestBody Favorit favorit) { public ResponseEntity<Void> create(@RequestBody Favorit favorit, Principal principal) {
UUID userId = (UUID) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); UUID userId = userService.requireUser(principal).getUserId();
if (favorit.getAufgabenGruppeId() == null) { if (favorit.getAufgabenGruppeId() == null) {
return ResponseEntity.badRequest().build(); return ResponseEntity.badRequest().build();
} }
@@ -72,9 +75,9 @@ public class FavoritController {
} }
@DeleteMapping @DeleteMapping
public ResponseEntity<Void> delete(@RequestBody Favorit favorit) { public ResponseEntity<Void> delete(@RequestBody Favorit favorit, Principal principal) {
try { try {
UUID userId = (UUID) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); UUID userId = userService.requireUser(principal).getUserId();
favoritRepository.findByUserIdAndAufgabenGruppeId(userId, favorit.getAufgabenGruppeId()) favoritRepository.findByUserIdAndAufgabenGruppeId(userId, favorit.getAufgabenGruppeId())
.forEach(favoritRepository::delete); .forEach(favoritRepository::delete);
return ResponseEntity.accepted().build(); return ResponseEntity.accepted().build();

View File

@@ -8,7 +8,7 @@ import de.oaa.xxx.games.common.repository.GruppenAboRepository;
import de.oaa.xxx.games.common.repository.ToyRepository; import de.oaa.xxx.games.common.repository.ToyRepository;
import de.oaa.xxx.subscription.SubscriptionLimitService; import de.oaa.xxx.subscription.SubscriptionLimitService;
import de.oaa.xxx.user.UserEntity; import de.oaa.xxx.user.UserEntity;
import de.oaa.xxx.user.UserRepository; import de.oaa.xxx.user.UserService;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
@@ -45,16 +45,16 @@ public class ToyController {
private static final int DEFAULT_PAGE_SIZE = 12; private static final int DEFAULT_PAGE_SIZE = 12;
private final ToyRepository toyRepository; private final ToyRepository toyRepository;
private final UserRepository userRepository; private final UserService userService;
private final GruppenAboRepository aboRepository; private final GruppenAboRepository aboRepository;
private final SubscriptionLimitService limitService; private final SubscriptionLimitService limitService;
public ToyController(ToyRepository toyRepository, public ToyController(ToyRepository toyRepository,
UserRepository userRepository, UserService userService,
GruppenAboRepository aboRepository, GruppenAboRepository aboRepository,
SubscriptionLimitService limitService) { SubscriptionLimitService limitService) {
this.toyRepository = toyRepository; this.toyRepository = toyRepository;
this.userRepository = userRepository; this.userService = userService;
this.aboRepository = aboRepository; this.aboRepository = aboRepository;
this.limitService = limitService; this.limitService = limitService;
} }
@@ -64,10 +64,7 @@ public class ToyController {
@RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "" + DEFAULT_PAGE_SIZE) int size, @RequestParam(defaultValue = "" + DEFAULT_PAGE_SIZE) int size,
Principal principal) { Principal principal) {
UserEntity user = userRepository.findByEmail(principal.getName()).orElse(null); UserEntity user = userService.requireUser(principal);
if (user == null) {
return ResponseEntity.status(401).build();
}
Page<ToyEntity> result = toyRepository.findByUserId( Page<ToyEntity> result = toyRepository.findByUserId(
user.getUserId(), PageRequest.of(page, size, Sort.by("name"))); user.getUserId(), PageRequest.of(page, size, Sort.by("name")));
return ResponseEntity.ok(toToyPage(result)); return ResponseEntity.ok(toToyPage(result));
@@ -88,8 +85,7 @@ public class ToyController {
*/ */
@GetMapping("/available") @GetMapping("/available")
public ResponseEntity<List<Toy>> available(Principal principal) { public ResponseEntity<List<Toy>> available(Principal principal) {
UserEntity user = userRepository.findByEmail(principal.getName()).orElse(null); UserEntity user = userService.requireUser(principal);
if (user == null) return ResponseEntity.status(401).build();
List<ToyEntity> own = toyRepository.findByUserId(user.getUserId(), PageRequest.of(0, 500, Sort.by("name"))).getContent(); List<ToyEntity> own = toyRepository.findByUserId(user.getUserId(), PageRequest.of(0, 500, Sort.by("name"))).getContent();
List<ToyEntity> system = toyRepository.findByUserIdIsNull(PageRequest.of(0, 500, Sort.by("name"))).getContent(); List<ToyEntity> system = toyRepository.findByUserIdIsNull(PageRequest.of(0, 500, Sort.by("name"))).getContent();
@@ -136,10 +132,7 @@ public class ToyController {
if (toy.getName() == null || toy.getName().isBlank()) { if (toy.getName() == null || toy.getName().isBlank()) {
return ResponseEntity.badRequest().build(); return ResponseEntity.badRequest().build();
} }
UserEntity user = userRepository.findByEmail(principal.getName()).orElse(null); UserEntity user = userService.requireUser(principal);
if (user == null) {
return ResponseEntity.status(401).build();
}
if (toyRepository.existsByNameIgnoreCaseAndUserIdIsNull(toy.getName()) if (toyRepository.existsByNameIgnoreCaseAndUserIdIsNull(toy.getName())
|| toyRepository.existsByNameIgnoreCaseAndUserId(toy.getName(), user.getUserId())) { || toyRepository.existsByNameIgnoreCaseAndUserId(toy.getName(), user.getUserId())) {
return ResponseEntity.status(409) return ResponseEntity.status(409)
@@ -162,10 +155,7 @@ public class ToyController {
@PostMapping("/copy/{toyId}") @PostMapping("/copy/{toyId}")
public ResponseEntity<Void> copy(@PathVariable UUID toyId, Principal principal) { public ResponseEntity<Void> copy(@PathVariable UUID toyId, Principal principal) {
UserEntity user = userRepository.findByEmail(principal.getName()).orElse(null); UserEntity user = userService.requireUser(principal);
if (user == null) {
return ResponseEntity.status(401).build();
}
ToyEntity source = toyRepository.findById(toyId).orElse(null); ToyEntity source = toyRepository.findById(toyId).orElse(null);
if (source == null) { if (source == null) {
return ResponseEntity.notFound().build(); return ResponseEntity.notFound().build();
@@ -194,10 +184,7 @@ public class ToyController {
if (toy.getName() == null || toy.getName().isBlank()) { if (toy.getName() == null || toy.getName().isBlank()) {
return ResponseEntity.badRequest().build(); return ResponseEntity.badRequest().build();
} }
UserEntity user = userRepository.findByEmail(principal.getName()).orElse(null); UserEntity user = userService.requireUser(principal);
if (user == null) {
return ResponseEntity.status(401).build();
}
ToyEntity entity = toyRepository.findById(toyId).orElse(null); ToyEntity entity = toyRepository.findById(toyId).orElse(null);
if (entity == null) { if (entity == null) {
return ResponseEntity.notFound().build(); return ResponseEntity.notFound().build();
@@ -223,10 +210,7 @@ public class ToyController {
@DeleteMapping("/{toyId}") @DeleteMapping("/{toyId}")
public ResponseEntity<Void> delete(@PathVariable UUID toyId, Principal principal) { public ResponseEntity<Void> delete(@PathVariable UUID toyId, Principal principal) {
UserEntity user = userRepository.findByEmail(principal.getName()).orElse(null); UserEntity user = userService.requireUser(principal);
if (user == null) {
return ResponseEntity.status(401).build();
}
ToyEntity toy = toyRepository.findById(toyId).orElse(null); ToyEntity toy = toyRepository.findById(toyId).orElse(null);
if (toy == null) { if (toy == null) {
return ResponseEntity.noContent().build(); return ResponseEntity.noContent().build();

View File

@@ -7,7 +7,7 @@ import java.util.Optional;
import java.util.UUID; import java.util.UUID;
import de.oaa.xxx.games.bdsm.AktiveSperre; import de.oaa.xxx.games.bdsm.AktiveSperre;
import de.oaa.xxx.games.bdsm.Mitspieler; import de.oaa.xxx.games.bdsm.BdsmMitspieler;
import de.oaa.xxx.games.common.aufgaben.Werkzeug; import de.oaa.xxx.games.common.aufgaben.Werkzeug;
import jakarta.persistence.CollectionTable; import jakarta.persistence.CollectionTable;
import jakarta.persistence.Column; import jakarta.persistence.Column;
@@ -52,7 +52,7 @@ public class AktiveSperreEntity {
@JoinColumn(name = "sessionId", nullable = false) @JoinColumn(name = "sessionId", nullable = false)
private BdsmGameEntity session; private BdsmGameEntity session;
public AktiveSperre toSperre(List<Mitspieler> mitspielerList) { public AktiveSperre toSperre(List<BdsmMitspieler> mitspielerList) {
AktiveSperre sperre = new AktiveSperre(); AktiveSperre sperre = new AktiveSperre();
sperre.setAktiveSperreId(aktiveSperreId); sperre.setAktiveSperreId(aktiveSperreId);
sperre.setEndzeit(endzeit); sperre.setEndzeit(endzeit);
@@ -70,8 +70,8 @@ public class AktiveSperreEntity {
+ ", " + minuten + "min, von=" + startzeit + ", bis=" + endzeit + ", fuer=" + fuer + "]"; + ", " + minuten + "min, von=" + startzeit + ", bis=" + endzeit + ", fuer=" + fuer + "]";
} }
private Mitspieler getMitspielerFromList(List<Mitspieler> mitspielerList, UUID id) { private BdsmMitspieler getMitspielerFromList(List<BdsmMitspieler> mitspielerList, UUID id) {
Optional<Mitspieler> first = mitspielerList.stream().filter(m -> m.getId().equals(id)).findFirst(); Optional<BdsmMitspieler> first = mitspielerList.stream().filter(m -> m.getId().equals(id)).findFirst();
return first.orElse(null); return first.orElse(null);
} }
} }

View File

@@ -5,7 +5,7 @@ import java.util.List;
import java.util.UUID; import java.util.UUID;
import de.oaa.xxx.games.bdsm.GeschlechtEnum; import de.oaa.xxx.games.bdsm.GeschlechtEnum;
import de.oaa.xxx.games.bdsm.Mitspieler; import de.oaa.xxx.games.bdsm.BdsmMitspieler;
import de.oaa.xxx.games.bdsm.RolleEnum; import de.oaa.xxx.games.bdsm.RolleEnum;
import de.oaa.xxx.games.common.aufgaben.Werkzeug; import de.oaa.xxx.games.common.aufgaben.Werkzeug;
import jakarta.persistence.CollectionTable; import jakarta.persistence.CollectionTable;
@@ -70,8 +70,8 @@ public class MitspielerEntity {
+ ", geschlecht=" + geschlecht + ", rollen=" + rollen + ", werkzeuge=" + werkzeuge + "]"; + ", geschlecht=" + geschlecht + ", rollen=" + rollen + ", werkzeuge=" + werkzeuge + "]";
} }
public Mitspieler toMitspieler() { public BdsmMitspieler toMitspieler() {
Mitspieler mitspieler = new Mitspieler(); BdsmMitspieler mitspieler = new BdsmMitspieler();
mitspieler.setGeschlecht(geschlecht); mitspieler.setGeschlecht(geschlecht);
mitspieler.setId(mitspielerId); mitspieler.setId(mitspielerId);
mitspieler.setUserId(userId); mitspieler.setUserId(userId);

View File

@@ -60,6 +60,7 @@ import de.oaa.xxx.games.chastity.unlock.UnlockCodeHistoryService;
import de.oaa.xxx.social.SystemMessageService; import de.oaa.xxx.social.SystemMessageService;
import de.oaa.xxx.subscription.SubscriptionLimitService; import de.oaa.xxx.subscription.SubscriptionLimitService;
import de.oaa.xxx.user.UserRepository; import de.oaa.xxx.user.UserRepository;
import de.oaa.xxx.user.UserService;
@RestController @RestController
@RequestMapping("/keyholder") @RequestMapping("/keyholder")
@@ -80,6 +81,7 @@ public class CardLockController {
private final SystemMessageService systemMessageService; private final SystemMessageService systemMessageService;
private final CardLockServiceFactory cardLockServiceFactory; private final CardLockServiceFactory cardLockServiceFactory;
private final SubscriptionLimitService subscriptionLimitService; private final SubscriptionLimitService subscriptionLimitService;
private final UserService userService;
@Value("${app.base-url:http://localhost:8080}") @Value("${app.base-url:http://localhost:8080}")
private String baseUrl; private String baseUrl;
@@ -99,7 +101,8 @@ public class CardLockController {
SystemMessageService systemMessageService, SystemMessageService systemMessageService,
CardLockServiceFactory cardLockServiceFactory, CardLockServiceFactory cardLockServiceFactory,
LockControlFactory lockControlFactory, LockControlFactory lockControlFactory,
SubscriptionLimitService subscriptionLimitService) { SubscriptionLimitService subscriptionLimitService,
UserService userService) {
this.cardlockRepository = cardlockRepository; this.cardlockRepository = cardlockRepository;
this.userRepository = userRepository; this.userRepository = userRepository;
this.invitationRepository = invitationRepository; this.invitationRepository = invitationRepository;
@@ -115,6 +118,7 @@ public class CardLockController {
this.systemMessageService = systemMessageService; this.systemMessageService = systemMessageService;
this.cardLockServiceFactory = cardLockServiceFactory; this.cardLockServiceFactory = cardLockServiceFactory;
this.subscriptionLimitService = subscriptionLimitService; this.subscriptionLimitService = subscriptionLimitService;
this.userService = userService;
} }
record CreateCardLockRequest(String name, UUID keyholder, UUID lockeeUserId, boolean lockeeDetailsVisible, record CreateCardLockRequest(String name, UUID keyholder, UUID lockeeUserId, boolean lockeeDetailsVisible,
@@ -138,10 +142,7 @@ public class CardLockController {
public ResponseEntity<Map<String, Object>> createCardLock(@RequestBody CreateCardLockRequest req, public ResponseEntity<Map<String, Object>> createCardLock(@RequestBody CreateCardLockRequest req,
Principal principal) { Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); var me = userService.requireUser(principal);
if (meOpt.isEmpty())
return ResponseEntity.status(401).build();
var me = meOpt.get();
UUID myId = me.getUserId(); UUID myId = me.getUserId();
if (req.initialCards() == null || req.initialCards().isEmpty() || req.pickEveryMinute() == null if (req.initialCards() == null || req.initialCards().isEmpty() || req.pickEveryMinute() == null
@@ -188,10 +189,7 @@ public class CardLockController {
inv.setDetailsVisible(req.lockeeDetailsVisible()); inv.setDetailsVisible(req.lockeeDetailsVisible());
lockeeInvitationRepository.save(inv); lockeeInvitationRepository.save(inv);
String lockName = req.name() != null && !req.name().isBlank() ? req.name() : "Unbenanntes Lock"; systemMessageService.pushInvitationUpdate(lockee.getUserId());
sendMessage(myId, lockee.getUserId(),
me.getName() + " hat dich als Lockee für das Lock „" + lockName + "\" eingeladen.",
"/einladungen.html", de.oaa.xxx.social.entity.MessageCause.INVITATION);
return ResponseEntity.ok(Map.of("lockId", lock.getLockId().toString(), "lockeeInvitationSent", true)); return ResponseEntity.ok(Map.of("lockId", lock.getLockId().toString(), "lockeeInvitationSent", true));
} }
@@ -260,10 +258,7 @@ public class CardLockController {
inv.setCreatedAt(now); inv.setCreatedAt(now);
invitationRepository.save(inv); invitationRepository.save(inv);
String lockName = req.name() != null && !req.name().isBlank() ? req.name() : "Unbenanntes Lock"; systemMessageService.pushInvitationUpdate(kh.getUserId());
sendMessage(me.getUserId(), kh.getUserId(),
me.getName() + " hat dich als Keyholder*In für das Lock „" + lockName + "\" eingeladen.",
"/einladungen.html", de.oaa.xxx.social.entity.MessageCause.INVITATION);
keyholderPending = true; keyholderPending = true;
} }
@@ -276,10 +271,7 @@ public class CardLockController {
@PostMapping("/cardlock/{lockId}/draw") @PostMapping("/cardlock/{lockId}/draw")
@Transactional @Transactional
public ResponseEntity<Map<String, Object>> drawCard(@PathVariable UUID lockId, Principal principal) { public ResponseEntity<Map<String, Object>> drawCard(@PathVariable UUID lockId, Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty())
return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
var lockOpt = cardlockRepository.findById(lockId); var lockOpt = cardlockRepository.findById(lockId);
if (lockOpt.isEmpty()) if (lockOpt.isEmpty())
@@ -305,9 +297,10 @@ public class CardLockController {
if (dto.unlockCode() != null && !dto.unlockCode().isBlank()) { if (dto.unlockCode() != null && !dto.unlockCode().isBlank()) {
unlockCodeHistoryService.save(myId, l.getLockId(), l.getName(), dto.unlockCode(), "GREEN_CARD"); unlockCodeHistoryService.save(myId, l.getLockId(), l.getName(), dto.unlockCode(), "GREEN_CARD");
if (l.getKeyholder() != null) { if (l.getKeyholder() != null) {
String meName = userRepository.findById(myId).map(u -> u.getName()).orElse("");
sendMessage(myId, l.getKeyholder(), sendMessage(myId, l.getKeyholder(),
meOpt.get().getName() + " hat die grüne Karte gezogen! Der Entsperrcode wurde angezeigt.", meName + " hat die grüne Karte gezogen! Der Entsperrcode wurde angezeigt.",
"/keyholder.html", de.oaa.xxx.social.entity.MessageCause.GAME_STATE); "/games/chastity/keyholder.html", de.oaa.xxx.social.entity.MessageCause.GAME_STATE);
} }
} }
@@ -317,10 +310,7 @@ public class CardLockController {
@PostMapping("/cardlock/{lockId}/hygiene/start") @PostMapping("/cardlock/{lockId}/hygiene/start")
@Transactional @Transactional
public ResponseEntity<Map<String, Object>> startHygieneOpening(@PathVariable UUID lockId, Principal principal) { public ResponseEntity<Map<String, Object>> startHygieneOpening(@PathVariable UUID lockId, Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty())
return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
var lockOpt = cardlockRepository.findById(lockId); var lockOpt = cardlockRepository.findById(lockId);
if (lockOpt.isEmpty()) if (lockOpt.isEmpty())
@@ -337,10 +327,7 @@ public class CardLockController {
@PostMapping("/cardlock/{lockId}/hygiene/end") @PostMapping("/cardlock/{lockId}/hygiene/end")
@Transactional @Transactional
public ResponseEntity<Map<String, Object>> endHygieneOpening(@PathVariable UUID lockId, Principal principal) { public ResponseEntity<Map<String, Object>> endHygieneOpening(@PathVariable UUID lockId, Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty())
return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
var lockOpt = cardlockRepository.findById(lockId); var lockOpt = cardlockRepository.findById(lockId);
if (lockOpt.isEmpty()) if (lockOpt.isEmpty())
@@ -356,10 +343,7 @@ public class CardLockController {
@PostMapping("/cardlock/{lockId}/task/complete") @PostMapping("/cardlock/{lockId}/task/complete")
@Transactional @Transactional
public ResponseEntity<Void> completeTask(@PathVariable UUID lockId, Principal principal) { public ResponseEntity<Void> completeTask(@PathVariable UUID lockId, Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty())
return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
var lockOpt = cardlockRepository.findById(lockId); var lockOpt = cardlockRepository.findById(lockId);
if (lockOpt.isEmpty()) if (lockOpt.isEmpty())
@@ -376,9 +360,7 @@ public class CardLockController {
@PostMapping("/cardlock/{lockId}/relock") @PostMapping("/cardlock/{lockId}/relock")
@Transactional @Transactional
public ResponseEntity<Void> relock(@PathVariable UUID lockId, Principal principal) { public ResponseEntity<Void> relock(@PathVariable UUID lockId, Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
var lockOpt = cardlockRepository.findById(lockId); var lockOpt = cardlockRepository.findById(lockId);
if (lockOpt.isEmpty()) return ResponseEntity.notFound().build(); if (lockOpt.isEmpty()) return ResponseEntity.notFound().build();
@@ -394,10 +376,7 @@ public class CardLockController {
@PostMapping("/cardlock/{lockId}/green/keep") @PostMapping("/cardlock/{lockId}/green/keep")
@Transactional @Transactional
public ResponseEntity<Void> greenKeep(@PathVariable UUID lockId, Principal principal) { public ResponseEntity<Void> greenKeep(@PathVariable UUID lockId, Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty())
return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
var lockOpt = cardlockRepository.findById(lockId); var lockOpt = cardlockRepository.findById(lockId);
if (lockOpt.isEmpty()) if (lockOpt.isEmpty())
@@ -411,8 +390,9 @@ public class CardLockController {
// Grüne Karte zurückgelegt → Keyholder benachrichtigen // Grüne Karte zurückgelegt → Keyholder benachrichtigen
if (l.getKeyholder() != null) { if (l.getKeyholder() != null) {
String meName = userRepository.findById(myId).map(u -> u.getName()).orElse("");
sendMessage(myId, l.getKeyholder(), sendMessage(myId, l.getKeyholder(),
meOpt.get().getName() + " hat die grüne Karte zurückgelegt und bleibt im Lock.", "/keyholder.html", meName + " hat die grüne Karte zurückgelegt und bleibt im Lock.", "/games/chastity/keyholder.html",
de.oaa.xxx.social.entity.MessageCause.GAME_STATE); de.oaa.xxx.social.entity.MessageCause.GAME_STATE);
} }
@@ -421,10 +401,7 @@ public class CardLockController {
@GetMapping("/mylock") @GetMapping("/mylock")
public ResponseEntity<Map<String, Object>> getMyActiveLock(Principal principal) { public ResponseEntity<Map<String, Object>> getMyActiveLock(Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty())
return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
var activeLockId = cardLockServiceFactory.findActiveLockId(myId); var activeLockId = cardLockServiceFactory.findActiveLockId(myId);
if (activeLockId.isEmpty()) if (activeLockId.isEmpty())
return ResponseEntity.noContent().build(); return ResponseEntity.noContent().build();
@@ -433,10 +410,7 @@ public class CardLockController {
@GetMapping("/cardlock/{lockId}") @GetMapping("/cardlock/{lockId}")
public ResponseEntity<Map<String, Object>> getLock(@PathVariable UUID lockId, Principal principal) { public ResponseEntity<Map<String, Object>> getLock(@PathVariable UUID lockId, Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty())
return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
var lockOpt = cardlockRepository.findById(lockId); var lockOpt = cardlockRepository.findById(lockId);
if (lockOpt.isEmpty()) if (lockOpt.isEmpty())
@@ -547,7 +521,7 @@ public class CardLockController {
assignedTaskRepository.save(t); assignedTaskRepository.save(t);
sendMessage(l.getKeyholder(), l.getLockee(), sendMessage(l.getKeyholder(), l.getLockee(),
"Die dir gestellte Aufgabe ist abgelaufen, ohne dass du reagiert hast. Die Strafe wurde automatisch angewendet.", "Die dir gestellte Aufgabe ist abgelaufen, ohne dass du reagiert hast. Die Strafe wurde automatisch angewendet.",
"/activelock.html?lockId=" + l.getLockId(), de.oaa.xxx.social.entity.MessageCause.GAME_STATE); "/games/chastity/activelock.html?lockId=" + l.getLockId(), de.oaa.xxx.social.entity.MessageCause.GAME_STATE);
} }
} }
@@ -614,10 +588,7 @@ public class CardLockController {
@PostMapping("/cardlock/{lockId}/verification/start") @PostMapping("/cardlock/{lockId}/verification/start")
@Transactional @Transactional
public ResponseEntity<Map<String, Object>> startVerification(@PathVariable UUID lockId, Principal principal) { public ResponseEntity<Map<String, Object>> startVerification(@PathVariable UUID lockId, Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty())
return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
var lockOpt = cardlockRepository.findById(lockId); var lockOpt = cardlockRepository.findById(lockId);
if (lockOpt.isEmpty()) if (lockOpt.isEmpty())
@@ -660,10 +631,7 @@ public class CardLockController {
@Transactional @Transactional
public ResponseEntity<Void> completeVerification(@PathVariable UUID lockId, @PathVariable UUID verificationId, public ResponseEntity<Void> completeVerification(@PathVariable UUID lockId, @PathVariable UUID verificationId,
@RequestParam MultipartFile image, Principal principal) throws IOException { @RequestParam MultipartFile image, Principal principal) throws IOException {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty())
return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
var lockOpt = cardlockRepository.findById(lockId); var lockOpt = cardlockRepository.findById(lockId);
if (lockOpt.isEmpty()) if (lockOpt.isEmpty())
@@ -683,9 +651,9 @@ public class CardLockController {
var lock = lockOpt.get(); var lock = lockOpt.get();
if (lock.getKeyholder() != null) { if (lock.getKeyholder() != null) {
var lockee = meOpt.get(); String meName = userRepository.findById(myId).map(u -> u.getName()).orElse("");
sendMessage(myId, lock.getKeyholder(), "📸 " + lockee.getName() + " hat eine Verifikation eingereicht.", sendMessage(myId, lock.getKeyholder(), "📸 " + meName + " hat eine Verifikation eingereicht.",
"/keyholder.html", de.oaa.xxx.social.entity.MessageCause.GAME_STATE); "/games/chastity/keyholder.html", de.oaa.xxx.social.entity.MessageCause.GAME_STATE);
} }
return ResponseEntity.noContent().build(); return ResponseEntity.noContent().build();
@@ -716,10 +684,7 @@ public class CardLockController {
@DeleteMapping("/cardlock/{lockId}/verification/today") @DeleteMapping("/cardlock/{lockId}/verification/today")
@Transactional @Transactional
public ResponseEntity<Void> renewVerification(@PathVariable UUID lockId, Principal principal) { public ResponseEntity<Void> renewVerification(@PathVariable UUID lockId, Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty())
return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
var lockOpt = cardlockRepository.findById(lockId); var lockOpt = cardlockRepository.findById(lockId);
if (lockOpt.isEmpty()) if (lockOpt.isEmpty())
@@ -740,12 +705,22 @@ public class CardLockController {
// ── Keyholder-Dashboard Endpunkte ── // ── Keyholder-Dashboard Endpunkte ──
@GetMapping("/invitations/mine/count")
public ResponseEntity<Integer> countMyInvitations(Principal principal) {
UUID myId = userService.requireUser(principal).getUserId();
int count = 0;
for (var inv : invitationRepository.findByKeyholderUserId(myId)) {
var lockOpt = cardlockRepository.findById(inv.getLockId());
if (lockOpt.isEmpty()) continue;
if (lockOpt.get().getKeyholder() != null) continue;
count++;
}
return ResponseEntity.ok(count);
}
@GetMapping("/invitations/mine") @GetMapping("/invitations/mine")
public ResponseEntity<List<Map<String, Object>>> getMyInvitations(Principal principal) { public ResponseEntity<List<Map<String, Object>>> getMyInvitations(Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty())
return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
var invitations = invitationRepository.findByKeyholderUserId(myId); var invitations = invitationRepository.findByKeyholderUserId(myId);
List<Map<String, Object>> result = new ArrayList<>(); List<Map<String, Object>> result = new ArrayList<>();
@@ -776,10 +751,7 @@ public class CardLockController {
@Transactional @Transactional
@DeleteMapping("/invitations/mine/{token}") @DeleteMapping("/invitations/mine/{token}")
public ResponseEntity<Void> declineInvitation(@PathVariable String token, Principal principal) { public ResponseEntity<Void> declineInvitation(@PathVariable String token, Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); var me = userService.requireUser(principal);
if (meOpt.isEmpty())
return ResponseEntity.status(401).build();
var me = meOpt.get();
UUID myId = me.getUserId(); UUID myId = me.getUserId();
var invOpt = invitationRepository.findByToken(token); var invOpt = invitationRepository.findByToken(token);
@@ -794,10 +766,7 @@ public class CardLockController {
if (lockOpt.isPresent()) { if (lockOpt.isPresent()) {
var lock = lockOpt.get(); var lock = lockOpt.get();
String lockName = lock.getName() != null && !lock.getName().isBlank() ? lock.getName() : "Unbenanntes Lock"; systemMessageService.pushInvitationUpdate(lock.getLockee());
sendMessage(myId, lock.getLockee(),
me.getName() + " hat die Einladung als Keyholder*In für das Lock „" + lockName + "\" abgelehnt.",
null, de.oaa.xxx.social.entity.MessageCause.INVITATION);
} }
return ResponseEntity.noContent().build(); return ResponseEntity.noContent().build();
@@ -805,10 +774,7 @@ public class CardLockController {
@GetMapping("/invitations/sent") @GetMapping("/invitations/sent")
public ResponseEntity<List<Map<String, Object>>> getSentKeyholderInvitations(Principal principal) { public ResponseEntity<List<Map<String, Object>>> getSentKeyholderInvitations(Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty())
return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
var invitations = invitationRepository.findByLockeeUserId(myId); var invitations = invitationRepository.findByLockeeUserId(myId);
List<Map<String, Object>> result = new ArrayList<>(); List<Map<String, Object>> result = new ArrayList<>();
@@ -838,10 +804,7 @@ public class CardLockController {
@Transactional @Transactional
@DeleteMapping("/invitations/sent/{token}") @DeleteMapping("/invitations/sent/{token}")
public ResponseEntity<Void> cancelSentKeyholderInvitation(@PathVariable String token, Principal principal) { public ResponseEntity<Void> cancelSentKeyholderInvitation(@PathVariable String token, Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); var me = userService.requireUser(principal);
if (meOpt.isEmpty())
return ResponseEntity.status(401).build();
var me = meOpt.get();
UUID myId = me.getUserId(); UUID myId = me.getUserId();
var invOpt = invitationRepository.findByToken(token); var invOpt = invitationRepository.findByToken(token);
@@ -856,22 +819,14 @@ public class CardLockController {
invitationRepository.delete(inv); invitationRepository.delete(inv);
String lockName = lockOpt.get().getName() != null && !lockOpt.get().getName().isBlank() systemMessageService.pushInvitationUpdate(inv.getKeyholderUserId());
? lockOpt.get().getName()
: "Unbenanntes Lock";
sendMessage(myId, inv.getKeyholderUserId(),
me.getName() + " hat die Keyholder-Einladung für das Lock „" + lockName + "\" zurückgezogen.", null,
de.oaa.xxx.social.entity.MessageCause.INVITATION);
return ResponseEntity.noContent().build(); return ResponseEntity.noContent().build();
} }
@GetMapping("/as-keyholder") @GetMapping("/as-keyholder")
public ResponseEntity<List<Map<String, Object>>> getLocksAsKeyholder(Principal principal) { public ResponseEntity<List<Map<String, Object>>> getLocksAsKeyholder(Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty())
return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
var locks = cardlockRepository.findByKeyholderAndUnlockTimeIsNull(myId); var locks = cardlockRepository.findByKeyholderAndUnlockTimeIsNull(myId);
List<Map<String, Object>> result = new ArrayList<>(); List<Map<String, Object>> result = new ArrayList<>();
@@ -898,10 +853,7 @@ public class CardLockController {
@GetMapping("/as-keyholder/{lockId}") @GetMapping("/as-keyholder/{lockId}")
public ResponseEntity<Map<String, Object>> getLockAsKeyholder(@PathVariable UUID lockId, Principal principal) { public ResponseEntity<Map<String, Object>> getLockAsKeyholder(@PathVariable UUID lockId, Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty())
return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
var lockOpt = cardlockRepository.findById(lockId); var lockOpt = cardlockRepository.findById(lockId);
if (lockOpt.isEmpty()) if (lockOpt.isEmpty())
@@ -1058,10 +1010,7 @@ public class CardLockController {
@Transactional @Transactional
@DeleteMapping("/cardlock/{lockId}") @DeleteMapping("/cardlock/{lockId}")
public ResponseEntity<Void> deleteLock(@PathVariable UUID lockId, Principal principal) { public ResponseEntity<Void> deleteLock(@PathVariable UUID lockId, Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty())
return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
var lockOpt = cardlockRepository.findById(lockId); var lockOpt = cardlockRepository.findById(lockId);
if (lockOpt.isEmpty()) if (lockOpt.isEmpty())
@@ -1090,10 +1039,7 @@ public class CardLockController {
@Transactional @Transactional
public ResponseEntity<Void> addCards(@PathVariable UUID lockId, @RequestBody ModifyCardsRequest req, public ResponseEntity<Void> addCards(@PathVariable UUID lockId, @RequestBody ModifyCardsRequest req,
Principal principal) { Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); var me = userService.requireUser(principal);
if (meOpt.isEmpty())
return ResponseEntity.status(401).build();
var me = meOpt.get();
UUID myId = me.getUserId(); UUID myId = me.getUserId();
var lockOpt = cardlockRepository.findById(lockId); var lockOpt = cardlockRepository.findById(lockId);
@@ -1130,7 +1076,7 @@ public class CardLockController {
? me.getName() + " hat " + toAdd.size() + " Karte(n) zu deinem Lock hinzugefügt: " + detail + "." ? me.getName() + " hat " + toAdd.size() + " Karte(n) zu deinem Lock hinzugefügt: " + detail + "."
: me.getName() + " hat Karten zu deinem Lock hinzugefügt."; : me.getName() + " hat Karten zu deinem Lock hinzugefügt.";
sendMessage(myId, l.getLockee(), msgText, "/activelock.html?lockId=" + lockId, sendMessage(myId, l.getLockee(), msgText, "/games/chastity/activelock.html?lockId=" + lockId,
de.oaa.xxx.social.entity.MessageCause.GAME_STATE); de.oaa.xxx.social.entity.MessageCause.GAME_STATE);
return ResponseEntity.noContent().build(); return ResponseEntity.noContent().build();
@@ -1140,10 +1086,7 @@ public class CardLockController {
@Transactional @Transactional
public ResponseEntity<Map<String, String>> removeCards(@PathVariable UUID lockId, public ResponseEntity<Map<String, String>> removeCards(@PathVariable UUID lockId,
@RequestBody ModifyCardsRequest req, Principal principal) { @RequestBody ModifyCardsRequest req, Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); var me = userService.requireUser(principal);
if (meOpt.isEmpty())
return ResponseEntity.status(401).build();
var me = meOpt.get();
UUID myId = me.getUserId(); UUID myId = me.getUserId();
var lockOpt = cardlockRepository.findById(lockId); var lockOpt = cardlockRepository.findById(lockId);
@@ -1195,7 +1138,7 @@ public class CardLockController {
? me.getName() + " hat " + removed.size() + " Karte(n) aus deinem Lock entfernt: " + detail + "." ? me.getName() + " hat " + removed.size() + " Karte(n) aus deinem Lock entfernt: " + detail + "."
: me.getName() + " hat Karten aus deinem Lock entfernt."; : me.getName() + " hat Karten aus deinem Lock entfernt.";
sendMessage(myId, l.getLockee(), msgText, "/activelock.html?lockId=" + lockId, sendMessage(myId, l.getLockee(), msgText, "/games/chastity/activelock.html?lockId=" + lockId,
de.oaa.xxx.social.entity.MessageCause.GAME_STATE); de.oaa.xxx.social.entity.MessageCause.GAME_STATE);
return ResponseEntity.noContent().build(); return ResponseEntity.noContent().build();
@@ -1210,10 +1153,7 @@ public class CardLockController {
@GetMapping("/cardlock/unlock-history") @GetMapping("/cardlock/unlock-history")
public ResponseEntity<List<Map<String, Object>>> getUnlockHistory(Principal principal) { public ResponseEntity<List<Map<String, Object>>> getUnlockHistory(Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty())
return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
var entries = unlockCodeHistoryRepository.findByUserIdOrderByReceivedAtDesc(myId, var entries = unlockCodeHistoryRepository.findByUserIdOrderByReceivedAtDesc(myId,
org.springframework.data.domain.PageRequest.of(0, 10)); org.springframework.data.domain.PageRequest.of(0, 10));
List<Map<String, Object>> result = new ArrayList<>(); List<Map<String, Object>> result = new ArrayList<>();
@@ -1238,10 +1178,7 @@ public class CardLockController {
@PostMapping("/as-keyholder/{lockId}/assign-task") @PostMapping("/as-keyholder/{lockId}/assign-task")
public ResponseEntity<?> assignTask(@PathVariable UUID lockId, @RequestBody AssignTaskRequest req, public ResponseEntity<?> assignTask(@PathVariable UUID lockId, @RequestBody AssignTaskRequest req,
Principal principal) { Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); var me = userService.requireUser(principal);
if (meOpt.isEmpty())
return ResponseEntity.status(401).build();
var me = meOpt.get();
var lockOpt = cardlockRepository.findById(lockId); var lockOpt = cardlockRepository.findById(lockId);
if (lockOpt.isEmpty()) if (lockOpt.isEmpty())
@@ -1282,7 +1219,7 @@ public class CardLockController {
sendMessage(me.getUserId(), l.getLockee(), sendMessage(me.getUserId(), l.getLockee(),
me.getName() + " hat dir eine Aufgabe gestellt. Du hast " + req.acceptDeadlineMinutes() me.getName() + " hat dir eine Aufgabe gestellt. Du hast " + req.acceptDeadlineMinutes()
+ " Minuten, um sie anzunehmen.", + " Minuten, um sie anzunehmen.",
"/activelock.html?lockId=" + lockId, de.oaa.xxx.social.entity.MessageCause.GAME_STATE); "/games/chastity/activelock.html?lockId=" + lockId, de.oaa.xxx.social.entity.MessageCause.GAME_STATE);
return ResponseEntity.noContent().build(); return ResponseEntity.noContent().build();
} }
@@ -1293,10 +1230,7 @@ public class CardLockController {
@PostMapping("/cardlock/{lockId}/assigned-tasks/{taskId}/accept") @PostMapping("/cardlock/{lockId}/assigned-tasks/{taskId}/accept")
public ResponseEntity<?> acceptAssignedTask(@PathVariable UUID lockId, @PathVariable UUID taskId, public ResponseEntity<?> acceptAssignedTask(@PathVariable UUID lockId, @PathVariable UUID taskId,
Principal principal) { Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty())
return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
var lockOpt = cardlockRepository.findById(lockId); var lockOpt = cardlockRepository.findById(lockId);
if (lockOpt.isEmpty()) if (lockOpt.isEmpty())
@@ -1343,8 +1277,9 @@ public class CardLockController {
assignedTaskRepository.save(task); assignedTaskRepository.save(task);
cardlockRepository.save(l); cardlockRepository.save(l);
sendMessage(myId, l.getKeyholder(), meOpt.get().getName() + " hat die gestellte Aufgabe angenommen.", String meName = userRepository.findById(myId).map(u -> u.getName()).orElse("");
"/keyholder.html", de.oaa.xxx.social.entity.MessageCause.GAME_STATE); sendMessage(myId, l.getKeyholder(), meName + " hat die gestellte Aufgabe angenommen.",
"/games/chastity/keyholder.html", de.oaa.xxx.social.entity.MessageCause.GAME_STATE);
return ResponseEntity.noContent().build(); return ResponseEntity.noContent().build();
} }
@@ -1354,10 +1289,7 @@ public class CardLockController {
@PostMapping("/cardlock/{lockId}/assigned-tasks/{taskId}/decline") @PostMapping("/cardlock/{lockId}/assigned-tasks/{taskId}/decline")
public ResponseEntity<?> declineAssignedTask(@PathVariable UUID lockId, @PathVariable UUID taskId, public ResponseEntity<?> declineAssignedTask(@PathVariable UUID lockId, @PathVariable UUID taskId,
Principal principal) { Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty())
return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
var lockOpt = cardlockRepository.findById(lockId); var lockOpt = cardlockRepository.findById(lockId);
if (lockOpt.isEmpty()) if (lockOpt.isEmpty())
@@ -1377,9 +1309,10 @@ public class CardLockController {
cardLockServiceFactory.create(l).applyAssignedTaskPenalty(task); cardLockServiceFactory.create(l).applyAssignedTaskPenalty(task);
assignedTaskRepository.save(task); assignedTaskRepository.save(task);
String meNameDecl = userRepository.findById(myId).map(u -> u.getName()).orElse("");
sendMessage(myId, l.getKeyholder(), sendMessage(myId, l.getKeyholder(),
meOpt.get().getName() + " hat die gestellte Aufgabe abgelehnt. Die Strafe wurde angewendet.", meNameDecl + " hat die gestellte Aufgabe abgelehnt. Die Strafe wurde angewendet.",
"/keyholder.html", de.oaa.xxx.social.entity.MessageCause.GAME_STATE); "/games/chastity/keyholder.html", de.oaa.xxx.social.entity.MessageCause.GAME_STATE);
return ResponseEntity.noContent().build(); return ResponseEntity.noContent().build();
} }
@@ -1389,10 +1322,7 @@ public class CardLockController {
@DeleteMapping("/as-keyholder/{lockId}/assigned-tasks/{taskId}") @DeleteMapping("/as-keyholder/{lockId}/assigned-tasks/{taskId}")
public ResponseEntity<?> cancelAssignedTask(@PathVariable UUID lockId, @PathVariable UUID taskId, public ResponseEntity<?> cancelAssignedTask(@PathVariable UUID lockId, @PathVariable UUID taskId,
Principal principal) { Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty())
return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
var lockOpt = cardlockRepository.findById(lockId); var lockOpt = cardlockRepository.findById(lockId);
if (lockOpt.isEmpty()) if (lockOpt.isEmpty())
@@ -1419,10 +1349,7 @@ public class CardLockController {
@PostMapping("/as-keyholder/{lockId}/freeze") @PostMapping("/as-keyholder/{lockId}/freeze")
public ResponseEntity<?> freezeLock(@PathVariable UUID lockId, @RequestBody FreezeRequest req, public ResponseEntity<?> freezeLock(@PathVariable UUID lockId, @RequestBody FreezeRequest req,
Principal principal) { Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); var me = userService.requireUser(principal);
if (meOpt.isEmpty())
return ResponseEntity.status(401).build();
var me = meOpt.get();
UUID myId = me.getUserId(); UUID myId = me.getUserId();
var lockOpt = cardlockRepository.findById(lockId); var lockOpt = cardlockRepository.findById(lockId);
@@ -1454,7 +1381,7 @@ public class CardLockController {
+ until.toLocalDate().format(java.time.format.DateTimeFormatter.ofPattern("dd.MM.yyyy")) + " " + until.toLocalDate().format(java.time.format.DateTimeFormatter.ofPattern("dd.MM.yyyy")) + " "
+ until.toLocalTime().format(java.time.format.DateTimeFormatter.ofPattern("HH:mm")) + until.toLocalTime().format(java.time.format.DateTimeFormatter.ofPattern("HH:mm"))
+ " Uhr eingefroren.", + " Uhr eingefroren.",
"/activelock.html?lockId=" + lockId, de.oaa.xxx.social.entity.MessageCause.GAME_STATE); "/games/chastity/activelock.html?lockId=" + lockId, de.oaa.xxx.social.entity.MessageCause.GAME_STATE);
return ResponseEntity.noContent().build(); return ResponseEntity.noContent().build();
} }
@@ -1462,10 +1389,7 @@ public class CardLockController {
@Transactional @Transactional
@DeleteMapping("/as-keyholder/{lockId}/freeze") @DeleteMapping("/as-keyholder/{lockId}/freeze")
public ResponseEntity<?> unfreezeLock(@PathVariable UUID lockId, Principal principal) { public ResponseEntity<?> unfreezeLock(@PathVariable UUID lockId, Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); var me = userService.requireUser(principal);
if (meOpt.isEmpty())
return ResponseEntity.status(401).build();
var me = meOpt.get();
UUID myId = me.getUserId(); UUID myId = me.getUserId();
var lockOpt = cardlockRepository.findById(lockId); var lockOpt = cardlockRepository.findById(lockId);
@@ -1483,7 +1407,7 @@ public class CardLockController {
cardlockRepository.save(l); cardlockRepository.save(l);
sendMessage(myId, l.getLockee(), me.getName() + " hat dein Lock wieder entfroren.", sendMessage(myId, l.getLockee(), me.getName() + " hat dein Lock wieder entfroren.",
"/activelock.html?lockId=" + lockId, de.oaa.xxx.social.entity.MessageCause.GAME_STATE); "/games/chastity/activelock.html?lockId=" + lockId, de.oaa.xxx.social.entity.MessageCause.GAME_STATE);
return ResponseEntity.noContent().build(); return ResponseEntity.noContent().build();
} }
@@ -1491,10 +1415,7 @@ public class CardLockController {
@Transactional @Transactional
@PostMapping("/as-keyholder/{lockId}/request-unlock") @PostMapping("/as-keyholder/{lockId}/request-unlock")
public ResponseEntity<?> requestUnlock(@PathVariable UUID lockId, Principal principal) { public ResponseEntity<?> requestUnlock(@PathVariable UUID lockId, Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty())
return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
var lockOpt = cardlockRepository.findById(lockId); var lockOpt = cardlockRepository.findById(lockId);
if (lockOpt.isEmpty()) if (lockOpt.isEmpty())
@@ -1508,7 +1429,7 @@ public class CardLockController {
sendMessage(myId, l.getLockee(), sendMessage(myId, l.getLockee(),
"Dein Keyholder hat das Lock freigeschaltet. Du erhältst beim nächsten Laden deinen Entsperrcode.", "Dein Keyholder hat das Lock freigeschaltet. Du erhältst beim nächsten Laden deinen Entsperrcode.",
"/activelock.html?lockId=" + lockId, de.oaa.xxx.social.entity.MessageCause.GAME_STATE); "/games/chastity/activelock.html?lockId=" + lockId, de.oaa.xxx.social.entity.MessageCause.GAME_STATE);
return ResponseEntity.noContent().build(); return ResponseEntity.noContent().build();
} }
@@ -1516,10 +1437,7 @@ public class CardLockController {
@Transactional @Transactional
@PostMapping("/cardlock/{lockId}/emergency-unlock") @PostMapping("/cardlock/{lockId}/emergency-unlock")
public ResponseEntity<?> requestEmergencyUnlock(@PathVariable UUID lockId, Principal principal) { public ResponseEntity<?> requestEmergencyUnlock(@PathVariable UUID lockId, Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); var me = userService.requireUser(principal);
if (meOpt.isEmpty())
return ResponseEntity.status(401).build();
var me = meOpt.get();
UUID myId = me.getUserId(); UUID myId = me.getUserId();
var lockOpt = cardlockRepository.findById(lockId); var lockOpt = cardlockRepository.findById(lockId);
@@ -1543,7 +1461,7 @@ public class CardLockController {
// Keyholderin benachrichtigen // Keyholderin benachrichtigen
sendMessage(myId, l.getKeyholder(), "⚠️ NOTFALL: " + me.getName() sendMessage(myId, l.getKeyholder(), "⚠️ NOTFALL: " + me.getName()
+ " bittet dringend um Freigabe des Locks. Bitte reagiere innerhalb einer Stunde, sonst öffnet sich das Lock automatisch.", + " bittet dringend um Freigabe des Locks. Bitte reagiere innerhalb einer Stunde, sonst öffnet sich das Lock automatisch.",
"/keyholder.html", de.oaa.xxx.social.entity.MessageCause.EMERGENCY); "/games/chastity/keyholder.html", de.oaa.xxx.social.entity.MessageCause.EMERGENCY);
} }
cardlockRepository.save(l); cardlockRepository.save(l);
return ResponseEntity.noContent().build(); return ResponseEntity.noContent().build();
@@ -1568,19 +1486,19 @@ public class CardLockController {
throws Exception { throws Exception {
var invOpt = invitationRepository.findByToken(token); var invOpt = invitationRepository.findByToken(token);
if (invOpt.isEmpty()) { if (invOpt.isEmpty()) {
response.sendRedirect("/keyholder-invitation-confirmed.html?status=invalid"); response.sendRedirect("/games/chastity/keyholder-invitation-confirmed.html?status=invalid");
return; return;
} }
var inv = invOpt.get(); var inv = invOpt.get();
var lockOpt = cardlockRepository.findById(inv.getLockId()); var lockOpt = cardlockRepository.findById(inv.getLockId());
if (lockOpt.isEmpty()) { if (lockOpt.isEmpty()) {
response.sendRedirect("/keyholder-invitation-confirmed.html?status=invalid"); response.sendRedirect("/games/chastity/keyholder-invitation-confirmed.html?status=invalid");
return; return;
} }
var lock = lockOpt.get(); var lock = lockOpt.get();
lock.setKeyholder(inv.getKeyholderUserId()); lock.setKeyholder(inv.getKeyholderUserId());
cardlockRepository.save(lock); cardlockRepository.save(lock);
invitationRepository.delete(inv); invitationRepository.delete(inv);
response.sendRedirect("/keyholder.html"); response.sendRedirect("/games/chastity/keyholder.html");
} }
} }

View File

@@ -3,7 +3,7 @@ package de.oaa.xxx.games.chastity.cardlock;
import de.oaa.xxx.games.chastity.tasks.Task; import de.oaa.xxx.games.chastity.tasks.Task;
import de.oaa.xxx.games.chastity.tasks.TaskMode; import de.oaa.xxx.games.chastity.tasks.TaskMode;
import de.oaa.xxx.subscription.SubscriptionLimitService; import de.oaa.xxx.subscription.SubscriptionLimitService;
import de.oaa.xxx.user.UserRepository; import de.oaa.xxx.user.UserService;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
@@ -18,16 +18,16 @@ import java.util.stream.Collectors;
public class CardlockTemplateController { public class CardlockTemplateController {
private final CardlockTemplateRepository templateRepository; private final CardlockTemplateRepository templateRepository;
private final UserRepository userRepository; private final UserService userService;
private final TimeLockTemplateRepository timeLockTemplateRepository; private final TimeLockTemplateRepository timeLockTemplateRepository;
private final SubscriptionLimitService limitService; private final SubscriptionLimitService limitService;
public CardlockTemplateController(CardlockTemplateRepository templateRepository, public CardlockTemplateController(CardlockTemplateRepository templateRepository,
UserRepository userRepository, UserService userService,
TimeLockTemplateRepository timeLockTemplateRepository, TimeLockTemplateRepository timeLockTemplateRepository,
SubscriptionLimitService limitService) { SubscriptionLimitService limitService) {
this.templateRepository = templateRepository; this.templateRepository = templateRepository;
this.userRepository = userRepository; this.userService = userService;
this.timeLockTemplateRepository = timeLockTemplateRepository; this.timeLockTemplateRepository = timeLockTemplateRepository;
this.limitService = limitService; this.limitService = limitService;
} }
@@ -65,9 +65,7 @@ public class CardlockTemplateController {
@GetMapping @GetMapping
public ResponseEntity<List<Map<String, Object>>> list(Principal principal) { public ResponseEntity<List<Map<String, Object>>> list(Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
List<Map<String, Object>> result = templateRepository.findByOwner(myId) List<Map<String, Object>> result = templateRepository.findByOwner(myId)
.stream().map(this::toDto).collect(Collectors.toList()); .stream().map(this::toDto).collect(Collectors.toList());
return ResponseEntity.ok(result); return ResponseEntity.ok(result);
@@ -75,9 +73,7 @@ public class CardlockTemplateController {
@PostMapping @PostMapping
public ResponseEntity<Map<String, Object>> create(@RequestBody TemplateRequest req, Principal principal) { public ResponseEntity<Map<String, Object>> create(@RequestBody TemplateRequest req, Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
if (req.pickEveryMinute() == null || req.pickEveryMinute() < 1) if (req.pickEveryMinute() == null || req.pickEveryMinute() < 1)
return ResponseEntity.badRequest().build(); return ResponseEntity.badRequest().build();
@@ -100,9 +96,7 @@ public class CardlockTemplateController {
public ResponseEntity<Map<String, Object>> update(@PathVariable UUID id, public ResponseEntity<Map<String, Object>> update(@PathVariable UUID id,
@RequestBody TemplateRequest req, @RequestBody TemplateRequest req,
Principal principal) { Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
var opt = templateRepository.findById(id); var opt = templateRepository.findById(id);
if (opt.isEmpty()) return ResponseEntity.notFound().build(); if (opt.isEmpty()) return ResponseEntity.notFound().build();
@@ -121,9 +115,7 @@ public class CardlockTemplateController {
@DeleteMapping("/{id}") @DeleteMapping("/{id}")
public ResponseEntity<Void> delete(@PathVariable UUID id, Principal principal) { public ResponseEntity<Void> delete(@PathVariable UUID id, Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
var opt = templateRepository.findById(id); var opt = templateRepository.findById(id);
if (opt.isEmpty()) return ResponseEntity.notFound().build(); if (opt.isEmpty()) return ResponseEntity.notFound().build();

View File

@@ -132,7 +132,7 @@ public abstract class BaseLockService {
userRepository.findById(lock.getKeyholder()).ifPresent(kh -> userRepository.findById(lock.getKeyholder()).ifPresent(kh ->
sendMessage(lock.getLockee(), kh.getUserId(), sendMessage(lock.getLockee(), kh.getUserId(),
"Deine Lockee hat die Hygiene-Öffnung um " + overtime + " Minuten überschritten.", "Deine Lockee hat die Hygiene-Öffnung um " + overtime + " Minuten überschritten.",
"/keyholder.html?lockId=" + lock.getLockId(), "/games/chastity/keyholder.html?lockId=" + lock.getLockId(),
de.oaa.xxx.social.entity.MessageCause.GAME_STATE)); de.oaa.xxx.social.entity.MessageCause.GAME_STATE));
} }
@@ -181,7 +181,7 @@ public abstract class BaseLockService {
userRepository.findById(lock.getKeyholder()) userRepository.findById(lock.getKeyholder())
.ifPresent(kh -> sendMessage(lock.getLockee(), kh.getUserId(), .ifPresent(kh -> sendMessage(lock.getLockee(), kh.getUserId(),
"Deine Lockee hat eine Aufgaben-Karte gezogen wähle eine Aufgabe aus.", "Deine Lockee hat eine Aufgaben-Karte gezogen wähle eine Aufgabe aus.",
"/keyholder.html", de.oaa.xxx.social.entity.MessageCause.GAME_STATE)); "/games/chastity/keyholder.html", de.oaa.xxx.social.entity.MessageCause.GAME_STATE));
} }
protected void startCommunityVote() { protected void startCommunityVote() {

View File

@@ -2,7 +2,7 @@ package de.oaa.xxx.games.chastity.common;
import de.oaa.xxx.games.chastity.cardlock.CardlockTemplateEntity; import de.oaa.xxx.games.chastity.cardlock.CardlockTemplateEntity;
import de.oaa.xxx.games.chastity.timelock.TimeLockTemplateEntity; import de.oaa.xxx.games.chastity.timelock.TimeLockTemplateEntity;
import de.oaa.xxx.user.UserRepository; import de.oaa.xxx.user.UserService;
import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
@@ -16,12 +16,12 @@ import java.util.*;
public class BaseLockTemplateController { public class BaseLockTemplateController {
private final BaseLockTemplateRepository templateRepository; private final BaseLockTemplateRepository templateRepository;
private final UserRepository userRepository; private final UserService userService;
public BaseLockTemplateController(BaseLockTemplateRepository templateRepository, public BaseLockTemplateController(BaseLockTemplateRepository templateRepository,
UserRepository userRepository) { UserService userService) {
this.templateRepository = templateRepository; this.templateRepository = templateRepository;
this.userRepository = userRepository; this.userService = userService;
} }
@GetMapping @GetMapping
@@ -29,9 +29,7 @@ public class BaseLockTemplateController {
@RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "20") int size, @RequestParam(defaultValue = "20") int size,
Principal principal) { Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
var pageable = PageRequest.of(page, Math.min(size, 50), Sort.by("name")); var pageable = PageRequest.of(page, Math.min(size, 50), Sort.by("name"));
var pageResult = templateRepository.findByOwner(myId, pageable); var pageResult = templateRepository.findByOwner(myId, pageable);
@@ -60,9 +58,7 @@ public class BaseLockTemplateController {
@GetMapping("/{id}") @GetMapping("/{id}")
public ResponseEntity<Map<String, Object>> getById(@PathVariable UUID id, Principal principal) { public ResponseEntity<Map<String, Object>> getById(@PathVariable UUID id, Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
var opt = templateRepository.findById(id); var opt = templateRepository.findById(id);
if (opt.isEmpty()) return ResponseEntity.notFound().build(); if (opt.isEmpty()) return ResponseEntity.notFound().build();

View File

@@ -6,6 +6,7 @@ import de.oaa.xxx.games.chastity.timelock.TimeLockTemplateEntity;
import de.oaa.xxx.games.chastity.timelock.TimeLockTemplateRepository; import de.oaa.xxx.games.chastity.timelock.TimeLockTemplateRepository;
import de.oaa.xxx.games.chastity.tasks.TaskMode; import de.oaa.xxx.games.chastity.tasks.TaskMode;
import de.oaa.xxx.user.UserRepository; import de.oaa.xxx.user.UserRepository;
import de.oaa.xxx.user.UserService;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort;
@@ -27,17 +28,20 @@ public class TemplateExploreController {
private final TimeLockTemplateRepository timeLockTemplateRepository; private final TimeLockTemplateRepository timeLockTemplateRepository;
private final CardlockTemplateRepository cardlockTemplateRepository; private final CardlockTemplateRepository cardlockTemplateRepository;
private final UserRepository userRepository; private final UserRepository userRepository;
private final UserService userService;
public TemplateExploreController(BaseLockTemplateRepository templateRepository, public TemplateExploreController(BaseLockTemplateRepository templateRepository,
TemplateSubscriptionRepository subscriptionRepository, TemplateSubscriptionRepository subscriptionRepository,
TimeLockTemplateRepository timeLockTemplateRepository, TimeLockTemplateRepository timeLockTemplateRepository,
CardlockTemplateRepository cardlockTemplateRepository, CardlockTemplateRepository cardlockTemplateRepository,
UserRepository userRepository) { UserRepository userRepository,
UserService userService) {
this.templateRepository = templateRepository; this.templateRepository = templateRepository;
this.subscriptionRepository = subscriptionRepository; this.subscriptionRepository = subscriptionRepository;
this.timeLockTemplateRepository = timeLockTemplateRepository; this.timeLockTemplateRepository = timeLockTemplateRepository;
this.cardlockTemplateRepository = cardlockTemplateRepository; this.cardlockTemplateRepository = cardlockTemplateRepository;
this.userRepository = userRepository; this.userRepository = userRepository;
this.userService = userService;
} }
// ── Öffentliche Vorlagen entdecken ──────────────────────────────────────── // ── Öffentliche Vorlagen entdecken ────────────────────────────────────────
@@ -49,9 +53,7 @@ public class TemplateExploreController {
@RequestParam(defaultValue = "") String q, @RequestParam(defaultValue = "") String q,
Principal principal) { Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
size = Math.min(size, 20); size = Math.min(size, 20);
var pageable = PageRequest.of(page, size, Sort.by(Sort.Direction.DESC, "subscriberCount")); var pageable = PageRequest.of(page, size, Sort.by(Sort.Direction.DESC, "subscriberCount"));
@@ -79,9 +81,7 @@ public class TemplateExploreController {
@GetMapping("/{id}/public") @GetMapping("/{id}/public")
public ResponseEntity<Map<String, Object>> getTemplate( public ResponseEntity<Map<String, Object>> getTemplate(
@PathVariable UUID id, Principal principal) { @PathVariable UUID id, Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
var tOpt = templateRepository.findById(id); var tOpt = templateRepository.findById(id);
if (tOpt.isEmpty()) return ResponseEntity.notFound().build(); if (tOpt.isEmpty()) return ResponseEntity.notFound().build();
@@ -99,9 +99,7 @@ public class TemplateExploreController {
@GetMapping("/mine") @GetMapping("/mine")
public ResponseEntity<List<Map<String, Object>>> getMyTemplates(Principal principal) { public ResponseEntity<List<Map<String, Object>>> getMyTemplates(Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
List<Map<String, Object>> result = templateRepository.findByOwner(myId).stream() List<Map<String, Object>> result = templateRepository.findByOwner(myId).stream()
.map(t -> toPublicDto(t, myId, Set.of())) .map(t -> toPublicDto(t, myId, Set.of()))
@@ -113,9 +111,7 @@ public class TemplateExploreController {
@GetMapping("/subscribed") @GetMapping("/subscribed")
public ResponseEntity<List<Map<String, Object>>> getSubscribedTemplates(Principal principal) { public ResponseEntity<List<Map<String, Object>>> getSubscribedTemplates(Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
List<TemplateSubscriptionEntity> subs = subscriptionRepository.findByUserId(myId); List<TemplateSubscriptionEntity> subs = subscriptionRepository.findByUserId(myId);
Set<UUID> subscribedIds = subs.stream().map(TemplateSubscriptionEntity::getTemplateId).collect(Collectors.toSet()); Set<UUID> subscribedIds = subs.stream().map(TemplateSubscriptionEntity::getTemplateId).collect(Collectors.toSet());
@@ -136,9 +132,7 @@ public class TemplateExploreController {
@PostMapping("/{id}/subscribe") @PostMapping("/{id}/subscribe")
@Transactional @Transactional
public ResponseEntity<Void> subscribe(@PathVariable UUID id, Principal principal) { public ResponseEntity<Void> subscribe(@PathVariable UUID id, Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
var tOpt = templateRepository.findById(id); var tOpt = templateRepository.findById(id);
if (tOpt.isEmpty() || !tOpt.get().isPublished()) return ResponseEntity.notFound().build(); if (tOpt.isEmpty() || !tOpt.get().isPublished()) return ResponseEntity.notFound().build();
@@ -164,9 +158,7 @@ public class TemplateExploreController {
@DeleteMapping("/{id}/subscribe") @DeleteMapping("/{id}/subscribe")
@Transactional @Transactional
public ResponseEntity<Void> unsubscribe(@PathVariable UUID id, Principal principal) { public ResponseEntity<Void> unsubscribe(@PathVariable UUID id, Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
var subOpt = subscriptionRepository.findByUserIdAndTemplateId(myId, id); var subOpt = subscriptionRepository.findByUserIdAndTemplateId(myId, id);
if (subOpt.isEmpty()) return ResponseEntity.noContent().build(); if (subOpt.isEmpty()) return ResponseEntity.noContent().build();
@@ -186,9 +178,7 @@ public class TemplateExploreController {
@PostMapping("/{id}/fork") @PostMapping("/{id}/fork")
@Transactional @Transactional
public ResponseEntity<Map<String, Object>> fork(@PathVariable UUID id, Principal principal) { public ResponseEntity<Map<String, Object>> fork(@PathVariable UUID id, Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
var tOpt = templateRepository.findById(id); var tOpt = templateRepository.findById(id);
if (tOpt.isEmpty() || !tOpt.get().isPublished()) return ResponseEntity.notFound().build(); if (tOpt.isEmpty() || !tOpt.get().isPublished()) return ResponseEntity.notFound().build();
@@ -244,9 +234,7 @@ public class TemplateExploreController {
@PatchMapping("/{id}/publish") @PatchMapping("/{id}/publish")
@Transactional @Transactional
public ResponseEntity<Void> publish(@PathVariable UUID id, Principal principal) { public ResponseEntity<Void> publish(@PathVariable UUID id, Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); var me = userService.requireUser(principal);
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
var me = meOpt.get();
var tOpt = templateRepository.findById(id); var tOpt = templateRepository.findById(id);
if (tOpt.isEmpty()) return ResponseEntity.notFound().build(); if (tOpt.isEmpty()) return ResponseEntity.notFound().build();
@@ -264,9 +252,7 @@ public class TemplateExploreController {
@DeleteMapping("/{id}/publish") @DeleteMapping("/{id}/publish")
@Transactional @Transactional
public ResponseEntity<Void> unpublish(@PathVariable UUID id, Principal principal) { public ResponseEntity<Void> unpublish(@PathVariable UUID id, Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
var tOpt = templateRepository.findById(id); var tOpt = templateRepository.findById(id);
if (tOpt.isEmpty()) return ResponseEntity.notFound().build(); if (tOpt.isEmpty()) return ResponseEntity.notFound().build();

View File

@@ -14,22 +14,22 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import de.oaa.xxx.games.chastity.common.BaseLockRepository; import de.oaa.xxx.games.chastity.common.BaseLockRepository;
import de.oaa.xxx.user.UserRepository; import de.oaa.xxx.user.UserService;
@RestController @RestController
@RequestMapping("/games/chastity/community/taskvote") @RequestMapping("/games/chastity/community/taskvote")
public class CommunityTaskVoteController { public class CommunityTaskVoteController {
private final UserRepository userRepository; private final UserService userService;
private final CommunityTaskVoteRepository taskVoteRepository; private final CommunityTaskVoteRepository taskVoteRepository;
private final CommunityTaskVoteEntryRepository taskVoteEntryRepository; private final CommunityTaskVoteEntryRepository taskVoteEntryRepository;
private final BaseLockRepository baseLockRepository; private final BaseLockRepository baseLockRepository;
public CommunityTaskVoteController(UserRepository userRepository, public CommunityTaskVoteController(UserService userService,
CommunityTaskVoteRepository taskVoteRepository, CommunityTaskVoteRepository taskVoteRepository,
CommunityTaskVoteEntryRepository taskVoteEntryRepository, CommunityTaskVoteEntryRepository taskVoteEntryRepository,
BaseLockRepository baseLockRepository) { BaseLockRepository baseLockRepository) {
this.userRepository = userRepository; this.userService = userService;
this.taskVoteRepository = taskVoteRepository; this.taskVoteRepository = taskVoteRepository;
this.taskVoteEntryRepository = taskVoteEntryRepository; this.taskVoteEntryRepository = taskVoteEntryRepository;
this.baseLockRepository = baseLockRepository; this.baseLockRepository = baseLockRepository;
@@ -37,10 +37,7 @@ public class CommunityTaskVoteController {
@GetMapping("/{displayId}") @GetMapping("/{displayId}")
public ResponseEntity<CommunityTaskVoteDisplayDTO> getVote(@PathVariable UUID displayId, Principal principal) { public ResponseEntity<CommunityTaskVoteDisplayDTO> getVote(@PathVariable UUID displayId, Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty())
return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
var voteOpt = taskVoteRepository.findById(displayId); var voteOpt = taskVoteRepository.findById(displayId);
if (voteOpt.isEmpty() || !voteOpt.get().isActive()) { if (voteOpt.isEmpty() || !voteOpt.get().isActive()) {
@@ -74,10 +71,7 @@ public class CommunityTaskVoteController {
@Transactional @Transactional
public ResponseEntity<Void> castVote(@PathVariable UUID displayId, @PathVariable int taskIndex, public ResponseEntity<Void> castVote(@PathVariable UUID displayId, @PathVariable int taskIndex,
Principal principal) { Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty())
return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
var voteOpt = taskVoteRepository.findById(displayId); var voteOpt = taskVoteRepository.findById(displayId);
if (voteOpt.isEmpty()) if (voteOpt.isEmpty())

View File

@@ -104,7 +104,7 @@ public class CommunityTaskVoteScheduler {
sendMessage(lock.getLockee(), sendMessage(lock.getLockee(),
"Die Community hat für deine Aufgabe abgestimmt: \"" + task.getTitle() + "\"", "Die Community hat für deine Aufgabe abgestimmt: \"" + task.getTitle() + "\"",
"/activelock.html?lockId=" + lock.getLockId()); "/games/chastity/activelock.html?lockId=" + lock.getLockId());
} }
} }

View File

@@ -17,6 +17,7 @@ import org.springframework.web.bind.annotation.RestController;
import de.oaa.xxx.games.chastity.common.CodeCreator; import de.oaa.xxx.games.chastity.common.CodeCreator;
import de.oaa.xxx.user.UserRepository; import de.oaa.xxx.user.UserRepository;
import de.oaa.xxx.user.UserService;
@RestController @RestController
@RequestMapping("/games/chastity/community/verification") @RequestMapping("/games/chastity/community/verification")
@@ -26,12 +27,16 @@ public class CommunityVerificationController {
private final CommunityVerificationRepository verificationRepository; private final CommunityVerificationRepository verificationRepository;
private final CommunityVerificationVoteRepository verificationVoteRepository; private final CommunityVerificationVoteRepository verificationVoteRepository;
private final UserRepository userRepository; private final UserRepository userRepository;
private final UserService userService;
public CommunityVerificationController(CommunityVerificationRepository verificationRepository, public CommunityVerificationController(CommunityVerificationRepository verificationRepository,
CommunityVerificationVoteRepository verificationVoteRepository, UserRepository userRepository) { CommunityVerificationVoteRepository verificationVoteRepository,
UserRepository userRepository,
UserService userService) {
this.verificationRepository = verificationRepository; this.verificationRepository = verificationRepository;
this.verificationVoteRepository = verificationVoteRepository; this.verificationVoteRepository = verificationVoteRepository;
this.userRepository = userRepository; this.userRepository = userRepository;
this.userService = userService;
} }
@GetMapping("/{displayId}") @GetMapping("/{displayId}")
@@ -78,10 +83,7 @@ public class CommunityVerificationController {
@PutMapping("/{displayId}") @PutMapping("/{displayId}")
public ResponseEntity<Void> update(@PathVariable UUID displayId, @RequestBody CommunityVerificationDTO dto, public ResponseEntity<Void> update(@PathVariable UUID displayId, @RequestBody CommunityVerificationDTO dto,
Principal principal) { Principal principal) {
var user = userRepository.findByEmail(principal.getName()).orElse(null); userService.requireUser(principal);
if (user == null) {
return ResponseEntity.status(401).build();
}
var entity = verificationRepository.findById(displayId).orElse(null); var entity = verificationRepository.findById(displayId).orElse(null);
if (entity == null) { if (entity == null) {
return ResponseEntity.notFound().build(); return ResponseEntity.notFound().build();
@@ -99,8 +101,7 @@ public class CommunityVerificationController {
@PostMapping("/{verificationId}/vote/") @PostMapping("/{verificationId}/vote/")
public ResponseEntity<Void> addVote(@PathVariable UUID verificationId, @RequestBody CommunityVerificationVoteDTO dto, public ResponseEntity<Void> addVote(@PathVariable UUID verificationId, @RequestBody CommunityVerificationVoteDTO dto,
Principal principal) { Principal principal) {
var user = userRepository.findByEmail(principal.getName()).orElse(null); var user = userService.requireUser(principal);
if (user == null) return ResponseEntity.status(401).build();
if (!verificationRepository.existsById(verificationId)) return ResponseEntity.notFound().build(); if (!verificationRepository.existsById(verificationId)) return ResponseEntity.notFound().build();
var vEntity = verificationRepository.findById(verificationId).orElse(null); var vEntity = verificationRepository.findById(verificationId).orElse(null);

View File

@@ -37,9 +37,9 @@ import de.oaa.xxx.games.chastity.timelock.TimeLockRepository;
import de.oaa.xxx.games.chastity.timelock.TimeLockServiceFactory; import de.oaa.xxx.games.chastity.timelock.TimeLockServiceFactory;
import de.oaa.xxx.games.chastity.timelock.TimeLockTemplateEntity; import de.oaa.xxx.games.chastity.timelock.TimeLockTemplateEntity;
import de.oaa.xxx.social.SystemMessageService; import de.oaa.xxx.social.SystemMessageService;
import de.oaa.xxx.social.entity.MessageCause;
import de.oaa.xxx.subscription.SubscriptionLimitService; import de.oaa.xxx.subscription.SubscriptionLimitService;
import de.oaa.xxx.user.UserRepository; import de.oaa.xxx.user.UserRepository;
import de.oaa.xxx.user.UserService;
@RestController @RestController
@RequestMapping("/keyholder-offers") @RequestMapping("/keyholder-offers")
@@ -56,6 +56,7 @@ public class KeyholderOfferController {
private final KeyholderInvitationRepository invitationRepository; private final KeyholderInvitationRepository invitationRepository;
private final SystemMessageService systemMessageService; private final SystemMessageService systemMessageService;
private final SubscriptionLimitService subscriptionLimitService; private final SubscriptionLimitService subscriptionLimitService;
private final UserService userService;
public KeyholderOfferController( public KeyholderOfferController(
KeyholderOfferRepository offerRepository, KeyholderOfferRepository offerRepository,
@@ -68,7 +69,8 @@ public class KeyholderOfferController {
TimeLockServiceFactory timeLockServiceFactory, TimeLockServiceFactory timeLockServiceFactory,
KeyholderInvitationRepository invitationRepository, KeyholderInvitationRepository invitationRepository,
SystemMessageService systemMessageService, SystemMessageService systemMessageService,
SubscriptionLimitService subscriptionLimitService) { SubscriptionLimitService subscriptionLimitService,
UserService userService) {
this.offerRepository = offerRepository; this.offerRepository = offerRepository;
this.templateRepository = templateRepository; this.templateRepository = templateRepository;
this.subscriptionRepository = subscriptionRepository; this.subscriptionRepository = subscriptionRepository;
@@ -80,15 +82,14 @@ public class KeyholderOfferController {
this.invitationRepository = invitationRepository; this.invitationRepository = invitationRepository;
this.systemMessageService = systemMessageService; this.systemMessageService = systemMessageService;
this.subscriptionLimitService = subscriptionLimitService; this.subscriptionLimitService = subscriptionLimitService;
this.userService = userService;
} }
// ── Eigene Angebote ─────────────────────────────────────────────────────── // ── Eigene Angebote ───────────────────────────────────────────────────────
@GetMapping("/mine") @GetMapping("/mine")
public ResponseEntity<List<Map<String, Object>>> getMyOffers(Principal principal) { public ResponseEntity<List<Map<String, Object>>> getMyOffers(Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
List<Map<String, Object>> result = offerRepository.findByOffererId(myId).stream() List<Map<String, Object>> result = offerRepository.findByOffererId(myId).stream()
.map(this::toDto) .map(this::toDto)
@@ -102,9 +103,7 @@ public class KeyholderOfferController {
@Transactional @Transactional
public ResponseEntity<Map<String, Object>> createOffer( public ResponseEntity<Map<String, Object>> createOffer(
@RequestBody CreateOfferRequest req, Principal principal) { @RequestBody CreateOfferRequest req, Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); var me = userService.requireUser(principal);
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
var me = meOpt.get();
UUID myId = me.getUserId(); UUID myId = me.getUserId();
// Limit prüfen // Limit prüfen
@@ -148,9 +147,7 @@ public class KeyholderOfferController {
@PathVariable UUID id, @PathVariable UUID id,
@RequestBody CreateOfferRequest req, @RequestBody CreateOfferRequest req,
Principal principal) { Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
var offerOpt = offerRepository.findById(id); var offerOpt = offerRepository.findById(id);
if (offerOpt.isEmpty()) return ResponseEntity.notFound().build(); if (offerOpt.isEmpty()) return ResponseEntity.notFound().build();
@@ -181,9 +178,7 @@ public class KeyholderOfferController {
@DeleteMapping("/{id}") @DeleteMapping("/{id}")
@Transactional @Transactional
public ResponseEntity<Void> deleteOffer(@PathVariable UUID id, Principal principal) { public ResponseEntity<Void> deleteOffer(@PathVariable UUID id, Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
var offerOpt = offerRepository.findById(id); var offerOpt = offerRepository.findById(id);
if (offerOpt.isEmpty()) return ResponseEntity.notFound().build(); if (offerOpt.isEmpty()) return ResponseEntity.notFound().build();
@@ -198,9 +193,7 @@ public class KeyholderOfferController {
@GetMapping("/user/{userId}") @GetMapping("/user/{userId}")
public ResponseEntity<List<Map<String, Object>>> getOffersForUser( public ResponseEntity<List<Map<String, Object>>> getOffersForUser(
@PathVariable UUID userId, Principal principal) { @PathVariable UUID userId, Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
List<Map<String, Object>> result = offerRepository.findByOffererId(userId).stream() List<Map<String, Object>> result = offerRepository.findByOffererId(userId).stream()
.map(o -> toPublicDto(o, myId)) .map(o -> toPublicDto(o, myId))
@@ -212,9 +205,7 @@ public class KeyholderOfferController {
@GetMapping("/public") @GetMapping("/public")
public ResponseEntity<List<Map<String, Object>>> getPublicOffers(Principal principal) { public ResponseEntity<List<Map<String, Object>>> getPublicOffers(Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); var me = userService.requireUser(principal);
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
var me = meOpt.get();
String userGender = me.getGeschlecht() != null ? me.getGeschlecht().name() : null; String userGender = me.getGeschlecht() != null ? me.getGeschlecht().name() : null;
@@ -244,9 +235,7 @@ public class KeyholderOfferController {
@RequestBody JoinOfferRequest req, @RequestBody JoinOfferRequest req,
Principal principal) { Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); var me = userService.requireUser(principal);
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
var me = meOpt.get();
UUID myId = me.getUserId(); UUID myId = me.getUserId();
var offerOpt = offerRepository.findById(id); var offerOpt = offerRepository.findById(id);
@@ -329,7 +318,6 @@ public class KeyholderOfferController {
return ResponseEntity.status(500).build(); return ResponseEntity.status(500).build();
} }
String lockName = template.getName() != null ? template.getName() : "Unbenanntes Lock";
boolean invitationSent = false; boolean invitationSent = false;
if (!directStart) { if (!directStart) {
// Normaler Einladungsworkflow: Keyholder muss bestätigen // Normaler Einladungsworkflow: Keyholder muss bestätigen
@@ -341,17 +329,11 @@ public class KeyholderOfferController {
inv.setCreatedAt(LocalDateTime.now()); inv.setCreatedAt(LocalDateTime.now());
invitationRepository.save(inv); invitationRepository.save(inv);
systemMessageService.send(myId, offerer.getUserId(), systemMessageService.pushInvitationUpdate(offerer.getUserId());
me.getName() + " möchte dein Keyholder-Angebot annehmen und lädt dich als Keyholder für „"
+ lockName + "\" ein.",
"/einladungen.html", MessageCause.INVITATION);
invitationSent = true; invitationSent = true;
} else { } else {
// Direktstart: Keyholder wird direkt gesetzt, aber trotzdem benachrichtigen // Direktstart: Keyholder wird direkt gesetzt
systemMessageService.send(myId, offerer.getUserId(), systemMessageService.pushInvitationUpdate(offerer.getUserId());
me.getName() + " hat dein Keyholder-Angebot angenommen und das Lock „"
+ lockName + "\" gestartet.",
"/keyholder.html", MessageCause.INVITATION);
} }
// Annahmezähler erhöhen // Annahmezähler erhöhen

View File

@@ -22,6 +22,7 @@ import de.oaa.xxx.games.chastity.tasks.AssignedTaskRepository;
import de.oaa.xxx.games.chastity.tasks.Task; import de.oaa.xxx.games.chastity.tasks.Task;
import de.oaa.xxx.social.SystemMessageService; import de.oaa.xxx.social.SystemMessageService;
import de.oaa.xxx.user.UserRepository; import de.oaa.xxx.user.UserRepository;
import de.oaa.xxx.user.UserService;
@RestController @RestController
@RequestMapping("/games/chastity/keyholder/choices") @RequestMapping("/games/chastity/keyholder/choices")
@@ -32,26 +33,27 @@ public class KeyholderTaskChoiceController {
private final KeyholderTaskChoiceRepository keyholderTaskChoiceRepository; private final KeyholderTaskChoiceRepository keyholderTaskChoiceRepository;
private final AssignedTaskRepository assignedTaskRepository; private final AssignedTaskRepository assignedTaskRepository;
private final SystemMessageService systemMessageService; private final SystemMessageService systemMessageService;
private final UserService userService;
public KeyholderTaskChoiceController(CardlockRepository cardlockRepository, public KeyholderTaskChoiceController(CardlockRepository cardlockRepository,
UserRepository userRepository, UserRepository userRepository,
KeyholderTaskChoiceRepository keyholderTaskChoiceRepository, KeyholderTaskChoiceRepository keyholderTaskChoiceRepository,
AssignedTaskRepository assignedTaskRepository, AssignedTaskRepository assignedTaskRepository,
SystemMessageService systemMessageService) { SystemMessageService systemMessageService,
UserService userService) {
this.cardlockRepository = cardlockRepository; this.cardlockRepository = cardlockRepository;
this.userRepository = userRepository; this.userRepository = userRepository;
this.keyholderTaskChoiceRepository = keyholderTaskChoiceRepository; this.keyholderTaskChoiceRepository = keyholderTaskChoiceRepository;
this.assignedTaskRepository = assignedTaskRepository; this.assignedTaskRepository = assignedTaskRepository;
this.systemMessageService = systemMessageService; this.systemMessageService = systemMessageService;
this.userService = userService;
} }
// ── Keyholder: ausstehende Aufgaben-Karten-Entscheidungen ───────────────── // ── Keyholder: ausstehende Aufgaben-Karten-Entscheidungen ─────────────────
@GetMapping @GetMapping
@Transactional(readOnly = true) @Transactional(readOnly = true)
public ResponseEntity<List<Map<String, Object>>> getPendingChoices(Principal principal) { public ResponseEntity<List<Map<String, Object>>> getPendingChoices(Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
// Alle Locks bei denen ich Keyholder bin // Alle Locks bei denen ich Keyholder bin
var locks = cardlockRepository.findByKeyholderAndUnlockTimeIsNull(myId); var locks = cardlockRepository.findByKeyholderAndUnlockTimeIsNull(myId);
@@ -86,9 +88,7 @@ public class KeyholderTaskChoiceController {
@PathVariable int taskIndex, @PathVariable int taskIndex,
@org.springframework.web.bind.annotation.RequestBody(required = false) PenaltyRequest penalty, @org.springframework.web.bind.annotation.RequestBody(required = false) PenaltyRequest penalty,
Principal principal) { Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
var choiceOpt = keyholderTaskChoiceRepository.findById(choiceId); var choiceOpt = keyholderTaskChoiceRepository.findById(choiceId);
if (choiceOpt.isEmpty()) return ResponseEntity.notFound().build(); if (choiceOpt.isEmpty()) return ResponseEntity.notFound().build();
@@ -126,7 +126,7 @@ public class KeyholderTaskChoiceController {
sendMessage(myId, lock.getLockee(), sendMessage(myId, lock.getLockee(),
"Dein Keyholder hat eine Aufgabe für dich ausgewählt.", "Dein Keyholder hat eine Aufgabe für dich ausgewählt.",
"/activelock.html?lockId=" + lock.getLockId()); "/games/chastity/activelock.html?lockId=" + lock.getLockId());
return ResponseEntity.noContent().build(); return ResponseEntity.noContent().build();
} }

View File

@@ -81,12 +81,12 @@ public class KeyholderTaskChoiceScheduler {
sendMessage(lock.getLockee(), sendMessage(lock.getLockee(),
"Dein Keyholder hat nicht rechtzeitig gewählt eine zufällige Aufgabe wurde vergeben: \"" + task.getTitle() + "\"", "Dein Keyholder hat nicht rechtzeitig gewählt eine zufällige Aufgabe wurde vergeben: \"" + task.getTitle() + "\"",
"/activelock.html?lockId=" + lock.getLockId()); "/games/chastity/activelock.html?lockId=" + lock.getLockId());
if (lock.getKeyholder() != null) { if (lock.getKeyholder() != null) {
sendMessage(lock.getKeyholder(), sendMessage(lock.getKeyholder(),
"Du hast die Aufgabenwahl für \"" + (lock.getName() != null ? lock.getName() : "ein Schloss") + "\" nicht rechtzeitig getroffen. Eine zufällige Aufgabe wurde vergeben.", "Du hast die Aufgabenwahl für \"" + (lock.getName() != null ? lock.getName() : "ein Schloss") + "\" nicht rechtzeitig getroffen. Eine zufällige Aufgabe wurde vergeben.",
"/keyholder.html"); "/games/chastity/keyholder.html");
} }
} }
} }

View File

@@ -30,6 +30,7 @@ import de.oaa.xxx.games.chastity.timelock.TimeLockEntity;
import de.oaa.xxx.games.chastity.timelock.TimeLockRepository; import de.oaa.xxx.games.chastity.timelock.TimeLockRepository;
import de.oaa.xxx.social.SystemMessageService; import de.oaa.xxx.social.SystemMessageService;
import de.oaa.xxx.user.UserRepository; import de.oaa.xxx.user.UserRepository;
import de.oaa.xxx.user.UserService;
@RestController @RestController
@RequestMapping("/lockee") @RequestMapping("/lockee")
@@ -41,6 +42,7 @@ public class LockeeInvitationController {
private final TimeLockRepository timeLockRepository; private final TimeLockRepository timeLockRepository;
private final UserRepository userRepository; private final UserRepository userRepository;
private final SystemMessageService systemMessageService; private final SystemMessageService systemMessageService;
private final UserService userService;
@Value("${app.base-url:http://localhost:8080}") @Value("${app.base-url:http://localhost:8080}")
private String baseUrl; private String baseUrl;
@@ -52,17 +54,15 @@ public class LockeeInvitationController {
BaseLockRepository baseLockRepository, BaseLockRepository baseLockRepository,
TimeLockRepository timeLockRepository, TimeLockRepository timeLockRepository,
UserRepository userRepository, UserRepository userRepository,
SystemMessageService systemMessageService) { SystemMessageService systemMessageService,
UserService userService) {
this.lockeeInvitationRepository = lockeeInvitationRepository; this.lockeeInvitationRepository = lockeeInvitationRepository;
this.cardlockRepository = cardlockRepository; this.cardlockRepository = cardlockRepository;
this.baseLockRepository = baseLockRepository; this.baseLockRepository = baseLockRepository;
this.timeLockRepository = timeLockRepository; this.timeLockRepository = timeLockRepository;
this.userRepository = userRepository; this.userRepository = userRepository;
this.systemMessageService = systemMessageService; this.systemMessageService = systemMessageService;
} this.userService = userService;
private void sendMessage(UUID senderId, UUID receiverId, String text, String targetUrl, de.oaa.xxx.social.entity.MessageCause cause) {
systemMessageService.send(senderId, receiverId, text, targetUrl, cause);
} }
private String generateUnlockCode(int lines) { private String generateUnlockCode(int lines) {
@@ -77,11 +77,22 @@ public class LockeeInvitationController {
return min + new Random().nextInt(max - min); return min + new Random().nextInt(max - min);
} }
@GetMapping("/invitations/mine/count")
public ResponseEntity<Integer> countMyInvitations(Principal principal) {
UUID myId = userService.requireUser(principal).getUserId();
int count = 0;
for (var inv : lockeeInvitationRepository.findByLockeeUserId(myId)) {
var lockOpt = baseLockRepository.findById(inv.getLockId());
if (lockOpt.isEmpty()) continue;
if (lockOpt.get().getStartTime() != null) continue;
count++;
}
return ResponseEntity.ok(count);
}
@GetMapping("/invitations/mine") @GetMapping("/invitations/mine")
public ResponseEntity<List<Map<String, Object>>> getMyInvitations(Principal principal) { public ResponseEntity<List<Map<String, Object>>> getMyInvitations(Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
var invitations = lockeeInvitationRepository.findByLockeeUserId(myId); var invitations = lockeeInvitationRepository.findByLockeeUserId(myId);
List<Map<String, Object>> result = new ArrayList<>(); List<Map<String, Object>> result = new ArrayList<>();
@@ -108,9 +119,7 @@ public class LockeeInvitationController {
@GetMapping("/invitations/sent") @GetMapping("/invitations/sent")
public ResponseEntity<List<Map<String, Object>>> getSentInvitations(Principal principal) { public ResponseEntity<List<Map<String, Object>>> getSentInvitations(Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
var invitations = lockeeInvitationRepository.findByKeyholderUserId(myId); var invitations = lockeeInvitationRepository.findByKeyholderUserId(myId);
List<Map<String, Object>> result = new ArrayList<>(); List<Map<String, Object>> result = new ArrayList<>();
@@ -138,9 +147,7 @@ public class LockeeInvitationController {
@DeleteMapping("/invitations/sent/{token}") @DeleteMapping("/invitations/sent/{token}")
@Transactional @Transactional
public ResponseEntity<Void> cancelSentInvitation(@PathVariable String token, Principal principal) { public ResponseEntity<Void> cancelSentInvitation(@PathVariable String token, Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); var me = userService.requireUser(principal);
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
var me = meOpt.get();
UUID myId = me.getUserId(); UUID myId = me.getUserId();
var invOpt = lockeeInvitationRepository.findByToken(token); var invOpt = lockeeInvitationRepository.findByToken(token);
@@ -154,10 +161,7 @@ public class LockeeInvitationController {
if (lockOpt.isPresent()) { if (lockOpt.isPresent()) {
var lock = lockOpt.get(); var lock = lockOpt.get();
baseLockRepository.delete(lock); baseLockRepository.delete(lock);
String lockName = lock.getName() != null && !lock.getName().isBlank() ? lock.getName() : "Unbenanntes Lock"; systemMessageService.pushInvitationUpdate(inv.getLockeeUserId());
sendMessage(myId, inv.getLockeeUserId(),
me.getName() + " hat die Lockee-Einladung für das Lock „" + lockName + "\" zurückgezogen.",
null, de.oaa.xxx.social.entity.MessageCause.INVITATION);
} }
return ResponseEntity.noContent().build(); return ResponseEntity.noContent().build();
@@ -165,9 +169,7 @@ public class LockeeInvitationController {
@GetMapping("/invitation/{token}") @GetMapping("/invitation/{token}")
public ResponseEntity<Map<String, Object>> getInvitation(@PathVariable String token, Principal principal) { public ResponseEntity<Map<String, Object>> getInvitation(@PathVariable String token, Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
var invOpt = lockeeInvitationRepository.findByToken(token); var invOpt = lockeeInvitationRepository.findByToken(token);
if (invOpt.isEmpty()) return ResponseEntity.notFound().build(); if (invOpt.isEmpty()) return ResponseEntity.notFound().build();
@@ -215,9 +217,7 @@ public class LockeeInvitationController {
public ResponseEntity<Map<String, Object>> acceptInvitation(@PathVariable String token, public ResponseEntity<Map<String, Object>> acceptInvitation(@PathVariable String token,
@RequestBody AcceptRequest req, @RequestBody AcceptRequest req,
Principal principal) { Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); var me = userService.requireUser(principal);
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
var me = meOpt.get();
UUID myId = me.getUserId(); UUID myId = me.getUserId();
var invOpt = lockeeInvitationRepository.findByToken(token); var invOpt = lockeeInvitationRepository.findByToken(token);
@@ -238,7 +238,6 @@ public class LockeeInvitationController {
LocalDateTime now = LocalDateTime.now(); LocalDateTime now = LocalDateTime.now();
String unlockCode; String unlockCode;
String lockName;
if (lock instanceof CardLockEntity cardLock) { if (lock instanceof CardLockEntity cardLock) {
unlockCode = generateUnlockCode(codeLines); unlockCode = generateUnlockCode(codeLines);
@@ -252,7 +251,6 @@ public class LockeeInvitationController {
cardLock.setLastHygineOpening(now); cardLock.setLastHygineOpening(now);
} }
cardlockRepository.save(cardLock); cardlockRepository.save(cardLock);
lockName = cardLock.getName() != null && !cardLock.getName().isBlank() ? cardLock.getName() : "Unbenanntes Lock";
} else if (lock instanceof TimeLockEntity timeLock) { } else if (lock instanceof TimeLockEntity timeLock) {
unlockCode = CodeCreator.createNumeric(codeLines); unlockCode = CodeCreator.createNumeric(codeLines);
int unlockMinutes = randomBetween(timeLock.getMinTimeInMinutes(), timeLock.getMaxTimeInMinutes()); int unlockMinutes = randomBetween(timeLock.getMinTimeInMinutes(), timeLock.getMaxTimeInMinutes());
@@ -264,16 +262,13 @@ public class LockeeInvitationController {
timeLock.setLastHygineOpening(now); timeLock.setLastHygineOpening(now);
} }
timeLockRepository.save(timeLock); timeLockRepository.save(timeLock);
lockName = timeLock.getName() != null && !timeLock.getName().isBlank() ? timeLock.getName() : "Unbenanntes Lock";
} else { } else {
return ResponseEntity.status(500).build(); return ResponseEntity.status(500).build();
} }
lockeeInvitationRepository.delete(inv); lockeeInvitationRepository.delete(inv);
sendMessage(myId, inv.getKeyholderUserId(), systemMessageService.pushInvitationUpdate(inv.getKeyholderUserId());
me.getName() + " hat die Einladung als Lockee für das Lock „" + lockName + "\" angenommen.",
"/keyholder.html", de.oaa.xxx.social.entity.MessageCause.INVITATION);
return ResponseEntity.ok(Map.of( return ResponseEntity.ok(Map.of(
"lockId", lock.getLockId().toString(), "lockId", lock.getLockId().toString(),
@@ -284,9 +279,7 @@ public class LockeeInvitationController {
@DeleteMapping("/invitation/{token}") @DeleteMapping("/invitation/{token}")
@Transactional @Transactional
public ResponseEntity<Void> declineInvitation(@PathVariable String token, Principal principal) { public ResponseEntity<Void> declineInvitation(@PathVariable String token, Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); var me = userService.requireUser(principal);
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
var me = meOpt.get();
UUID myId = me.getUserId(); UUID myId = me.getUserId();
var invOpt = lockeeInvitationRepository.findByToken(token); var invOpt = lockeeInvitationRepository.findByToken(token);
@@ -300,10 +293,7 @@ public class LockeeInvitationController {
if (lockOpt.isPresent()) { if (lockOpt.isPresent()) {
var lock = lockOpt.get(); var lock = lockOpt.get();
baseLockRepository.delete(lock); baseLockRepository.delete(lock);
String lockName = lock.getName() != null && !lock.getName().isBlank() ? lock.getName() : "Unbenanntes Lock"; systemMessageService.pushInvitationUpdate(inv.getKeyholderUserId());
sendMessage(myId, inv.getKeyholderUserId(),
me.getName() + " hat die Einladung als Lockee für das Lock „" + lockName + "\" abgelehnt.",
null, de.oaa.xxx.social.entity.MessageCause.INVITATION);
} }
return ResponseEntity.noContent().build(); return ResponseEntity.noContent().build();

View File

@@ -46,6 +46,7 @@ import de.oaa.xxx.games.chastity.unlock.TempOpeningReason;
import de.oaa.xxx.games.chastity.unlock.UnlockCodeHistoryService; import de.oaa.xxx.games.chastity.unlock.UnlockCodeHistoryService;
import de.oaa.xxx.social.SystemMessageService; import de.oaa.xxx.social.SystemMessageService;
import de.oaa.xxx.user.UserRepository; import de.oaa.xxx.user.UserRepository;
import de.oaa.xxx.user.UserService;
@RestController @RestController
@RequestMapping("/keyholder") @RequestMapping("/keyholder")
@@ -54,6 +55,7 @@ public class TimeLockController {
private final TimeLockRepository timeLockRepository; private final TimeLockRepository timeLockRepository;
private final TimeLockTemplateRepository templateRepository; private final TimeLockTemplateRepository templateRepository;
private final UserRepository userRepository; private final UserRepository userRepository;
private final UserService userService;
private final KeyholderInvitationRepository invitationRepository; private final KeyholderInvitationRepository invitationRepository;
private final LockeeInvitationRepository lockeeInvitationRepository; private final LockeeInvitationRepository lockeeInvitationRepository;
private final SystemMessageService systemMessageService; private final SystemMessageService systemMessageService;
@@ -66,6 +68,7 @@ public class TimeLockController {
public TimeLockController(TimeLockRepository timeLockRepository, public TimeLockController(TimeLockRepository timeLockRepository,
TimeLockTemplateRepository templateRepository, TimeLockTemplateRepository templateRepository,
UserRepository userRepository, UserRepository userRepository,
UserService userService,
KeyholderInvitationRepository invitationRepository, KeyholderInvitationRepository invitationRepository,
LockeeInvitationRepository lockeeInvitationRepository, LockeeInvitationRepository lockeeInvitationRepository,
SystemMessageService systemMessageService, SystemMessageService systemMessageService,
@@ -77,6 +80,7 @@ public class TimeLockController {
this.timeLockRepository = timeLockRepository; this.timeLockRepository = timeLockRepository;
this.templateRepository = templateRepository; this.templateRepository = templateRepository;
this.userRepository = userRepository; this.userRepository = userRepository;
this.userService = userService;
this.invitationRepository = invitationRepository; this.invitationRepository = invitationRepository;
this.lockeeInvitationRepository = lockeeInvitationRepository; this.lockeeInvitationRepository = lockeeInvitationRepository;
this.systemMessageService = systemMessageService; this.systemMessageService = systemMessageService;
@@ -104,9 +108,7 @@ public class TimeLockController {
public ResponseEntity<Map<String, Object>> createTimeLock( public ResponseEntity<Map<String, Object>> createTimeLock(
@RequestBody CreateTimeLockRequest req, Principal principal) { @RequestBody CreateTimeLockRequest req, Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); var me = userService.requireUser(principal);
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
var me = meOpt.get();
UUID myId = me.getUserId(); UUID myId = me.getUserId();
var templateOpt = templateRepository.findById(req.templateId()); var templateOpt = templateRepository.findById(req.templateId());
@@ -138,10 +140,7 @@ public class TimeLockController {
inv.setDetailsVisible(req.lockeeDetailsVisible()); inv.setDetailsVisible(req.lockeeDetailsVisible());
lockeeInvitationRepository.save(inv); lockeeInvitationRepository.save(inv);
String lockName = template.getName() != null ? template.getName() : "Unbenanntes Lock"; systemMessageService.pushInvitationUpdate(lockee.getUserId());
systemMessageService.send(myId, lockee.getUserId(),
me.getName() + " hat dich als Lockee für das Lock „" + lockName + "\" eingeladen.",
"/einladungen.html", de.oaa.xxx.social.entity.MessageCause.INVITATION);
return ResponseEntity.ok(Map.of( return ResponseEntity.ok(Map.of(
"lockId", lock.getLockId().toString(), "lockId", lock.getLockId().toString(),
@@ -176,10 +175,7 @@ public class TimeLockController {
inv.setCreatedAt(LocalDateTime.now()); inv.setCreatedAt(LocalDateTime.now());
invitationRepository.save(inv); invitationRepository.save(inv);
String lockName = template.getName() != null ? template.getName() : "Unbenanntes Lock"; systemMessageService.pushInvitationUpdate(kh.getUserId());
systemMessageService.send(myId, kh.getUserId(),
me.getName() + " hat dich als Keyholder*In für das Lock „" + lockName + "\" eingeladen.",
"/einladungen.html", de.oaa.xxx.social.entity.MessageCause.INVITATION);
keyholderPending = true; keyholderPending = true;
} }
@@ -196,9 +192,7 @@ public class TimeLockController {
@GetMapping("/timelock/{lockId}") @GetMapping("/timelock/{lockId}")
@Transactional @Transactional
public ResponseEntity<Map<String, Object>> getTimeLock(@PathVariable UUID lockId, Principal principal) { public ResponseEntity<Map<String, Object>> getTimeLock(@PathVariable UUID lockId, Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
var lockOpt = timeLockRepository.findById(lockId); var lockOpt = timeLockRepository.findById(lockId);
if (lockOpt.isEmpty()) return ResponseEntity.notFound().build(); if (lockOpt.isEmpty()) return ResponseEntity.notFound().build();
@@ -381,9 +375,7 @@ public class TimeLockController {
@PostMapping("/timelock/{lockId}/spin") @PostMapping("/timelock/{lockId}/spin")
@Transactional @Transactional
public ResponseEntity<Map<String, Object>> spin(@PathVariable UUID lockId, Principal principal) { public ResponseEntity<Map<String, Object>> spin(@PathVariable UUID lockId, Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
var lockOpt = timeLockRepository.findById(lockId); var lockOpt = timeLockRepository.findById(lockId);
if (lockOpt.isEmpty()) return ResponseEntity.notFound().build(); if (lockOpt.isEmpty()) return ResponseEntity.notFound().build();
@@ -433,9 +425,7 @@ public class TimeLockController {
@PostMapping("/timelock/{lockId}/relock") @PostMapping("/timelock/{lockId}/relock")
@Transactional @Transactional
public ResponseEntity<Void> relock(@PathVariable UUID lockId, Principal principal) { public ResponseEntity<Void> relock(@PathVariable UUID lockId, Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
var lockOpt = timeLockRepository.findById(lockId); var lockOpt = timeLockRepository.findById(lockId);
if (lockOpt.isEmpty()) return ResponseEntity.notFound().build(); if (lockOpt.isEmpty()) return ResponseEntity.notFound().build();
@@ -451,9 +441,7 @@ public class TimeLockController {
@PostMapping("/timelock/{lockId}/task/done") @PostMapping("/timelock/{lockId}/task/done")
@Transactional @Transactional
public ResponseEntity<Void> taskDone(@PathVariable UUID lockId, Principal principal) { public ResponseEntity<Void> taskDone(@PathVariable UUID lockId, Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
var lockOpt = timeLockRepository.findById(lockId); var lockOpt = timeLockRepository.findById(lockId);
if (lockOpt.isEmpty()) return ResponseEntity.notFound().build(); if (lockOpt.isEmpty()) return ResponseEntity.notFound().build();
@@ -475,9 +463,7 @@ public class TimeLockController {
@PostMapping("/timelock/{lockId}/hygiene/start") @PostMapping("/timelock/{lockId}/hygiene/start")
@Transactional @Transactional
public ResponseEntity<Map<String, Object>> startHygiene(@PathVariable UUID lockId, Principal principal) { public ResponseEntity<Map<String, Object>> startHygiene(@PathVariable UUID lockId, Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
var lockOpt = timeLockRepository.findById(lockId); var lockOpt = timeLockRepository.findById(lockId);
if (lockOpt.isEmpty()) return ResponseEntity.notFound().build(); if (lockOpt.isEmpty()) return ResponseEntity.notFound().build();
@@ -500,9 +486,7 @@ public class TimeLockController {
@PostMapping("/timelock/{lockId}/hygiene/end") @PostMapping("/timelock/{lockId}/hygiene/end")
@Transactional @Transactional
public ResponseEntity<Map<String, Object>> endHygiene(@PathVariable UUID lockId, Principal principal) { public ResponseEntity<Map<String, Object>> endHygiene(@PathVariable UUID lockId, Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
var lockOpt = timeLockRepository.findById(lockId); var lockOpt = timeLockRepository.findById(lockId);
if (lockOpt.isEmpty()) return ResponseEntity.notFound().build(); if (lockOpt.isEmpty()) return ResponseEntity.notFound().build();
@@ -524,9 +508,7 @@ public class TimeLockController {
@PostMapping("/timelock/{lockId}/verification/start") @PostMapping("/timelock/{lockId}/verification/start")
@Transactional @Transactional
public ResponseEntity<Map<String, Object>> startVerification(@PathVariable UUID lockId, Principal principal) { public ResponseEntity<Map<String, Object>> startVerification(@PathVariable UUID lockId, Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
var lockOpt = timeLockRepository.findById(lockId); var lockOpt = timeLockRepository.findById(lockId);
if (lockOpt.isEmpty()) return ResponseEntity.notFound().build(); if (lockOpt.isEmpty()) return ResponseEntity.notFound().build();
@@ -548,9 +530,7 @@ public class TimeLockController {
@RequestParam MultipartFile image, @RequestParam MultipartFile image,
Principal principal) throws IOException { Principal principal) throws IOException {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
var lockOpt = timeLockRepository.findById(lockId); var lockOpt = timeLockRepository.findById(lockId);
if (lockOpt.isEmpty()) return ResponseEntity.notFound().build(); if (lockOpt.isEmpty()) return ResponseEntity.notFound().build();
@@ -561,9 +541,10 @@ public class TimeLockController {
if (!found) return ResponseEntity.notFound().build(); if (!found) return ResponseEntity.notFound().build();
if (l.getKeyholder() != null) { if (l.getKeyholder() != null) {
String meName = userRepository.findById(myId).map(u -> u.getName()).orElse("");
systemMessageService.send(myId, l.getKeyholder(), systemMessageService.send(myId, l.getKeyholder(),
"📸 " + meOpt.get().getName() + " hat eine Verifikation eingereicht.", "📸 " + meName + " hat eine Verifikation eingereicht.",
"/keyholder.html", de.oaa.xxx.social.entity.MessageCause.GAME_STATE); "/games/chastity/keyholder.html", de.oaa.xxx.social.entity.MessageCause.GAME_STATE);
} }
return ResponseEntity.noContent().build(); return ResponseEntity.noContent().build();
} }
@@ -573,9 +554,7 @@ public class TimeLockController {
@DeleteMapping("/timelock/{lockId}") @DeleteMapping("/timelock/{lockId}")
@Transactional @Transactional
public ResponseEntity<Void> endLock(@PathVariable UUID lockId, Principal principal) { public ResponseEntity<Void> endLock(@PathVariable UUID lockId, Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
var lockOpt = timeLockRepository.findById(lockId); var lockOpt = timeLockRepository.findById(lockId);
if (lockOpt.isEmpty()) return ResponseEntity.notFound().build(); if (lockOpt.isEmpty()) return ResponseEntity.notFound().build();
@@ -616,9 +595,7 @@ public class TimeLockController {
@PatchMapping("/timelock/{lockId}/unlock-time") @PatchMapping("/timelock/{lockId}/unlock-time")
@Transactional @Transactional
public ResponseEntity<Void> setActualUnlockTime(@PathVariable UUID lockId, Principal principal) { public ResponseEntity<Void> setActualUnlockTime(@PathVariable UUID lockId, Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
var lockOpt = timeLockRepository.findById(lockId); var lockOpt = timeLockRepository.findById(lockId);
if (lockOpt.isEmpty()) return ResponseEntity.notFound().build(); if (lockOpt.isEmpty()) return ResponseEntity.notFound().build();
@@ -639,9 +616,7 @@ public class TimeLockController {
@GetMapping("/timelock/as-keyholder") @GetMapping("/timelock/as-keyholder")
public ResponseEntity<List<Map<String, Object>>> getTimeLocksAsKeyholder(Principal principal) { public ResponseEntity<List<Map<String, Object>>> getTimeLocksAsKeyholder(Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
var locks = timeLockRepository.findByKeyholderAndStartTimeIsNotNullAndUnlockTimeIsNull(myId); var locks = timeLockRepository.findByKeyholderAndStartTimeIsNotNullAndUnlockTimeIsNull(myId);
List<Map<String, Object>> result = new ArrayList<>(); List<Map<String, Object>> result = new ArrayList<>();
@@ -670,9 +645,7 @@ public class TimeLockController {
@GetMapping("/timelock/as-keyholder/{lockId}") @GetMapping("/timelock/as-keyholder/{lockId}")
public ResponseEntity<Map<String, Object>> getTimeLockAsKeyholder( public ResponseEntity<Map<String, Object>> getTimeLockAsKeyholder(
@PathVariable UUID lockId, Principal principal) { @PathVariable UUID lockId, Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
var lockOpt = timeLockRepository.findById(lockId); var lockOpt = timeLockRepository.findById(lockId);
if (lockOpt.isEmpty()) return ResponseEntity.notFound().build(); if (lockOpt.isEmpty()) return ResponseEntity.notFound().build();
@@ -738,9 +711,7 @@ public class TimeLockController {
@Transactional @Transactional
public ResponseEntity<?> requestUnlockAsKeyholder( public ResponseEntity<?> requestUnlockAsKeyholder(
@PathVariable UUID lockId, Principal principal) { @PathVariable UUID lockId, Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
var lockOpt = timeLockRepository.findById(lockId); var lockOpt = timeLockRepository.findById(lockId);
if (lockOpt.isEmpty()) return ResponseEntity.notFound().build(); if (lockOpt.isEmpty()) return ResponseEntity.notFound().build();
@@ -760,9 +731,7 @@ public class TimeLockController {
@Transactional @Transactional
public ResponseEntity<?> freezeTimeLock(@PathVariable UUID lockId, public ResponseEntity<?> freezeTimeLock(@PathVariable UUID lockId,
@RequestBody FreezeRequest req, Principal principal) { @RequestBody FreezeRequest req, Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); var me = userService.requireUser(principal);
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
var me = meOpt.get();
UUID myId = me.getUserId(); UUID myId = me.getUserId();
var lockOpt = timeLockRepository.findById(lockId); var lockOpt = timeLockRepository.findById(lockId);
@@ -788,7 +757,7 @@ public class TimeLockController {
+ until.toLocalDate().format(java.time.format.DateTimeFormatter.ofPattern("dd.MM.yyyy")) + " " + until.toLocalDate().format(java.time.format.DateTimeFormatter.ofPattern("dd.MM.yyyy")) + " "
+ until.toLocalTime().format(java.time.format.DateTimeFormatter.ofPattern("HH:mm")) + until.toLocalTime().format(java.time.format.DateTimeFormatter.ofPattern("HH:mm"))
+ " Uhr eingefroren.", + " Uhr eingefroren.",
"/activetimelock.html?lockId=" + lockId, de.oaa.xxx.social.entity.MessageCause.GAME_STATE); "/games/chastity/activetimelock.html?lockId=" + lockId, de.oaa.xxx.social.entity.MessageCause.GAME_STATE);
return ResponseEntity.noContent().build(); return ResponseEntity.noContent().build();
} }
@@ -796,9 +765,7 @@ public class TimeLockController {
@DeleteMapping("/timelock/as-keyholder/{lockId}/freeze") @DeleteMapping("/timelock/as-keyholder/{lockId}/freeze")
@Transactional @Transactional
public ResponseEntity<?> unfreezeTimeLock(@PathVariable UUID lockId, Principal principal) { public ResponseEntity<?> unfreezeTimeLock(@PathVariable UUID lockId, Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); var me = userService.requireUser(principal);
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
var me = meOpt.get();
UUID myId = me.getUserId(); UUID myId = me.getUserId();
var lockOpt = timeLockRepository.findById(lockId); var lockOpt = timeLockRepository.findById(lockId);
@@ -814,7 +781,7 @@ public class TimeLockController {
systemMessageService.send(myId, l.getLockee(), systemMessageService.send(myId, l.getLockee(),
me.getName() + " hat dein Lock entfroren.", me.getName() + " hat dein Lock entfroren.",
"/activetimelock.html?lockId=" + lockId, de.oaa.xxx.social.entity.MessageCause.GAME_STATE); "/games/chastity/activetimelock.html?lockId=" + lockId, de.oaa.xxx.social.entity.MessageCause.GAME_STATE);
return ResponseEntity.noContent().build(); return ResponseEntity.noContent().build();
} }
@@ -824,9 +791,7 @@ public class TimeLockController {
@PostMapping("/timelock/{lockId}/emergency-unlock") @PostMapping("/timelock/{lockId}/emergency-unlock")
@Transactional @Transactional
public ResponseEntity<?> requestEmergencyUnlock(@PathVariable UUID lockId, Principal principal) { public ResponseEntity<?> requestEmergencyUnlock(@PathVariable UUID lockId, Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); var me = userService.requireUser(principal);
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
var me = meOpt.get();
UUID myId = me.getUserId(); UUID myId = me.getUserId();
var lockOpt = timeLockRepository.findById(lockId); var lockOpt = timeLockRepository.findById(lockId);
@@ -849,7 +814,7 @@ public class TimeLockController {
} else { } else {
systemMessageService.send(myId, l.getKeyholder(), systemMessageService.send(myId, l.getKeyholder(),
"⚠️ NOTFALL: " + me.getName() + " bittet dringend um Freigabe des Locks. Bitte reagiere innerhalb einer Stunde.", "⚠️ NOTFALL: " + me.getName() + " bittet dringend um Freigabe des Locks. Bitte reagiere innerhalb einer Stunde.",
"/keyholder.html", de.oaa.xxx.social.entity.MessageCause.EMERGENCY); "/games/chastity/keyholder.html", de.oaa.xxx.social.entity.MessageCause.EMERGENCY);
timeLockRepository.save(l); timeLockRepository.save(l);
return ResponseEntity.noContent().build(); return ResponseEntity.noContent().build();
} }

View File

@@ -6,7 +6,7 @@ import de.oaa.xxx.games.chastity.spinningwheel.SpinningWheelEntry;
import de.oaa.xxx.games.chastity.tasks.Task; import de.oaa.xxx.games.chastity.tasks.Task;
import de.oaa.xxx.games.chastity.tasks.TaskMode; import de.oaa.xxx.games.chastity.tasks.TaskMode;
import de.oaa.xxx.subscription.SubscriptionLimitService; import de.oaa.xxx.subscription.SubscriptionLimitService;
import de.oaa.xxx.user.UserRepository; import de.oaa.xxx.user.UserService;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
@@ -19,16 +19,16 @@ import java.util.stream.Collectors;
public class TimeLockTemplateController { public class TimeLockTemplateController {
private final TimeLockTemplateRepository templateRepository; private final TimeLockTemplateRepository templateRepository;
private final UserRepository userRepository; private final UserService userService;
private final CardlockTemplateRepository cardlockTemplateRepository; private final CardlockTemplateRepository cardlockTemplateRepository;
private final SubscriptionLimitService limitService; private final SubscriptionLimitService limitService;
public TimeLockTemplateController(TimeLockTemplateRepository templateRepository, public TimeLockTemplateController(TimeLockTemplateRepository templateRepository,
UserRepository userRepository, UserService userService,
CardlockTemplateRepository cardlockTemplateRepository, CardlockTemplateRepository cardlockTemplateRepository,
SubscriptionLimitService limitService) { SubscriptionLimitService limitService) {
this.templateRepository = templateRepository; this.templateRepository = templateRepository;
this.userRepository = userRepository; this.userService = userService;
this.cardlockTemplateRepository = cardlockTemplateRepository; this.cardlockTemplateRepository = cardlockTemplateRepository;
this.limitService = limitService; this.limitService = limitService;
} }
@@ -76,9 +76,7 @@ public class TimeLockTemplateController {
@GetMapping @GetMapping
public ResponseEntity<List<Map<String, Object>>> list(Principal principal) { public ResponseEntity<List<Map<String, Object>>> list(Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
List<Map<String, Object>> result = templateRepository.findByOwner(myId) List<Map<String, Object>> result = templateRepository.findByOwner(myId)
.stream().map(this::toDto).collect(Collectors.toList()); .stream().map(this::toDto).collect(Collectors.toList());
return ResponseEntity.ok(result); return ResponseEntity.ok(result);
@@ -86,9 +84,7 @@ public class TimeLockTemplateController {
@PostMapping @PostMapping
public ResponseEntity<Map<String, Object>> create(@RequestBody TemplateRequest req, Principal principal) { public ResponseEntity<Map<String, Object>> create(@RequestBody TemplateRequest req, Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
if (req.name() == null || req.name().isBlank()) return ResponseEntity.badRequest().build(); if (req.name() == null || req.name().isBlank()) return ResponseEntity.badRequest().build();
if (req.maxTimeInMinutes() == null || req.maxTimeInMinutes() < 1) return ResponseEntity.badRequest().build(); if (req.maxTimeInMinutes() == null || req.maxTimeInMinutes() < 1) return ResponseEntity.badRequest().build();
@@ -109,9 +105,7 @@ public class TimeLockTemplateController {
public ResponseEntity<Map<String, Object>> update(@PathVariable UUID id, public ResponseEntity<Map<String, Object>> update(@PathVariable UUID id,
@RequestBody TemplateRequest req, @RequestBody TemplateRequest req,
Principal principal) { Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
var opt = templateRepository.findById(id); var opt = templateRepository.findById(id);
if (opt.isEmpty()) return ResponseEntity.notFound().build(); if (opt.isEmpty()) return ResponseEntity.notFound().build();
@@ -128,9 +122,7 @@ public class TimeLockTemplateController {
@DeleteMapping("/{id}") @DeleteMapping("/{id}")
public ResponseEntity<Void> delete(@PathVariable UUID id, Principal principal) { public ResponseEntity<Void> delete(@PathVariable UUID id, Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
var opt = templateRepository.findById(id); var opt = templateRepository.findById(id);
if (opt.isEmpty()) return ResponseEntity.notFound().build(); if (opt.isEmpty()) return ResponseEntity.notFound().build();

View File

@@ -164,7 +164,7 @@ public class TTLockCallback {
userRepository.findById(lock.getKeyholder()).ifPresent(kh -> userRepository.findById(lock.getKeyholder()).ifPresent(kh ->
systemMessageService.send(lock.getLockee(), kh.getUserId(), systemMessageService.send(lock.getLockee(), kh.getUserId(),
"Deine Lockee hat ihr Schloss unerlaubt geöffnet!", "Deine Lockee hat ihr Schloss unerlaubt geöffnet!",
"/keyholder.html?lockId=" + lock.getLockId(), "/games/chastity/keyholder.html?lockId=" + lock.getLockId(),
MessageCause.GAME_STATE)); MessageCause.GAME_STATE));
} }

View File

@@ -3,7 +3,6 @@ package de.oaa.xxx.games.common.aufgaben;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import de.oaa.xxx.games.bdsm.Mitspieler;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
@@ -28,7 +27,7 @@ public class Aufgabe {
+ ", sekunden=" + sekundenVon + "-" + sekundenBis + ", gruppeId=" + gruppeId + "]"; + ", sekunden=" + sekundenVon + "-" + sekundenBis + ", gruppeId=" + gruppeId + "]";
} }
public boolean isAufgabePassend(int level, Mitspieler aktiv, Mitspieler passiv) { public boolean isAufgabePassend(int level, CommonMitspieler aktiv, CommonMitspieler passiv) {
if (level != this.level && level - 1 != this.level) { if (level != this.level && level - 1 != this.level) {
return false; return false;
} }

View File

@@ -0,0 +1,6 @@
package de.oaa.xxx.games.common.aufgaben;
public interface CommonMitspieler {
boolean isVerfuegbar(Werkzeug werkzeug);
}

View File

@@ -4,7 +4,6 @@ import java.util.List;
import java.util.UUID; import java.util.UUID;
import de.oaa.xxx.games.bdsm.GeschlechtEnum; import de.oaa.xxx.games.bdsm.GeschlechtEnum;
import de.oaa.xxx.games.bdsm.Mitspieler;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
@@ -26,7 +25,7 @@ public class Finisher {
return "Finisher[id=" + finisherId + ", kurzText=" + kurzText + ", geschlecht=" + geschlecht + ", gruppeId=" + gruppeId + "]"; return "Finisher[id=" + finisherId + ", kurzText=" + kurzText + ", geschlecht=" + geschlecht + ", gruppeId=" + gruppeId + "]";
} }
public boolean isAufgabePassend(Mitspieler aktiv, Mitspieler passiv) { public boolean isAufgabePassend(CommonMitspieler aktiv, CommonMitspieler passiv) {
if (benoetigtPassiv != null) { if (benoetigtPassiv != null) {
for (Werkzeug werkzeug : benoetigtPassiv) { for (Werkzeug werkzeug : benoetigtPassiv) {
if (!passiv.isVerfuegbar(werkzeug)) { if (!passiv.isVerfuegbar(werkzeug)) {

View File

@@ -3,7 +3,7 @@ package de.oaa.xxx.games.common.aufgaben;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import de.oaa.xxx.games.bdsm.Mitspieler; import de.oaa.xxx.games.bdsm.BdsmMitspieler;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
@@ -27,7 +27,7 @@ public class Sperre {
+ ", minuten=" + minutenVon + "-" + minutenBis + ", fuer=" + sperreFuer + ", gruppeId=" + gruppeId + "]"; + ", minuten=" + minutenVon + "-" + minutenBis + ", fuer=" + sperreFuer + ", gruppeId=" + gruppeId + "]";
} }
public boolean isAufgabePassend(Mitspieler passiv) { public boolean isAufgabePassend(BdsmMitspieler passiv) {
for (Werkzeug werkzeug : sperreFuer) { for (Werkzeug werkzeug : sperreFuer) {
if (!passiv.isVerfuegbar(werkzeug)) { if (!passiv.isVerfuegbar(werkzeug)) {
return false; return false;

View File

@@ -3,7 +3,7 @@ package de.oaa.xxx.games.common.aufgaben;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import de.oaa.xxx.games.bdsm.Mitspieler; import de.oaa.xxx.games.bdsm.BdsmMitspieler;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
@@ -22,7 +22,7 @@ public class Strafe {
private List<Werkzeug> benoetigtPassiv; private List<Werkzeug> benoetigtPassiv;
private List<Toy> benoetigteToys; private List<Toy> benoetigteToys;
public boolean isAufgabePassend(int level, Mitspieler aktiv, Mitspieler passiv) { public boolean isAufgabePassend(int level, BdsmMitspieler aktiv, BdsmMitspieler passiv) {
if (level != this.level && level - 1 != this.level) { if (level != this.level && level - 1 != this.level) {
return false; return false;
} }

View File

@@ -3,6 +3,7 @@ package de.oaa.xxx.games.common.repository;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query; import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param; import org.springframework.data.repository.query.Param;
@@ -37,4 +38,10 @@ public interface AufgabenGruppeRepository extends JpaRepository<AufgabenGruppeEn
@Query("SELECT g FROM AufgabenGruppeEntity g WHERE g.privateGruppe = false AND g.userId IS NOT NULL AND g.userId <> :userId AND g.strafen IS EMPTY AND g.sperren IS EMPTY AND (:name IS NULL OR LOWER(g.name) LIKE LOWER(:name))") @Query("SELECT g FROM AufgabenGruppeEntity g WHERE g.privateGruppe = false AND g.userId IS NOT NULL AND g.userId <> :userId AND g.strafen IS EMPTY AND g.sperren IS EMPTY AND (:name IS NULL OR LOWER(g.name) LIKE LOWER(:name))")
List<AufgabenGruppeEntity> findVanillaSafePublicFromOthers(@Param("userId") UUID userId, @Param("name") String name); List<AufgabenGruppeEntity> findVanillaSafePublicFromOthers(@Param("userId") UUID userId, @Param("name") String name);
@Query("SELECT g FROM AufgabenGruppeEntity g WHERE g.userId = :userId AND (g.aufgaben IS NOT EMPTY OR g.finisher IS NOT EMPTY)")
Page<AufgabenGruppeEntity> findByUserIdWithContent(@Param("userId") UUID userId, Pageable pageable);
@Query("SELECT g FROM AufgabenGruppeEntity g WHERE g.userId IS NULL AND (g.aufgaben IS NOT EMPTY OR g.finisher IS NOT EMPTY)")
Page<AufgabenGruppeEntity> findSystemGroupsWithContent(Pageable pageable);
} }

View File

@@ -1,6 +1,10 @@
package de.oaa.xxx.games.common.repository; package de.oaa.xxx.games.common.repository;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import de.oaa.xxx.games.common.entity.AufgabenGruppeEntity; import de.oaa.xxx.games.common.entity.AufgabenGruppeEntity;
import de.oaa.xxx.games.common.entity.GruppenAboEntity; import de.oaa.xxx.games.common.entity.GruppenAboEntity;
@@ -19,4 +23,8 @@ public interface GruppenAboRepository extends JpaRepository<GruppenAboEntity, UU
long countByAufgabenGruppe(AufgabenGruppeEntity gruppe); long countByAufgabenGruppe(AufgabenGruppeEntity gruppe);
void deleteByAufgabenGruppe(AufgabenGruppeEntity gruppe); void deleteByAufgabenGruppe(AufgabenGruppeEntity gruppe);
@Query(value = "SELECT a FROM GruppenAboEntity a JOIN a.aufgabenGruppe g WHERE a.userId = :userId AND g.privateGruppe = false AND (g.aufgaben IS NOT EMPTY OR g.finisher IS NOT EMPTY)",
countQuery = "SELECT COUNT(a) FROM GruppenAboEntity a JOIN a.aufgabenGruppe g WHERE a.userId = :userId AND g.privateGruppe = false AND (g.aufgaben IS NOT EMPTY OR g.finisher IS NOT EMPTY)")
Page<GruppenAboEntity> findByUserIdWithContent(@Param("userId") UUID userId, Pageable pageable);
} }

View File

@@ -1,6 +1,7 @@
package de.oaa.xxx.games.history; package de.oaa.xxx.games.history;
import de.oaa.xxx.user.UserRepository; import de.oaa.xxx.user.UserRepository;
import de.oaa.xxx.user.UserService;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
@@ -20,17 +21,18 @@ public class GameHistoryController {
private final UserRepository userRepository; private final UserRepository userRepository;
private final GameHistoryRepository gameHistoryRepository; private final GameHistoryRepository gameHistoryRepository;
private final UserService userService;
public GameHistoryController(UserRepository userRepository, GameHistoryRepository gameHistoryRepository) { public GameHistoryController(UserRepository userRepository, GameHistoryRepository gameHistoryRepository, UserService userService) {
this.userRepository = userRepository; this.userRepository = userRepository;
this.gameHistoryRepository = gameHistoryRepository; this.gameHistoryRepository = gameHistoryRepository;
this.userService = userService;
} }
@GetMapping @GetMapping
@Transactional(readOnly = true) @Transactional(readOnly = true)
public ResponseEntity<List<Map<String, Object>>> get(@RequestParam UUID userId, Principal principal) { public ResponseEntity<List<Map<String, Object>>> get(@RequestParam UUID userId, Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); userService.requireUser(principal);
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
var result = gameHistoryRepository.findByParticipantUserId(userId).stream() var result = gameHistoryRepository.findByParticipantUserId(userId).stream()
.map(e -> { .map(e -> {

View File

@@ -1,16 +1,16 @@
package de.oaa.xxx.games.vanilla; package de.oaa.xxx.games.vanilla;
import com.fasterxml.jackson.databind.ObjectMapper;
import de.oaa.xxx.games.common.aufgaben.Aufgabe;
import de.oaa.xxx.games.common.aufgaben.AufgabenList;
import de.oaa.xxx.games.bdsm.GeschlechtEnum;
import de.oaa.xxx.games.bdsm.RolleEnum;
import de.oaa.xxx.games.vanilla.entity.VanillaGameEntity;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Random; import java.util.Random;
import com.fasterxml.jackson.databind.ObjectMapper;
import de.oaa.xxx.games.bdsm.GeschlechtEnum;
import de.oaa.xxx.games.common.aufgaben.Aufgabe;
import de.oaa.xxx.games.common.aufgaben.AufgabenList;
import de.oaa.xxx.games.vanilla.entity.VanillaGameEntity;
public class VanillaGameDurchfuehren { public class VanillaGameDurchfuehren {
private final AufgabenList aufgabenList; private final AufgabenList aufgabenList;
@@ -34,13 +34,14 @@ public class VanillaGameDurchfuehren {
public VanillaAufgabeAnzeige getNext() { public VanillaAufgabeAnzeige getNext() {
checkLevel(); checkLevel();
if (level == 6) return null; if (level == 6) return null;
// Fallback
VanillaMitspieler aktiv = findeMitspielerMitRolle();
VanillaMitspieler passiv = findeMitspielerMitRolle(aktiv);
VanillaAufgabeAnzeige anzeige = findeAufgabe(); VanillaAufgabeAnzeige anzeige = findeAufgabe(aktiv, passiv);
if (anzeige != null) return anzeige; if (anzeige != null) return anzeige;
// Fallback
VanillaMitspieler aktiv = findeMitspielerMitRolle(RolleEnum.AUFGABE_AKTIV);
VanillaMitspieler passiv = findeMitspielerMitRolle(RolleEnum.AUFGABE_PASSIV, aktiv);
VanillaAufgabeAnzeige fallback = new VanillaAufgabeAnzeige(); VanillaAufgabeAnzeige fallback = new VanillaAufgabeAnzeige();
fallback.setNameAktiverMitspieler(aktiv != null ? aktiv.getName() : ""); fallback.setNameAktiverMitspieler(aktiv != null ? aktiv.getName() : "");
if (aktiv != null) { fallback.setMitspielerId(aktiv.getId()); fallback.setEigenesGeraet(aktiv.isEigenesGeraet()); } if (aktiv != null) { fallback.setMitspielerId(aktiv.getId()); fallback.setEigenesGeraet(aktiv.isEigenesGeraet()); }
@@ -54,10 +55,10 @@ public class VanillaGameDurchfuehren {
public List<VanillaAufgabeAnzeige> getFinisher() { public List<VanillaAufgabeAnzeige> getFinisher() {
var list = new ArrayList<VanillaAufgabeAnzeige>(); var list = new ArrayList<VanillaAufgabeAnzeige>();
List.of(GeschlechtEnum.WEIBLICH, GeschlechtEnum.DIVERS, GeschlechtEnum.MAENNLICH).forEach(geschlecht -> { List.of(GeschlechtEnum.WEIBLICH, GeschlechtEnum.DIVERS, GeschlechtEnum.MAENNLICH).forEach(geschlecht -> {
mitspieler.stream().filter(m -> geschlecht == m.getGeschlecht()).toList().forEach(cumming -> { mitspieler.stream().forEach(cumming -> {
var partner = findeMitspielerMitRolle(RolleEnum.AUFGABE_PASSIV, cumming); var partner = findeMitspielerMitRolle(cumming);
var finishers = aufgabenList.getFinisher().stream() var finishers = aufgabenList.getFinisher().stream()
.filter(f -> geschlecht == f.getGeschlecht() && f.isAufgabePassend(partner != null ? toMitspielerBdsm(partner) : null, toMitspielerBdsm(cumming))) .filter(f -> geschlecht == f.getGeschlecht() && f.isAufgabePassend(cumming, partner))
.toList(); .toList();
VanillaAufgabeAnzeige anzeige = new VanillaAufgabeAnzeige(); VanillaAufgabeAnzeige anzeige = new VanillaAufgabeAnzeige();
anzeige.setNameAktiverMitspieler(cumming.getName()); anzeige.setNameAktiverMitspieler(cumming.getName());
@@ -82,14 +83,9 @@ public class VanillaGameDurchfuehren {
} }
} }
private VanillaAufgabeAnzeige findeAufgabe() { private VanillaAufgabeAnzeige findeAufgabe(VanillaMitspieler aktiv, VanillaMitspieler passiv) {
VanillaMitspieler aktiv = findeMitspielerMitRolle(RolleEnum.AUFGABE_AKTIV);
if (aktiv == null) return null;
VanillaMitspieler passiv = findeMitspielerMitRolle(RolleEnum.AUFGABE_PASSIV, aktiv);
de.oaa.xxx.games.bdsm.Mitspieler aktivBdsm = toMitspielerBdsm(aktiv);
de.oaa.xxx.games.bdsm.Mitspieler passivBdsm = passiv != null ? toMitspielerBdsm(passiv) : null;
List<Aufgabe> passende = aufgabenList.getAufgaben().stream() List<Aufgabe> passende = aufgabenList.getAufgaben().stream()
.filter(a -> a.isAufgabePassend(level, aktivBdsm, passivBdsm != null ? passivBdsm : aktivBdsm)) .filter(a -> a.isAufgabePassend(level, aktiv, passiv))
.toList(); .toList();
if (passende.isEmpty()) return null; if (passende.isEmpty()) return null;
Aufgabe aufgabe = passende.get(new Random().nextInt(passende.size())); Aufgabe aufgabe = passende.get(new Random().nextInt(passende.size()));
@@ -104,23 +100,18 @@ public class VanillaGameDurchfuehren {
return anzeige; return anzeige;
} }
private VanillaMitspieler findeMitspielerMitRolle(RolleEnum rolle) { return findeMitspielerMitRolle(rolle, null); } private VanillaMitspieler findeMitspielerMitRolle() {
private VanillaMitspieler findeMitspielerMitRolle(RolleEnum rolle, VanillaMitspieler ausschliessen) { return findeMitspielerMitRolle(null);
}
private VanillaMitspieler findeMitspielerMitRolle(VanillaMitspieler ausschliessen) {
List<VanillaMitspieler> kandidaten = mitspieler.stream() List<VanillaMitspieler> kandidaten = mitspieler.stream()
.filter(m -> m.getRollen().contains(rolle) && !m.equals(ausschliessen)) .filter(m -> !m.equals(ausschliessen))
.toList(); .toList();
if (kandidaten.isEmpty()) return null; if (kandidaten.isEmpty()) return null;
return kandidaten.get(new Random().nextInt(kandidaten.size())); return kandidaten.get(new Random().nextInt(kandidaten.size()));
} }
private de.oaa.xxx.games.bdsm.Mitspieler toMitspielerBdsm(VanillaMitspieler vm) {
de.oaa.xxx.games.bdsm.Mitspieler m = new de.oaa.xxx.games.bdsm.Mitspieler();
m.setId(vm.getId()); m.setName(vm.getName()); m.setGeschlecht(vm.getGeschlecht());
m.setRollen(vm.getRollen()); m.setSpieltMit(vm.getSpieltMit());
m.setVerfuegbareWerkzeuge(vm.getVerfuegbareWerkzeuge()); m.setEigenesGeraet(vm.isEigenesGeraet());
return m;
}
private String getAnzeigeText(String text, String aktiv, String passiv) { private String getAnzeigeText(String text, String aktiv, String passiv) {
if (text == null) return ""; if (text == null) return "";
return text.replace("{AKTIV}", aktiv != null ? aktiv : "").replace("{PASSIV}", passiv != null ? passiv : ""); return text.replace("{AKTIV}", aktiv != null ? aktiv : "").replace("{PASSIV}", passiv != null ? passiv : "");

View File

@@ -1,20 +1,17 @@
package de.oaa.xxx.games.vanilla; package de.oaa.xxx.games.vanilla;
import de.oaa.xxx.games.bdsm.GeschlechtEnum;
import de.oaa.xxx.games.bdsm.RolleEnum;
import de.oaa.xxx.games.common.aufgaben.Werkzeug;
import lombok.Getter;
import lombok.Setter;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import de.oaa.xxx.games.common.aufgaben.CommonMitspieler;
import de.oaa.xxx.games.common.aufgaben.Werkzeug;
import lombok.Getter;
import lombok.Setter;
@Getter @Setter @Getter @Setter
public class VanillaMitspieler { public class VanillaMitspieler implements CommonMitspieler {
private UUID id; private UUID id;
private UUID userId; private UUID userId;
private String name; private String name;
private GeschlechtEnum geschlecht;
private List<RolleEnum> rollen;
private List<GeschlechtEnum> spieltMit;
private List<Werkzeug> verfuegbareWerkzeuge; private List<Werkzeug> verfuegbareWerkzeuge;
private boolean eigenesGeraet; private boolean eigenesGeraet;

View File

@@ -1,13 +1,14 @@
package de.oaa.xxx.games.vanilla.controller; package de.oaa.xxx.games.vanilla.controller;
import de.oaa.xxx.games.common.aufgaben.AufgabenGruppe; import java.security.Principal;
import de.oaa.xxx.games.common.aufgaben.AufgabenGruppePage; import java.util.List;
import de.oaa.xxx.games.common.entity.AufgabenGruppeEntity; import java.util.UUID;
import de.oaa.xxx.games.common.entity.GruppenAboEntity;
import de.oaa.xxx.games.common.repository.AufgabenGruppeRepository; import org.slf4j.Logger;
import de.oaa.xxx.games.common.repository.GruppenAboRepository; import org.slf4j.LoggerFactory;
import de.oaa.xxx.user.UserEntity; import org.springframework.data.domain.Page;
import de.oaa.xxx.user.UserRepository; import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.DeleteMapping;
@@ -18,13 +19,14 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import org.slf4j.Logger; import de.oaa.xxx.games.common.aufgaben.AufgabenGruppe;
import org.slf4j.LoggerFactory; import de.oaa.xxx.games.common.aufgaben.AufgabenGruppePage;
import de.oaa.xxx.games.common.entity.AufgabenGruppeEntity;
import java.security.Principal; import de.oaa.xxx.games.common.entity.GruppenAboEntity;
import java.util.Comparator; import de.oaa.xxx.games.common.repository.AufgabenGruppeRepository;
import java.util.List; import de.oaa.xxx.games.common.repository.GruppenAboRepository;
import java.util.UUID; import de.oaa.xxx.user.UserEntity;
import de.oaa.xxx.user.UserService;
@RestController @RestController
@RequestMapping("/vanilla/abo") @RequestMapping("/vanilla/abo")
@@ -37,14 +39,14 @@ public class VanillaAboController {
private final GruppenAboRepository aboRepository; private final GruppenAboRepository aboRepository;
private final AufgabenGruppeRepository gruppeRepository; private final AufgabenGruppeRepository gruppeRepository;
private final UserRepository userRepository; private final UserService userService;
public VanillaAboController(GruppenAboRepository aboRepository, public VanillaAboController(GruppenAboRepository aboRepository,
AufgabenGruppeRepository gruppeRepository, AufgabenGruppeRepository gruppeRepository,
UserRepository userRepository) { UserService userService) {
this.aboRepository = aboRepository; this.aboRepository = aboRepository;
this.gruppeRepository = gruppeRepository; this.gruppeRepository = gruppeRepository;
this.userRepository = userRepository; this.userService = userService;
} }
// ── Abonnierte Gruppen laden (nur vanilla-safe) ── // ── Abonnierte Gruppen laden (nur vanilla-safe) ──
@@ -54,18 +56,19 @@ public class VanillaAboController {
@RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "" + DEFAULT_PAGE_SIZE) int size, @RequestParam(defaultValue = "" + DEFAULT_PAGE_SIZE) int size,
Principal principal) { Principal principal) {
UserEntity user = resolveUser(principal); UserEntity user = userService.requireUser(principal);
if (user == null) return ResponseEntity.status(401).build();
List<AufgabenGruppe> dtos = aboRepository.findByUserId(user.getUserId()).stream() Page<GruppenAboEntity> dbPage = aboRepository.findByUserIdWithContent(
.map(GruppenAboEntity::getAufgabenGruppe) user.getUserId(), PageRequest.of(page, size, Sort.by("aufgabenGruppe.name")));
.filter(g -> !g.isPrivateGruppe()) List<AufgabenGruppe> dtos = dbPage.getContent().stream()
.filter(g -> g.getStrafen().isEmpty() && g.getSperren().isEmpty()) .map(a -> enrich(a.getAufgabenGruppe(), user.getUserId(), true))
.map(g -> enrich(g, user.getUserId(), true))
.sorted(Comparator.comparing(AufgabenGruppe::getName, String.CASE_INSENSITIVE_ORDER))
.toList(); .toList();
AufgabenGruppePage result = new AufgabenGruppePage();
return ResponseEntity.ok(manualPage(dtos, page, size)); result.setContent(dtos);
result.setCurrentPage(dbPage.getNumber());
result.setTotalPages(dbPage.getTotalPages());
result.setTotalElements(dbPage.getTotalElements());
return ResponseEntity.ok(result);
} }
// ── Entdecken (nur vanilla-safe Gruppen von anderen) ── // ── Entdecken (nur vanilla-safe Gruppen von anderen) ──
@@ -76,27 +79,33 @@ public class VanillaAboController {
@RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "" + DISCOVER_PAGE_SIZE) int size, @RequestParam(defaultValue = "" + DISCOVER_PAGE_SIZE) int size,
Principal principal) { Principal principal) {
UserEntity user = resolveUser(principal); UserEntity user = userService.requireUser(principal);
if (user == null) return ResponseEntity.status(401).build();
String namePattern = name != null && !name.isBlank() ? "%" + name.trim() + "%" : null; String namePattern = name != null && !name.isBlank() ? "%" + name.trim() + "%" : null;
List<AufgabenGruppe> dtos = gruppeRepository List<AufgabenGruppe> dtos = gruppeRepository
.findVanillaSafePublicFromOthers(user.getUserId(), namePattern).stream() .findVanillaSafePublicFromOthers(user.getUserId(), namePattern).stream()
.map(g -> enrich(g, user.getUserId(), aboRepository.existsByUserIdAndAufgabenGruppe(user.getUserId(), g))) .map(g -> enrich(g, user.getUserId(), aboRepository.existsByUserIdAndAufgabenGruppe(user.getUserId(), g)))
.sorted(Comparator.comparingLong(AufgabenGruppe::getSubscriberCount).reversed() .sorted(java.util.Comparator.comparingLong(AufgabenGruppe::getSubscriberCount).reversed()
.thenComparing(AufgabenGruppe::getName, String.CASE_INSENSITIVE_ORDER)) .thenComparing(AufgabenGruppe::getName, String.CASE_INSENSITIVE_ORDER))
.toList(); .toList();
return ResponseEntity.ok(manualPage(dtos, page, size)); int total = dtos.size();
int start = page * size;
List<AufgabenGruppe> content = start >= total ? List.of() : dtos.subList(start, Math.min(start + size, total));
AufgabenGruppePage discoverPage = new AufgabenGruppePage();
discoverPage.setContent(content);
discoverPage.setCurrentPage(page);
discoverPage.setTotalPages(total == 0 ? 1 : (int) Math.ceil((double) total / size));
discoverPage.setTotalElements(total);
return ResponseEntity.ok(discoverPage);
} }
// ── Abonnieren (nur vanilla-safe) ── // ── Abonnieren (nur vanilla-safe) ──
@PostMapping("/{gruppenId}") @PostMapping("/{gruppenId}")
public ResponseEntity<Void> subscribe(@PathVariable UUID gruppenId, Principal principal) { public ResponseEntity<Void> subscribe(@PathVariable UUID gruppenId, Principal principal) {
UserEntity user = resolveUser(principal); UserEntity user = userService.requireUser(principal);
if (user == null) return ResponseEntity.status(401).build();
AufgabenGruppeEntity gruppe = gruppeRepository.findById(gruppenId).orElse(null); AufgabenGruppeEntity gruppe = gruppeRepository.findById(gruppenId).orElse(null);
if (gruppe == null || gruppe.isPrivateGruppe() || user.getUserId().equals(gruppe.getUserId())) { if (gruppe == null || gruppe.isPrivateGruppe() || user.getUserId().equals(gruppe.getUserId())) {
@@ -122,8 +131,7 @@ public class VanillaAboController {
@DeleteMapping("/{gruppenId}") @DeleteMapping("/{gruppenId}")
public ResponseEntity<Void> unsubscribe(@PathVariable UUID gruppenId, Principal principal) { public ResponseEntity<Void> unsubscribe(@PathVariable UUID gruppenId, Principal principal) {
UserEntity user = resolveUser(principal); UserEntity user = userService.requireUser(principal);
if (user == null) return ResponseEntity.status(401).build();
AufgabenGruppeEntity gruppe = gruppeRepository.findById(gruppenId).orElse(null); AufgabenGruppeEntity gruppe = gruppeRepository.findById(gruppenId).orElse(null);
if (gruppe == null) return ResponseEntity.noContent().build(); if (gruppe == null) return ResponseEntity.noContent().build();
@@ -142,19 +150,5 @@ public class VanillaAboController {
return g; return g;
} }
private AufgabenGruppePage manualPage(List<AufgabenGruppe> all, int page, int size) {
int total = all.size();
int start = page * size;
List<AufgabenGruppe> content = start >= total ? List.of() : all.subList(start, Math.min(start + size, total));
AufgabenGruppePage result = new AufgabenGruppePage();
result.setContent(content);
result.setCurrentPage(page);
result.setTotalPages(total == 0 ? 1 : (int) Math.ceil((double) total / size));
result.setTotalElements(total);
return result;
}
private UserEntity resolveUser(Principal principal) {
return userRepository.findByEmail(principal.getName()).orElse(null);
}
} }

View File

@@ -9,7 +9,7 @@ import de.oaa.xxx.games.common.repository.AufgabeRepository;
import de.oaa.xxx.games.common.repository.AufgabenGruppeRepository; import de.oaa.xxx.games.common.repository.AufgabenGruppeRepository;
import de.oaa.xxx.games.common.repository.ToyRepository; import de.oaa.xxx.games.common.repository.ToyRepository;
import de.oaa.xxx.subscription.SubscriptionLimitService; import de.oaa.xxx.subscription.SubscriptionLimitService;
import de.oaa.xxx.user.UserRepository; import de.oaa.xxx.user.UserService;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
@@ -39,18 +39,18 @@ public class VanillaAufgabeController {
private final AufgabeRepository aufgabeRepository; private final AufgabeRepository aufgabeRepository;
private final AufgabenGruppeRepository gruppeRepository; private final AufgabenGruppeRepository gruppeRepository;
private final ToyRepository toyRepository; private final ToyRepository toyRepository;
private final UserRepository userRepository; private final UserService userService;
private final SubscriptionLimitService limitService; private final SubscriptionLimitService limitService;
public VanillaAufgabeController(AufgabeRepository aufgabeRepository, public VanillaAufgabeController(AufgabeRepository aufgabeRepository,
AufgabenGruppeRepository gruppeRepository, AufgabenGruppeRepository gruppeRepository,
ToyRepository toyRepository, ToyRepository toyRepository,
UserRepository userRepository, UserService userService,
SubscriptionLimitService limitService) { SubscriptionLimitService limitService) {
this.aufgabeRepository = aufgabeRepository; this.aufgabeRepository = aufgabeRepository;
this.gruppeRepository = gruppeRepository; this.gruppeRepository = gruppeRepository;
this.toyRepository = toyRepository; this.toyRepository = toyRepository;
this.userRepository = userRepository; this.userService = userService;
this.limitService = limitService; this.limitService = limitService;
} }
@@ -70,9 +70,7 @@ public class VanillaAufgabeController {
if (gruppeEntity == null) { if (gruppeEntity == null) {
return ResponseEntity.badRequest().build(); return ResponseEntity.badRequest().build();
} }
var ownerOpt = userRepository.findByEmail(principal.getName()); int limit = limitService.maxTasksPerGroup(userService.requireUser(principal).getUserId());
int limit = ownerOpt.map(u -> limitService.maxTasksPerGroup(u.getUserId()))
.orElse(SubscriptionLimitService.STANDARD_MAX_TASKS_PER_GROUP);
if (gruppeEntity.getAufgaben().size() >= limit) { if (gruppeEntity.getAufgaben().size() >= limit) {
return ResponseEntity.status(409).build(); return ResponseEntity.status(409).build();
} }

View File

@@ -10,7 +10,6 @@ import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
@@ -36,7 +35,7 @@ import de.oaa.xxx.games.common.repository.SperreRepository;
import de.oaa.xxx.games.common.repository.StrafeRepository; import de.oaa.xxx.games.common.repository.StrafeRepository;
import de.oaa.xxx.subscription.SubscriptionLimitService; import de.oaa.xxx.subscription.SubscriptionLimitService;
import de.oaa.xxx.user.UserEntity; import de.oaa.xxx.user.UserEntity;
import de.oaa.xxx.user.UserRepository; import de.oaa.xxx.user.UserService;
@RestController @RestController
@RequestMapping("/vanilla/gruppe") @RequestMapping("/vanilla/gruppe")
@@ -51,29 +50,29 @@ public class VanillaAufgabenGruppeController {
private final StrafeRepository strafeRepository; private final StrafeRepository strafeRepository;
private final SperreRepository sperreRepository; private final SperreRepository sperreRepository;
private final FinisherRepository finisherRepository; private final FinisherRepository finisherRepository;
private final UserRepository userRepository;
private final GruppenAboRepository aboRepository; private final GruppenAboRepository aboRepository;
private final AufgabenGruppeService aufgabenGruppeService; private final AufgabenGruppeService aufgabenGruppeService;
private final SubscriptionLimitService limitService; private final SubscriptionLimitService limitService;
private final UserService userService;
public VanillaAufgabenGruppeController(AufgabenGruppeRepository gruppeRepository, public VanillaAufgabenGruppeController(AufgabenGruppeRepository gruppeRepository,
AufgabeRepository aufgabeRepository, AufgabeRepository aufgabeRepository,
StrafeRepository strafeRepository, StrafeRepository strafeRepository,
SperreRepository sperreRepository, SperreRepository sperreRepository,
FinisherRepository finisherRepository, FinisherRepository finisherRepository,
UserRepository userRepository,
GruppenAboRepository aboRepository, GruppenAboRepository aboRepository,
AufgabenGruppeService aufgabenGruppeService, AufgabenGruppeService aufgabenGruppeService,
SubscriptionLimitService limitService) { SubscriptionLimitService limitService,
UserService userService) {
this.gruppeRepository = gruppeRepository; this.gruppeRepository = gruppeRepository;
this.aufgabeRepository = aufgabeRepository; this.aufgabeRepository = aufgabeRepository;
this.strafeRepository = strafeRepository; this.strafeRepository = strafeRepository;
this.sperreRepository = sperreRepository; this.sperreRepository = sperreRepository;
this.finisherRepository = finisherRepository; this.finisherRepository = finisherRepository;
this.userRepository = userRepository;
this.aboRepository = aboRepository; this.aboRepository = aboRepository;
this.aufgabenGruppeService = aufgabenGruppeService; this.aufgabenGruppeService = aufgabenGruppeService;
this.limitService = limitService; this.limitService = limitService;
this.userService = userService;
} }
// ── Paginierte Listen ── // ── Paginierte Listen ──
@@ -85,18 +84,17 @@ public class VanillaAufgabenGruppeController {
Principal principal) { Principal principal) {
UserEntity user = resolveUser(principal); UserEntity user = resolveUser(principal);
if (user == null) return ResponseEntity.status(401).build(); if (user == null) return ResponseEntity.status(401).build();
// Only vanilla-safe user groups (no Strafen, no Sperren) Page<AufgabenGruppeEntity> dbPage = gruppeRepository.findByUserIdWithContent(
UUID userId = user.getUserId(); user.getUserId(), PageRequest.of(page, size, Sort.by("name")));
String searchPattern = null; AufgabenGruppePage result = new AufgabenGruppePage();
java.util.List<AufgabenGruppeEntity> all = gruppeRepository.listVanillaSafeWithUserAndSearch( result.setContent(dbPage.getContent().stream().map(entity -> {
userId, searchPattern, PageRequest.of(0, 500, Sort.by("name")));
java.util.List<AufgabenGruppeEntity> ownOnly = all.stream()
.filter(g -> userId.equals(g.getUserId())).toList();
AufgabenGruppePage result = manualPage(ownOnly.stream().map(entity -> {
AufgabenGruppe g = entity.toAufgabenGruppe(); AufgabenGruppe g = entity.toAufgabenGruppe();
g.setSubscriberCount(aboRepository.countByAufgabenGruppe(entity)); g.setSubscriberCount(aboRepository.countByAufgabenGruppe(entity));
return g; return g;
}).toList(), page, DEFAULT_PAGE_SIZE); }).toList());
result.setCurrentPage(dbPage.getNumber());
result.setTotalPages(dbPage.getTotalPages());
result.setTotalElements(dbPage.getTotalElements());
return ResponseEntity.ok(result); return ResponseEntity.ok(result);
} }
@@ -104,24 +102,22 @@ public class VanillaAufgabenGruppeController {
public ResponseEntity<AufgabenGruppePage> listSystem( public ResponseEntity<AufgabenGruppePage> listSystem(
@RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "" + DEFAULT_PAGE_SIZE) int size) { @RequestParam(defaultValue = "" + DEFAULT_PAGE_SIZE) int size) {
// Only vanilla-safe system groups (userId IS NULL → no strafen/sperren anyway, but filter for safety) Page<AufgabenGruppeEntity> dbPage = gruppeRepository.findSystemGroupsWithContent(
Page<AufgabenGruppeEntity> result = gruppeRepository.findByUserIdIsNull(
PageRequest.of(page, size, Sort.by("name"))); PageRequest.of(page, size, Sort.by("name")));
AufgabenGruppePage r = new AufgabenGruppePage(); AufgabenGruppePage r = new AufgabenGruppePage();
r.setContent(result.getContent().stream() r.setContent(dbPage.getContent().stream()
.filter(g -> g.getStrafen().isEmpty() && g.getSperren().isEmpty())
.map(AufgabenGruppeEntity::toAufgabenGruppe).toList()); .map(AufgabenGruppeEntity::toAufgabenGruppe).toList());
r.setCurrentPage(result.getNumber()); r.setCurrentPage(dbPage.getNumber());
r.setTotalPages(result.getTotalPages()); r.setTotalPages(dbPage.getTotalPages());
r.setTotalElements(result.getTotalElements()); r.setTotalElements(dbPage.getTotalElements());
return ResponseEntity.ok(r); return ResponseEntity.ok(r);
} }
// ── Bestehende Endpunkte ── // ── Bestehende Endpunkte ──
@GetMapping("/all") @GetMapping("/all")
public ResponseEntity<AufgabenGruppeList> getAll(@RequestParam(required = false) String search) { public ResponseEntity<AufgabenGruppeList> getAll(@RequestParam(required = false) String search, Principal principal) {
UUID userId = (UUID) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); UUID userId = userService.requireUser(principal).getUserId();
String searchPattern = search != null ? "%" + search + "%" : null; String searchPattern = search != null ? "%" + search + "%" : null;
AufgabenGruppeList list = new AufgabenGruppeList(); AufgabenGruppeList list = new AufgabenGruppeList();
list.setGruppen(gruppeRepository.listVanillaSafeWithUserAndSearch(userId, searchPattern, PageRequest.of(0, 500)) list.setGruppen(gruppeRepository.listVanillaSafeWithUserAndSearch(userId, searchPattern, PageRequest.of(0, 500))
@@ -267,18 +263,8 @@ public class VanillaAufgabenGruppeController {
// ── Hilfsmethoden ── // ── Hilfsmethoden ──
private UserEntity resolveUser(Principal principal) { private UserEntity resolveUser(Principal principal) {
return userRepository.findByEmail(principal.getName()).orElse(null); if (principal == null) return null;
return userService.requireUser(principal);
} }
private AufgabenGruppePage manualPage(java.util.List<AufgabenGruppe> all, int page, int size) {
int total = all.size();
int start = page * size;
java.util.List<AufgabenGruppe> content = start >= total ? java.util.List.of() : all.subList(start, Math.min(start + size, total));
AufgabenGruppePage result = new AufgabenGruppePage();
result.setContent(content);
result.setCurrentPage(page);
result.setTotalPages(total == 0 ? 1 : (int) Math.ceil((double) total / size));
result.setTotalElements(total);
return result;
}
} }

View File

@@ -23,9 +23,9 @@ import de.oaa.xxx.games.vanilla.entity.VanillaEinladungEntity;
import de.oaa.xxx.games.vanilla.entity.VanillaEinladungEntity.Status; import de.oaa.xxx.games.vanilla.entity.VanillaEinladungEntity.Status;
import de.oaa.xxx.games.vanilla.repository.VanillaEinladungRepository; import de.oaa.xxx.games.vanilla.repository.VanillaEinladungRepository;
import de.oaa.xxx.social.SystemMessageService; import de.oaa.xxx.social.SystemMessageService;
import de.oaa.xxx.social.entity.MessageCause;
import de.oaa.xxx.social.repository.FriendshipRepository; import de.oaa.xxx.social.repository.FriendshipRepository;
import de.oaa.xxx.user.UserRepository; import de.oaa.xxx.user.UserRepository;
import de.oaa.xxx.user.UserService;
@RestController @RestController
@RequestMapping("/vanilla/einladung") @RequestMapping("/vanilla/einladung")
@@ -36,15 +36,18 @@ public class VanillaEinladungController {
private final UserRepository userRepository; private final UserRepository userRepository;
private final FriendshipRepository friendshipRepository; private final FriendshipRepository friendshipRepository;
private final SystemMessageService systemMessageService; private final SystemMessageService systemMessageService;
private final UserService userService;
public VanillaEinladungController(VanillaEinladungRepository einladungRepository, public VanillaEinladungController(VanillaEinladungRepository einladungRepository,
UserRepository userRepository, UserRepository userRepository,
FriendshipRepository friendshipRepository, FriendshipRepository friendshipRepository,
SystemMessageService systemMessageService) { SystemMessageService systemMessageService,
UserService userService) {
this.einladungRepository = einladungRepository; this.einladungRepository = einladungRepository;
this.userRepository = userRepository; this.userRepository = userRepository;
this.friendshipRepository = friendshipRepository; this.friendshipRepository = friendshipRepository;
this.systemMessageService = systemMessageService; this.systemMessageService = systemMessageService;
this.userService = userService;
} }
record EinladungRequest(UUID setupId, int slotIndex, UUID inviteeId) {} record EinladungRequest(UUID setupId, int slotIndex, UUID inviteeId) {}
@@ -52,8 +55,7 @@ public class VanillaEinladungController {
record SpielerDatenRequest(String spielerDatenJson) {} record SpielerDatenRequest(String spielerDatenJson) {}
private UUID currentUserId(Principal principal) { private UUID currentUserId(Principal principal) {
return userRepository.findByEmail(principal.getName()) return userService.requireUser(principal).getUserId();
.map(u -> u.getUserId()).orElse(null);
} }
@PostMapping @PostMapping
@@ -103,13 +105,7 @@ public class VanillaEinladungController {
entity.setCreatedAt(LocalDateTime.now()); entity.setCreatedAt(LocalDateTime.now());
einladungRepository.save(entity); einladungRepository.save(entity);
String inviterName = userRepository.findById(inviterId).map(u -> u.getName()).orElse("Jemand"); systemMessageService.pushInvitationUpdate(req.inviteeId());
systemMessageService.send(
inviterId, req.inviteeId(),
inviterName + " hat dich zum Vanilla Game eingeladen.",
"/einladungen.html",
MessageCause.INVITATION
);
Map<String, Object> result = new LinkedHashMap<>(); Map<String, Object> result = new LinkedHashMap<>();
result.put("einladungId", entity.getEinladungId()); result.put("einladungId", entity.getEinladungId());
@@ -124,10 +120,7 @@ public class VanillaEinladungController {
if (e == null) return ResponseEntity.notFound().build(); if (e == null) return ResponseEntity.notFound().build();
if (!e.getInviterId().equals(userId)) return ResponseEntity.status(403).build(); if (!e.getInviterId().equals(userId)) return ResponseEntity.status(403).build();
e.setStatus(Status.CANCELLED); e.setStatus(Status.CANCELLED);
String inviterName = userRepository.findById(userId).map(u -> u.getName()).orElse("Jemand"); systemMessageService.pushInvitationUpdate(e.getInviteeId());
systemMessageService.send(userId, e.getInviteeId(),
inviterName + " hat die Vanilla-Spieleinladung zurückgezogen.",
"/einladungen.html", MessageCause.INVITATION);
return ResponseEntity.accepted().build(); return ResponseEntity.accepted().build();
} }
@@ -150,6 +143,13 @@ public class VanillaEinladungController {
.orElse(ResponseEntity.noContent().build()); .orElse(ResponseEntity.noContent().build());
} }
@GetMapping("/pending/count")
public ResponseEntity<Integer> getPendingCount(Principal principal) {
UUID userId = currentUserId(principal);
if (userId == null) return ResponseEntity.status(401).build();
return ResponseEntity.ok(einladungRepository.findByInviteeIdAndStatus(userId, Status.PENDING).size());
}
@GetMapping("/pending") @GetMapping("/pending")
public ResponseEntity<List<Map<String, Object>>> getPending(Principal principal) { public ResponseEntity<List<Map<String, Object>>> getPending(Principal principal) {
UUID userId = currentUserId(principal); UUID userId = currentUserId(principal);
@@ -241,6 +241,7 @@ public class VanillaEinladungController {
m.put("sessionId", e.getSessionId()); m.put("sessionId", e.getSessionId());
m.put("bereit", e.isBereit()); m.put("bereit", e.isBereit());
m.put("spielerDatenJson", e.getSpielerDatenJson()); m.put("spielerDatenJson", e.getSpielerDatenJson());
m.put("createdAt", e.getCreatedAt().toString());
userRepository.findById(e.getInviteeId()).ifPresent(u -> m.put("inviteeName", u.getName())); userRepository.findById(e.getInviteeId()).ifPresent(u -> m.put("inviteeName", u.getName()));
return m; return m;
} }

View File

@@ -5,11 +5,11 @@ import de.oaa.xxx.games.common.aufgaben.FavoritList;
import de.oaa.xxx.games.common.entity.FavoritEntity; import de.oaa.xxx.games.common.entity.FavoritEntity;
import de.oaa.xxx.games.common.repository.AufgabenGruppeRepository; import de.oaa.xxx.games.common.repository.AufgabenGruppeRepository;
import de.oaa.xxx.games.common.repository.FavoritRepository; import de.oaa.xxx.games.common.repository.FavoritRepository;
import de.oaa.xxx.user.UserService;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
@@ -20,6 +20,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder; import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
import java.security.Principal;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
@@ -32,11 +33,14 @@ public class VanillaFavoritController {
private final FavoritRepository favoritRepository; private final FavoritRepository favoritRepository;
private final AufgabenGruppeRepository gruppeRepository; private final AufgabenGruppeRepository gruppeRepository;
private final UserService userService;
public VanillaFavoritController(FavoritRepository favoritRepository, public VanillaFavoritController(FavoritRepository favoritRepository,
AufgabenGruppeRepository gruppeRepository) { AufgabenGruppeRepository gruppeRepository,
UserService userService) {
this.favoritRepository = favoritRepository; this.favoritRepository = favoritRepository;
this.gruppeRepository = gruppeRepository; this.gruppeRepository = gruppeRepository;
this.userService = userService;
} }
@GetMapping("/{favoritId}") @GetMapping("/{favoritId}")
@@ -47,8 +51,8 @@ public class VanillaFavoritController {
} }
@GetMapping @GetMapping
public ResponseEntity<FavoritList> all() { public ResponseEntity<FavoritList> all(Principal principal) {
UUID userId = (UUID) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); UUID userId = userService.requireUser(principal).getUserId();
List<FavoritEntity> entities = favoritRepository.findByUserId(userId); List<FavoritEntity> entities = favoritRepository.findByUserId(userId);
FavoritList result = new FavoritList(); FavoritList result = new FavoritList();
// Only return favorites pointing to vanilla-safe groups // Only return favorites pointing to vanilla-safe groups
@@ -62,8 +66,8 @@ public class VanillaFavoritController {
} }
@PostMapping @PostMapping
public ResponseEntity<Void> create(@RequestBody Favorit favorit) { public ResponseEntity<Void> create(@RequestBody Favorit favorit, Principal principal) {
UUID userId = (UUID) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); UUID userId = userService.requireUser(principal).getUserId();
if (favorit.getAufgabenGruppeId() == null) { if (favorit.getAufgabenGruppeId() == null) {
return ResponseEntity.badRequest().build(); return ResponseEntity.badRequest().build();
} }
@@ -87,9 +91,9 @@ public class VanillaFavoritController {
} }
@DeleteMapping @DeleteMapping
public ResponseEntity<Void> delete(@RequestBody Favorit favorit) { public ResponseEntity<Void> delete(@RequestBody Favorit favorit, Principal principal) {
try { try {
UUID userId = (UUID) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); UUID userId = userService.requireUser(principal).getUserId();
favoritRepository.findByUserIdAndAufgabenGruppeId(userId, favorit.getAufgabenGruppeId()) favoritRepository.findByUserIdAndAufgabenGruppeId(userId, favorit.getAufgabenGruppeId())
.forEach(favoritRepository::delete); .forEach(favoritRepository::delete);
return ResponseEntity.accepted().build(); return ResponseEntity.accepted().build();

View File

@@ -41,7 +41,7 @@ import de.oaa.xxx.games.vanilla.repository.VanillaGameRepository;
import de.oaa.xxx.games.vanilla.repository.VanillaMitspielerRepository; import de.oaa.xxx.games.vanilla.repository.VanillaMitspielerRepository;
import de.oaa.xxx.social.SystemMessageService; import de.oaa.xxx.social.SystemMessageService;
import de.oaa.xxx.social.entity.MessageCause; import de.oaa.xxx.social.entity.MessageCause;
import de.oaa.xxx.user.UserRepository; import de.oaa.xxx.user.UserService;
@RestController @RestController
@RequestMapping("/vanilla") @RequestMapping("/vanilla")
@@ -58,22 +58,23 @@ public class VanillaGameController {
private final VanillaGameRepository sessionRepository; private final VanillaGameRepository sessionRepository;
private final VanillaMitspielerRepository mitspielerRepository; private final VanillaMitspielerRepository mitspielerRepository;
private final VanillaEinladungRepository einladungRepository; private final VanillaEinladungRepository einladungRepository;
private final UserRepository userRepository;
private final ObjectMapper objectMapper; private final ObjectMapper objectMapper;
private final SystemMessageService systemMessageService; private final SystemMessageService systemMessageService;
private final UserService userService;
public VanillaGameController(VanillaGameRepository sessionRepository, public VanillaGameController(VanillaGameRepository sessionRepository,
VanillaMitspielerRepository mitspielerRepository, VanillaMitspielerRepository mitspielerRepository,
VanillaEinladungRepository einladungRepository, VanillaEinladungRepository einladungRepository,
UserRepository userRepository,
ObjectMapper objectMapper, ObjectMapper objectMapper,
SystemMessageService systemMessageService) { SystemMessageService systemMessageService,
UserService userService) {
this.sessionRepository = sessionRepository; this.sessionRepository = sessionRepository;
this.mitspielerRepository = mitspielerRepository; this.mitspielerRepository = mitspielerRepository;
this.einladungRepository = einladungRepository; this.einladungRepository = einladungRepository;
this.userRepository = userRepository;
this.objectMapper = objectMapper; this.objectMapper = objectMapper;
this.systemMessageService = systemMessageService; this.systemMessageService = systemMessageService;
this.userService = userService;
} }
@GetMapping("/{sessionId}") @GetMapping("/{sessionId}")
@@ -91,10 +92,8 @@ public class VanillaGameController {
} }
@PostMapping @PostMapping
public ResponseEntity<Void> create(@RequestBody VanillaGame session) { public ResponseEntity<Void> create(@RequestBody VanillaGame session, Principal principal) {
String email = (String) org.springframework.security.core.context.SecurityContextHolder.getContext().getAuthentication().getPrincipal(); UUID userId = userService.requireUser(principal).getUserId();
UUID userId = userRepository.findByEmail(email).map(u -> u.getUserId()).orElse(null);
if (userId == null) return ResponseEntity.status(401).build();
var existingOpt = sessionRepository.findByUserId(userId); var existingOpt = sessionRepository.findByUserId(userId);
if (existingOpt.isPresent()) { if (existingOpt.isPresent()) {
VanillaGameEntity existing = existingOpt.get(); VanillaGameEntity existing = existingOpt.get();
@@ -151,8 +150,7 @@ public class VanillaGameController {
@DeleteMapping("/{sessionId}/verlassen") @DeleteMapping("/{sessionId}/verlassen")
public ResponseEntity<Void> verlasseSpiel(@PathVariable UUID sessionId, Principal principal) { public ResponseEntity<Void> verlasseSpiel(@PathVariable UUID sessionId, Principal principal) {
UUID userId = userRepository.findByEmail(principal.getName()).map(u -> u.getUserId()).orElse(null); UUID userId = userService.requireUser(principal).getUserId();
if (userId == null) return ResponseEntity.status(401).build();
VanillaGameEntity session = sessionRepository.findById(sessionId).orElse(null); VanillaGameEntity session = sessionRepository.findById(sessionId).orElse(null);
if (session == null) return ResponseEntity.notFound().build(); if (session == null) return ResponseEntity.notFound().build();
@@ -232,9 +230,7 @@ public class VanillaGameController {
@PostMapping("/{sessionId}/mitspieler") @PostMapping("/{sessionId}/mitspieler")
public ResponseEntity<Void> addMitspieler(@RequestBody VanillaMitspieler mitspieler, @PathVariable UUID sessionId) { public ResponseEntity<Void> addMitspieler(@RequestBody VanillaMitspieler mitspieler, @PathVariable UUID sessionId) {
if (mitspieler.getName() == null || mitspieler.getGeschlecht() == null || mitspieler.getRollen() == null if (mitspieler.getName() == null || mitspieler.getVerfuegbareWerkzeuge() == null || mitspieler.getVerfuegbareWerkzeuge().isEmpty()) {
|| mitspieler.getRollen().isEmpty() || mitspieler.getSpieltMit() == null || mitspieler.getSpieltMit().isEmpty()
|| mitspieler.getVerfuegbareWerkzeuge() == null || mitspieler.getVerfuegbareWerkzeuge().isEmpty()) {
return ResponseEntity.badRequest().build(); return ResponseEntity.badRequest().build();
} }
VanillaGameEntity session = sessionRepository.findById(sessionId).orElse(null); VanillaGameEntity session = sessionRepository.findById(sessionId).orElse(null);
@@ -242,15 +238,12 @@ public class VanillaGameController {
return ResponseEntity.badRequest().build(); return ResponseEntity.badRequest().build();
} }
// Max 2 Mitspieler (1 Host + max 1 Gast) // Max 2 Mitspieler (1 Host + max 1 Gast)
if (session.getMitspieler().size() >= 1) { if (session.getMitspieler().size() >= 2) {
return ResponseEntity.status(409).build(); return ResponseEntity.status(409).build();
} }
VanillaMitspielerEntity entity = new VanillaMitspielerEntity(); VanillaMitspielerEntity entity = new VanillaMitspielerEntity();
entity.setMitspielerId(UUID.randomUUID()); entity.setMitspielerId(UUID.randomUUID());
entity.setGeschlecht(mitspieler.getGeschlecht());
entity.setName(mitspieler.getName()); entity.setName(mitspieler.getName());
entity.setRollen(mitspieler.getRollen());
entity.setSpieltMit(mitspieler.getSpieltMit());
entity.setWerkzeuge(new ArrayList<>(mitspieler.getVerfuegbareWerkzeuge())); entity.setWerkzeuge(new ArrayList<>(mitspieler.getVerfuegbareWerkzeuge()));
entity.setUserId(mitspieler.getUserId()); entity.setUserId(mitspieler.getUserId());
entity.setEigenesGeraet(mitspieler.isEigenesGeraet()); entity.setEigenesGeraet(mitspieler.isEigenesGeraet());
@@ -291,8 +284,7 @@ public class VanillaGameController {
@GetMapping("/{sessionId}/mitspieler/me") @GetMapping("/{sessionId}/mitspieler/me")
public ResponseEntity<Map<String, Object>> getMeinMitspieler(@PathVariable UUID sessionId, Principal principal) { public ResponseEntity<Map<String, Object>> getMeinMitspieler(@PathVariable UUID sessionId, Principal principal) {
UUID userId = userRepository.findByEmail(principal.getName()).map(u -> u.getUserId()).orElse(null); UUID userId = userService.requireUser(principal).getUserId();
if (userId == null) return ResponseEntity.status(401).build();
VanillaGameEntity session = sessionRepository.findById(sessionId).orElse(null); VanillaGameEntity session = sessionRepository.findById(sessionId).orElse(null);
if (session == null) return ResponseEntity.notFound().build(); if (session == null) return ResponseEntity.notFound().build();
return session.getMitspieler().stream() return session.getMitspieler().stream()
@@ -382,10 +374,7 @@ public class VanillaGameController {
mp.put("mitspielerId", m.getMitspielerId()); mp.put("mitspielerId", m.getMitspielerId());
mp.put("name", m.getName()); mp.put("name", m.getName());
mp.put("userId", m.getUserId()); mp.put("userId", m.getUserId());
mp.put("geschlecht", m.getGeschlecht());
mp.put("rollen", m.getRollen());
mp.put("werkzeuge", m.getWerkzeuge()); mp.put("werkzeuge", m.getWerkzeuge());
mp.put("spieltMit", m.getSpieltMit());
mp.put("eigenesGeraet", m.isEigenesGeraet()); mp.put("eigenesGeraet", m.isEigenesGeraet());
return mp; return mp;
}).toList(); }).toList();

View File

@@ -2,7 +2,7 @@ package de.oaa.xxx.games.vanilla.controller;
import de.oaa.xxx.games.vanilla.entity.VanillaSetupDraftEntity; import de.oaa.xxx.games.vanilla.entity.VanillaSetupDraftEntity;
import de.oaa.xxx.games.vanilla.repository.VanillaSetupDraftRepository; import de.oaa.xxx.games.vanilla.repository.VanillaSetupDraftRepository;
import de.oaa.xxx.user.UserRepository; import de.oaa.xxx.user.UserService;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
@@ -19,25 +19,20 @@ import java.util.UUID;
public class VanillaSetupDraftController { public class VanillaSetupDraftController {
private final VanillaSetupDraftRepository draftRepository; private final VanillaSetupDraftRepository draftRepository;
private final UserRepository userRepository; private final UserService userService;
public VanillaSetupDraftController(VanillaSetupDraftRepository draftRepository, UserRepository userRepository) { public VanillaSetupDraftController(VanillaSetupDraftRepository draftRepository, UserService userService) {
this.draftRepository = draftRepository; this.draftRepository = draftRepository;
this.userRepository = userRepository; this.userService = userService;
} }
record DraftRequest(String setupId, String settingsJson, String setupJson, String gruppenJson) {} record DraftRequest(String setupId, String settingsJson, String setupJson, String gruppenJson) {}
private UUID currentUserId(Principal principal) {
return userRepository.findByEmail(principal.getName()).map(u -> u.getUserId()).orElse(null);
}
@GetMapping @GetMapping
public ResponseEntity<Map<String, Object>> getDraft( public ResponseEntity<Map<String, Object>> getDraft(
@RequestParam(required = false) String setupId, @RequestParam(required = false) String setupId,
Principal principal) { Principal principal) {
UUID userId = currentUserId(principal); UUID userId = userService.requireUser(principal).getUserId();
if (userId == null) return ResponseEntity.status(401).build();
var lookup = (setupId != null && !setupId.isBlank()) var lookup = (setupId != null && !setupId.isBlank())
? draftRepository.findBySetupId(setupId) ? draftRepository.findBySetupId(setupId)
: draftRepository.findByUserId(userId); : draftRepository.findByUserId(userId);
@@ -55,8 +50,7 @@ public class VanillaSetupDraftController {
@PutMapping @PutMapping
public ResponseEntity<Void> saveDraft(@RequestBody DraftRequest req, Principal principal) { public ResponseEntity<Void> saveDraft(@RequestBody DraftRequest req, Principal principal) {
UUID userId = currentUserId(principal); UUID userId = userService.requireUser(principal).getUserId();
if (userId == null) return ResponseEntity.status(401).build();
VanillaSetupDraftEntity d = draftRepository.findByUserId(userId) VanillaSetupDraftEntity d = draftRepository.findByUserId(userId)
.orElseGet(() -> { VanillaSetupDraftEntity n = new VanillaSetupDraftEntity(); n.setUserId(userId); return n; }); .orElseGet(() -> { VanillaSetupDraftEntity n = new VanillaSetupDraftEntity(); n.setUserId(userId); return n; });
if (req.setupId() != null) d.setSetupId(req.setupId()); if (req.setupId() != null) d.setSetupId(req.setupId());
@@ -70,8 +64,7 @@ public class VanillaSetupDraftController {
@DeleteMapping @DeleteMapping
public ResponseEntity<Void> deleteDraft(Principal principal) { public ResponseEntity<Void> deleteDraft(Principal principal) {
UUID userId = currentUserId(principal); UUID userId = userService.requireUser(principal).getUserId();
if (userId == null) return ResponseEntity.status(401).build();
draftRepository.findByUserId(userId).ifPresent(draftRepository::delete); draftRepository.findByUserId(userId).ifPresent(draftRepository::delete);
return ResponseEntity.accepted().build(); return ResponseEntity.accepted().build();
} }

View File

@@ -4,11 +4,12 @@ import de.oaa.xxx.games.common.aufgaben.Toy;
import de.oaa.xxx.games.common.aufgaben.ToyPage; import de.oaa.xxx.games.common.aufgaben.ToyPage;
import de.oaa.xxx.games.common.entity.AufgabenGruppeEntity; import de.oaa.xxx.games.common.entity.AufgabenGruppeEntity;
import de.oaa.xxx.games.common.entity.ToyEntity; import de.oaa.xxx.games.common.entity.ToyEntity;
import de.oaa.xxx.games.common.repository.AufgabenGruppeRepository;
import de.oaa.xxx.games.common.repository.GruppenAboRepository; import de.oaa.xxx.games.common.repository.GruppenAboRepository;
import de.oaa.xxx.games.common.repository.ToyRepository; import de.oaa.xxx.games.common.repository.ToyRepository;
import de.oaa.xxx.subscription.SubscriptionLimitService; import de.oaa.xxx.subscription.SubscriptionLimitService;
import de.oaa.xxx.user.UserEntity; import de.oaa.xxx.user.UserEntity;
import de.oaa.xxx.user.UserRepository; import de.oaa.xxx.user.UserService;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
@@ -33,6 +34,7 @@ import java.util.Base64;
import java.util.Comparator; import java.util.Comparator;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
@@ -45,17 +47,20 @@ public class VanillaToyController {
private static final int DEFAULT_PAGE_SIZE = 12; private static final int DEFAULT_PAGE_SIZE = 12;
private final ToyRepository toyRepository; private final ToyRepository toyRepository;
private final UserRepository userRepository; private final UserService userService;
private final GruppenAboRepository aboRepository; private final GruppenAboRepository aboRepository;
private final AufgabenGruppeRepository gruppeRepository;
private final SubscriptionLimitService limitService; private final SubscriptionLimitService limitService;
public VanillaToyController(ToyRepository toyRepository, public VanillaToyController(ToyRepository toyRepository,
UserRepository userRepository, UserService userService,
GruppenAboRepository aboRepository, GruppenAboRepository aboRepository,
AufgabenGruppeRepository gruppeRepository,
SubscriptionLimitService limitService) { SubscriptionLimitService limitService) {
this.toyRepository = toyRepository; this.toyRepository = toyRepository;
this.userRepository = userRepository; this.userService = userService;
this.aboRepository = aboRepository; this.aboRepository = aboRepository;
this.gruppeRepository = gruppeRepository;
this.limitService = limitService; this.limitService = limitService;
} }
@@ -64,10 +69,7 @@ public class VanillaToyController {
@RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "" + DEFAULT_PAGE_SIZE) int size, @RequestParam(defaultValue = "" + DEFAULT_PAGE_SIZE) int size,
Principal principal) { Principal principal) {
UserEntity user = userRepository.findByEmail(principal.getName()).orElse(null); UserEntity user = userService.requireUser(principal);
if (user == null) {
return ResponseEntity.status(401).build();
}
Page<ToyEntity> result = toyRepository.findByUserId( Page<ToyEntity> result = toyRepository.findByUserId(
user.getUserId(), PageRequest.of(page, size, Sort.by("name"))); user.getUserId(), PageRequest.of(page, size, Sort.by("name")));
return ResponseEntity.ok(toToyPage(result)); return ResponseEntity.ok(toToyPage(result));
@@ -88,8 +90,7 @@ public class VanillaToyController {
*/ */
@GetMapping("/available") @GetMapping("/available")
public ResponseEntity<List<Toy>> available(Principal principal) { public ResponseEntity<List<Toy>> available(Principal principal) {
UserEntity user = userRepository.findByEmail(principal.getName()).orElse(null); UserEntity user = userService.requireUser(principal);
if (user == null) return ResponseEntity.status(401).build();
List<ToyEntity> own = toyRepository.findByUserId(user.getUserId(), PageRequest.of(0, 500, Sort.by("name"))).getContent(); List<ToyEntity> own = toyRepository.findByUserId(user.getUserId(), PageRequest.of(0, 500, Sort.by("name"))).getContent();
List<ToyEntity> system = toyRepository.findByUserIdIsNull(PageRequest.of(0, 500, Sort.by("name"))).getContent(); List<ToyEntity> system = toyRepository.findByUserIdIsNull(PageRequest.of(0, 500, Sort.by("name"))).getContent();
@@ -118,6 +119,30 @@ public class VanillaToyController {
return ResponseEntity.ok(result); return ResponseEntity.ok(result);
} }
/**
* Returns all distinct toys required by aufgaben and finisher of the given gruppe IDs.
* Only vanilla-safe groups (no Strafen, no Sperren) are considered.
*/
@GetMapping("/required")
public ResponseEntity<List<Toy>> required(@RequestParam List<UUID> gruppenIds) {
Map<UUID, ToyEntity> toyMap = new java.util.LinkedHashMap<>();
gruppeRepository.findAllById(gruppenIds).forEach(gruppe -> {
gruppe.getAufgaben().forEach(a -> {
if (a.getBenoetigteToys() != null)
a.getBenoetigteToys().forEach(t -> toyMap.putIfAbsent(t.getToyId(), t));
});
gruppe.getFinisher().forEach(f -> {
if (f.getBenoetigteToys() != null)
f.getBenoetigteToys().forEach(t -> toyMap.putIfAbsent(t.getToyId(), t));
});
});
List<Toy> result = toyMap.values().stream()
.sorted(Comparator.comparing(ToyEntity::getName, String.CASE_INSENSITIVE_ORDER))
.map(ToyEntity::toToy)
.toList();
return ResponseEntity.ok(result);
}
@GetMapping("/{toyId}") @GetMapping("/{toyId}")
public ResponseEntity<Toy> get(@PathVariable UUID toyId) { public ResponseEntity<Toy> get(@PathVariable UUID toyId) {
return toyRepository.findById(toyId) return toyRepository.findById(toyId)
@@ -130,10 +155,7 @@ public class VanillaToyController {
if (toy.getName() == null || toy.getName().isBlank()) { if (toy.getName() == null || toy.getName().isBlank()) {
return ResponseEntity.badRequest().build(); return ResponseEntity.badRequest().build();
} }
UserEntity user = userRepository.findByEmail(principal.getName()).orElse(null); UserEntity user = userService.requireUser(principal);
if (user == null) {
return ResponseEntity.status(401).build();
}
if (toyRepository.existsByNameIgnoreCaseAndUserIdIsNull(toy.getName()) if (toyRepository.existsByNameIgnoreCaseAndUserIdIsNull(toy.getName())
|| toyRepository.existsByNameIgnoreCaseAndUserId(toy.getName(), user.getUserId())) { || toyRepository.existsByNameIgnoreCaseAndUserId(toy.getName(), user.getUserId())) {
return ResponseEntity.status(409) return ResponseEntity.status(409)
@@ -156,10 +178,7 @@ public class VanillaToyController {
@PostMapping("/copy/{toyId}") @PostMapping("/copy/{toyId}")
public ResponseEntity<Void> copy(@PathVariable UUID toyId, Principal principal) { public ResponseEntity<Void> copy(@PathVariable UUID toyId, Principal principal) {
UserEntity user = userRepository.findByEmail(principal.getName()).orElse(null); UserEntity user = userService.requireUser(principal);
if (user == null) {
return ResponseEntity.status(401).build();
}
ToyEntity source = toyRepository.findById(toyId).orElse(null); ToyEntity source = toyRepository.findById(toyId).orElse(null);
if (source == null) { if (source == null) {
return ResponseEntity.notFound().build(); return ResponseEntity.notFound().build();
@@ -188,10 +207,7 @@ public class VanillaToyController {
if (toy.getName() == null || toy.getName().isBlank()) { if (toy.getName() == null || toy.getName().isBlank()) {
return ResponseEntity.badRequest().build(); return ResponseEntity.badRequest().build();
} }
UserEntity user = userRepository.findByEmail(principal.getName()).orElse(null); UserEntity user = userService.requireUser(principal);
if (user == null) {
return ResponseEntity.status(401).build();
}
ToyEntity entity = toyRepository.findById(toyId).orElse(null); ToyEntity entity = toyRepository.findById(toyId).orElse(null);
if (entity == null) { if (entity == null) {
return ResponseEntity.notFound().build(); return ResponseEntity.notFound().build();
@@ -217,10 +233,7 @@ public class VanillaToyController {
@DeleteMapping("/{toyId}") @DeleteMapping("/{toyId}")
public ResponseEntity<Void> delete(@PathVariable UUID toyId, Principal principal) { public ResponseEntity<Void> delete(@PathVariable UUID toyId, Principal principal) {
UserEntity user = userRepository.findByEmail(principal.getName()).orElse(null); UserEntity user = userService.requireUser(principal);
if (user == null) {
return ResponseEntity.status(401).build();
}
ToyEntity toy = toyRepository.findById(toyId).orElse(null); ToyEntity toy = toyRepository.findById(toyId).orElse(null);
if (toy == null) { if (toy == null) {
return ResponseEntity.noContent().build(); return ResponseEntity.noContent().build();

View File

@@ -1,41 +1,54 @@
package de.oaa.xxx.games.vanilla.entity; package de.oaa.xxx.games.vanilla.entity;
import de.oaa.xxx.games.bdsm.GeschlechtEnum;
import de.oaa.xxx.games.bdsm.RolleEnum;
import de.oaa.xxx.games.common.aufgaben.Werkzeug;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
@Getter @Setter @Entity @Table(name = "vanilla_mitspieler") import de.oaa.xxx.games.common.aufgaben.Werkzeug;
public class VanillaMitspielerEntity { import jakarta.persistence.CollectionTable;
@Id @Column private UUID mitspielerId; import jakarta.persistence.Column;
@Column private UUID userId; import jakarta.persistence.ElementCollection;
@Column private boolean eigenesGeraet; import jakarta.persistence.Entity;
@Column private String name; import jakarta.persistence.EnumType;
@Enumerated(EnumType.STRING) @Column private GeschlechtEnum geschlecht; import jakarta.persistence.Enumerated;
@Enumerated(EnumType.STRING) import jakarta.persistence.FetchType;
@ElementCollection(targetClass = Werkzeug.class, fetch = FetchType.EAGER) import jakarta.persistence.Id;
@CollectionTable(name = "vanilla_mitspieler_werkzeuge", joinColumns = @JoinColumn(name = "mitspielerId")) import jakarta.persistence.JoinColumn;
@Column(name = "werkzeug") private List<Werkzeug> werkzeuge = new ArrayList<>(); import jakarta.persistence.ManyToOne;
@Enumerated(EnumType.STRING) import jakarta.persistence.Table;
@ElementCollection(targetClass = GeschlechtEnum.class, fetch = FetchType.EAGER) import lombok.Getter;
@CollectionTable(name = "vanilla_mitspieler_spieltMit", joinColumns = @JoinColumn(name = "mitspielerId")) import lombok.Setter;
@Column(name = "geschlecht") private List<GeschlechtEnum> spieltMit = new ArrayList<>();
@Enumerated(EnumType.STRING)
@ElementCollection(targetClass = RolleEnum.class, fetch = FetchType.EAGER)
@CollectionTable(name = "vanilla_mitspieler_rollen", joinColumns = @JoinColumn(name = "mitspielerId"))
@Column(name = "rolle") private List<RolleEnum> rollen = new ArrayList<>();
@ManyToOne @JoinColumn(name = "sessionId", nullable = false) private VanillaGameEntity session;
public de.oaa.xxx.games.vanilla.VanillaMitspieler toMitspieler() { @Getter
de.oaa.xxx.games.vanilla.VanillaMitspieler m = new de.oaa.xxx.games.vanilla.VanillaMitspieler(); @Setter
m.setGeschlecht(geschlecht); m.setId(mitspielerId); m.setUserId(userId); @Entity
m.setEigenesGeraet(eigenesGeraet); m.setName(name); m.setRollen(rollen); @Table(name = "vanilla_mitspieler")
m.setSpieltMit(spieltMit); m.setVerfuegbareWerkzeuge(new ArrayList<>(werkzeuge)); public class VanillaMitspielerEntity {
return m; @Id
} @Column
private UUID mitspielerId;
@Column
private UUID userId;
@Column
private boolean eigenesGeraet;
@Column
private String name;
@Enumerated(EnumType.STRING)
@ElementCollection(targetClass = Werkzeug.class, fetch = FetchType.EAGER)
@CollectionTable(name = "vanilla_mitspieler_werkzeuge", joinColumns = @JoinColumn(name = "mitspielerId"))
@Column(name = "werkzeug")
private List<Werkzeug> werkzeuge = new ArrayList<>();
@ManyToOne
@JoinColumn(name = "sessionId", nullable = false)
private VanillaGameEntity session;
public de.oaa.xxx.games.vanilla.VanillaMitspieler toMitspieler() {
de.oaa.xxx.games.vanilla.VanillaMitspieler m = new de.oaa.xxx.games.vanilla.VanillaMitspieler();
m.setId(mitspielerId);
m.setUserId(userId);
m.setEigenesGeraet(eigenesGeraet);
m.setName(name);
m.setVerfuegbareWerkzeuge(new ArrayList<>(werkzeuge));
return m;
}
} }

View File

@@ -7,6 +7,7 @@ import de.oaa.xxx.social.entity.KommentarEntity;
import de.oaa.xxx.social.repository.KommentarRepository; import de.oaa.xxx.social.repository.KommentarRepository;
import de.oaa.xxx.user.UserEntity; import de.oaa.xxx.user.UserEntity;
import de.oaa.xxx.user.UserRepository; import de.oaa.xxx.user.UserRepository;
import de.oaa.xxx.user.UserService;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
@@ -32,6 +33,7 @@ public class GruppeController {
private final BeitragMeldungRepository meldungRepository; private final BeitragMeldungRepository meldungRepository;
private final KommentarRepository kommentarRepository; private final KommentarRepository kommentarRepository;
private final UserRepository userRepository; private final UserRepository userRepository;
private final UserService userService;
public GruppeController(GruppeRepository gruppeRepository, public GruppeController(GruppeRepository gruppeRepository,
GruppenmitgliedRepository mitgliedRepository, GruppenmitgliedRepository mitgliedRepository,
@@ -42,7 +44,8 @@ public class GruppeController {
GruppenbeitragLikeRepository likeRepository, GruppenbeitragLikeRepository likeRepository,
BeitragMeldungRepository meldungRepository, BeitragMeldungRepository meldungRepository,
KommentarRepository kommentarRepository, KommentarRepository kommentarRepository,
UserRepository userRepository) { UserRepository userRepository,
UserService userService) {
this.gruppeRepository = gruppeRepository; this.gruppeRepository = gruppeRepository;
this.mitgliedRepository = mitgliedRepository; this.mitgliedRepository = mitgliedRepository;
this.anfrageRepository = anfrageRepository; this.anfrageRepository = anfrageRepository;
@@ -53,6 +56,7 @@ public class GruppeController {
this.meldungRepository = meldungRepository; this.meldungRepository = meldungRepository;
this.kommentarRepository = kommentarRepository; this.kommentarRepository = kommentarRepository;
this.userRepository = userRepository; this.userRepository = userRepository;
this.userService = userService;
} }
record CreateGruppeRequest(String name, String beschreibung, String bild, boolean isPrivate) {} record CreateGruppeRequest(String name, String beschreibung, String bild, boolean isPrivate) {}
@@ -66,15 +70,15 @@ public class GruppeController {
UUID myId = resolveMyId(principal); UUID myId = resolveMyId(principal);
if (myId == null) return ResponseEntity.status(401).build(); if (myId == null) return ResponseEntity.status(401).build();
List<GruppeDto> result = gruppeRepository.findByNameContainingIgnoreCase(q) List<GruppeEntity> gruppen = gruppeRepository.findByNameContainingIgnoreCase(q)
.stream() .stream().limit(30).toList();
.limit(30) List<UUID> gruppeIds = gruppen.stream().map(GruppeEntity::getGruppeId).toList();
Map<UUID, LocalDateTime> latestActivity = buildLatestActivityMap(gruppeIds);
List<GruppeDto> result = gruppen.stream()
.map(g -> toDto(g, myId)) .map(g -> toDto(g, myId))
.sorted((a, b) -> { .sorted((a, b) -> {
LocalDateTime la = beitragRepository.findFirstByGruppeIdOrderByCreatedAtDesc(a.gruppeId()) LocalDateTime la = latestActivity.getOrDefault(a.gruppeId(), a.createdAt());
.map(GruppenbeitragEntity::getCreatedAt).orElse(a.createdAt()); LocalDateTime lb = latestActivity.getOrDefault(b.gruppeId(), b.createdAt());
LocalDateTime lb = beitragRepository.findFirstByGruppeIdOrderByCreatedAtDesc(b.gruppeId())
.map(GruppenbeitragEntity::getCreatedAt).orElse(b.createdAt());
return lb.compareTo(la); return lb.compareTo(la);
}) })
.toList(); .toList();
@@ -88,16 +92,18 @@ public class GruppeController {
UUID myId = resolveMyId(principal); UUID myId = resolveMyId(principal);
if (myId == null) return ResponseEntity.status(401).build(); if (myId == null) return ResponseEntity.status(401).build();
List<GruppeDto> result = mitgliedRepository.findByUserId(myId) List<GruppeEntity> gruppen = mitgliedRepository.findByUserId(myId)
.stream() .stream()
.map(m -> gruppeRepository.findById(m.getGruppeId()).orElse(null)) .map(m -> gruppeRepository.findById(m.getGruppeId()).orElse(null))
.filter(Objects::nonNull) .filter(Objects::nonNull)
.toList();
List<UUID> gruppeIds = gruppen.stream().map(GruppeEntity::getGruppeId).toList();
Map<UUID, LocalDateTime> latestActivity = buildLatestActivityMap(gruppeIds);
List<GruppeDto> result = gruppen.stream()
.map(g -> toDto(g, myId)) .map(g -> toDto(g, myId))
.sorted((a, b) -> { .sorted((a, b) -> {
LocalDateTime la = beitragRepository.findFirstByGruppeIdOrderByCreatedAtDesc(a.gruppeId()) LocalDateTime la = latestActivity.getOrDefault(a.gruppeId(), a.createdAt());
.map(GruppenbeitragEntity::getCreatedAt).orElse(a.createdAt()); LocalDateTime lb = latestActivity.getOrDefault(b.gruppeId(), b.createdAt());
LocalDateTime lb = beitragRepository.findFirstByGruppeIdOrderByCreatedAtDesc(b.gruppeId())
.map(GruppenbeitragEntity::getCreatedAt).orElse(b.createdAt());
return lb.compareTo(la); return lb.compareTo(la);
}) })
.toList(); .toList();
@@ -465,11 +471,20 @@ public class GruppeController {
// ── Helpers ── // ── Helpers ──
private Map<UUID, LocalDateTime> buildLatestActivityMap(List<UUID> gruppeIds) {
if (gruppeIds.isEmpty()) return Map.of();
Map<UUID, LocalDateTime> map = new HashMap<>();
beitragRepository.findLatestCreatedAtByGruppeIds(gruppeIds).forEach(row -> {
UUID gId = (UUID) row[0];
LocalDateTime latest = (LocalDateTime) row[1];
map.put(gId, latest);
});
return map;
}
private UUID resolveMyId(Principal principal) { private UUID resolveMyId(Principal principal) {
if (principal == null) return null; if (principal == null) return null;
return userRepository.findByEmail(principal.getName()) return userService.requireUser(principal).getUserId();
.map(UserEntity::getUserId)
.orElse(null);
} }
private boolean isAdmin(UUID gruppeId, UUID userId) { private boolean isAdmin(UUID gruppeId, UUID userId) {

View File

@@ -3,9 +3,11 @@ package de.oaa.xxx.gruppe;
import de.oaa.xxx.gruppe.dto.*; import de.oaa.xxx.gruppe.dto.*;
import de.oaa.xxx.gruppe.entity.*; import de.oaa.xxx.gruppe.entity.*;
import de.oaa.xxx.gruppe.repository.*; import de.oaa.xxx.gruppe.repository.*;
import de.oaa.xxx.social.LikeService;
import de.oaa.xxx.social.repository.KommentarRepository; import de.oaa.xxx.social.repository.KommentarRepository;
import de.oaa.xxx.user.UserEntity; import de.oaa.xxx.user.UserEntity;
import de.oaa.xxx.user.UserRepository; import de.oaa.xxx.user.UserRepository;
import de.oaa.xxx.user.UserService;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.PageRequest;
@@ -31,6 +33,8 @@ public class GruppenbeitragController {
private final BeitragMeldungRepository meldungRepository; private final BeitragMeldungRepository meldungRepository;
private final KommentarRepository kommentarRepository; private final KommentarRepository kommentarRepository;
private final UserRepository userRepository; private final UserRepository userRepository;
private final UserService userService;
private final LikeService likeService;
public GruppenbeitragController(GruppeRepository gruppeRepository, public GruppenbeitragController(GruppeRepository gruppeRepository,
GruppenmitgliedRepository mitgliedRepository, GruppenmitgliedRepository mitgliedRepository,
@@ -40,7 +44,9 @@ public class GruppenbeitragController {
GruppenbeitragLikeRepository likeRepository, GruppenbeitragLikeRepository likeRepository,
BeitragMeldungRepository meldungRepository, BeitragMeldungRepository meldungRepository,
KommentarRepository kommentarRepository, KommentarRepository kommentarRepository,
UserRepository userRepository) { UserRepository userRepository,
UserService userService,
LikeService likeService) {
this.gruppeRepository = gruppeRepository; this.gruppeRepository = gruppeRepository;
this.mitgliedRepository = mitgliedRepository; this.mitgliedRepository = mitgliedRepository;
this.beitragRepository = beitragRepository; this.beitragRepository = beitragRepository;
@@ -50,6 +56,8 @@ public class GruppenbeitragController {
this.meldungRepository = meldungRepository; this.meldungRepository = meldungRepository;
this.kommentarRepository = kommentarRepository; this.kommentarRepository = kommentarRepository;
this.userRepository = userRepository; this.userRepository = userRepository;
this.userService = userService;
this.likeService = likeService;
} }
record CreateBeitragRequest(String beitragTyp, String text, Boolean multiChoice, List<String> optionen, List<String> bilder) {} record CreateBeitragRequest(String beitragTyp, String text, Boolean multiChoice, List<String> optionen, List<String> bilder) {}
@@ -175,19 +183,7 @@ public class GruppenbeitragController {
if (mitgliedRepository.findFirstByGruppeIdAndUserId(id, myId).isEmpty()) if (mitgliedRepository.findFirstByGruppeIdAndUserId(id, myId).isEmpty())
return ResponseEntity.status(403).build(); return ResponseEntity.status(403).build();
var existing = likeRepository.findByBeitragIdAndUserId(postId, myId); likeService.toggleGruppenbeitragLike(postId, myId);
if (existing.isPresent()) {
likeRepository.delete(existing.get());
LOGGER.debug("User {} hat Like auf Beitrag {} entfernt", myId, postId);
} else {
GruppenbeitragLikeEntity like = new GruppenbeitragLikeEntity();
like.setLikeId(UUID.randomUUID());
like.setBeitragId(postId);
like.setUserId(myId);
like.setLikedAt(LocalDateTime.now());
likeRepository.save(like);
LOGGER.debug("User {} hat Beitrag {} geliked", myId, postId);
}
return ResponseEntity.ok().build(); return ResponseEntity.ok().build();
} }
@@ -310,9 +306,7 @@ public class GruppenbeitragController {
private UUID resolveMyId(Principal principal) { private UUID resolveMyId(Principal principal) {
if (principal == null) return null; if (principal == null) return null;
return userRepository.findByEmail(principal.getName()) return userService.requireUser(principal).getUserId();
.map(UserEntity::getUserId)
.orElse(null);
} }
private void deleteBeitragCascade(GruppenbeitragEntity beitrag) { private void deleteBeitragCascade(GruppenbeitragEntity beitrag) {

View File

@@ -4,6 +4,8 @@ import de.oaa.xxx.gruppe.entity.GruppenbeitragEntity;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice; import org.springframework.data.domain.Slice;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime; import java.time.LocalDateTime;
@@ -21,6 +23,9 @@ public interface GruppenbeitragRepository extends JpaRepository<GruppenbeitragEn
List<GruppenbeitragEntity> findByGruppeIdInAndCreatedAtAfterOrderByCreatedAtDesc(List<UUID> gruppeIds, LocalDateTime since); List<GruppenbeitragEntity> findByGruppeIdInAndCreatedAtAfterOrderByCreatedAtDesc(List<UUID> gruppeIds, LocalDateTime since);
@Query("SELECT b.gruppeId, MAX(b.createdAt) FROM GruppenbeitragEntity b WHERE b.gruppeId IN :gruppeIds GROUP BY b.gruppeId")
List<Object[]> findLatestCreatedAtByGruppeIds(@Param("gruppeIds") List<UUID> gruppeIds);
@Transactional @Transactional
void deleteByGruppeId(UUID gruppeId); void deleteByGruppeId(UUID gruppeId);

View File

@@ -12,26 +12,28 @@ import org.springframework.stereotype.Service;
@Service @Service
public class MailService { public class MailService {
private static final Logger LOGGER = LoggerFactory.getLogger(MailService.class); private static final Logger LOGGER = LoggerFactory.getLogger(MailService.class);
private final JavaMailSender mailSender; private final JavaMailSender mailSender;
public MailService(JavaMailSender mailSender) { public MailService(JavaMailSender mailSender) {
this.mailSender = mailSender; this.mailSender = mailSender;
} }
public boolean send(Email email) { public boolean send(Email email) {
try { try {
MimeMessage message = mailSender.createMimeMessage(); MimeMessage message = mailSender.createMimeMessage();
message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(email.getEmailAdresse())); message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(email.getEmailAdresse()));
message.setSubject(email.getTitel()); message.setSubject(email.getTitel());
message.setFrom(InternetAddress.parse("noreply@xxx-bdsmgame.de")[0]); message.setFrom(InternetAddress.parse("noreply@xxx-sphere.de")[0]);
message.setContent(email.getText(), "text/html; charset=utf-8"); message.setContent(email.getText(), "text/html; charset=utf-8");
mailSender.send(message); message.addHeader("X-Mailin-Tag", "no-tracking");
return true; message.addHeader("X-Sib-Attributes", "{\"X-SIB-TRACKING\":\"0\"}");
} catch (MessagingException e) { mailSender.send(message);
LOGGER.error(e.getLocalizedMessage(), e); return true;
return false; } catch (MessagingException e) {
} LOGGER.error(e.getLocalizedMessage(), e);
} return false;
}
}
} }

View File

@@ -178,7 +178,7 @@ public class MailTemplateService {
""".formatted(baseUrl, targetUrl, colorPrimary) """.formatted(baseUrl, targetUrl, colorPrimary)
: "<div style=\"margin:0 0 2rem 0;\"></div>"; : "<div style=\"margin:0 0 2rem 0;\"></div>";
String settingsUrl = baseUrl + "/einstellungen.html#sec-benachrichtigungen"; String settingsUrl = baseUrl + "/konto/einstellungen.html#sec-benachrichtigungen";
return """ return """
<!DOCTYPE html> <!DOCTYPE html>

View File

@@ -1,6 +1,6 @@
package de.oaa.xxx.meldung; package de.oaa.xxx.meldung;
import de.oaa.xxx.user.UserRepository; import de.oaa.xxx.user.UserService;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
@@ -13,11 +13,11 @@ import java.util.UUID;
public class MeldungController { public class MeldungController {
private final MeldungRepository meldungRepository; private final MeldungRepository meldungRepository;
private final UserRepository userRepository; private final UserService userService;
public MeldungController(MeldungRepository meldungRepository, UserRepository userRepository) { public MeldungController(MeldungRepository meldungRepository, UserService userService) {
this.meldungRepository = meldungRepository; this.meldungRepository = meldungRepository;
this.userRepository = userRepository; this.userService = userService;
} }
record MeldungRequest(MeldungZielTyp zielTyp, UUID zielId, String grund) {} record MeldungRequest(MeldungZielTyp zielTyp, UUID zielId, String grund) {}
@@ -25,7 +25,7 @@ public class MeldungController {
@PostMapping @PostMapping
@Transactional @Transactional
public ResponseEntity<Void> melden(@RequestBody MeldungRequest request, Principal principal) { public ResponseEntity<Void> melden(@RequestBody MeldungRequest request, Principal principal) {
var user = userRepository.findByEmail(principal.getName()).orElseThrow(); var user = userService.requireUser(principal);
if (meldungRepository.existsByMelderIdAndZielTypAndZielId(user.getUserId(), request.zielTyp(), request.zielId())) { if (meldungRepository.existsByMelderIdAndZielTypAndZielId(user.getUserId(), request.zielTyp(), request.zielId())) {
return ResponseEntity.status(409).build(); return ResponseEntity.status(409).build();
} }

View File

@@ -46,10 +46,19 @@ public class RegistrationController {
@PostMapping @PostMapping
public ResponseEntity<String> create(@RequestBody Registration registration) { public ResponseEntity<String> create(@RequestBody Registration registration) {
LOGGER.info("POST {}: {}", getClass().getName(), registration); LOGGER.info("POST {}: {}", getClass().getName(), registration);
if (registration.getEmail() == null
|| !registration.getEmail().matches("^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$")) {
LOGGER.warn("Registrierung abgelehnt ungültige E-Mail-Adresse");
return ResponseEntity.status(422).body("EMAIL_FORMAT");
}
if (registration.getPassword() == null || registration.getPassword().length() < 8) {
LOGGER.warn("Registrierung abgelehnt Passwort zu kurz (min. 8 Zeichen)");
return ResponseEntity.status(422).body("PASSWORT_ZU_KURZ");
}
if (registration.getGeburtsdatum() == null if (registration.getGeburtsdatum() == null
|| Period.between(registration.getGeburtsdatum(), LocalDate.now()).getYears() < 18) { || Period.between(registration.getGeburtsdatum(), LocalDate.now()).getYears() < 18) {
LOGGER.warn("Registrierung abgelehnt Mindestalter nicht erreicht"); LOGGER.warn("Registrierung abgelehnt Mindestalter nicht erreicht");
return ResponseEntity.status(422).build(); return ResponseEntity.status(422).body("ALTER");
} }
// Bereits aktivierte User blockieren // Bereits aktivierte User blockieren
if (userRepository.findByEmail(registration.getEmail()).isPresent()) { if (userRepository.findByEmail(registration.getEmail()).isPresent()) {

View File

@@ -1,30 +1,28 @@
package de.oaa.xxx.social; package de.oaa.xxx.social;
import de.oaa.xxx.user.UserRepository; import de.oaa.xxx.user.UserService;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import java.security.Principal; import java.security.Principal;
@RestController @RestController
@RequestMapping("/events") @RequestMapping("/events")
public class EventController { public class EventController {
private final SseService sseService; private final SseService sseService;
private final UserRepository userRepository; private final UserService userService;
public EventController(SseService sseService, UserRepository userRepository) { public EventController(SseService sseService, UserService userService) {
this.sseService = sseService; this.sseService = sseService;
this.userRepository = userRepository; this.userService = userService;
} }
@GetMapping(path = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE) @GetMapping(path = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public SseEmitter stream(Principal principal) { public SseEmitter stream(Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); return sseService.subscribe(userService.requireUser(principal).getUserId());
if (meOpt.isEmpty()) throw new RuntimeException("Not authenticated"); }
return sseService.subscribe(meOpt.get().getUserId()); }
}
}

View File

@@ -2,11 +2,11 @@ package de.oaa.xxx.social;
import de.oaa.xxx.social.dto.KommentarDto; import de.oaa.xxx.social.dto.KommentarDto;
import de.oaa.xxx.social.entity.KommentarEntity; import de.oaa.xxx.social.entity.KommentarEntity;
import de.oaa.xxx.social.entity.KommentarLikeEntity;
import de.oaa.xxx.social.repository.KommentarLikeRepository; import de.oaa.xxx.social.repository.KommentarLikeRepository;
import de.oaa.xxx.social.repository.KommentarRepository; import de.oaa.xxx.social.repository.KommentarRepository;
import de.oaa.xxx.user.UserEntity; import de.oaa.xxx.user.UserEntity;
import de.oaa.xxx.user.UserRepository; import de.oaa.xxx.user.UserRepository;
import de.oaa.xxx.user.UserService;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
@@ -26,13 +26,19 @@ public class KommentarController {
private final KommentarRepository kommentarRepository; private final KommentarRepository kommentarRepository;
private final KommentarLikeRepository likeRepository; private final KommentarLikeRepository likeRepository;
private final UserRepository userRepository; private final UserRepository userRepository;
private final UserService userService;
private final LikeService likeService;
public KommentarController(KommentarRepository kommentarRepository, public KommentarController(KommentarRepository kommentarRepository,
KommentarLikeRepository likeRepository, KommentarLikeRepository likeRepository,
UserRepository userRepository) { UserRepository userRepository,
UserService userService,
LikeService likeService) {
this.kommentarRepository = kommentarRepository; this.kommentarRepository = kommentarRepository;
this.likeRepository = likeRepository; this.likeRepository = likeRepository;
this.userRepository = userRepository; this.userRepository = userRepository;
this.userService = userService;
this.likeService = likeService;
} }
record CreateKommentarRequest(String targetType, UUID targetId, String text) {} record CreateKommentarRequest(String targetType, UUID targetId, String text) {}
@@ -42,9 +48,7 @@ public class KommentarController {
@RequestParam String targetType, @RequestParam String targetType,
@RequestParam UUID targetId, @RequestParam UUID targetId,
Principal principal) { Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
List<KommentarDto> dtos = kommentarRepository List<KommentarDto> dtos = kommentarRepository
.findByTargetTypeAndTargetIdOrderByCreatedAtAsc(targetType, targetId) .findByTargetTypeAndTargetIdOrderByCreatedAtAsc(targetType, targetId)
@@ -56,9 +60,7 @@ public class KommentarController {
@PostMapping @PostMapping
public ResponseEntity<KommentarDto> createKommentar(@RequestBody CreateKommentarRequest request, Principal principal) { public ResponseEntity<KommentarDto> createKommentar(@RequestBody CreateKommentarRequest request, Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
if (request.text() == null || request.text().isBlank()) return ResponseEntity.badRequest().build(); if (request.text() == null || request.text().isBlank()) return ResponseEntity.badRequest().build();
if (request.text().length() > 500) return ResponseEntity.badRequest().build(); if (request.text().length() > 500) return ResponseEntity.badRequest().build();
@@ -81,9 +83,7 @@ public class KommentarController {
@DeleteMapping("/{kommentarId}") @DeleteMapping("/{kommentarId}")
public ResponseEntity<Void> deleteKommentar(@PathVariable UUID kommentarId, Principal principal) { public ResponseEntity<Void> deleteKommentar(@PathVariable UUID kommentarId, Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
var kOpt = kommentarRepository.findById(kommentarId); var kOpt = kommentarRepository.findById(kommentarId);
if (kOpt.isEmpty()) return ResponseEntity.notFound().build(); if (kOpt.isEmpty()) return ResponseEntity.notFound().build();
@@ -104,25 +104,11 @@ public class KommentarController {
@PostMapping("/{kommentarId}/like") @PostMapping("/{kommentarId}/like")
public ResponseEntity<Void> toggleLike(@PathVariable UUID kommentarId, Principal principal) { public ResponseEntity<Void> toggleLike(@PathVariable UUID kommentarId, Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
if (kommentarRepository.findById(kommentarId).isEmpty()) return ResponseEntity.notFound().build(); if (kommentarRepository.findById(kommentarId).isEmpty()) return ResponseEntity.notFound().build();
var existing = likeRepository.findByKommentarIdAndUserId(kommentarId, myId); likeService.toggleKommentarLike(kommentarId, myId);
if (existing.isPresent()) {
likeRepository.delete(existing.get());
LOGGER.debug("User {} hat Like auf Kommentar {} entfernt", myId, kommentarId);
} else {
KommentarLikeEntity like = new KommentarLikeEntity();
like.setLikeId(UUID.randomUUID());
like.setKommentarId(kommentarId);
like.setUserId(myId);
like.setLikedAt(LocalDateTime.now());
likeRepository.save(like);
LOGGER.debug("User {} hat Kommentar {} geliked", myId, kommentarId);
}
return ResponseEntity.ok().build(); return ResponseEntity.ok().build();
} }

View File

@@ -0,0 +1,122 @@
package de.oaa.xxx.social;
import de.oaa.xxx.feed.entity.FeedPostLikeEntity;
import de.oaa.xxx.feed.repository.FeedPostLikeRepository;
import de.oaa.xxx.gruppe.entity.GruppenbeitragLikeEntity;
import de.oaa.xxx.gruppe.repository.GruppenbeitragLikeRepository;
import de.oaa.xxx.social.entity.KommentarLikeEntity;
import de.oaa.xxx.social.entity.PinnwandLikeEntity;
import de.oaa.xxx.social.entity.ProfileImageLikeEntity;
import de.oaa.xxx.social.repository.KommentarLikeRepository;
import de.oaa.xxx.social.repository.PinnwandLikeRepository;
import de.oaa.xxx.social.repository.ProfileImageLikeRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.UUID;
@Service
public class LikeService {
private static final Logger LOGGER = LoggerFactory.getLogger(LikeService.class);
private final PinnwandLikeRepository pinnwandLikeRepository;
private final KommentarLikeRepository kommentarLikeRepository;
private final ProfileImageLikeRepository profileImageLikeRepository;
private final FeedPostLikeRepository feedPostLikeRepository;
private final GruppenbeitragLikeRepository gruppenbeitragLikeRepository;
public LikeService(PinnwandLikeRepository pinnwandLikeRepository,
KommentarLikeRepository kommentarLikeRepository,
ProfileImageLikeRepository profileImageLikeRepository,
FeedPostLikeRepository feedPostLikeRepository,
GruppenbeitragLikeRepository gruppenbeitragLikeRepository) {
this.pinnwandLikeRepository = pinnwandLikeRepository;
this.kommentarLikeRepository = kommentarLikeRepository;
this.profileImageLikeRepository = profileImageLikeRepository;
this.feedPostLikeRepository = feedPostLikeRepository;
this.gruppenbeitragLikeRepository = gruppenbeitragLikeRepository;
}
public void togglePinnwandLike(UUID eintragId, UUID userId) {
var existing = pinnwandLikeRepository.findByEintragIdAndUserId(eintragId, userId);
if (existing.isPresent()) {
pinnwandLikeRepository.delete(existing.get());
LOGGER.debug("User {} hat Like auf Pinnwand-Eintrag {} entfernt", userId, eintragId);
} else {
PinnwandLikeEntity like = new PinnwandLikeEntity();
like.setLikeId(UUID.randomUUID());
like.setEintragId(eintragId);
like.setUserId(userId);
like.setLikedAt(LocalDateTime.now());
pinnwandLikeRepository.save(like);
LOGGER.debug("User {} hat Pinnwand-Eintrag {} geliked", userId, eintragId);
}
}
public void toggleKommentarLike(UUID kommentarId, UUID userId) {
var existing = kommentarLikeRepository.findByKommentarIdAndUserId(kommentarId, userId);
if (existing.isPresent()) {
kommentarLikeRepository.delete(existing.get());
LOGGER.debug("User {} hat Like auf Kommentar {} entfernt", userId, kommentarId);
} else {
KommentarLikeEntity like = new KommentarLikeEntity();
like.setLikeId(UUID.randomUUID());
like.setKommentarId(kommentarId);
like.setUserId(userId);
like.setLikedAt(LocalDateTime.now());
kommentarLikeRepository.save(like);
LOGGER.debug("User {} hat Kommentar {} geliked", userId, kommentarId);
}
}
public void toggleProfileImageLike(UUID imageId, UUID userId) {
var existing = profileImageLikeRepository.findByImageIdAndUserId(imageId, userId);
if (existing.isPresent()) {
profileImageLikeRepository.delete(existing.get());
LOGGER.debug("User {} hat Like auf Profilbild {} entfernt", userId, imageId);
} else {
ProfileImageLikeEntity like = new ProfileImageLikeEntity();
like.setLikeId(UUID.randomUUID());
like.setImageId(imageId);
like.setUserId(userId);
like.setLikedAt(LocalDateTime.now());
profileImageLikeRepository.save(like);
LOGGER.debug("User {} hat Profilbild {} geliked", userId, imageId);
}
}
public void toggleFeedPostLike(UUID postId, UUID userId) {
var existing = feedPostLikeRepository.findByPostIdAndUserId(postId, userId);
if (existing.isPresent()) {
feedPostLikeRepository.delete(existing.get());
LOGGER.debug("User {} hat Like auf Feed-Post {} entfernt", userId, postId);
} else {
FeedPostLikeEntity like = new FeedPostLikeEntity();
like.setLikeId(UUID.randomUUID());
like.setPostId(postId);
like.setUserId(userId);
like.setLikedAt(LocalDateTime.now());
feedPostLikeRepository.save(like);
LOGGER.debug("User {} hat Feed-Post {} geliked", userId, postId);
}
}
public void toggleGruppenbeitragLike(UUID beitragId, UUID userId) {
var existing = gruppenbeitragLikeRepository.findByBeitragIdAndUserId(beitragId, userId);
if (existing.isPresent()) {
gruppenbeitragLikeRepository.delete(existing.get());
LOGGER.debug("User {} hat Like auf Beitrag {} entfernt", userId, beitragId);
} else {
GruppenbeitragLikeEntity like = new GruppenbeitragLikeEntity();
like.setLikeId(UUID.randomUUID());
like.setBeitragId(beitragId);
like.setUserId(userId);
like.setLikedAt(LocalDateTime.now());
gruppenbeitragLikeRepository.save(like);
LOGGER.debug("User {} hat Beitrag {} geliked", userId, beitragId);
}
}
}

View File

@@ -2,6 +2,7 @@ package de.oaa.xxx.social;
import de.oaa.xxx.social.repository.MessageRepository; import de.oaa.xxx.social.repository.MessageRepository;
import de.oaa.xxx.user.UserRepository; import de.oaa.xxx.user.UserRepository;
import de.oaa.xxx.user.UserService;
import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.PageRequest;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@@ -17,18 +18,19 @@ public class NotificationController {
private final MessageRepository messageRepository; private final MessageRepository messageRepository;
private final UserRepository userRepository; private final UserRepository userRepository;
private final UserService userService;
public NotificationController(MessageRepository messageRepository, public NotificationController(MessageRepository messageRepository,
UserRepository userRepository) { UserRepository userRepository,
UserService userService) {
this.messageRepository = messageRepository; this.messageRepository = messageRepository;
this.userRepository = userRepository; this.userRepository = userRepository;
this.userService = userService;
} }
@GetMapping @GetMapping
public ResponseEntity<List<Map<String, Object>>> getNotifications(Principal principal) { public ResponseEntity<List<Map<String, Object>>> getNotifications(Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
List<Map<String, Object>> result = messageRepository List<Map<String, Object>> result = messageRepository
.findNotificationsForUser(myId, PageRequest.of(0, 10)) .findNotificationsForUser(myId, PageRequest.of(0, 10))
@@ -52,9 +54,7 @@ public class NotificationController {
@GetMapping("/unread/count") @GetMapping("/unread/count")
public ResponseEntity<Long> getUnreadCount(Principal principal) { public ResponseEntity<Long> getUnreadCount(Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
return ResponseEntity.ok( return ResponseEntity.ok(
messageRepository.countByReceiverIdAndSystemMessageAndReadAtIsNull(myId, true)); messageRepository.countByReceiverIdAndSystemMessageAndReadAtIsNull(myId, true));
} }
@@ -62,9 +62,7 @@ public class NotificationController {
@Transactional @Transactional
@PostMapping("/{id}/read") @PostMapping("/{id}/read")
public ResponseEntity<Void> markOneRead(@PathVariable UUID id, Principal principal) { public ResponseEntity<Void> markOneRead(@PathVariable UUID id, Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
messageRepository.markNotificationAsRead(id, myId, LocalDateTime.now()); messageRepository.markNotificationAsRead(id, myId, LocalDateTime.now());
return ResponseEntity.noContent().build(); return ResponseEntity.noContent().build();
} }
@@ -72,9 +70,7 @@ public class NotificationController {
@Transactional @Transactional
@PostMapping("/read-all") @PostMapping("/read-all")
public ResponseEntity<Void> markAllRead(Principal principal) { public ResponseEntity<Void> markAllRead(Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
messageRepository.markAllNotificationsAsRead(myId, LocalDateTime.now()); messageRepository.markAllNotificationsAsRead(myId, LocalDateTime.now());
return ResponseEntity.noContent().build(); return ResponseEntity.noContent().build();
} }

View File

@@ -2,12 +2,12 @@ package de.oaa.xxx.social;
import de.oaa.xxx.social.dto.PinnwandEintragDto; import de.oaa.xxx.social.dto.PinnwandEintragDto;
import de.oaa.xxx.social.entity.PinnwandEintragEntity; import de.oaa.xxx.social.entity.PinnwandEintragEntity;
import de.oaa.xxx.social.entity.PinnwandLikeEntity;
import de.oaa.xxx.social.repository.KommentarRepository; import de.oaa.xxx.social.repository.KommentarRepository;
import de.oaa.xxx.social.repository.PinnwandEintragRepository; import de.oaa.xxx.social.repository.PinnwandEintragRepository;
import de.oaa.xxx.social.repository.PinnwandLikeRepository; import de.oaa.xxx.social.repository.PinnwandLikeRepository;
import de.oaa.xxx.user.UserEntity; import de.oaa.xxx.user.UserEntity;
import de.oaa.xxx.user.UserRepository; import de.oaa.xxx.user.UserRepository;
import de.oaa.xxx.user.UserService;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
@@ -28,24 +28,28 @@ public class PinnwandController {
private final PinnwandLikeRepository likeRepository; private final PinnwandLikeRepository likeRepository;
private final KommentarRepository kommentarRepository; private final KommentarRepository kommentarRepository;
private final UserRepository userRepository; private final UserRepository userRepository;
private final UserService userService;
private final LikeService likeService;
public PinnwandController(PinnwandEintragRepository eintragRepository, public PinnwandController(PinnwandEintragRepository eintragRepository,
PinnwandLikeRepository likeRepository, PinnwandLikeRepository likeRepository,
KommentarRepository kommentarRepository, KommentarRepository kommentarRepository,
UserRepository userRepository) { UserRepository userRepository,
UserService userService,
LikeService likeService) {
this.eintragRepository = eintragRepository; this.eintragRepository = eintragRepository;
this.likeRepository = likeRepository; this.likeRepository = likeRepository;
this.kommentarRepository = kommentarRepository; this.kommentarRepository = kommentarRepository;
this.userRepository = userRepository; this.userRepository = userRepository;
this.userService = userService;
this.likeService = likeService;
} }
record CreateEintragRequest(UUID profilUserId, String text) {} record CreateEintragRequest(UUID profilUserId, String text) {}
@GetMapping @GetMapping
public ResponseEntity<List<PinnwandEintragDto>> getEintraege(@RequestParam UUID userId, Principal principal) { public ResponseEntity<List<PinnwandEintragDto>> getEintraege(@RequestParam UUID userId, Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
List<PinnwandEintragDto> dtos = eintragRepository List<PinnwandEintragDto> dtos = eintragRepository
.findByProfilUserIdOrderByCreatedAtDesc(userId) .findByProfilUserIdOrderByCreatedAtDesc(userId)
@@ -57,9 +61,7 @@ public class PinnwandController {
@PostMapping @PostMapping
public ResponseEntity<PinnwandEintragDto> createEintrag(@RequestBody CreateEintragRequest request, Principal principal) { public ResponseEntity<PinnwandEintragDto> createEintrag(@RequestBody CreateEintragRequest request, Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
if (request.text() == null || request.text().isBlank()) return ResponseEntity.badRequest().build(); if (request.text() == null || request.text().isBlank()) return ResponseEntity.badRequest().build();
if (request.text().length() > 1000) return ResponseEntity.badRequest().build(); if (request.text().length() > 1000) return ResponseEntity.badRequest().build();
@@ -78,9 +80,7 @@ public class PinnwandController {
@DeleteMapping("/{eintragId}") @DeleteMapping("/{eintragId}")
public ResponseEntity<Void> deleteEintrag(@PathVariable UUID eintragId, Principal principal) { public ResponseEntity<Void> deleteEintrag(@PathVariable UUID eintragId, Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
var eintragOpt = eintragRepository.findById(eintragId); var eintragOpt = eintragRepository.findById(eintragId);
if (eintragOpt.isEmpty()) return ResponseEntity.notFound().build(); if (eintragOpt.isEmpty()) return ResponseEntity.notFound().build();
@@ -102,25 +102,11 @@ public class PinnwandController {
@PostMapping("/{eintragId}/like") @PostMapping("/{eintragId}/like")
public ResponseEntity<Void> toggleLike(@PathVariable UUID eintragId, Principal principal) { public ResponseEntity<Void> toggleLike(@PathVariable UUID eintragId, Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
if (eintragRepository.findById(eintragId).isEmpty()) return ResponseEntity.notFound().build(); if (eintragRepository.findById(eintragId).isEmpty()) return ResponseEntity.notFound().build();
var existing = likeRepository.findByEintragIdAndUserId(eintragId, myId); likeService.togglePinnwandLike(eintragId, myId);
if (existing.isPresent()) {
likeRepository.delete(existing.get());
LOGGER.debug("User {} hat Like auf Pinnwand-Eintrag {} entfernt", myId, eintragId);
} else {
PinnwandLikeEntity like = new PinnwandLikeEntity();
like.setLikeId(UUID.randomUUID());
like.setEintragId(eintragId);
like.setUserId(myId);
like.setLikedAt(LocalDateTime.now());
likeRepository.save(like);
LOGGER.debug("User {} hat Pinnwand-Eintrag {} geliked", myId, eintragId);
}
return ResponseEntity.ok().build(); return ResponseEntity.ok().build();
} }

View File

@@ -2,10 +2,9 @@ package de.oaa.xxx.social;
import de.oaa.xxx.social.dto.ProfileImageDto; import de.oaa.xxx.social.dto.ProfileImageDto;
import de.oaa.xxx.social.entity.ProfileImageEntity; 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.ProfileImageLikeRepository;
import de.oaa.xxx.social.repository.ProfileImageRepository; import de.oaa.xxx.social.repository.ProfileImageRepository;
import de.oaa.xxx.user.UserRepository; import de.oaa.xxx.user.UserService;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
@@ -25,23 +24,24 @@ public class ProfileImageController {
private final ProfileImageRepository profileImageRepository; private final ProfileImageRepository profileImageRepository;
private final ProfileImageLikeRepository profileImageLikeRepository; private final ProfileImageLikeRepository profileImageLikeRepository;
private final UserRepository userRepository; private final UserService userService;
private final LikeService likeService;
public ProfileImageController(ProfileImageRepository profileImageRepository, public ProfileImageController(ProfileImageRepository profileImageRepository,
ProfileImageLikeRepository profileImageLikeRepository, ProfileImageLikeRepository profileImageLikeRepository,
UserRepository userRepository) { UserService userService,
LikeService likeService) {
this.profileImageRepository = profileImageRepository; this.profileImageRepository = profileImageRepository;
this.profileImageLikeRepository = profileImageLikeRepository; this.profileImageLikeRepository = profileImageLikeRepository;
this.userRepository = userRepository; this.userService = userService;
this.likeService = likeService;
} }
record UploadRequest(String imageData) {} record UploadRequest(String imageData) {}
@PostMapping @PostMapping
public ResponseEntity<ProfileImageDto> uploadImage(@RequestBody UploadRequest request, Principal principal) { public ResponseEntity<ProfileImageDto> uploadImage(@RequestBody UploadRequest request, Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
if (request.imageData() == null || request.imageData().isBlank()) { if (request.imageData() == null || request.imageData().isBlank()) {
return ResponseEntity.badRequest().build(); return ResponseEntity.badRequest().build();
@@ -63,9 +63,7 @@ public class ProfileImageController {
@GetMapping @GetMapping
public ResponseEntity<List<ProfileImageDto>> getImages(@RequestParam UUID userId, Principal principal) { public ResponseEntity<List<ProfileImageDto>> getImages(@RequestParam UUID userId, Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
List<ProfileImageEntity> images = profileImageRepository.findByUserIdOrderByUploadedAtDesc(userId); List<ProfileImageEntity> images = profileImageRepository.findByUserIdOrderByUploadedAtDesc(userId);
List<ProfileImageDto> dtos = images.stream().map(img -> toDto(img, myId)).toList(); List<ProfileImageDto> dtos = images.stream().map(img -> toDto(img, myId)).toList();
@@ -74,9 +72,7 @@ public class ProfileImageController {
@DeleteMapping("/{imageId}") @DeleteMapping("/{imageId}")
public ResponseEntity<Void> deleteImage(@PathVariable UUID imageId, Principal principal) { public ResponseEntity<Void> deleteImage(@PathVariable UUID imageId, Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
var imgOpt = profileImageRepository.findById(imageId); var imgOpt = profileImageRepository.findById(imageId);
if (imgOpt.isEmpty()) return ResponseEntity.notFound().build(); if (imgOpt.isEmpty()) return ResponseEntity.notFound().build();
@@ -90,25 +86,11 @@ public class ProfileImageController {
@PostMapping("/{imageId}/like") @PostMapping("/{imageId}/like")
public ResponseEntity<Void> toggleLike(@PathVariable UUID imageId, Principal principal) { public ResponseEntity<Void> toggleLike(@PathVariable UUID imageId, Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
if (profileImageRepository.findById(imageId).isEmpty()) return ResponseEntity.notFound().build(); if (profileImageRepository.findById(imageId).isEmpty()) return ResponseEntity.notFound().build();
var existing = profileImageLikeRepository.findByImageIdAndUserId(imageId, myId); likeService.toggleProfileImageLike(imageId, myId);
if (existing.isPresent()) {
profileImageLikeRepository.delete(existing.get());
LOGGER.debug("User {} hat Like auf Profilbild {} entfernt", myId, imageId);
} else {
ProfileImageLikeEntity like = new ProfileImageLikeEntity();
like.setLikeId(UUID.randomUUID());
like.setImageId(imageId);
like.setUserId(myId);
like.setLikedAt(LocalDateTime.now());
profileImageLikeRepository.save(like);
LOGGER.debug("User {} hat Profilbild {} geliked", myId, imageId);
}
return ResponseEntity.ok().build(); return ResponseEntity.ok().build();
} }

View File

@@ -13,6 +13,7 @@ import de.oaa.xxx.social.repository.MessageRepository;
import de.oaa.xxx.support.SupportUserService; import de.oaa.xxx.support.SupportUserService;
import de.oaa.xxx.user.UserEntity; import de.oaa.xxx.user.UserEntity;
import de.oaa.xxx.user.UserRepository; import de.oaa.xxx.user.UserRepository;
import de.oaa.xxx.user.UserService;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.PageRequest;
@@ -34,17 +35,20 @@ public class SocialController {
private final MessageRepository messageRepository; private final MessageRepository messageRepository;
private final SseService sseService; private final SseService sseService;
private final SystemMessageService systemMessageService; private final SystemMessageService systemMessageService;
private final UserService userService;
public SocialController(UserRepository userRepository, public SocialController(UserRepository userRepository,
FriendshipRepository friendshipRepository, FriendshipRepository friendshipRepository,
MessageRepository messageRepository, MessageRepository messageRepository,
SseService sseService, SseService sseService,
SystemMessageService systemMessageService) { SystemMessageService systemMessageService,
UserService userService) {
this.userRepository = userRepository; this.userRepository = userRepository;
this.friendshipRepository = friendshipRepository; this.friendshipRepository = friendshipRepository;
this.messageRepository = messageRepository; this.messageRepository = messageRepository;
this.sseService = sseService; this.sseService = sseService;
this.systemMessageService = systemMessageService; this.systemMessageService = systemMessageService;
this.userService = userService;
} }
record FriendRequestBody(UUID receiverId) {} record FriendRequestBody(UUID receiverId) {}
@@ -55,9 +59,7 @@ public class SocialController {
@GetMapping("/users/{userId}") @GetMapping("/users/{userId}")
public ResponseEntity<UserProfile> getUserProfile(@PathVariable UUID userId, Principal principal) { public ResponseEntity<UserProfile> getUserProfile(@PathVariable UUID userId, Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
return userRepository.findById(userId) return userRepository.findById(userId)
.map(u -> ResponseEntity.ok(toUserProfileWithStatus(u, myId))) .map(u -> ResponseEntity.ok(toUserProfileWithStatus(u, myId)))
.orElse(ResponseEntity.notFound().build()); .orElse(ResponseEntity.notFound().build());
@@ -67,9 +69,7 @@ public class SocialController {
@GetMapping("/users/search") @GetMapping("/users/search")
public ResponseEntity<List<UserProfile>> searchUsers(@RequestParam String q, Principal principal) { public ResponseEntity<List<UserProfile>> searchUsers(@RequestParam String q, Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
List<UserEntity> results = userRepository.findByNameContainingIgnoreCase(q); List<UserEntity> results = userRepository.findByNameContainingIgnoreCase(q);
List<UserProfile> profiles = results.stream() List<UserProfile> profiles = results.stream()
@@ -84,9 +84,8 @@ public class SocialController {
@PostMapping("/friends/request") @PostMapping("/friends/request")
public ResponseEntity<Void> sendFriendRequest(@RequestBody FriendRequestBody body, Principal principal) { public ResponseEntity<Void> sendFriendRequest(@RequestBody FriendRequestBody body, Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); var me = userService.requireUser(principal);
if (meOpt.isEmpty()) return ResponseEntity.status(401).build(); UUID myId = me.getUserId();
UUID myId = meOpt.get().getUserId();
if (myId.equals(body.receiverId())) { if (myId.equals(body.receiverId())) {
return ResponseEntity.badRequest().build(); return ResponseEntity.badRequest().build();
@@ -103,10 +102,10 @@ public class SocialController {
friendshipRepository.save(f); friendshipRepository.save(f);
LOGGER.info("User {} hat Freundschaftsanfrage an User {} gesendet", myId, body.receiverId()); LOGGER.info("User {} hat Freundschaftsanfrage an User {} gesendet", myId, body.receiverId());
String senderName = meOpt.get().getName(); String senderName = me.getName();
systemMessageService.send(myId, body.receiverId(), systemMessageService.send(myId, body.receiverId(),
senderName + " hat dir eine Freundschaftsanfrage gesendet.", senderName + " hat dir eine Freundschaftsanfrage gesendet.",
"/benutzer.html?userId=" + myId, "/community/benutzer.html?userId=" + myId,
MessageCause.FRIENDREQUEST); MessageCause.FRIENDREQUEST);
return ResponseEntity.status(201).build(); return ResponseEntity.status(201).build();
@@ -114,9 +113,7 @@ public class SocialController {
@PostMapping("/friends/accept") @PostMapping("/friends/accept")
public ResponseEntity<Void> acceptFriendRequest(@RequestBody FriendshipActionBody body, Principal principal) { public ResponseEntity<Void> acceptFriendRequest(@RequestBody FriendshipActionBody body, Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
var fOpt = friendshipRepository.findById(body.friendshipId()); var fOpt = friendshipRepository.findById(body.friendshipId());
if (fOpt.isEmpty()) return ResponseEntity.notFound().build(); if (fOpt.isEmpty()) return ResponseEntity.notFound().build();
@@ -131,9 +128,7 @@ public class SocialController {
@DeleteMapping("/friends/reject") @DeleteMapping("/friends/reject")
public ResponseEntity<Void> rejectOrRemoveFriend(@RequestBody FriendshipActionBody body, Principal principal) { public ResponseEntity<Void> rejectOrRemoveFriend(@RequestBody FriendshipActionBody body, Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
var fOpt = friendshipRepository.findById(body.friendshipId()); var fOpt = friendshipRepository.findById(body.friendshipId());
if (fOpt.isEmpty()) return ResponseEntity.notFound().build(); if (fOpt.isEmpty()) return ResponseEntity.notFound().build();
@@ -148,7 +143,7 @@ public class SocialController {
@GetMapping("/friends/user/{userId}") @GetMapping("/friends/user/{userId}")
public ResponseEntity<List<UserProfile>> getFriendsOfUser(@PathVariable UUID userId, Principal principal) { public ResponseEntity<List<UserProfile>> getFriendsOfUser(@PathVariable UUID userId, Principal principal) {
if (userRepository.findByEmail(principal.getName()).isEmpty()) return ResponseEntity.status(401).build(); userService.requireUser(principal);
List<UserProfile> profiles = friendshipRepository.findFriends(userId, Status.ACCEPTED).stream() List<UserProfile> profiles = friendshipRepository.findFriends(userId, Status.ACCEPTED).stream()
.map(f -> { .map(f -> {
UUID friendId = f.getSenderId().equals(userId) ? f.getReceiverId() : f.getSenderId(); UUID friendId = f.getSenderId().equals(userId) ? f.getReceiverId() : f.getSenderId();
@@ -163,9 +158,7 @@ public class SocialController {
@GetMapping("/friends") @GetMapping("/friends")
public ResponseEntity<List<FriendshipDto>> getFriends(Principal principal) { public ResponseEntity<List<FriendshipDto>> getFriends(Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
List<FriendshipDto> dtos = friendshipRepository.findFriends(myId, Status.ACCEPTED).stream() List<FriendshipDto> dtos = friendshipRepository.findFriends(myId, Status.ACCEPTED).stream()
.map(f -> { .map(f -> {
@@ -185,9 +178,7 @@ public class SocialController {
@GetMapping("/friends/pending") @GetMapping("/friends/pending")
public ResponseEntity<List<FriendshipDto>> getPendingRequests(Principal principal) { public ResponseEntity<List<FriendshipDto>> getPendingRequests(Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
List<FriendshipDto> dtos = friendshipRepository.findByReceiverIdAndStatus(myId, Status.PENDING).stream() List<FriendshipDto> dtos = friendshipRepository.findByReceiverIdAndStatus(myId, Status.PENDING).stream()
.map(f -> userRepository.findById(f.getSenderId()) .map(f -> userRepository.findById(f.getSenderId())
@@ -204,9 +195,7 @@ public class SocialController {
@GetMapping("/friends/pending/count") @GetMapping("/friends/pending/count")
public ResponseEntity<Long> getPendingCount(Principal principal) { public ResponseEntity<Long> getPendingCount(Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
return ResponseEntity.ok(friendshipRepository.countByReceiverIdAndStatus(myId, Status.PENDING)); return ResponseEntity.ok(friendshipRepository.countByReceiverIdAndStatus(myId, Status.PENDING));
} }
@@ -214,9 +203,7 @@ public class SocialController {
@PostMapping("/messages") @PostMapping("/messages")
public ResponseEntity<Void> sendMessage(@RequestBody SendMessageBody body, Principal principal) { public ResponseEntity<Void> sendMessage(@RequestBody SendMessageBody body, Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
if (body.text() == null || body.text().isBlank()) return ResponseEntity.badRequest().build(); if (body.text() == null || body.text().isBlank()) return ResponseEntity.badRequest().build();
@@ -240,9 +227,7 @@ public class SocialController {
@GetMapping("/messages") @GetMapping("/messages")
public ResponseEntity<List<ConversationSummary>> getConversations(Principal principal) { public ResponseEntity<List<ConversationSummary>> getConversations(Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
List<MessageEntity> allMessages = messageRepository.findAllByUser(myId); List<MessageEntity> allMessages = messageRepository.findAllByUser(myId);
@@ -274,9 +259,7 @@ public class SocialController {
@GetMapping("/messages/unread/count") @GetMapping("/messages/unread/count")
public ResponseEntity<Long> getUnreadCount(Principal principal) { public ResponseEntity<Long> getUnreadCount(Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
return ResponseEntity.ok(messageRepository.countUnread(myId)); return ResponseEntity.ok(messageRepository.countUnread(myId));
} }
@@ -288,9 +271,7 @@ public class SocialController {
@RequestParam(required = false) String before, @RequestParam(required = false) String before,
@RequestParam(required = false) String after, @RequestParam(required = false) String after,
Principal principal) { Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID myId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
UUID myId = meOpt.get().getUserId();
if (after != null) { if (after != null) {
LocalDateTime afterDt = LocalDateTime.parse(after); LocalDateTime afterDt = LocalDateTime.parse(after);
@@ -366,6 +347,7 @@ public class SocialController {
user.getSichtbarkeitPinnwand(), user.getSichtbarkeitPinnwand(),
user.getSichtbarkeitXp(), user.getSichtbarkeitXp(),
user.getSichtbarkeitLockhistorie(), user.getSichtbarkeitLockhistorie(),
user.getSichtbarkeitVorlieben(),
user.isProfilBeiVeroeffentlichungenSichtbar()); user.isProfilBeiVeroeffentlichungenSichtbar());
} }

View File

@@ -57,8 +57,8 @@ public class SystemMessageService {
.findByUserIdAndCause(receiverId, cause) .findByUserIdAndCause(receiverId, cause)
.orElseGet(() -> NotificationPreferenceEntity.defaultFor(receiverId, cause)); .orElseGet(() -> NotificationPreferenceEntity.defaultFor(receiverId, cause));
// FRIENDREQUEST ist immer in-app, unabhängig von der Einstellung // FRIENDREQUEST und INVITATION sind immer nur in-app, kein E-Mail
boolean sendInApp = cause == MessageCause.FRIENDREQUEST || pref.isInApp(); boolean sendInApp = cause == MessageCause.FRIENDREQUEST || cause == MessageCause.INVITATION || pref.isInApp();
if (sendInApp) { if (sendInApp) {
MessageEntity msg = new MessageEntity(); MessageEntity msg = new MessageEntity();
@@ -76,7 +76,7 @@ public class SystemMessageService {
sseService.push(receiverId, "NOTIFICATION", Map.of("unreadCount", unread, "text", text)); sseService.push(receiverId, "NOTIFICATION", Map.of("unreadCount", unread, "text", text));
} }
if (pref.isEmail()) { if (pref.isEmail() && cause != MessageCause.INVITATION) {
userRepository.findById(receiverId).ifPresent(user -> { userRepository.findById(receiverId).ifPresent(user -> {
try { try {
Email email = new Email(); Email email = new Email();
@@ -91,6 +91,15 @@ public class SystemMessageService {
} }
} }
/**
* Benachrichtigt den Empfänger per SSE, dass sich seine Einladungsliste geändert hat,
* ohne eine In-App-Nachricht oder E-Mail zu erstellen.
*/
public void pushInvitationUpdate(UUID receiverId) {
if (receiverId == null) return;
sseService.push(receiverId, "INVITATION", java.util.Map.of());
}
private String causeTitel(MessageCause cause) { private String causeTitel(MessageCause cause) {
return switch (cause) { return switch (cause) {
case INVITATION -> "XXX The Game Neue Einladung"; case INVITATION -> "XXX The Game Neue Einladung";

View File

@@ -31,12 +31,13 @@ public record UserProfile(
Sichtbarkeit sichtbarkeitPinnwand, Sichtbarkeit sichtbarkeitPinnwand,
Sichtbarkeit sichtbarkeitXp, Sichtbarkeit sichtbarkeitXp,
Sichtbarkeit sichtbarkeitLockhistorie, Sichtbarkeit sichtbarkeitLockhistorie,
Sichtbarkeit sichtbarkeitVorlieben,
boolean profilBeiVeroeffentlichungenSichtbar boolean profilBeiVeroeffentlichungenSichtbar
) { ) {
/** Compact constructor for contexts where profile details are not needed (friend list etc.) */ /** Compact constructor for contexts where profile details are not needed (friend list etc.) */
public UserProfile(UUID userId, String name, String profilePicture, String profilePictureHq, String friendStatus) { public UserProfile(UUID userId, String name, String profilePicture, String profilePictureHq, String friendStatus) {
this(userId, name, profilePicture, profilePictureHq, friendStatus, this(userId, name, profilePicture, profilePictureHq, friendStatus,
null, null, null, null, null, null, null, 0, 0, 0, null, null, null, null, null, null, null, 0, 0, 0,
null, null, null, null, null, null, null, false); null, null, null, null, null, null, null, null, false);
} }
} }

View File

@@ -1,6 +1,6 @@
package de.oaa.xxx.subscription; package de.oaa.xxx.subscription;
import de.oaa.xxx.user.UserRepository; import de.oaa.xxx.user.UserService;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
@@ -15,20 +15,18 @@ import java.util.UUID;
@RequestMapping("/subscription") @RequestMapping("/subscription")
public class SubscriptionController { public class SubscriptionController {
private final UserRepository userRepository; private final UserService userService;
private final SubscriptionLimitService limitService; private final SubscriptionLimitService limitService;
public SubscriptionController(UserRepository userRepository, public SubscriptionController(UserService userService,
SubscriptionLimitService limitService) { SubscriptionLimitService limitService) {
this.userRepository = userRepository; this.userService = userService;
this.limitService = limitService; this.limitService = limitService;
} }
@GetMapping("/me") @GetMapping("/me")
public ResponseEntity<Map<String, Object>> getMySubscription(Principal principal) { public ResponseEntity<Map<String, Object>> getMySubscription(Principal principal) {
var meOpt = userRepository.findByEmail(principal.getName()); UUID userId = userService.requireUser(principal).getUserId();
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
UUID userId = meOpt.get().getUserId();
Map<String, Object> result = new LinkedHashMap<>(); Map<String, Object> result = new LinkedHashMap<>();
limitService.getActiveSubscription(userId).ifPresentOrElse(sub -> { limitService.getActiveSubscription(userId).ifPresentOrElse(sub -> {

View File

@@ -1,5 +1,6 @@
package de.oaa.xxx.user; package de.oaa.xxx.user;
import de.oaa.xxx.admin.AdminRepository;
import de.oaa.xxx.config.JwtService; import de.oaa.xxx.config.JwtService;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger; import org.slf4j.Logger;
@@ -30,11 +31,15 @@ public class LoginController {
private final UserRepository userRepository; private final UserRepository userRepository;
private final JwtService jwtService; private final JwtService jwtService;
private final PasswordEncoder passwordEncoder; private final PasswordEncoder passwordEncoder;
private final AdminRepository adminRepository;
private final UserService userService;
public LoginController(UserRepository userRepository, JwtService jwtService, PasswordEncoder passwordEncoder) { public LoginController(UserRepository userRepository, JwtService jwtService, PasswordEncoder passwordEncoder, AdminRepository adminRepository, UserService userService) {
this.userRepository = userRepository; this.userRepository = userRepository;
this.jwtService = jwtService; this.jwtService = jwtService;
this.passwordEncoder = passwordEncoder; this.passwordEncoder = passwordEncoder;
this.adminRepository = adminRepository;
this.userService = userService;
} }
@PostMapping @PostMapping
@@ -51,7 +56,9 @@ public class LoginController {
.maxAge(Duration.ofHours(24)) .maxAge(Duration.ofHours(24))
.build(); .build();
response.addHeader(HttpHeaders.SET_COOKIE, cookie.toString()); response.addHeader(HttpHeaders.SET_COOKIE, cookie.toString());
return ResponseEntity.ok(user.toUser()); User u = user.toUser();
u.setAdmin(adminRepository.existsByUserId(user.getUserId()));
return ResponseEntity.ok(u);
} else { } else {
return ResponseEntity.noContent().build(); return ResponseEntity.noContent().build();
} }
@@ -62,9 +69,10 @@ public class LoginController {
if (principal == null) { if (principal == null) {
return ResponseEntity.status(401).build(); return ResponseEntity.status(401).build();
} }
return userRepository.findByEmail(principal.getName()) UserEntity entity = userService.requireUser(principal);
.map(entity -> ResponseEntity.ok(entity.toUser())) User u = entity.toUser();
.orElse(ResponseEntity.status(401).build()); u.setAdmin(adminRepository.existsByUserId(entity.getUserId()));
return ResponseEntity.ok(u);
} }
@GetMapping("/logout") @GetMapping("/logout")

View File

@@ -15,6 +15,7 @@ public class User {
private String name; private String name;
private String email; private String email;
private String password; private String password;
private boolean admin;
private String profilePicture; private String profilePicture;
private LocalDate geburtsdatum; private LocalDate geburtsdatum;
private Integer groesse; private Integer groesse;

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