Move AvatarController to ImageController

This commit is contained in:
grimsi
2024-12-22 13:09:02 +01:00
parent eb14007190
commit a45d8812dc
10 changed files with 67 additions and 66 deletions
@@ -5,7 +5,7 @@ export async function uploadAvatar(avatar: any) {
const formData = new FormData();
formData.append("file", avatar);
const response = await fetchWithAuth("avatar/upload", formData);
const response = await fetchWithAuth("images/avatar/upload", formData);
const result = await response.text();
@@ -17,7 +17,7 @@ export async function uploadAvatar(avatar: any) {
}
export async function removeAvatar() {
const response = await fetchWithAuth("avatar/delete")
const response = await fetchWithAuth("images/avatar/delete")
const result = await response.text();
@@ -29,7 +29,7 @@ export async function removeAvatar() {
}
export async function removeAvatarByName(name: string) {
const response = await fetchWithAuth("avatar/deleteByName?" + new URLSearchParams({name: name}))
const response = await fetchWithAuth("images/avatar/deleteByName?" + new URLSearchParams({name: name}))
const result = await response.text();
@@ -3,11 +3,11 @@ import {Button, Input} from "@nextui-org/react";
import {toast} from "sonner";
import {LibraryEndpoint, SystemEndpoint} from "Frontend/generated/endpoints";
import {useState} from "react";
import Game from "Frontend/generated/de/grimsi/gameyfin/games/Game";
import GameDto from "Frontend/generated/de/grimsi/gameyfin/games/dto/GameDto";
export default function TestView() {
const [gameTitle, setGameTitle] = useState("");
const [game, setGame] = useState<Game>();
const [game, setGame] = useState<GameDto>();
function getGame() {
LibraryEndpoint.test(gameTitle).then(game => {
@@ -16,7 +16,7 @@ class Image(
@GeneratedValue(strategy = GenerationType.AUTO)
var id: Long? = null,
val originalUrl: URL,
val originalUrl: URL? = null,
val type: ImageType,
@@ -35,5 +35,6 @@ class Image(
enum class ImageType {
COVER,
SCREENSHOT
SCREENSHOT,
AVATAR
}
@@ -1,26 +1,24 @@
package de.grimsi.gameyfin.users.avatar
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 jakarta.servlet.http.HttpServletResponse
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.GetMapping
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.bind.annotation.*
import org.springframework.web.multipart.MultipartFile
@RestController
class AvatarController(
private val userService: UserService
@RequestMapping("/images")
class ImageController(
private val userService: UserService,
private val imageService: ImageService
) {
@PostMapping("/avatar/upload")
@@ -42,14 +40,13 @@ class AvatarController(
}
@PermitAll
@GetMapping("/images/avatar")
@GetMapping("/avatar")
fun getAvatar(
@RequestParam("username") username: String,
response: HttpServletResponse
@RequestParam("username") username: String
): ResponseEntity<InputStreamResource>? {
val avatar = userService.getAvatar(username) ?: return ResponseEntity.notFound().build()
val file = avatar.let { userService.getAvatarFile(it) }
val file = avatar.let { imageService.getFileContent(it) }
val inputStreamResource = InputStreamResource(file)
val headers = HttpHeaders()
@@ -0,0 +1,36 @@
package de.grimsi.gameyfin.media
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.stereotype.Service
import java.io.InputStream
@Service
class ImageService(
private val imageRepository: ImageRepository,
private val imageContentStore: ImageContentStore
) {
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 {
return imageContentStore.getContent(image)
}
fun deleteFile(image: Image) {
imageContentStore.unsetContent(image)
imageRepository.delete(image)
}
fun updateFileContent(image: Image, content: InputStream, mimeType: String? = null): Image {
mimeType?.let { image.mimeType = it }
imageRepository.save(image)
return imageContentStore.setContent(image, content)
}
}
@@ -5,14 +5,15 @@ import de.grimsi.gameyfin.config.ConfigService
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
import de.grimsi.gameyfin.users.dto.UserUpdateDto
import de.grimsi.gameyfin.users.emailconfirmation.EmailConfirmationService
import de.grimsi.gameyfin.users.entities.Avatar
import de.grimsi.gameyfin.users.entities.User
import de.grimsi.gameyfin.users.enums.RoleAssignmentResult
import de.grimsi.gameyfin.users.persistence.AvatarContentStore
import de.grimsi.gameyfin.users.persistence.UserRepository
import io.github.oshai.kotlinlogging.KotlinLogging
import jakarta.transaction.Transactional
@@ -28,14 +29,13 @@ 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
import java.io.InputStream
@Service
@Transactional
class UserService(
private val userRepository: UserRepository,
private val avatarStore: AvatarContentStore,
private val imageService: ImageService,
private val passwordEncoder: PasswordEncoder,
private val roleService: RoleService,
private val sessionService: SessionService,
@@ -100,23 +100,20 @@ class UserService(
return toUserInfo(user)
}
fun getAvatar(username: String): Avatar? {
fun getAvatar(username: String): Image? {
val user = getByUsernameNonNull(username)
return user.avatar
}
fun getAvatarFile(avatar: Avatar): InputStream {
return avatarStore.getContent(avatar)
}
fun setAvatar(username: String, file: MultipartFile) {
val user = getByUsernameNonNull(username)
if (user.avatar == null) {
user.avatar = Avatar(mimeType = file.contentType)
user.avatar = imageService.createFile(ImageType.AVATAR, file.inputStream, file.contentType!!)
} else {
user.avatar = imageService.updateFileContent(user.avatar!!, file.inputStream, file.contentType!!)
}
avatarStore.setContent(user.avatar, file.inputStream)
userRepository.save(user)
}
@@ -124,8 +121,7 @@ class UserService(
val user = getByUsernameNonNull(username)
if (user.avatar == null) return
avatarStore.unsetContent(user.avatar)
imageService.deleteFile(user.avatar!!)
user.avatar = null
userRepository.save(user)
@@ -275,6 +271,7 @@ class UserService(
emailConfirmed = user.emailConfirmed,
isEnabled = user.enabled,
hasAvatar = user.avatar != null,
avatarId = user.avatar?.contentId,
managedBySso = user.oidcProviderId != null,
roles = user.roles
)
@@ -9,5 +9,6 @@ data class UserInfoDto(
val emailConfirmed: Boolean,
val isEnabled: Boolean,
val hasAvatar: Boolean,
val avatarId: String? = null,
var roles: Set<Role>
)
@@ -1,23 +0,0 @@
package de.grimsi.gameyfin.users.entities
import jakarta.annotation.Nullable
import jakarta.persistence.Embeddable
import org.springframework.content.commons.annotations.ContentId
import org.springframework.content.commons.annotations.ContentLength
import org.springframework.content.commons.annotations.MimeType
@Embeddable
class Avatar(
@ContentId
@Nullable
var contentId: String? = null,
@ContentLength
@Nullable
var contentLength: Long? = null,
@MimeType
@Nullable
var mimeType: String? = null
)
@@ -2,6 +2,7 @@ package de.grimsi.gameyfin.users.entities
import de.grimsi.gameyfin.core.Role
import de.grimsi.gameyfin.core.security.EncryptionConverter
import de.grimsi.gameyfin.games.entities.Image
import jakarta.annotation.Nullable
import jakarta.persistence.*
import jakarta.validation.constraints.NotNull
@@ -32,9 +33,8 @@ class User(
var enabled: Boolean = false,
@Embedded
@Nullable
var avatar: Avatar? = null,
@OneToOne(cascade = [CascadeType.ALL])
var avatar: Image? = null,
@ElementCollection(targetClass = Role::class, fetch = FetchType.EAGER)
@Enumerated(EnumType.STRING)
@@ -1,8 +0,0 @@
package de.grimsi.gameyfin.users.persistence
import de.grimsi.gameyfin.users.entities.Avatar
import org.springframework.content.commons.store.ContentStore
import org.springframework.stereotype.Repository
@Repository
interface AvatarContentStore : ContentStore<Avatar, String>