diff --git a/app/package.json b/app/package.json index 4d7345d..59d3828 100644 --- a/app/package.json +++ b/app/package.json @@ -265,4 +265,4 @@ "disableUsageStatistics": true, "hash": "962eccc3fa0735d5234901be4f9e384096113c45bec22564a53688096d62aef4" } -} +} \ No newline at end of file diff --git a/app/src/main/kotlin/org/gameyfin/app/core/filesystem/FilesystemService.kt b/app/src/main/kotlin/org/gameyfin/app/core/filesystem/FilesystemService.kt index f126d77..3d81dab 100644 --- a/app/src/main/kotlin/org/gameyfin/app/core/filesystem/FilesystemService.kt +++ b/app/src/main/kotlin/org/gameyfin/app/core/filesystem/FilesystemService.kt @@ -1,10 +1,10 @@ package org.gameyfin.app.core.filesystem -import org.gameyfin.app.config.ConfigService import io.github.oshai.kotlinlogging.KotlinLogging import org.apache.commons.io.FilenameUtils 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 java.io.File import java.nio.file.FileSystems diff --git a/app/src/main/kotlin/org/gameyfin/app/games/GameService.kt b/app/src/main/kotlin/org/gameyfin/app/games/GameService.kt index 5d42048..ba0b461 100644 --- a/app/src/main/kotlin/org/gameyfin/app/games/GameService.kt +++ b/app/src/main/kotlin/org/gameyfin/app/games/GameService.kt @@ -22,7 +22,7 @@ import org.gameyfin.app.games.dto.* import org.gameyfin.app.games.entities.* import org.gameyfin.app.games.extensions.toDtos 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.users.UserService import org.gameyfin.pluginapi.gamemetadata.* diff --git a/app/src/main/kotlin/org/gameyfin/app/games/entities/Game.kt b/app/src/main/kotlin/org/gameyfin/app/games/entities/Game.kt index ce6991d..2cdbcdd 100644 --- a/app/src/main/kotlin/org/gameyfin/app/games/entities/Game.kt +++ b/app/src/main/kotlin/org/gameyfin/app/games/entities/Game.kt @@ -1,7 +1,7 @@ package org.gameyfin.app.games.entities 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.Genre import org.gameyfin.pluginapi.gamemetadata.PlayerPerspective diff --git a/app/src/main/kotlin/org/gameyfin/app/games/entities/GameEntityListener.kt b/app/src/main/kotlin/org/gameyfin/app/games/entities/GameEntityListener.kt index efe500b..9a04b3e 100644 --- a/app/src/main/kotlin/org/gameyfin/app/games/entities/GameEntityListener.kt +++ b/app/src/main/kotlin/org/gameyfin/app/games/entities/GameEntityListener.kt @@ -12,19 +12,19 @@ import org.gameyfin.app.games.extensions.toUserDto class GameEntityListener { @PostPersist fun created(game: Game) { - GameService.Companion.emitUser(GameUserEvent.Created(game.toUserDto())) - GameService.Companion.emitAdmin(GameAdminEvent.Created(game.toAdminDto())) + GameService.emitUser(GameUserEvent.Created(game.toUserDto())) + GameService.emitAdmin(GameAdminEvent.Created(game.toAdminDto())) } @PostUpdate fun updated(game: Game) { - GameService.Companion.emitUser(GameUserEvent.Updated(game.toUserDto())) - GameService.Companion.emitAdmin(GameAdminEvent.Updated(game.toAdminDto())) + GameService.emitUser(GameUserEvent.Updated(game.toUserDto())) + GameService.emitAdmin(GameAdminEvent.Updated(game.toAdminDto())) } @PostRemove fun deleted(game: Game) { - GameService.Companion.emitUser(GameUserEvent.Deleted(game.id!!)) - GameService.Companion.emitAdmin(GameAdminEvent.Deleted(game.id!!)) + GameService.emitUser(GameUserEvent.Deleted(game.id!!)) + GameService.emitAdmin(GameAdminEvent.Deleted(game.id!!)) } } \ No newline at end of file diff --git a/app/src/main/kotlin/org/gameyfin/app/libraries/LibraryCoreService.kt b/app/src/main/kotlin/org/gameyfin/app/libraries/LibraryCoreService.kt index 6fc27eb..0f04c26 100644 --- a/app/src/main/kotlin/org/gameyfin/app/libraries/LibraryCoreService.kt +++ b/app/src/main/kotlin/org/gameyfin/app/libraries/LibraryCoreService.kt @@ -2,6 +2,7 @@ package org.gameyfin.app.libraries import org.gameyfin.app.games.GameService import org.gameyfin.app.games.entities.Game +import org.gameyfin.app.libraries.entities.Library import org.springframework.stereotype.Service import java.time.Instant diff --git a/app/src/main/kotlin/org/gameyfin/app/libraries/LibraryRepository.kt b/app/src/main/kotlin/org/gameyfin/app/libraries/LibraryRepository.kt index b60aefc..560f8de 100644 --- a/app/src/main/kotlin/org/gameyfin/app/libraries/LibraryRepository.kt +++ b/app/src/main/kotlin/org/gameyfin/app/libraries/LibraryRepository.kt @@ -1,5 +1,6 @@ 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.Query diff --git a/app/src/main/kotlin/org/gameyfin/app/libraries/LibraryScanService.kt b/app/src/main/kotlin/org/gameyfin/app/libraries/LibraryScanService.kt index e66d23e..7ac4798 100644 --- a/app/src/main/kotlin/org/gameyfin/app/libraries/LibraryScanService.kt +++ b/app/src/main/kotlin/org/gameyfin/app/libraries/LibraryScanService.kt @@ -4,9 +4,8 @@ import io.github.oshai.kotlinlogging.KotlinLogging import org.gameyfin.app.core.filesystem.FilesystemService import org.gameyfin.app.games.GameService import org.gameyfin.app.games.entities.Game -import org.gameyfin.app.libraries.dto.LibraryScanProgress -import org.gameyfin.app.libraries.dto.LibraryScanStatus -import org.gameyfin.app.libraries.dto.LibraryScanStep +import org.gameyfin.app.libraries.dto.* +import org.gameyfin.app.libraries.entities.Library import org.gameyfin.app.libraries.enums.ScanType import org.gameyfin.app.libraries.scan.* import org.gameyfin.app.media.ImageService diff --git a/app/src/main/kotlin/org/gameyfin/app/libraries/LibraryService.kt b/app/src/main/kotlin/org/gameyfin/app/libraries/LibraryService.kt index f9c61f5..c359e00 100644 --- a/app/src/main/kotlin/org/gameyfin/app/libraries/LibraryService.kt +++ b/app/src/main/kotlin/org/gameyfin/app/libraries/LibraryService.kt @@ -3,6 +3,8 @@ package org.gameyfin.app.libraries import com.vaadin.hilla.exception.EndpointException import io.github.oshai.kotlinlogging.KotlinLogging 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.extensions.toDtos import org.springframework.data.repository.findByIdOrNull diff --git a/app/src/main/kotlin/org/gameyfin/app/libraries/dto/LibraryScanProgress.kt b/app/src/main/kotlin/org/gameyfin/app/libraries/dto/LibraryScanProgress.kt index 3666cea..5e648b7 100644 --- a/app/src/main/kotlin/org/gameyfin/app/libraries/dto/LibraryScanProgress.kt +++ b/app/src/main/kotlin/org/gameyfin/app/libraries/dto/LibraryScanProgress.kt @@ -1,6 +1,5 @@ package org.gameyfin.app.libraries.dto -import org.gameyfin.app.libraries.LibraryScanResult import org.gameyfin.app.libraries.enums.ScanType import java.time.Instant import java.util.* diff --git a/app/src/main/kotlin/org/gameyfin/app/libraries/LibraryScanResult.kt b/app/src/main/kotlin/org/gameyfin/app/libraries/dto/LibraryScanResult.kt similarity index 94% rename from app/src/main/kotlin/org/gameyfin/app/libraries/LibraryScanResult.kt rename to app/src/main/kotlin/org/gameyfin/app/libraries/dto/LibraryScanResult.kt index 0454ea3..779b751 100644 --- a/app/src/main/kotlin/org/gameyfin/app/libraries/LibraryScanResult.kt +++ b/app/src/main/kotlin/org/gameyfin/app/libraries/dto/LibraryScanResult.kt @@ -1,4 +1,4 @@ -package org.gameyfin.app.libraries +package org.gameyfin.app.libraries.dto interface LibraryScanResult { /** diff --git a/app/src/main/kotlin/org/gameyfin/app/libraries/DirectoryMapping.kt b/app/src/main/kotlin/org/gameyfin/app/libraries/entities/DirectoryMapping.kt similarity index 84% rename from app/src/main/kotlin/org/gameyfin/app/libraries/DirectoryMapping.kt rename to app/src/main/kotlin/org/gameyfin/app/libraries/entities/DirectoryMapping.kt index 48e0d51..627d955 100644 --- a/app/src/main/kotlin/org/gameyfin/app/libraries/DirectoryMapping.kt +++ b/app/src/main/kotlin/org/gameyfin/app/libraries/entities/DirectoryMapping.kt @@ -1,4 +1,4 @@ -package org.gameyfin.app.libraries +package org.gameyfin.app.libraries.entities import jakarta.persistence.* diff --git a/app/src/main/kotlin/org/gameyfin/app/libraries/Library.kt b/app/src/main/kotlin/org/gameyfin/app/libraries/entities/Library.kt similarity index 90% rename from app/src/main/kotlin/org/gameyfin/app/libraries/Library.kt rename to app/src/main/kotlin/org/gameyfin/app/libraries/entities/Library.kt index 0b1b93d..938b877 100644 --- a/app/src/main/kotlin/org/gameyfin/app/libraries/Library.kt +++ b/app/src/main/kotlin/org/gameyfin/app/libraries/entities/Library.kt @@ -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 org.gameyfin.app.games.entities.LibraryEntityListener +import org.gameyfin.app.games.entities.Game import org.hibernate.annotations.CreationTimestamp import org.hibernate.annotations.UpdateTimestamp import java.time.Instant diff --git a/app/src/main/kotlin/org/gameyfin/app/games/entities/LibraryEntityListener.kt b/app/src/main/kotlin/org/gameyfin/app/libraries/entities/LibraryEntityListener.kt similarity index 92% rename from app/src/main/kotlin/org/gameyfin/app/games/entities/LibraryEntityListener.kt rename to app/src/main/kotlin/org/gameyfin/app/libraries/entities/LibraryEntityListener.kt index c035ea2..1ec83d2 100644 --- a/app/src/main/kotlin/org/gameyfin/app/games/entities/LibraryEntityListener.kt +++ b/app/src/main/kotlin/org/gameyfin/app/libraries/entities/LibraryEntityListener.kt @@ -1,9 +1,8 @@ -package org.gameyfin.app.games.entities +package org.gameyfin.app.libraries.entities import jakarta.persistence.PostPersist import jakarta.persistence.PostRemove import jakarta.persistence.PostUpdate -import org.gameyfin.app.libraries.Library import org.gameyfin.app.libraries.LibraryService import org.gameyfin.app.libraries.dto.LibraryAdminEvent import org.gameyfin.app.libraries.dto.LibraryUserEvent diff --git a/app/src/main/kotlin/org/gameyfin/app/libraries/extensions/LibraryExtensions.kt b/app/src/main/kotlin/org/gameyfin/app/libraries/extensions/LibraryExtensions.kt index a73f0d7..0e73176 100644 --- a/app/src/main/kotlin/org/gameyfin/app/libraries/extensions/LibraryExtensions.kt +++ b/app/src/main/kotlin/org/gameyfin/app/libraries/extensions/LibraryExtensions.kt @@ -1,8 +1,8 @@ package org.gameyfin.app.libraries.extensions import org.gameyfin.app.core.security.isCurrentUserAdmin -import org.gameyfin.app.libraries.Library import org.gameyfin.app.libraries.dto.* +import org.gameyfin.app.libraries.entities.Library fun Library.toDto(): LibraryDto { diff --git a/app/src/main/kotlin/org/gameyfin/app/requests/GameRequestEndpoint.kt b/app/src/main/kotlin/org/gameyfin/app/requests/GameRequestEndpoint.kt new file mode 100644 index 0000000..4cc235a --- /dev/null +++ b/app/src/main/kotlin/org/gameyfin/app/requests/GameRequestEndpoint.kt @@ -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> { + 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) + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/org/gameyfin/app/requests/GameRequestRepository.kt b/app/src/main/kotlin/org/gameyfin/app/requests/GameRequestRepository.kt index 20ea18b..c045b4d 100644 --- a/app/src/main/kotlin/org/gameyfin/app/requests/GameRequestRepository.kt +++ b/app/src/main/kotlin/org/gameyfin/app/requests/GameRequestRepository.kt @@ -1,5 +1,6 @@ package org.gameyfin.app.requests +import org.gameyfin.app.requests.entities.GameRequest import org.springframework.data.jpa.repository.JpaRepository interface GameRequestRepository : JpaRepository \ No newline at end of file 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 0ea279b..dfef3f4 100644 --- a/app/src/main/kotlin/org/gameyfin/app/requests/GameRequestService.kt +++ b/app/src/main/kotlin/org/gameyfin/app/requests/GameRequestService.kt @@ -2,8 +2,11 @@ package org.gameyfin.app.requests import io.github.oshai.kotlinlogging.KotlinLogging 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.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.users.UserService import org.springframework.stereotype.Service @@ -22,9 +25,9 @@ class GameRequestService( private val log = KotlinLogging.logger {} /* Websockets */ - private val gameRequestEvents = Sinks.many().multicast().onBackpressureBuffer(1024, false) + private val gameRequestEvents = Sinks.many().multicast().onBackpressureBuffer(1024, false) - fun subscribe(): Flux> { + fun subscribe(): Flux> { log.debug { "New user subscription for gameRequestEvents" } return gameRequestEvents.asFlux() .buffer(100.milliseconds.toJavaDuration()) @@ -36,11 +39,16 @@ class GameRequestService( } } - fun emit(event: GameUserEvent) { + fun emit(event: GameRequestEvent) { gameRequestEvents.tryEmitNext(event) } } + fun getAll(): List { + val entities = gameRequestRepository.findAll() + return entities.toDtos() + } + fun createRequest(gameRequest: GameRequestCreationDto) { val currentUser = userService.getByUsername(getCurrentAuth().name) @@ -54,4 +62,36 @@ class GameRequestService( 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) + } } \ No newline at end of file diff --git a/app/src/main/kotlin/org/gameyfin/app/requests/dto/GameRequestDto.kt b/app/src/main/kotlin/org/gameyfin/app/requests/dto/GameRequestDto.kt index 1a4169d..58f6743 100644 --- a/app/src/main/kotlin/org/gameyfin/app/requests/dto/GameRequestDto.kt +++ b/app/src/main/kotlin/org/gameyfin/app/requests/dto/GameRequestDto.kt @@ -1,13 +1,18 @@ package org.gameyfin.app.requests.dto +import org.gameyfin.app.requests.entities.ExternalProviderIds 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 class GameRequestDto( val id: Long, val title: String, val release: Instant, + val externalProviderIds: ExternalProviderIds, val status: GameRequestStatus, - val requester: UserInfoAdminDto + val requester: UserInfoDto?, + val voters: List, + val createdAt: Instant?, + val updatedAt: Instant? ) \ No newline at end of file diff --git a/app/src/main/kotlin/org/gameyfin/app/requests/GameRequest.kt b/app/src/main/kotlin/org/gameyfin/app/requests/entities/GameRequest.kt similarity index 75% rename from app/src/main/kotlin/org/gameyfin/app/requests/GameRequest.kt rename to app/src/main/kotlin/org/gameyfin/app/requests/entities/GameRequest.kt index 6808044..20947f4 100644 --- a/app/src/main/kotlin/org/gameyfin/app/requests/GameRequest.kt +++ b/app/src/main/kotlin/org/gameyfin/app/requests/entities/GameRequest.kt @@ -1,42 +1,45 @@ -package org.gameyfin.app.requests +package org.gameyfin.app.requests.entities import jakarta.persistence.* import org.gameyfin.app.requests.status.GameRequestStatus import org.gameyfin.app.users.entities.User import org.hibernate.annotations.CreationTimestamp import org.hibernate.annotations.UpdateTimestamp +import org.springframework.data.jpa.domain.support.AuditingEntityListener import java.time.Instant typealias ExternalProviderIds = Map @Entity +@EntityListeners(GameRequestEntityListener::class, AuditingEntityListener::class) class GameRequest( @Id @GeneratedValue(strategy = GenerationType.AUTO) 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) val title: String, @Column(nullable = false) val release: Instant, + @ElementCollection + val externalProviderIds: ExternalProviderIds, + @Column(nullable = false) var status: GameRequestStatus, @ManyToOne(fetch = FetchType.EAGER) var requester: User? = null, + @OneToMany var voters: MutableList = mutableListOf(), - @ElementCollection - val externalProviderIds: ExternalProviderIds + @CreationTimestamp + @Column(nullable = false, updatable = false) + var createdAt: Instant? = null, + + @UpdateTimestamp + @Column(nullable = false) + var updatedAt: Instant? = null ) \ No newline at end of file diff --git a/app/src/main/kotlin/org/gameyfin/app/requests/entities/GameRequestEntityListener.kt b/app/src/main/kotlin/org/gameyfin/app/requests/entities/GameRequestEntityListener.kt new file mode 100644 index 0000000..625010b --- /dev/null +++ b/app/src/main/kotlin/org/gameyfin/app/requests/entities/GameRequestEntityListener.kt @@ -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!!)) + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/org/gameyfin/app/requests/extensions/GameRequestExtensions.kt b/app/src/main/kotlin/org/gameyfin/app/requests/extensions/GameRequestExtensions.kt index 8136b13..7301814 100644 --- a/app/src/main/kotlin/org/gameyfin/app/requests/extensions/GameRequestExtensions.kt +++ b/app/src/main/kotlin/org/gameyfin/app/requests/extensions/GameRequestExtensions.kt @@ -1,7 +1,8 @@ package org.gameyfin.app.requests.extensions -import org.gameyfin.app.requests.GameRequest 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 { return GameRequestDto( @@ -10,6 +11,13 @@ fun GameRequest.toDto(): GameRequestDto { release = this.release, externalProviderIds = this.externalProviderIds, 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.toDtos(): List { + return this.map { it.toDto() } } \ No newline at end of file diff --git a/app/src/main/kotlin/org/gameyfin/app/setup/SetupEndpoint.kt b/app/src/main/kotlin/org/gameyfin/app/setup/SetupEndpoint.kt index e020434..443f8b0 100644 --- a/app/src/main/kotlin/org/gameyfin/app/setup/SetupEndpoint.kt +++ b/app/src/main/kotlin/org/gameyfin/app/setup/SetupEndpoint.kt @@ -3,7 +3,7 @@ package org.gameyfin.app.setup import com.vaadin.flow.server.auth.AnonymousAllowed import com.vaadin.hilla.Endpoint 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 @Endpoint @@ -16,7 +16,7 @@ class SetupEndpoint( } @AnonymousAllowed - fun registerSuperAdmin(superAdminRegistration: UserRegistrationDto): UserInfoAdminDto { + fun registerSuperAdmin(superAdminRegistration: UserRegistrationDto): ExtendedUserInfoDto { if (setupService.isSetupCompleted()) throw EndpointException("Setup already completed") return setupService.createInitialAdminUser(superAdminRegistration) } diff --git a/app/src/main/kotlin/org/gameyfin/app/setup/SetupService.kt b/app/src/main/kotlin/org/gameyfin/app/setup/SetupService.kt index 1a6c441..02eaa3e 100644 --- a/app/src/main/kotlin/org/gameyfin/app/setup/SetupService.kt +++ b/app/src/main/kotlin/org/gameyfin/app/setup/SetupService.kt @@ -3,9 +3,10 @@ package org.gameyfin.app.setup import org.gameyfin.app.core.Role import org.gameyfin.app.users.RoleService 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.entities.User +import org.gameyfin.app.users.extensions.toExtendedUserInfoDto import org.springframework.stereotype.Service @Service @@ -26,7 +27,7 @@ class SetupService( /** * Creates the initial user with Super-Admin permissions */ - fun createInitialAdminUser(registration: UserRegistrationDto): UserInfoAdminDto { + fun createInitialAdminUser(registration: UserRegistrationDto): ExtendedUserInfoDto { val superAdmin = User( username = registration.username, password = registration.password, @@ -36,6 +37,6 @@ class SetupService( ) val user = userService.registerOrUpdateUser(superAdmin) - return userService.toUserInfo(user) + return user.toExtendedUserInfoDto() } } \ No newline at end of file diff --git a/app/src/main/kotlin/org/gameyfin/app/users/UserEndpoint.kt b/app/src/main/kotlin/org/gameyfin/app/users/UserEndpoint.kt index a114da8..d7c5739 100644 --- a/app/src/main/kotlin/org/gameyfin/app/users/UserEndpoint.kt +++ b/app/src/main/kotlin/org/gameyfin/app/users/UserEndpoint.kt @@ -6,7 +6,7 @@ import jakarta.annotation.security.PermitAll import jakarta.annotation.security.RolesAllowed import org.gameyfin.app.core.Role 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.enums.RoleAssignmentResult import org.springframework.security.core.Authentication @@ -18,7 +18,7 @@ class UserEndpoint( private val roleService: RoleService ) { @AnonymousAllowed - fun getUserInfo(): UserInfoAdminDto? { + fun getUserInfo(): ExtendedUserInfoDto? { val auth = getCurrentAuth() if (!auth.isAuthenticated || auth.principal == "anonymousUser") return null return userService.getUserInfo() @@ -36,7 +36,7 @@ class UserEndpoint( } @RolesAllowed(Role.Names.ADMIN) - fun getAllUsers(): List { + fun getAllUsers(): List { return userService.getAllUsers() } 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 e22bcda..dad6137 100644 --- a/app/src/main/kotlin/org/gameyfin/app/users/UserService.kt +++ b/app/src/main/kotlin/org/gameyfin/app/users/UserService.kt @@ -9,15 +9,15 @@ import org.gameyfin.app.core.events.* import org.gameyfin.app.core.security.getCurrentAuth import org.gameyfin.app.games.entities.Image 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.UserUpdateDto import org.gameyfin.app.users.emailconfirmation.EmailConfirmationService 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.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.UserDetails import org.springframework.security.core.userdetails.UserDetailsService @@ -56,7 +56,7 @@ class UserService( true, true, true, - toAuthorities(user.roles) + user.roles.toAuthorities() ) } @@ -67,7 +67,7 @@ class UserService( 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? = userRepository.findByOidcProviderId(oidcProviderId) - fun getAllUsers(): List { - return userRepository.findAll().map { u -> toUserInfo(u) } + fun getAllUsers(): List { + return userRepository.findAll().map { it.toExtendedUserInfoDto() } } 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'") } - fun getUserInfo(): UserInfoAdminDto { + fun getUserInfo(): ExtendedUserInfoDto { val auth = getCurrentAuth() val principal = auth.principal if (principal is OidcUser) { val oidcUser = org.gameyfin.app.users.entities.User(principal) - val userInfoDto = toUserInfo(oidcUser) + val userInfoDto = oidcUser.toExtendedUserInfoDto() userInfoDto.roles = roleService.extractGrantedAuthorities(principal.authorities) - .mapNotNull { Role.Companion.safeValueOf(it.authority) } + .mapNotNull { Role.safeValueOf(it.authority) } return userInfoDto } val user = getByUsernameNonNull(auth.name) - return toUserInfo(user) + return user.toExtendedUserInfoDto() } fun getAvatar(username: String): Image? { @@ -158,7 +158,7 @@ class UserService( RegistrationAttemptWithExistingEmailEvent( this, it, - Utils.Companion.getBaseUrl() + Utils.getBaseUrl() ) ) return @@ -179,12 +179,12 @@ class UserService( if (adminNeedsToApprove) { eventPublisher.publishEvent(UserRegistrationWaitingForApprovalEvent(this, user)) } else { - eventPublisher.publishEvent(AccountStatusChangedEvent(this, user, Utils.Companion.getBaseUrl())) + eventPublisher.publishEvent(AccountStatusChangedEvent(this, user, Utils.getBaseUrl())) } if (!user.emailConfirmed) { 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.emailConfirmed = false val token = emailConfirmationService.generate(user) - eventPublisher.publishEvent(EmailNeedsConfirmationEvent(this, token, Utils.Companion.getBaseUrl())) + eventPublisher.publishEvent(EmailNeedsConfirmationEvent(this, token, Utils.getBaseUrl())) } userRepository.save(user) @@ -246,7 +246,7 @@ class UserService( 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 currentUserLevel = roleService.getHighestRoleFromAuthorities(currentUser.authorities).powerLevel @@ -276,29 +276,12 @@ class UserService( val user = getByUsernameNonNull(username) user.enabled = enabled userRepository.save(user) - eventPublisher.publishEvent(AccountStatusChangedEvent(this, user, Utils.Companion.getBaseUrl())) + eventPublisher.publishEvent(AccountStatusChangedEvent(this, user, Utils.getBaseUrl())) } fun deleteUser(username: String) { val user = getByUsernameNonNull(username) userRepository.delete(user) - eventPublisher.publishEvent(AccountDeletedEvent(this, user, Utils.Companion.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): List { - return roles.map { r -> SimpleGrantedAuthority(r.roleName) } + eventPublisher.publishEvent(AccountDeletedEvent(this, user, Utils.getBaseUrl())) } } \ No newline at end of file diff --git a/app/src/main/kotlin/org/gameyfin/app/users/dto/UserInfoAdminDto.kt b/app/src/main/kotlin/org/gameyfin/app/users/dto/ExtendedUserInfoDto.kt similarity index 90% rename from app/src/main/kotlin/org/gameyfin/app/users/dto/UserInfoAdminDto.kt rename to app/src/main/kotlin/org/gameyfin/app/users/dto/ExtendedUserInfoDto.kt index 5a93cde..d125756 100644 --- a/app/src/main/kotlin/org/gameyfin/app/users/dto/UserInfoAdminDto.kt +++ b/app/src/main/kotlin/org/gameyfin/app/users/dto/ExtendedUserInfoDto.kt @@ -2,7 +2,7 @@ package org.gameyfin.app.users.dto import org.gameyfin.app.core.Role -data class UserInfoAdminDto( +data class ExtendedUserInfoDto( val username: String, val managedBySso: Boolean, val email: String, diff --git a/app/src/main/kotlin/org/gameyfin/app/users/dto/UserInfoUserDto.kt b/app/src/main/kotlin/org/gameyfin/app/users/dto/UserInfoDto.kt similarity index 81% rename from app/src/main/kotlin/org/gameyfin/app/users/dto/UserInfoUserDto.kt rename to app/src/main/kotlin/org/gameyfin/app/users/dto/UserInfoDto.kt index 5bbbfbc..d975a85 100644 --- a/app/src/main/kotlin/org/gameyfin/app/users/dto/UserInfoUserDto.kt +++ b/app/src/main/kotlin/org/gameyfin/app/users/dto/UserInfoDto.kt @@ -1,6 +1,6 @@ package org.gameyfin.app.users.dto -data class UserInfoUserDto( +data class UserInfoDto( val username: String, val hasAvatar: Boolean, val avatarId: Long? = null, diff --git a/app/src/main/kotlin/org/gameyfin/app/users/extensions/UserExtensions.kt b/app/src/main/kotlin/org/gameyfin/app/users/extensions/UserExtensions.kt new file mode 100644 index 0000000..77e715f --- /dev/null +++ b/app/src/main/kotlin/org/gameyfin/app/users/extensions/UserExtensions.kt @@ -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.toAuthorities(): List { + return this.map { r -> SimpleGrantedAuthority(r.roleName) } +} \ No newline at end of file