mirror of
https://github.com/BrenBroZAYT/gameyfin.git
synced 2026-06-16 16:20:04 +00:00
BE implementation of request system
This commit is contained in:
+1
-1
@@ -265,4 +265,4 @@
|
|||||||
"disableUsageStatistics": true,
|
"disableUsageStatistics": true,
|
||||||
"hash": "962eccc3fa0735d5234901be4f9e384096113c45bec22564a53688096d62aef4"
|
"hash": "962eccc3fa0735d5234901be4f9e384096113c45bec22564a53688096d62aef4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
package org.gameyfin.app.core.filesystem
|
package org.gameyfin.app.core.filesystem
|
||||||
|
|
||||||
import org.gameyfin.app.config.ConfigService
|
|
||||||
import io.github.oshai.kotlinlogging.KotlinLogging
|
import io.github.oshai.kotlinlogging.KotlinLogging
|
||||||
import org.apache.commons.io.FilenameUtils
|
import org.apache.commons.io.FilenameUtils
|
||||||
import org.gameyfin.app.config.ConfigProperties
|
import org.gameyfin.app.config.ConfigProperties
|
||||||
import org.gameyfin.app.libraries.Library
|
import org.gameyfin.app.config.ConfigService
|
||||||
|
import org.gameyfin.app.libraries.entities.Library
|
||||||
import org.springframework.stereotype.Service
|
import org.springframework.stereotype.Service
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.nio.file.FileSystems
|
import java.nio.file.FileSystems
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import org.gameyfin.app.games.dto.*
|
|||||||
import org.gameyfin.app.games.entities.*
|
import org.gameyfin.app.games.entities.*
|
||||||
import org.gameyfin.app.games.extensions.toDtos
|
import org.gameyfin.app.games.extensions.toDtos
|
||||||
import org.gameyfin.app.games.repositories.GameRepository
|
import org.gameyfin.app.games.repositories.GameRepository
|
||||||
import org.gameyfin.app.libraries.Library
|
import org.gameyfin.app.libraries.entities.Library
|
||||||
import org.gameyfin.app.media.ImageService
|
import org.gameyfin.app.media.ImageService
|
||||||
import org.gameyfin.app.users.UserService
|
import org.gameyfin.app.users.UserService
|
||||||
import org.gameyfin.pluginapi.gamemetadata.*
|
import org.gameyfin.pluginapi.gamemetadata.*
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package org.gameyfin.app.games.entities
|
package org.gameyfin.app.games.entities
|
||||||
|
|
||||||
import jakarta.persistence.*
|
import jakarta.persistence.*
|
||||||
import org.gameyfin.app.libraries.Library
|
import org.gameyfin.app.libraries.entities.Library
|
||||||
import org.gameyfin.pluginapi.gamemetadata.GameFeature
|
import org.gameyfin.pluginapi.gamemetadata.GameFeature
|
||||||
import org.gameyfin.pluginapi.gamemetadata.Genre
|
import org.gameyfin.pluginapi.gamemetadata.Genre
|
||||||
import org.gameyfin.pluginapi.gamemetadata.PlayerPerspective
|
import org.gameyfin.pluginapi.gamemetadata.PlayerPerspective
|
||||||
|
|||||||
@@ -12,19 +12,19 @@ import org.gameyfin.app.games.extensions.toUserDto
|
|||||||
class GameEntityListener {
|
class GameEntityListener {
|
||||||
@PostPersist
|
@PostPersist
|
||||||
fun created(game: Game) {
|
fun created(game: Game) {
|
||||||
GameService.Companion.emitUser(GameUserEvent.Created(game.toUserDto()))
|
GameService.emitUser(GameUserEvent.Created(game.toUserDto()))
|
||||||
GameService.Companion.emitAdmin(GameAdminEvent.Created(game.toAdminDto()))
|
GameService.emitAdmin(GameAdminEvent.Created(game.toAdminDto()))
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostUpdate
|
@PostUpdate
|
||||||
fun updated(game: Game) {
|
fun updated(game: Game) {
|
||||||
GameService.Companion.emitUser(GameUserEvent.Updated(game.toUserDto()))
|
GameService.emitUser(GameUserEvent.Updated(game.toUserDto()))
|
||||||
GameService.Companion.emitAdmin(GameAdminEvent.Updated(game.toAdminDto()))
|
GameService.emitAdmin(GameAdminEvent.Updated(game.toAdminDto()))
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostRemove
|
@PostRemove
|
||||||
fun deleted(game: Game) {
|
fun deleted(game: Game) {
|
||||||
GameService.Companion.emitUser(GameUserEvent.Deleted(game.id!!))
|
GameService.emitUser(GameUserEvent.Deleted(game.id!!))
|
||||||
GameService.Companion.emitAdmin(GameAdminEvent.Deleted(game.id!!))
|
GameService.emitAdmin(GameAdminEvent.Deleted(game.id!!))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,6 +2,7 @@ package org.gameyfin.app.libraries
|
|||||||
|
|
||||||
import org.gameyfin.app.games.GameService
|
import org.gameyfin.app.games.GameService
|
||||||
import org.gameyfin.app.games.entities.Game
|
import org.gameyfin.app.games.entities.Game
|
||||||
|
import org.gameyfin.app.libraries.entities.Library
|
||||||
import org.springframework.stereotype.Service
|
import org.springframework.stereotype.Service
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.gameyfin.app.libraries
|
package org.gameyfin.app.libraries
|
||||||
|
|
||||||
|
import org.gameyfin.app.libraries.entities.Library
|
||||||
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
|
||||||
|
|
||||||
|
|||||||
@@ -4,9 +4,8 @@ import io.github.oshai.kotlinlogging.KotlinLogging
|
|||||||
import org.gameyfin.app.core.filesystem.FilesystemService
|
import org.gameyfin.app.core.filesystem.FilesystemService
|
||||||
import org.gameyfin.app.games.GameService
|
import org.gameyfin.app.games.GameService
|
||||||
import org.gameyfin.app.games.entities.Game
|
import org.gameyfin.app.games.entities.Game
|
||||||
import org.gameyfin.app.libraries.dto.LibraryScanProgress
|
import org.gameyfin.app.libraries.dto.*
|
||||||
import org.gameyfin.app.libraries.dto.LibraryScanStatus
|
import org.gameyfin.app.libraries.entities.Library
|
||||||
import org.gameyfin.app.libraries.dto.LibraryScanStep
|
|
||||||
import org.gameyfin.app.libraries.enums.ScanType
|
import org.gameyfin.app.libraries.enums.ScanType
|
||||||
import org.gameyfin.app.libraries.scan.*
|
import org.gameyfin.app.libraries.scan.*
|
||||||
import org.gameyfin.app.media.ImageService
|
import org.gameyfin.app.media.ImageService
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ package org.gameyfin.app.libraries
|
|||||||
import com.vaadin.hilla.exception.EndpointException
|
import com.vaadin.hilla.exception.EndpointException
|
||||||
import io.github.oshai.kotlinlogging.KotlinLogging
|
import io.github.oshai.kotlinlogging.KotlinLogging
|
||||||
import org.gameyfin.app.libraries.dto.*
|
import org.gameyfin.app.libraries.dto.*
|
||||||
|
import org.gameyfin.app.libraries.entities.DirectoryMapping
|
||||||
|
import org.gameyfin.app.libraries.entities.Library
|
||||||
import org.gameyfin.app.libraries.enums.ScanType
|
import org.gameyfin.app.libraries.enums.ScanType
|
||||||
import org.gameyfin.app.libraries.extensions.toDtos
|
import org.gameyfin.app.libraries.extensions.toDtos
|
||||||
import org.springframework.data.repository.findByIdOrNull
|
import org.springframework.data.repository.findByIdOrNull
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package org.gameyfin.app.libraries.dto
|
package org.gameyfin.app.libraries.dto
|
||||||
|
|
||||||
import org.gameyfin.app.libraries.LibraryScanResult
|
|
||||||
import org.gameyfin.app.libraries.enums.ScanType
|
import org.gameyfin.app.libraries.enums.ScanType
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package org.gameyfin.app.libraries
|
package org.gameyfin.app.libraries.dto
|
||||||
|
|
||||||
interface LibraryScanResult {
|
interface LibraryScanResult {
|
||||||
/**
|
/**
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package org.gameyfin.app.libraries
|
package org.gameyfin.app.libraries.entities
|
||||||
|
|
||||||
import jakarta.persistence.*
|
import jakarta.persistence.*
|
||||||
|
|
||||||
+2
-3
@@ -1,8 +1,7 @@
|
|||||||
package org.gameyfin.app.libraries
|
package org.gameyfin.app.libraries.entities
|
||||||
|
|
||||||
import org.gameyfin.app.games.entities.Game
|
|
||||||
import jakarta.persistence.*
|
import jakarta.persistence.*
|
||||||
import org.gameyfin.app.games.entities.LibraryEntityListener
|
import org.gameyfin.app.games.entities.Game
|
||||||
import org.hibernate.annotations.CreationTimestamp
|
import org.hibernate.annotations.CreationTimestamp
|
||||||
import org.hibernate.annotations.UpdateTimestamp
|
import org.hibernate.annotations.UpdateTimestamp
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
+1
-2
@@ -1,9 +1,8 @@
|
|||||||
package org.gameyfin.app.games.entities
|
package org.gameyfin.app.libraries.entities
|
||||||
|
|
||||||
import jakarta.persistence.PostPersist
|
import jakarta.persistence.PostPersist
|
||||||
import jakarta.persistence.PostRemove
|
import jakarta.persistence.PostRemove
|
||||||
import jakarta.persistence.PostUpdate
|
import jakarta.persistence.PostUpdate
|
||||||
import org.gameyfin.app.libraries.Library
|
|
||||||
import org.gameyfin.app.libraries.LibraryService
|
import org.gameyfin.app.libraries.LibraryService
|
||||||
import org.gameyfin.app.libraries.dto.LibraryAdminEvent
|
import org.gameyfin.app.libraries.dto.LibraryAdminEvent
|
||||||
import org.gameyfin.app.libraries.dto.LibraryUserEvent
|
import org.gameyfin.app.libraries.dto.LibraryUserEvent
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
package org.gameyfin.app.libraries.extensions
|
package org.gameyfin.app.libraries.extensions
|
||||||
|
|
||||||
import org.gameyfin.app.core.security.isCurrentUserAdmin
|
import org.gameyfin.app.core.security.isCurrentUserAdmin
|
||||||
import org.gameyfin.app.libraries.Library
|
|
||||||
import org.gameyfin.app.libraries.dto.*
|
import org.gameyfin.app.libraries.dto.*
|
||||||
|
import org.gameyfin.app.libraries.entities.Library
|
||||||
|
|
||||||
|
|
||||||
fun Library.toDto(): LibraryDto {
|
fun Library.toDto(): LibraryDto {
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package org.gameyfin.app.requests
|
||||||
|
|
||||||
|
import com.vaadin.flow.server.auth.AnonymousAllowed
|
||||||
|
import com.vaadin.hilla.Endpoint
|
||||||
|
import jakarta.annotation.security.PermitAll
|
||||||
|
import jakarta.annotation.security.RolesAllowed
|
||||||
|
import org.gameyfin.app.core.Role
|
||||||
|
import org.gameyfin.app.core.annotations.DynamicPublicAccess
|
||||||
|
import org.gameyfin.app.requests.dto.GameRequestCreationDto
|
||||||
|
import org.gameyfin.app.requests.dto.GameRequestEvent
|
||||||
|
import org.gameyfin.app.requests.status.GameRequestStatus
|
||||||
|
import org.springframework.stereotype.Service
|
||||||
|
import reactor.core.publisher.Flux
|
||||||
|
|
||||||
|
@Endpoint
|
||||||
|
@Service
|
||||||
|
@DynamicPublicAccess
|
||||||
|
@AnonymousAllowed
|
||||||
|
class GameRequestEndpoint(
|
||||||
|
private val gameRequestService: GameRequestService
|
||||||
|
) {
|
||||||
|
|
||||||
|
fun subscribe(): Flux<List<GameRequestEvent>> {
|
||||||
|
return GameRequestService.subscribe()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getAll() = gameRequestService.getAll()
|
||||||
|
|
||||||
|
fun create(gameRequest: GameRequestCreationDto) {
|
||||||
|
gameRequestService.createRequest(gameRequest)
|
||||||
|
}
|
||||||
|
|
||||||
|
@PermitAll
|
||||||
|
fun toggleVote(gameRequestId: Long) {
|
||||||
|
gameRequestService.toggleRequestVote(gameRequestId)
|
||||||
|
}
|
||||||
|
|
||||||
|
@RolesAllowed(Role.Names.ADMIN)
|
||||||
|
fun changeStatus(gameRequestId: Long, newStatus: GameRequestStatus) {
|
||||||
|
gameRequestService.changeRequestStatus(gameRequestId, newStatus)
|
||||||
|
}
|
||||||
|
|
||||||
|
@RolesAllowed(Role.Names.ADMIN)
|
||||||
|
fun delete(gameRequestId: Long) {
|
||||||
|
gameRequestService.deleteRequest(gameRequestId)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.gameyfin.app.requests
|
package org.gameyfin.app.requests
|
||||||
|
|
||||||
|
import org.gameyfin.app.requests.entities.GameRequest
|
||||||
import org.springframework.data.jpa.repository.JpaRepository
|
import org.springframework.data.jpa.repository.JpaRepository
|
||||||
|
|
||||||
interface GameRequestRepository : JpaRepository<GameRequest, Long>
|
interface GameRequestRepository : JpaRepository<GameRequest, Long>
|
||||||
@@ -2,8 +2,11 @@ package org.gameyfin.app.requests
|
|||||||
|
|
||||||
import io.github.oshai.kotlinlogging.KotlinLogging
|
import io.github.oshai.kotlinlogging.KotlinLogging
|
||||||
import org.gameyfin.app.core.security.getCurrentAuth
|
import org.gameyfin.app.core.security.getCurrentAuth
|
||||||
import org.gameyfin.app.games.dto.GameUserEvent
|
|
||||||
import org.gameyfin.app.requests.dto.GameRequestCreationDto
|
import org.gameyfin.app.requests.dto.GameRequestCreationDto
|
||||||
|
import org.gameyfin.app.requests.dto.GameRequestDto
|
||||||
|
import org.gameyfin.app.requests.dto.GameRequestEvent
|
||||||
|
import org.gameyfin.app.requests.entities.GameRequest
|
||||||
|
import org.gameyfin.app.requests.extensions.toDtos
|
||||||
import org.gameyfin.app.requests.status.GameRequestStatus
|
import org.gameyfin.app.requests.status.GameRequestStatus
|
||||||
import org.gameyfin.app.users.UserService
|
import org.gameyfin.app.users.UserService
|
||||||
import org.springframework.stereotype.Service
|
import org.springframework.stereotype.Service
|
||||||
@@ -22,9 +25,9 @@ class GameRequestService(
|
|||||||
private val log = KotlinLogging.logger {}
|
private val log = KotlinLogging.logger {}
|
||||||
|
|
||||||
/* Websockets */
|
/* Websockets */
|
||||||
private val gameRequestEvents = Sinks.many().multicast().onBackpressureBuffer<GameUserEvent>(1024, false)
|
private val gameRequestEvents = Sinks.many().multicast().onBackpressureBuffer<GameRequestEvent>(1024, false)
|
||||||
|
|
||||||
fun subscribe(): Flux<List<GameUserEvent>> {
|
fun subscribe(): Flux<List<GameRequestEvent>> {
|
||||||
log.debug { "New user subscription for gameRequestEvents" }
|
log.debug { "New user subscription for gameRequestEvents" }
|
||||||
return gameRequestEvents.asFlux()
|
return gameRequestEvents.asFlux()
|
||||||
.buffer(100.milliseconds.toJavaDuration())
|
.buffer(100.milliseconds.toJavaDuration())
|
||||||
@@ -36,11 +39,16 @@ class GameRequestService(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun emit(event: GameUserEvent) {
|
fun emit(event: GameRequestEvent) {
|
||||||
gameRequestEvents.tryEmitNext(event)
|
gameRequestEvents.tryEmitNext(event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getAll(): List<GameRequestDto> {
|
||||||
|
val entities = gameRequestRepository.findAll()
|
||||||
|
return entities.toDtos()
|
||||||
|
}
|
||||||
|
|
||||||
fun createRequest(gameRequest: GameRequestCreationDto) {
|
fun createRequest(gameRequest: GameRequestCreationDto) {
|
||||||
val currentUser = userService.getByUsername(getCurrentAuth().name)
|
val currentUser = userService.getByUsername(getCurrentAuth().name)
|
||||||
|
|
||||||
@@ -54,4 +62,36 @@ class GameRequestService(
|
|||||||
|
|
||||||
gameRequestRepository.save(gameRequest)
|
gameRequestRepository.save(gameRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun deleteRequest(id: Long) {
|
||||||
|
val gameRequest = gameRequestRepository.findById(id)
|
||||||
|
.orElseThrow { NoSuchElementException("No game request found with id $id") }
|
||||||
|
gameRequestRepository.delete(gameRequest)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun changeRequestStatus(id: Long, status: GameRequestStatus) {
|
||||||
|
val gameRequest = gameRequestRepository.findById(id)
|
||||||
|
.orElseThrow { NoSuchElementException("No game request found with id $id") }
|
||||||
|
gameRequest.status = status
|
||||||
|
gameRequestRepository.save(gameRequest)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toggleRequestVote(id: Long) {
|
||||||
|
val currentUser =
|
||||||
|
userService.getByUsername(getCurrentAuth().name) ?: throw IllegalStateException("Current user not found")
|
||||||
|
val gameRequest = gameRequestRepository.findById(id)
|
||||||
|
.orElseThrow { NoSuchElementException("No game request found with id $id") }
|
||||||
|
|
||||||
|
if (gameRequest.requester?.id == currentUser.id) {
|
||||||
|
throw IllegalStateException("You cannot vote for your own request")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gameRequest.voters.contains(currentUser)) {
|
||||||
|
gameRequest.voters.remove(currentUser)
|
||||||
|
} else {
|
||||||
|
gameRequest.voters.add(currentUser)
|
||||||
|
}
|
||||||
|
|
||||||
|
gameRequestRepository.save(gameRequest)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,13 +1,18 @@
|
|||||||
package org.gameyfin.app.requests.dto
|
package org.gameyfin.app.requests.dto
|
||||||
|
|
||||||
|
import org.gameyfin.app.requests.entities.ExternalProviderIds
|
||||||
import org.gameyfin.app.requests.status.GameRequestStatus
|
import org.gameyfin.app.requests.status.GameRequestStatus
|
||||||
import org.gameyfin.app.users.dto.UserInfoAdminDto
|
import org.gameyfin.app.users.dto.UserInfoDto
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
|
|
||||||
class GameRequestDto(
|
class GameRequestDto(
|
||||||
val id: Long,
|
val id: Long,
|
||||||
val title: String,
|
val title: String,
|
||||||
val release: Instant,
|
val release: Instant,
|
||||||
|
val externalProviderIds: ExternalProviderIds,
|
||||||
val status: GameRequestStatus,
|
val status: GameRequestStatus,
|
||||||
val requester: UserInfoAdminDto
|
val requester: UserInfoDto?,
|
||||||
|
val voters: List<UserInfoDto>,
|
||||||
|
val createdAt: Instant?,
|
||||||
|
val updatedAt: Instant?
|
||||||
)
|
)
|
||||||
+14
-11
@@ -1,42 +1,45 @@
|
|||||||
package org.gameyfin.app.requests
|
package org.gameyfin.app.requests.entities
|
||||||
|
|
||||||
import jakarta.persistence.*
|
import jakarta.persistence.*
|
||||||
import org.gameyfin.app.requests.status.GameRequestStatus
|
import org.gameyfin.app.requests.status.GameRequestStatus
|
||||||
import org.gameyfin.app.users.entities.User
|
import org.gameyfin.app.users.entities.User
|
||||||
import org.hibernate.annotations.CreationTimestamp
|
import org.hibernate.annotations.CreationTimestamp
|
||||||
import org.hibernate.annotations.UpdateTimestamp
|
import org.hibernate.annotations.UpdateTimestamp
|
||||||
|
import org.springframework.data.jpa.domain.support.AuditingEntityListener
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
|
|
||||||
typealias ExternalProviderIds = Map<String, String>
|
typealias ExternalProviderIds = Map<String, String>
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
|
@EntityListeners(GameRequestEntityListener::class, AuditingEntityListener::class)
|
||||||
class GameRequest(
|
class GameRequest(
|
||||||
@Id
|
@Id
|
||||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||||
var id: Long? = null,
|
var id: Long? = null,
|
||||||
|
|
||||||
@CreationTimestamp
|
|
||||||
@Column(nullable = false, updatable = false)
|
|
||||||
var createdAt: Instant? = null,
|
|
||||||
|
|
||||||
@UpdateTimestamp
|
|
||||||
@Column(nullable = false)
|
|
||||||
var updatedAt: Instant? = null,
|
|
||||||
|
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
val title: String,
|
val title: String,
|
||||||
|
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
val release: Instant,
|
val release: Instant,
|
||||||
|
|
||||||
|
@ElementCollection
|
||||||
|
val externalProviderIds: ExternalProviderIds,
|
||||||
|
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
var status: GameRequestStatus,
|
var status: GameRequestStatus,
|
||||||
|
|
||||||
@ManyToOne(fetch = FetchType.EAGER)
|
@ManyToOne(fetch = FetchType.EAGER)
|
||||||
var requester: User? = null,
|
var requester: User? = null,
|
||||||
|
|
||||||
|
@OneToMany
|
||||||
var voters: MutableList<User> = mutableListOf(),
|
var voters: MutableList<User> = mutableListOf(),
|
||||||
|
|
||||||
@ElementCollection
|
@CreationTimestamp
|
||||||
val externalProviderIds: ExternalProviderIds
|
@Column(nullable = false, updatable = false)
|
||||||
|
var createdAt: Instant? = null,
|
||||||
|
|
||||||
|
@UpdateTimestamp
|
||||||
|
@Column(nullable = false)
|
||||||
|
var updatedAt: Instant? = null
|
||||||
)
|
)
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package org.gameyfin.app.requests.entities
|
||||||
|
|
||||||
|
import jakarta.persistence.PostPersist
|
||||||
|
import jakarta.persistence.PostRemove
|
||||||
|
import jakarta.persistence.PostUpdate
|
||||||
|
import org.gameyfin.app.requests.GameRequestService
|
||||||
|
import org.gameyfin.app.requests.dto.GameRequestEvent
|
||||||
|
import org.gameyfin.app.requests.extensions.toDto
|
||||||
|
|
||||||
|
class GameRequestEntityListener {
|
||||||
|
@PostPersist
|
||||||
|
fun created(gameRequest: GameRequest) {
|
||||||
|
GameRequestService.emit(GameRequestEvent.Created(gameRequest.toDto()))
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostUpdate
|
||||||
|
fun updated(gameRequest: GameRequest) {
|
||||||
|
GameRequestService.emit(GameRequestEvent.Updated(gameRequest.toDto()))
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostRemove
|
||||||
|
fun deleted(gameRequest: GameRequest) {
|
||||||
|
GameRequestService.emit(GameRequestEvent.Deleted(gameRequest.id!!))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
package org.gameyfin.app.requests.extensions
|
package org.gameyfin.app.requests.extensions
|
||||||
|
|
||||||
import org.gameyfin.app.requests.GameRequest
|
|
||||||
import org.gameyfin.app.requests.dto.GameRequestDto
|
import org.gameyfin.app.requests.dto.GameRequestDto
|
||||||
|
import org.gameyfin.app.requests.entities.GameRequest
|
||||||
|
import org.gameyfin.app.users.extensions.toUserInfoDto
|
||||||
|
|
||||||
fun GameRequest.toDto(): GameRequestDto {
|
fun GameRequest.toDto(): GameRequestDto {
|
||||||
return GameRequestDto(
|
return GameRequestDto(
|
||||||
@@ -10,6 +11,13 @@ fun GameRequest.toDto(): GameRequestDto {
|
|||||||
release = this.release,
|
release = this.release,
|
||||||
externalProviderIds = this.externalProviderIds,
|
externalProviderIds = this.externalProviderIds,
|
||||||
status = this.status,
|
status = this.status,
|
||||||
requester = this.requester.toDto()
|
requester = this.requester?.toUserInfoDto(),
|
||||||
|
voters = this.voters.map { it.toUserInfoDto() },
|
||||||
|
createdAt = this.createdAt,
|
||||||
|
updatedAt = this.updatedAt
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Collection<GameRequest>.toDtos(): List<GameRequestDto> {
|
||||||
|
return this.map { it.toDto() }
|
||||||
}
|
}
|
||||||
@@ -3,7 +3,7 @@ package org.gameyfin.app.setup
|
|||||||
import com.vaadin.flow.server.auth.AnonymousAllowed
|
import com.vaadin.flow.server.auth.AnonymousAllowed
|
||||||
import com.vaadin.hilla.Endpoint
|
import com.vaadin.hilla.Endpoint
|
||||||
import com.vaadin.hilla.exception.EndpointException
|
import com.vaadin.hilla.exception.EndpointException
|
||||||
import org.gameyfin.app.users.dto.UserInfoAdminDto
|
import org.gameyfin.app.users.dto.ExtendedUserInfoDto
|
||||||
import org.gameyfin.app.users.dto.UserRegistrationDto
|
import org.gameyfin.app.users.dto.UserRegistrationDto
|
||||||
|
|
||||||
@Endpoint
|
@Endpoint
|
||||||
@@ -16,7 +16,7 @@ class SetupEndpoint(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@AnonymousAllowed
|
@AnonymousAllowed
|
||||||
fun registerSuperAdmin(superAdminRegistration: UserRegistrationDto): UserInfoAdminDto {
|
fun registerSuperAdmin(superAdminRegistration: UserRegistrationDto): ExtendedUserInfoDto {
|
||||||
if (setupService.isSetupCompleted()) throw EndpointException("Setup already completed")
|
if (setupService.isSetupCompleted()) throw EndpointException("Setup already completed")
|
||||||
return setupService.createInitialAdminUser(superAdminRegistration)
|
return setupService.createInitialAdminUser(superAdminRegistration)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,9 +3,10 @@ package org.gameyfin.app.setup
|
|||||||
import org.gameyfin.app.core.Role
|
import org.gameyfin.app.core.Role
|
||||||
import org.gameyfin.app.users.RoleService
|
import org.gameyfin.app.users.RoleService
|
||||||
import org.gameyfin.app.users.UserService
|
import org.gameyfin.app.users.UserService
|
||||||
import org.gameyfin.app.users.dto.UserInfoAdminDto
|
import org.gameyfin.app.users.dto.ExtendedUserInfoDto
|
||||||
import org.gameyfin.app.users.dto.UserRegistrationDto
|
import org.gameyfin.app.users.dto.UserRegistrationDto
|
||||||
import org.gameyfin.app.users.entities.User
|
import org.gameyfin.app.users.entities.User
|
||||||
|
import org.gameyfin.app.users.extensions.toExtendedUserInfoDto
|
||||||
import org.springframework.stereotype.Service
|
import org.springframework.stereotype.Service
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@@ -26,7 +27,7 @@ class SetupService(
|
|||||||
/**
|
/**
|
||||||
* Creates the initial user with Super-Admin permissions
|
* Creates the initial user with Super-Admin permissions
|
||||||
*/
|
*/
|
||||||
fun createInitialAdminUser(registration: UserRegistrationDto): UserInfoAdminDto {
|
fun createInitialAdminUser(registration: UserRegistrationDto): ExtendedUserInfoDto {
|
||||||
val superAdmin = User(
|
val superAdmin = User(
|
||||||
username = registration.username,
|
username = registration.username,
|
||||||
password = registration.password,
|
password = registration.password,
|
||||||
@@ -36,6 +37,6 @@ class SetupService(
|
|||||||
)
|
)
|
||||||
|
|
||||||
val user = userService.registerOrUpdateUser(superAdmin)
|
val user = userService.registerOrUpdateUser(superAdmin)
|
||||||
return userService.toUserInfo(user)
|
return user.toExtendedUserInfoDto()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6,7 +6,7 @@ import jakarta.annotation.security.PermitAll
|
|||||||
import jakarta.annotation.security.RolesAllowed
|
import jakarta.annotation.security.RolesAllowed
|
||||||
import org.gameyfin.app.core.Role
|
import org.gameyfin.app.core.Role
|
||||||
import org.gameyfin.app.core.security.getCurrentAuth
|
import org.gameyfin.app.core.security.getCurrentAuth
|
||||||
import org.gameyfin.app.users.dto.UserInfoAdminDto
|
import org.gameyfin.app.users.dto.ExtendedUserInfoDto
|
||||||
import org.gameyfin.app.users.dto.UserUpdateDto
|
import org.gameyfin.app.users.dto.UserUpdateDto
|
||||||
import org.gameyfin.app.users.enums.RoleAssignmentResult
|
import org.gameyfin.app.users.enums.RoleAssignmentResult
|
||||||
import org.springframework.security.core.Authentication
|
import org.springframework.security.core.Authentication
|
||||||
@@ -18,7 +18,7 @@ class UserEndpoint(
|
|||||||
private val roleService: RoleService
|
private val roleService: RoleService
|
||||||
) {
|
) {
|
||||||
@AnonymousAllowed
|
@AnonymousAllowed
|
||||||
fun getUserInfo(): UserInfoAdminDto? {
|
fun getUserInfo(): ExtendedUserInfoDto? {
|
||||||
val auth = getCurrentAuth()
|
val auth = getCurrentAuth()
|
||||||
if (!auth.isAuthenticated || auth.principal == "anonymousUser") return null
|
if (!auth.isAuthenticated || auth.principal == "anonymousUser") return null
|
||||||
return userService.getUserInfo()
|
return userService.getUserInfo()
|
||||||
@@ -36,7 +36,7 @@ class UserEndpoint(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@RolesAllowed(Role.Names.ADMIN)
|
@RolesAllowed(Role.Names.ADMIN)
|
||||||
fun getAllUsers(): List<UserInfoAdminDto> {
|
fun getAllUsers(): List<ExtendedUserInfoDto> {
|
||||||
return userService.getAllUsers()
|
return userService.getAllUsers()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,15 +9,15 @@ import org.gameyfin.app.core.events.*
|
|||||||
import org.gameyfin.app.core.security.getCurrentAuth
|
import org.gameyfin.app.core.security.getCurrentAuth
|
||||||
import org.gameyfin.app.games.entities.Image
|
import org.gameyfin.app.games.entities.Image
|
||||||
import org.gameyfin.app.media.ImageService
|
import org.gameyfin.app.media.ImageService
|
||||||
import org.gameyfin.app.users.dto.UserInfoAdminDto
|
import org.gameyfin.app.users.dto.ExtendedUserInfoDto
|
||||||
import org.gameyfin.app.users.dto.UserRegistrationDto
|
import org.gameyfin.app.users.dto.UserRegistrationDto
|
||||||
import org.gameyfin.app.users.dto.UserUpdateDto
|
import org.gameyfin.app.users.dto.UserUpdateDto
|
||||||
import org.gameyfin.app.users.emailconfirmation.EmailConfirmationService
|
import org.gameyfin.app.users.emailconfirmation.EmailConfirmationService
|
||||||
import org.gameyfin.app.users.enums.RoleAssignmentResult
|
import org.gameyfin.app.users.enums.RoleAssignmentResult
|
||||||
|
import org.gameyfin.app.users.extensions.toAuthorities
|
||||||
|
import org.gameyfin.app.users.extensions.toExtendedUserInfoDto
|
||||||
import org.gameyfin.app.users.persistence.UserRepository
|
import org.gameyfin.app.users.persistence.UserRepository
|
||||||
import org.springframework.context.ApplicationEventPublisher
|
import org.springframework.context.ApplicationEventPublisher
|
||||||
import org.springframework.security.core.GrantedAuthority
|
|
||||||
import org.springframework.security.core.authority.SimpleGrantedAuthority
|
|
||||||
import org.springframework.security.core.userdetails.User
|
import org.springframework.security.core.userdetails.User
|
||||||
import org.springframework.security.core.userdetails.UserDetails
|
import org.springframework.security.core.userdetails.UserDetails
|
||||||
import org.springframework.security.core.userdetails.UserDetailsService
|
import org.springframework.security.core.userdetails.UserDetailsService
|
||||||
@@ -56,7 +56,7 @@ class UserService(
|
|||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
toAuthorities(user.roles)
|
user.roles.toAuthorities()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,7 +67,7 @@ class UserService(
|
|||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
toAuthorities(user.roles)
|
user.roles.toAuthorities()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,8 +77,8 @@ class UserService(
|
|||||||
fun findByOidcProviderId(oidcProviderId: String): org.gameyfin.app.users.entities.User? =
|
fun findByOidcProviderId(oidcProviderId: String): org.gameyfin.app.users.entities.User? =
|
||||||
userRepository.findByOidcProviderId(oidcProviderId)
|
userRepository.findByOidcProviderId(oidcProviderId)
|
||||||
|
|
||||||
fun getAllUsers(): List<UserInfoAdminDto> {
|
fun getAllUsers(): List<ExtendedUserInfoDto> {
|
||||||
return userRepository.findAll().map { u -> toUserInfo(u) }
|
return userRepository.findAll().map { it.toExtendedUserInfoDto() }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getByEmail(email: String): org.gameyfin.app.users.entities.User? {
|
fun getByEmail(email: String): org.gameyfin.app.users.entities.User? {
|
||||||
@@ -93,20 +93,20 @@ class UserService(
|
|||||||
return userRepository.findByUsername(username) ?: throw UsernameNotFoundException("Unknown user '$username'")
|
return userRepository.findByUsername(username) ?: throw UsernameNotFoundException("Unknown user '$username'")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getUserInfo(): UserInfoAdminDto {
|
fun getUserInfo(): ExtendedUserInfoDto {
|
||||||
val auth = getCurrentAuth()
|
val auth = getCurrentAuth()
|
||||||
val principal = auth.principal
|
val principal = auth.principal
|
||||||
|
|
||||||
if (principal is OidcUser) {
|
if (principal is OidcUser) {
|
||||||
val oidcUser = org.gameyfin.app.users.entities.User(principal)
|
val oidcUser = org.gameyfin.app.users.entities.User(principal)
|
||||||
val userInfoDto = toUserInfo(oidcUser)
|
val userInfoDto = oidcUser.toExtendedUserInfoDto()
|
||||||
userInfoDto.roles = roleService.extractGrantedAuthorities(principal.authorities)
|
userInfoDto.roles = roleService.extractGrantedAuthorities(principal.authorities)
|
||||||
.mapNotNull { Role.Companion.safeValueOf(it.authority) }
|
.mapNotNull { Role.safeValueOf(it.authority) }
|
||||||
return userInfoDto
|
return userInfoDto
|
||||||
}
|
}
|
||||||
|
|
||||||
val user = getByUsernameNonNull(auth.name)
|
val user = getByUsernameNonNull(auth.name)
|
||||||
return toUserInfo(user)
|
return user.toExtendedUserInfoDto()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getAvatar(username: String): Image? {
|
fun getAvatar(username: String): Image? {
|
||||||
@@ -158,7 +158,7 @@ class UserService(
|
|||||||
RegistrationAttemptWithExistingEmailEvent(
|
RegistrationAttemptWithExistingEmailEvent(
|
||||||
this,
|
this,
|
||||||
it,
|
it,
|
||||||
Utils.Companion.getBaseUrl()
|
Utils.getBaseUrl()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
@@ -179,12 +179,12 @@ class UserService(
|
|||||||
if (adminNeedsToApprove) {
|
if (adminNeedsToApprove) {
|
||||||
eventPublisher.publishEvent(UserRegistrationWaitingForApprovalEvent(this, user))
|
eventPublisher.publishEvent(UserRegistrationWaitingForApprovalEvent(this, user))
|
||||||
} else {
|
} else {
|
||||||
eventPublisher.publishEvent(AccountStatusChangedEvent(this, user, Utils.Companion.getBaseUrl()))
|
eventPublisher.publishEvent(AccountStatusChangedEvent(this, user, Utils.getBaseUrl()))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!user.emailConfirmed) {
|
if (!user.emailConfirmed) {
|
||||||
val token = emailConfirmationService.generate(user)
|
val token = emailConfirmationService.generate(user)
|
||||||
eventPublisher.publishEvent(EmailNeedsConfirmationEvent(this, token, Utils.Companion.getBaseUrl()))
|
eventPublisher.publishEvent(EmailNeedsConfirmationEvent(this, token, Utils.getBaseUrl()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -222,7 +222,7 @@ class UserService(
|
|||||||
user.email = it
|
user.email = it
|
||||||
user.emailConfirmed = false
|
user.emailConfirmed = false
|
||||||
val token = emailConfirmationService.generate(user)
|
val token = emailConfirmationService.generate(user)
|
||||||
eventPublisher.publishEvent(EmailNeedsConfirmationEvent(this, token, Utils.Companion.getBaseUrl()))
|
eventPublisher.publishEvent(EmailNeedsConfirmationEvent(this, token, Utils.getBaseUrl()))
|
||||||
}
|
}
|
||||||
|
|
||||||
userRepository.save(user)
|
userRepository.save(user)
|
||||||
@@ -246,7 +246,7 @@ class UserService(
|
|||||||
return RoleAssignmentResult.TARGET_POWER_LEVEL_TOO_HIGH
|
return RoleAssignmentResult.TARGET_POWER_LEVEL_TOO_HIGH
|
||||||
}
|
}
|
||||||
|
|
||||||
val newAssignedRoles = roleNames.mapNotNull { r -> Role.Companion.safeValueOf(r) }
|
val newAssignedRoles = roleNames.mapNotNull { r -> Role.safeValueOf(r) }
|
||||||
val newAssignedRolesLevel = roleService.getHighestRole(newAssignedRoles).powerLevel
|
val newAssignedRolesLevel = roleService.getHighestRole(newAssignedRoles).powerLevel
|
||||||
val currentUserLevel = roleService.getHighestRoleFromAuthorities(currentUser.authorities).powerLevel
|
val currentUserLevel = roleService.getHighestRoleFromAuthorities(currentUser.authorities).powerLevel
|
||||||
|
|
||||||
@@ -276,29 +276,12 @@ class UserService(
|
|||||||
val user = getByUsernameNonNull(username)
|
val user = getByUsernameNonNull(username)
|
||||||
user.enabled = enabled
|
user.enabled = enabled
|
||||||
userRepository.save(user)
|
userRepository.save(user)
|
||||||
eventPublisher.publishEvent(AccountStatusChangedEvent(this, user, Utils.Companion.getBaseUrl()))
|
eventPublisher.publishEvent(AccountStatusChangedEvent(this, user, Utils.getBaseUrl()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun deleteUser(username: String) {
|
fun deleteUser(username: String) {
|
||||||
val user = getByUsernameNonNull(username)
|
val user = getByUsernameNonNull(username)
|
||||||
userRepository.delete(user)
|
userRepository.delete(user)
|
||||||
eventPublisher.publishEvent(AccountDeletedEvent(this, user, Utils.Companion.getBaseUrl()))
|
eventPublisher.publishEvent(AccountDeletedEvent(this, user, Utils.getBaseUrl()))
|
||||||
}
|
|
||||||
|
|
||||||
fun toUserInfo(user: org.gameyfin.app.users.entities.User): UserInfoAdminDto {
|
|
||||||
return UserInfoAdminDto(
|
|
||||||
username = user.username,
|
|
||||||
email = user.email,
|
|
||||||
emailConfirmed = user.emailConfirmed,
|
|
||||||
enabled = user.enabled,
|
|
||||||
hasAvatar = user.avatar != null,
|
|
||||||
avatarId = user.avatar?.id,
|
|
||||||
managedBySso = user.oidcProviderId != null,
|
|
||||||
roles = user.roles
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun toAuthorities(roles: Collection<Role>): List<GrantedAuthority> {
|
|
||||||
return roles.map { r -> SimpleGrantedAuthority(r.roleName) }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+1
-1
@@ -2,7 +2,7 @@ package org.gameyfin.app.users.dto
|
|||||||
|
|
||||||
import org.gameyfin.app.core.Role
|
import org.gameyfin.app.core.Role
|
||||||
|
|
||||||
data class UserInfoAdminDto(
|
data class ExtendedUserInfoDto(
|
||||||
val username: String,
|
val username: String,
|
||||||
val managedBySso: Boolean,
|
val managedBySso: Boolean,
|
||||||
val email: String,
|
val email: String,
|
||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
package org.gameyfin.app.users.dto
|
package org.gameyfin.app.users.dto
|
||||||
|
|
||||||
data class UserInfoUserDto(
|
data class UserInfoDto(
|
||||||
val username: String,
|
val username: String,
|
||||||
val hasAvatar: Boolean,
|
val hasAvatar: Boolean,
|
||||||
val avatarId: Long? = null,
|
val avatarId: Long? = null,
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package org.gameyfin.app.users.extensions
|
||||||
|
|
||||||
|
import org.gameyfin.app.core.Role
|
||||||
|
import org.gameyfin.app.users.dto.ExtendedUserInfoDto
|
||||||
|
import org.gameyfin.app.users.dto.UserInfoDto
|
||||||
|
import org.gameyfin.app.users.entities.User
|
||||||
|
import org.springframework.security.core.GrantedAuthority
|
||||||
|
import org.springframework.security.core.authority.SimpleGrantedAuthority
|
||||||
|
|
||||||
|
fun User.toUserInfoDto(): UserInfoDto {
|
||||||
|
return UserInfoDto(
|
||||||
|
username = this.username,
|
||||||
|
hasAvatar = this.avatar != null,
|
||||||
|
avatarId = this.avatar?.id
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun User.toExtendedUserInfoDto(): ExtendedUserInfoDto {
|
||||||
|
return ExtendedUserInfoDto(
|
||||||
|
username = this.username,
|
||||||
|
email = this.email,
|
||||||
|
emailConfirmed = this.emailConfirmed,
|
||||||
|
enabled = this.enabled,
|
||||||
|
hasAvatar = this.avatar != null,
|
||||||
|
avatarId = this.avatar?.id,
|
||||||
|
managedBySso = this.oidcProviderId != null,
|
||||||
|
roles = this.roles
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Collection<Role>.toAuthorities(): List<GrantedAuthority> {
|
||||||
|
return this.map { r -> SimpleGrantedAuthority(r.roleName) }
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user