diff --git a/komga/src/main/kotlin/org/gotson/komga/interfaces/api/rest/ReleaseController.kt b/komga/src/main/kotlin/org/gotson/komga/interfaces/api/rest/ReleaseController.kt new file mode 100644 index 000000000..dfa606bf8 --- /dev/null +++ b/komga/src/main/kotlin/org/gotson/komga/interfaces/api/rest/ReleaseController.kt @@ -0,0 +1,68 @@ +package org.gotson.komga.interfaces.api.rest + +import com.github.benmanes.caffeine.cache.Caffeine +import org.gotson.komga.domain.model.ROLE_ADMIN +import org.gotson.komga.infrastructure.security.KomgaPrincipal +import org.gotson.komga.interfaces.api.rest.dto.GithubReleaseDto +import org.gotson.komga.interfaces.api.rest.dto.ReleaseDto +import org.springframework.core.ParameterizedTypeReference +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.GetMapping +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RestController +import org.springframework.web.reactive.function.client.WebClient +import org.springframework.web.server.ResponseStatusException +import java.util.concurrent.TimeUnit + +private const val GITHUB_API = "https://api.github.com/repos/gotson/komga/releases" + +@RestController +@PreAuthorize("hasRole('$ROLE_ADMIN')") +@RequestMapping("api/v1/releases", produces = [MediaType.APPLICATION_JSON_VALUE]) +class ReleaseController( + webClientBuilder: WebClient.Builder, +) { + private val webClient = webClientBuilder.baseUrl(GITHUB_API).build() + + private val cache = + Caffeine + .newBuilder() + .expireAfterAccess(1, TimeUnit.DAYS) + .build>() + + @GetMapping + @PreAuthorize("hasRole('$ROLE_ADMIN')") + fun getAnnouncements( + @AuthenticationPrincipal principal: KomgaPrincipal, + ): List = + cache + .get("releases") { fetchGitHubReleases() } + ?.let { releases -> + releases.mapIndexed { index, ghRel -> + ReleaseDto( + ghRel.tagName, + ghRel.publishedAt, + ghRel.htmlUrl, + index == 0, + ghRel.prerelease, + ghRel.body, + ) + } + } + ?: throw ResponseStatusException(HttpStatus.NOT_FOUND) + + fun fetchGitHubReleases(): List? { + val response = + webClient + .get() + .uri { + it.queryParam("per_page", 20).build() + }.retrieve() + .toEntity(object : ParameterizedTypeReference>() {}) + .block() + return response?.body + } +} diff --git a/komga/src/main/kotlin/org/gotson/komga/interfaces/api/rest/dto/GithubReleaseDto.kt b/komga/src/main/kotlin/org/gotson/komga/interfaces/api/rest/dto/GithubReleaseDto.kt new file mode 100644 index 000000000..e914f45e4 --- /dev/null +++ b/komga/src/main/kotlin/org/gotson/komga/interfaces/api/rest/dto/GithubReleaseDto.kt @@ -0,0 +1,16 @@ +package org.gotson.komga.interfaces.api.rest.dto + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties +import com.fasterxml.jackson.databind.PropertyNamingStrategies +import com.fasterxml.jackson.databind.annotation.JsonNaming +import java.time.ZonedDateTime + +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy::class) +data class GithubReleaseDto( + val htmlUrl: String, + val tagName: String, + val publishedAt: ZonedDateTime, + val body: String, + val prerelease: Boolean, +) diff --git a/komga/src/main/kotlin/org/gotson/komga/interfaces/api/rest/dto/ReleaseDto.kt b/komga/src/main/kotlin/org/gotson/komga/interfaces/api/rest/dto/ReleaseDto.kt new file mode 100644 index 000000000..5865cc252 --- /dev/null +++ b/komga/src/main/kotlin/org/gotson/komga/interfaces/api/rest/dto/ReleaseDto.kt @@ -0,0 +1,12 @@ +package org.gotson.komga.interfaces.api.rest.dto + +import java.time.ZonedDateTime + +data class ReleaseDto( + val version: String, + val releaseDate: ZonedDateTime, + val url: String, + val latest: Boolean, + val preRelease: Boolean, + val description: String, +) diff --git a/komga/src/main/resources/application.yml b/komga/src/main/resources/application.yml index 2d2847cc0..6b3cf1fb9 100644 --- a/komga/src/main/resources/application.yml +++ b/komga/src/main/resources/application.yml @@ -24,6 +24,8 @@ komga: file: \${komga.config-dir}/tasks.sqlite spring: + codec: + max-in-memory-size: 10MB flyway: enabled: true locations: classpath:db/migration/{vendor}