Add endpoint for exporting all keys in bulk

Signed-off-by: Graham <gpe@openrs2.org>
Graham 4 years ago
parent 93ee863e20
commit 4337020b6b
  1. 52
      archive/src/main/kotlin/org/openrs2/archive/key/KeyExporter.kt
  2. 26
      archive/src/main/kotlin/org/openrs2/archive/web/KeysController.kt
  3. 4
      archive/src/main/kotlin/org/openrs2/archive/web/WebServer.kt
  4. 35
      archive/src/main/resources/org/openrs2/archive/templates/keys/index.html
  5. 3
      archive/src/main/resources/org/openrs2/archive/templates/layout.html

@ -9,16 +9,45 @@ import javax.inject.Singleton
public class KeyExporter @Inject constructor( public class KeyExporter @Inject constructor(
private val database: Database private val database: Database
) { ) {
public suspend fun exportValid(): List<XteaKey> { public suspend fun count(): Pair<Long, Long> {
return database.execute { connection -> return database.execute { connection ->
connection.prepareStatement( connection.prepareStatement(
""" """
SELECT (k.key).k0, (k.key).k1, (k.key).k2, (k.key).k3 SELECT
COUNT(DISTINCT k.id),
COUNT(DISTINCT k.id) FILTER (WHERE c.key_id IS NOT NULL)
FROM keys k FROM keys k
JOIN containers c ON c.key_id = k.id LEFT JOIN containers c ON c.key_id = k.id
ORDER BY k.id ASC
""".trimIndent() """.trimIndent()
).use { stmt -> ).use { stmt ->
stmt.executeQuery().use { rows ->
check(rows.next())
val all = rows.getLong(1)
val valid = rows.getLong(2)
Pair(all, valid)
}
}
}
}
public suspend fun exportAll(): List<XteaKey> {
return export(validOnly = false)
}
public suspend fun exportValid(): List<XteaKey> {
return export(validOnly = true)
}
private suspend fun export(validOnly: Boolean): List<XteaKey> {
return database.execute { connection ->
val query = if (validOnly) {
EXPORT_VALID_QUERY
} else {
EXPORT_ALL_QUERY
}
connection.prepareStatement(query).use { stmt ->
stmt.executeQuery().use { rows -> stmt.executeQuery().use { rows ->
val keys = mutableListOf<XteaKey>() val keys = mutableListOf<XteaKey>()
@ -35,4 +64,19 @@ public class KeyExporter @Inject constructor(
} }
} }
} }
private companion object {
private val EXPORT_ALL_QUERY = """
SELECT (k.key).k0, (k.key).k1, (k.key).k2, (k.key).k3
FROM keys k
ORDER BY k.id ASC
""".trimIndent()
private val EXPORT_VALID_QUERY = """
SELECT (k.key).k0, (k.key).k1, (k.key).k2, (k.key).k3
FROM keys k
JOIN containers c ON c.key_id = k.id
ORDER BY k.id ASC
""".trimIndent()
}
} }

@ -0,0 +1,26 @@
package org.openrs2.archive.web
import io.ktor.application.ApplicationCall
import io.ktor.response.respond
import io.ktor.thymeleaf.ThymeleafContent
import org.openrs2.archive.key.KeyExporter
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
public class KeysController @Inject constructor(
private val exporter: KeyExporter
) {
public suspend fun index(call: ApplicationCall) {
val (all, valid) = exporter.count()
call.respond(ThymeleafContent("keys/index.html", mapOf("all" to all, "valid" to valid)))
}
public suspend fun exportAll(call: ApplicationCall) {
call.respond(exporter.exportAll())
}
public suspend fun exportValid(call: ApplicationCall) {
call.respond(exporter.exportValid())
}
}

@ -29,6 +29,7 @@ import javax.inject.Singleton
@Singleton @Singleton
public class WebServer @Inject constructor( public class WebServer @Inject constructor(
private val cachesController: CachesController, private val cachesController: CachesController,
private val keysController: KeysController,
@Json private val mapper: ObjectMapper @Json private val mapper: ObjectMapper
) { ) {
public fun start(address: String, port: Int) { public fun start(address: String, port: Int) {
@ -68,6 +69,9 @@ public class WebServer @Inject constructor(
get("/caches/{id}/keys.json") { cachesController.exportKeysJson(call) } get("/caches/{id}/keys.json") { cachesController.exportKeysJson(call) }
get("/caches/{id}/keys.zip") { cachesController.exportKeysZip(call) } get("/caches/{id}/keys.zip") { cachesController.exportKeysZip(call) }
get("/caches/{id}/map.png") { cachesController.renderMap(call) } get("/caches/{id}/map.png") { cachesController.renderMap(call) }
get("/keys") { keysController.index(call) }
get("/keys/all.json") { keysController.exportAll(call) }
get("/keys/valid.json") { keysController.exportValid(call) }
static("/static") { resources("/org/openrs2/archive/static") } static("/static") { resources("/org/openrs2/archive/static") }
} }
}.start(wait = true) }.start(wait = true)

@ -0,0 +1,35 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" lang="en">
<head th:replace="layout.html :: head(title='Keys')">
<title>Keys - OpenRS2 Archive</title>
<link rel="stylesheet" href="/webjars/bootstrap/css/bootstrap.min.css" />
<link rel="stylesheet" href="/static/css/openrs2.css" />
<script src="/webjars/jquery/jquery.min.js" defer></script>
<script src="/webjars/bootstrap/js/bootstrap.bundle.min.js" defer></script>
</head>
<body>
<nav th:replace="layout.html :: nav(active='keys')"></nav>
<main class="container">
<h1>Keys</h1>
<p>
There are <strong th:text="${#numbers.formatInteger(all, 1, 'COMMA')}">0</strong> candidate keys in
the database, of which <strong th:text="${#numbers.formatInteger(valid, 1, 'COMMA')}">0</strong> have
been validated against at least one encrypted group.
</p>
<h2>Download</h2>
<p>
This page allows all raw keys in the database to be exported in
bulk. It doesn't include the archive/group IDs or versions each
key is valid for. To export the keys for a particular build of
the cache, use the download link on the
<a href="/caches">caches</a> page instead.
</p>
<div class="btn-group">
<a href="/keys/all.json"
class="btn btn-primary btn-sm">All candidate keys (JSON)</a>
<a href="/keys/valid.json"
class="btn btn-primary btn-sm">Validated keys (JSON)</a>
</div>
</main>
</body>
</html>

@ -17,6 +17,9 @@
<li class="nav-item" th:classappend="${active == 'caches'}? 'active'"> <li class="nav-item" th:classappend="${active == 'caches'}? 'active'">
<a class="nav-link" href="/caches">Caches</a> <a class="nav-link" href="/caches">Caches</a>
</li> </li>
<li class="nav-item" th:classappend="${active == 'keys'}? 'active'">
<a class="nav-link" href="/keys">Keys</a>
</li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/pub">Other</a> <a class="nav-link" href="/pub">Other</a>
</li> </li>

Loading…
Cancel
Save