Compare commits
13 commits
TheKodeToa
...
mistress
Author | SHA1 | Date | |
---|---|---|---|
Ecorous | 87b50e0a2e | ||
Ecorous | 5512c89bc6 | ||
Ecorous | 10a9a99651 | ||
Ecorous | 5fe594ed39 | ||
Ecorous | 21ed28b3a1 | ||
moehreag | 8427003d37 | ||
moehreag | ddd7bb6ba3 | ||
moehreag | e0e35eb97c | ||
moehreag | bc8a864fc1 | ||
moehreag | 76daadaebe | ||
moehreag | 3b8c103ec1 | ||
TheKodeToad | 596aa06809 | ||
Ecorous | 2c8016e77f |
|
@ -14,7 +14,7 @@ group = "dev.frogmc"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
|
|
||||||
application {
|
application {
|
||||||
mainClass.set("dev.frogmc.ApplicationKt")
|
mainClass.set("dev.frogmc.meta.ApplicationKt")
|
||||||
|
|
||||||
val isDevelopment: Boolean = project.ext.has("development")
|
val isDevelopment: Boolean = project.ext.has("development")
|
||||||
applicationDefaultJvmArgs = listOf("-Dio.ktor.development=$isDevelopment")
|
applicationDefaultJvmArgs = listOf("-Dio.ktor.development=$isDevelopment")
|
||||||
|
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
|
@ -1,5 +1,5 @@
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
|
|
@ -1,12 +1,15 @@
|
||||||
package dev.frogmc
|
package dev.frogmc.meta
|
||||||
|
|
||||||
import dev.frogmc.plugins.*
|
import dev.frogmc.meta.plugins.configureHTTP
|
||||||
|
import dev.frogmc.meta.plugins.configureRouting
|
||||||
|
import dev.frogmc.meta.plugins.configureSerialization
|
||||||
import io.ktor.server.application.*
|
import io.ktor.server.application.*
|
||||||
import io.ktor.server.engine.*
|
import io.ktor.server.engine.*
|
||||||
import io.ktor.server.netty.*
|
import io.ktor.server.netty.*
|
||||||
|
import org.slf4j.Logger
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
|
|
||||||
val logger = LoggerFactory.getLogger("FrogMC/Meta")
|
val logger: Logger = LoggerFactory.getLogger("FrogMC/Meta")
|
||||||
|
|
||||||
fun main() {
|
fun main() {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package dev.frogmc
|
package dev.frogmc.meta
|
||||||
|
|
||||||
object Config {
|
object Config {
|
||||||
val POSTGRES_DATABASE = getEnv("DATABASE", "frogmc")
|
val POSTGRES_DATABASE = getEnv("DATABASE", "frogmc")
|
||||||
|
@ -6,7 +6,7 @@ object Config {
|
||||||
val POSTGRES_PASSWORD = getEnv("PASSWORD", "example")
|
val POSTGRES_PASSWORD = getEnv("PASSWORD", "example")
|
||||||
val POSTGRES_HOST = getEnv("HOST", "localhost")
|
val POSTGRES_HOST = getEnv("HOST", "localhost")
|
||||||
val POSTGRES_PORT = getEnv("PORT", "5432")
|
val POSTGRES_PORT = getEnv("PORT", "5432")
|
||||||
val UPLOAD_SECRET = getEnv("UPLOAD_SECRET", "").toByteArray()
|
val UPLOAD_SECRET = getEnv("UPLOAD_SECRET", "")
|
||||||
|
|
||||||
private fun getEnv(key: String, default: String): String {
|
private fun getEnv(key: String, default: String): String {
|
||||||
return System.getenv("FROGMC_META_$key") ?: default
|
return System.getenv("FROGMC_META_$key") ?: default
|
|
@ -1,6 +1,8 @@
|
||||||
package dev.frogmc
|
package dev.frogmc.meta
|
||||||
|
|
||||||
import dev.frogmc.types.*
|
import dev.frogmc.meta.types.LibraryVersion
|
||||||
|
import dev.frogmc.meta.types.LibraryVersions
|
||||||
|
import dev.frogmc.meta.types.LoaderVersions
|
||||||
import org.jetbrains.exposed.sql.*
|
import org.jetbrains.exposed.sql.*
|
||||||
import org.jetbrains.exposed.sql.transactions.transaction
|
import org.jetbrains.exposed.sql.transactions.transaction
|
||||||
|
|
||||||
|
@ -21,8 +23,7 @@ object DB {
|
||||||
transaction(db) {
|
transaction(db) {
|
||||||
SchemaUtils.create(
|
SchemaUtils.create(
|
||||||
LoaderVersions,
|
LoaderVersions,
|
||||||
LibraryVersions,
|
LibraryVersions
|
||||||
File
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (db == null) {
|
if (db == null) {
|
|
@ -1,16 +1,20 @@
|
||||||
package dev.frogmc.plugins
|
package dev.frogmc.meta.plugins
|
||||||
|
|
||||||
import dev.frogmc.Config
|
import dev.frogmc.meta.Config
|
||||||
import io.ktor.http.*
|
import io.ktor.http.*
|
||||||
import io.ktor.server.application.*
|
import io.ktor.server.application.*
|
||||||
import io.ktor.server.response.*
|
import io.ktor.server.response.*
|
||||||
import java.nio.charset.StandardCharsets
|
|
||||||
import java.security.MessageDigest
|
import java.security.MessageDigest
|
||||||
|
|
||||||
val authPlugin = createRouteScopedPlugin("auth") {
|
val authPlugin = createRouteScopedPlugin("auth") {
|
||||||
onCall {
|
onCall {
|
||||||
val authorization = it.request.headers["Authorization"]
|
val authorization = it.request.headers["Authorization"]
|
||||||
if (authorization.isNullOrEmpty() || !MessageDigest.isEqual(authorization.toByteArray(), Config.UPLOAD_SECRET))
|
if (authorization.isNullOrEmpty() || !MessageDigest.isEqual(
|
||||||
it.respond(HttpStatusCode.Unauthorized);
|
authorization.toByteArray(),
|
||||||
|
Config.UPLOAD_SECRET.toByteArray()
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
it.respond(HttpStatusCode.Unauthorized)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,18 +1,16 @@
|
||||||
package dev.frogmc.plugins
|
package dev.frogmc.meta.plugins
|
||||||
|
|
||||||
import io.ktor.http.*
|
import io.ktor.http.*
|
||||||
import io.ktor.server.application.*
|
import io.ktor.server.application.*
|
||||||
import io.ktor.server.plugins.cors.routing.*
|
import io.ktor.server.plugins.cors.routing.*
|
||||||
import io.ktor.server.response.*
|
|
||||||
|
|
||||||
fun Application.configureHTTP() {
|
fun Application.configureHTTP() {
|
||||||
install(CORS) {
|
install(CORS) {
|
||||||
|
anyHost()
|
||||||
allowMethod(HttpMethod.Options)
|
allowMethod(HttpMethod.Options)
|
||||||
allowMethod(HttpMethod.Put)
|
allowMethod(HttpMethod.Put)
|
||||||
allowMethod(HttpMethod.Delete)
|
allowMethod(HttpMethod.Delete)
|
||||||
allowMethod(HttpMethod.Patch)
|
allowMethod(HttpMethod.Patch)
|
||||||
allowHeader(HttpHeaders.Authorization)
|
allowHeader(HttpHeaders.Authorization)
|
||||||
allowHeader("MyCustomHeader")
|
|
||||||
anyHost() // @TODO: Don't do this in production if possible. Try to limit it.
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,21 +1,19 @@
|
||||||
package dev.frogmc.plugins
|
package dev.frogmc.meta.plugins
|
||||||
|
|
||||||
import dev.frogmc.DB
|
import dev.frogmc.meta.DB
|
||||||
import dev.frogmc.types.LoaderVersion
|
import dev.frogmc.meta.types.LoaderVersion
|
||||||
import dev.frogmc.types.PartialLoaderVersion
|
import dev.frogmc.meta.types.PartialLoaderVersion
|
||||||
import dev.frogmc.types.LoaderVersions
|
import dev.frogmc.meta.types.LoaderVersions
|
||||||
import dev.frogmc.types.ModrinthVersion
|
import dev.frogmc.meta.types.ModrinthVersion
|
||||||
import io.ktor.client.*
|
import io.ktor.client.*
|
||||||
import io.ktor.client.call.*
|
import io.ktor.client.call.*
|
||||||
import io.ktor.client.request.*
|
import io.ktor.client.request.*
|
||||||
import io.ktor.http.*
|
import io.ktor.http.*
|
||||||
import io.ktor.server.application.*
|
import io.ktor.server.application.*
|
||||||
import io.ktor.server.html.*
|
|
||||||
import io.ktor.server.http.content.*
|
import io.ktor.server.http.content.*
|
||||||
import io.ktor.server.request.*
|
import io.ktor.server.request.*
|
||||||
import io.ktor.server.response.*
|
import io.ktor.server.response.*
|
||||||
import io.ktor.server.routing.*
|
import io.ktor.server.routing.*
|
||||||
import kotlinx.html.*
|
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import org.jetbrains.exposed.exceptions.ExposedSQLException
|
import org.jetbrains.exposed.exceptions.ExposedSQLException
|
||||||
import org.jetbrains.exposed.sql.*
|
import org.jetbrains.exposed.sql.*
|
||||||
|
@ -31,7 +29,7 @@ fun Application.configureRouting() {
|
||||||
call.respond(transaction(DB.db) {
|
call.respond(transaction(DB.db) {
|
||||||
LoaderVersions
|
LoaderVersions
|
||||||
.select(LoaderVersions.version, LoaderVersions.releaseDate)
|
.select(LoaderVersions.version, LoaderVersions.releaseDate)
|
||||||
.orderBy(LoaderVersions.releaseDate to SortOrder.ASC)
|
.orderBy(LoaderVersions.releaseDate to SortOrder.DESC)
|
||||||
.map {
|
.map {
|
||||||
PartialLoaderVersion(
|
PartialLoaderVersion(
|
||||||
it[LoaderVersions.version],
|
it[LoaderVersions.version],
|
||||||
|
@ -40,7 +38,7 @@ fun Application.configureRouting() {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
get("/versions/latest") {
|
get("/version/latest") {
|
||||||
val row = transaction(DB.db) {
|
val row = transaction(DB.db) {
|
||||||
LoaderVersions
|
LoaderVersions
|
||||||
.selectAll()
|
.selectAll()
|
||||||
|
@ -61,7 +59,7 @@ fun Application.configureRouting() {
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
get("/versions/{version}") {
|
get("/version/{version}") {
|
||||||
val version = call.parameters["version"] ?: return@get call.respond(HttpStatusCode.BadRequest)
|
val version = call.parameters["version"] ?: return@get call.respond(HttpStatusCode.BadRequest)
|
||||||
val row = transaction {
|
val row = transaction {
|
||||||
LoaderVersions
|
LoaderVersions
|
||||||
|
@ -83,7 +81,7 @@ fun Application.configureRouting() {
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
route("/versions/upload") {
|
route("/version/upload") {
|
||||||
install(authPlugin)
|
install(authPlugin)
|
||||||
post {
|
post {
|
||||||
val versionObj = call.receive<LoaderVersion>()
|
val versionObj = call.receive<LoaderVersion>()
|
||||||
|
@ -103,7 +101,7 @@ fun Application.configureRouting() {
|
||||||
call.respond(HttpStatusCode.OK)
|
call.respond(HttpStatusCode.OK)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
route("/versions/delete/{version}") {
|
route("/version/delete/{version}") {
|
||||||
install(authPlugin)
|
install(authPlugin)
|
||||||
delete {
|
delete {
|
||||||
val version =
|
val version =
|
||||||
|
@ -126,11 +124,11 @@ fun Application.configureRouting() {
|
||||||
val versions = DB.getLibraryVersions()
|
val versions = DB.getLibraryVersions()
|
||||||
call.respond(versions)
|
call.respond(versions)
|
||||||
}
|
}
|
||||||
get("/versions/latest") {
|
get("/version/latest") {
|
||||||
val versions = DB.getLibraryVersions()
|
val versions = DB.getLibraryVersions()
|
||||||
call.respond(versions.first())
|
call.respond(versions.first())
|
||||||
}
|
}
|
||||||
get("/versions/{version}/download") {
|
get("/version/{version}/download") {
|
||||||
val version = call.parameters["version"] ?: return@get call.respond(
|
val version = call.parameters["version"] ?: return@get call.respond(
|
||||||
HttpStatusCode.BadRequest,
|
HttpStatusCode.BadRequest,
|
||||||
"message" to "Invalid version."
|
"message" to "Invalid version."
|
||||||
|
@ -152,7 +150,7 @@ fun Application.configureRouting() {
|
||||||
val mrVersion = json.decodeFromString<ModrinthVersion>(mrVersionString)
|
val mrVersion = json.decodeFromString<ModrinthVersion>(mrVersionString)
|
||||||
call.respond(mrVersion.files[0])
|
call.respond(mrVersion.files[0])
|
||||||
}
|
}
|
||||||
get("/versions/{version}") {
|
get("/version/{version}") {
|
||||||
val version = call.parameters["version"] ?: return@get call.respond(
|
val version = call.parameters["version"] ?: return@get call.respond(
|
||||||
HttpStatusCode.BadRequest,
|
HttpStatusCode.BadRequest,
|
||||||
"message" to "Invalid version."
|
"message" to "Invalid version."
|
|
@ -1,4 +1,4 @@
|
||||||
package dev.frogmc.plugins
|
package dev.frogmc.meta.plugins
|
||||||
|
|
||||||
import io.ktor.serialization.kotlinx.json.*
|
import io.ktor.serialization.kotlinx.json.*
|
||||||
import io.ktor.server.application.*
|
import io.ktor.server.application.*
|
|
@ -1,4 +1,4 @@
|
||||||
package dev.frogmc.types
|
package dev.frogmc.meta.types
|
||||||
|
|
||||||
import kotlinx.datetime.LocalDateTime
|
import kotlinx.datetime.LocalDateTime
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
|
@ -1,4 +1,4 @@
|
||||||
package dev.frogmc.types
|
package dev.frogmc.meta.types
|
||||||
|
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import org.jetbrains.exposed.sql.Table
|
import org.jetbrains.exposed.sql.Table
|
||||||
|
@ -19,9 +19,3 @@ object LibraryVersions : Table() {
|
||||||
override val primaryKey = PrimaryKey(version)
|
override val primaryKey = PrimaryKey(version)
|
||||||
}
|
}
|
||||||
|
|
||||||
object File : Table() {
|
|
||||||
val version = text("version")
|
|
||||||
val sha1 = text("sha1")
|
|
||||||
val size = text("size")
|
|
||||||
val url = text("url")
|
|
||||||
}
|
|
|
@ -1,14 +1,24 @@
|
||||||
import ch.qos.logback.core.joran.spi.ConsoleTarget
|
import ch.qos.logback.core.joran.spi.ConsoleTarget
|
||||||
import ch.qos.logback.core.ConsoleAppender
|
import ch.qos.logback.core.ConsoleAppender
|
||||||
|
import ch.qos.logback.core.FileAppender
|
||||||
|
|
||||||
def environment = System.getenv("ENVIRONMENT") ?: "production"
|
def environment = System.getenv("ENVIRONMENT") ?: "production"
|
||||||
|
|
||||||
def defaultLevel = INFO
|
def defaultLevel = INFO
|
||||||
def defaultTarget = ConsoleTarget.SystemErr
|
def defaultTarget = ConsoleTarget.SystemErr
|
||||||
|
|
||||||
|
appender("FILE", FileAppender) {
|
||||||
|
file = System.getenv("FROGMC_META_LOG") ?: "logs_meta.log"
|
||||||
|
append = true
|
||||||
|
|
||||||
|
encoder(PatternLayoutEncoder) {
|
||||||
|
pattern = "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{35} - %msg%n"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
appender("CONSOLE", ConsoleAppender) {
|
appender("CONSOLE", ConsoleAppender) {
|
||||||
encoder(PatternLayoutEncoder) {
|
encoder(PatternLayoutEncoder) {
|
||||||
pattern = "WAWA:: %boldGreen(%d{yyyy-MM-dd}) %boldYellow(%d{HH:mm:ss}) %gray(|) %highlight(%5level) %gray(|) %boldMagenta(%40.40logger{40}) %gray(|) %msg%n"
|
pattern = "%boldGreen(%d{yyyy-MM-dd}) %boldYellow(%d{HH:mm:ss}) %gray(|) %highlight(%5level) %gray(|) %boldMagenta(%40.40logger{40}) %gray(|) %msg%n"
|
||||||
|
|
||||||
withJansi = true
|
withJansi = true
|
||||||
}
|
}
|
||||||
|
@ -16,4 +26,4 @@ appender("CONSOLE", ConsoleAppender) {
|
||||||
target = defaultTarget
|
target = defaultTarget
|
||||||
}
|
}
|
||||||
|
|
||||||
root(defaultLevel, ["CONSOLE"])
|
root(defaultLevel, ["CONSOLE", "FILE"])
|
||||||
|
|
|
@ -17,6 +17,7 @@ importsAcceptList = [
|
||||||
|
|
||||||
'ch.qos.logback.core.BasicStatusManager',
|
'ch.qos.logback.core.BasicStatusManager',
|
||||||
'ch.qos.logback.core.ConsoleAppender',
|
'ch.qos.logback.core.ConsoleAppender',
|
||||||
|
'ch.qos.logback.core.FileAppender',
|
||||||
'ch.qos.logback.core.hook.ShutdownHook',
|
'ch.qos.logback.core.hook.ShutdownHook',
|
||||||
'ch.qos.logback.core.hook.ShutdownHookBase',
|
'ch.qos.logback.core.hook.ShutdownHookBase',
|
||||||
'ch.qos.logback.core.hook.DelayingShutdownHook',
|
'ch.qos.logback.core.hook.DelayingShutdownHook',
|
||||||
|
|
|
@ -18,28 +18,28 @@
|
||||||
</div>
|
</div>
|
||||||
<br>
|
<br>
|
||||||
<div class="route">
|
<div class="route">
|
||||||
<code>GET /v1/loader/versions/latest</code>
|
<code>GET /v1/loader/version/latest</code>
|
||||||
<p>Fetches the latest version of FrogMC loader.</p>
|
<p>Fetches the latest version of FrogMC loader.</p>
|
||||||
</div>
|
</div>
|
||||||
<br>
|
<br>
|
||||||
<div class="route">
|
<div class="route">
|
||||||
<code>GET /v1/loader/versions/{version}</code>
|
<code>GET /v1/loader/version/{version}</code>
|
||||||
<p>Fetches a specific version of FrogMC loader.</p>
|
<p>Fetches a specific version of FrogMC loader.</p>
|
||||||
</div>
|
</div>
|
||||||
<br>
|
<br>
|
||||||
<div class="route">
|
<div class="route">
|
||||||
<code>GET /v1/loader/versions/{version}/download</code>
|
<code>GET /v1/loader/version/{version}/download</code>
|
||||||
<p>Fetches the download URL of a specific version of FrogMC loader.</p>
|
<p>Fetches the download URL of a specific version of FrogMC loader.</p>
|
||||||
</div>
|
</div>
|
||||||
<br>
|
<br>
|
||||||
<div class="route">
|
<div class="route">
|
||||||
<code>POST /v1/loader/versions/upload</code>
|
<code>POST /v1/loader/version/upload</code>
|
||||||
<p>Uploads a version of the loader.</p>
|
<p>Uploads a version of the loader.</p>
|
||||||
<p><strong>This endpoint requires authorization.</strong></p>
|
<p><strong>This endpoint requires authorization.</strong></p>
|
||||||
</div>
|
</div>
|
||||||
<br>
|
<br>
|
||||||
<div class="route">
|
<div class="route">
|
||||||
<code>DELETE /v1/loader/versions/delete/{version}</code>
|
<code>DELETE /v1/loader/version/delete/{version}</code>
|
||||||
<p>Deletes a version of the loader.</p>
|
<p>Deletes a version of the loader.</p>
|
||||||
<p><strong>This endpoint requires authorization.</strong></p>
|
<p><strong>This endpoint requires authorization.</strong></p>
|
||||||
</div>
|
</div>
|
||||||
|
@ -50,17 +50,17 @@
|
||||||
</div>
|
</div>
|
||||||
<br>
|
<br>
|
||||||
<div class="route">
|
<div class="route">
|
||||||
<code>GET /v1/library/versions/latest</code>
|
<code>GET /v1/library/version/latest</code>
|
||||||
<p>Fetches the latest version of FrogMC library.</p>
|
<p>Fetches the latest version of FrogMC library.</p>
|
||||||
</div>
|
</div>
|
||||||
<br>
|
<br>
|
||||||
<div class="route">
|
<div class="route">
|
||||||
<code>GET /v1/library/versions/{version}</code>
|
<code>GET /v1/library/version/{version}</code>
|
||||||
<p>Fetches a specific version of FrogMC library.</p>
|
<p>Fetches a specific version of FrogMC library.</p>
|
||||||
</div>
|
</div>
|
||||||
<br>
|
<br>
|
||||||
<div class="route">
|
<div class="route">
|
||||||
<code>GET /v1/library/versions/{version}/download</code>
|
<code>GET /v1/library/version/{version}/download</code>
|
||||||
<p>Fetches the download URL of a specific version of FrogMC library.</p>
|
<p>Fetches the download URL of a specific version of FrogMC library.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in a new issue