Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,18 @@

package dev.nextftc.telemetry.ftc

import io.ktor.http.CacheControl
import io.ktor.http.ContentDisposition
import io.ktor.http.ContentType
import io.ktor.http.HttpHeaders
import io.ktor.http.HttpStatusCode
import io.ktor.http.withCharset
import io.ktor.server.http.content.LocalFileContent
import io.ktor.server.http.content.staticResources
import io.ktor.server.response.header
import io.ktor.server.response.respond
import io.ktor.server.response.respondBytes
import io.ktor.server.response.respondText
import io.ktor.server.routing.Routing
import io.ktor.server.routing.RoutingContext
import io.ktor.server.routing.get
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
Expand Down Expand Up @@ -67,10 +68,12 @@ internal class LogDownloads(private val directory: () -> File, private val activ
}

fun install(routing: Routing) = with(routing) {
staticResources(PAGE_PATH, PAGE_RESOURCES) {
cacheControl { listOf(CacheControl.NoCache(null)) }
for (path in listOf(PAGE_PATH, "$PAGE_PATH/")) {
get(path) { respondPageFile("index.html") }
}

get("$PAGE_PATH/{file}") { respondPageFile(call.parameters["file"]) }

get("/logs") {
call.respondText(withContext(Dispatchers.IO) { json(entries()) }, ContentType.Application.Json)
}
Expand Down Expand Up @@ -98,6 +101,25 @@ internal class LogDownloads(private val directory: () -> File, private val activ
}
}

private suspend fun RoutingContext.respondPageFile(name: String?) {
val type = PAGE_FILES[name]
val bytes = if (type == null) {
null
} else {
withContext(Dispatchers.IO) {
LogDownloads::class.java.classLoader?.getResourceAsStream("$PAGE_RESOURCES/$name")?.use {
it.readBytes()
}
}
}
if (type == null || bytes == null) {
call.respondText("Not found", status = HttpStatusCode.NotFound)
return
}
call.response.header(HttpHeaders.CacheControl, "no-cache")
call.respondBytes(bytes, type)
}

/** Every log in the directory, newest first. */
internal fun entries(): List<Entry> {
val active = activeFile()?.let { runCatching { it.canonicalFile }.getOrNull() }
Expand Down Expand Up @@ -153,6 +175,13 @@ internal class LogDownloads(private val directory: () -> File, private val activ
*/
const val PAGE_RESOURCES = "dev/nextftc/telemetry/ftc/download"

/** The files of the page, with their types. */
val PAGE_FILES = mapOf(
"index.html" to ContentType.Text.Html.withCharset(Charsets.UTF_8),
"logs.css" to ContentType.Text.CSS.withCharset(Charsets.UTF_8),
"logs.js" to ContentType.Application.JavaScript.withCharset(Charsets.UTF_8),
)

/** The name [FtcTelemetry] gives a log: the stamp, then the sanitized OpMode name. */
val NAME = Regex("""(\d{4}_\d{2}_\d{2}__\d{2}_\d{2}_\d{2})__(.*)\.wpilog""")

Expand Down
Loading