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.