diff --git a/komga-webui/src/components/dialogs/LibraryEditDialog.vue b/komga-webui/src/components/dialogs/LibraryEditDialog.vue
index d9492279eda..158eb5de931 100644
--- a/komga-webui/src/components/dialogs/LibraryEditDialog.vue
+++ b/komga-webui/src/components/dialogs/LibraryEditDialog.vue
@@ -215,6 +215,22 @@
+
+
+
+
+ mdi-alert-circle-outline
+
+ {{ $t('dialog.edit_library.tooltip_use_resources') }}
+
+
+
+
@@ -150,6 +151,11 @@ class TaskHandler(
bookLifecycle.hashAndPersist(book)
} ?: logger.warn { "Cannot execute task $task: Book does not exist" }
+ is Task.HashBookKoreader ->
+ bookRepository.findByIdOrNull(task.bookId)?.let { book ->
+ bookLifecycle.hashKoreaderAndPersist(book)
+ } ?: logger.warn { "Cannot execute task $task: Book does not exist" }
+
is Task.HashBookPages ->
bookRepository.findByIdOrNull(task.bookId)?.let { book ->
bookLifecycle.hashPagesAndPersist(book)
diff --git a/komga/src/main/kotlin/org/gotson/komga/domain/model/Book.kt b/komga/src/main/kotlin/org/gotson/komga/domain/model/Book.kt
index d74f04c2290..b27ff81f8c1 100644
--- a/komga/src/main/kotlin/org/gotson/komga/domain/model/Book.kt
+++ b/komga/src/main/kotlin/org/gotson/komga/domain/model/Book.kt
@@ -12,6 +12,7 @@ data class Book(
val fileLastModified: LocalDateTime,
val fileSize: Long = 0,
val fileHash: String = "",
+ val fileHashKoreader: String = "",
val number: Int = 0,
val id: String = TsidCreator.getTsid256().toString(),
val seriesId: String = "",
diff --git a/komga/src/main/kotlin/org/gotson/komga/domain/model/Library.kt b/komga/src/main/kotlin/org/gotson/komga/domain/model/Library.kt
index a274badeb23..8ed2fd71499 100644
--- a/komga/src/main/kotlin/org/gotson/komga/domain/model/Library.kt
+++ b/komga/src/main/kotlin/org/gotson/komga/domain/model/Library.kt
@@ -32,6 +32,7 @@ data class Library(
val seriesCover: SeriesCover = SeriesCover.FIRST,
val hashFiles: Boolean = true,
val hashPages: Boolean = false,
+ val hashKoreader: Boolean = false,
val analyzeDimensions: Boolean = true,
val oneshotsDirectory: String? = null,
val unavailableDate: LocalDateTime? = null,
diff --git a/komga/src/main/kotlin/org/gotson/komga/domain/persistence/BookRepository.kt b/komga/src/main/kotlin/org/gotson/komga/domain/persistence/BookRepository.kt
index 7045b66f8ca..fa73e80ce2a 100644
--- a/komga/src/main/kotlin/org/gotson/komga/domain/persistence/BookRepository.kt
+++ b/komga/src/main/kotlin/org/gotson/komga/domain/persistence/BookRepository.kt
@@ -37,6 +37,10 @@ interface BookRepository {
fun findAllByLibraryIdAndWithEmptyHash(libraryId: String): Collection
+ fun findAllByLibraryIdAndWithEmptyHashKoreader(libraryId: String): Collection
+
+ fun findAllByHashKoreader(hashKoreader: String): Collection
+
fun findAllByLibraryIdAndMediaTypes(
libraryId: String,
mediaTypes: Collection,
diff --git a/komga/src/main/kotlin/org/gotson/komga/domain/service/BookLifecycle.kt b/komga/src/main/kotlin/org/gotson/komga/domain/service/BookLifecycle.kt
index 052d40bf840..5d7fc605d43 100644
--- a/komga/src/main/kotlin/org/gotson/komga/domain/service/BookLifecycle.kt
+++ b/komga/src/main/kotlin/org/gotson/komga/domain/service/BookLifecycle.kt
@@ -31,6 +31,7 @@ import org.gotson.komga.domain.persistence.ReadProgressRepository
import org.gotson.komga.domain.persistence.ThumbnailBookRepository
import org.gotson.komga.infrastructure.configuration.KomgaSettingsProvider
import org.gotson.komga.infrastructure.hash.Hasher
+import org.gotson.komga.infrastructure.hash.KoreaderHasher
import org.gotson.komga.infrastructure.image.ImageConverter
import org.gotson.komga.infrastructure.image.ImageType
import org.gotson.komga.language.toCurrentTimeZone
@@ -66,6 +67,7 @@ class BookLifecycle(
private val eventPublisher: ApplicationEventPublisher,
private val transactionTemplate: TransactionTemplate,
private val hasher: Hasher,
+ private val hasherKoreader: KoreaderHasher,
private val historicalEventRepository: HistoricalEventRepository,
private val komgaSettingsProvider: KomgaSettingsProvider,
@Qualifier("pdfImageType")
@@ -113,6 +115,19 @@ class BookLifecycle(
}
}
+ fun hashKoreaderAndPersist(book: Book) {
+ if (!libraryRepository.findById(book.libraryId).hashKoreader)
+ return logger.info { "File hashing for Koreader is disabled for the library, it may have changed since the task was submitted, skipping" }
+
+ logger.info { "Hash Koreader and persist book: $book" }
+ if (book.fileHashKoreader.isBlank()) {
+ val hash = hasherKoreader.computeHash(book.path)
+ bookRepository.update(book.copy(fileHashKoreader = hash))
+ } else {
+ logger.info { "Book already has a Koreader hash, skipping" }
+ }
+ }
+
fun hashPagesAndPersist(book: Book) {
if (!libraryRepository.findById(book.libraryId).hashPages)
return logger.info { "Page hashing is disabled for the library, it may have changed since the task was submitted, skipping" }
diff --git a/komga/src/main/kotlin/org/gotson/komga/infrastructure/hash/KoreaderHasher.kt b/komga/src/main/kotlin/org/gotson/komga/infrastructure/hash/KoreaderHasher.kt
new file mode 100644
index 00000000000..158678ce096
--- /dev/null
+++ b/komga/src/main/kotlin/org/gotson/komga/infrastructure/hash/KoreaderHasher.kt
@@ -0,0 +1,39 @@
+package org.gotson.komga.infrastructure.hash
+
+import com.appmattus.crypto.Algorithm
+import io.github.oshai.kotlinlogging.KotlinLogging
+import org.springframework.stereotype.Component
+import java.io.RandomAccessFile
+import java.nio.file.Path
+
+private val logger = KotlinLogging.logger {}
+
+@Component
+class KoreaderHasher {
+ fun computeHash(path: Path): String {
+ logger.debug { "Koreader hashing: $path" }
+
+ return partialMd5(path)
+ }
+
+ /**
+ * From https://github.com/koreader/koreader/blob/5bd3f3b42c95fd143d98f8fc9695d486fd92b7c8/frontend/util.lua#L1093-L1119
+ */
+ @OptIn(ExperimentalStdlibApi::class)
+ private fun partialMd5(path: Path): String {
+ val step = 1024L
+ val size = 1024
+ val digest = Algorithm.MD5.createDigest()
+
+ val file = RandomAccessFile(path.toFile(), "r")
+
+ val buffer = ByteArray(size)
+ (-1..10).forEach {
+ file.seek(step shl (2 * it))
+ val s = file.read(buffer)
+ if (s > 0) digest.update(buffer)
+ }
+
+ return digest.digest().toHexString()
+ }
+}
diff --git a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/main/BookDao.kt b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/main/BookDao.kt
index 488c63dc946..9b0ce070a35 100644
--- a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/main/BookDao.kt
+++ b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/main/BookDao.kt
@@ -300,6 +300,21 @@ class BookDao(
.fetchInto(b)
.map { it.toDomain() }
+ override fun findAllByLibraryIdAndWithEmptyHashKoreader(libraryId: String): Collection =
+ dsl
+ .selectFrom(b)
+ .where(b.LIBRARY_ID.eq(libraryId))
+ .and(b.FILE_HASH_KOREADER.eq(""))
+ .fetchInto(b)
+ .map { it.toDomain() }
+
+ override fun findAllByHashKoreader(hashKoreader: String): Collection =
+ dsl
+ .selectFrom(b)
+ .where(b.FILE_HASH_KOREADER.eq(hashKoreader))
+ .fetchInto(b)
+ .map { it.toDomain() }
+
@Transactional
override fun insert(book: Book) {
insert(listOf(book))
@@ -321,11 +336,12 @@ class BookDao(
b.FILE_LAST_MODIFIED,
b.FILE_SIZE,
b.FILE_HASH,
+ b.FILE_HASH_KOREADER,
b.LIBRARY_ID,
b.SERIES_ID,
b.DELETED_DATE,
b.ONESHOT,
- ).values(null as String?, null, null, null, null, null, null, null, null, null, null),
+ ).values(null as String?, null, null, null, null, null, null, null, null, null, null, null),
).also { step ->
chunk.forEach {
step.bind(
@@ -336,6 +352,7 @@ class BookDao(
it.fileLastModified,
it.fileSize,
it.fileHash,
+ it.fileHashKoreader,
it.libraryId,
it.seriesId,
it.deletedDate,
@@ -366,6 +383,7 @@ class BookDao(
.set(b.FILE_LAST_MODIFIED, book.fileLastModified)
.set(b.FILE_SIZE, book.fileSize)
.set(b.FILE_HASH, book.fileHash)
+ .set(b.FILE_HASH_KOREADER, book.fileHashKoreader)
.set(b.LIBRARY_ID, book.libraryId)
.set(b.SERIES_ID, book.seriesId)
.set(b.DELETED_DATE, book.deletedDate)
@@ -413,6 +431,7 @@ class BookDao(
fileLastModified = fileLastModified,
fileSize = fileSize,
fileHash = fileHash,
+ fileHashKoreader = fileHashKoreader,
id = id,
libraryId = libraryId,
seriesId = seriesId,
diff --git a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/main/LibraryDao.kt b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/main/LibraryDao.kt
index 98552c181e7..e8318327646 100644
--- a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/main/LibraryDao.kt
+++ b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/main/LibraryDao.kt
@@ -102,6 +102,7 @@ class LibraryDao(
.set(l.SERIES_COVER, library.seriesCover.toString())
.set(l.HASH_FILES, library.hashFiles)
.set(l.HASH_PAGES, library.hashPages)
+ .set(l.HASH_KOREADER, library.hashKoreader)
.set(l.ANALYZE_DIMENSIONS, library.analyzeDimensions)
.set(l.ONESHOTS_DIRECTORY, library.oneshotsDirectory)
.set(l.UNAVAILABLE_DATE, library.unavailableDate)
@@ -138,6 +139,7 @@ class LibraryDao(
.set(l.SERIES_COVER, library.seriesCover.toString())
.set(l.HASH_FILES, library.hashFiles)
.set(l.HASH_PAGES, library.hashPages)
+ .set(l.HASH_KOREADER, library.hashKoreader)
.set(l.ANALYZE_DIMENSIONS, library.analyzeDimensions)
.set(l.ONESHOTS_DIRECTORY, library.oneshotsDirectory)
.set(l.UNAVAILABLE_DATE, library.unavailableDate)
@@ -200,6 +202,7 @@ class LibraryDao(
seriesCover = Library.SeriesCover.valueOf(seriesCover),
hashFiles = hashFiles,
hashPages = hashPages,
+ hashKoreader = hashKoreader,
analyzeDimensions = analyzeDimensions,
oneshotsDirectory = oneshotsDirectory,
unavailableDate = unavailableDate,
diff --git a/komga/src/main/kotlin/org/gotson/komga/infrastructure/security/SecurityConfiguration.kt b/komga/src/main/kotlin/org/gotson/komga/infrastructure/security/SecurityConfiguration.kt
index 23ed469080e..fb11c1da5a4 100644
--- a/komga/src/main/kotlin/org/gotson/komga/infrastructure/security/SecurityConfiguration.kt
+++ b/komga/src/main/kotlin/org/gotson/komga/infrastructure/security/SecurityConfiguration.kt
@@ -92,6 +92,8 @@ class SecurityConfiguration(
"/api/v1/books/{bookId}/resource/**",
// OPDS authentication document
"/opds/v2/auth",
+ // KOReader user creation
+ "/koreader/users/create",
).permitAll()
// all other endpoints are restricted to authenticated users
@@ -210,7 +212,7 @@ class SecurityConfiguration(
httpBasic { disable() }
logout { disable() }
- securityMatcher("/kosync/**")
+ securityMatcher("/koreader/**")
authorizeHttpRequests {
authorize(anyRequest, hasRole(UserRoles.KOREADER_SYNC.name))
}
diff --git a/komga/src/main/kotlin/org/gotson/komga/interfaces/api/kosync/KoreaderSyncController.kt b/komga/src/main/kotlin/org/gotson/komga/interfaces/api/kosync/KoreaderSyncController.kt
index 2228b8cd6f9..0dc2a8e11da 100644
--- a/komga/src/main/kotlin/org/gotson/komga/interfaces/api/kosync/KoreaderSyncController.kt
+++ b/komga/src/main/kotlin/org/gotson/komga/interfaces/api/kosync/KoreaderSyncController.kt
@@ -1,10 +1,20 @@
package org.gotson.komga.interfaces.api.kosync
-import com.fasterxml.jackson.databind.JsonNode
import io.github.oshai.kotlinlogging.KotlinLogging
+import org.gotson.komga.domain.model.MediaProfile
+import org.gotson.komga.domain.model.R2Device
+import org.gotson.komga.domain.model.R2Locator
+import org.gotson.komga.domain.model.R2Progression
+import org.gotson.komga.domain.persistence.BookRepository
+import org.gotson.komga.domain.persistence.MediaRepository
+import org.gotson.komga.domain.persistence.ReadProgressRepository
+import org.gotson.komga.domain.service.BookLifecycle
+import org.gotson.komga.infrastructure.security.KomgaPrincipal
+import org.gotson.komga.interfaces.api.kosync.dto.DocumentProgressDto
import org.gotson.komga.interfaces.api.kosync.dto.UserAuthenticationDto
import org.springframework.http.HttpStatus
-import org.springframework.http.MediaType
+import org.springframework.http.ResponseEntity
+import org.springframework.security.core.annotation.AuthenticationPrincipal
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping
@@ -13,31 +23,97 @@ import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.server.ResponseStatusException
+import java.time.ZonedDateTime
private val logger = KotlinLogging.logger {}
@RestController
-@RequestMapping("/kosync", produces = [MediaType.APPLICATION_JSON_VALUE])
-class KoreaderSyncController {
+@RequestMapping("/koreader", produces = ["application/vnd.koreader.v1+json"])
+class KoreaderSyncController(
+ private val bookRepository: BookRepository,
+ private val mediaRepository: MediaRepository,
+ private val readProgressRepository: ReadProgressRepository,
+ private val bookLifecycle: BookLifecycle,
+) {
@PostMapping("users/create")
- fun registerUser(): Unit = throw ResponseStatusException(HttpStatus.FORBIDDEN)
+ fun registerUser(): ResponseEntity = throw ResponseStatusException(HttpStatus.FORBIDDEN, "User creation is disabled")
@GetMapping("users/auth")
fun authorize() = UserAuthenticationDto()
@GetMapping("syncs/progress/{bookHash}")
fun getProgress(
+ @AuthenticationPrincipal principal: KomgaPrincipal,
@PathVariable bookHash: String,
- ) {
- logger.debug { "Received progress request for hash: $bookHash" }
- throw ResponseStatusException(HttpStatus.NOT_FOUND)
+ ): DocumentProgressDto {
+ val books = bookRepository.findAllByHashKoreader(bookHash)
+ if (books.isEmpty()) throw ResponseStatusException(HttpStatus.NOT_FOUND, "Book not found")
+ if (books.size > 1) throw ResponseStatusException(HttpStatus.CONFLICT, "More than 1 book found with the same hash")
+
+ val book = books.first()
+
+ val progress =
+ readProgressRepository.findByBookIdAndUserIdOrNull(book.id, principal.user.id)
+ ?: throw ResponseStatusException(HttpStatus.OK, "No progress found for this book")
+
+ val progressPercentage =
+ progress
+ .locator
+ ?.locations
+ ?.totalProgression
+ ?: (progress.page.toFloat() / mediaRepository.findById(book.id).pageCount.toFloat())
+
+ return DocumentProgressDto(
+ document = bookHash,
+ percentage = progressPercentage,
+ // TODO: handle EPUB
+ progress = progress.page.toString(),
+ device = progress.deviceName,
+ deviceId = progress.deviceId,
+ )
}
@PutMapping("syncs/progress")
fun updateProgress(
- @RequestBody body: JsonNode,
+ @AuthenticationPrincipal principal: KomgaPrincipal,
+ @RequestBody koreaderProgress: DocumentProgressDto,
) {
- logger.debug { "Received progress update: $body" }
- throw ResponseStatusException(HttpStatus.NOT_FOUND)
+ logger.debug { "Received progress update: $koreaderProgress" }
+ val books = bookRepository.findAllByHashKoreader(koreaderProgress.document)
+ if (books.isEmpty()) throw ResponseStatusException(HttpStatus.NOT_FOUND, "Book not found")
+ if (books.size > 1) throw ResponseStatusException(HttpStatus.CONFLICT, "More than 1 book found with the same hash")
+
+ val book = books.first()
+ val media = mediaRepository.findById(book.id)
+
+ // convert the Kobo update request to an R2Progression
+ val locator =
+ when (media.profile) {
+ MediaProfile.DIVINA, MediaProfile.PDF ->
+ R2Locator(
+ href = "",
+ type = "",
+ locations =
+ R2Locator.Location(
+ position = koreaderProgress.progress.toInt(),
+ totalProgression = koreaderProgress.percentage,
+ ),
+ )
+ MediaProfile.EPUB -> TODO()
+ null -> TODO()
+ }
+
+ val r2Progression =
+ R2Progression(
+ device =
+ R2Device(
+ id = koreaderProgress.deviceId,
+ name = koreaderProgress.device,
+ ),
+ modified = ZonedDateTime.now(),
+ locator = locator,
+ )
+
+ bookLifecycle.markProgression(book, principal.user, r2Progression)
}
}
diff --git a/komga/src/main/kotlin/org/gotson/komga/interfaces/api/kosync/dto/DocumentProgressDto.kt b/komga/src/main/kotlin/org/gotson/komga/interfaces/api/kosync/dto/DocumentProgressDto.kt
new file mode 100644
index 00000000000..44961b6b4aa
--- /dev/null
+++ b/komga/src/main/kotlin/org/gotson/komga/interfaces/api/kosync/dto/DocumentProgressDto.kt
@@ -0,0 +1,27 @@
+package org.gotson.komga.interfaces.api.kosync.dto
+
+import com.fasterxml.jackson.databind.PropertyNamingStrategies
+import com.fasterxml.jackson.databind.annotation.JsonNaming
+
+@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy::class)
+data class DocumentProgressDto(
+ /**
+ * The document hash, computed using the KOReader partial MD5 algorithm.
+ */
+ val document: String,
+ /**
+ * Total progress percentage in the document, between 0 and 1.
+ */
+ val percentage: Float,
+ /**
+ * Current progress.
+ *
+ * For PDF and CBZ, contains the current page number (starting from 1).
+ *
+ * For EPUB, contains a position in the form '/body/DocFragment[10]/body/div/p[1]/text().0'
+ * 'DocFragment[10]' correspond to the index of the resource in the manifest
+ */
+ val progress: String,
+ val device: String,
+ val deviceId: String,
+)
diff --git a/komga/src/main/kotlin/org/gotson/komga/interfaces/api/rest/LibraryController.kt b/komga/src/main/kotlin/org/gotson/komga/interfaces/api/rest/LibraryController.kt
index c7451d35480..1b789d7e88c 100644
--- a/komga/src/main/kotlin/org/gotson/komga/interfaces/api/rest/LibraryController.kt
+++ b/komga/src/main/kotlin/org/gotson/komga/interfaces/api/rest/LibraryController.kt
@@ -107,6 +107,7 @@ class LibraryController(
seriesCover = library.seriesCover.toDomain(),
hashFiles = library.hashFiles,
hashPages = library.hashPages,
+ hashKoreader = library.hashKoreader,
analyzeDimensions = library.analyzeDimensions,
oneshotsDirectory = library.oneshotsDirectory?.ifBlank { null },
),
@@ -176,6 +177,7 @@ class LibraryController(
seriesCover = seriesCover?.toDomain() ?: existing.seriesCover,
hashFiles = hashFiles ?: existing.hashFiles,
hashPages = hashPages ?: existing.hashPages,
+ hashKoreader = hashKoreader ?: existing.hashKoreader,
analyzeDimensions = analyzeDimensions ?: existing.analyzeDimensions,
oneshotsDirectory = if (isSet("oneshotsDirectory")) oneshotsDirectory?.ifBlank { null } else existing.oneshotsDirectory,
)
diff --git a/komga/src/main/kotlin/org/gotson/komga/interfaces/api/rest/dto/LibraryCreationDto.kt b/komga/src/main/kotlin/org/gotson/komga/interfaces/api/rest/dto/LibraryCreationDto.kt
index 3b8a3f08fb9..9309195c44f 100644
--- a/komga/src/main/kotlin/org/gotson/komga/interfaces/api/rest/dto/LibraryCreationDto.kt
+++ b/komga/src/main/kotlin/org/gotson/komga/interfaces/api/rest/dto/LibraryCreationDto.kt
@@ -28,6 +28,7 @@ data class LibraryCreationDto(
val seriesCover: SeriesCoverDto = SeriesCoverDto.FIRST,
val hashFiles: Boolean = true,
val hashPages: Boolean = false,
+ val hashKoreader: Boolean = false,
val analyzeDimensions: Boolean = true,
val oneshotsDirectory: String? = null,
)
diff --git a/komga/src/main/kotlin/org/gotson/komga/interfaces/api/rest/dto/LibraryDto.kt b/komga/src/main/kotlin/org/gotson/komga/interfaces/api/rest/dto/LibraryDto.kt
index fee0b6b0fc3..9f45e831df9 100644
--- a/komga/src/main/kotlin/org/gotson/komga/interfaces/api/rest/dto/LibraryDto.kt
+++ b/komga/src/main/kotlin/org/gotson/komga/interfaces/api/rest/dto/LibraryDto.kt
@@ -30,6 +30,7 @@ data class LibraryDto(
val seriesCover: SeriesCoverDto,
val hashFiles: Boolean,
val hashPages: Boolean,
+ val hashKoreader: Boolean,
val analyzeDimensions: Boolean,
val oneshotsDirectory: String?,
val unavailable: Boolean,
@@ -63,6 +64,7 @@ fun Library.toDto(includeRoot: Boolean) =
seriesCover = seriesCover.toDto(),
hashFiles = hashFiles,
hashPages = hashPages,
+ hashKoreader = hashKoreader,
analyzeDimensions = analyzeDimensions,
oneshotsDirectory = oneshotsDirectory,
unavailable = unavailableDate != null,
diff --git a/komga/src/main/kotlin/org/gotson/komga/interfaces/api/rest/dto/LibraryUpdateDto.kt b/komga/src/main/kotlin/org/gotson/komga/interfaces/api/rest/dto/LibraryUpdateDto.kt
index f46481ce156..54e4731e144 100644
--- a/komga/src/main/kotlin/org/gotson/komga/interfaces/api/rest/dto/LibraryUpdateDto.kt
+++ b/komga/src/main/kotlin/org/gotson/komga/interfaces/api/rest/dto/LibraryUpdateDto.kt
@@ -42,6 +42,7 @@ class LibraryUpdateDto {
val seriesCover: SeriesCoverDto? = null
val hashFiles: Boolean? = null
val hashPages: Boolean? = null
+ val hashKoreader: Boolean? = null
val analyzeDimensions: Boolean? = null
var oneshotsDirectory: String?
by Delegates.observable(null) { prop, _, _ ->
diff --git a/komga/src/test/kotlin/org/gotson/komga/interfaces/api/kosync/KoreaderSyncControllerTest.kt b/komga/src/test/kotlin/org/gotson/komga/interfaces/api/kosync/KoreaderSyncControllerTest.kt
index 2b68cda57f9..a1e8521f0c0 100644
--- a/komga/src/test/kotlin/org/gotson/komga/interfaces/api/kosync/KoreaderSyncControllerTest.kt
+++ b/komga/src/test/kotlin/org/gotson/komga/interfaces/api/kosync/KoreaderSyncControllerTest.kt
@@ -43,7 +43,7 @@ class KoreaderSyncControllerTest(
@Test
fun `when creating user then forbidden is thrown`() {
mockMvc
- .post("/kosync/users/create")
+ .post("/koreader/users/create")
.andExpect {
status { isForbidden() }
}
@@ -52,7 +52,7 @@ class KoreaderSyncControllerTest(
@Test
fun `given missing X-Auth-User header when authenticating user then forbidden is thrown`() {
mockMvc
- .get("/kosync/users/auth")
+ .get("/koreader/users/auth")
.andExpect {
status { isForbidden() }
}
@@ -61,7 +61,7 @@ class KoreaderSyncControllerTest(
@Test
fun `given api key in X-Auth-User header when authenticating user then returns OK`() {
mockMvc
- .get("/kosync/users/auth") {
+ .get("/koreader/users/auth") {
header("x-auth-user", apiKey)
}.andExpect {
status { isOk() }