fix: prevent transient scanning of directories that are part of existing libraries

This commit is contained in:
Gauthier Roebroeck 2021-04-20 16:00:36 +08:00
parent e83eded07b
commit 8a92b84fd0
6 changed files with 46 additions and 13 deletions

View file

@ -20,3 +20,4 @@ ERR_1013 | No unique match for book number within series
ERR_1014 | No match for book number within series
ERR_1015 | Error while deserializing ComicRack ReadingList
ERR_1016 | Directory not accessible or not a directory
ERR_1017 | Cannot scan folder that is part of an existing library

View file

@ -434,7 +434,9 @@
"ERR_1012": "No match for series",
"ERR_1013": "No unique match for book number within series",
"ERR_1014": "No match for book number within series",
"ERR_1015": "Error while deserializing ComicRack ReadingList"
"ERR_1015": "Error while deserializing ComicRack ReadingList",
"ERR_1016": "Directory not accessible or not a directory",
"ERR_1017": "Cannot scan folder that is part of an existing library"
},
"filter": {
"age_rating": "age rating",

View file

@ -6,25 +6,22 @@ const API_TRANSIENT_BOOKS = '/api/v1/transient-books'
export default class KomgaTransientBooksService {
private http: AxiosInstance
constructor (http: AxiosInstance) {
constructor(http: AxiosInstance) {
this.http = http
}
async scanForTransientBooks (path: string): Promise<TransientBookDto[]> {
async scanForTransientBooks(path: string): Promise<TransientBookDto[]> {
try {
return (await this.http.post(API_TRANSIENT_BOOKS, {
path: path,
})).data
} catch (e) {
let msg = `An error occurred while trying to scan for transient book`
if (e.response.data.message) {
msg += `: ${e.response.data.message}`
}
throw new Error(msg)
if (e.response.data.message) throw new Error(e.response.data.message)
throw new Error('An error occurred while trying to scan for transient book')
}
}
async analyze (id: string): Promise<TransientBookDto> {
async analyze(id: string): Promise<TransientBookDto> {
try {
return (await this.http.post(`${API_TRANSIENT_BOOKS}/${id}/analyze`)).data
} catch (e) {

View file

@ -84,6 +84,19 @@
</v-row>
</template>
<v-snackbar
v-model="snackbar"
bottom
color="error"
>
{{ snackText }}
<v-btn
text
@click="snackbar = false"
>{{ $t('common.close') }}
</v-btn>
</v-snackbar>
</v-container>
</template>
@ -96,6 +109,7 @@ import SeriesPickerDialog from "@/components/dialogs/SeriesPickerDialog.vue";
import {SeriesDto} from "@/types/komga-series";
import {BookImportBatchDto, BookImportDto} from "@/types/komga-books";
import {CopyMode} from "@/types/enum-books";
import {convertErrorCodes} from "@/functions/error-codes";
export default Vue.extend({
name: 'BookImport',
@ -110,6 +124,8 @@ export default Vue.extend({
transientBooks: [] as TransientBookDto[],
copyMode: CopyMode.HARDLINK,
importFinished: false,
snackbar: false,
snackText: '',
}),
computed: {
globalSelect: {
@ -146,7 +162,11 @@ export default Vue.extend({
methods: {
async scanBooks() {
this.transientBooks = []
this.transientBooks = await this.$komgaTransientBooks.scanForTransientBooks(this.importPath)
try {
this.transientBooks = await this.$komgaTransientBooks.scanForTransientBooks(this.importPath)
} catch (e) {
this.showSnack(convertErrorCodes(e.message))
}
this.selected = this.$_.range(this.transientBooks.length)
this.payloads = this.payloads.splice(this.transientBooks.length, this.payloads.length)
this.importFinished = false
@ -157,6 +177,10 @@ export default Vue.extend({
this.importFinished = true
}
},
showSnack(message: string) {
this.snackText = message
this.snackbar = true
},
},
})
</script>

View file

@ -4,6 +4,8 @@ import org.gotson.komga.domain.model.BookPageContent
import org.gotson.komga.domain.model.BookWithMedia
import org.gotson.komga.domain.model.Media
import org.gotson.komga.domain.model.MediaNotReadyException
import org.gotson.komga.domain.model.PathContainedInPath
import org.gotson.komga.domain.persistence.LibraryRepository
import org.gotson.komga.domain.persistence.TransientBookRepository
import org.springframework.stereotype.Service
import java.nio.file.Paths
@ -13,10 +15,17 @@ class TransientBookLifecycle(
private val transientBookRepository: TransientBookRepository,
private val bookAnalyzer: BookAnalyzer,
private val fileSystemScanner: FileSystemScanner,
private val libraryRepository: LibraryRepository,
) {
fun scanAndPersist(filePath: String): List<BookWithMedia> {
val books = fileSystemScanner.scanRootFolder(Paths.get(filePath)).values.flatten().map { BookWithMedia(it, Media()) }
val folderToScan = Paths.get(filePath)
libraryRepository.findAll().forEach { library ->
if (folderToScan.startsWith(library.path())) throw PathContainedInPath("Cannot scan folder that is part of an existing library", "ERR_1017")
}
val books = fileSystemScanner.scanRootFolder(folderToScan).values.flatten().map { BookWithMedia(it, Media()) }
transientBookRepository.saveAll(books)

View file

@ -3,7 +3,7 @@ package org.gotson.komga.interfaces.rest
import com.jakewharton.byteunits.BinaryByteUnit
import mu.KotlinLogging
import org.gotson.komga.domain.model.BookWithMedia
import org.gotson.komga.domain.model.DirectoryNotFoundException
import org.gotson.komga.domain.model.CodedException
import org.gotson.komga.domain.model.MediaNotReadyException
import org.gotson.komga.domain.model.ROLE_ADMIN
import org.gotson.komga.domain.persistence.TransientBookRepository
@ -43,7 +43,7 @@ class TransientBooksController(
transientBookLifecycle.scanAndPersist(request.path)
.sortedBy { it.book.path() }
.map { it.toDto() }
} catch (e: DirectoryNotFoundException) {
} catch (e: CodedException) {
throw ResponseStatusException(HttpStatus.BAD_REQUEST, e.code)
}