feat(api): http eTag caching for all API calls

this will reduce the network load
This commit is contained in:
Gauthier Roebroeck 2020-08-14 11:39:00 +08:00
parent b30849698d
commit fe22cb5ce6
5 changed files with 29 additions and 21 deletions

View file

@ -55,9 +55,11 @@ class SecurityConfiguration(
// authorize frames for H2 console
.and()
.headers().frameOptions().sameOrigin()
.headers {
it.frameOptions().sameOrigin()
it.cacheControl().disable() //headers are set in WebMvcConfiguration
}
.and()
.httpBasic()
.and()

View file

@ -13,7 +13,10 @@ fun filePathToUrl(filePath: String): URL =
Paths.get(filePath).toUri().toURL()
fun ResponseEntity.BodyBuilder.setCachePrivate() =
this.cacheControl(CacheControl.maxAge(0, TimeUnit.SECONDS)
.cachePrivate()
.mustRevalidate()
)
this.cacheControl(cachePrivate)
val cachePrivate = CacheControl
.maxAge(0, TimeUnit.SECONDS)
.noTransform()
.cachePrivate()
.mustRevalidate()

View file

@ -6,13 +6,15 @@ import org.springframework.stereotype.Component
import org.springframework.web.bind.annotation.ControllerAdvice
import org.springframework.web.bind.annotation.ExceptionHandler
import org.springframework.web.servlet.NoHandlerFoundException
import org.springframework.web.servlet.config.annotation.InterceptorRegistry
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer
import org.springframework.web.servlet.mvc.WebContentInterceptor
import java.util.concurrent.TimeUnit
@Configuration
class StaticResourceConfiguration : WebMvcConfigurer {
class WebMvcConfiguration : WebMvcConfigurer {
override fun addResourceHandlers(registry: ResourceHandlerRegistry) {
if (!registry.hasMappingForPattern("/webjars/**")) {
registry.addResourceHandler("/webjars/**")
@ -51,6 +53,17 @@ class StaticResourceConfiguration : WebMvcConfigurer {
)
.setCacheControl(CacheControl.maxAge(365, TimeUnit.DAYS).cachePublic())
}
override fun addInterceptors(registry: InterceptorRegistry) {
registry.addInterceptor(
WebContentInterceptor().apply {
addCacheMapping(
cachePrivate,
"/api/**", "/opds/**"
)
}
)
}
}
@Component

View file

@ -191,16 +191,12 @@ class BookController(
fun getBookThumbnail(
@AuthenticationPrincipal principal: KomgaPrincipal,
@PathVariable bookId: String
): ResponseEntity<ByteArray> {
): ByteArray {
bookRepository.getLibraryId(bookId)?.let {
if (!principal.user.canAccessLibrary(it)) throw ResponseStatusException(HttpStatus.FORBIDDEN)
} ?: throw ResponseStatusException(HttpStatus.NOT_FOUND)
return bookLifecycle.getThumbnailBytes(bookId)?.let {
ResponseEntity.ok()
.setCachePrivate()
.body(it)
} ?: throw ResponseStatusException(HttpStatus.NOT_FOUND)
return bookLifecycle.getThumbnailBytes(bookId) ?: throw ResponseStatusException(HttpStatus.NOT_FOUND)
}
@Operation(description = "Download the book file.")

View file

@ -23,7 +23,6 @@ import org.gotson.komga.infrastructure.jooq.UnpagedSorted
import org.gotson.komga.infrastructure.security.KomgaPrincipal
import org.gotson.komga.infrastructure.swagger.PageableAsQueryParam
import org.gotson.komga.infrastructure.swagger.PageableWithoutSortAsQueryParam
import org.gotson.komga.infrastructure.web.setCachePrivate
import org.gotson.komga.interfaces.rest.dto.BookDto
import org.gotson.komga.interfaces.rest.dto.CollectionDto
import org.gotson.komga.interfaces.rest.dto.SeriesDto
@ -38,7 +37,6 @@ 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.http.ResponseEntity
import org.springframework.security.access.prepost.PreAuthorize
import org.springframework.security.core.annotation.AuthenticationPrincipal
import org.springframework.web.bind.annotation.DeleteMapping
@ -196,16 +194,12 @@ class SeriesController(
fun getSeriesThumbnail(
@AuthenticationPrincipal principal: KomgaPrincipal,
@PathVariable(name = "seriesId") seriesId: String
): ResponseEntity<ByteArray> {
): ByteArray {
seriesRepository.getLibraryId(seriesId)?.let {
if (!principal.user.canAccessLibrary(it)) throw ResponseStatusException(HttpStatus.FORBIDDEN)
} ?: throw ResponseStatusException(HttpStatus.NOT_FOUND)
return seriesLifecycle.getThumbnailBytes(seriesId)?.let {
ResponseEntity.ok()
.setCachePrivate()
.body(it)
} ?: throw ResponseStatusException(HttpStatus.NOT_FOUND)
return seriesLifecycle.getThumbnailBytes(seriesId) ?: throw ResponseStatusException(HttpStatus.NOT_FOUND)
}
@PageableAsQueryParam