From ec7e116a2b714fd45b9da9e3f1656eb0faae0893 Mon Sep 17 00:00:00 2001 From: Dokkae6949 Date: Sun, 14 Dec 2025 03:40:05 +0100 Subject: [PATCH] feat: ui rework --- .env.example | 5 +- .../JteIndexGenerated$Companion.class | Bin 7863 -> 9727 bytes .../precompiled/JteIndexGenerated.class | Bin 2178 -> 2253 bytes .../precompiled/JteIndexGenerated.kt | 31 ++- .../JteMessageGenerated$Companion.class | Bin 3124 -> 5186 bytes .../partials/JteMessageGenerated.class | Bin 2220 -> 2384 bytes .../partials/JteMessageGenerated.kt | 46 ++-- jte-classes/META-INF/main.kotlin_module | Bin 0 -> 24 bytes .../generated/ondemand/JteIndexGenerated.kt | 35 +++ .../ondemand/partials/JteMessageGenerated.kt | 46 ++++ .../kotlin/at/dokkae/homepage/Homepage.kt | 61 +++-- .../at/dokkae/homepage/config/Environment.kt | 15 +- .../at/dokkae/homepage/templates/Index.kt | 2 +- .../at/dokkae/homepage/templates/Message.kt | 8 + src/main/kte/Index.kte | 221 +++++++++--------- src/main/kte/partials/Message.kte | 40 +++- src/main/resources/static/css/.keep | 0 src/main/resources/static/css/index.css | 0 src/main/resources/static/images/.keep | 0 src/main/resources/static/js/.keep | 0 20 files changed, 341 insertions(+), 169 deletions(-) create mode 100644 jte-classes/META-INF/main.kotlin_module create mode 100644 jte-classes/gg/jte/generated/ondemand/JteIndexGenerated.kt create mode 100644 jte-classes/gg/jte/generated/ondemand/partials/JteMessageGenerated.kt create mode 100644 src/main/kotlin/at/dokkae/homepage/templates/Message.kt create mode 100644 src/main/resources/static/css/.keep create mode 100644 src/main/resources/static/css/index.css create mode 100644 src/main/resources/static/images/.keep create mode 100644 src/main/resources/static/js/.keep diff --git a/.env.example b/.env.example index eb9c21b..71b5d1a 100644 --- a/.env.example +++ b/.env.example @@ -1,5 +1,6 @@ -PORT=9000 -HOST=localhost +APP_PORT=9000 +APP_DOMAIN=localhost +APP_ENV=development DB_URL=jdbc:postgresql://localhost:5432/homepage DB_USERNAME=postgres diff --git a/build/generated-resources/jte/gg/jte/generated/precompiled/JteIndexGenerated$Companion.class b/build/generated-resources/jte/gg/jte/generated/precompiled/JteIndexGenerated$Companion.class index 8cd23688daa5987dfe4dcaf44666dadefb7196d3..692b37c5c7342e2f136008629efe4420c086e7c5 100644 GIT binary patch literal 9727 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 Uf?@{#Vur$EhN5DI(qe`w0NTDf3IG5A literal 7863 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 Km?5s1p%wu1UqDa* diff --git a/build/generated-resources/jte/gg/jte/generated/precompiled/JteIndexGenerated.class b/build/generated-resources/jte/gg/jte/generated/precompiled/JteIndexGenerated.class index f31a6e92c0fda11dbcc0b35ff01f0dadef7e44c7..d3e2cd6757bf082345b66256ed0f1d0f48e0cf1b 100644 GIT binary patch delta 330 zcmZn?JS#Y16JyR~9%lQApOiR4QgaJ(5=&Ayi!c^3GU`nHsEiO=#Z=48$TnGn&5==d zau!=JqvGT&HpR)3>_PS48JHOu85kID?`DvQWZcf6v6(?kNNY2Lo`CWmE^jSnW`8Xf zW`8YKW`8X<7Jn^vR(~xHc7H7aHW0xMBEVcu5Q|Gd$X`o<8%**DsQPOO@CumvYYFfP zc=~Gz@C*3)YY7Ml1p8|V2!cr=fslHCEdgPH5PvNJ5rI&DEdfz5DJBr@uO%QZkm#=^ zAR&aQgrEyys%UrRs+LdilXIgktFL4*Q`Py`W5?EYGt8H_eE iFfcGOI5RLXa4|43a5Hc+2rzIlFf({EFih6t2mt^C5kDCK delta 247 zcmX>r*d#b%6JyF`8D{&5pOiMsFcvW~s!aT-ym=Q>Ei)s_WD7P&Mxn`7Y`u(PldISi zCu_0?)$d_oW?*DsU|6u5K_ZfIJA=k%1~DP6%?x@15_`D3wV0XxwOE+_wOE<`wb)qw zwb)txwK!P)wFE#M0T54sL%_jbOMnwhatSi@`fCYrLnt0WhCY8S0bU5jC&tAqXW5GFk*gh=K?)5FyU$ueF)MXd?py10#bo0|NsW0}}%`13QBN K1IOfIjwk?<&@TS~ diff --git a/build/generated-resources/jte/gg/jte/generated/precompiled/JteIndexGenerated.kt b/build/generated-resources/jte/gg/jte/generated/precompiled/JteIndexGenerated.kt index 3b1fa73..35daa35 100644 --- a/build/generated-resources/jte/gg/jte/generated/precompiled/JteIndexGenerated.kt +++ b/build/generated-resources/jte/gg/jte/generated/precompiled/JteIndexGenerated.kt @@ -1,23 +1,34 @@ @file:Suppress("ktlint") package gg.jte.generated.precompiled -import at.dokkae.homepage.templates.Index +import at.dokkae.homepage.templates.IndexTemplate +import at.dokkae.homepage.templates.MessageTemplate +import gg.jte.support.ForSupport @Suppress("UNCHECKED_CAST", "UNUSED_PARAMETER") class JteIndexGenerated { companion object { @JvmField val JTE_NAME = "Index.kte" - @JvmField val JTE_LINE_INFO = intArrayOf(0,0,0,2,2,2,2,2,64,64,141,141,142,142,143,143,158,158,158,2,2,2,2,2) - @JvmStatic fun render(jteOutput:gg.jte.html.HtmlTemplateOutput, jteHtmlInterceptor:gg.jte.html.HtmlInterceptor?, model:Index) { - jteOutput.writeContent("\n\n\n\n \n \n Simple Chat — http4k + JTE + htmx\n\n \n \n\n \n\n\n
\n

