implementation("io.ktor:ktor-server-core-jvm:2.3.2")
implementation("io.ktor:ktor-server-netty-jvm:2.3.2")
+ implementation("io.ktor:ktor-server-caching-headers:2.3.2")
implementation("io.ktor:ktor-server-call-id:2.3.2")
implementation("io.ktor:ktor-server-call-logging:2.3.2")
+ implementation("io.ktor:ktor-server-conditional-headers:2.3.2")
implementation("io.ktor:ktor-server-forwarded-header:2.3.2")
implementation("io.ktor:ktor-server-html-builder:2.3.2")
implementation("io.ktor:ktor-server-sessions-jvm:2.3.2")
implementation("org.slf4j:slf4j-api:2.0.7")
implementation("ch.qos.logback:logback-classic:1.4.7")
+ implementation("io.ktor:ktor-server-conditional-headers-jvm:2.3.2")
+ implementation("io.ktor:ktor-server-caching-headers-jvm:2.3.2")
}
java {
import info.mechyrdia.data.*
import info.mechyrdia.lore.*
import io.ktor.http.*
+import io.ktor.http.content.*
import io.ktor.server.application.*
import io.ktor.server.engine.*
import io.ktor.server.html.*
import io.ktor.server.http.content.*
import io.ktor.server.netty.*
import io.ktor.server.plugins.*
+import io.ktor.server.plugins.cachingheaders.*
import io.ktor.server.plugins.callid.*
import io.ktor.server.plugins.callloging.*
+import io.ktor.server.plugins.conditionalheaders.*
import io.ktor.server.plugins.forwardedheaders.*
import io.ktor.server.plugins.statuspages.*
import io.ktor.server.request.*
install(IgnoreTrailingSlash)
install(XForwardedHeaders)
+ install(CachingHeaders) {
+ options { _, outgoingContent ->
+ if (outgoingContent is JarFileContent)
+ CachingOptions(CacheControl.MaxAge(maxAgeSeconds = 3600))
+ else
+ null
+ }
+ }
+
+ install(ConditionalHeaders) {
+ version { _, outgoingContent ->
+ if (outgoingContent is LocalFileContent)
+ listOf(LastModifiedVersion(outgoingContent.file.lastModified()))
+ else
+ emptyList()
+ }
+ }
+
install(CallId) {
val counter = AtomicLong(0)
generate {
staticResources("/static", "static", index = null) {
preCompressed(CompressedFileType.BROTLI, CompressedFileType.GZIP)
+ enableAutoHeadResponse()
}
get("/lore/{path...}") {
call.respondHtml(HttpStatusCode.OK, call.loreArticlePage())
}
- staticFiles("/assets", File(Configuration.CurrentConfiguration.assetDir), index = null)
+ staticFiles("/assets", File(Configuration.CurrentConfiguration.assetDir), index = null) {
+ enableAutoHeadResponse()
+ }
// Client settings
import io.ktor.util.*
import kotlinx.html.*
+private val fonts = listOf(
+ "DejaVuSans-Bold.woff",
+ "DejaVuSans-BoldOblique.woff",
+ "DejaVuSans-Oblique.woff",
+ "DejaVuSans.woff",
+ "JetBrainsMono-ExtraBold.woff",
+ "JetBrainsMono-ExtraBoldItalic.woff",
+ "JetBrainsMono-Medium.woff",
+ "JetBrainsMono-MediumItalic.woff",
+ "NotoSans-Black.woff",
+ "NotoSans-BlackItalic.woff",
+ "NotoSans-Medium.woff",
+ "NotoSans-MediumItalic.woff",
+ "NotoSansGothic-Regular.woff",
+ "Oxanium-Bold.woff",
+ "Oxanium-ExtraBold.woff",
+ "Oxanium-Regular.woff",
+ "Oxanium-SemiBold.woff",
+ "thedish-language-alphabet.woff",
+ "tylan-language-alphabet-3.woff",
+)
+
+private val images = listOf(
+ "external-link-dark.png",
+ "external-link.png",
+ "icon.png",
+ "panel-dark.png",
+ "panel.png",
+)
+
fun ApplicationCall.page(pageTitle: String, navBar: List<NavItem>? = null, sidebar: Sidebar? = null, content: SECTIONS.() -> Unit): HTML.() -> Unit {
val theme = request.cookies["FACTBOOK_THEME"]
link(rel = "icon", type = "image/svg+xml", href = "/static/images/icon.png")
+ for (font in fonts)
+ link(
+ rel = "preload",
+ href = "/static/font/$font",
+ type = "font/woff"
+ ) {
+ attributes["as"] = "font"
+ attributes["crossorigin"] = "anonymous"
+ }
+
+ for (image in images)
+ link(
+ rel = "preload",
+ href = "/static/images/$image",
+ type = "image/png"
+ ) {
+ attributes["as"] = "image"
+ attributes["crossorigin"] = "anonymous"
+ }
+
link(rel = "stylesheet", href = "/static/style.css")
request.queryParameters["redirect"]?.let { redirect ->