Adminmasken angefangen

This commit is contained in:
2026-03-23 23:16:30 +01:00
parent 4f521b6725
commit b63af50786
62 changed files with 4881 additions and 1856 deletions

View File

@@ -1,5 +1,5 @@
#Mon Mar 23 15:59:12 CET 2026
#Mon Mar 23 21:09:40 CET 2026
display=\:0
host=Mario-Linux
process-id=144468
host=mario-mint
process-id=80279
user=mario

View File

@@ -44,3 +44,185 @@ Command-line arguments: -os linux -ws gtk -arch x86_64 -clean -product org.ecli
!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-23 15:59:13.162
!MESSAGE Commands should really have a category: plug-in='org.springframework.tooling.boot.ls', id='spring.initializr.addStarters', categoryId='org.eclipse.lsp4e.commandCategory'
!SESSION 2026-03-23 17:25:44.070 -----------------------------------------------
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-23 17:25:45.609
!MESSAGE Activated before the state location was initialized. Retry after the state location is initialized.
!ENTRY ch.qos.logback.classic 1 0 2026-03-23 17:25:48.467
!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-23 17:25:48.662
!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-23 17:25:48.662
!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-23 17:25:48.852
!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-23 17:25:48.852
!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.springframework.tooling.boot.ls 1 0 2026-03-23 17:26:29.969
!MESSAGE DelegatingStreamConnectionProvider - Stopping Boot LS
!SESSION 2026-03-23 17:26:31.429 -----------------------------------------------
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-23 17:26:32.616
!MESSAGE Activated before the state location was initialized. Retry after the state location is initialized.
!ENTRY ch.qos.logback.classic 1 0 2026-03-23 17:26:35.371
!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-23 17:26:35.486
!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-23 17:26:35.486
!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-23 17:26:35.621
!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-23 17:26:35.621
!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.springframework.tooling.boot.ls 1 0 2026-03-23 17:29:17.229
!MESSAGE DelegatingStreamConnectionProvider - Stopping Boot LS
!SESSION 2026-03-23 17:36:46.699 -----------------------------------------------
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-23 17:36:48.204
!MESSAGE Activated before the state location was initialized. Retry after the state location is initialized.
!ENTRY ch.qos.logback.classic 1 0 2026-03-23 17:36:52.468
!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-23 17:36:52.616
!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-23 17:36:52.616
!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-23 17:36:52.744
!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-23 17:36:52.744
!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.springframework.tooling.boot.ls 1 0 2026-03-23 17:38:14.624
!MESSAGE DelegatingStreamConnectionProvider - Stopping Boot LS
!SESSION 2026-03-23 17:38:45.521 -----------------------------------------------
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-23 17:38:46.707
!MESSAGE Activated before the state location was initialized. Retry after the state location is initialized.
!ENTRY ch.qos.logback.classic 1 0 2026-03-23 17:38:49.211
!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-23 17:38:49.361
!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-23 17:38:49.361
!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-23 17:38:49.491
!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-23 17:38:49.492
!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-23 17:49:15.882
!MESSAGE Keybinding conflicts occurred. They may interfere with normal accelerator operation.
!SUBENTRY 1 org.eclipse.jface 2 0 2026-03-23 17:49:15.882
!MESSAGE A conflict occurred for CTRL+SHIFT+T:
Binding(CTRL+SHIFT+T,
ParameterizedCommand(Command(org.eclipse.jdt.ui.navigate.open.type,Open Type,
Open a type in a Java editor,
Category(org.eclipse.ui.category.navigate,Navigate,null,true),
WorkbenchHandlerServiceHandler("org.eclipse.jdt.ui.navigate.open.type"),
,,true),null),
org.eclipse.ui.defaultAcceleratorConfiguration,
org.eclipse.ui.contexts.window,,,system)
Binding(CTRL+SHIFT+T,
ParameterizedCommand(Command(org.eclipse.lsp4e.symbolInWorkspace,Go to Symbol in Workspace,
,
Category(org.eclipse.lsp4e.category,Language Servers,null,true),
WorkbenchHandlerServiceHandler("org.eclipse.lsp4e.symbolInWorkspace"),
,,true),null),
org.eclipse.ui.defaultAcceleratorConfiguration,
org.eclipse.ui.contexts.window,,,system)
!ENTRY org.eclipse.debug.core 4 125 2026-03-23 17:50:18.648
!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-23 19:07:16.440
!MESSAGE DelegatingStreamConnectionProvider - Stopping Boot LS
!SESSION 2026-03-23 21:09:34.939 -----------------------------------------------
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-23 21:09:36.148
!MESSAGE Activated before the state location was initialized. Retry after the state location is initialized.
!ENTRY ch.qos.logback.classic 1 0 2026-03-23 21:09:41.162
!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-23 21:09:41.310
!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-23 21:09:41.310
!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-23 21:09:41.447
!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-23 21:09:41.447
!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-23 21:57:04.900
!MESSAGE Keybinding conflicts occurred. They may interfere with normal accelerator operation.
!SUBENTRY 1 org.eclipse.jface 2 0 2026-03-23 21:57:04.900
!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)

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<dirs>
<entry loc="/usr/lib/jvm/java-21-openjdk-amd64" stamp="1774248231673"/>
<entry loc="/usr/lib/jvm/java-21-openjdk-amd64" stamp="1774283020812"/>
<entry loc="/usr/lib/jvm/java-26-openjdk-amd64" stamp="1774247449025"/>
<entry loc="/home/mario/Programme/jdk-21" stamp="1774248849578"/>
</dirs>

View File

@@ -1,2 +1,4 @@
<?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"/>
</typeInfoHistroy>

View File

