mirror of
https://github.com/revanced/revanced-api.git
synced 2025-05-02 07:34:29 +02:00
feat: Add /list
route
This commit is contained in:
parent
e798a4c070
commit
6c930fff9a
@ -21,7 +21,7 @@ tasks {
|
|||||||
Because semantic-release is not designed to handle this case, we need to hack it.
|
Because semantic-release is not designed to handle this case, we need to hack it.
|
||||||
|
|
||||||
RE: https://github.com/KengoTODA/gradle-semantic-release-plugin/issues/435
|
RE: https://github.com/KengoTODA/gradle-semantic-release-plugin/issues/435
|
||||||
*/
|
*/
|
||||||
register<DefaultTask>("publish") {
|
register<DefaultTask>("publish") {
|
||||||
group = "publishing"
|
group = "publishing"
|
||||||
description = "Dummy task to hack gradle-semantic-release-plugin to release ReVanced API"
|
description = "Dummy task to hack gradle-semantic-release-plugin to release ReVanced API"
|
||||||
@ -41,6 +41,9 @@ ktor {
|
|||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
google()
|
||||||
|
maven { url = uri("https://jitpack.io") }
|
||||||
|
mavenLocal()
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
@ -72,6 +75,9 @@ dependencies {
|
|||||||
implementation(libs.ktoml.file)
|
implementation(libs.ktoml.file)
|
||||||
implementation(libs.picocli)
|
implementation(libs.picocli)
|
||||||
implementation(libs.kotlinx.datetime)
|
implementation(libs.kotlinx.datetime)
|
||||||
|
implementation(libs.revanced.patcher)
|
||||||
|
implementation(libs.revanced.library)
|
||||||
|
implementation(libs.caffeine)
|
||||||
|
|
||||||
testImplementation(libs.mockk)
|
testImplementation(libs.mockk)
|
||||||
testImplementation(libs.ktor.server.tests)
|
testImplementation(libs.ktor.server.tests)
|
||||||
|
@ -10,6 +10,9 @@ ktoml = "0.5.1"
|
|||||||
picocli = "4.7.3"
|
picocli = "4.7.3"
|
||||||
datetime = "0.5.0"
|
datetime = "0.5.0"
|
||||||
mockk = "1.13.9"
|
mockk = "1.13.9"
|
||||||
|
revanced-patcher = "19.2.0"
|
||||||
|
revanced-library = "1.5.0"
|
||||||
|
caffeine = "3.1.8"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
ktor-client-core = { module = "io.ktor:ktor-client-core" }
|
ktor-client-core = { module = "io.ktor:ktor-client-core" }
|
||||||
@ -43,6 +46,9 @@ ktoml-file = { module = "com.akuleshov7:ktoml-file", version.ref = "ktoml" }
|
|||||||
picocli = { module = "info.picocli:picocli", version.ref = "picocli" }
|
picocli = { module = "info.picocli:picocli", version.ref = "picocli" }
|
||||||
kotlinx-datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.ref = "datetime" }
|
kotlinx-datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.ref = "datetime" }
|
||||||
mockk = { module = "io.mockk:mockk", version.ref = "mockk" }
|
mockk = { module = "io.mockk:mockk", version.ref = "mockk" }
|
||||||
|
revanced-patcher = { module = "app.revanced:revanced-patcher", version.ref = "revanced-patcher" }
|
||||||
|
revanced-library = { module = "app.revanced:revanced-library", version.ref = "revanced-library" }
|
||||||
|
caffeine = { module = "com.github.ben-manes.caffeine:caffeine", version.ref = "caffeine" }
|
||||||
|
|
||||||
[plugins]
|
[plugins]
|
||||||
serilization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
|
serilization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
|
||||||
|
@ -19,8 +19,6 @@ fun Application.configureHTTP() {
|
|||||||
anyHost() // @TODO: Don't do this in production if possible. Try to limit it.
|
anyHost() // @TODO: Don't do this in production if possible. Try to limit it.
|
||||||
}
|
}
|
||||||
install(CachingHeaders) {
|
install(CachingHeaders) {
|
||||||
options { _, _ ->
|
options { _, _ -> CachingOptions(CacheControl.MaxAge(maxAgeSeconds = 5.minutes.inWholeSeconds.toInt())) }
|
||||||
CachingOptions(CacheControl.MaxAge(maxAgeSeconds = 5.minutes.inWholeSeconds.toInt()))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,9 @@ package app.revanced.api.modules
|
|||||||
|
|
||||||
import app.revanced.api.backend.Backend
|
import app.revanced.api.backend.Backend
|
||||||
import app.revanced.api.schema.*
|
import app.revanced.api.schema.*
|
||||||
|
import app.revanced.library.PatchUtils
|
||||||
|
import app.revanced.patcher.PatchBundleLoader
|
||||||
|
import com.github.benmanes.caffeine.cache.Caffeine
|
||||||
import io.ktor.http.*
|
import io.ktor.http.*
|
||||||
import io.ktor.server.application.*
|
import io.ktor.server.application.*
|
||||||
import io.ktor.server.auth.*
|
import io.ktor.server.auth.*
|
||||||
@ -12,6 +15,8 @@ import io.ktor.server.routing.*
|
|||||||
import io.ktor.util.pipeline.*
|
import io.ktor.util.pipeline.*
|
||||||
import kotlinx.coroutines.async
|
import kotlinx.coroutines.async
|
||||||
import kotlinx.coroutines.awaitAll
|
import kotlinx.coroutines.awaitAll
|
||||||
|
import java.io.File
|
||||||
|
import java.net.URL
|
||||||
import org.koin.ktor.ext.get as koinGet
|
import org.koin.ktor.ext.get as koinGet
|
||||||
|
|
||||||
fun Application.configureRouting() {
|
fun Application.configureRouting() {
|
||||||
@ -116,34 +121,70 @@ fun Application.configureRouting() {
|
|||||||
route("/patches") {
|
route("/patches") {
|
||||||
route("latest") {
|
route("latest") {
|
||||||
get {
|
get {
|
||||||
val patches = backend.getRelease(configuration.organization, configuration.patchesRepository)
|
val patchesRelease =
|
||||||
val integrations =
|
backend.getRelease(configuration.organization, configuration.patchesRepository)
|
||||||
configuration.integrationsRepositoryNames.map {
|
val integrationsReleases = configuration.integrationsRepositoryNames.map {
|
||||||
async { backend.getRelease(configuration.organization, it) }
|
async { backend.getRelease(configuration.organization, it) }
|
||||||
}.awaitAll()
|
}.awaitAll()
|
||||||
|
|
||||||
val assets =
|
val assets = (patchesRelease.assets + integrationsReleases.flatMap { it.assets })
|
||||||
(patches.assets + integrations.flatMap { it.assets }).filter {
|
.map { APIAsset(it.downloadUrl) }
|
||||||
it.downloadUrl.endsWith(".apk") || it.downloadUrl.endsWith(".jar")
|
.filter { it.type != APIAsset.Type.UNKNOWN }
|
||||||
}.map { APIAsset(it.downloadUrl) }.toSet()
|
.toSet()
|
||||||
|
|
||||||
val release =
|
val apiRelease = APIRelease(
|
||||||
APIRelease(
|
patchesRelease.tag,
|
||||||
patches.tag,
|
patchesRelease.createdAt,
|
||||||
patches.createdAt,
|
patchesRelease.releaseNote,
|
||||||
patches.releaseNote,
|
assets,
|
||||||
assets,
|
)
|
||||||
)
|
|
||||||
|
|
||||||
call.respond(release)
|
call.respond(apiRelease)
|
||||||
}
|
}
|
||||||
|
|
||||||
get("/version") {
|
get("/version") {
|
||||||
val patches = backend.getRelease(configuration.organization, configuration.patchesRepository)
|
val patchesRelease =
|
||||||
|
backend.getRelease(configuration.organization, configuration.patchesRepository)
|
||||||
|
|
||||||
val release = APIReleaseVersion(patches.tag)
|
val apiPatchesRelease = APIReleaseVersion(patchesRelease.tag)
|
||||||
|
|
||||||
call.respond(release)
|
call.respond(apiPatchesRelease)
|
||||||
|
}
|
||||||
|
|
||||||
|
val fileCache = Caffeine
|
||||||
|
.newBuilder()
|
||||||
|
.evictionListener<String, File> { _, value, _ -> value?.delete() }
|
||||||
|
.maximumSize(1)
|
||||||
|
.build<String, File>()
|
||||||
|
|
||||||
|
get("/list") {
|
||||||
|
val patchesRelease =
|
||||||
|
backend.getRelease(configuration.organization, configuration.patchesRepository)
|
||||||
|
|
||||||
|
// Get the cached patches file or download and cache a new one.
|
||||||
|
// The old file is deleted on eviction.
|
||||||
|
val patchesFile = fileCache.getIfPresent(patchesRelease.tag) ?: run {
|
||||||
|
val downloadUrl = patchesRelease.assets
|
||||||
|
.map { APIAsset(it.downloadUrl) }
|
||||||
|
.find { it.type == APIAsset.Type.PATCHES }
|
||||||
|
?.downloadUrl
|
||||||
|
|
||||||
|
kotlin.io.path.createTempFile().toFile().apply {
|
||||||
|
outputStream().use { URL(downloadUrl).openStream().copyTo(it) }
|
||||||
|
}.also {
|
||||||
|
fileCache.put(patchesRelease.tag, it)
|
||||||
|
it.deleteOnExit()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
call.respondOutputStream(
|
||||||
|
contentType = ContentType.Application.Json,
|
||||||
|
) {
|
||||||
|
PatchUtils.Json.serialize(
|
||||||
|
PatchBundleLoader.Jar(patchesFile),
|
||||||
|
outputStream = this,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package app.revanced.api.schema
|
package app.revanced.api.schema
|
||||||
|
|
||||||
import kotlinx.datetime.LocalDateTime
|
import kotlinx.datetime.LocalDateTime
|
||||||
|
import kotlinx.serialization.SerialName
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@ -44,9 +45,20 @@ class APIAsset(
|
|||||||
val downloadUrl: String,
|
val downloadUrl: String,
|
||||||
) {
|
) {
|
||||||
val type = when {
|
val type = when {
|
||||||
downloadUrl.endsWith(".jar") -> "patches"
|
downloadUrl.endsWith(".jar") -> Type.PATCHES
|
||||||
downloadUrl.endsWith(".apk") -> "integrations"
|
downloadUrl.endsWith(".apk") -> Type.INTEGRATIONS
|
||||||
else -> "unknown"
|
else -> Type.UNKNOWN
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class Type {
|
||||||
|
@SerialName("patches")
|
||||||
|
PATCHES,
|
||||||
|
|
||||||
|
@SerialName("integrations")
|
||||||
|
INTEGRATIONS,
|
||||||
|
|
||||||
|
@SerialName("unknown")
|
||||||
|
UNKNOWN,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user