package suwayomi.tachidesk.manga.controller /* * 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/. */ import io.javalin.http.HttpStatus import mu.KotlinLogging import suwayomi.tachidesk.manga.impl.extension.Extension import suwayomi.tachidesk.manga.impl.extension.ExtensionsList import suwayomi.tachidesk.manga.model.dataclass.ExtensionDataClass import suwayomi.tachidesk.server.JavalinSetup.future import suwayomi.tachidesk.server.util.handler import suwayomi.tachidesk.server.util.pathParam import suwayomi.tachidesk.server.util.withOperation import kotlin.time.Duration.Companion.days object ExtensionController { private val logger = KotlinLogging.logger {} /** list all extensions */ val list = handler( documentWith = { withOperation { summary("Extension list") description("List all extensions") } }, behaviorOf = { ctx -> ctx.future { future { ExtensionsList.getExtensionList() }.thenApply { ctx.json(it) } } }, withResults = { json>(HttpStatus.OK) }, ) /** install extension identified with "pkgName" */ val install = handler( pathParam("pkgName"), documentWith = { withOperation { summary("Extension install") description("install extension identified with \"pkgName\"") } }, behaviorOf = { ctx, pkgName -> ctx.future { future { Extension.installExtension(pkgName) }.thenApply { ctx.status(it) } } }, withResults = { httpCode(HttpStatus.CREATED) httpCode(HttpStatus.FOUND) httpCode(HttpStatus.INTERNAL_SERVER_ERROR) }, ) /** install the uploaded apk file */ val installFile = handler( documentWith = { withOperation { summary("Extension install apk") description("Install the uploaded apk file") } uploadedFile("file") { it.description("Extension apk") it.required(true) } }, behaviorOf = { ctx -> val uploadedFile = ctx.uploadedFile("file")!! logger.debug { "Uploaded extension file name: " + uploadedFile.filename() } ctx.future { future { Extension.installExternalExtension( uploadedFile.content(), uploadedFile.filename(), ) }.thenApply { ctx.status(it) } } }, withResults = { httpCode(HttpStatus.CREATED) httpCode(HttpStatus.FOUND) httpCode(HttpStatus.INTERNAL_SERVER_ERROR) }, ) /** update extension identified with "pkgName" */ val update = handler( pathParam("pkgName"), documentWith = { withOperation { summary("Extension update") description("Update extension identified with \"pkgName\"") } }, behaviorOf = { ctx, pkgName -> ctx.future { future { Extension.updateExtension(pkgName) }.thenApply { ctx.status(it) } } }, withResults = { httpCode(HttpStatus.CREATED) httpCode(HttpStatus.FOUND) httpCode(HttpStatus.NOT_FOUND) httpCode(HttpStatus.INTERNAL_SERVER_ERROR) }, ) /** uninstall extension identified with "pkgName" */ val uninstall = handler( pathParam("pkgName"), documentWith = { withOperation { summary("Extension uninstall") description("Uninstall extension identified with \"pkgName\"") } }, behaviorOf = { ctx, pkgName -> Extension.uninstallExtension(pkgName) ctx.status(200) }, withResults = { httpCode(HttpStatus.CREATED) httpCode(HttpStatus.FOUND) httpCode(HttpStatus.NOT_FOUND) httpCode(HttpStatus.INTERNAL_SERVER_ERROR) }, ) /** icon for extension named `apkName` */ val icon = handler( pathParam("apkName"), documentWith = { withOperation { summary("Extension icon") description("Icon for extension named `apkName`") } }, behaviorOf = { ctx, apkName -> ctx.future { future { Extension.getExtensionIcon(apkName) } .thenApply { ctx.header("content-type", it.second) val httpCacheSeconds = 365.days.inWholeSeconds ctx.header("cache-control", "max-age=$httpCacheSeconds, immutable") ctx.result(it.first) } } }, withResults = { image(HttpStatus.OK) httpCode(HttpStatus.NOT_FOUND) }, ) }