@@ -2,9 +2,27 @@
<section name="Workbench">
<section name="org.eclipse.jdt.internal.ui.packageview.PackageExplorerPart">
<item key="group_libraries" value="true"/>
<item key="layout" value="2"/>
<item key="layout" value="1"/>
<item key="rootMode" value="1"/>
<item key="linkWithEditor" value="false"/>
<item key="memento" value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#x0A;&lt;packageExplorer group_libraries=&quot;1&quot; layout=&quot;2&quot; linkWithEditor=&quot;0&quot; rootMode=&quot;1&quot; workingSetName=&quot;Aggregate for window 1774277926242&quot;&gt;&#x0A;&lt;customFilters userDefinedPatternsEnabled=&quot;false&quot;&gt;&#x0A;&lt;xmlDefinedFilters&gt;&#x0A;&lt;child filterId=&quot;org.eclipse.jdt.ui.PackageExplorer.StaticsFilter&quot; isEnabled=&quot;false&quot;/&gt;&#x0A;&lt;child filterId=&quot;org.eclipse.buildship.ui.packageexplorer.filter.gradle.buildfolder&quot; isEnabled=&quot;true&quot;/&gt;&#x0A;&lt;child filterId=&quot;org.eclipse.mylyn.java.ui.MembersFilter&quot; isEnabled=&quot;false&quot;/&gt;&#x0A;&lt;child filterId=&quot;org.eclipse.jdt.ui.PackageExplorer.NonJavaProjectsFilter&quot; isEnabled=&quot;false&quot;/&gt;&#x0A;&lt;child filterId=&quot;org.eclipse.jdt.ui.PackageExplorer_patternFilterId_.*&quot; isEnabled=&quot;true&quot;/&gt;&#x0A;&lt;child filterId=&quot;org.eclipse.jdt.ui.PackageExplorer.NonSharedProjectsFilter&quot; isEnabled=&quot;false&quot;/&gt;&#x0A;&lt;child filterId=&quot;org.eclipse.jdt.ui.PackageExplorer.SyntheticMembersFilter&quot; isEnabled=&quot;true&quot;/&gt;&#x0A;&lt;child filterId=&quot;org.eclipse.jdt.ui.PackageExplorer.ContainedLibraryFilter&quot; isEnabled=&quot;false&quot;/&gt;&#x0A;&lt;child filterId=&quot;org.eclipse.jdt.internal.ui.PackageExplorer.HideInnerClassFilesFilter&quot; isEnabled=&quot;true&quot;/&gt;&#x0A;&lt;child filterId=&quot;org.eclipse.jdt.internal.ui.PackageExplorer.EmptyInnerPackageFilter&quot; isEnabled=&quot;true&quot;/&gt;&#x0A;&lt;child filterId=&quot;org.eclipse.m2e.MavenModuleFilter&quot; isEnabled=&quot;false&quot;/&gt;&#x0A;&lt;child filterId=&quot;org.eclipse.buildship.ui.packageexplorer.filter.gradle.subProject&quot; isEnabled=&quot;true&quot;/&gt;&#x0A;&lt;child filterId=&quot;org.eclipse.jdt.ui.PackageExplorer.ClosedProjectsFilter&quot; isEnabled=&quot;false&quot;/&gt;&#x0A;&lt;child filterId=&quot;org.eclipse.jdt.ui.PackageExplorer.DeprecatedMembersFilter&quot; isEnabled=&quot;false&quot;/&gt;&#x0A;&lt;child filterId=&quot;org.eclipse.jdt.ui.PackageExplorer.EmptyLibraryContainerFilter&quot; isEnabled=&quot;true&quot;/&gt;&#x0A;&lt;child filterId=&quot;org.eclipse.jdt.ui.PackageExplorer.PackageDeclarationFilter&quot; isEnabled=&quot;true&quot;/&gt;&#x0A;&lt;child filterId=&quot;org.eclipse.jdt.ui.PackageExplorer.ImportDeclarationFilter&quot; isEnabled=&quot;true&quot;/&gt;&#x0A;&lt;child filterId=&quot;org.eclipse.jdt.ui.PackageExplorer.NonJavaElementFilter&quot; isEnabled=&quot;false&quot;/&gt;&#x0A;&lt;child filterId=&quot;org.eclipse.jdt.ui.PackageExplorer.LibraryFilter&quot; isEnabled=&quot;false&quot;/&gt;&#x0A;&lt;child filterId=&quot;org.eclipse.jdt.ui.PackageExplorer.CuAndClassFileFilter&quot; isEnabled=&quot;false&quot;/&gt;&#x0A;&lt;child filterId=&quot;org.eclipse.jdt.internal.ui.PackageExplorer.EmptyPackageFilter&quot; isEnabled=&quot;false&quot;/&gt;&#x0A;&lt;child filterId=&quot;org.eclipse.jdt.ui.PackageExplorer.NonPublicFilter&quot; isEnabled=&quot;false&quot;/&gt;&#x0A;&lt;child filterId=&quot;org.eclipse.jdt.ui.PackageExplorer.LocalTypesFilter&quot; isEnabled=&quot;false&quot;/&gt;&#x0A;&lt;child filterId=&quot;org.eclipse.jdt.ui.PackageExplorer.FieldsFilter&quot; isEnabled=&quot;false&quot;/&gt;&#x0A;&lt;/xmlDefinedFilters&gt;&#x0A;&lt;/customFilters&gt;&#x0A;&lt;/packageExplorer&gt;"/>
<item key="memento" value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#x0A;&lt;packageExplorer group_libraries=&quot;1&quot; layout=&quot;1&quot; linkWithEditor=&quot;0&quot; rootMode=&quot;1&quot; workingSetName=&quot;Aggregate for window 1774277926242&quot;&gt;&#x0A;&lt;customFilters userDefinedPatternsEnabled=&quot;false&quot;&gt;&#x0A;&lt;xmlDefinedFilters&gt;&#x0A;&lt;child filterId=&quot;org.eclipse.jdt.ui.PackageExplorer.StaticsFilter&quot; isEnabled=&quot;false&quot;/&gt;&#x0A;&lt;child filterId=&quot;org.eclipse.buildship.ui.packageexplorer.filter.gradle.buildfolder&quot; isEnabled=&quot;true&quot;/&gt;&#x0A;&lt;child filterId=&quot;org.eclipse.mylyn.java.ui.MembersFilter&quot; isEnabled=&quot;false&quot;/&gt;&#x0A;&lt;child filterId=&quot;org.eclipse.jdt.ui.PackageExplorer.NonJavaProjectsFilter&quot; isEnabled=&quot;false&quot;/&gt;&#x0A;&lt;child filterId=&quot;org.eclipse.jdt.ui.PackageExplorer_patternFilterId_.*&quot; isEnabled=&quot;true&quot;/&gt;&#x0A;&lt;child filterId=&quot;org.eclipse.jdt.ui.PackageExplorer.NonSharedProjectsFilter&quot; isEnabled=&quot;false&quot;/&gt;&#x0A;&lt;child filterId=&quot;org.eclipse.jdt.ui.PackageExplorer.SyntheticMembersFilter&quot; isEnabled=&quot;true&quot;/&gt;&#x0A;&lt;child filterId=&quot;org.eclipse.jdt.ui.PackageExplorer.ContainedLibraryFilter&quot; isEnabled=&quot;false&quot;/&gt;&#x0A;&lt;child filterId=&quot;org.eclipse.jdt.internal.ui.PackageExplorer.HideInnerClassFilesFilter&quot; isEnabled=&quot;true&quot;/&gt;&#x0A;&lt;child filterId=&quot;org.eclipse.jdt.internal.ui.PackageExplorer.EmptyInnerPackageFilter&quot; isEnabled=&quot;true&quot;/&gt;&#x0A;&lt;child filterId=&quot;org.eclipse.m2e.MavenModuleFilter&quot; isEnabled=&quot;false&quot;/&gt;&#x0A;&lt;child filterId=&quot;org.eclipse.buildship.ui.packageexplorer.filter.gradle.subProject&quot; isEnabled=&quot;true&quot;/&gt;&#x0A;&lt;child filterId=&quot;org.eclipse.jdt.ui.PackageExplorer.ClosedProjectsFilter&quot; isEnabled=&quot;false&quot;/&gt;&#x0A;&lt;child filterId=&quot;org.eclipse.jdt.ui.PackageExplorer.DeprecatedMembersFilter&quot; isEnabled=&quot;false&quot;/&gt;&#x0A;&lt;child filterId=&quot;org.eclipse.jdt.ui.PackageExplorer.EmptyLibraryContainerFilter&quot; isEnabled=&quot;true&quot;/&gt;&#x0A;&lt;child filterId=&quot;org.eclipse.jdt.ui.PackageExplorer.PackageDeclarationFilter&quot; isEnabled=&quot;true&quot;/&gt;&#x0A;&lt;child filterId=&quot;org.eclipse.jdt.ui.PackageExplorer.ImportDeclarationFilter&quot; isEnabled=&quot;true&quot;/&gt;&#x0A;&lt;child filterId=&quot;org.eclipse.jdt.ui.PackageExplorer.NonJavaElementFilter&quot; isEnabled=&quot;false&quot;/&gt;&#x0A;&lt;child filterId=&quot;org.eclipse.jdt.ui.PackageExplorer.LibraryFilter&quot; isEnabled=&quot;false&quot;/&gt;&#x0A;&lt;child filterId=&quot;org.eclipse.jdt.ui.PackageExplorer.CuAndClassFileFilter&quot; isEnabled=&quot;false&quot;/&gt;&#x0A;&lt;child filterId=&quot;org.eclipse.jdt.internal.ui.PackageExplorer.EmptyPackageFilter&quot; isEnabled=&quot;false&quot;/&gt;&#x0A;&lt;child filterId=&quot;org.eclipse.jdt.ui.PackageExplorer.NonPublicFilter&quot; isEnabled=&quot;false&quot;/&gt;&#x0A;&lt;child filterId=&quot;org.eclipse.jdt.ui.PackageExplorer.LocalTypesFilter&quot; isEnabled=&quot;false&quot;/&gt;&#x0A;&lt;child filterId=&quot;org.eclipse.jdt.ui.PackageExplorer.FieldsFilter&quot; isEnabled=&quot;false&quot;/&gt;&#x0A;&lt;/xmlDefinedFilters&gt;&#x0A;&lt;/customFilters&gt;&#x0A;&lt;/packageExplorer&gt;"/>
</section>
<section name="JavaElementSearchActions">
</section>
<section name="org.eclipse.jdt.internal.ui.dialogs.OpenTypeSelectionDialog2">
<item key="ShowStatusLine" value="true"/>
<item key="History" value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#x0A;&lt;History/&gt;"/>
<item key="WorkingSet" value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#x0A;&lt;workingSet workingSetName=&quot;&quot;/&gt;"/>
<section name="DialogBoundsSettings">
<item key="DIALOG_HEIGHT" value="500"/>
<item key="DIALOG_WIDTH" value="600"/>
<item key="DIALOG_X_ORIGIN" value="980"/>
<item key="DIALOG_Y_ORIGIN" value="351"/>
<item key="DIALOG_FONT_NAME" value="1|Ubuntu|10.0|0|GTK|1|"/>
</section>
</section>
<section name="completion_proposal_size">
</section>
<section name="quick_assist_proposal_size">
</section>
</section>

View File