Simple Chat

\n\n
\n ") + @JvmField val JTE_LINE_INFO = intArrayOf(0,0,0,1,2,4,4,4,4,4,18,18,37,53,73,78,83,83,84,84,85,85,91,97,108,121,133,148,148,148,4,4,4,4,4) + @JvmStatic fun render(jteOutput:gg.jte.html.HtmlTemplateOutput, jteHtmlInterceptor:gg.jte.html.HtmlInterceptor?, model:IndexTemplate) { + jteOutput.writeContent("\n\n\n\n \n \n Dokkae's Chat\n\n \n \n \n\n \n\n\n
\n ") + jteOutput.writeContent("\n
\n

Simple Chat

\n
\n\n ") + jteOutput.writeContent("\n
\n
\n
\n
\n ") for (message in model.messages.reversed()) { - jteOutput.writeContent("\n ") - gg.jte.generated.precompiled.partials.JteMessageGenerated.render(jteOutput, jteHtmlInterceptor, message); - jteOutput.writeContent("\n ") + jteOutput.writeContent("\n ") + gg.jte.generated.precompiled.partials.JteMessageGenerated.render(jteOutput, jteHtmlInterceptor, MessageTemplate(message)); + jteOutput.writeContent("\n ") } - jteOutput.writeContent("\n
\n\n\n
\n \n \n \n
\n\n

No auth — anyone can post. Messages are stored only in memory.

