Skip to content

Commit 09cda6c

Browse files
committed
EXPERIMENTAL: Migrate Foxy Website (partially) to Kotlin
1 parent 46f9c05 commit 09cda6c

File tree

108 files changed

+9390
-2
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

108 files changed

+9390
-2
lines changed

buildSrc/src/main/kotlin/Versions.kt

+6-2
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ object Versions {
77

88
// Libraries
99
const val KTOR = "3.0.3"
10-
const val KOTLIN_SERIALIZATION = "1.6.0"
11-
const val KOTLIN_COROUTINES = "1.8.1"
10+
const val KOTLIN_SERIALIZATION = "1.8.0"
11+
const val KOTLIN_COROUTINES = "1.10.1"
1212
const val KOTLIN_LOGGING = "2.1.16"
1313
const val JVM_TARGET = 21
1414
const val LOGBACK = "1.5.16"
@@ -17,4 +17,8 @@ object Versions {
1717
const val JDA_KTX = "0.12.0"
1818
const val SHADOW_JAR = "7.1.2"
1919
const val JACKSON = "2.18.0"
20+
const val HIKARICP = "6.2.1"
21+
const val EXPOSED = "0.59.0"
22+
const val POSTGRESQL = "42.7.1"
23+
const val KOTLINX_HTML = "0.8.0"
2024
}

web/build.gradle.kts

+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
plugins {
2+
kotlin("jvm") version Versions.KOTLIN
3+
kotlin("plugin.serialization") version Versions.KOTLIN_SERIALIZATION
4+
id("com.github.johnrengelman.shadow")
5+
id("io.freefair.sass-java") version "8.12.1"
6+
application
7+
}
8+
9+
group = "net.cakeyfox.foxy"
10+
version = Versions.FOXY_VERSION
11+
12+
repositories {
13+
mavenCentral()
14+
}
15+
16+
application {
17+
mainClass.set("net.cakeyfox.foxy.web.FoxyWebLauncher")
18+
}
19+
20+
dependencies {
21+
implementation(project(":common"))
22+
implementation(project(":cirno-serializable"))
23+
24+
// Discord
25+
implementation(libs.jda)
26+
27+
// MongoDB
28+
implementation("org.mongodb:bson-kotlinx:5.3.0")
29+
implementation("org.mongodb:mongodb-driver-kotlin-coroutine:5.3.0")
30+
31+
// Frontend Stuff
32+
implementation("org.jetbrains.kotlinx:kotlinx-html-jvm:0.9.1")
33+
implementation("io.ktor:ktor-server-html-builder:${Versions.KTOR}")
34+
35+
// Server Stuff
36+
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
37+
implementation("io.ktor:ktor-serialization-kotlinx-json:${Versions.KTOR}")
38+
implementation("io.ktor:ktor-server-netty:${Versions.KTOR}")
39+
implementation("io.ktor:ktor-server-core:${Versions.KTOR}")
40+
implementation("io.ktor:ktor-server-content-negotiation:${Versions.KTOR}")
41+
implementation("io.ktor:ktor-server-auth:${Versions.KTOR}")
42+
43+
// Coroutines and DateTime
44+
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:${Versions.KOTLIN_COROUTINES}")
45+
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-debug:${Versions.KOTLIN_COROUTINES}")
46+
implementation("org.jetbrains.kotlinx:kotlinx-datetime:${Versions.KOTLINX_DATE_TIME}")
47+
48+
// Logging
49+
implementation("ch.qos.logback:logback-classic:${Versions.LOGBACK}")
50+
implementation("io.github.microutils:kotlin-logging:${Versions.KOTLIN_LOGGING}")
51+
52+
// Serialization
53+
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:${Versions.KOTLIN_SERIALIZATION}")
54+
implementation("org.jetbrains.kotlinx:kotlinx-serialization-hocon:${Versions.KOTLIN_SERIALIZATION}")
55+
implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:${Versions.JACKSON}")
56+
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:${Versions.JACKSON}")
57+
}
58+
59+
60+
tasks.named<io.freefair.gradle.plugins.sass.SassCompile>("compileSass") {
61+
source = fileTree("src/main/resources/scss")
62+
destinationDir = file("src/main/resources/css")
63+
}
64+
65+
tasks {
66+
shadowJar {
67+
archiveBaseName.set("FoxyWeb")
68+
archiveVersion.set(version.toString())
69+
archiveClassifier.set("")
70+
}
71+
}
72+
73+
kotlin {
74+
jvmToolchain(Versions.JVM_TARGET)
75+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package net.cakeyfox.foxy.web
2+
3+
import io.ktor.serialization.kotlinx.json.*
4+
import io.ktor.server.application.*
5+
import io.ktor.server.engine.*
6+
import io.ktor.server.http.content.*
7+
import io.ktor.server.netty.*
8+
import io.ktor.server.plugins.contentnegotiation.*
9+
import io.ktor.server.response.*
10+
import io.ktor.server.routing.*
11+
import net.cakeyfox.foxy.web.routes.HomePageRoute
12+
import net.cakeyfox.foxy.web.routes.SupportPageRoute
13+
14+
class FoxyWebInstance {
15+
private val server = embeddedServer(Netty, port = 3040) {
16+
install(ContentNegotiation) { json() }
17+
18+
routing {
19+
get("/") { call.respondRedirect("/br/")}
20+
HomePageRoute().apply { homePage("/{lang}/") }
21+
SupportPageRoute().apply { supportPage("/{lang}/support") }
22+
23+
staticResources("/assets", "assets")
24+
staticResources("/styles", "css")
25+
}
26+
}
27+
28+
fun start() {
29+
server.start(wait = true)
30+
}
31+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package net.cakeyfox.foxy.web
2+
3+
object FoxyWebLauncher {
4+
5+
@JvmStatic
6+
fun main(args: Array<String>) {
7+
FoxyWebInstance().start()
8+
}
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package net.cakeyfox.foxy.web.frontend.global
2+
3+
import kotlinx.html.*
4+
import kotlinx.html.stream.createHTML
5+
import net.cakeyfox.common.Constants
6+
7+
class AnalyticsScripts {
8+
fun build(): String {
9+
return createHTML().html {
10+
body {
11+
link(rel = "canonical", href = Constants.FOXY_WEBSITE)
12+
13+
script {
14+
attributes["async"] = "async"
15+
src = "https://www.googletagmanager.com/gtag/js?id=G-E3J7N4BP8L"
16+
}
17+
script {
18+
unsafe {
19+
+"""
20+
window.dataLayer = window.dataLayer || [];
21+
function gtag(){dataLayer.push(arguments);}
22+
gtag('js', new Date());
23+
gtag('config', 'G-E3J7N4BP8L');
24+
""".trimIndent()
25+
}
26+
}
27+
}
28+
}
29+
}
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package net.cakeyfox.foxy.web.frontend.global
2+
3+
import kotlinx.html.*
4+
import kotlinx.html.stream.createHTML
5+
import net.cakeyfox.foxy.web.utils.Locale
6+
7+
class HeaderBuilder(private val locale: Locale) {
8+
fun build(): String {
9+
val localeKey = locale.localeKey
10+
return createHTML().html {
11+
head {
12+
link(rel = "stylesheet", href = "/styles/header.css")
13+
link {
14+
href = "https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css"
15+
rel = "stylesheet"
16+
}
17+
}
18+
19+
body {
20+
header {
21+
nav("marisa-navbar fixed") {
22+
id = "navigation-bar"
23+
24+
div("left-entries") {
25+
button {
26+
id = "menu-open"
27+
classes = setOf("mobile")
28+
29+
i("fas fa-bars")
30+
}
31+
32+
button {
33+
id = "menu-close"
34+
classes = setOf("mobile")
35+
style = "display: none;"
36+
37+
i("fas fa-times")
38+
}
39+
40+
div("entry foxy-navbar-logo") {
41+
a("/$localeKey/") {
42+
img {
43+
src = "/assets/images/FoxyAvatar.png"
44+
45+
style =
46+
"line-height: 0px; width: 40px; height: 40px; position: absolute; top: 3px; border-radius: 100%;"
47+
48+
}
49+
}
50+
}
51+
52+
div("entry") { a("/$localeKey/support") { +locale["entries.support"] } }
53+
div("entry") { a("/$localeKey/status") { +locale["entries.status"] } }
54+
div("entry") { a("/$localeKey/support/terms") { +locale["entries.termsOfUse"] } }
55+
div("entry") { a("/upvote") { +locale["entries.upvote"] } }
56+
div("entry") { a("/$localeKey/premium") { +locale["entries.premium"] } }
57+
div("entry") { a("/$localeKey/commands") { +locale["entries.commands"] } }
58+
div("entry") { a("/$localeKey/store") { +locale["entries.dailyShop"] } }
59+
div("entry") { a("/$localeKey/daily") { +locale["entries.daily"] } }
60+
}
61+
62+
div("right-entries") {
63+
div("entry") {
64+
a("/$localeKey/dashboard") {
65+
id = "login-button"
66+
span { +"Login" }
67+
}
68+
}
69+
}
70+
}
71+
}
72+
}
73+
}
74+
}
75+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package net.cakeyfox.foxy.web.frontend.home
2+
3+
import kotlinx.html.*
4+
import kotlinx.html.stream.createHTML
5+
import net.cakeyfox.common.Constants
6+
import net.cakeyfox.foxy.web.frontend.global.AnalyticsScripts
7+
import net.cakeyfox.foxy.web.frontend.global.HeaderBuilder
8+
import net.cakeyfox.foxy.web.utils.Locale
9+
10+
class HomePageBuilder(private val locale: Locale) {
11+
private val localeKey = locale.localeKey
12+
13+
fun build(): String {
14+
return createHTML().html {
15+
lang = "pt-br"
16+
head {
17+
link {
18+
rel = "canonical"
19+
href = Constants.FOXY_WEBSITE
20+
}
21+
meta(charset = "UTF-8")
22+
meta {
23+
httpEquiv = "X-UA-Compatible"
24+
content = "IE=edge"
25+
}
26+
meta {
27+
name = "viewport"
28+
content = "width=device-width, initial-scale=1.0"
29+
}
30+
link(rel = "icon", href = "/assets/images/foxycake.png")
31+
link(rel = "stylesheet", href = "/styles/header.css", type = "text/css")
32+
link(rel = "stylesheet", href = "/styles/footer.css", type = "text/css")
33+
link(rel = "stylesheet", href = "/styles/main.css", type = "text/css")
34+
35+
unsafe { AnalyticsScripts().build() }
36+
37+
title { +locale["homepage-title"] }
38+
}
39+
body {
40+
unsafe { +HeaderBuilder(locale).build() }
41+
main {
42+
h2(classes = "greetings") { +locale["greetings"] }
43+
img(classes = "foxy-logo") {
44+
src = "/assets/images/logo.png"
45+
}
46+
h2(classes = "greetings-2") { +locale["greetings-2"] }
47+
br {}
48+
a(href = Constants.INVITE_LINK) {
49+
button(classes = "main-buttons", type = ButtonType.button) {
50+
id = "add"
51+
+locale["buttons.add"]
52+
}
53+
}
54+
a(href = "/$localeKey/support") {
55+
button(classes = "main-buttons", type = ButtonType.button) { +locale["buttons.support"] }
56+
}
57+
br {}
58+
br {}
59+
}
60+
61+
unsafe { +FeatureDivBuilder(locale).build() }
62+
63+
style {
64+
unsafe {
65+
+"""
66+
img#yourkit {
67+
width: 13rem;
68+
height: auto;
69+
margin-left: 20px;
70+
}
71+
72+
a {
73+
color: #e7385d;
74+
}
75+
""".trimIndent()
76+
}
77+
}
78+
}
79+
}
80+
}
81+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package net.cakeyfox.foxy.web.frontend.home
2+
3+
import kotlinx.html.*
4+
import kotlinx.html.stream.createHTML
5+
6+
class ServersBuilder {
7+
fun build(): String {
8+
return createHTML().html {
9+
body {
10+
div(classes = "server-container") {
11+
div(classes = "server-wrapper") {
12+
a(href = "https://discord.gg/fhany", target = "_blank") {
13+
title = "Servidor da Fhany"
14+
div(classes = "server-card") {
15+
img(classes = "server-avatar") {
16+
src = "/assets/icons/fhany.png"
17+
}
18+
}
19+
}
20+
a(href = "https://discord.gg/Q9BzXvHbpb", target = "_blank") {
21+
title = "Cafeteria do Sally"
22+
div(classes = "server-card") {
23+
img(classes = "server-avatar") {
24+
src = "/assets/icons/sally.gif"
25+
}
26+
}
27+
}
28+
a(
29+
href = "https://discord.gg/station-coffee-772182631798276158",
30+
target = "_blank"
31+
) {
32+
title = "Station Coffee"
33+
div(classes = "server-card") {
34+
img(classes = "server-avatar") {
35+
src = "/assets/icons/stationcoffee.gif"
36+
}
37+
}
38+
}
39+
a(href = "https://discord.gg/gMz858Zahf", target = "_blank") {
40+
title = "Red Dead Brasil"
41+
div(classes = "server-card") {
42+
img(classes = "server-avatar") {
43+
src = "/assets/icons/raposas.png"
44+
}
45+
}
46+
}
47+
}
48+
}
49+
}
50+
}
51+
}
52+
}

0 commit comments

Comments
 (0)