@@ -1,2 +1,7 @@
2026-03-23 15:58:46,968 [Worker-6: Loading available Gradle versions] INFO o.e.b.c.i.u.g.PublishedGradleVersions - Gradle version information cache is not available. Remote download required.
2026-03-23 15:59:14,530 [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-23 17:25:50,444 [Worker-6: Loading available Gradle versions] INFO o.e.b.c.i.u.g.PublishedGradleVersions - Gradle version information cache is up-to-date. Trying to read.
2026-03-23 17:26:37,131 [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-23 17:36:54,482 [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-23 17:38:51,039 [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-23 21:09:44,347 [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.

View File

@@ -1,6 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<section name="Workbench">
<section name="ImportExportAction">
<item key="ImportExportPage.STORE_SELECTED_IMPORT_WIZARD_ID" value="org.eclipse.buildship.ui.wizards.project.import"/>
<list key="ImportExportPage.STORE_EXPANDED_IMPORT_CATEGORIES">
<item value="org.eclipse.buildship.ui.importwizards"/>
</list>
<section name="ImportExportWizard.dialogBounds">
<item key="DIALOG_X_ORIGIN" value="974"/>
<item key="DIALOG_Y_ORIGIN" value="343"/>
@@ -9,4 +13,11 @@
<item key="DIALOG_FONT_NAME" value="1|Ubuntu Sans|11.0|0|GTK|1|"/>
</section>
</section>
<section name="WorkbenchPreferenceDialog.dialogBounds">
<item key="DIALOG_X_ORIGIN" value="976"/>
<item key="DIALOG_Y_ORIGIN" value="338"/>
<item key="DIALOG_WIDTH" value="608"/>
<item key="DIALOG_HEIGHT" value="520"/>
<item key="DIALOG_FONT_NAME" value="1|Ubuntu|10.0|0|GTK|1|"/>
</section>
</section>

View File

@@ -1,3 +1,3 @@
#Mon Mar 23 15:59:12 CET 2026
#Mon Mar 23 21:09:40 CET 2026
org.eclipse.core.runtime=2
org.eclipse.platform=4.39.0.v20260226-0420

BIN
bilder/toys/augenbinde.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

BIN
bilder/toys/dildo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

BIN
bilder/toys/dilliator.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

BIN
bilder/toys/femaleCB.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
bilder/toys/gerte.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

BIN
bilder/toys/handfesseln.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
bilder/toys/knebel.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

BIN
bilder/toys/maleCB.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
bilder/toys/mastubator.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

BIN
bilder/toys/paddel.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

BIN
bilder/toys/peitsche.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
bilder/toys/plugs.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

BIN
bilder/toys/pnsknebel.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
bilder/toys/pumpplug.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

BIN
bilder/toys/seil.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
bilder/toys/strapon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

BIN
bilder/toys/vibrator.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

504
testdaten.sql Normal file
View File

@@ -0,0 +1,504 @@
-- =============================================================
-- XXX The Game Testdaten
-- =============================================================
-- Passwort für alle User: Test1234!
-- SHA-256("Test1234!") = 11a1162b984f8cf531e07d9bde6e27f26d6e9c0a2c4c52a6c1f0e2e79cd4e4a
-- Hinweis: Login erwartet SHA-256-Hash vom Client
-- =============================================================
SET FOREIGN_KEY_CHECKS = 0;
-- Aufräumen (Reihenfolge wegen FK)
DELETE FROM kommentar_like;
DELETE FROM kommentar;
DELETE FROM pinnwand_like;
DELETE FROM pinnwand_eintrag;
DELETE FROM feed_post_vote;
DELETE FROM feed_post_option;
DELETE FROM feed_post_like;
DELETE FROM feed_post;
DELETE FROM umfrage_stimme;
DELETE FROM umfrage_option;
DELETE FROM gruppe_beitrag_like;
DELETE FROM gruppe_beitrag;
DELETE FROM beitrittsanfrage;
DELETE FROM gruppe_mitglied;
DELETE FROM gruppe;
DELETE FROM profile_image_like;
DELETE FROM profile_image;
DELETE FROM friendship;
DELETE FROM registration;
DELETE FROM `user`;
SET FOREIGN_KEY_CHECKS = 1;
-- =============================================================
-- BENUTZER (5 User mit unterschiedlichen Profilen)
-- =============================================================
INSERT INTO `user` (
user_id, name, email, password, geburtsdatum,
groesse, gewicht, geschlecht, neigung, beziehungsstatus, beschreibung,
lockee_xp, keyholder_xp, bdsm_xp,
sichtbarkeit_grunddaten, sichtbarkeit_galerie, sichtbarkeit_freunde,
sichtbarkeit_feed, sichtbarkeit_pinnwand, sichtbarkeit_xp, sichtbarkeit_lockhistorie
) VALUES
-- 1. MaxMuster dominant, Single
('11111111-1111-1111-1111-000000000001',
'MaxMuster', 'max@test.de',
'11a1162b984f8cf531e07d9bde6e27f26d6e9c0a2c4c52a6c1f0e2e79cd4e4a',
'1990-05-15',
182, 80, 'MAENNLICH', 'DOMINANT', 'SINGLE',
'Erfahrener Keyholder, der auf striktes aber faires Spiel steht. Immer offen für neue Spielpartner.',
120, 850, 300,
'ALLE', 'ALLE', 'ALLE', 'ALLE', 'ALLE', 'ALLE', 'ALLE'),
-- 2. LisaLust devot, Single
('11111111-1111-1111-1111-000000000002',
'LisaLust', 'lisa@test.de',
'11a1162b984f8cf531e07d9bde6e27f26d6e9c0a2c4c52a6c1f0e2e79cd4e4a',
'1995-08-22',
165, 58, 'WEIBLICH', 'DEVOT', 'SINGLE',
'Neugierigie Lockee auf der Suche nach einem verlässlichen Keyholder. Mag lange Sperren und herausfordernde Aufgaben.',
740, 0, 150,
'ALLE', 'NUR_FREUNDE', 'ALLE', 'ALLE', 'ALLE', 'ALLE', 'NUR_FREUNDE'),
-- 3. SamSwitcher Switcher, in Beziehung
('11111111-1111-1111-1111-000000000003',
'SamSwitcher', 'sam@test.de',
'11a1162b984f8cf531e07d9bde6e27f26d6e9c0a2c4c52a6c1f0e2e79cd4e4a',
'1988-11-03',
175, 70, 'DIVERS', 'SWITCHER', 'IN_EINER_BEZIEHUNG',
'Mal oben, mal unten kommt auf die Stimmung an. Spiele gerne mit meinem Partner zusammen.',
430, 390, 600,
'ALLE', 'ALLE', 'ALLE', 'NUR_FREUNDE', 'ALLE', 'NUR_FREUNDE', 'ALLE'),
-- 4. KajaKette eher devot, Single
('11111111-1111-1111-1111-000000000004',
'KajaKette', 'kaja@test.de',
'11a1162b984f8cf531e07d9bde6e27f26d6e9c0a2c4c52a6c1f0e2e79cd4e4a',
'1998-02-14',
170, 62, 'WEIBLICH', 'EHER_DEVOT', 'SINGLE',
'Chastity-Enthusiastin mit Fokus auf Community-Locks. Schreibe gerne auf Pinnwände!',
920, 50, 80,
'ALLE', 'ALLE', 'ALLE', 'ALLE', 'ALLE', 'ALLE', 'ALLE'),
-- 5. TomTop eher dominant, verheiratet
('11111111-1111-1111-1111-000000000005',
'TomTop', 'tom@test.de',
'11a1162b984f8cf531e07d9bde6e27f26d6e9c0a2c4c52a6c1f0e2e79cd4e4a',
'1985-07-30',
178, 85, 'MAENNLICH', 'EHER_DOMINANT', 'VERHEIRATET',
'Verheiratet, spielen als Paar. Biete Keyholder-Service für seriöse Anfragen.',
200, 560, 410,
'ALLE', 'NUR_FREUNDE', 'NUR_FREUNDE', 'NUR_FREUNDE', 'ALLE', 'ALLE', 'NUR_FREUNDE');
-- =============================================================
-- NICHT AKTIVIERTE REGISTRIERUNG (für Registrierungs-Tests)
-- =============================================================
INSERT INTO registration (
registration_id, name, email, password, activated, activation_code, geburtsdatum
) VALUES
('99999999-9999-9999-9999-000000000001',
'NeuerUser', 'neu@test.de',
'11a1162b984f8cf531e07d9bde6e27f26d6e9c0a2c4c52a6c1f0e2e79cd4e4a',
FALSE, '347821', '2000-01-01');
-- =============================================================
-- FREUNDSCHAFTEN
-- =============================================================
INSERT INTO friendship (friendship_id, sender_id, receiver_id, status, created_at) VALUES
-- Max ↔ Lisa (akzeptiert)
('22222222-2222-2222-2222-000000000001',
'11111111-1111-1111-1111-000000000001',
'11111111-1111-1111-1111-000000000002',
'ACCEPTED', '2025-11-01 10:00:00'),
-- Max ↔ Sam (akzeptiert)
('22222222-2222-2222-2222-000000000002',
'11111111-1111-1111-1111-000000000001',
'11111111-1111-1111-1111-000000000003',
'ACCEPTED', '2025-11-15 14:30:00'),
-- Lisa ↔ Kaja (akzeptiert)
('22222222-2222-2222-2222-000000000003',
'11111111-1111-1111-1111-000000000002',
'11111111-1111-1111-1111-000000000004',
'ACCEPTED', '2025-12-03 09:15:00'),
-- Tom → Kaja (ausstehend)
('22222222-2222-2222-2222-000000000004',
'11111111-1111-1111-1111-000000000005',
'11111111-1111-1111-1111-000000000004',
'PENDING', '2026-01-10 18:45:00'),
-- Sam ↔ Kaja (akzeptiert)
('22222222-2222-2222-2222-000000000005',
'11111111-1111-1111-1111-000000000003',
'11111111-1111-1111-1111-000000000004',
'ACCEPTED', '2026-01-20 11:00:00');
-- =============================================================
-- PINNWAND-EINTRÄGE
-- =============================================================
INSERT INTO pinnwand_eintrag (eintrag_id, profil_user_id, author_id, text, created_at) VALUES
-- Auf Lisas Pinnwand
('33333333-3333-3333-3333-000000000001',
'11111111-1111-1111-1111-000000000002',
'11111111-1111-1111-1111-000000000001',
'Hey Lisa! Schön, dich hier zu sehen. Viel Spaß beim Spielen 🔒',
'2025-12-10 16:00:00'),
('33333333-3333-3333-3333-000000000002',
'11111111-1111-1111-1111-000000000002',
'11111111-1111-1111-1111-000000000004',
'Wir sollten mal ein gemeinsames Lock starten! Meld dich 😊',
'2026-01-05 12:30:00'),
-- Auf Maxs Pinnwand
('33333333-3333-3333-3333-000000000003',
'11111111-1111-1111-1111-000000000001',
'11111111-1111-1111-1111-000000000002',
'Danke für den tollen Keyholder-Service letzte Woche!',
'2026-01-08 20:00:00'),
-- Auf Kajas Pinnwand
('33333333-3333-3333-3333-000000000004',
'11111111-1111-1111-1111-000000000004',
'11111111-1111-1111-1111-000000000003',
'Kaja, du bist die Community-Queen! Immer so aktiv hier.',
'2026-02-14 09:00:00');
-- Pinnwand-Likes
INSERT INTO pinnwand_like (like_id, eintrag_id, user_id, liked_at) VALUES
('33333333-3333-3333-3333-000000000101',
'33333333-3333-3333-3333-000000000001',
'11111111-1111-1111-1111-000000000002',
'2025-12-10 16:05:00'),
('33333333-3333-3333-3333-000000000102',
'33333333-3333-3333-3333-000000000002',
'11111111-1111-1111-1111-000000000001',
'2026-01-05 13:00:00'),
('33333333-3333-3333-3333-000000000103',
'33333333-3333-3333-3333-000000000003',
'11111111-1111-1111-1111-000000000004',
'2026-01-09 10:00:00');
-- =============================================================
-- KOMMENTARE
-- =============================================================
INSERT INTO kommentar (kommentar_id, author_id, target_type, target_id, text, created_at) VALUES
-- Kommentar auf Pinnwand-Eintrag
('44444444-4444-4444-4444-000000000001',
'11111111-1111-1111-1111-000000000002',
'PINNWAND',
'33333333-3333-3333-3333-000000000001',
'Danke Max! Ich freu mich auch 😊',
'2025-12-10 17:00:00'),
('44444444-4444-4444-4444-000000000002',
'11111111-1111-1111-1111-000000000003',
'PINNWAND',
'33333333-3333-3333-3333-000000000001',
'+1, willkommen in der Community!',
'2025-12-10 18:30:00'),
-- Reply auf Kommentar
('44444444-4444-4444-4444-000000000003',
'11111111-1111-1111-1111-000000000001',
'KOMMENTAR',
'44444444-4444-4444-4444-000000000001',
'Na logo! Wir machen das 😄',
'2025-12-10 17:15:00');
-- Kommentar-Likes
INSERT INTO kommentar_like (like_id, kommentar_id, user_id, liked_at) VALUES
('44444444-4444-4444-4444-000000000101',
'44444444-4444-4444-4444-000000000001',
'11111111-1111-1111-1111-000000000001',
'2025-12-10 17:10:00'),
('44444444-4444-4444-4444-000000000102',
'44444444-4444-4444-4444-000000000002',
'11111111-1111-1111-1111-000000000002',
'2025-12-10 19:00:00');
-- =============================================================
-- FEED-POSTS (Text + Umfrage)
-- =============================================================
INSERT INTO feed_post (post_id, author_id, text, beitrag_typ, multi_choice, is_public, created_at) VALUES
-- Öffentlicher Text-Post von Max
('55555555-5555-5555-5555-000000000001',
'11111111-1111-1111-1111-000000000001',
'Wer hat Lust auf ein Cardlock-Turnier nächsten Monat? Community vs. Keyholder! 🃏',
'TEXT', NULL, TRUE, '2026-02-01 10:00:00'),
-- Öffentlicher Text-Post von Lisa
('55555555-5555-5555-5555-000000000002',
'11111111-1111-1111-1111-000000000002',
'48 Stunden geschafft! Das war mein bisher längstes Lock. Ich bin so stolz auf mich! 🔐✨',
'TEXT', NULL, TRUE, '2026-02-05 14:30:00'),
-- Öffentliche Umfrage von Kaja (Single-Choice)
('55555555-5555-5555-5555-000000000003',
'11111111-1111-1111-1111-000000000004',
'Was bevorzugt ihr: Cardlock oder Timelock?',
'UMFRAGE', FALSE, TRUE, '2026-02-10 09:00:00'),
-- Öffentliche Umfrage von Sam (Multi-Choice)
('55555555-5555-5555-5555-000000000004',
'11111111-1111-1111-1111-000000000003',
'Welche Features wollt ihr als nächstes sehen? (Mehrfachauswahl möglich)',
'UMFRAGE', TRUE, TRUE, '2026-02-15 20:00:00'),
-- Nicht-öffentlicher Post von Tom
('55555555-5555-5555-5555-000000000005',
'11111111-1111-1111-1111-000000000005',
'Spielen heute Abend mit meiner Frau eine Runde BDSM. Sie darf den Keyholder spielen!',
'TEXT', NULL, FALSE, '2026-02-20 18:00:00');
-- Umfrage-Optionen
INSERT INTO feed_post_option (option_id, post_id, text, reihenfolge) VALUES
-- Kajas Umfrage
('55555555-5555-5555-5555-000000000101', '55555555-5555-5555-5555-000000000003', 'Cardlock ich liebe die Ungewissheit!', 0),
('55555555-5555-5555-5555-000000000102', '55555555-5555-5555-5555-000000000003', 'Timelock Struktur ist alles.', 1),
('55555555-5555-5555-5555-000000000103', '55555555-5555-5555-5555-000000000003', 'Beides gleich gerne.', 2),
-- Sams Umfrage
('55555555-5555-5555-5555-000000000104', '55555555-5555-5555-5555-000000000004', 'Mobile App', 0),
('55555555-5555-5555-5555-000000000105', '55555555-5555-5555-5555-000000000004', 'Mehr Aufgaben-Vorlagen', 1),
('55555555-5555-5555-5555-000000000106', '55555555-5555-5555-5555-000000000004', 'Dark/Light Theme Toggle', 2),
('55555555-5555-5555-5555-000000000107', '55555555-5555-5555-5555-000000000004', 'Push-Benachrichtigungen', 3);
-- Umfrage-Stimmen
INSERT INTO feed_post_vote (stimme_id, option_id, post_id, user_id) VALUES
-- Kajas Umfrage
('55555555-5555-5555-5555-000000000201', '55555555-5555-5555-5555-000000000101', '55555555-5555-5555-5555-000000000003', '11111111-1111-1111-1111-000000000001'),
('55555555-5555-5555-5555-000000000202', '55555555-5555-5555-5555-000000000101', '55555555-5555-5555-5555-000000000003', '11111111-1111-1111-1111-000000000002'),
('55555555-5555-5555-5555-000000000203', '55555555-5555-5555-5555-000000000102', '55555555-5555-5555-5555-000000000003', '11111111-1111-1111-1111-000000000005'),
('55555555-5555-5555-5555-000000000204', '55555555-5555-5555-5555-000000000103', '55555555-5555-5555-5555-000000000003', '11111111-1111-1111-1111-000000000003'),
-- Sams Umfrage (Multi-Choice)
('55555555-5555-5555-5555-000000000205', '55555555-5555-5555-5555-000000000104', '55555555-5555-5555-5555-000000000004', '11111111-1111-1111-1111-000000000001'),
('55555555-5555-5555-5555-000000000206', '55555555-5555-5555-5555-000000000105', '55555555-5555-5555-5555-000000000004', '11111111-1111-1111-1111-000000000001'),
('55555555-5555-5555-5555-000000000207', '55555555-5555-5555-5555-000000000104', '55555555-5555-5555-5555-000000000004', '11111111-1111-1111-1111-000000000002'),
('55555555-5555-5555-5555-000000000208', '55555555-5555-5555-5555-000000000107', '55555555-5555-5555-5555-000000000004', '11111111-1111-1111-1111-000000000002'),
('55555555-5555-5555-5555-000000000209', '55555555-5555-5555-5555-000000000105', '55555555-5555-5555-5555-000000000004', '11111111-1111-1111-1111-000000000004');
-- Feed-Likes
INSERT INTO feed_post_like (like_id, post_id, user_id, liked_at) VALUES
('55555555-5555-5555-5555-000000000301', '55555555-5555-5555-5555-000000000001', '11111111-1111-1111-1111-000000000002', '2026-02-01 10:30:00'),
('55555555-5555-5555-5555-000000000302', '55555555-5555-5555-5555-000000000001', '11111111-1111-1111-1111-000000000003', '2026-02-01 11:00:00'),
('55555555-5555-5555-5555-000000000303', '55555555-5555-5555-5555-000000000001', '11111111-1111-1111-1111-000000000004', '2026-02-01 11:15:00'),
('55555555-5555-5555-5555-000000000304', '55555555-5555-5555-5555-000000000002', '11111111-1111-1111-1111-000000000001', '2026-02-05 15:00:00'),
('55555555-5555-5555-5555-000000000305', '55555555-5555-5555-5555-000000000002', '11111111-1111-1111-1111-000000000004', '2026-02-05 15:30:00'),
('55555555-5555-5555-5555-000000000306', '55555555-5555-5555-5555-000000000002', '11111111-1111-1111-1111-000000000003', '2026-02-05 16:00:00');
-- Kommentare unter Feed-Posts
INSERT INTO kommentar (kommentar_id, author_id, target_type, target_id, text, created_at) VALUES
('66666666-6666-6666-6666-000000000001',
'11111111-1111-1111-1111-000000000002',
'FEED_POST',
'55555555-5555-5555-5555-000000000001',
'Bin dabei! Wann genau? 🙋‍♀️',
'2026-02-01 11:00:00'),
('66666666-6666-6666-6666-000000000002',
'11111111-1111-1111-1111-000000000003',
'FEED_POST',
'55555555-5555-5555-5555-000000000001',
'Klingt mega! Ich schlage vor: 1 Woche Mindestlaufzeit.',
'2026-02-01 11:30:00'),
('66666666-6666-6666-6666-000000000003',
'11111111-1111-1111-1111-000000000001',
'FEED_POST',
'55555555-5555-5555-5555-000000000002',
'Respekt! 48h ist eine echte Leistung 👏',
'2026-02-05 15:00:00');
-- =============================================================
-- GRUPPEN
-- =============================================================
INSERT INTO gruppe (gruppe_id, name, beschreibung, bild, is_private, created_at, created_by_user_id) VALUES
-- Öffentliche Gruppe
('77777777-7777-7777-7777-000000000001',
'Cardlock Community',
'Die Gruppe für alle Cardlock-Fans! Hier tauschen wir Erfahrungen aus, veranstalten Turniere und helfen Neulingen beim Einstieg.',
NULL, FALSE, '2025-10-01 12:00:00',
'11111111-1111-1111-1111-000000000001'),
-- Private Gruppe
('77777777-7777-7777-7777-000000000002',
'Keyholder-Stammtisch',
'Privater Austausch unter erfahrenen Keyholdern. Nur auf Einladung.',
NULL, TRUE, '2025-11-15 18:00:00',
'11111111-1111-1111-1111-000000000005'),
-- Öffentliche Gruppe
('77777777-7777-7777-7777-000000000003',
'Anfänger & Fragen',
'Neuling? Frag einfach! Hier ist jede Frage willkommen. Keine Scheu.',
NULL, FALSE, '2026-01-01 00:00:00',
'11111111-1111-1111-1111-000000000004');
-- =============================================================
-- GRUPPENMITGLIEDER
-- =============================================================
INSERT INTO gruppe_mitglied (mitglied_id, gruppe_id, user_id, rolle, joined_at) VALUES
-- Cardlock Community
('77777777-7777-7777-7777-000000000101', '77777777-7777-7777-7777-000000000001', '11111111-1111-1111-1111-000000000001', 'ADMIN', '2025-10-01 12:00:00'),
('77777777-7777-7777-7777-000000000102', '77777777-7777-7777-7777-000000000001', '11111111-1111-1111-1111-000000000002', 'MITGLIED', '2025-10-05 09:00:00'),
('77777777-7777-7777-7777-000000000103', '77777777-7777-7777-7777-000000000001', '11111111-1111-1111-1111-000000000003', 'MITGLIED', '2025-10-10 14:00:00'),
('77777777-7777-7777-7777-000000000104', '77777777-7777-7777-7777-000000000001', '11111111-1111-1111-1111-000000000004', 'MITGLIED', '2025-10-20 11:00:00'),
-- Keyholder-Stammtisch
('77777777-7777-7777-7777-000000000105', '77777777-7777-7777-7777-000000000002', '11111111-1111-1111-1111-000000000005', 'ADMIN', '2025-11-15 18:00:00'),
('77777777-7777-7777-7777-000000000106', '77777777-7777-7777-7777-000000000002', '11111111-1111-1111-1111-000000000001', 'MITGLIED', '2025-11-20 10:00:00'),
-- Anfänger & Fragen
('77777777-7777-7777-7777-000000000107', '77777777-7777-7777-7777-000000000003', '11111111-1111-1111-1111-000000000004', 'ADMIN', '2026-01-01 00:00:00'),
('77777777-7777-7777-7777-000000000108', '77777777-7777-7777-7777-000000000003', '11111111-1111-1111-1111-000000000002', 'MITGLIED', '2026-01-03 08:00:00'),
('77777777-7777-7777-7777-000000000109', '77777777-7777-7777-7777-000000000003', '11111111-1111-1111-1111-000000000003', 'MITGLIED', '2026-01-05 12:00:00');
-- Ausstehende Beitrittsanfrage zur privaten Gruppe
INSERT INTO beitrittsanfrage (anfrage_id, gruppe_id, user_id, nachricht, angefragt_at, status) VALUES
('77777777-7777-7777-7777-000000000201',
'77777777-7777-7777-7777-000000000002',
'11111111-1111-1111-1111-000000000003',
'Hallo! Ich bin seit 2 Jahren aktiver Keyholder und würde gerne dazugehören.',
'2026-02-01 15:00:00', 'AUSSTEHEND'),
('77777777-7777-7777-7777-000000000202',
'77777777-7777-7777-7777-000000000002',
'11111111-1111-1111-1111-000000000004',
'Bitte nehmt mich auf! Habe schon ein paar Monate Erfahrung als Keyholderin.',
'2026-02-10 09:00:00', 'ABGELEHNT');
-- =============================================================
-- GRUPPEN-BEITRÄGE (Text + Umfrage)
-- =============================================================
INSERT INTO gruppe_beitrag (beitrag_id, gruppe_id, author_id, beitrag_typ, text, multi_choice, bild, created_at) VALUES
-- Cardlock Community
('88888888-8888-8888-8888-000000000001',
'77777777-7777-7777-7777-000000000001',
'11111111-1111-1111-1111-000000000001',
'TEXT',
'Willkommen in der Cardlock Community! Stellt euch kurz vor und erzählt, wie ihr zum Cardlock gekommen seid.',
NULL, NULL, '2025-10-01 12:05:00'),
('88888888-8888-8888-8888-000000000002',
'77777777-7777-7777-7777-000000000001',
'11111111-1111-1111-1111-000000000002',
'TEXT',
'Ich bin Lisa und liebe Cardlocks seit über einem Jahr! Mein Rekord sind 5 Tage habt ihr Tipps für längere Sperren?',
NULL, NULL, '2025-10-05 10:00:00'),
('88888888-8888-8888-8888-000000000003',
'77777777-7777-7777-7777-000000000001',
'11111111-1111-1111-1111-000000000004',
'UMFRAGE',
'Wie viele Karten startet ihr typischerweise mit?',
FALSE, NULL, '2025-10-20 14:00:00'),
-- Anfänger & Fragen
('88888888-8888-8888-8888-000000000004',
'77777777-7777-7777-7777-000000000003',
'11111111-1111-1111-1111-000000000002',
'TEXT',
'Frage: Wie erkläre ich Cardlocks am besten meinem Partner, der noch nie davon gehört hat?',
NULL, NULL, '2026-01-10 19:00:00'),
('88888888-8888-8888-8888-000000000005',
'77777777-7777-7777-7777-000000000003',
'11111111-1111-1111-1111-000000000001',
'TEXT',
'Gute Frage! Ich würde empfehlen, erst mit einem kurzen Timelock anzufangen. So kann der Partner das Grundkonzept verstehen, ohne direkt mit der Karten-Mechanik überfordert zu werden.',
NULL, NULL, '2026-01-10 19:30:00');
-- Umfrage-Optionen für Gruppen-Beitrag
INSERT INTO umfrage_option (option_id, beitrag_id, text, reihenfolge) VALUES
('88888888-8888-8888-8888-000000000101', '88888888-8888-8888-8888-000000000003', 'Unter 20 Karten', 0),
('88888888-8888-8888-8888-000000000102', '88888888-8888-8888-8888-000000000003', '2040 Karten', 1),
('88888888-8888-8888-8888-000000000103', '88888888-8888-8888-8888-000000000003', '4060 Karten', 2),
('88888888-8888-8888-8888-000000000104', '88888888-8888-8888-8888-000000000003', 'Über 60 Karten', 3);
-- Umfrage-Stimmen (Gruppen)
INSERT INTO umfrage_stimme (stimme_id, option_id, beitrag_id, user_id) VALUES
('88888888-8888-8888-8888-000000000201', '88888888-8888-8888-8888-000000000101', '88888888-8888-8888-8888-000000000003', '11111111-1111-1111-1111-000000000002'),
('88888888-8888-8888-8888-000000000202', '88888888-8888-8888-8888-000000000102', '88888888-8888-8888-8888-000000000003', '11111111-1111-1111-1111-000000000001'),
('88888888-8888-8888-8888-000000000203', '88888888-8888-8888-8888-000000000102', '88888888-8888-8888-8888-000000000003', '11111111-1111-1111-1111-000000000003'),
('88888888-8888-8888-8888-000000000204', '88888888-8888-8888-8888-000000000103', '88888888-8888-8888-8888-000000000003', '11111111-1111-1111-1111-000000000004');
-- Gruppen-Beitrag-Likes
INSERT INTO gruppe_beitrag_like (like_id, beitrag_id, user_id, liked_at) VALUES
('88888888-8888-8888-8888-000000000301', '88888888-8888-8888-8888-000000000001', '11111111-1111-1111-1111-000000000002', '2025-10-01 12:10:00'),
('88888888-8888-8888-8888-000000000302', '88888888-8888-8888-8888-000000000001', '11111111-1111-1111-1111-000000000003', '2025-10-01 13:00:00'),
('88888888-8888-8888-8888-000000000303', '88888888-8888-8888-8888-000000000001', '11111111-1111-1111-1111-000000000004', '2025-10-01 14:00:00'),
('88888888-8888-8888-8888-000000000304', '88888888-8888-8888-8888-000000000002', '11111111-1111-1111-1111-000000000001', '2025-10-05 10:30:00'),
('88888888-8888-8888-8888-000000000305', '88888888-8888-8888-8888-000000000002', '11111111-1111-1111-1111-000000000004', '2025-10-05 11:00:00'),
('88888888-8888-8888-8888-000000000306', '88888888-8888-8888-8888-000000000005', '11111111-1111-1111-1111-000000000002', '2026-01-10 19:45:00'),
('88888888-8888-8888-8888-000000000307', '88888888-8888-8888-8888-000000000005', '11111111-1111-1111-1111-000000000004', '2026-01-10 20:00:00');
-- Kommentare auf Gruppen-Beiträge
INSERT INTO kommentar (kommentar_id, author_id, target_type, target_id, text, created_at) VALUES
('99999999-0000-0000-0000-000000000001',
'11111111-1111-1111-1111-000000000003',
'GROUP_POST',
'88888888-8888-8888-8888-000000000002',
'Hi Lisa! Mein Tipp: Fang mit mehr Green Cards an als du denkst. Du wirst es brauchen 😄',
'2025-10-05 11:00:00'),
('99999999-0000-0000-0000-000000000002',
'11111111-1111-1111-1111-000000000001',
'GROUP_POST',
'88888888-8888-8888-8888-000000000002',
'Mentale Vorbereitung ist alles. Schreib dir vorher auf, warum du es tust.',
'2025-10-05 12:00:00'),
('99999999-0000-0000-0000-000000000003',
'11111111-1111-1111-1111-000000000004',
'GROUP_POST',
'88888888-8888-8888-8888-000000000004',
'Ich würde sagen: zeig ihm/ihr einfach die App! Das visuelle Konzept erklärt sich fast von selbst.',
'2026-01-10 19:15:00');
-- =============================================================
-- CHASTITY LOCK (ein aktives Cardlock: Lisa gesperrt von Max)
-- =============================================================
INSERT INTO current_lock (
lock_id, lock_type, name, lockee, keyholder,
test_lock, requires_verification,
unlock_code_length, unlock_code,
start_time, unlock_time,
hygine_opening_duration_minutes, hygine_opening_everyminites,
task_mode,
keyholder_requested_unlock, emergency_auto_unlocked,
-- CARDLOCK-spezifisch
initial_cards, pick_every_minute, accumulate_picks,
show_remaining_cards, open_picks,
available_cards
) VALUES (
'aaaaaaaa-aaaa-aaaa-aaaa-000000000001',
'CARDLOCK',
'Lisas Frühlings-Lock',
'11111111-1111-1111-1111-000000000002', -- lockee: Lisa
'11111111-1111-1111-1111-000000000001', -- keyholder: Max
FALSE, FALSE,
6, NULL,
'2026-03-20 10:00:00', NULL,
30, 1440, -- Hygiene alle 24h, 30 Min offen
'KEYHOLDER',
FALSE, FALSE,
-- 30 Karten: 5×GREEN, 15×RED, 5×YELLOW, 3×TASK, 2×FREEZE
'["GREEN","GREEN","GREEN","GREEN","GREEN","RED","RED","RED","RED","RED","RED","RED","RED","RED","RED","RED","RED","RED","RED","RED","YELLOW","YELLOW","YELLOW","YELLOW","YELLOW","TASK","TASK","TASK","FREEZE","FREEZE"]',
240, FALSE, -- Karte alle 4h ziehen, kein Akkumulieren
TRUE, 0,
'["GREEN","GREEN","GREEN","GREEN","GREEN","RED","RED","RED","RED","RED","RED","RED","RED","RED","RED","RED","RED","RED","RED","RED","YELLOW","YELLOW","YELLOW","YELLOW","YELLOW","TASK","TASK","TASK","FREEZE","FREEZE"]'
);
-- =============================================================
-- FERTIG
-- =============================================================
-- Überblick:
-- 5 User (max@test.de, lisa@test.de, sam@test.de, kaja@test.de, tom@test.de)
-- 1 nicht aktivierte Registrierung (neu@test.de, Code: 347821)
-- 5 Freundschaften (4 akzeptiert, 1 ausstehend)
-- 4 Pinnwand-Einträge + 3 Likes
-- 3 Kommentare auf Pinnwand + 3 auf Feed + 3 auf Gruppen-Beiträge
-- 5 Feed-Posts (3 Text, 2 Umfragen) + 6 Likes
-- 3 Gruppen (2 öffentlich, 1 privat) mit je 4-6 Mitgliedern
-- 5 Gruppen-Beiträge (4 Text, 1 Umfrage) + 7 Likes
-- 1 aktives Cardlock (Lisa ← Max)
-- =============================================================

Binary file not shown.

BIN
testdaten/toys-export.zip Normal file

Binary file not shown.

View File

@@ -0,0 +1,270 @@
package de.oaa.xxx.admin;
import de.oaa.xxx.aufgaben.AufgabenGruppe;
import de.oaa.xxx.aufgaben.Toy;
import de.oaa.xxx.aufgaben.entity.AufgabenGruppeEntity;
import de.oaa.xxx.aufgaben.entity.ToyEntity;
import de.oaa.xxx.aufgaben.repository.AufgabenGruppeRepository;
import de.oaa.xxx.aufgaben.repository.ToyRepository;
import de.oaa.xxx.meldung.MeldungEntity;
import de.oaa.xxx.meldung.MeldungRepository;
import de.oaa.xxx.meldung.MeldungStatus;
import de.oaa.xxx.user.UserEntity;
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.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import java.security.Principal;
import java.time.LocalDateTime;
import java.util.List;
import java.util.UUID;
@RestController
@RequestMapping("/admin")
@Transactional
public class AdminController {
private final AdminRepository adminRepository;
private final UserRepository userRepository;
private final MeldungRepository meldungRepository;
private final AufgabenGruppeRepository aufgabenGruppeRepository;
private final ToyRepository toyRepository;
public AdminController(AdminRepository adminRepository, UserRepository userRepository,
MeldungRepository meldungRepository,
AufgabenGruppeRepository aufgabenGruppeRepository,
ToyRepository toyRepository) {
this.adminRepository = adminRepository;
this.userRepository = userRepository;
this.meldungRepository = meldungRepository;
this.aufgabenGruppeRepository = aufgabenGruppeRepository;
this.toyRepository = toyRepository;
}
// ── DTOs ─────────────────────────────────────────────────────────────────
record AdminDto(UUID adminId, UUID userId, String userName, AdminRolle rolle, LocalDateTime createdAt) {}
record MeldungDto(UUID meldungId, UUID melderId, String melderName,
de.oaa.xxx.meldung.MeldungZielTyp zielTyp, UUID zielId,
String grund, LocalDateTime gemeldetAt,
MeldungStatus status, UUID bearbeitetVon, LocalDateTime bearbeitetAt) {}
record CreateAdminRequest(UUID userId, AdminRolle rolle) {}
record StatusRequest(MeldungStatus status) {}
// ── Hilfsmethoden ────────────────────────────────────────────────────────
private AdminEntity requireAdmin(Principal principal) {
var user = userRepository.findByEmail(principal.getName()).orElseThrow();
return adminRepository.findByUserId(user.getUserId())
.orElseThrow(() -> new org.springframework.web.server.ResponseStatusException(
org.springframework.http.HttpStatus.FORBIDDEN, "Kein Admin"));
}
private AdminEntity requireSuperAdmin(Principal principal) {
AdminEntity admin = requireAdmin(principal);
if (admin.getRolle() != AdminRolle.SUPERADMIN) {
throw new org.springframework.web.server.ResponseStatusException(
org.springframework.http.HttpStatus.FORBIDDEN, "Kein Superadmin");
}
return admin;
}
private AdminDto toDto(AdminEntity e) {
String name = userRepository.findById(e.getUserId()).map(UserEntity::getName).orElse("?");
return new AdminDto(e.getAdminId(), e.getUserId(), name, e.getRolle(), e.getCreatedAt());
}
private MeldungDto toMeldungDto(MeldungEntity e) {
String melderName = userRepository.findById(e.getMelderId()).map(UserEntity::getName).orElse("?");
return new MeldungDto(e.getMeldungId(), e.getMelderId(), melderName,
e.getZielTyp(), e.getZielId(), e.getGrund(), e.getGemeldetAt(),
e.getStatus(), e.getBearbeitetVon(), e.getBearbeitetAt());
}
// ── /admin/me ────────────────────────────────────────────────────────────
@GetMapping("/me")
public ResponseEntity<AdminDto> me(Principal principal) {
var user = userRepository.findByEmail(principal.getName()).orElse(null);
if (user == null) return ResponseEntity.status(403).build();
return adminRepository.findByUserId(user.getUserId())
.map(a -> ResponseEntity.ok(toDto(a)))
.orElse(ResponseEntity.status(403).build());
}
// ── Meldungen ────────────────────────────────────────────────────────────
@GetMapping("/meldungen")
public ResponseEntity<List<MeldungDto>> getMeldungen(
@RequestParam(name = "status", required = false) MeldungStatus status,
Principal principal) {
requireAdmin(principal);
List<MeldungEntity> list = status != null
? meldungRepository.findByStatusOrderByGemeldetAtDesc(status)
: meldungRepository.findAllByOrderByGemeldetAtDesc();
return ResponseEntity.ok(list.stream().map(this::toMeldungDto).toList());
}
@PutMapping("/meldungen/{id}")
public ResponseEntity<Void> updateMeldung(@PathVariable("id") UUID id,
@RequestBody StatusRequest body,
Principal principal) {
var admin = requireAdmin(principal);
var user = userRepository.findByEmail(principal.getName()).orElseThrow();
MeldungEntity meldung = meldungRepository.findById(id)
.orElseThrow(() -> new org.springframework.web.server.ResponseStatusException(
org.springframework.http.HttpStatus.NOT_FOUND));
meldung.setStatus(body.status());
meldung.setBearbeitetVon(user.getUserId());
meldung.setBearbeitetAt(LocalDateTime.now());
return ResponseEntity.noContent().build();
}
// ── Aufgabengruppen ──────────────────────────────────────────────────────
@GetMapping("/aufgabengruppen")
public ResponseEntity<List<de.oaa.xxx.aufgaben.AufgabenGruppe>> getAufgabengruppen(Principal principal) {
requireAdmin(principal);
List<AufgabenGruppeEntity> list = aufgabenGruppeRepository
.findByUserIdIsNull(PageRequest.of(0, 1000)).getContent();
return ResponseEntity.ok(list.stream().map(AufgabenGruppeEntity::toAufgabenGruppe).toList());
}
@PostMapping("/aufgabengruppen")
public ResponseEntity<de.oaa.xxx.aufgaben.AufgabenGruppeDisplay> createAufgabengruppe(
@RequestBody AufgabenGruppe gruppe, Principal principal) {
requireAdmin(principal);
gruppe.setUserId(null);
gruppe.setPrivateGruppe(false);
AufgabenGruppeEntity entity = AufgabenGruppeEntity.create(gruppe);
aufgabenGruppeRepository.save(entity);
return ResponseEntity.status(201).body(entity.toAufgabenGruppeDisplay());
}
@PutMapping("/aufgabengruppen/{id}")
public ResponseEntity<Void> updateAufgabengruppe(@PathVariable("id") UUID id,
@RequestBody AufgabenGruppe gruppe,
Principal principal) {
requireAdmin(principal);
AufgabenGruppeEntity entity = aufgabenGruppeRepository.findById(id)
.orElseThrow(() -> new org.springframework.web.server.ResponseStatusException(
org.springframework.http.HttpStatus.NOT_FOUND));
entity.setName(gruppe.getName());
entity.setBeschreibung(gruppe.getBeschreibung());
entity.setVon(gruppe.getVon());
if (gruppe.getBild() != null) {
entity.setBild(java.util.Base64.getDecoder().decode(gruppe.getBild()));
}
return ResponseEntity.noContent().build();
}
@DeleteMapping("/aufgabengruppen/{id}")
public ResponseEntity<Void> deleteAufgabengruppe(@PathVariable("id") UUID id, Principal principal) {
requireAdmin(principal);
AufgabenGruppeEntity entity = aufgabenGruppeRepository.findById(id)
.orElseThrow(() -> new org.springframework.web.server.ResponseStatusException(
org.springframework.http.HttpStatus.NOT_FOUND));
if (entity.getUserId() != null) {
return ResponseEntity.status(403).build(); // Nur System-Gruppen
}
aufgabenGruppeRepository.delete(entity);
return ResponseEntity.noContent().build();
}
// ── Toys ─────────────────────────────────────────────────────────────────
@GetMapping("/toys")
public ResponseEntity<List<Toy>> getToys(Principal principal) {
requireAdmin(principal);
List<ToyEntity> list = toyRepository.findByUserIdIsNull(PageRequest.of(0, 1000, Sort.by(Sort.Direction.ASC, "name"))).getContent();
return ResponseEntity.ok(list.stream().map(ToyEntity::toToy).toList());
}
@PostMapping("/toys")
public ResponseEntity<Toy> createToy(@RequestBody Toy toy, Principal principal) {
requireAdmin(principal);
if (toyRepository.existsByNameIgnoreCaseAndUserIdIsNull(toy.getName())) {
return ResponseEntity.status(409).build();
}
toy.setUserId(null);
ToyEntity entity = ToyEntity.create(toy);
toyRepository.save(entity);
return ResponseEntity.status(201).body(entity.toToy());
}
@PutMapping("/toys/{id}")
public ResponseEntity<Void> updateToy(@PathVariable("id") UUID id,
@RequestBody Toy toy, Principal principal) {
requireAdmin(principal);
ToyEntity entity = toyRepository.findById(id)
.orElseThrow(() -> new org.springframework.web.server.ResponseStatusException(
org.springframework.http.HttpStatus.NOT_FOUND));
if (toyRepository.existsByNameIgnoreCaseAndUserIdIsNullAndToyIdNot(toy.getName(), id)) {
return ResponseEntity.status(409).build();
}
entity.setName(toy.getName());
entity.setBeschreibung(toy.getBeschreibung());
if (toy.getBild() != null) {
entity.setBild(java.util.Base64.getDecoder().decode(toy.getBild()));
}
return ResponseEntity.noContent().build();
}
@DeleteMapping("/toys/{id}")
public ResponseEntity<Void> deleteToy(@PathVariable("id") UUID id, Principal principal) {
requireAdmin(principal);
ToyEntity entity = toyRepository.findById(id)
.orElseThrow(() -> new org.springframework.web.server.ResponseStatusException(
org.springframework.http.HttpStatus.NOT_FOUND));
long usage = toyRepository.countAufgabeUsage(id)
+ toyRepository.countStrafeUsage(id)
+ toyRepository.countSperreUsage(id);
if (usage > 0) {
return ResponseEntity.status(409).build();
}
toyRepository.delete(entity);
return ResponseEntity.noContent().build();
}
// ── Admin-Verwaltung (nur SUPERADMIN) ────────────────────────────────────
@GetMapping("/admins")
public ResponseEntity<List<AdminDto>> getAdmins(Principal principal) {
requireSuperAdmin(principal);
return ResponseEntity.ok(adminRepository.findAll().stream().map(this::toDto).toList());
}
@PostMapping("/admins")
public ResponseEntity<AdminDto> createAdmin(@RequestBody CreateAdminRequest request, Principal principal) {
requireSuperAdmin(principal);
if (!userRepository.existsById(request.userId())) {
return ResponseEntity.status(404).build();
}
if (adminRepository.existsByUserId(request.userId())) {
return ResponseEntity.status(409).build();
}
AdminEntity entity = AdminEntity.create(request.userId(), request.rolle());
adminRepository.save(entity);
return ResponseEntity.status(201).body(toDto(entity));
}
@DeleteMapping("/admins/{id}")
public ResponseEntity<Void> deleteAdmin(@PathVariable("id") UUID id, Principal principal) {
var requestingUser = userRepository.findByEmail(principal.getName()).orElseThrow();
requireSuperAdmin(principal);
AdminEntity entity = adminRepository.findById(id)
.orElseThrow(() -> new org.springframework.web.server.ResponseStatusException(
org.springframework.http.HttpStatus.NOT_FOUND));
if (entity.getUserId().equals(requestingUser.getUserId())) {
return ResponseEntity.status(400).build(); // Selbstlöschung verhindern
}
adminRepository.delete(entity);
return ResponseEntity.noContent().build();
}
}

View File

@@ -0,0 +1,38 @@
package de.oaa.xxx.admin;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;
import java.time.LocalDateTime;
import java.util.UUID;
@Getter
@Setter
@Entity
@Table(name = "admin")
public class AdminEntity {
@Id
@Column
private UUID adminId;
@Column(nullable = false)
private UUID userId;
@Enumerated(EnumType.STRING)
@Column(length = 20, nullable = false)
private AdminRolle rolle;
@Column(nullable = false)
private LocalDateTime createdAt;
public static AdminEntity create(UUID userId, AdminRolle rolle) {
AdminEntity entity = new AdminEntity();
entity.setAdminId(UUID.randomUUID());
entity.setUserId(userId);
entity.setRolle(rolle);
entity.setCreatedAt(LocalDateTime.now());
return entity;
}
}

View File

@@ -0,0 +1,13 @@
package de.oaa.xxx.admin;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;
import java.util.UUID;
public interface AdminRepository extends JpaRepository<AdminEntity, UUID> {
Optional<AdminEntity> findByUserId(UUID userId);
boolean existsByUserId(UUID userId);
}

View File

@@ -0,0 +1,5 @@
package de.oaa.xxx.admin;
public enum AdminRolle {
ADMIN, SUPERADMIN
}

View File

@@ -61,6 +61,7 @@ public class SecurityConfig {
.requestMatchers("/gruppen.html").authenticated()
.requestMatchers("/gruppe.html").authenticated()
.requestMatchers("/feed.html").authenticated()
.requestMatchers("/admin.html").authenticated()
.requestMatchers("/communityvotes.html").authenticated()
.requestMatchers("/keyholder.html").authenticated()
.requestMatchers("/meine-locks.html").authenticated()

View File

@@ -0,0 +1,35 @@
package de.oaa.xxx.meldung;
import de.oaa.xxx.user.UserRepository;
import org.springframework.http.ResponseEntity;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import java.security.Principal;
import java.util.UUID;
@RestController
@RequestMapping("/meldung")
public class MeldungController {
private final MeldungRepository meldungRepository;
private final UserRepository userRepository;
public MeldungController(MeldungRepository meldungRepository, UserRepository userRepository) {
this.meldungRepository = meldungRepository;
this.userRepository = userRepository;
}
record MeldungRequest(MeldungZielTyp zielTyp, UUID zielId, String grund) {}
@PostMapping
@Transactional
public ResponseEntity<Void> melden(@RequestBody MeldungRequest request, Principal principal) {
var user = userRepository.findByEmail(principal.getName()).orElseThrow();
if (meldungRepository.existsByMelderIdAndZielTypAndZielId(user.getUserId(), request.zielTyp(), request.zielId())) {
return ResponseEntity.status(409).build();
}
meldungRepository.save(MeldungEntity.create(user.getUserId(), request.zielTyp(), request.zielId(), request.grund()));
return ResponseEntity.status(201).build();
}
}

View File

@@ -0,0 +1,59 @@
package de.oaa.xxx.meldung;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;
import java.time.LocalDateTime;
import java.util.UUID;
@Getter
@Setter
@Entity
@Table(name = "meldung", uniqueConstraints = {
@UniqueConstraint(columnNames = {"melder_id", "ziel_typ", "ziel_id"})
})
public class MeldungEntity {
@Id
@Column
private UUID meldungId;
@Column(name = "melder_id", nullable = false)
private UUID melderId;
@Enumerated(EnumType.STRING)
@Column(name = "ziel_typ", length = 10, nullable = false)
private MeldungZielTyp zielTyp;
@Column(name = "ziel_id", nullable = false)
private UUID zielId;
@Column(columnDefinition = "TEXT")
private String grund;
@Column(nullable = false)
private LocalDateTime gemeldetAt;
@Enumerated(EnumType.STRING)
@Column(length = 20, nullable = false)
private MeldungStatus status;
@Column(name = "bearbeitet_von")
private UUID bearbeitetVon;
@Column(name = "bearbeitet_at")
private LocalDateTime bearbeitetAt;
public static MeldungEntity create(UUID melderId, MeldungZielTyp zielTyp, UUID zielId, String grund) {
MeldungEntity entity = new MeldungEntity();
entity.setMeldungId(UUID.randomUUID());
entity.setMelderId(melderId);
entity.setZielTyp(zielTyp);
entity.setZielId(zielId);
entity.setGrund(grund);
entity.setGemeldetAt(LocalDateTime.now());
entity.setStatus(MeldungStatus.OFFEN);
return entity;
}
}

View File

@@ -0,0 +1,15 @@
package de.oaa.xxx.meldung;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
import java.util.UUID;
public interface MeldungRepository extends JpaRepository<MeldungEntity, UUID> {
List<MeldungEntity> findAllByOrderByGemeldetAtDesc();
List<MeldungEntity> findByStatusOrderByGemeldetAtDesc(MeldungStatus status);
boolean existsByMelderIdAndZielTypAndZielId(UUID melderId, MeldungZielTyp zielTyp, UUID zielId);
}

View File

@@ -0,0 +1,5 @@
package de.oaa.xxx.meldung;
public enum MeldungStatus {
OFFEN, BEARBEITET, ABGELEHNT
}

View File

@@ -0,0 +1,5 @@
package de.oaa.xxx.meldung;
public enum MeldungZielTyp {
POST, PROFIL
}

View File

@@ -19,7 +19,7 @@ public class ActivationController {
}
@GetMapping("/{uuid}")
public ResponseEntity<Void> activate(@PathVariable String uuid) {
public ResponseEntity<Void> activate(@PathVariable("uuid") String uuid) {
try {
String email = registrationService.activate(uuid);
String redirect = "/login.html?email=" + java.net.URLEncoder.encode(email, java.nio.charset.StandardCharsets.UTF_8);

View File

@@ -51,16 +51,24 @@ public class RegistrationController {
LOGGER.warn("Registrierung abgelehnt Mindestalter nicht erreicht");
return ResponseEntity.status(422).build();
}
if (registrationRepository.findByEmail(registration.getEmail()).isPresent()
|| userRepository.findByEmail(registration.getEmail()).isPresent()) {
// Bereits aktivierte User blockieren
if (userRepository.findByEmail(registration.getEmail()).isPresent()) {
LOGGER.warn("User mit E-Mail {} bereits vorhanden", registration.getEmail());
return ResponseEntity.badRequest().build();
}
if (registrationRepository.findByName(registration.getName()).isPresent()
|| userRepository.findByName(registration.getName()).isPresent()) {
if (userRepository.findByName(registration.getName()).isPresent()) {
LOGGER.warn("User mit Name {} bereits vorhanden", registration.getName());
return ResponseEntity.status(409).build();
}
// Noch nicht aktivierte Registrierungen mit gleicher E-Mail oder Name überschreiben
registrationRepository.findByEmail(registration.getEmail()).ifPresent(old -> {
LOGGER.info("Überschreibe nicht aktivierte Registrierung mit E-Mail {}", registration.getEmail());
registrationRepository.delete(old);
});
registrationRepository.findByName(registration.getName()).ifPresent(old -> {
LOGGER.info("Überschreibe nicht aktivierte Registrierung mit Name {}", registration.getName());
registrationRepository.delete(old);
});
// Passwort serverseitig mit BCrypt hashen
registration.setPassword(passwordEncoder.encode(registration.getPassword()));
RegistrationEntity entity = RegistrationEntity.create(registration);
@@ -72,7 +80,7 @@ public class RegistrationController {
String uuid = entity.getRegistrationId().toString();
String activationLink = baseUrl + "/activation/" + uuid;
String activatePageUrl = baseUrl + "/activate.html";
email.setText(mailTemplateService.buildActivationMail(registration.getName(), activationLink, activatePageUrl, uuid));
email.setText(mailTemplateService.buildActivationMail(registration.getName(), activationLink, activatePageUrl, entity.getActivationCode()));
if (!mailService.send(email)) {
registrationRepository.delete(entity);

View File

@@ -7,6 +7,7 @@ import jakarta.persistence.Table;
import lombok.Getter;
import lombok.Setter;
import java.security.SecureRandom;
import java.time.LocalDate;
import java.util.UUID;
@@ -27,6 +28,8 @@ public class RegistrationEntity {
private String password;
@Column
private Boolean activated;
@Column(length = 6)
private String activationCode;
@Column
private LocalDate geburtsdatum;
@@ -50,6 +53,7 @@ public class RegistrationEntity {
entity.setRegistrationId(UUID.randomUUID());
entity.setEmail(registration.getEmail());
entity.setActivated(Boolean.FALSE);
entity.setActivationCode(String.format("%06d", new SecureRandom().nextInt(1_000_000)));
entity.setName(registration.getName());
entity.setPassword(registration.getPassword());
entity.setGeburtsdatum(registration.getGeburtsdatum());

View File

@@ -9,4 +9,5 @@ public interface RegistrationRepository extends JpaRepository<RegistrationEntity
Optional<RegistrationEntity> findByEmail(String email);
Optional<RegistrationEntity> findByName(String name);
Optional<RegistrationEntity> findByActivationCode(String activationCode);
}

View File

@@ -32,17 +32,18 @@ public class RegistrationService {
* @throws IllegalArgumentException wenn UUID ungültig oder Registration nicht gefunden
* @throws IllegalStateException wenn Registration bereits aktiviert
*/
public String activate(String uuid) {
UUID registrationId;
public String activate(String token) {
RegistrationEntity registration;
try {
registrationId = UUID.fromString(uuid);
UUID registrationId = UUID.fromString(token);
registration = registrationRepository.findById(registrationId)
.orElseThrow(() -> new IllegalArgumentException("Registration nicht gefunden: " + token));
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException("Ungültige UUID: " + uuid);
// Kein UUID-Format → nach kurzem Aktivierungscode suchen
registration = registrationRepository.findByActivationCode(token)
.orElseThrow(() -> new IllegalArgumentException("Aktivierungscode ungültig: " + token));
}
RegistrationEntity registration = registrationRepository.findById(registrationId)
.orElseThrow(() -> new IllegalArgumentException("Registration nicht gefunden: " + uuid));
if (Boolean.TRUE.equals(registration.getActivated())) {
throw new IllegalStateException("Registration bereits aktiviert");
}
@@ -52,7 +53,7 @@ public class RegistrationService {
registration.setActivated(Boolean.TRUE);
registrationRepository.save(registration);
LOGGER.info("Registration {} aktiviert, User {} angelegt", uuid, registration.getEmail());
LOGGER.info("Registration {} aktiviert, User {} angelegt", token, registration.getEmail());
return registration.getEmail();
}
}

View File

@@ -24,7 +24,7 @@ public class SseService {
}
public SseEmitter subscribe(UUID userId) {
SseEmitter emitter = new SseEmitter(30_000L); // 30 s Client reconnects automatically
SseEmitter emitter = new SseEmitter(300_000L); // 5 min Client reconnects automatically
emitters.computeIfAbsent(userId, k -> new CopyOnWriteArrayList<>()).add(emitter);
Runnable cleanup = () -> removeEmitter(userId, emitter);
emitter.onCompletion(cleanup);

View File

@@ -1,7 +1,7 @@
# Datasource
spring.datasource.url=jdbc:mysql://localhost:3306/xxxthegame?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC
spring.datasource.url=jdbc:mysql://localhost:3306/xxx_sphere?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC
spring.datasource.username=${DB_USER:xxx}
spring.datasource.password=${DB_PASSWORD:xxxthegame$123!}
spring.datasource.password=${DB_PASSWORD:xxx}
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# JPA / Hibernate

View File

@@ -1,7 +1,7 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta charset="UTF-8">
<link rel="icon" href="/img/icon.png" type="image/png">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>XXX The Game Aktivierung</title>
@@ -19,7 +19,7 @@
</p>
<label for="uuid" style="margin-top:1.5rem;">Aktivierungscode</label>
<input type="text" id="uuid" placeholder="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" autocomplete="off" />
<input type="text" id="uuid" placeholder="6-stelliger Code" autocomplete="off" inputmode="numeric" maxlength="6" />
<button class="full-width" id="activateBtn" onclick="activate()">Jetzt aktivieren</button>

File diff suppressed because it is too large Load Diff

View File

@@ -464,6 +464,7 @@
<script src="/js/icons.js"></script>
<script src="/js/sidebar.js"></script>
<script src="/js/social-sidebar.js"></script>
<script src="/js/meldung.js"></script>
<script>
// ── State ──
const params = new URLSearchParams(window.location.search);
@@ -629,6 +630,7 @@
} else {
html += `<button id="friendActionBtn" onclick="addFriend()">+ Freund hinzufügen</button>`;
}
html += ` <button onclick="openMeldungDialog('PROFIL','${profile.userId}')" style="background:none;border:1px solid var(--color-secondary);color:var(--color-muted);border-radius:6px;padding:0.4rem 0.8rem;cursor:pointer;font-size:0.85rem;">⚑ Melden</button>`;
actions.innerHTML = html;
}
}

View File

@@ -1,7 +1,7 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta charset="UTF-8">
<link rel="icon" href="/img/icon.png" type="image/png">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Feed XXX The Game</title>
@@ -163,9 +163,10 @@
</div>
<script src="/js/shared.js"></script>
<script src="/js/icons.js"></script>
<script src="/js/icons.js"></script>
<script src="/js/sidebar.js"></script>
<script src="/js/social-sidebar.js"></script>
<script src="/js/meldung.js"></script>
<script>
// ── State ──
let myUserId = null;
@@ -260,6 +261,9 @@
const deleteBtn = canDelete
? `<button class="post-action-btn post-delete" onclick="event.stopPropagation(); deletePost('${p.postId}')">🗑</button>`
: '';
const meldenBtn = p.authorId !== myUserId
? `<button class="post-action-btn" onclick="event.stopPropagation(); openMeldungDialog('POST','${p.postId}')" title="Melden" style="color:var(--color-muted)">⚑</button>`
: '';
const gruppeIdAttr = p.gruppeId ? ` data-gruppe-id="${p.gruppeId}"` : '';
return `<div class="post-card" id="pc-${p.postId}"${gruppeIdAttr} onclick="openLb('${p.postId}','${p.postType}')" style="cursor:pointer;">
@@ -281,6 +285,7 @@
<button class="post-action-btn" onclick="event.stopPropagation(); openLb('${p.postId}','${p.postType}')">
💬 <span id="kc-${p.postId}">${p.kommentarCount}</span>
</button>
${meldenBtn}
</div>
</div>`;
}

View File

@@ -0,0 +1,87 @@
/**
* Wiederverwendbares Meldungs-Modul.
* Bietet openMeldungDialog(zielTyp, zielId) und renderMeldenBtn(zielTyp, zielId).
*/
(function () {
// Dialog einmalig in den DOM einfügen
if (!document.getElementById('meldungDialog')) {
document.body.insertAdjacentHTML('beforeend', `
<div id="meldungDialog" style="
display:none; position:fixed; inset:0; z-index:9999;
background:rgba(0,0,0,0.6); align-items:center; justify-content:center;">
<div style="background:var(--color-card);border:1px solid var(--color-secondary);
border-radius:12px;padding:1.5rem;width:min(420px,90vw);position:relative;">
<h3 style="margin:0 0 1rem 0;color:var(--color-primary)">Inhalt melden</h3>
<p id="meldungDialogLabel" style="color:var(--color-muted);font-size:0.9rem;margin:0 0 0.75rem 0;"></p>
<textarea id="meldungGrund" placeholder="Grund (optional)"
style="width:100%;box-sizing:border-box;padding:0.5rem;border-radius:6px;
border:1px solid var(--color-secondary);background:var(--color-card);
color:var(--color-text);resize:vertical;min-height:80px;font-family:inherit;"></textarea>
<div style="display:flex;gap:0.5rem;justify-content:flex-end;margin-top:1rem;">
<button id="meldungAbbrechen" style="padding:0.45rem 1rem;border-radius:6px;
border:1px solid var(--color-secondary);background:transparent;
color:var(--color-text);cursor:pointer;">Abbrechen</button>
<button id="meldungSenden" style="padding:0.45rem 1rem;border-radius:6px;
border:none;background:var(--color-primary);color:#fff;cursor:pointer;font-weight:600;">
Melden</button>
</div>
<p id="meldungMsg" style="margin:0.5rem 0 0 0;font-size:0.85rem;color:var(--color-primary);display:none;"></p>
</div>
</div>
`);
document.getElementById('meldungAbbrechen').addEventListener('click', () => closeMeldungDialog());
document.getElementById('meldungDialog').addEventListener('click', function (e) {
if (e.target === this) closeMeldungDialog();
});
}
let _zielTyp = null, _zielId = null;
window.openMeldungDialog = function (zielTyp, zielId) {
_zielTyp = zielTyp;
_zielId = zielId;
document.getElementById('meldungGrund').value = '';
document.getElementById('meldungMsg').style.display = 'none';
document.getElementById('meldungDialogLabel').textContent =
zielTyp === 'PROFIL' ? 'Profil melden' : 'Post melden';
document.getElementById('meldungDialog').style.display = 'flex';
document.getElementById('meldungSenden').onclick = async function () {
const grund = document.getElementById('meldungGrund').value.trim();
const r = await fetch('/meldung', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ zielTyp: _zielTyp, zielId: _zielId, grund: grund || null })
});
const msg = document.getElementById('meldungMsg');
msg.style.display = 'block';
if (r.status === 201) {
msg.style.color = 'var(--color-success, #2ecc71)';
msg.textContent = 'Meldung wurde übermittelt.';
setTimeout(closeMeldungDialog, 1500);
} else if (r.status === 409) {
msg.style.color = 'var(--color-primary)';
msg.textContent = 'Du hast diesen Inhalt bereits gemeldet.';
} else {
msg.style.color = 'var(--color-primary)';
msg.textContent = 'Fehler beim Senden.';
}
};
};
window.closeMeldungDialog = function () {
document.getElementById('meldungDialog').style.display = 'none';
};
/**
* Erzeugt einen kleinen "Melden"-Button-HTML-String.
* Verwendung: in innerHTML-Templates, wo onclick genutzt werden kann.
*/
window.renderMeldenBtn = function (zielTyp, zielId) {
return `<button onclick="openMeldungDialog('${zielTyp}','${zielId}')"
style="background:none;border:none;color:var(--color-muted,#888);
font-size:0.8rem;cursor:pointer;padding:0.2rem 0.4rem;border-radius:4px;"
title="Melden">⚑ Melden</button>`;
};
})();

View File

@@ -74,6 +74,9 @@
</li>`;
}).join('');
const adminCls = path === '/admin.html' ? ' class="active"' : '';
const adminItem = `<li id="navAdminLink" style="display:none"><a href="/admin.html"${adminCls}><span class="icon">${I('ADMIN') || '⚙'}</span> Administration</a></li>`;
document.body.insertAdjacentHTML('afterbegin', `
<div class="sidebar-overlay" id="sidebarOverlay"></div>
<button class="burger" id="burgerBtn" aria-label="Menü öffnen">
@@ -89,6 +92,8 @@
${socialNav}
<li><hr style="border:none;border-top:1px solid var(--color-secondary);margin:0.4rem 1rem;"></li>
${nav}
<li><hr style="border:none;border-top:1px solid var(--color-secondary);margin:0.4rem 1rem;" id="navAdminDivider" style="display:none"></li>
${adminItem}
</ul>
</aside>
`);
@@ -155,6 +160,17 @@
}
}
} catch (_) {}
// Admin-Link
try {
const adminRes = await fetch('/admin/me');
if (adminRes.ok) {
const navAdminLink = document.getElementById('navAdminLink');
const navAdminDivider = document.getElementById('navAdminDivider');
if (navAdminLink) navAdminLink.style.display = '';
if (navAdminDivider) navAdminDivider.style.display = '';
}
} catch (_) {}
})
.catch(() => {});

View File

@@ -86,9 +86,17 @@
es.onerror = () => {
es.close();
setTimeout(connectSse, 5000);
// Vor dem Reconnect prüfen ob noch eingeloggt (verhindert Endlos-Schleife bei abgelaufener Session)
setTimeout(() => {
fetch('/login/me', { method: 'GET' })
.then(r => { if (r.ok) connectSse(); })
.catch(() => {});
}, 5000);
};
}
connectSse();
// SSE nur starten wenn authentifiziert verhindert Fehler-Spam bei nicht eingeloggten Seiten
fetch('/login/me', { method: 'GET' })
.then(r => { if (r.ok) connectSse(); })
.catch(() => {});
})();