Add API endpoint for fetching groups by content address

Signed-off-by: Graham <gpe@openrs2.org>
master
Graham 3 weeks ago
parent 064663a55e
commit fb184f24aa
  1. 41
      archive/src/main/kotlin/org/openrs2/archive/cache/CacheExporter.kt
  2. 36
      archive/src/main/kotlin/org/openrs2/archive/web/CachesController.kt
  3. 1
      archive/src/main/kotlin/org/openrs2/archive/web/WebServer.kt
  4. 18
      archive/src/main/resources/org/openrs2/archive/templates/api/index.html

@ -640,6 +640,47 @@ public class CacheExporter @Inject constructor(
}
}
public suspend fun exportGroupByContentAddress(scope: String, archiveId: Int, groupId: Int, version: Int, checksum: Int): ByteBuf? {
return database.execute { connection ->
connection.prepareStatement("""
SELECT data FROM resolve_group((SELECT id FROM scopes WHERE name = ?), ?::uint1, ?, ?, ?)
""".trimIndent()).use { stmt ->
stmt.setString(1, scope)
stmt.setInt(2, archiveId)
stmt.setInt(3, groupId)
stmt.setInt(4, checksum)
stmt.setInt(5, version)
stmt.executeQuery().use { rows ->
if (rows.next()) {
val data = rows.getBytes(1)
return@execute Unpooled.wrappedBuffer(data)
}
if (scope == "runescape" && groupId in 0..0xFFFF && version in 0..0xFFFF) {
connection.prepareStatement("""
SELECT data FROM resolve_file(?::uint1, ?::uint2, ?::uint2, ?)
""".trimIndent()).use { stmt ->
stmt.setInt(1, archiveId)
stmt.setInt(2, groupId)
stmt.setInt(3, version)
stmt.setInt(4, checksum)
stmt.executeQuery().use { rows ->
if (rows.next()) {
val data = rows.getBytes(1)
return@execute Unpooled.wrappedBuffer(data)
}
}
}
}
return@execute null
}
}
}
}
public fun export(scope: String, id: Int, storeFactory: (Boolean) -> Store) {
database.executeOnce { connection ->
val legacy = connection.prepareStatement(

@ -132,6 +132,42 @@ public class CachesController @Inject constructor(
}
}
public suspend fun exportGroupByContentAddress(call: ApplicationCall) {
val scope = call.parameters["scope"]!!
val archiveId = call.parameters["archive"]?.toIntOrNull()
val groupId = call.parameters["group"]?.toIntOrNull()
val version = call.parameters["version"]?.toIntOrNull()
val checksum = call.parameters["checksum"]?.toIntOrNull()
if (archiveId == null || groupId == null || version == null || checksum == null) {
call.respond(HttpStatusCode.NotFound)
return
}
exporter.exportGroupByContentAddress(scope, archiveId, groupId, version, checksum).use { buf ->
if (buf == null) {
call.respond(HttpStatusCode.NotFound)
return
}
val etag = Base64.getEncoder().encodeToString(buf.whirlpool().sliceArray(0 until 16))
val bytes = ByteBufUtil.getBytes(buf, 0, buf.readableBytes(), false)
call.respondBytes(bytes, contentType = ContentType.Application.OctetStream) {
caching = CachingOptions(
cacheControl = CacheControl.MaxAge(
maxAgeSeconds = 86400,
visibility = CacheControl.Visibility.Public
),
expires = ZonedDateTime.now(ZoneOffset.UTC).plusSeconds(86400)
)
versions = listOf(
EntityTagVersion(etag, weak = false)
)
}
}
}
public suspend fun exportDisk(call: ApplicationCall) {
val scope = call.parameters["scope"]!!
val id = call.parameters["id"]?.toIntOrNull()

@ -88,6 +88,7 @@ public class WebServer @Inject constructor(
get("/caches.json") { cachesController.indexJson(call) }
get("/caches/{scope}/{id}") { cachesController.show(call) }
get("/caches/{scope}/{id}/archives/{archive}/groups/{group}.dat") { cachesController.exportGroup(call) }
get("/caches/{scope}/archives/{archive}/groups/{group}/versions/{version}/checksums/{checksum}.dat") { cachesController.exportGroupByContentAddress(call) }
get("/caches/{scope}/{id}/disk.zip") { cachesController.exportDisk(call) }
get("/caches/{scope}/{id}/flat-file.tar.gz") { cachesController.exportFlatFile(call) }
get("/caches/{scope}/{id}/keys.json") { cachesController.exportKeysJson(call) }

@ -245,6 +245,24 @@
container). The two byte version trailer is not included.
</p>
<h2><code>GET /caches/&lt;scope&gt;/archives/&lt;archive&gt;/groups/&lt;group&gt;/versions/&lt;version&gt;/checksums/&lt;checksum&gt;.dat</code></h2>
<p>
Returns a single file (old engine) or group (new engine) in
binary format. The response contains a <code>.jag</code>
archive (index 0 of an old engine cache), a GZIP-compressed
file (the remaining indexes of an old engine cache) or
JS5-compressed data (new engine cache, also known as a
container). The two byte version trailer is not included.
</p>
<p>
This is faster than the previous endpoint as it doesn't need to
look the group's version and checksum up in the JS5 index. If
you need to download more than a handful of groups, you should
use this endpoint.
</p>
<h2><code>GET /keys/all.json</code></h2>
<p>

Loading…
Cancel
Save