From: Lanius Trolling Date: Sun, 14 May 2023 13:49:08 +0000 (-0400) Subject: Various improvements X-Git-Url: https://gitweb.starshipfights.net/?a=commitdiff_plain;h=3060a269842693ab792a826231ae11836be061a9;p=factbooks Various improvements --- diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml index 2b8a50f..0fc3113 100644 --- a/.idea/kotlinc.xml +++ b/.idea/kotlinc.xml @@ -1,6 +1,6 @@ - \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index 6a6f8f7..50e2cc0 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,9 +1,8 @@ +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar import com.nixxcode.jvmbrotli.common.BrotliLoader import com.nixxcode.jvmbrotli.enc.BrotliOutputStream import com.nixxcode.jvmbrotli.enc.Encoder import org.jetbrains.kotlin.gradle.tasks.KotlinCompile -import java.security.MessageDigest -import java.util.* import java.util.concurrent.CountDownLatch import java.util.concurrent.Executors import java.util.zip.GZIPOutputStream @@ -25,8 +24,8 @@ buildscript { plugins { java - kotlin("jvm") version "1.8.0" - kotlin("plugin.serialization") version "1.8.0" + kotlin("jvm") version "1.8.10" + kotlin("plugin.serialization") version "1.8.10" id("com.github.johnrengelman.shadow") version "7.1.2" application } @@ -45,16 +44,16 @@ dependencies { implementation("org.jetbrains.kotlinx:kotlinx-serialization-core-jvm:1.4.1") implementation("org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:1.4.1") - implementation("io.ktor:ktor-server-core-jvm:2.2.4") - implementation("io.ktor:ktor-server-netty-jvm:2.2.4") + implementation("io.ktor:ktor-server-core-jvm:2.3.0") + implementation("io.ktor:ktor-server-netty-jvm:2.3.0") - implementation("io.ktor:ktor-server-call-id:2.2.4") - implementation("io.ktor:ktor-server-call-logging:2.2.4") - implementation("io.ktor:ktor-server-conditional-headers:2.2.4") - implementation("io.ktor:ktor-server-forwarded-header:2.2.4") - implementation("io.ktor:ktor-server-html-builder:2.2.4") - implementation("io.ktor:ktor-server-sessions-jvm:2.2.4") - implementation("io.ktor:ktor-server-status-pages:2.2.4") + implementation("io.ktor:ktor-server-call-id:2.3.0") + implementation("io.ktor:ktor-server-call-logging:2.3.0") + implementation("io.ktor:ktor-server-conditional-headers:2.3.0") + implementation("io.ktor:ktor-server-forwarded-header:2.3.0") + implementation("io.ktor:ktor-server-html-builder:2.3.0") + implementation("io.ktor:ktor-server-sessions-jvm:2.3.0") + implementation("io.ktor:ktor-server-status-pages:2.3.0") implementation("org.jetbrains.kotlinx:kotlinx-html-jvm:0.8.0") @@ -98,8 +97,6 @@ tasks.named("processResources") { val pool = Executors.newWorkStealingPool() val encoderParams = if (BrotliLoader.isBrotliAvailable()) Encoder.Parameters().setQuality(8) else null - val base64 = Base64.getUrlEncoder() - val hashDigest = ThreadLocal.withInitial { MessageDigest.getInstance("SHA-256") } val resourceTree = fileTree(mapOf("dir" to outputs.files.asPath + "/static/", "exclude" to listOf("*.gz", "*.br", "*.sha256"))) val countDownLatch = CountDownLatch(resourceTree.count()) @@ -107,9 +104,6 @@ tasks.named("processResources") { for (file in resourceTree) { pool.execute { val bytes = file.readBytes() - val hashFile = File("${file.absolutePath}.sha256").bufferedWriter() - hashFile.write(base64.encodeToString(hashDigest.get().digest(bytes)).trimEnd('=')) - hashFile.close() val result = File("${file.absolutePath}.gz").outputStream() val gzipStream = GZIPOutputStream(result) @@ -132,6 +126,11 @@ tasks.named("processResources") { } } +tasks.named("shadowJar") { + mergeServiceFiles() + exclude { it.name == "module-info.class" } +} + application { mainClass.set("info.mechyrdia.Factbooks") } diff --git a/src/main/kotlin/info/mechyrdia/Factbooks.kt b/src/main/kotlin/info/mechyrdia/Factbooks.kt index b915734..6ab8f1f 100644 --- a/src/main/kotlin/info/mechyrdia/Factbooks.kt +++ b/src/main/kotlin/info/mechyrdia/Factbooks.kt @@ -23,7 +23,6 @@ import io.ktor.server.response.* import io.ktor.server.routing.* import io.ktor.server.sessions.* import io.ktor.server.sessions.serialization.* -import io.ktor.server.util.* import io.ktor.util.* import org.slf4j.event.Level import java.io.File @@ -31,12 +30,6 @@ import java.io.IOException import java.io.InputStream import java.util.concurrent.atomic.AtomicLong -object ResourceLoader { - fun getResource(resource: String): InputStream? = javaClass.getResourceAsStream(resource) - - val SHA256AttributeKey = AttributeKey("SHA256Hash") -} - lateinit var application: Application private set @@ -73,14 +66,6 @@ fun Application.factbooks() { } } - install(ConditionalHeaders) { - version { call, _ -> - call.attributes.getOrNull(ResourceLoader.SHA256AttributeKey)?.let { hash -> - listOf(EntityTagVersion(hash)) - }.orEmpty() - } - } - install(Sessions) { cookie("USER_SESSION", SessionStorageMongoDB) { identity { Id().id } @@ -131,59 +116,15 @@ fun Application.factbooks() { // Factbooks and assets - static("/static") { - get("{static-content...}") { - val staticContentPath = call.parameters.getAll("static-content")?.joinToString("/") ?: return@get - val contentPath = "/static/$staticContentPath" - - ResourceLoader.getResource("$contentPath.sha256")?.reader()?.readText()?.let { sha256Hash -> - call.attributes.put(ResourceLoader.SHA256AttributeKey, sha256Hash) - } - - val brContentPath = "$contentPath.br" - val gzContentPath = "$contentPath.gz" - - val contentType = ContentType.fromFileExtension(contentPath.substringAfterLast('.')).firstOrNull() - - val acceptedEncodings = call.request.acceptEncodingItems().map { it.value }.toSet() - - if (CompressedFileType.BROTLI.encoding in acceptedEncodings) { - val brContent = ResourceLoader.getResource(brContentPath) - if (brContent != null) { - call.attributes.put(SuppressionAttribute, true) - - call.response.header(HttpHeaders.ContentEncoding, CompressedFileType.BROTLI.encoding) - - call.respondBytes(brContent.readBytes(), contentType) - - return@get - } - } - - if (CompressedFileType.GZIP.encoding in acceptedEncodings) { - val gzContent = ResourceLoader.getResource(gzContentPath) - if (gzContent != null) { - call.attributes.put(SuppressionAttribute, true) - - call.response.header(HttpHeaders.ContentEncoding, CompressedFileType.GZIP.encoding) - - call.respondBytes(gzContent.readBytes(), contentType) - - return@get - } - } - - ResourceLoader.getResource(contentPath)?.let { call.respondBytes(it.readBytes(), contentType) } - } + staticResources("/static", "static", index = null) { + preCompressed(CompressedFileType.BROTLI, CompressedFileType.GZIP) } get("/lore/{path...}") { call.respondHtml(HttpStatusCode.OK, call.loreArticlePage()) } - static("/assets") { - files(File(Configuration.CurrentConfiguration.assetDir)) - } + staticFiles("/assets", File(Configuration.CurrentConfiguration.assetDir), index = null) // Client settings diff --git a/src/main/kotlin/info/mechyrdia/data/view_comments.kt b/src/main/kotlin/info/mechyrdia/data/view_comments.kt index 42e044e..b069c6e 100644 --- a/src/main/kotlin/info/mechyrdia/data/view_comments.kt +++ b/src/main/kotlin/info/mechyrdia/data/view_comments.kt @@ -88,7 +88,8 @@ fun FlowContent.commentBox(comment: CommentRenderData, loggedInAs: Id p { style = "font-size:0.8em" - +"Edited ${comment.numEdits} times, last edited at " + val nounSuffix = if (comment.numEdits != 1) "s" else "" + +"Edited ${comment.numEdits} time$nounSuffix, last edited at " dateTime(lastEdit) } } diff --git a/src/main/resources/static/init.js b/src/main/resources/static/init.js index a96343e..fb1726f 100644 --- a/src/main/resources/static/init.js +++ b/src/main/resources/static/init.js @@ -36,9 +36,8 @@ // Preview themes const themeChoices = document.getElementsByName("theme"); for (const themeChoice of themeChoices) { - const theme = themeChoice.value; - themeChoice.addEventListener("click", () => { - document.documentElement.setAttribute("data-theme", theme); + themeChoice.addEventListener("click", e => { + document.documentElement.setAttribute("data-theme", e.currentTarget.value); }); } }); @@ -68,13 +67,11 @@ window.addEventListener("load", function () { // Image previewing - const thumbView = document.getElementById("thumb-view"); - const thumbViewImg = thumbView.getElementsByTagName("img")[0]; - thumbView.addEventListener("click", e => { + document.getElementById("thumb-view").addEventListener("click", e => { e.preventDefault(); - thumbView.classList.remove("visible"); - thumbViewImg.src = ""; + e.currentTarget.classList.remove("visible"); + e.currentTarget.getElementsByTagName("img")[0].src = ""; }); const thumbs = document.querySelectorAll("a.thumb"); @@ -82,6 +79,8 @@ thumb.onclick = e => { e.preventDefault(); + const thumbView = document.getElementById("thumb-view"); + const thumbViewImg = thumbView.getElementsByTagName("img")[0]; thumbViewImg.src = e.currentTarget.getAttribute("href"); thumbView.classList.add("visible"); }; @@ -98,10 +97,10 @@ const canvases = document.getElementsByTagName("canvas"); for (const canvas of canvases) { - const modelName = canvas.getAttribute("data-model"); - if (modelName == null || modelName === "") continue; + const canvasModelName = canvas.getAttribute("data-model"); + if (canvasModelName == null || canvasModelName === "") continue; - (async () => { + (async (modelName) => { const modelAsync = loadObj(modelName); const camera = new THREE.PerspectiveCamera(69, 1, 0.01, 1000.0); @@ -149,8 +148,8 @@ light.position.set(0, 0, 0); render(); - })().catch(reason => { - console.error("Error rendering model " + modelName, reason); + })(canvasModelName).catch(reason => { + console.error("Error rendering model " + canvasModelName, reason); }); } }); @@ -167,10 +166,10 @@ let form = document.createElement("form"); form.style.display = "none"; - form.action = anchor.href; - form.method = method; + form.action = e.currentTarget.href; + form.method = e.currentTarget.getAttribute("data-method"); - const csrfToken = anchor.getAttribute("data-csrf-token"); + const csrfToken = e.currentTarget.getAttribute("data-csrf-token"); if (csrfToken != null) { let csrfInput = document.createElement("input"); csrfInput.name = "csrf-token"; diff --git a/src/main/resources/static/style.css b/src/main/resources/static/style.css index 3b77eb7..1260553 100644 --- a/src/main/resources/static/style.css +++ b/src/main/resources/static/style.css @@ -626,7 +626,7 @@ div.list { display: flex; flex-wrap: nowrap; align-items: stretch; - justify-content: center; + justify-content: start; flex-direction: column; margin: 0;