diff --git a/app/src/main/frontend/views/GameRequestView.tsx b/app/src/main/frontend/views/GameRequestView.tsx
index 03a784a..427229e 100644
--- a/app/src/main/frontend/views/GameRequestView.tsx
+++ b/app/src/main/frontend/views/GameRequestView.tsx
@@ -35,9 +35,11 @@ export default function GameRequestView() {
const gameRequests = useSnapshot(gameRequestState).gameRequests;
const [areGameRequestsEnabled, setAreGameRequestsEnabled] = useState(false);
+ const [areGuestsAllowedToRequestGames, setAreGuestsAllowedToRequestGames] = useState(false);
useEffect(() => {
ConfigEndpoint.areGameRequestsEnabled().then(setAreGameRequestsEnabled);
+ ConfigEndpoint.areGuestsAllowedToRequestGames().then(setAreGuestsAllowedToRequestGames);
}, []);
const [searchTerm, setSearchTerm] = useState("");
@@ -168,7 +170,7 @@ export default function GameRequestView() {
color="primary"
startContent={}
onPress={requestGameModal.onOpen}
- isDisabled={!areGameRequestsEnabled}>
+ isDisabled={!areGameRequestsEnabled || (!auth.state.user && !areGuestsAllowedToRequestGames)}>
Request a Game
diff --git a/app/src/main/frontend/views/MainLayout.tsx b/app/src/main/frontend/views/MainLayout.tsx
index 4a00641..a8aa302 100644
--- a/app/src/main/frontend/views/MainLayout.tsx
+++ b/app/src/main/frontend/views/MainLayout.tsx
@@ -94,18 +94,16 @@ export default function MainLayout() {
}
- {auth.state.user &&
-
-
-
-
-
- }
+
+
+
+
+
{isAdmin(auth) &&
diff --git a/app/src/main/kotlin/org/gameyfin/app/config/ConfigEndpoint.kt b/app/src/main/kotlin/org/gameyfin/app/config/ConfigEndpoint.kt
index 395c038..67b11ff 100644
--- a/app/src/main/kotlin/org/gameyfin/app/config/ConfigEndpoint.kt
+++ b/app/src/main/kotlin/org/gameyfin/app/config/ConfigEndpoint.kt
@@ -59,4 +59,9 @@ class ConfigEndpoint(
@DynamicPublicAccess
@AnonymousAllowed
fun areGameRequestsEnabled(): Boolean = configService.get(ConfigProperties.Requests.Games.Enabled) == true
+
+ @DynamicPublicAccess
+ @AnonymousAllowed
+ fun areGuestsAllowedToRequestGames(): Boolean =
+ configService.get(ConfigProperties.Requests.Games.AllowGuestsToRequestGames) == true
}
diff --git a/app/src/main/kotlin/org/gameyfin/app/requests/GameRequestService.kt b/app/src/main/kotlin/org/gameyfin/app/requests/GameRequestService.kt
index c56ee14..3f81d65 100644
--- a/app/src/main/kotlin/org/gameyfin/app/requests/GameRequestService.kt
+++ b/app/src/main/kotlin/org/gameyfin/app/requests/GameRequestService.kt
@@ -91,7 +91,7 @@ class GameRequestService(
// Check if guests are allowed to create requests
if (config.get(ConfigProperties.Requests.Games.AllowGuestsToRequestGames) != true && currentUser == null) {
- throw EndpointException("Only registered users can create game requests")
+ throw EndpointException("Only registered users can submit game requests")
}
// Check if user has too many open requests (0 means no limit per user)
diff --git a/app/src/main/kotlin/org/gameyfin/app/users/UserService.kt b/app/src/main/kotlin/org/gameyfin/app/users/UserService.kt
index b98b41b..c3d58a4 100644
--- a/app/src/main/kotlin/org/gameyfin/app/users/UserService.kt
+++ b/app/src/main/kotlin/org/gameyfin/app/users/UserService.kt
@@ -99,7 +99,9 @@ class UserService(
if (principal is OidcUser) {
val oidcUser = org.gameyfin.app.users.entities.User(principal)
- val userInfoDto = oidcUser.toExtendedUserInfoDto()
+ val user = userRepository.findByOidcProviderId(oidcUser.oidcProviderId!!)
+ ?: throw UsernameNotFoundException("Unknown OIDC user with provider ID '${oidcUser.oidcProviderId}'")
+ val userInfoDto = user.toExtendedUserInfoDto()
userInfoDto.roles = roleService.extractGrantedAuthorities(principal.authorities)
.mapNotNull { Role.safeValueOf(it.authority) }
return userInfoDto
diff --git a/app/src/main/kotlin/org/gameyfin/app/users/entities/User.kt b/app/src/main/kotlin/org/gameyfin/app/users/entities/User.kt
index f170bb0..9c5994f 100644
--- a/app/src/main/kotlin/org/gameyfin/app/users/entities/User.kt
+++ b/app/src/main/kotlin/org/gameyfin/app/users/entities/User.kt
@@ -1,14 +1,15 @@
package org.gameyfin.app.users.entities
-import org.gameyfin.app.games.entities.Image
import jakarta.persistence.*
import org.gameyfin.app.core.Role
import org.gameyfin.app.core.security.EncryptionConverter
+import org.gameyfin.app.games.entities.Image
import org.springframework.security.oauth2.core.oidc.user.OidcUser
@Entity
@Table(name = "users")
+@EntityListeners(UserEntityListener::class)
class User(
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
diff --git a/app/src/main/kotlin/org/gameyfin/app/users/entities/UserEntityListener.kt b/app/src/main/kotlin/org/gameyfin/app/users/entities/UserEntityListener.kt
new file mode 100644
index 0000000..9779485
--- /dev/null
+++ b/app/src/main/kotlin/org/gameyfin/app/users/entities/UserEntityListener.kt
@@ -0,0 +1,25 @@
+package org.gameyfin.app.users.entities
+
+import jakarta.persistence.EntityManager
+import jakarta.persistence.PreRemove
+import org.gameyfin.app.requests.entities.GameRequest
+import org.gameyfin.app.util.EntityManagerHolder
+
+class UserEntityListener {
+
+ @PreRemove
+ fun preRemove(user: User) {
+ val entityManager: EntityManager = EntityManagerHolder.getEntityManager()
+
+ // Remove user from all GameRequest voters and requester fields
+ val gameRequests = entityManager.createQuery(
+ "SELECT gr FROM GameRequest gr WHERE :user MEMBER OF gr.voters OR gr.requester = :user",
+ GameRequest::class.java
+ ).setParameter("user", user).resultList
+ for (gr in gameRequests) {
+ gr.voters.remove(user)
+ if (gr.requester == user) gr.requester = null
+ entityManager.merge(gr)
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/kotlin/org/gameyfin/app/util/EntityManagerHolder.kt b/app/src/main/kotlin/org/gameyfin/app/util/EntityManagerHolder.kt
new file mode 100644
index 0000000..08059b7
--- /dev/null
+++ b/app/src/main/kotlin/org/gameyfin/app/util/EntityManagerHolder.kt
@@ -0,0 +1,19 @@
+package org.gameyfin.app.util
+
+import jakarta.persistence.EntityManager
+import org.springframework.context.ApplicationContext
+import org.springframework.context.ApplicationContextAware
+import org.springframework.stereotype.Component
+
+@Component
+object EntityManagerHolder : ApplicationContextAware {
+ private var entityManager: EntityManager? = null
+
+ override fun setApplicationContext(context: ApplicationContext) {
+ entityManager = context.getBean(EntityManager::class.java)
+ }
+
+ fun getEntityManager(): EntityManager {
+ return entityManager ?: throw IllegalStateException("EntityManager not set")
+ }
+}
\ No newline at end of file