diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..2d6d258 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,3 @@ +[*.{kt,kts}] +ktlint_code_style = intellij_idea +ktlint_standard_no-wildcard-imports = disabled \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/api/backend/Backend.kt b/src/main/kotlin/app/revanced/api/backend/Backend.kt index e231949..c3e53c1 100644 --- a/src/main/kotlin/app/revanced/api/backend/Backend.kt +++ b/src/main/kotlin/app/revanced/api/backend/Backend.kt @@ -11,7 +11,7 @@ import kotlinx.serialization.Serializable * @param httpClientConfig The configuration of the HTTP client. */ abstract class Backend( - httpClientConfig: HttpClientConfig.() -> Unit = {} + httpClientConfig: HttpClientConfig.() -> Unit = {}, ) { protected val client: HttpClient = HttpClient(OkHttp, httpClientConfig) @@ -34,7 +34,7 @@ abstract class Backend( * @property members The members of the organization. */ class BackendOrganization( - val members: Set + val members: Set, ) { /** * A member of an organization. @@ -46,12 +46,12 @@ abstract class Backend( * @property gpgKeysUrl The URL to the GPG keys of the member. */ @Serializable - class BackendMember ( + class BackendMember( override val name: String, override val avatarUrl: String, override val url: String, val bio: String?, - val gpgKeysUrl: String + val gpgKeysUrl: String, ) : BackendUser /** @@ -60,7 +60,7 @@ abstract class Backend( * @property contributors The contributors of the repository. */ class BackendRepository( - val contributors: Set + val contributors: Set, ) { /** * A contributor of a repository. @@ -75,7 +75,7 @@ abstract class Backend( override val name: String, override val avatarUrl: String, override val url: String, - val contributions: Int + val contributions: Int, ) : BackendUser /** @@ -91,7 +91,7 @@ abstract class Backend( val tag: String, val releaseNote: String, val createdAt: LocalDateTime, - val assets: Set + val assets: Set, ) { /** * An asset of a release. @@ -100,7 +100,7 @@ abstract class Backend( */ @Serializable class BackendAsset( - val downloadUrl: String + val downloadUrl: String, ) } } diff --git a/src/main/kotlin/app/revanced/api/backend/github/GitHubBackend.kt b/src/main/kotlin/app/revanced/api/backend/github/GitHubBackend.kt index 49d4c32..be0db97 100644 --- a/src/main/kotlin/app/revanced/api/backend/github/GitHubBackend.kt +++ b/src/main/kotlin/app/revanced/api/backend/github/GitHubBackend.kt @@ -1,7 +1,18 @@ package app.revanced.api.backend.github import app.revanced.api.backend.Backend +import app.revanced.api.backend.Backend.BackendOrganization.BackendMember +import app.revanced.api.backend.Backend.BackendOrganization.BackendRepository.BackendContributor +import app.revanced.api.backend.Backend.BackendOrganization.BackendRepository.BackendRelease +import app.revanced.api.backend.Backend.BackendOrganization.BackendRepository.BackendRelease.BackendAsset import app.revanced.api.backend.github.api.Request +import app.revanced.api.backend.github.api.Request.Organization.Members +import app.revanced.api.backend.github.api.Request.Organization.Repository.Contributors +import app.revanced.api.backend.github.api.Request.Organization.Repository.Releases +import app.revanced.api.backend.github.api.Response +import app.revanced.api.backend.github.api.Response.GitHubOrganization.GitHubMember +import app.revanced.api.backend.github.api.Response.GitHubOrganization.GitHubRepository.GitHubContributor +import app.revanced.api.backend.github.api.Response.GitHubOrganization.GitHubRepository.GitHubRelease import io.ktor.client.call.* import io.ktor.client.plugins.* import io.ktor.client.plugins.auth.* @@ -9,17 +20,6 @@ import io.ktor.client.plugins.auth.providers.* import io.ktor.client.plugins.cache.* import io.ktor.client.plugins.contentnegotiation.* import io.ktor.client.plugins.resources.* -import app.revanced.api.backend.Backend.BackendOrganization.BackendMember -import app.revanced.api.backend.Backend.BackendOrganization.BackendRepository.BackendRelease -import app.revanced.api.backend.Backend.BackendOrganization.BackendRepository.BackendContributor -import app.revanced.api.backend.Backend.BackendOrganization.BackendRepository.BackendRelease.BackendAsset -import app.revanced.api.backend.github.api.Request.Organization.Repository.Releases -import app.revanced.api.backend.github.api.Request.Organization.Repository.Contributors -import app.revanced.api.backend.github.api.Request.Organization.Members -import app.revanced.api.backend.github.api.Response -import app.revanced.api.backend.github.api.Response.GitHubOrganization.GitHubRepository.GitHubRelease -import app.revanced.api.backend.github.api.Response.GitHubOrganization.GitHubRepository.GitHubContributor -import app.revanced.api.backend.github.api.Response.GitHubOrganization.GitHubMember import io.ktor.client.plugins.resources.Resources import io.ktor.serialization.kotlinx.json.* import kotlinx.coroutines.* @@ -28,18 +28,18 @@ import kotlinx.datetime.toLocalDateTime import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonNamingStrategy -import org.koin.dsl.bind -import org.koin.dsl.module @OptIn(ExperimentalSerializationApi::class) class GitHubBackend(token: String? = null) : Backend({ install(HttpCache) install(Resources) install(ContentNegotiation) { - json(Json { - ignoreUnknownKeys = true - namingStrategy = JsonNamingStrategy.SnakeCase - }) + json( + Json { + ignoreUnknownKeys = true + namingStrategy = JsonNamingStrategy.SnakeCase + }, + ) } defaultRequest { url("https://api.github.com") } @@ -50,7 +50,7 @@ class GitHubBackend(token: String? = null) : Backend({ loadTokens { BearerTokens( accessToken = it, - refreshToken = "" // Required dummy value + refreshToken = "", // Required dummy value ) } @@ -64,11 +64,11 @@ class GitHubBackend(token: String? = null) : Backend({ repository: String, tag: String?, ): BackendRelease { - val release: GitHubRelease = if (tag != null) + val release: GitHubRelease = if (tag != null) { client.get(Releases.Tag(owner, repository, tag)).body() - else + } else { client.get(Releases.Latest(owner, repository)).body() - + } return BackendRelease( tag = release.tagName, @@ -76,13 +76,13 @@ class GitHubBackend(token: String? = null) : Backend({ createdAt = release.createdAt.toLocalDateTime(TimeZone.UTC), assets = release.assets.map { BackendAsset(downloadUrl = it.browserDownloadUrl) - }.toSet() + }.toSet(), ) } override suspend fun getContributors( owner: String, - repository: String + repository: String, ): Set { val contributors: Set = client.get(Contributors(owner, repository)).body() @@ -91,7 +91,7 @@ class GitHubBackend(token: String? = null) : Backend({ name = it.login, avatarUrl = it.avatarUrl, url = it.url, - contributions = it.contributions + contributions = it.contributions, ) }.toSet() } diff --git a/src/main/kotlin/app/revanced/api/backend/github/api/RequestResource.kt b/src/main/kotlin/app/revanced/api/backend/github/api/RequestResource.kt index ab78199..dc480ba 100644 --- a/src/main/kotlin/app/revanced/api/backend/github/api/RequestResource.kt +++ b/src/main/kotlin/app/revanced/api/backend/github/api/RequestResource.kt @@ -5,6 +5,7 @@ import io.ktor.resources.* class Request { @Resource("/users/{username}") class User(val username: String) + class Organization { @Resource("/orgs/{org}/members") class Members(val org: String) diff --git a/src/main/kotlin/app/revanced/api/backend/github/api/ResponseSchema.kt b/src/main/kotlin/app/revanced/api/backend/github/api/ResponseSchema.kt index fbfd89a..693a897 100644 --- a/src/main/kotlin/app/revanced/api/backend/github/api/ResponseSchema.kt +++ b/src/main/kotlin/app/revanced/api/backend/github/api/ResponseSchema.kt @@ -3,7 +3,6 @@ package app.revanced.api.backend.github.api import kotlinx.datetime.Instant import kotlinx.serialization.Serializable - class Response { interface IGitHubUser { val login: String @@ -12,7 +11,7 @@ class Response { } @Serializable - class GitHubUser ( + class GitHubUser( override val login: String, override val avatarUrl: String, override val url: String, @@ -41,11 +40,11 @@ class Response { val tagName: String, val assets: Set, val createdAt: Instant, - val body: String + val body: String, ) { @Serializable class GitHubAsset( - val browserDownloadUrl: String + val browserDownloadUrl: String, ) } } diff --git a/src/main/kotlin/app/revanced/api/command/MainCommand.kt b/src/main/kotlin/app/revanced/api/command/MainCommand.kt index 3038e96..6b77387 100644 --- a/src/main/kotlin/app/revanced/api/command/MainCommand.kt +++ b/src/main/kotlin/app/revanced/api/command/MainCommand.kt @@ -31,4 +31,4 @@ private object CLIVersionProvider : CommandLine.IVersionProvider { StartAPICommand::class, ], ) -private object MainCommand \ No newline at end of file +private object MainCommand diff --git a/src/main/kotlin/app/revanced/api/modules/Database.kt b/src/main/kotlin/app/revanced/api/modules/Database.kt index fcd0955..4727a0b 100644 --- a/src/main/kotlin/app/revanced/api/modules/Database.kt +++ b/src/main/kotlin/app/revanced/api/modules/Database.kt @@ -1,11 +1,10 @@ package app.revanced.api.modules import app.revanced.api.modules.AnnouncementService.Attachments.announcement -import app.revanced.api.schema.APIResponseAnnouncement import app.revanced.api.schema.APIAnnouncement import app.revanced.api.schema.APILatestAnnouncement +import app.revanced.api.schema.APIResponseAnnouncement import kotlinx.datetime.* -import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.dao.IntEntity import org.jetbrains.exposed.dao.IntEntityClass import org.jetbrains.exposed.dao.id.EntityID @@ -13,7 +12,7 @@ import org.jetbrains.exposed.dao.id.IntIdTable import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq import org.jetbrains.exposed.sql.kotlin.datetime.datetime - +import org.jetbrains.exposed.sql.transactions.transaction class AnnouncementService(private val database: Database) { private object Announcements : IntIdTable() { @@ -52,7 +51,7 @@ class AnnouncementService(private val database: Database) { channel, createdAt, archivedAt, - level + level, ) } @@ -107,7 +106,7 @@ class AnnouncementService(private val database: Database) { fun archive( id: Int, - archivedAt: LocalDateTime? + archivedAt: LocalDateTime?, ) = transaction { Announcement.findById(id)?.apply { this.archivedAt = archivedAt ?: java.time.LocalDateTime.now().toKotlinLocalDateTime() diff --git a/src/main/kotlin/app/revanced/api/modules/Dependencies.kt b/src/main/kotlin/app/revanced/api/modules/Dependencies.kt index 3910400..cf8afe0 100644 --- a/src/main/kotlin/app/revanced/api/modules/Dependencies.kt +++ b/src/main/kotlin/app/revanced/api/modules/Dependencies.kt @@ -19,7 +19,7 @@ fun Application.configureDependencies() { globalModule, gitHubBackendModule, databaseModule, - authModule + authModule, ) } } @@ -51,7 +51,7 @@ val databaseModule = module { url = dotenv["DB_URL"], user = dotenv["DB_USER"], password = dotenv["DB_PASSWORD"], - driver = "org.h2.Driver" + driver = "org.h2.Driver", ) } factory { diff --git a/src/main/kotlin/app/revanced/api/modules/Routing.kt b/src/main/kotlin/app/revanced/api/modules/Routing.kt index f39d7e7..846bfc2 100644 --- a/src/main/kotlin/app/revanced/api/modules/Routing.kt +++ b/src/main/kotlin/app/revanced/api/modules/Routing.kt @@ -23,41 +23,68 @@ fun Application.configureRouting() { routing { route("/v${configuration.apiVersion}") { route("/announcements") { - suspend fun PipelineContext<*, ApplicationCall>.announcement( - block: AnnouncementService.() -> APIResponseAnnouncement? - ) = announcementService.block()?.let { call.respond(it) } - ?: call.respond(HttpStatusCode.NotFound) + suspend fun PipelineContext<*, ApplicationCall>.announcement(block: AnnouncementService.() -> APIResponseAnnouncement?) = + announcementService.block()?.let { call.respond(it) } + ?: call.respond(HttpStatusCode.NotFound) - suspend fun PipelineContext<*, ApplicationCall>.announcementId( - block: AnnouncementService.() -> APILatestAnnouncement? - ) = announcementService.block()?.let { call.respond(it) } - ?: call.respond(HttpStatusCode.NotFound) + suspend fun PipelineContext<*, ApplicationCall>.announcementId(block: AnnouncementService.() -> APILatestAnnouncement?) = + announcementService.block()?.let { call.respond(it) } + ?: call.respond(HttpStatusCode.NotFound) suspend fun PipelineContext<*, ApplicationCall>.channel(block: suspend (String) -> Unit) = block(call.parameters["channel"]!!) route("/{channel}/latest") { - get("/id") { channel { announcementId { latestId(it) } } } + get("/id") { + channel { + announcementId { + latestId(it) + } + } + } - get { channel { announcement { latest(it) } } } + get { + channel { + announcement { + latest(it) + } + } + } } - get("/{channel}") { channel { call.respond(announcementService.read(it)) } } + get("/{channel}") { + channel { + call.respond(announcementService.read(it)) + } + } route("/latest") { - get("/id") { announcementId { latestId() } } + get("/id") { + announcementId { + latestId() + } + } - get { announcement { latest() } } + get { + announcement { + latest() + } + } } - get { call.respond(announcementService.read()) } + get { + call.respond(announcementService.read()) + } authenticate("jwt") { suspend fun PipelineContext<*, ApplicationCall>.id(block: suspend (Int) -> Unit) = - call.parameters["id"]!!.toIntOrNull()?.let { block(it) } - ?: call.respond(HttpStatusCode.BadRequest) + call.parameters["id"]!!.toIntOrNull()?.let { + block(it) + } ?: call.respond(HttpStatusCode.BadRequest) - post { announcementService.new(call.receive()) } + post { + announcementService.new(call.receive()) + } post("/{id}/archive") { id { @@ -66,11 +93,23 @@ fun Application.configureRouting() { } } - post("/{id}/unarchive") { id { announcementService.unarchive(it) } } + post("/{id}/unarchive") { + id { + announcementService.unarchive(it) + } + } - patch("/{id}") { id { announcementService.update(it, call.receive()) } } + patch("/{id}") { + id { + announcementService.update(it, call.receive()) + } + } - delete("/{id}") { id { announcementService.delete(it) } } + delete("/{id}") { + id { + announcementService.delete(it) + } + } } } @@ -78,20 +117,23 @@ fun Application.configureRouting() { route("latest") { get { val patches = backend.getRelease(configuration.organization, configuration.patchesRepository) - val integrations = configuration.integrationsRepositoryNames.map { - async { backend.getRelease(configuration.organization, it) } - }.awaitAll() + val integrations = + configuration.integrationsRepositoryNames.map { + async { backend.getRelease(configuration.organization, it) } + }.awaitAll() - val assets = (patches.assets + integrations.flatMap { it.assets }).filter { - it.downloadUrl.endsWith(".apk") || it.downloadUrl.endsWith(".jar") - }.map { APIAsset(it.downloadUrl) }.toSet() + val assets = + (patches.assets + integrations.flatMap { it.assets }).filter { + it.downloadUrl.endsWith(".apk") || it.downloadUrl.endsWith(".jar") + }.map { APIAsset(it.downloadUrl) }.toSet() - val release = APIRelease( - patches.tag, - patches.createdAt, - patches.releaseNote, - assets - ) + val release = + APIRelease( + patches.tag, + patches.createdAt, + patches.releaseNote, + assets, + ) call.respond(release) } @@ -112,24 +154,26 @@ fun Application.configureRouting() { } get("/contributors") { - val contributors = configuration.contributorsRepositoryNames.map { - async { - APIContributable( - it, - backend.getContributors(configuration.organization, it).map { - APIContributor(it.name, it.avatarUrl, it.url, it.contributions) - }.toSet() - ) - } - }.awaitAll() + val contributors = + configuration.contributorsRepositoryNames.map { + async { + APIContributable( + it, + backend.getContributors(configuration.organization, it).map { + APIContributor(it.name, it.avatarUrl, it.url, it.contributions) + }.toSet(), + ) + } + }.awaitAll() call.respond(contributors) } get("/team") { - val team = backend.getMembers(configuration.organization).map { - APIMember(it.name, it.avatarUrl, it.url, it.gpgKeysUrl) - } + val team = + backend.getMembers(configuration.organization).map { + APIMember(it.name, it.avatarUrl, it.url, it.gpgKeysUrl) + } call.respond(team) } @@ -140,7 +184,11 @@ fun Application.configureRouting() { } } - authenticate("basic") { get("/token") { call.respond(authService.newToken()) } } + authenticate("basic") { + get("/token") { + call.respond(authService.newToken()) + } + } } } } diff --git a/src/main/kotlin/app/revanced/api/modules/Security.kt b/src/main/kotlin/app/revanced/api/modules/Security.kt index 0dd2dcb..02cc5f3 100644 --- a/src/main/kotlin/app/revanced/api/modules/Security.kt +++ b/src/main/kotlin/app/revanced/api/modules/Security.kt @@ -22,7 +22,7 @@ class AuthService( verifier( JWT.require(Algorithm.HMAC256(jwtSecret)) .withIssuer(issuer) - .build() + .build(), ) validate { credential -> JWTPrincipal(credential.payload) } } @@ -50,4 +50,4 @@ class AuthService( fun Application.configureSecurity() { val configureSecurity = get().configureSecurity configureSecurity() -} \ No newline at end of file +} diff --git a/src/main/kotlin/app/revanced/api/schema/APISchema.kt b/src/main/kotlin/app/revanced/api/schema/APISchema.kt index 295278a..fe3e2c9 100644 --- a/src/main/kotlin/app/revanced/api/schema/APISchema.kt +++ b/src/main/kotlin/app/revanced/api/schema/APISchema.kt @@ -8,7 +8,7 @@ class APIRelease( val version: String, val createdAt: LocalDateTime, val changelog: String, - val assets: Set + val assets: Set, ) interface APIUser { @@ -22,7 +22,7 @@ class APIMember( override val name: String, override val avatarUrl: String, override val url: String, - val gpgKeysUrl: String + val gpgKeysUrl: String, ) : APIUser @Serializable @@ -36,7 +36,7 @@ class APIContributor( @Serializable class APIContributable( val name: String, - val contributors: Set + val contributors: Set, ) @Serializable @@ -52,7 +52,7 @@ class APIAsset( @Serializable class APIReleaseVersion( - val version: String + val version: String, ) @Serializable @@ -63,7 +63,7 @@ class APIAnnouncement( val attachmentUrls: Set = emptySet(), val channel: String? = null, val archivedAt: LocalDateTime? = null, - val level: Int = 0 + val level: Int = 0, ) @Serializable @@ -76,15 +76,15 @@ class APIResponseAnnouncement( val channel: String? = null, val createdAt: LocalDateTime, val archivedAt: LocalDateTime? = null, - val level: Int = 0 + val level: Int = 0, ) @Serializable class APILatestAnnouncement( - val id: Int + val id: Int, ) @Serializable class APIAnnouncementArchivedAt( - val archivedAt: LocalDateTime -) \ No newline at end of file + val archivedAt: LocalDateTime, +) diff --git a/src/main/kotlin/app/revanced/api/schema/ConfigurationSchema.kt b/src/main/kotlin/app/revanced/api/schema/ConfigurationSchema.kt index db90377..5eff0b6 100644 --- a/src/main/kotlin/app/revanced/api/schema/ConfigurationSchema.kt +++ b/src/main/kotlin/app/revanced/api/schema/ConfigurationSchema.kt @@ -13,5 +13,5 @@ class APIConfiguration( @SerialName("contributors-repositories") val contributorsRepositoryNames: Set, @SerialName("api-version") - val apiVersion: Int = 1 -) \ No newline at end of file + val apiVersion: Int = 1, +) diff --git a/src/test/kotlin/app/revanced/ApplicationTest.kt b/src/test/kotlin/app/revanced/ApplicationTest.kt index 51682fe..5f1ce2e 100644 --- a/src/test/kotlin/app/revanced/ApplicationTest.kt +++ b/src/test/kotlin/app/revanced/ApplicationTest.kt @@ -23,8 +23,8 @@ class ApplicationTest { organization = "ReVanced", patchesRepository = "", integrationsRepositoryNames = setOf(), - contributorsRepositoryNames = setOf() - ) + contributorsRepositoryNames = setOf(), + ), ).let(::writeText) deleteOnExit() @@ -47,7 +47,7 @@ class ApplicationTest { headers { append( HttpHeaders.Authorization, - "Basic ${"${dotenv["BASIC_USERNAME"]}:${dotenv["BASIC_PASSWORD"]}".encodeBase64()}" + "Basic ${"${dotenv["BASIC_USERNAME"]}:${dotenv["BASIC_PASSWORD"]}".encodeBase64()}", ) } }.bodyAsText()