mirror of
https://github.com/BrenBroZAYT/gameyfin.git
synced 2026-06-13 16:40:01 +00:00
Migrate to ImageEndpoint
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="UI debug" type="JavascriptDebugType" uri="http://localhost:8080">
|
||||
<configuration default="false" name="UI debug" type="JavascriptDebugType" engineId="37cae5b9-e8b2-4949-9172-aafa37fbc09c" uri="http://localhost:8080">
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
@@ -1,6 +1,7 @@
|
||||
package de.grimsi.gameyfin.core.annotations
|
||||
|
||||
import kotlin.annotation.AnnotationRetention.RUNTIME
|
||||
import kotlin.annotation.AnnotationTarget.CLASS
|
||||
import kotlin.annotation.AnnotationTarget.FUNCTION
|
||||
|
||||
/**
|
||||
@@ -9,6 +10,6 @@ import kotlin.annotation.AnnotationTarget.FUNCTION
|
||||
* One example would be the main library view.
|
||||
*/
|
||||
|
||||
@Target(FUNCTION)
|
||||
@Target(FUNCTION, CLASS)
|
||||
@Retention(RUNTIME)
|
||||
annotation class DynamicPublicAccess
|
||||
@@ -66,7 +66,7 @@ class GameService(
|
||||
|
||||
var game = toEntity(metadata, path, plugin)
|
||||
game = createOrUpdate(game)
|
||||
|
||||
|
||||
return toDto(game)
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ class GameService(
|
||||
keywords = game.keywords.toList(),
|
||||
features = game.features.map { it.name },
|
||||
perspectives = game.perspectives.map { it.name },
|
||||
images = game.images.mapNotNull { it.contentId },
|
||||
images = game.images.mapNotNull { it.id },
|
||||
videoUrls = game.videoUrls.map { it.toString() },
|
||||
source = game.source.pluginId
|
||||
)
|
||||
|
||||
@@ -16,7 +16,7 @@ class GameDto(
|
||||
val keywords: List<String>?,
|
||||
val features: List<String>?,
|
||||
val perspectives: List<String>?,
|
||||
val images: List<String>?,
|
||||
val images: List<Long>?,
|
||||
val videoUrls: List<String>?,
|
||||
val source: String?
|
||||
)
|
||||
@@ -1,60 +0,0 @@
|
||||
package de.grimsi.gameyfin.media
|
||||
|
||||
import de.grimsi.gameyfin.core.Role
|
||||
import de.grimsi.gameyfin.users.UserService
|
||||
import jakarta.annotation.security.PermitAll
|
||||
import jakarta.annotation.security.RolesAllowed
|
||||
import org.springframework.core.io.InputStreamResource
|
||||
import org.springframework.http.HttpHeaders
|
||||
import org.springframework.http.MediaType
|
||||
import org.springframework.http.ResponseEntity
|
||||
import org.springframework.security.core.Authentication
|
||||
import org.springframework.security.core.context.SecurityContextHolder
|
||||
import org.springframework.web.bind.annotation.*
|
||||
import org.springframework.web.multipart.MultipartFile
|
||||
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/images")
|
||||
class ImageController(
|
||||
private val userService: UserService,
|
||||
private val imageService: ImageService
|
||||
) {
|
||||
|
||||
@PostMapping("/avatar/upload")
|
||||
fun uploadAvatar(@RequestParam("file") file: MultipartFile) {
|
||||
val auth: Authentication = SecurityContextHolder.getContext().authentication
|
||||
userService.setAvatar(auth.name, file)
|
||||
}
|
||||
|
||||
@PostMapping("/avatar/delete")
|
||||
fun deleteAvatar() {
|
||||
val auth: Authentication = SecurityContextHolder.getContext().authentication
|
||||
userService.deleteAvatar(auth.name)
|
||||
}
|
||||
|
||||
@RolesAllowed(Role.Names.ADMIN)
|
||||
@PostMapping("/avatar/deleteByName")
|
||||
fun deleteAvatarByName(@RequestParam("name") name: String) {
|
||||
userService.deleteAvatar(name)
|
||||
}
|
||||
|
||||
@PermitAll
|
||||
@GetMapping("/avatar")
|
||||
fun getAvatar(
|
||||
@RequestParam("username") username: String
|
||||
): ResponseEntity<InputStreamResource>? {
|
||||
val avatar = userService.getAvatar(username) ?: return ResponseEntity.notFound().build()
|
||||
|
||||
val file = avatar.let { imageService.getFileContent(it) }
|
||||
|
||||
val inputStreamResource = InputStreamResource(file)
|
||||
val headers = HttpHeaders()
|
||||
headers.contentLength = avatar.contentLength!!
|
||||
headers.contentType = MediaType.parseMediaType(avatar.mimeType!!)
|
||||
|
||||
return ResponseEntity.ok()
|
||||
.headers(headers)
|
||||
.body(inputStreamResource)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
package de.grimsi.gameyfin.media
|
||||
|
||||
import de.grimsi.gameyfin.core.Role
|
||||
import de.grimsi.gameyfin.core.annotations.DynamicPublicAccess
|
||||
import de.grimsi.gameyfin.games.entities.Image
|
||||
import de.grimsi.gameyfin.games.entities.ImageType
|
||||
import de.grimsi.gameyfin.users.UserService
|
||||
import jakarta.annotation.security.RolesAllowed
|
||||
import org.springframework.core.io.InputStreamResource
|
||||
import org.springframework.http.HttpHeaders
|
||||
import org.springframework.http.MediaType
|
||||
import org.springframework.http.ResponseEntity
|
||||
import org.springframework.security.core.Authentication
|
||||
import org.springframework.security.core.context.SecurityContextHolder
|
||||
import org.springframework.web.bind.annotation.*
|
||||
import org.springframework.web.multipart.MultipartFile
|
||||
|
||||
@DynamicPublicAccess
|
||||
@RestController
|
||||
@RequestMapping("/images")
|
||||
class ImageEndpoint(
|
||||
private val imageService: ImageService,
|
||||
private val userService: UserService
|
||||
) {
|
||||
|
||||
@GetMapping("/screenshot/{id}")
|
||||
fun getScreenshot(@PathVariable("id") id: Long): ResponseEntity<InputStreamResource>? {
|
||||
return getImageContent(id)
|
||||
}
|
||||
|
||||
@GetMapping("/cover/{id}")
|
||||
fun getCover(@PathVariable("id") id: Long): ResponseEntity<InputStreamResource>? {
|
||||
return getImageContent(id)
|
||||
}
|
||||
|
||||
@GetMapping("/avatar")
|
||||
fun getAvatarByUsername(@RequestParam username: String): ResponseEntity<InputStreamResource>? {
|
||||
val avatar = userService.getAvatar(username) ?: return ResponseEntity.notFound().build()
|
||||
if (avatar.id == null) return ResponseEntity.notFound().build()
|
||||
return getImageContent(avatar.id!!)
|
||||
}
|
||||
|
||||
@PostMapping("/avatar/upload")
|
||||
fun uploadAvatar(@RequestParam("file") file: MultipartFile) {
|
||||
val auth: Authentication = SecurityContextHolder.getContext().authentication
|
||||
|
||||
val image: Image = if (userService.hasAvatar(auth.name)) {
|
||||
imageService.createFile(ImageType.AVATAR, file.inputStream, file.contentType!!)
|
||||
} else {
|
||||
val existingAvatar = userService.getAvatar(auth.name)!!
|
||||
imageService.updateFileContent(existingAvatar, file.inputStream, file.contentType!!)
|
||||
}
|
||||
|
||||
userService.updateAvatar(auth.name, image)
|
||||
}
|
||||
|
||||
@PostMapping("/avatar/delete")
|
||||
fun deleteAvatar() {
|
||||
val auth: Authentication = SecurityContextHolder.getContext().authentication
|
||||
userService.deleteAvatar(auth.name)
|
||||
}
|
||||
|
||||
@RolesAllowed(Role.Names.ADMIN)
|
||||
@PostMapping("/avatar/deleteByName")
|
||||
fun deleteAvatarByName(@RequestParam("name") name: String) {
|
||||
userService.deleteAvatar(name)
|
||||
}
|
||||
|
||||
private fun getImageContent(id: Long): ResponseEntity<InputStreamResource>? {
|
||||
val image = imageService.getImage(id) ?: return ResponseEntity.notFound().build()
|
||||
|
||||
val file = image.let { imageService.getFileContent(it) }
|
||||
|
||||
if (file == null) return ResponseEntity.notFound().build()
|
||||
|
||||
val inputStreamResource = InputStreamResource(file)
|
||||
|
||||
val headers = HttpHeaders()
|
||||
headers.contentLength = image.contentLength!!
|
||||
headers.contentType = MediaType.parseMediaType(image.mimeType!!)
|
||||
|
||||
return ResponseEntity.ok()
|
||||
.headers(headers)
|
||||
.body(inputStreamResource)
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import de.grimsi.gameyfin.games.entities.Image
|
||||
import de.grimsi.gameyfin.games.entities.ImageType
|
||||
import de.grimsi.gameyfin.games.repositories.ImageContentStore
|
||||
import de.grimsi.gameyfin.games.repositories.ImageRepository
|
||||
import org.springframework.data.repository.findByIdOrNull
|
||||
import org.springframework.stereotype.Service
|
||||
import java.io.InputStream
|
||||
|
||||
@@ -13,14 +14,24 @@ class ImageService(
|
||||
private val imageContentStore: ImageContentStore
|
||||
) {
|
||||
|
||||
fun getImage(id: Long): Image? {
|
||||
return imageRepository.findByIdOrNull(id)
|
||||
}
|
||||
|
||||
fun createFile(type: ImageType, content: InputStream, mimeType: String): Image {
|
||||
val image = Image(type = type, mimeType = mimeType)
|
||||
imageRepository.save(image)
|
||||
return imageContentStore.setContent(image, content)
|
||||
}
|
||||
|
||||
fun getFileContent(image: Image): InputStream {
|
||||
fun getFileContent(id: Long): InputStream? {
|
||||
val image = getImage(id) ?: return null
|
||||
return getFileContent(image)
|
||||
}
|
||||
|
||||
fun getFileContent(image: Image): InputStream? {
|
||||
return imageContentStore.getContent(image)
|
||||
|
||||
}
|
||||
|
||||
fun deleteFile(image: Image) {
|
||||
|
||||
@@ -6,7 +6,6 @@ import de.grimsi.gameyfin.core.Role
|
||||
import de.grimsi.gameyfin.core.Utils
|
||||
import de.grimsi.gameyfin.core.events.*
|
||||
import de.grimsi.gameyfin.games.entities.Image
|
||||
import de.grimsi.gameyfin.games.entities.ImageType
|
||||
import de.grimsi.gameyfin.media.ImageService
|
||||
import de.grimsi.gameyfin.users.dto.UserInfoDto
|
||||
import de.grimsi.gameyfin.users.dto.UserRegistrationDto
|
||||
@@ -28,7 +27,6 @@ import org.springframework.security.core.userdetails.UsernameNotFoundException
|
||||
import org.springframework.security.crypto.password.PasswordEncoder
|
||||
import org.springframework.security.oauth2.core.oidc.user.OidcUser
|
||||
import org.springframework.stereotype.Service
|
||||
import org.springframework.web.multipart.MultipartFile
|
||||
|
||||
|
||||
@Service
|
||||
@@ -105,15 +103,9 @@ class UserService(
|
||||
return user.avatar
|
||||
}
|
||||
|
||||
fun setAvatar(username: String, file: MultipartFile) {
|
||||
fun updateAvatar(username: String, newAvatar: Image) {
|
||||
val user = getByUsernameNonNull(username)
|
||||
|
||||
if (user.avatar == null) {
|
||||
user.avatar = imageService.createFile(ImageType.AVATAR, file.inputStream, file.contentType!!)
|
||||
} else {
|
||||
user.avatar = imageService.updateFileContent(user.avatar!!, file.inputStream, file.contentType!!)
|
||||
}
|
||||
|
||||
user.avatar = newAvatar
|
||||
userRepository.save(user)
|
||||
}
|
||||
|
||||
@@ -127,6 +119,11 @@ class UserService(
|
||||
userRepository.save(user)
|
||||
}
|
||||
|
||||
fun hasAvatar(username: String): Boolean {
|
||||
val user = getByUsernameNonNull(username)
|
||||
return user.avatar != null && user.avatar!!.id != null
|
||||
}
|
||||
|
||||
fun registerOrUpdateUser(user: User): User {
|
||||
user.password = passwordEncoder.encode(user.password)
|
||||
return userRepository.save(user)
|
||||
@@ -271,7 +268,7 @@ class UserService(
|
||||
emailConfirmed = user.emailConfirmed,
|
||||
isEnabled = user.enabled,
|
||||
hasAvatar = user.avatar != null,
|
||||
avatarId = user.avatar?.contentId,
|
||||
avatarId = user.avatar?.id,
|
||||
managedBySso = user.oidcProviderId != null,
|
||||
roles = user.roles
|
||||
)
|
||||
|
||||
@@ -9,6 +9,6 @@ data class UserInfoDto(
|
||||
val emailConfirmed: Boolean,
|
||||
val isEnabled: Boolean,
|
||||
val hasAvatar: Boolean,
|
||||
val avatarId: String? = null,
|
||||
val avatarId: Long? = null,
|
||||
var roles: Set<Role>
|
||||
)
|
||||
Reference in New Issue
Block a user