mirror of
https://github.com/Suwayomi/Suwayomi-Server.git
synced 2026-07-10 14:24:34 -05:00
* Reset backup status to idle in case of an exception * Rename "performRestore" function * Set backup status to failure on exception Makes it possible to detect if the restore failed or not after the first status was received * Set backup status to success on completion Since the status is not provided over a subscription, but over a query that should be pulled, it is not really easily detectable if a restore finished or not, since both states will be indicated by "idle" * Correctly wait for first new status when triggering backup import The status is only "Idle" in case no backup import has ever run. Once the first backup process finished it is either "Failure" or "Success" * Rename "ProtoBackupImport::restore" function * Add id to restore process Makes it possible to differentiate between backup restore processes.
180 lines
6.6 KiB
Kotlin
180 lines
6.6 KiB
Kotlin
package suwayomi.tachidesk.manga.controller
|
|
|
|
import io.javalin.http.HttpCode
|
|
import suwayomi.tachidesk.manga.impl.backup.BackupFlags
|
|
import suwayomi.tachidesk.manga.impl.backup.proto.ProtoBackupExport
|
|
import suwayomi.tachidesk.manga.impl.backup.proto.ProtoBackupImport
|
|
import suwayomi.tachidesk.manga.impl.backup.proto.ProtoBackupValidator
|
|
import suwayomi.tachidesk.server.JavalinSetup.future
|
|
import suwayomi.tachidesk.server.util.handler
|
|
import suwayomi.tachidesk.server.util.withOperation
|
|
|
|
/*
|
|
* Copyright (C) Contributors to the Suwayomi project
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
|
|
object BackupController {
|
|
/** expects a Tachiyomi protobuf backup in the body */
|
|
val protobufImport =
|
|
handler(
|
|
documentWith = {
|
|
withOperation {
|
|
summary("Restore a backup")
|
|
description("Expects a Tachiyomi protobuf backup in the body")
|
|
}
|
|
},
|
|
behaviorOf = { ctx ->
|
|
ctx.future(
|
|
future {
|
|
ProtoBackupImport.restoreLegacy(ctx.bodyAsInputStream())
|
|
},
|
|
)
|
|
},
|
|
withResults = {
|
|
httpCode(HttpCode.OK)
|
|
},
|
|
)
|
|
|
|
/** expects a Tachiyomi protobuf backup as a file upload, the file must be named "backup.proto.gz" */
|
|
val protobufImportFile =
|
|
handler(
|
|
documentWith = {
|
|
withOperation {
|
|
summary("Restore a backup file")
|
|
description("Expects a Tachiyomi protobuf backup as a file upload, the file must be named \"backup.proto.gz\"")
|
|
}
|
|
uploadedFile("backup.proto.gz") {
|
|
it.description("Protobuf backup")
|
|
it.required(true)
|
|
}
|
|
},
|
|
behaviorOf = { ctx ->
|
|
// TODO: rewrite this with ctx.uploadedFiles(), don't call the multipart field "backup.proto.gz"
|
|
ctx.future(
|
|
future {
|
|
ProtoBackupImport.restoreLegacy(ctx.uploadedFile("backup.proto.gz")!!.content)
|
|
},
|
|
)
|
|
},
|
|
withResults = {
|
|
httpCode(HttpCode.OK)
|
|
httpCode(HttpCode.NOT_FOUND)
|
|
},
|
|
)
|
|
|
|
/** returns a Tachiyomi protobuf backup created from the current database as a body */
|
|
val protobufExport =
|
|
handler(
|
|
documentWith = {
|
|
withOperation {
|
|
summary("Create a backup")
|
|
description("Returns a Tachiyomi protobuf backup created from the current database as a body")
|
|
}
|
|
},
|
|
behaviorOf = { ctx ->
|
|
ctx.contentType("application/octet-stream")
|
|
ctx.future(
|
|
future {
|
|
ProtoBackupExport.createBackup(
|
|
BackupFlags(
|
|
includeManga = true,
|
|
includeCategories = true,
|
|
includeChapters = true,
|
|
includeTracking = true,
|
|
includeHistory = true,
|
|
),
|
|
)
|
|
},
|
|
)
|
|
},
|
|
withResults = {
|
|
stream(HttpCode.OK)
|
|
},
|
|
)
|
|
|
|
/** returns a Tachiyomi protobuf backup created from the current database as a file */
|
|
val protobufExportFile =
|
|
handler(
|
|
documentWith = {
|
|
withOperation {
|
|
summary("Create a backup file")
|
|
description("Returns a Tachiyomi protobuf backup created from the current database as a file")
|
|
}
|
|
},
|
|
behaviorOf = { ctx ->
|
|
ctx.contentType("application/octet-stream")
|
|
|
|
ctx.header("Content-Disposition", """attachment; filename="${ProtoBackupExport.getBackupFilename()}"""")
|
|
ctx.future(
|
|
future {
|
|
ProtoBackupExport.createBackup(
|
|
BackupFlags(
|
|
includeManga = true,
|
|
includeCategories = true,
|
|
includeChapters = true,
|
|
includeTracking = true,
|
|
includeHistory = true,
|
|
),
|
|
)
|
|
},
|
|
)
|
|
},
|
|
withResults = {
|
|
stream(HttpCode.OK)
|
|
},
|
|
)
|
|
|
|
/** Reports missing sources and trackers, expects a Tachiyomi protobuf backup in the body */
|
|
val protobufValidate =
|
|
handler(
|
|
documentWith = {
|
|
withOperation {
|
|
summary("Validate a backup")
|
|
description("Reports missing sources and trackers, expects a Tachiyomi protobuf backup in the body")
|
|
}
|
|
},
|
|
behaviorOf = { ctx ->
|
|
ctx.future(
|
|
future {
|
|
ProtoBackupValidator.validate(ctx.bodyAsInputStream())
|
|
},
|
|
)
|
|
},
|
|
withResults = {
|
|
json<ProtoBackupValidator.ValidationResult>(HttpCode.OK)
|
|
},
|
|
)
|
|
|
|
/** Reports missing sources and trackers, expects a Tachiyomi protobuf backup as a file upload, the file must be named "backup.proto.gz" */
|
|
val protobufValidateFile =
|
|
handler(
|
|
documentWith = {
|
|
withOperation {
|
|
summary("Validate a backup file")
|
|
description(
|
|
"Reports missing sources and trackers, " +
|
|
"expects a Tachiyomi protobuf backup as a file upload, " +
|
|
"the file must be named \"backup.proto.gz\"",
|
|
)
|
|
}
|
|
uploadedFile("backup.proto.gz") {
|
|
it.description("Protobuf backup")
|
|
it.required(true)
|
|
}
|
|
},
|
|
behaviorOf = { ctx ->
|
|
ctx.future(
|
|
future {
|
|
ProtoBackupValidator.validate(ctx.uploadedFile("backup.proto.gz")!!.content)
|
|
},
|
|
)
|
|
},
|
|
withResults = {
|
|
json<ProtoBackupValidator.ValidationResult>(HttpCode.OK)
|
|
},
|
|
)
|
|
}
|