diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e0d2c47..7dd62926 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,32 @@ +# [1.2.0-dev.4](https://github.com/ReVanced/revanced-api/compare/v1.2.0-dev.3...v1.2.0-dev.4) (2024-09-06) + + +### Bug Fixes + +* Add back deprecated routes for backwards compatibility ([9f0eb5b](https://github.com/ReVanced/revanced-api/commit/9f0eb5bfe9d0436e76462b9c094f8b1158e04a44)) +* Make sure, expected paths in configuration exist ([32bedb7](https://github.com/ReVanced/revanced-api/commit/32bedb7fad3eef8116625964e5e1f0a2543ea2a4)) + +# [1.2.0-dev.3](https://github.com/ReVanced/revanced-api/compare/v1.2.0-dev.2...v1.2.0-dev.3) (2024-09-04) + + +### Bug Fixes + +* Return correct GPG keys url ([#187](https://github.com/ReVanced/revanced-api/issues/187)) ([74e5389](https://github.com/ReVanced/revanced-api/commit/74e53891a17bd3f76f358477e4228550e6f70149)) + +# [1.2.0-dev.2](https://github.com/ReVanced/revanced-api/compare/v1.2.0-dev.1...v1.2.0-dev.2) (2024-08-24) + + +### Features + +* Respond to all ping request methods ([df116bd](https://github.com/ReVanced/revanced-api/commit/df116bd22134c8222c72b28e9387bc9871d3473e)) + +# [1.2.0-dev.1](https://github.com/ReVanced/revanced-api/compare/v1.1.0...v1.2.0-dev.1) (2024-08-16) + + +### Features + +* Move /latest routes to parent ([4e8e83d](https://github.com/ReVanced/revanced-api/commit/4e8e83db1a20c76a81967af4e7e3a8634649790a)) + # [1.1.0](https://github.com/ReVanced/revanced-api/compare/v1.0.0...v1.1.0) (2024-07-15) diff --git a/README.md b/README.md index e520b85d..1ed9dac6 100644 --- a/README.md +++ b/README.md @@ -96,20 +96,30 @@ so before you can pull the image, you need to [authenticate to the Container reg 1. Create an `.env` file using [.env.example](.env.example) as a template 2. Create a `configuration.toml` file using [configuration.example.toml](configuration.example.toml) as a template -3. Create a `docker-compose.yml` file using [docker-compose.example.yml](docker-compose.example.yml) as a template -4. Run `docker-compose up -d` to start the server +3. Create an `about.json` file using [about.example.json](about.example.json) as a template +4. Create a `docker-compose.yml` file using [docker-compose.example.yml](docker-compose.example.yml) as a template +5. Run `docker-compose up -d` to start the server ### 💻 Docker CLI 1. Create an `.env` file using [.env.example](.env.example) as a template 2. Create a `configuration.toml` file using [configuration.example.toml](configuration.example.toml) as a template -3. Start the container using the following command: +3. Create an `about.json` file using [about.example.json](about.example.json) as a template +4. Start the container using the following command: ```shell docker run -d --name revanced-api \ # Mount the .env file -v $(pwd)/.env:/app/.env \ # Mount the configuration.toml file -v $(pwd)/configuration.toml:/app/configuration.toml \ + # Mount the patches public key + -v $(pwd)/patches-public-key.asc:/app/patches-public-key.asc \ + # Mount the integrations public key + -v $(pwd)/integrations-public-key.asc:/app/integrations-public-key.asc \ + # Mount the static folder + -v $(pwd)/static:/app/static \ + # Mount the about.json file + -v $(pwd)/about.json:/app/about.json \ # Mount the persistence folder -v $(pwd)/persistence:/app/persistence \ # Expose the port 8888 @@ -132,7 +142,8 @@ A Java Runtime Environment (JRE) must be installed. 2. In the same folder, create an `.env` file using [.env.example](.env.example) as a template 3. In the same folder, create a `configuration.toml` file using [configuration.example.toml](configuration.example.toml) as a template -4. Run `java -jar revanced-api.jar start` to start the server +4. In the same folder, create an `about.json` file using [about.example.json](about.example.json) as a template +5. Run `java -jar revanced-api.jar start` to start the server ### 🛠️ From source @@ -141,7 +152,8 @@ A Java Development Kit (JDK) and Git must be installed. 1. Run `git clone git@github.com:ReVanced/revanced-api.git` to clone the repository 2. Copy [.env.example](.env.example) to `.env` and fill in the required values 3. Copy [configuration.example.toml](configuration.example.toml) to `configuration.toml` and fill in the required values -4. Run `gradlew run --args=start` to start the server +4. Copy [about.example.json](about.example.json) to `about.json` and fill in the required values +5. Run `gradlew run --args=start` to start the server ## 📚 Everything else diff --git a/docker-compose.example.yml b/docker-compose.example.yml index 8034e332..db40175e 100644 --- a/docker-compose.example.yml +++ b/docker-compose.example.yml @@ -13,5 +13,5 @@ services: environment: - COMMAND=start ports: - - 8888:8888 + - "8888:8888" restart: unless-stopped diff --git a/gradle.properties b/gradle.properties index 24b8e245..3bab40c6 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ org.gradle.parallel = true org.gradle.caching = true kotlin.code.style = official -version = 1.1.0 +version = 1.2.0-dev.4 diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 6730386a..2816c9ef 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -10,8 +10,8 @@ ktor = "2.3.7" ktoml = "0.5.2" picocli = "4.7.6" datetime = "0.6.0" -revanced-patcher = "19.3.1" -revanced-library = "2.3.0" +revanced-patcher = "20.0.0" +revanced-library = "3.0.1-dev.1" caffeine = "3.1.8" bouncy-castle = "1.78.1" diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index e6441136..2c352119 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 8a1f6b97..68e8816d 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=a4b4158601f8636cdeeab09bd76afb640030bb5b144aafe261a5e8af027dc612 -distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip +distributionSha256Sum=d725d707bfabd4dfdc958c624003b3c80accc03f7037b5122c4b1d0ef15cecab +distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index b740cf13..f5feea6d 100755 --- a/gradlew +++ b/gradlew @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -84,7 +86,8 @@ done # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s +' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum diff --git a/gradlew.bat b/gradlew.bat index 25da30db..9d21a218 100755 --- a/gradlew.bat +++ b/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## diff --git a/src/main/kotlin/app/revanced/api/configuration/repository/ConfigurationRepository.kt b/src/main/kotlin/app/revanced/api/configuration/repository/ConfigurationRepository.kt index 22a654e5..65b82f68 100644 --- a/src/main/kotlin/app/revanced/api/configuration/repository/ConfigurationRepository.kt +++ b/src/main/kotlin/app/revanced/api/configuration/repository/ConfigurationRepository.kt @@ -17,6 +17,7 @@ import kotlinx.serialization.json.JsonNamingStrategy import kotlinx.serialization.json.decodeFromStream import java.io.File import java.nio.file.Path +import kotlin.io.path.createDirectories /** * The repository storing the configuration for the API. @@ -60,6 +61,11 @@ internal class ConfigurationRepository( @SerialName("about-json-file-path") val about: APIAbout, ) { + init { + staticFilesPath.createDirectories() + versionedStaticFilesPath.createDirectories() + } + /** * Am asset configuration whose asset is signed. * diff --git a/src/main/kotlin/app/revanced/api/configuration/repository/GitHubBackendRepository.kt b/src/main/kotlin/app/revanced/api/configuration/repository/GitHubBackendRepository.kt index d5d91489..39d02368 100644 --- a/src/main/kotlin/app/revanced/api/configuration/repository/GitHubBackendRepository.kt +++ b/src/main/kotlin/app/revanced/api/configuration/repository/GitHubBackendRepository.kt @@ -98,7 +98,7 @@ class GitHubBackendRepository(client: HttpClient) : BackendRepository(client) { gpgKeys = BackendMember.GpgKeys( ids = gpgKeys.map { it.keyId }, - url = "https://api.github.com/users/${user.login}.gpg", + url = "https://github.com/${user.login}.gpg", ), ) } diff --git a/src/main/kotlin/app/revanced/api/configuration/routes/ApiRoute.kt b/src/main/kotlin/app/revanced/api/configuration/routes/ApiRoute.kt index 834f8573..d041461f 100644 --- a/src/main/kotlin/app/revanced/api/configuration/routes/ApiRoute.kt +++ b/src/main/kotlin/app/revanced/api/configuration/routes/ApiRoute.kt @@ -72,7 +72,7 @@ internal fun Route.apiRoute() { installPingRouteDocumentation() - head { + handle { call.respond(HttpStatusCode.NoContent) } } diff --git a/src/main/kotlin/app/revanced/api/configuration/routes/ManagerRoute.kt b/src/main/kotlin/app/revanced/api/configuration/routes/ManagerRoute.kt index b87024cf..811f3a93 100644 --- a/src/main/kotlin/app/revanced/api/configuration/routes/ManagerRoute.kt +++ b/src/main/kotlin/app/revanced/api/configuration/routes/ManagerRoute.kt @@ -14,33 +14,41 @@ import io.ktor.server.routing.* import org.koin.ktor.ext.get as koinGet internal fun Route.managerRoute() = route("manager") { - val managerService = koinGet() + configure() + // TODO: Remove this deprecated route eventually. route("latest") { - installLatestManagerRouteDocumentation() + configure(deprecated = true) + } +} - rateLimit(RateLimitName("weak")) { - get { - call.respond(managerService.latestRelease()) - } +private fun Route.configure(deprecated: Boolean = false) { + val managerService = koinGet() + + installManagerRouteDocumentation(deprecated) - route("version") { - installLatestManagerVersionRouteDocumentation() + rateLimit(RateLimitName("weak")) { + get { + call.respond(managerService.latestRelease()) + } - get { - call.respond(managerService.latestVersion()) - } + route("version") { + installManagerVersionRouteDocumentation(deprecated) + + get { + call.respond(managerService.latestVersion()) } } } } -private fun Route.installLatestManagerRouteDocumentation() = installNotarizedRoute { +private fun Route.installManagerRouteDocumentation(deprecated: Boolean) = installNotarizedRoute { tags = setOf("Manager") get = GetInfo.builder { - description("Get the latest manager release") - summary("Get latest manager release") + if (deprecated) isDeprecated() + description("Get the current manager release") + summary("Get current manager release") response { description("The latest manager release") mediaTypes("application/json") @@ -50,14 +58,15 @@ private fun Route.installLatestManagerRouteDocumentation() = installNotarizedRou } } -private fun Route.installLatestManagerVersionRouteDocumentation() = installNotarizedRoute { +private fun Route.installManagerVersionRouteDocumentation(deprecated: Boolean) = installNotarizedRoute { tags = setOf("Manager") get = GetInfo.builder { - description("Get the latest manager release version") - summary("Get latest manager release version") + if (deprecated) isDeprecated() + description("Get the current manager release version") + summary("Get current manager release version") response { - description("The latest manager release version") + description("The current manager release version") mediaTypes("application/json") responseCode(HttpStatusCode.OK) responseType() diff --git a/src/main/kotlin/app/revanced/api/configuration/routes/PatchesRoute.kt b/src/main/kotlin/app/revanced/api/configuration/routes/PatchesRoute.kt index b1ff8412..efe7e100 100644 --- a/src/main/kotlin/app/revanced/api/configuration/routes/PatchesRoute.kt +++ b/src/main/kotlin/app/revanced/api/configuration/routes/PatchesRoute.kt @@ -17,32 +17,39 @@ import kotlin.time.Duration.Companion.days import org.koin.ktor.ext.get as koinGet internal fun Route.patchesRoute() = route("patches") { - val patchesService = koinGet() + configure() + // TODO: Remove this deprecated route eventually. route("latest") { - installLatestPatchesRouteDocumentation() + configure(deprecated = true) + } +} - rateLimit(RateLimitName("weak")) { - get { - call.respond(patchesService.latestRelease()) - } +private fun Route.configure(deprecated: Boolean = false) { + val patchesService = koinGet() - route("version") { - installLatestPatchesVersionRouteDocumentation() + installPatchesRouteDocumentation(deprecated) - get { - call.respond(patchesService.latestVersion()) - } + rateLimit(RateLimitName("weak")) { + get { + call.respond(patchesService.latestRelease()) + } + + route("version") { + installPatchesVersionRouteDocumentation(deprecated) + + get { + call.respond(patchesService.latestVersion()) } } + } - rateLimit(RateLimitName("strong")) { - route("list") { - installLatestPatchesListRouteDocumentation() + rateLimit(RateLimitName("strong")) { + route("list") { + installPatchesListRouteDocumentation(deprecated) - get { - call.respondBytes(ContentType.Application.Json) { patchesService.list() } - } + get { + call.respondBytes(ContentType.Application.Json) { patchesService.list() } } } } @@ -51,7 +58,7 @@ internal fun Route.patchesRoute() = route("patches") { route("keys") { installCache(356.days) - installPatchesPublicKeyRouteDocumentation() + installPatchesPublicKeyRouteDocumentation(deprecated) get { call.respond(patchesService.publicKeys()) @@ -60,14 +67,15 @@ internal fun Route.patchesRoute() = route("patches") { } } -private fun Route.installLatestPatchesRouteDocumentation() = installNotarizedRoute { +private fun Route.installPatchesRouteDocumentation(deprecated: Boolean) = installNotarizedRoute { tags = setOf("Patches") get = GetInfo.builder { - description("Get the latest patches release") - summary("Get latest patches release") + if (deprecated) isDeprecated() + description("Get the current patches release") + summary("Get current patches release") response { - description("The latest patches release") + description("The current patches release") mediaTypes("application/json") responseCode(HttpStatusCode.OK) responseType>() @@ -75,14 +83,15 @@ private fun Route.installLatestPatchesRouteDocumentation() = installNotarizedRou } } -private fun Route.installLatestPatchesVersionRouteDocumentation() = installNotarizedRoute { +private fun Route.installPatchesVersionRouteDocumentation(deprecated: Boolean) = installNotarizedRoute { tags = setOf("Patches") get = GetInfo.builder { - description("Get the latest patches release version") - summary("Get latest patches release version") + if (deprecated) isDeprecated() + description("Get the current patches release version") + summary("Get current patches release version") response { - description("The latest patches release version") + description("The current patches release version") mediaTypes("application/json") responseCode(HttpStatusCode.OK) responseType() @@ -90,12 +99,13 @@ private fun Route.installLatestPatchesVersionRouteDocumentation() = installNotar } } -private fun Route.installLatestPatchesListRouteDocumentation() = installNotarizedRoute { +private fun Route.installPatchesListRouteDocumentation(deprecated: Boolean) = installNotarizedRoute { tags = setOf("Patches") get = GetInfo.builder { - description("Get the list of patches from the latest patches release") - summary("Get list of patches from latest patches release") + if (deprecated) isDeprecated() + description("Get the list of patches from the current patches release") + summary("Get list of patches from current patches release") response { description("The list of patches") mediaTypes("application/json") @@ -105,10 +115,11 @@ private fun Route.installLatestPatchesListRouteDocumentation() = installNotarize } } -private fun Route.installPatchesPublicKeyRouteDocumentation() = installNotarizedRoute { +private fun Route.installPatchesPublicKeyRouteDocumentation(deprecated: Boolean) = installNotarizedRoute { tags = setOf("Patches") get = GetInfo.builder { + if (deprecated) isDeprecated() description("Get the public keys for verifying patches and integrations assets") summary("Get patches and integrations public keys") response { diff --git a/src/main/kotlin/app/revanced/api/configuration/services/PatchesService.kt b/src/main/kotlin/app/revanced/api/configuration/services/PatchesService.kt index a1e7608c..6a70041a 100644 --- a/src/main/kotlin/app/revanced/api/configuration/services/PatchesService.kt +++ b/src/main/kotlin/app/revanced/api/configuration/services/PatchesService.kt @@ -4,8 +4,8 @@ import app.revanced.api.configuration.repository.BackendRepository import app.revanced.api.configuration.repository.BackendRepository.BackendOrganization.BackendRepository.BackendRelease.Companion.first import app.revanced.api.configuration.repository.ConfigurationRepository import app.revanced.api.configuration.schema.* -import app.revanced.library.PatchUtils -import app.revanced.patcher.PatchBundleLoader +import app.revanced.library.serializeTo +import app.revanced.patcher.patch.loadPatchesFromJar import com.github.benmanes.caffeine.cache.Caffeine import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext @@ -94,7 +94,7 @@ internal class PatchesService( configurationRepository.patches.publicKeyId, ) ) { - PatchBundleLoader.Jar(patchesFile) + loadPatchesFromJar(setOf(patchesFile)) } else { // Use an empty set of patches if the signature is invalid. emptySet() @@ -103,7 +103,7 @@ internal class PatchesService( patchesFile.delete() ByteArrayOutputStream().use { stream -> - PatchUtils.Json.serialize(patches, outputStream = stream) + patches.serializeTo(outputStream = stream) stream.toByteArray() }