refactor: remove deprecated classes

BREAKING-CHANGE: /api/v1/users and /api/v1/series/{seriesId}/read-progress/tachiyomi have been removed
This commit is contained in:
Gauthier Roebroeck 2022-11-10 17:40:57 +08:00
parent d4aa113baf
commit 2a5ce25193
8 changed files with 15 additions and 355 deletions

View File

@ -24,9 +24,6 @@ class KomgaProperties {
var deleteEmptyCollections: Boolean = true
@Deprecated("Deprecated since 0.143.0, you can configure this in the library options directly")
var fileHashing: Boolean = true
@Positive
var pageHashing: Int = 3

View File

@ -27,24 +27,6 @@ class ReadProgressDtoDao(
private val countRead: AggregateFunction<BigDecimal> = DSL.sum(DSL.`when`(r.COMPLETED.isTrue, 1).otherwise(0))
private val countInProgress: AggregateFunction<BigDecimal> = DSL.sum(DSL.`when`(r.COMPLETED.isFalse, 1).otherwise(0))
override fun findProgressBySeries(seriesId: String, userId: String): TachiyomiReadProgressDto {
val indexedReadProgress = dsl.select(
rowNumber().over().orderBy(d.NUMBER_SORT),
r.COMPLETED,
)
.from(b)
.leftJoin(r).on(b.ID.eq(r.BOOK_ID)).and(readProgressCondition(userId))
.leftJoin(d).on(b.ID.eq(d.BOOK_ID))
.where(b.SERIES_ID.eq(seriesId))
.orderBy(d.NUMBER_SORT)
.fetch()
.toList()
val booksCount = getSeriesBooksCount(seriesId, userId)
return booksCountToDto(booksCount, indexedReadProgress.lastRead() ?: 0)
}
override fun findProgressV2BySeries(seriesId: String, userId: String): TachiyomiReadProgressV2Dto {
val numberSortReadProgress = dsl.select(
d.NUMBER_SORT,

View File

@ -4,7 +4,6 @@ import org.gotson.komga.interfaces.api.rest.dto.TachiyomiReadProgressDto
import org.gotson.komga.interfaces.api.rest.dto.TachiyomiReadProgressV2Dto
interface ReadProgressDtoRepository {
fun findProgressBySeries(seriesId: String, userId: String): TachiyomiReadProgressDto
fun findProgressV2BySeries(seriesId: String, userId: String): TachiyomiReadProgressV2Dto
fun findProgressByReadList(readListId: String, userId: String): TachiyomiReadProgressDto
}

View File

@ -4,8 +4,8 @@ import jakarta.validation.constraints.Email
import jakarta.validation.constraints.NotBlank
import org.gotson.komga.domain.model.KomgaUser
import org.gotson.komga.domain.service.KomgaUserLifecycle
import org.gotson.komga.interfaces.api.rest.dto.UserDtoV2
import org.gotson.komga.interfaces.api.rest.dto.toDtoV2
import org.gotson.komga.interfaces.api.rest.dto.UserDto
import org.gotson.komga.interfaces.api.rest.dto.toDto
import org.springframework.http.HttpStatus
import org.springframework.http.MediaType
import org.springframework.validation.annotation.Validated
@ -34,7 +34,7 @@ class ClaimController(
@NotBlank
@RequestHeader("X-Komga-Password")
password: String,
): UserDtoV2 {
): UserDto {
if (userDetailsLifecycle.countUsers() > 0)
throw ResponseStatusException(HttpStatus.BAD_REQUEST, "This server has already been claimed")
@ -44,7 +44,7 @@ class ClaimController(
password = password,
roleAdmin = true,
),
).toDtoV2()
).toDto()
}
data class ClaimStatus(

View File

@ -58,8 +58,6 @@ import org.gotson.komga.interfaces.api.rest.dto.GroupCountDto
import org.gotson.komga.interfaces.api.rest.dto.SeriesDto
import org.gotson.komga.interfaces.api.rest.dto.SeriesMetadataUpdateDto
import org.gotson.komga.interfaces.api.rest.dto.SeriesThumbnailDto
import org.gotson.komga.interfaces.api.rest.dto.TachiyomiReadProgressDto
import org.gotson.komga.interfaces.api.rest.dto.TachiyomiReadProgressUpdateDto
import org.gotson.komga.interfaces.api.rest.dto.TachiyomiReadProgressUpdateV2Dto
import org.gotson.komga.interfaces.api.rest.dto.TachiyomiReadProgressV2Dto
import org.gotson.komga.interfaces.api.rest.dto.restrictUrl
@ -596,17 +594,6 @@ class SeriesController(
seriesLifecycle.deleteReadProgress(seriesId, principal.user)
}
@Deprecated("Use v2 for proper handling of chapter number with numberSort")
@GetMapping("v1/series/{seriesId}/read-progress/tachiyomi")
fun getReadProgressTachiyomi(
@PathVariable seriesId: String,
@AuthenticationPrincipal principal: KomgaPrincipal,
): TachiyomiReadProgressDto {
principal.user.checkContentRestriction(seriesId)
return readProgressDtoRepository.findProgressBySeries(seriesId, principal.user.id)
}
@GetMapping("v2/series/{seriesId}/read-progress/tachiyomi")
fun getReadProgressTachiyomiV2(
@PathVariable seriesId: String,
@ -617,28 +604,6 @@ class SeriesController(
return readProgressDtoRepository.findProgressV2BySeries(seriesId, principal.user.id)
}
@Deprecated("Use v2 for proper handling of chapter number with numberSort")
@PutMapping("v1/series/{seriesId}/read-progress/tachiyomi")
@ResponseStatus(HttpStatus.NO_CONTENT)
fun markReadProgressTachiyomi(
@PathVariable seriesId: String,
@Valid @RequestBody
readProgress: TachiyomiReadProgressUpdateDto,
@AuthenticationPrincipal principal: KomgaPrincipal,
) {
principal.user.checkContentRestriction(seriesId)
bookDtoRepository.findAll(
BookSearchWithReadProgress(seriesIds = listOf(seriesId)),
principal.user.id,
UnpagedSorted(Sort.by(Sort.Order.asc("metadata.numberSort"))),
).filterIndexed { index, _ -> index < readProgress.lastBookRead }
.forEach { book ->
if (book.readProgress?.completed != true)
bookLifecycle.markReadProgressCompleted(book.id, principal.user)
}
}
@PutMapping("v2/series/{seriesId}/read-progress/tachiyomi")
@ResponseStatus(HttpStatus.NO_CONTENT)
fun markReadProgressTachiyomiV2(

View File

@ -18,10 +18,9 @@ import org.gotson.komga.infrastructure.security.KomgaPrincipal
import org.gotson.komga.interfaces.api.rest.dto.AuthenticationActivityDto
import org.gotson.komga.interfaces.api.rest.dto.PasswordUpdateDto
import org.gotson.komga.interfaces.api.rest.dto.UserCreationDto
import org.gotson.komga.interfaces.api.rest.dto.UserDtoV2
import org.gotson.komga.interfaces.api.rest.dto.UserDto
import org.gotson.komga.interfaces.api.rest.dto.UserUpdateDto
import org.gotson.komga.interfaces.api.rest.dto.toDto
import org.gotson.komga.interfaces.api.rest.dto.toDtoV2
import org.springdoc.core.converters.models.PageableAsQueryParam
import org.springframework.core.env.Environment
import org.springframework.data.domain.Page
@ -48,7 +47,7 @@ private val logger = KotlinLogging.logger {}
@RestController
@RequestMapping("api/v2/users", produces = [MediaType.APPLICATION_JSON_VALUE])
class UserV2Controller(
class UserController(
private val userLifecycle: KomgaUserLifecycle,
private val userRepository: KomgaUserRepository,
private val libraryRepository: LibraryRepository,
@ -59,8 +58,8 @@ class UserV2Controller(
private val demo = env.activeProfiles.contains("demo")
@GetMapping("me")
fun getMe(@AuthenticationPrincipal principal: KomgaPrincipal): UserDtoV2 =
principal.toDtoV2()
fun getMe(@AuthenticationPrincipal principal: KomgaPrincipal): UserDto =
principal.toDto()
@PatchMapping("me/password")
@ResponseStatus(HttpStatus.NO_CONTENT)
@ -77,8 +76,8 @@ class UserV2Controller(
@GetMapping
@PreAuthorize("hasRole('$ROLE_ADMIN')")
fun getAll(): List<UserDtoV2> =
userRepository.findAll().map { it.toDtoV2() }
fun getAll(): List<UserDto> =
userRepository.findAll().map { it.toDto() }
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
@ -86,9 +85,9 @@ class UserV2Controller(
fun addOne(
@Valid @RequestBody
newUser: UserCreationDto,
): UserDtoV2 =
): UserDto =
try {
userLifecycle.createUser(newUser.toDomain()).toDtoV2()
userLifecycle.createUser(newUser.toDomain()).toDto()
} catch (e: UserEmailAlreadyExistsException) {
throw ResponseStatusException(HttpStatus.BAD_REQUEST, "A user with this email already exists")
}

View File

@ -1,233 +0,0 @@
@file:Suppress("DEPRECATION")
package org.gotson.komga.interfaces.api.rest
import io.swagger.v3.oas.annotations.Parameter
import jakarta.validation.Valid
import mu.KotlinLogging
import org.gotson.komga.domain.model.ROLE_ADMIN
import org.gotson.komga.domain.model.ROLE_FILE_DOWNLOAD
import org.gotson.komga.domain.model.ROLE_PAGE_STREAMING
import org.gotson.komga.domain.model.UserEmailAlreadyExistsException
import org.gotson.komga.domain.persistence.AuthenticationActivityRepository
import org.gotson.komga.domain.persistence.KomgaUserRepository
import org.gotson.komga.domain.persistence.LibraryRepository
import org.gotson.komga.domain.service.KomgaUserLifecycle
import org.gotson.komga.infrastructure.jooq.UnpagedSorted
import org.gotson.komga.infrastructure.security.KomgaPrincipal
import org.gotson.komga.interfaces.api.rest.dto.AuthenticationActivityDto
import org.gotson.komga.interfaces.api.rest.dto.PasswordUpdateDto
import org.gotson.komga.interfaces.api.rest.dto.RolesUpdateDto
import org.gotson.komga.interfaces.api.rest.dto.SharedLibrariesUpdateDto
import org.gotson.komga.interfaces.api.rest.dto.UserCreationDto
import org.gotson.komga.interfaces.api.rest.dto.UserDto
import org.gotson.komga.interfaces.api.rest.dto.UserWithSharedLibrariesDto
import org.gotson.komga.interfaces.api.rest.dto.toDto
import org.gotson.komga.interfaces.api.rest.dto.toWithSharedLibrariesDto
import org.springdoc.core.converters.models.PageableAsQueryParam
import org.springframework.core.env.Environment
import org.springframework.data.domain.Page
import org.springframework.data.domain.PageRequest
import org.springframework.data.domain.Pageable
import org.springframework.data.domain.Sort
import org.springframework.http.HttpStatus
import org.springframework.http.MediaType
import org.springframework.security.access.prepost.PreAuthorize
import org.springframework.security.core.annotation.AuthenticationPrincipal
import org.springframework.web.bind.annotation.DeleteMapping
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PatchMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.ResponseStatus
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.server.ResponseStatusException
private val logger = KotlinLogging.logger {}
@Deprecated("User api/v2/users instead")
@RestController
@RequestMapping("api/v1/users", produces = [MediaType.APPLICATION_JSON_VALUE])
class UserV1Controller(
private val userLifecycle: KomgaUserLifecycle,
private val userRepository: KomgaUserRepository,
private val libraryRepository: LibraryRepository,
private val authenticationActivityRepository: AuthenticationActivityRepository,
env: Environment,
) {
private val demo = env.activeProfiles.contains("demo")
@GetMapping("me")
@Deprecated("User api/v2/users instead")
fun getMe(@AuthenticationPrincipal principal: KomgaPrincipal): UserDto =
principal.user.toDto()
@PatchMapping("me/password")
@Deprecated("User api/v2/users instead")
@ResponseStatus(HttpStatus.NO_CONTENT)
fun updateMyPassword(
@AuthenticationPrincipal principal: KomgaPrincipal,
@Valid @RequestBody
newPasswordDto: PasswordUpdateDto,
) {
if (demo) throw ResponseStatusException(HttpStatus.FORBIDDEN)
userRepository.findByEmailIgnoreCaseOrNull(principal.username)?.let { user ->
userLifecycle.updatePassword(user, newPasswordDto.password, false)
} ?: throw ResponseStatusException(HttpStatus.NOT_FOUND)
}
@GetMapping
@PreAuthorize("hasRole('$ROLE_ADMIN')")
@Deprecated("User api/v2/users instead")
fun getAll(): List<UserWithSharedLibrariesDto> =
userRepository.findAll().map { it.toWithSharedLibrariesDto() }
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
@PreAuthorize("hasRole('$ROLE_ADMIN')")
@Deprecated("User api/v2/users instead")
fun addOne(
@Valid @RequestBody
newUser: UserCreationDto,
): UserDto =
try {
userLifecycle.createUser(newUser.toDomain()).toDto()
} catch (e: UserEmailAlreadyExistsException) {
throw ResponseStatusException(HttpStatus.BAD_REQUEST, "A user with this email already exists")
}
@DeleteMapping("{id}")
@ResponseStatus(HttpStatus.NO_CONTENT)
@PreAuthorize("hasRole('$ROLE_ADMIN') and #principal.user.id != #id")
@Deprecated("User api/v2/users instead")
fun delete(
@PathVariable id: String,
@AuthenticationPrincipal principal: KomgaPrincipal,
) {
userRepository.findByIdOrNull(id)?.let {
userLifecycle.deleteUser(it)
} ?: throw ResponseStatusException(HttpStatus.NOT_FOUND)
}
@PatchMapping("{id}")
@ResponseStatus(HttpStatus.NO_CONTENT)
@PreAuthorize("hasRole('$ROLE_ADMIN') and #principal.user.id != #id")
@Deprecated("User api/v2/users instead")
fun updateUserRoles(
@PathVariable id: String,
@Valid @RequestBody
patch: RolesUpdateDto,
@AuthenticationPrincipal principal: KomgaPrincipal,
) {
userRepository.findByIdOrNull(id)?.let { user ->
val updatedUser = user.copy(
roleAdmin = patch.roles.contains(ROLE_ADMIN),
roleFileDownload = patch.roles.contains(ROLE_FILE_DOWNLOAD),
rolePageStreaming = patch.roles.contains(ROLE_PAGE_STREAMING),
)
userLifecycle.updateUser(updatedUser)
logger.info { "Updated user roles: $updatedUser" }
} ?: throw ResponseStatusException(HttpStatus.NOT_FOUND)
}
@PatchMapping("{id}/password")
@ResponseStatus(HttpStatus.NO_CONTENT)
@PreAuthorize("hasRole('$ROLE_ADMIN') or #principal.user.id == #id")
@Deprecated("User api/v2/users instead")
fun updatePassword(
@PathVariable id: String,
@AuthenticationPrincipal principal: KomgaPrincipal,
@Valid @RequestBody
newPasswordDto: PasswordUpdateDto,
) {
if (demo) throw ResponseStatusException(HttpStatus.FORBIDDEN)
userRepository.findByIdOrNull(id)?.let { user ->
userLifecycle.updatePassword(user, newPasswordDto.password, user.id != principal.user.id)
} ?: throw ResponseStatusException(HttpStatus.NOT_FOUND)
}
@PatchMapping("{id}/shared-libraries")
@ResponseStatus(HttpStatus.NO_CONTENT)
@PreAuthorize("hasRole('$ROLE_ADMIN')")
@Deprecated("User api/v2/users instead")
fun updateSharesLibraries(
@PathVariable id: String,
@Valid @RequestBody
sharedLibrariesUpdateDto: SharedLibrariesUpdateDto,
) {
userRepository.findByIdOrNull(id)?.let { user ->
val updatedUser = user.copy(
sharedAllLibraries = sharedLibrariesUpdateDto.all,
sharedLibrariesIds = if (sharedLibrariesUpdateDto.all) emptySet()
else libraryRepository.findAllByIds(sharedLibrariesUpdateDto.libraryIds)
.map { it.id }
.toSet(),
)
userLifecycle.updateUser(updatedUser)
logger.info { "Updated user shared libraries: $updatedUser" }
} ?: throw ResponseStatusException(HttpStatus.NOT_FOUND)
}
@GetMapping("me/authentication-activity")
@PageableAsQueryParam
@Deprecated("User api/v2/users instead")
fun getMyAuthenticationActivity(
@AuthenticationPrincipal principal: KomgaPrincipal,
@RequestParam(name = "unpaged", required = false) unpaged: Boolean = false,
@Parameter(hidden = true) page: Pageable,
): Page<AuthenticationActivityDto> {
if (demo && !principal.user.roleAdmin) throw ResponseStatusException(HttpStatus.FORBIDDEN)
val sort =
if (page.sort.isSorted) page.sort
else Sort.by(Sort.Order.desc("dateTime"))
val pageRequest =
if (unpaged) UnpagedSorted(sort)
else PageRequest.of(
page.pageNumber,
page.pageSize,
sort,
)
return authenticationActivityRepository.findAllByUser(principal.user, pageRequest).map { it.toDto() }
}
@GetMapping("authentication-activity")
@PageableAsQueryParam
@PreAuthorize("hasRole('$ROLE_ADMIN')")
@Deprecated("User api/v2/users instead")
fun getAuthenticationActivity(
@RequestParam(name = "unpaged", required = false) unpaged: Boolean = false,
@Parameter(hidden = true) page: Pageable,
): Page<AuthenticationActivityDto> {
val sort =
if (page.sort.isSorted) page.sort
else Sort.by(Sort.Order.desc("dateTime"))
val pageRequest =
if (unpaged) UnpagedSorted(sort)
else PageRequest.of(
page.pageNumber,
page.pageSize,
sort,
)
return authenticationActivityRepository.findAll(pageRequest).map { it.toDto() }
}
@GetMapping("{id}/authentication-activity/latest")
@PreAuthorize("hasRole('$ROLE_ADMIN') or #principal.user.id == #id")
@Deprecated("User api/v2/users instead")
fun getLatestAuthenticationActivityForUser(
@PathVariable id: String,
@AuthenticationPrincipal principal: KomgaPrincipal,
): AuthenticationActivityDto =
userRepository.findByIdOrNull(id)?.let { user ->
authenticationActivityRepository.findMostRecentByUser(user)?.toDto()
?: throw ResponseStatusException(HttpStatus.NOT_FOUND)
} ?: throw ResponseStatusException(HttpStatus.NOT_FOUND)
}

View File

@ -1,5 +1,3 @@
@file:Suppress("DEPRECATION")
package org.gotson.komga.interfaces.api.rest.dto
import jakarta.validation.constraints.Email
@ -12,25 +10,7 @@ import org.gotson.komga.domain.model.ROLE_FILE_DOWNLOAD
import org.gotson.komga.domain.model.ROLE_PAGE_STREAMING
import org.gotson.komga.infrastructure.security.KomgaPrincipal
@Deprecated("Deprecated since 0.153.0. Use UserDtoV2 instead")
data class UserDto(
val id: String,
val email: String,
val roles: List<String>,
)
@Deprecated("Deprecated since 0.153.0. Use toDtoV2() instead")
fun KomgaUser.toDto() =
UserDto(
id = id,
email = email,
roles = roles.toList(),
)
@Deprecated("Deprecated since 0.153.0. Use toDtoV2() instead")
fun KomgaPrincipal.toDto() = user.toDto()
data class UserDtoV2(
val id: String,
val email: String,
val roles: Set<String>,
@ -48,8 +28,8 @@ data class AgeRestrictionDto(
fun AgeRestriction.toDto() = AgeRestrictionDto(age, restriction)
fun KomgaUser.toDtoV2() =
UserDtoV2(
fun KomgaUser.toDto() =
UserDto(
id = id,
email = email,
roles = roles,
@ -60,31 +40,7 @@ fun KomgaUser.toDtoV2() =
ageRestriction = restrictions.ageRestriction?.toDto(),
)
fun KomgaPrincipal.toDtoV2() = user.toDtoV2()
@Deprecated("Deprecated since 0.153.0. Use UserDtoV2 instead")
data class UserWithSharedLibrariesDto(
val id: String,
val email: String,
val roles: List<String>,
val sharedAllLibraries: Boolean,
val sharedLibraries: List<SharedLibraryDto>,
)
@Deprecated("Deprecated since 0.153.0. Use UserDtoV2 instead")
data class SharedLibraryDto(
val id: String,
)
@Deprecated("Deprecated since 0.153.0. Use toDtoV2() instead")
fun KomgaUser.toWithSharedLibrariesDto() =
UserWithSharedLibrariesDto(
id = id,
email = email,
roles = roles.toList(),
sharedAllLibraries = sharedAllLibraries,
sharedLibraries = sharedLibrariesIds.map { SharedLibraryDto(it) },
)
fun KomgaPrincipal.toDto() = user.toDto()
data class UserCreationDto(
@get:Email(regexp = ".+@.+\\..+") val email: String,
@ -104,8 +60,3 @@ data class UserCreationDto(
data class PasswordUpdateDto(
@get:NotBlank val password: String,
)
@Deprecated("Deprecated since 0.153.0")
data class RolesUpdateDto(
val roles: List<String>,
)