\n
\n\n") + jteOutput.writeContent("\n
\n \n \n \n\n ") + jteOutput.writeContent("\n
\n
\n ") + jteOutput.writeContent("\n
\n
\n \n
\n
\n\n ") + jteOutput.writeContent("\n
\n
\n \n
\n
\n\n ") + jteOutput.writeContent("\n \n
\n
\n\n ") + jteOutput.writeContent("\n \n
\n\n\n") } @JvmStatic fun renderMap(jteOutput:gg.jte.html.HtmlTemplateOutput, jteHtmlInterceptor:gg.jte.html.HtmlInterceptor?, params:Map) { - val model = params["model"] as Index + val model = params["model"] as IndexTemplate render(jteOutput, jteHtmlInterceptor, model); } } diff --git a/build/generated-resources/jte/gg/jte/generated/precompiled/partials/JteMessageGenerated$Companion.class b/build/generated-resources/jte/gg/jte/generated/precompiled/partials/JteMessageGenerated$Companion.class index 7f497996c573798f3b938b1891f352889c1932c1..e642725ebfd0236cab4307a4386590f18040d638 100644 GIT binary patch literal 5186 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 delta 1247 zcmX@4u| z?`bJ>*`#EaDJ17478lzpL1mQexD*r=6l{u1it_W)?Kl{u85!gzKjfC1T)||<#Rd`P zU{IcXmerD5l|hZ0L5@M4kwI#*AREXUPDahi%^a%r+6+2e3|b7jj0^(hMVTe3p~b01 z&iQ#Isd*(l4EhWTAZ-SW4E#3w5EJdJz}9jw7%?(1r(~9KFqkkhaHXe~z%`jcOf+X? zfGD*oE=bH%C@!hYNwrl<&dhI$F)Ee#F^ zOGXA3kX8-`>&eobnt`?qb|9D9GcpKfC6*=Xmt^Lq>U-uDmn7zuuroL^GH{gS2bUCO z=B4v6ID_2a#NfinAfgF&uZ=#)C_642{gliyI}QdnMh1n+54aU3>vAhG$@oA6g?(}@ z_hTtPMh0H64W%WSIr_ed1?&s~j10`_sU?$3c(l2L7=pPO0vSRW8SE#o;<3>6g~SvG zLl`3iPjW_Ta<*T7iC<|>4i7^FgE$w1C_^L<12+Q?7XuSR^kzj~b|%Jx$(sDn7)vG# z2#D2}GB7Z(F))J3G6n_)9x$7gfs=uYfq_8~q?3V#p@e~f;U)t!0}BHKL#DQn@D2t^ zT_LF*43gSHGCLWRm>9H$R2X+MXfQKq>uzJv+sR-E5;BHLnIfbtpi)*)sd^iPoWnK- zSE#f*Sb8G^0|OHS$c1`fcW^O?FmN--GVm~{Gw?F#Gw?B3Fz_=tF$getF$gjQGYByx zFbFebFo-aKe4+($Ez~Cr44DkP3?5+LWis$Fcrq}7{8G=r&)~(t%uo&v07eD@1_p)- z1_lOUW(GzEK?Vi}Mg}1;31U|=Ffi~kGw?DnFz_)jFz|x|$eY23ffXD;O$;mytPBhc z8n6KJhXm73hEOJkNTKi@3{eLdw5(XPv~?s|wlTzjyjKtLA0GoV0}q23gFJ&cgF1r* z*!?C9tPJuDoL~on!d(OI6AideG~hnbVDMvzWngBg0*9F|u^y^N@sS?`g8(A~D?=Ou zBiKi}42H@W7#O&~nhF_;7$g~Lz^*9< zyC#K!ks*nJl_5Erf!UKG#gie`lOZjDA>ET9l_A5EA(J61nt`#HA)6s5nnAdjfvcDy ux0r#on1QXBArH*X2b0`jlDC+_qnLrOn8CA{fxno+tC+#Nn8B}@Ar}BtuhVM) diff --git a/build/generated-resources/jte/gg/jte/generated/precompiled/partials/JteMessageGenerated.class b/build/generated-resources/jte/gg/jte/generated/precompiled/partials/JteMessageGenerated.class index ea7798fa0d60d11f576b08c04a19b845f8baa431..a51102d1763b658a8b4118c8465ce17c824cab4a 100644 GIT binary patch delta 456 zcmZ1@ctL2w3C6t1TFmy7IT(c{N>XzRauQ2Yi}ig|i;EM}Q$rvElNA}&*;sS)Q&Mv_ z2QbDk>gxFSyHVrWPcor|RROMvci+*?JjuCQoHk zob19LSkGw7z|6qNz`&@un?WLyaXW*?W(F}Kt<4O20(yJ6ytSB_{k2$_{k2$`{k7Ow z{I%Fw{k1sQ{Ivww*!{Hx*abjL4lv0HCb_^QH<;uR;PBTH;02R>V3J>e(_c$K089!B zaQSQ13kZQI0bv0?e=PwK0e*ii0Z}k11}4S9q=bNwzm|ZcfT+KgfE1XN29q)Z68>5O zvS3mUOv-~v1u&@yCY1za{Ivv>1?2p-1XRGJDwtFgQ1aIja8w6V8emcr>}oAAsSPG| lz@#pS+{|FKk%57Mk-?dPfq{#GiGiDen?ZnqXL1ln6aa1kSRViY delta 291 zcmca0v_^2k3C5VoKFs!$IT(f6d{c{y6VoRfGpcj2=YrX(n-dsg7^P)=5=-<`^0TuO zQ}r|Qb5jcv(^K^!syDx6>SktSnOw@|$S5*-J6kWK#N_R4ij&jW1M3-fGcYqSGB7aA z-_0Nq$+(?CV>5%8kk)1fJprjbT;5vD%>G&|%>G)e%>G(zto~Z;to~XYto~X8AdUct zC&0nsuO+|VH;CX7VDr}!;02R>V3J<|BrX6Z1;M0{0GGd=mVmGTm%o;P2$&QF unJfk(#6g4vh>&FU*V@crw2^^\n ") - jteOutput.setContext("strong", null) - jteOutput.writeUserContent(message.author) - jteOutput.writeContent(":\n ") + @JvmField val JTE_LINE_INFO = intArrayOf(0,0,0,1,2,3,4,6,6,6,6,6,8,8,8,9,9,10,10,14,15,15,15,15,18,21,21,21,24,24,24,24,24,24,28,30,30,30,34,34,34,6,6,6,6,6) + @JvmStatic fun render(jteOutput:gg.jte.html.HtmlTemplateOutput, jteHtmlInterceptor:gg.jte.html.HtmlInterceptor?, model:MessageTemplate) { + jteOutput.writeContent("\n") + val dateFormatter: DateTimeFormatter = DateTimeFormatter.ofPattern("dd.MM.yyyy").withZone(ZoneId.systemDefault()) + jteOutput.writeContent("\n") + val timeFormatter: DateTimeFormatter = DateTimeFormatter.ofPattern("HH:mm").withZone(ZoneId.systemDefault()) + jteOutput.writeContent("\n") + val borderColors = listOf("blue", "green", "pink", "orange") + jteOutput.writeContent("\n\n
\n
\n ") + jteOutput.writeContent("\n
\n (") + jteOutput.writeContent("\">
\n\n
\n ") + jteOutput.writeContent("\n
\n \n ") jteOutput.setContext("span", null) - jteOutput.writeUserContent(message.createdAt.toString()) - jteOutput.writeContent(")\n \n
") + jteOutput.writeUserContent(model.message.author) + jteOutput.writeContent("\n \n \n ") + jteOutput.setContext("span", null) + jteOutput.writeUserContent(dateFormatter.format(model.message.createdAt)) + jteOutput.writeContent(" • ") + jteOutput.setContext("span", null) + jteOutput.writeUserContent(timeFormatter.format(model.message.createdAt)) + jteOutput.writeContent("\n \n
\n\n ") + jteOutput.writeContent("\n
\n ") + jteOutput.setContext("div", null) + jteOutput.writeUserContent(model.message.content) + jteOutput.writeContent("\n
\n
\n
\n") } @JvmStatic fun renderMap(jteOutput:gg.jte.html.HtmlTemplateOutput, jteHtmlInterceptor:gg.jte.html.HtmlInterceptor?, params:Map) { - val message = params["message"] as Message - render(jteOutput, jteHtmlInterceptor, message); + val model = params["model"] as MessageTemplate + render(jteOutput, jteHtmlInterceptor, model); } } } diff --git a/jte-classes/META-INF/main.kotlin_module b/jte-classes/META-INF/main.kotlin_module new file mode 100644 index 0000000000000000000000000000000000000000..9dbc290d21e8fd8815939343a7a5484dc57cfcde GIT binary patch literal 24 ZcmZQzU|?ooU|?ckU|?i`0wo451^@wK08;<} literal 0 HcmV?d00001 diff --git a/jte-classes/gg/jte/generated/ondemand/JteIndexGenerated.kt b/jte-classes/gg/jte/generated/ondemand/JteIndexGenerated.kt new file mode 100644 index 0000000..a762a23 --- /dev/null +++ b/jte-classes/gg/jte/generated/ondemand/JteIndexGenerated.kt @@ -0,0 +1,35 @@ +@file:Suppress("ktlint") +package gg.jte.generated.ondemand +import at.dokkae.homepage.templates.IndexTemplate +import at.dokkae.homepage.templates.MessageTemplate +import gg.jte.support.ForSupport +@Suppress("UNCHECKED_CAST", "UNUSED_PARAMETER") +class JteIndexGenerated { +companion object { + @JvmField val JTE_NAME = "Index.kte" + @JvmField val JTE_LINE_INFO = intArrayOf(0,0,0,1,2,4,4,4,4,4,18,18,37,58,88,93,96,96,97,97,98,98,102,108,119,132,146,161,161,161,4,4,4,4,4) + @JvmStatic fun render(jteOutput:gg.jte.html.HtmlTemplateOutput, jteHtmlInterceptor:gg.jte.html.HtmlInterceptor?, model:IndexTemplate) { + jteOutput.writeContent("\n\n\n\n \n \n Dokkae's Chat\n\n \n \n \n\n \n\n\n
\n ") + jteOutput.writeContent("\n
\n

Dokkae's Chat

\n
\n\n ") + jteOutput.writeContent("\n
\n
\n ") + for (message in model.messages) { + jteOutput.writeContent("\n ") + gg.jte.generated.ondemand.partials.JteMessageGenerated.render(jteOutput, jteHtmlInterceptor, MessageTemplate(message)); + jteOutput.writeContent("\n ") + } + jteOutput.writeContent("\n
\n
\n\n ") + jteOutput.writeContent("\n
\n
\n ") + jteOutput.writeContent("\n
\n
\n \n
\n
\n\n ") + jteOutput.writeContent("\n
\n
\n
\n \n
\n\n ") + jteOutput.writeContent("\n \n
\n
\n
\n
\n\n ") + jteOutput.writeContent("\n \n
\n\n\n") + } + @JvmStatic fun renderMap(jteOutput:gg.jte.html.HtmlTemplateOutput, jteHtmlInterceptor:gg.jte.html.HtmlInterceptor?, params:Map) { + val model = params["model"] as IndexTemplate + render(jteOutput, jteHtmlInterceptor, model); + } +} +} diff --git a/jte-classes/gg/jte/generated/ondemand/partials/JteMessageGenerated.kt b/jte-classes/gg/jte/generated/ondemand/partials/JteMessageGenerated.kt new file mode 100644 index 0000000..06f028d --- /dev/null +++ b/jte-classes/gg/jte/generated/ondemand/partials/JteMessageGenerated.kt @@ -0,0 +1,46 @@ +@file:Suppress("ktlint") +package gg.jte.generated.ondemand.partials +import at.dokkae.homepage.templates.MessageTemplate +import java.time.Instant +import java.time.ZoneId +import java.time.format.DateTimeFormatter +import kotlin.math.absoluteValue +@Suppress("UNCHECKED_CAST", "UNUSED_PARAMETER") +class JteMessageGenerated { +companion object { + @JvmField val JTE_NAME = "partials/Message.kte" + @JvmField val JTE_LINE_INFO = intArrayOf(0,0,0,1,2,3,4,6,6,6,6,6,8,8,8,9,9,10,10,14,15,15,15,15,18,21,21,21,24,24,24,24,24,24,28,30,30,30,34,34,34,6,6,6,6,6) + @JvmStatic fun render(jteOutput:gg.jte.html.HtmlTemplateOutput, jteHtmlInterceptor:gg.jte.html.HtmlInterceptor?, model:MessageTemplate) { + jteOutput.writeContent("\n") + val dateFormatter: DateTimeFormatter = DateTimeFormatter.ofPattern("dd.MM.yyyy").withZone(ZoneId.systemDefault()) + jteOutput.writeContent("\n") + val timeFormatter: DateTimeFormatter = DateTimeFormatter.ofPattern("HH:mm").withZone(ZoneId.systemDefault()) + jteOutput.writeContent("\n") + val borderColors = listOf("red", "orange", "yellow", "green", "blue", "pink" ) + jteOutput.writeContent("\n\n
\n
\n ") + jteOutput.writeContent("\n
\n\n
\n ") + jteOutput.writeContent("\n
\n \n ") + jteOutput.setContext("span", null) + jteOutput.writeUserContent(model.message.author) + jteOutput.writeContent("\n \n \n ") + jteOutput.setContext("span", null) + jteOutput.writeUserContent(dateFormatter.format(model.message.createdAt)) + jteOutput.writeContent(" • ") + jteOutput.setContext("span", null) + jteOutput.writeUserContent(timeFormatter.format(model.message.createdAt)) + jteOutput.writeContent("\n \n
\n\n ") + jteOutput.writeContent("\n
\n ") + jteOutput.setContext("div", null) + jteOutput.writeUserContent(model.message.content) + jteOutput.writeContent("\n
\n
\n
\n
") + } + @JvmStatic fun renderMap(jteOutput:gg.jte.html.HtmlTemplateOutput, jteHtmlInterceptor:gg.jte.html.HtmlInterceptor?, params:Map) { + val model = params["model"] as MessageTemplate + render(jteOutput, jteHtmlInterceptor, model); + } +} +} diff --git a/src/main/kotlin/at/dokkae/homepage/Homepage.kt b/src/main/kotlin/at/dokkae/homepage/Homepage.kt index 3f910d9..636ad47 100644 --- a/src/main/kotlin/at/dokkae/homepage/Homepage.kt +++ b/src/main/kotlin/at/dokkae/homepage/Homepage.kt @@ -1,10 +1,12 @@ package at.dokkae.homepage +import at.dokkae.homepage.config.Env import at.dokkae.homepage.config.Environment import at.dokkae.homepage.extensions.Precompiled import at.dokkae.homepage.repository.MessageRepository import at.dokkae.homepage.repository.impls.JooqMessageRepository -import at.dokkae.homepage.templates.Index +import at.dokkae.homepage.templates.IndexTemplate +import at.dokkae.homepage.templates.MessageTemplate import io.github.cdimascio.dotenv.dotenv import org.flywaydb.core.Flyway import org.http4k.core.HttpHandler @@ -14,18 +16,19 @@ import org.http4k.core.Status import org.http4k.core.body.form import org.http4k.core.getFirst import org.http4k.core.toParametersMap +import org.http4k.routing.ResourceLoader import org.http4k.routing.bindHttp import org.http4k.routing.bindSse import org.http4k.routing.poly import org.http4k.routing.routes import org.http4k.routing.sse +import org.http4k.routing.static import org.http4k.server.Jetty import org.http4k.server.asServer import org.http4k.sse.Sse import org.http4k.sse.SseMessage import org.http4k.sse.SseResponse import org.http4k.template.JTETemplates -import org.http4k.template.ViewModel import org.jooq.SQLDialect import org.jooq.impl.DSL import java.sql.DriverManager @@ -52,14 +55,11 @@ data class Message( val id: UUID = UUID.randomUUID(), val createdAt: Instant = Instant.now(), val updatedAt: Instant? = null -) : ViewModel { +) { init { require(author.length <= 31) { "Author must be 31 characters or less" } require(content.length <= 255) { "Content must be 255 characters or less" } } - - - override fun template(): String = "partials/Message" } fun main() { @@ -77,10 +77,19 @@ fun main() { val messageRepository: MessageRepository = JooqMessageRepository(dslContext) val subscribers = CopyOnWriteArrayList() - val renderer = JTETemplates().Precompiled("build/generated-resources/jte") + val renderer = when (env.appEnv) { + Env.DEVELOPMENT -> { + println("🔥 Hot-Reloading JTE templates") + JTETemplates().HotReload("src/main/kte") + } + Env.PRODUCTION -> { + println("📦 Loading pre-compiled JTE templates") + JTETemplates().Precompiled("build/generated-resources/jte") + } + } val indexHandler: HttpHandler = { - Response(Status.OK).body(renderer(Index(messageRepository.findAll()))) + Response(Status.OK).body(renderer(IndexTemplate(messageRepository.findAll()))) } val sse = sse( @@ -94,30 +103,38 @@ fun main() { ) val http = routes( + static(ResourceLoader.Classpath("static")), + "/" bindHttp GET to indexHandler, "/messages" bindHttp POST to { req -> - val params = req.form().toParametersMap() - val author = params.getFirst("author").takeIf { !it.isNullOrBlank() } ?: "Anonymous" - val message = params.getFirst("message") + try { + val params = req.form().toParametersMap() + val author = params.getFirst("author").takeIf { !it.isNullOrBlank() } ?: "Anonymous" + val message = params.getFirst("message") - if (message == null) { - Response(Status.BAD_REQUEST) - } else { - val msg = Message(author, message) - val sseMsg = SseMessage.Data(renderer(msg)) + if (message == null) { + Response(Status.BAD_REQUEST) + } else { + val msg = Message(author, message) + val sseMsg = SseMessage.Data(renderer(MessageTemplate(msg))) - messageRepository.save(msg) - subscribers.forEach { - thread { it.send(sseMsg) } + messageRepository.save(msg) + subscribers.forEach { + thread { it.send(sseMsg) } + } + + Response(Status.CREATED) } + } catch (ex: Exception) { + println("Failed to receive message: ${ex.toString()} ${ex.message}") - Response(Status.CREATED) + Response(Status.INTERNAL_SERVER_ERROR) } } ) - poly(http, sse).asServer(Jetty(port = env.port)).start() + poly(http, sse).asServer(Jetty(port = env.appPort)).start() - println("Server started on http://${env.host}:${env.port}") + println("Server started on http://${env.appDomain}:${env.appPort}") } diff --git a/src/main/kotlin/at/dokkae/homepage/config/Environment.kt b/src/main/kotlin/at/dokkae/homepage/config/Environment.kt index 6321095..528c86e 100644 --- a/src/main/kotlin/at/dokkae/homepage/config/Environment.kt +++ b/src/main/kotlin/at/dokkae/homepage/config/Environment.kt @@ -2,9 +2,15 @@ package at.dokkae.homepage.config import io.github.cdimascio.dotenv.Dotenv +enum class Env { + DEVELOPMENT, + PRODUCTION, +} + data class Environment( - val port: Int, - val host: String, + val appPort: Int, + val appDomain: String, + val appEnv: Env, val dbUrl: String, val dbUsername: String, val dbPassword: String, @@ -16,8 +22,9 @@ data class Environment( * @throws IllegalStateException if required environment variables were not found within the provided `dotenv` instance. */ fun load(dotenv: Dotenv): Environment = Environment( - port = requireEnv(dotenv, "PORT").toInt(), - host = requireEnv(dotenv, "HOST"), + appPort = requireEnv(dotenv, "APP_PORT").toInt(), + appDomain = requireEnv(dotenv, "APP_DOMAIN"), + appEnv = Env.valueOf(requireEnv(dotenv, "APP_ENV").uppercase()), dbUrl = requireEnv(dotenv, "DB_URL"), dbUsername = requireEnv(dotenv, "DB_USERNAME"), dbPassword = requireEnv(dotenv, "DB_PASSWORD"), diff --git a/src/main/kotlin/at/dokkae/homepage/templates/Index.kt b/src/main/kotlin/at/dokkae/homepage/templates/Index.kt index 1258984..1e75cb7 100644 --- a/src/main/kotlin/at/dokkae/homepage/templates/Index.kt +++ b/src/main/kotlin/at/dokkae/homepage/templates/Index.kt @@ -3,6 +3,6 @@ package at.dokkae.homepage.templates import at.dokkae.homepage.Message import org.http4k.template.ViewModel -data class Index(val messages: List = listOf()) : ViewModel { +data class IndexTemplate(val messages: List = listOf()) : ViewModel { override fun template(): String = "Index" } \ No newline at end of file diff --git a/src/main/kotlin/at/dokkae/homepage/templates/Message.kt b/src/main/kotlin/at/dokkae/homepage/templates/Message.kt new file mode 100644 index 0000000..1b9eb18 --- /dev/null +++ b/src/main/kotlin/at/dokkae/homepage/templates/Message.kt @@ -0,0 +1,8 @@ +package at.dokkae.homepage.templates + +import at.dokkae.homepage.Message +import org.http4k.template.ViewModel + +data class MessageTemplate(val message: Message) : ViewModel { + override fun template(): String = "partials/Message" +} \ No newline at end of file diff --git a/src/main/kte/Index.kte b/src/main/kte/Index.kte index 08857e8..7b7933e 100644 --- a/src/main/kte/Index.kte +++ b/src/main/kte/Index.kte @@ -1,159 +1,162 @@ -@import at.dokkae.homepage.templates.Index +@import at.dokkae.homepage.templates.IndexTemplate +@import at.dokkae.homepage.templates.MessageTemplate +@import gg.jte.support.ForSupport -@param model: Index +@param model: IndexTemplate - Simple Chat — http4k + JTE + htmx + Dokkae's Chat + - -
-

Simple Chat

+ +
+ +
+

Dokkae's Chat

+
-
- @for (message in model.messages.reversed()) - @template.partials.Message(message) - @endfor + +
+
+ @for (message in model.messages) + @template.partials.Message(MessageTemplate(message)) + @endfor +
+ +
+
+ +
+
+ +
+
- - - - + +
+
+
+ +
+ + + +
+
+
-

No auth — anyone can post. Messages are stored only in memory.

+ +
+ \ No newline at end of file diff --git a/src/main/kte/partials/Message.kte b/src/main/kte/partials/Message.kte index 91f406f..4e43280 100644 --- a/src/main/kte/partials/Message.kte +++ b/src/main/kte/partials/Message.kte @@ -1,11 +1,35 @@ -@import at.dokkae.homepage.Message +@import at.dokkae.homepage.templates.MessageTemplate +@import java.time.Instant +@import java.time.ZoneId +@import java.time.format.DateTimeFormatter +@import kotlin.math.absoluteValue -@param message: Message +@param model: MessageTemplate -
- ${message.author}: - ${message.content} - - (${message.createdAt.toString()}) - +!{val dateFormatter: DateTimeFormatter = DateTimeFormatter.ofPattern("dd.MM.yyyy").withZone(ZoneId.systemDefault())} +!{val timeFormatter: DateTimeFormatter = DateTimeFormatter.ofPattern("HH:mm").withZone(ZoneId.systemDefault())} +!{val borderColors = listOf("red", "orange", "yellow", "green", "blue", "pink" )} + +
+
+ +
+ +
+ +
+ + ${model.message.author} + + + ${dateFormatter.format(model.message.createdAt)} • ${timeFormatter.format(model.message.createdAt)} + +
+ + +
+ ${model.message.content} +
+
+
\ No newline at end of file diff --git a/src/main/resources/static/css/.keep b/src/main/resources/static/css/.keep new file mode 100644 index 0000000..e69de29 diff --git a/src/main/resources/static/css/index.css b/src/main/resources/static/css/index.css new file mode 100644 index 0000000..e69de29 diff --git a/src/main/resources/static/images/.keep b/src/main/resources/static/images/.keep new file mode 100644 index 0000000..e69de29 diff --git a/src/main/resources/static/js/.keep b/src/main/resources/static/js/.keep new file mode 100644 index 0000000..e69de29