diff --git a/server/src/main/kotlin/ir/armor/tachidesk/Main.kt b/server/src/main/kotlin/ir/armor/tachidesk/Main.kt index d22a6f9..819656e 100644 --- a/server/src/main/kotlin/ir/armor/tachidesk/Main.kt +++ b/server/src/main/kotlin/ir/armor/tachidesk/Main.kt @@ -17,6 +17,7 @@ import ir.armor.tachidesk.util.getSource import ir.armor.tachidesk.util.getSourceList import ir.armor.tachidesk.util.getThumbnail import ir.armor.tachidesk.util.installAPK +import ir.armor.tachidesk.util.removeExtension import ir.armor.tachidesk.util.sourceFilters import ir.armor.tachidesk.util.sourceGlobalSearch import ir.armor.tachidesk.util.sourceSearch @@ -79,11 +80,20 @@ class Main { app.get("/api/v1/extension/install/:apkName") { ctx -> val apkName = ctx.pathParam("apkName") - println(apkName) + println("installing $apkName") + ctx.status( installAPK(apkName) ) } + + app.get("/api/v1/extension/uninstall/:apkName") { ctx -> + val apkName = ctx.pathParam("apkName") + println("uninstalling $apkName") + removeExtension(apkName) + ctx.status(200) + } + app.get("/api/v1/source/list") { ctx -> ctx.json(getSourceList()) } diff --git a/server/src/main/kotlin/ir/armor/tachidesk/util/Manga.kt b/server/src/main/kotlin/ir/armor/tachidesk/util/Manga.kt index 01bc975..4c7758f 100644 --- a/server/src/main/kotlin/ir/armor/tachidesk/util/Manga.kt +++ b/server/src/main/kotlin/ir/armor/tachidesk/util/Manga.kt @@ -66,28 +66,28 @@ fun getManga(mangaId: Int, proxyThumbnail: Boolean = true): MangaDataClass { println("${mangaEntry[MangaTable.title]} is initialized") println("${mangaEntry[MangaTable.thumbnail_url]}") MangaDataClass( - mangaId, - mangaEntry[MangaTable.sourceReference].value, + mangaId, + mangaEntry[MangaTable.sourceReference].value, - mangaEntry[MangaTable.url], - mangaEntry[MangaTable.title], - if (proxyThumbnail) proxyThumbnailUrl(mangaId) else mangaEntry[MangaTable.thumbnail_url], + mangaEntry[MangaTable.url], + mangaEntry[MangaTable.title], + if (proxyThumbnail) proxyThumbnailUrl(mangaId) else mangaEntry[MangaTable.thumbnail_url], - true, + true, - mangaEntry[MangaTable.artist], - mangaEntry[MangaTable.author], - mangaEntry[MangaTable.description], - mangaEntry[MangaTable.genre], - MangaStatus.valueOf(mangaEntry[MangaTable.status]).name, + mangaEntry[MangaTable.artist], + mangaEntry[MangaTable.author], + mangaEntry[MangaTable.description], + mangaEntry[MangaTable.genre], + MangaStatus.valueOf(mangaEntry[MangaTable.status]).name, ) } else { // initialize manga val source = getHttpSource(mangaEntry[MangaTable.sourceReference].value) val fetchedManga = source.fetchMangaDetails( - SManga.create().apply { - url = mangaEntry[MangaTable.url] - title = mangaEntry[MangaTable.title] - } + SManga.create().apply { + url = mangaEntry[MangaTable.url] + title = mangaEntry[MangaTable.title] + } ).toBlocking().first() // update database @@ -97,25 +97,25 @@ fun getManga(mangaId: Int, proxyThumbnail: Boolean = true): MangaDataClass { // mangaEntry = MangaTable.select { MangaTable.id eq mangaId }.firstOrNull()!! val newThumbnail = - if (fetchedManga.thumbnail_url != null && fetchedManga.thumbnail_url!!.isNotEmpty()) { - fetchedManga.thumbnail_url - } else mangaEntry[MangaTable.thumbnail_url] + if (fetchedManga.thumbnail_url != null && fetchedManga.thumbnail_url!!.isNotEmpty()) { + fetchedManga.thumbnail_url + } else mangaEntry[MangaTable.thumbnail_url] MangaDataClass( - mangaId, - mangaEntry[MangaTable.sourceReference].value, + mangaId, + mangaEntry[MangaTable.sourceReference].value, - mangaEntry[MangaTable.url], - mangaEntry[MangaTable.title], - if (proxyThumbnail) proxyThumbnailUrl(mangaId) else newThumbnail, + mangaEntry[MangaTable.url], + mangaEntry[MangaTable.title], + if (proxyThumbnail) proxyThumbnailUrl(mangaId) else newThumbnail, - true, + true, - fetchedManga.artist, - fetchedManga.author, - fetchedManga.description, - fetchedManga.genre, - MangaStatus.valueOf(fetchedManga.status).name, + fetchedManga.artist, + fetchedManga.author, + fetchedManga.description, + fetchedManga.genre, + MangaStatus.valueOf(fetchedManga.status).name, ) } } @@ -135,8 +135,8 @@ fun getThumbnail(mangaId: Int): Pair { if (potentialCache != null) { println("using cached thumbnail file") return@transaction Pair( - pathToInputStream(potentialCache), - "image/${potentialCache.substringAfter("$mangaId.")}" + pathToInputStream(potentialCache), + "image/${potentialCache.substringAfter("$mangaId.")}" ) } @@ -149,7 +149,7 @@ fun getThumbnail(mangaId: Int): Pair { } println(thumbnailUrl) val response = source.client.newCall( - GET(thumbnailUrl, source.headers) + GET(thumbnailUrl, source.headers) ).execute() println(response.code) @@ -161,8 +161,8 @@ fun getThumbnail(mangaId: Int): Pair { writeStream(response.body!!.byteStream(), filePath) return@transaction Pair( - pathToInputStream(filePath), - contentType + pathToInputStream(filePath), + contentType ) } else { throw Exception("request error! ${response.code}") diff --git a/server/src/main/kotlin/ir/armor/tachidesk/util/APK.kt b/server/src/main/kotlin/ir/armor/tachidesk/util/ٍExtension.kt similarity index 86% rename from server/src/main/kotlin/ir/armor/tachidesk/util/APK.kt rename to server/src/main/kotlin/ir/armor/tachidesk/util/ٍExtension.kt index 4b5ba15..fe36b23 100644 --- a/server/src/main/kotlin/ir/armor/tachidesk/util/APK.kt +++ b/server/src/main/kotlin/ir/armor/tachidesk/util/ٍExtension.kt @@ -17,6 +17,7 @@ import kotlinx.coroutines.runBlocking import okhttp3.Request import okio.buffer import okio.sink +import org.jetbrains.exposed.sql.deleteWhere import org.jetbrains.exposed.sql.insert import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.transactions.transaction @@ -32,8 +33,8 @@ fun installAPK(apkName: String): Int { val dirPathWithoutType = "${Config.extensionsRoot}/$fileNameWithoutType" // check if we don't have the dex file already downloaded - val dexPath = "${Config.extensionsRoot}/$fileNameWithoutType.jar" - if (!File(dexPath).exists()) { + val jarPath = "${Config.extensionsRoot}/$fileNameWithoutType.jar" + if (!File(jarPath).exists()) { runBlocking { val api = ExtensionGithubApi() val apkToDownload = api.getApkUrl(extensionRecord) @@ -130,3 +131,21 @@ private fun downloadAPKFile(url: String, apkPath: String) { sink.writeAll(response.body!!.source()) sink.close() } + +fun removeExtension(pkgName: String) { + val extensionRecord = getExtensionList(true).first { it.apkName == pkgName } + val fileNameWithoutType = pkgName.substringBefore(".apk") + val jarPath = "${Config.extensionsRoot}/$fileNameWithoutType.jar" + transaction { + val extensionId = ExtensionsTable.select { ExtensionsTable.name eq extensionRecord.name }.first()[ExtensionsTable.id] + + SourceTable.deleteWhere { SourceTable.extension eq extensionId } + ExtensionsTable.update({ ExtensionsTable.name eq extensionRecord.name }) { + it[ExtensionsTable.installed] = false + } + } + + if (File(jarPath).exists()) { + File(jarPath).delete() + } +} diff --git a/webUI/react/src/components/ExtensionCard.tsx b/webUI/react/src/components/ExtensionCard.tsx index 75182b5..3a3d302 100644 --- a/webUI/react/src/components/ExtensionCard.tsx +++ b/webUI/react/src/components/ExtensionCard.tsx @@ -46,7 +46,7 @@ export default function ExtensionCard(props: IProps) { name, lang, versionName, iconUrl, installed, apkName, }, } = props; - const [installedState, setInstalledState] = useState((installed ? 'installed' : 'install')); + const [installedState, setInstalledState] = useState((installed ? 'uninstall' : 'install')); const classes = useStyles(); const langPress = lang === 'all' ? 'All' : lang.toUpperCase(); @@ -54,10 +54,25 @@ export default function ExtensionCard(props: IProps) { function install() { setInstalledState('installing'); fetch(`http://127.0.0.1:4567/api/v1/extension/install/${apkName}`).then(() => { - setInstalledState('installed'); + setInstalledState('uninstall'); }); } + function uninstall() { + setInstalledState('uninstalling'); + fetch(`http://127.0.0.1:4567/api/v1/extension/uninstall/${apkName}`).then(() => { + setInstalledState('install'); + }); + } + + function handleButtonClick() { + if (installedState === 'install') { + install(); + } else { + uninstall(); + } + } + return ( @@ -80,7 +95,7 @@ export default function ExtensionCard(props: IProps) { - + );