From 88d09ecdbf7de745c089700f1892a40e1b1fdca8 Mon Sep 17 00:00:00 2001 From: rnentjes Date: Sun, 1 Dec 2024 11:59:26 +0100 Subject: [PATCH] Refactor package structure and add web components. Reorganized Kotlin package structure under 'nl.astraeus' and removed 'gradle.properties' file. Updated '.gitignore' to exclude 'gradle.properties'. Added new web functionalities including ID generation, websocket and HTTP request handling to support dynamic web content delivery. Adjusted server configuration and modified build version. --- .gitignore | 1 + build.gradle.kts | 2 +- gradle.properties | 1 - src/jsMain/kotlin/nl/astraeus/tmpl/Main.kt | 19 +++++ src/jvmMain/kotlin/nl/astraeus/tmpl/Main.kt | 69 +++++++++++++++++++ .../{ => nl/astraeus}/tmpl/db/Database.kt | 2 +- .../{ => nl/astraeus}/tmpl/db/Migrations.kt | 4 +- .../kotlin/nl/astraeus/tmpl/web/GenerateId.kt | 16 +++++ .../kotlin/nl/astraeus/tmpl/web/Index.kt | 41 +++++++++++ .../nl/astraeus/tmpl/web/RequestHandler.kt | 39 +++++++++++ .../nl/astraeus/tmpl/web/WebsockerHandler.kt | 55 +++++++++++++++ src/jvmMain/kotlin/tmpl/Main.kt | 37 ---------- web/web.txt | 1 + 13 files changed, 245 insertions(+), 42 deletions(-) delete mode 100644 gradle.properties create mode 100644 src/jsMain/kotlin/nl/astraeus/tmpl/Main.kt create mode 100644 src/jvmMain/kotlin/nl/astraeus/tmpl/Main.kt rename src/jvmMain/kotlin/{ => nl/astraeus}/tmpl/db/Database.kt (98%) rename src/jvmMain/kotlin/{ => nl/astraeus}/tmpl/db/Migrations.kt (97%) create mode 100644 src/jvmMain/kotlin/nl/astraeus/tmpl/web/GenerateId.kt create mode 100644 src/jvmMain/kotlin/nl/astraeus/tmpl/web/Index.kt create mode 100644 src/jvmMain/kotlin/nl/astraeus/tmpl/web/RequestHandler.kt create mode 100644 src/jvmMain/kotlin/nl/astraeus/tmpl/web/WebsockerHandler.kt delete mode 100644 src/jvmMain/kotlin/tmpl/Main.kt create mode 100644 web/web.txt diff --git a/.gitignore b/.gitignore index c82ce0d..35b4a7f 100644 --- a/.gitignore +++ b/.gitignore @@ -44,3 +44,4 @@ bin/ ### .kotlin ### .kotlin kotlin-js-store +gradle.properties diff --git a/build.gradle.kts b/build.gradle.kts index af3b15e..5a7160e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -5,7 +5,7 @@ plugins { } group = "nl.astraeus" -version = "1.0.0-SNAPSHOT" +version = "0.1.0-SNAPSHOT" repositories { mavenCentral() diff --git a/gradle.properties b/gradle.properties deleted file mode 100644 index 7fc6f1f..0000000 --- a/gradle.properties +++ /dev/null @@ -1 +0,0 @@ -kotlin.code.style=official diff --git a/src/jsMain/kotlin/nl/astraeus/tmpl/Main.kt b/src/jsMain/kotlin/nl/astraeus/tmpl/Main.kt new file mode 100644 index 0000000..c141d33 --- /dev/null +++ b/src/jsMain/kotlin/nl/astraeus/tmpl/Main.kt @@ -0,0 +1,19 @@ +package nl.astraeus.tmpl + +import kotlinx.browser.document +import kotlinx.html.div +import nl.astraeus.komp.HtmlBuilder +import nl.astraeus.komp.Komponent + + +class HelloKomponent : Komponent() { + override fun HtmlBuilder.render() { + div { + + "Hello, world!" + } + } +} + +fun main() { + Komponent.create(document.body!!, HelloKomponent()) +} diff --git a/src/jvmMain/kotlin/nl/astraeus/tmpl/Main.kt b/src/jvmMain/kotlin/nl/astraeus/tmpl/Main.kt new file mode 100644 index 0000000..ac04344 --- /dev/null +++ b/src/jvmMain/kotlin/nl/astraeus/tmpl/Main.kt @@ -0,0 +1,69 @@ +package nl.astraeus.tmpl + +import com.zaxxer.hikari.HikariConfig +import io.undertow.Undertow +import io.undertow.UndertowOptions +import io.undertow.predicate.Predicates +import io.undertow.server.handlers.encoding.ContentEncodingRepository +import io.undertow.server.handlers.encoding.EncodingHandler +import io.undertow.server.handlers.encoding.GzipEncodingProvider +import nl.astraeus.logger.Logger +import nl.astraeus.tmpl.db.Database +import nl.astraeus.tmpl.web.RequestHandler + +val log = Logger() + +val REPO_NAME = "dummy so the gitea template compiles, please remove" + +val SERVER_PORT = 7001 +val JDBC_PORT = 8001 + +fun main() { + Thread.currentThread().uncaughtExceptionHandler = Thread.UncaughtExceptionHandler { t, e -> + log.warn(e) { + e.message + } + } + + Runtime.getRuntime().addShutdownHook( + object : Thread() { + override fun run() { + Database.vacuumDatabase() + Database.closeDatabase() + } + } + ) + + Class.forName("nl.astraeus.jdbc.Driver") + Database.initialize(HikariConfig().apply { + driverClassName = "nl.astraeus.jdbc.Driver" + jdbcUrl = "jdbc:stat:webServerPort=$JDBC_PORT:jdbc:sqlite:data/${REPO_NAME}.db" + username = "sa" + password = "" + maximumPoolSize = 25 + isAutoCommit = false + + validate() + }) + + val compressionHandler = + EncodingHandler( + ContentEncodingRepository() + .addEncodingHandler( + "gzip", + GzipEncodingProvider(), 50, + Predicates.parse("max-content-size(5)") + ) + ).setNext(RequestHandler) + + val server = Undertow.builder() + .addHttpListener(SERVER_PORT, "localhost") + .setIoThreads(4) + .setHandler(compressionHandler) + .setServerOption(UndertowOptions.SHUTDOWN_TIMEOUT, 1000) + .build() + + println("Starting undertow server at port 6007...") + server?.start() + +} diff --git a/src/jvmMain/kotlin/tmpl/db/Database.kt b/src/jvmMain/kotlin/nl/astraeus/tmpl/db/Database.kt similarity index 98% rename from src/jvmMain/kotlin/tmpl/db/Database.kt rename to src/jvmMain/kotlin/nl/astraeus/tmpl/db/Database.kt index eec7b94..9df850a 100644 --- a/src/jvmMain/kotlin/tmpl/db/Database.kt +++ b/src/jvmMain/kotlin/nl/astraeus/tmpl/db/Database.kt @@ -1,4 +1,4 @@ -package tmpl.db +package nl.astraeus.tmpl.db import com.zaxxer.hikari.HikariConfig import com.zaxxer.hikari.HikariDataSource diff --git a/src/jvmMain/kotlin/tmpl/db/Migrations.kt b/src/jvmMain/kotlin/nl/astraeus/tmpl/db/Migrations.kt similarity index 97% rename from src/jvmMain/kotlin/tmpl/db/Migrations.kt rename to src/jvmMain/kotlin/nl/astraeus/tmpl/db/Migrations.kt index cfc8f3a..60d075b 100644 --- a/src/jvmMain/kotlin/tmpl/db/Migrations.kt +++ b/src/jvmMain/kotlin/nl/astraeus/tmpl/db/Migrations.kt @@ -1,6 +1,6 @@ -package tmpl.db +package nl.astraeus.tmpl.db -import tmpl.log +import nl.astraeus.tmpl.log import java.sql.Connection import java.sql.SQLException import java.sql.Timestamp diff --git a/src/jvmMain/kotlin/nl/astraeus/tmpl/web/GenerateId.kt b/src/jvmMain/kotlin/nl/astraeus/tmpl/web/GenerateId.kt new file mode 100644 index 0000000..72ed436 --- /dev/null +++ b/src/jvmMain/kotlin/nl/astraeus/tmpl/web/GenerateId.kt @@ -0,0 +1,16 @@ +package nl.astraeus.tmpl.web + +import java.security.SecureRandom + +val idChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" +val random = SecureRandom() + +fun generateId(length: Int = 8): String { + val id = StringBuilder() + + repeat(length) { + id.append(idChars[random.nextInt(idChars.length)]) + } + + return id.toString() +} diff --git a/src/jvmMain/kotlin/nl/astraeus/tmpl/web/Index.kt b/src/jvmMain/kotlin/nl/astraeus/tmpl/web/Index.kt new file mode 100644 index 0000000..3da7ad0 --- /dev/null +++ b/src/jvmMain/kotlin/nl/astraeus/tmpl/web/Index.kt @@ -0,0 +1,41 @@ +package nl.astraeus.tmpl.web + +import kotlinx.html.body +import kotlinx.html.head +import kotlinx.html.html +import kotlinx.html.meta +import kotlinx.html.script +import kotlinx.html.stream.appendHTML +import kotlinx.html.title +import nl.astraeus.tmpl.REPO_NAME + +fun generateIndex(patch: String?): String { + val result = StringBuilder(); + + if (patch == null) { + result.appendHTML(true).html { + head { + title("${REPO_NAME}") + //link("/css/all.min.css", "stylesheet") + } + body { + script(src = "/${REPO_NAME}.js") {} + } + } + } else { + result.appendHTML(true).html { + head { + title("${REPO_NAME}") + meta { + httpEquiv = "refresh" + content = "0; url=/$itemUrl/$patch" + } + } + body { + +"Redirecting to $itemUrl $patch..." + } + } + } + + return result.toString() +} diff --git a/src/jvmMain/kotlin/nl/astraeus/tmpl/web/RequestHandler.kt b/src/jvmMain/kotlin/nl/astraeus/tmpl/web/RequestHandler.kt new file mode 100644 index 0000000..42abd85 --- /dev/null +++ b/src/jvmMain/kotlin/nl/astraeus/tmpl/web/RequestHandler.kt @@ -0,0 +1,39 @@ +package nl.astraeus.tmpl.web + +import io.undertow.server.HttpHandler +import io.undertow.server.HttpServerExchange +import io.undertow.server.handlers.PathHandler +import io.undertow.server.handlers.resource.PathResourceManager +import io.undertow.server.handlers.resource.ResourceHandler +import java.nio.file.Paths +import kotlin.text.startsWith + +val itemUrl = "blaat" + +object IndexHandler : HttpHandler { + override fun handleRequest(exchange: HttpServerExchange) { + if (exchange.requestPath.startsWith("/$itemUrl/")) { + exchange.responseSender.send(generateIndex(null)) + } else { + val itemId = generateId() + + exchange.responseSender.send(generateIndex(itemId)) + } + } +} + +object RequestHandler : HttpHandler { + val resourceHandler = ResourceHandler(PathResourceManager(Paths.get("web"))) + val pathHandler = PathHandler(resourceHandler) + + init { + pathHandler.addExactPath("/", IndexHandler) + pathHandler.addExactPath("/index.html", IndexHandler) + pathHandler.addPrefixPath("/song", IndexHandler) + pathHandler.addExactPath("/ws", WebsocketConnectHandler) + } + + override fun handleRequest(exchange: HttpServerExchange) { + pathHandler.handleRequest(exchange) + } +} diff --git a/src/jvmMain/kotlin/nl/astraeus/tmpl/web/WebsockerHandler.kt b/src/jvmMain/kotlin/nl/astraeus/tmpl/web/WebsockerHandler.kt new file mode 100644 index 0000000..d9ce8a5 --- /dev/null +++ b/src/jvmMain/kotlin/nl/astraeus/tmpl/web/WebsockerHandler.kt @@ -0,0 +1,55 @@ +package nl.astraeus.tmpl.web + +import io.undertow.Handlers.websocket +import io.undertow.server.HttpHandler +import io.undertow.server.HttpServerExchange +import io.undertow.websockets.WebSocketConnectionCallback +import io.undertow.websockets.core.AbstractReceiveListener +import io.undertow.websockets.core.BufferedBinaryMessage +import io.undertow.websockets.core.BufferedTextMessage +import io.undertow.websockets.core.WebSocketChannel +import io.undertow.websockets.extensions.PerMessageDeflateHandshake +import io.undertow.websockets.spi.WebSocketHttpExchange +import kotlin.also + +class WebsocketHandler : AbstractReceiveListener(), WebSocketConnectionCallback { + // var user: String? = null + + override fun onConnect(exchange: WebSocketHttpExchange, channel: WebSocketChannel) { + channel.receiveSetter.set(this) + channel.resumeReceives() + } + + override fun onFullTextMessage(channel: WebSocketChannel, message: BufferedTextMessage) { + val data = message.data + + TODO("handle text message") + } + + override fun onFullBinaryMessage(channel: WebSocketChannel, message: BufferedBinaryMessage?) { + message?.data?.also { data -> + var length = 0 + for (buffer in data.resource) { + length += buffer.remaining() + } + val bytes = ByteArray(length) + var offset = 0 + for (buffer in data.resource) { + buffer.get(bytes, offset, buffer.remaining()) + offset += buffer.remaining() + } + + TODO("handle binary message") + } + } +} + +object WebsocketConnectHandler : HttpHandler { + + override fun handleRequest(exchange: HttpServerExchange) { + val handshakeHandler = websocket(WebsocketHandler()) + handshakeHandler.addExtension(PerMessageDeflateHandshake()) + handshakeHandler.handleRequest(exchange) + } + +} diff --git a/src/jvmMain/kotlin/tmpl/Main.kt b/src/jvmMain/kotlin/tmpl/Main.kt deleted file mode 100644 index 68b2e69..0000000 --- a/src/jvmMain/kotlin/tmpl/Main.kt +++ /dev/null @@ -1,37 +0,0 @@ -package tmpl - -import com.zaxxer.hikari.HikariConfig -import nl.astraeus.logger.Logger -import tmpl.db.Database - -val log = Logger() - -fun main() { - Thread.currentThread().uncaughtExceptionHandler = Thread.UncaughtExceptionHandler { t, e -> - log.warn(e) { - e.message - } - } - - Runtime.getRuntime().addShutdownHook( - object : Thread() { - override fun run() { - Database.vacuumDatabase() - Database.closeDatabase() - } - } - ) - - Class.forName("nl.astraeus.jdbc.Driver") - Database.initialize(HikariConfig().apply { - driverClassName = "nl.astraeus.jdbc.Driver" - jdbcUrl = "jdbc:stat:webServerPort=6001:jdbc:sqlite:data/kotlin-server-web-undertow.db" - username = "sa" - password = "" - maximumPoolSize = 25 - isAutoCommit = false - - validate() - }) - -} diff --git a/web/web.txt b/web/web.txt new file mode 100644 index 0000000..efdc935 --- /dev/null +++ b/web/web.txt @@ -0,0 +1 @@ +Directory where the web content (html + css + resources) will be placed.