Skip to content

Commit 8025358

Browse files
committed
javalin 7
1 parent 642ed69 commit 8025358

9 files changed

Lines changed: 99 additions & 73 deletions

File tree

Dockerfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
FROM maven:3.5.2-jdk-8-alpine as maven-build
1+
FROM maven:3.9-eclipse-temurin-17-alpine as maven-build
22
WORKDIR /app
33
COPY . .
44
RUN mvn verify
55

6-
FROM openjdk:8-jre-alpine
6+
FROM eclipse-temurin:17-jre-alpine
77
RUN adduser \
88
-h /var/github-summary \
99
-D -u 1000 \

pom.xml

Lines changed: 17 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -10,49 +10,34 @@
1010
<name>${artifactId}</name>
1111

1212
<properties>
13-
<java.version>11</java.version>
14-
<kotlin.compiler.jvmTarget>11</kotlin.compiler.jvmTarget>
15-
<kotlin.version>1.7.20</kotlin.version>
13+
<java.version>17</java.version>
14+
<kotlin.compiler.jvmTarget>17</kotlin.compiler.jvmTarget>
15+
<kotlin.version>2.2.20</kotlin.version>
1616
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
1717
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
1818
</properties>
1919

2020
<dependencies>
2121
<dependency>
2222
<groupId>io.javalin</groupId>
23-
<artifactId>javalin</artifactId>
24-
<version>5.0.1</version>
23+
<artifactId>javalin-bundle</artifactId>
24+
<version>7.0.0-beta.3</version>
2525
</dependency>
2626
<dependency>
27-
<groupId>com.nixxcode.jvmbrotli</groupId>
28-
<artifactId>jvmbrotli</artifactId>
29-
<version>0.2.0</version>
30-
</dependency>
31-
<dependency>
32-
<groupId>com.fasterxml.jackson.module</groupId>
33-
<artifactId>jackson-module-kotlin</artifactId>
34-
<version>2.13.4</version>
27+
<groupId>org.slf4j</groupId>
28+
<artifactId>slf4j-simple</artifactId>
29+
<version>2.0.17</version>
3530
</dependency>
3631
<dependency>
3732
<groupId>org.apache.commons</groupId>
3833
<artifactId>commons-lang3</artifactId>
39-
<version>3.12.0</version>
40-
</dependency>
41-
<dependency>
42-
<groupId>org.slf4j</groupId>
43-
<artifactId>slf4j-simple</artifactId>
44-
<version>1.7.26</version>
34+
<version>3.17.0</version>
4535
</dependency>
4636
<dependency>
4737
<groupId>org.eclipse.mylyn.github</groupId>
4838
<artifactId>org.eclipse.egit.github.core</artifactId>
4939
<version>2.1.5</version>
5040
</dependency>
51-
<dependency>
52-
<groupId>org.jetbrains.kotlin</groupId>
53-
<artifactId>kotlin-stdlib-jdk8</artifactId>
54-
<version>${kotlin.version}</version>
55-
</dependency>
5641
<dependency>
5742
<groupId>org.jetbrains.kotlin</groupId>
5843
<artifactId>kotlin-test</artifactId>
@@ -62,13 +47,13 @@
6247
<dependency>
6348
<groupId>com.h2database</groupId>
6449
<artifactId>h2</artifactId>
65-
<version>2.2.220</version>
50+
<version>2.3.232</version>
6651
<scope>runtime</scope>
6752
</dependency>
6853
<dependency>
6954
<groupId>com.zaxxer</groupId>
7055
<artifactId>HikariCP</artifactId>
71-
<version>5.0.1</version>
56+
<version>6.2.1</version>
7257
</dependency>
7358
</dependencies>
7459

@@ -87,7 +72,7 @@
8772
<goal>compile</goal>
8873
</goals>
8974
<configuration>
90-
<jvmTarget>11</jvmTarget>
75+
<jvmTarget>17</jvmTarget>
9176
<sourceDirs>
9277
<sourceDir>${project.basedir}/src/main/kotlin</sourceDir>
9378
</sourceDirs>
@@ -102,7 +87,7 @@
10287
</execution>
10388
</executions>
10489
<configuration>
105-
<jvmTarget>11</jvmTarget>
90+
<jvmTarget>17</jvmTarget>
10691
</configuration>
10792
</plugin>
10893
<plugin>
@@ -131,17 +116,17 @@
131116
<groupId>org.apache.maven.plugins</groupId>
132117
<artifactId>maven-compiler-plugin</artifactId>
133118
<configuration>
134-
<source>11</source>
135-
<target>11</target>
119+
<source>17</source>
120+
<target>17</target>
136121
</configuration>
137122
</plugin>
138123

139124
<plugin>
140125
<groupId>com.heroku.sdk</groupId>
141126
<artifactId>heroku-maven-plugin</artifactId>
142-
<version>1.1.3</version>
127+
<version>3.0.7</version>
143128
<configuration>
144-
<jdkVersion>11</jdkVersion>
129+
<jdkVersion>17</jdkVersion>
145130
<appName>profile-summary-for-github</appName>
146131
<processTypes>
147132
<!-- Tell Heroku how to launch your application -->

src/main/kotlin/app/Config.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,6 @@ object Config {
2121

2222
private fun getProperty(name: String): String? = getHerokuProperty(name) ?: System.getProperty(name)
2323

24-
private fun getHerokuProperty(envStr: String) = ProcessBuilder().environment()[envStr.toUpperCase().replace("-", "_")]
24+
private fun getHerokuProperty(envStr: String) = ProcessBuilder().environment()[envStr.uppercase().replace("-", "_")]
2525

2626
}

src/main/kotlin/app/GhService.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ object GhService {
2929
val users: UserService get() = userServices.maxByOrNull { it.client.remainingRequests }!!
3030
val watchers: WatcherService get() = watcherServices.maxByOrNull { it.client.remainingRequests }!!
3131

32-
val remainingRequests: Int get() = clients.sumBy { it.remainingRequests }
32+
val remainingRequests: Int get() = clients.sumOf { it.remainingRequests }
3333

3434
// Allows for parallel iteration and O(1) put/remove
3535
private val clientSessions = ConcurrentHashMap<WsContext, Boolean>()

src/main/kotlin/app/Main.kt

Lines changed: 43 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
package app
22

33
import io.javalin.Javalin
4+
import io.javalin.compression.CompressionStrategy
45
import io.javalin.http.BadRequestResponse
56
import io.javalin.http.NotFoundResponse
7+
import io.javalin.http.queryParamAsClass
68
import io.javalin.http.staticfiles.Location
7-
import io.javalin.http.util.NaiveRateLimit
9+
import io.javalin.plugin.bundled.JavalinVuePlugin
10+
import io.javalin.plugin.bundled.RateLimitPlugin
811
import io.javalin.vue.VueComponent
9-
import io.javalin.http.queryParamAsClass
1012
import org.eclipse.jetty.server.HttpConnectionFactory
1113
import org.eclipse.jetty.server.Server
1214
import org.eclipse.jetty.server.ServerConnector
@@ -17,48 +19,55 @@ import java.util.concurrent.TimeUnit
1719
fun main() {
1820

1921
val log = LoggerFactory.getLogger("app.MainKt")
20-
val app = Javalin.create {
21-
it.plugins.enableSslRedirects()
22-
it.staticFiles.add("/public", Location.CLASSPATH)
23-
it.compression.brotliAndGzip()
24-
it.jetty.server {
25-
Server(QueuedThreadPool(200, 8, 120000)).apply {
26-
connectors = arrayOf(ServerConnector(server).apply {
27-
port = Config.getPort() ?: 7070
28-
idleTimeout = 120_000
29-
connectionFactories.filterIsInstance<HttpConnectionFactory>().forEach {
30-
it.httpConfiguration.sendServerVersion = false
31-
}
32-
})
33-
}
22+
Javalin.start { config ->
23+
config.bundledPlugins.enableSslRedirects()
24+
config.staticFiles.add("/public", Location.CLASSPATH)
25+
config.http.compressionStrategy = CompressionStrategy.GZIP
26+
config.unsafe.jettyInternal.server = Server(QueuedThreadPool(200, 8, 120000)).apply {
27+
connectors = arrayOf(ServerConnector(server).apply {
28+
port = Config.getPort() ?: 7070
29+
idleTimeout = 120_000
30+
connectionFactories.filterIsInstance<HttpConnectionFactory>().forEach {
31+
it.httpConfiguration.sendServerVersion = false
32+
}
33+
})
3434
}
35-
it.vue.optimizeDependencies = false
36-
}.apply {
37-
before("/api/*") { NaiveRateLimit.requestPerTimeUnit(it, 20, TimeUnit.MINUTES) }
38-
get("/api/can-load") { ctx ->
39-
val user = ctx.queryParamAsClass<String>("user").get()
40-
if (!UserService.userExists(user)) throw NotFoundResponse()
41-
ctx.status(if (UserService.canLoadUser(user)) 200 else 400)
35+
config.registerPlugin(RateLimitPlugin({}))
36+
config.registerPlugin(JavalinVuePlugin { vue ->
37+
vue.optimizeDependencies = false
38+
})
39+
40+
// Routes
41+
config.routes.before("/api/*") { it.with(RateLimitPlugin::class).requestPerTimeUnit(20, TimeUnit.MINUTES) }
42+
config.routes.get("/api/can-load") { ctx ->
43+
val user = ctx.queryParamAsClass<String>("user").required().get()
44+
// Use quick check that doesn't consume GitHub API requests
45+
// This runs before the spinner is shown
46+
ctx.status(if (UserService.canLoadUserQuick(user)) 200 else 400)
4247
}
43-
get("/api/user/{user}") { ctx ->
48+
config.routes.get("/api/user/{user}") { ctx ->
4449
val user = ctx.pathParam("user")
4550
if (!UserService.userExists(user)) throw NotFoundResponse()
4651
UserService.getUserIfCanLoad(user)?.let { ctx.json(it) } ?: throw BadRequestResponse("Can't load user")
4752
}
48-
get("/search", VueComponent("search-view"))
49-
get("/user/{user}", VueComponent("user-view"))
50-
ws("/rate-limit-status") { ws ->
53+
config.routes.get("/search", VueComponent("search-view"))
54+
config.routes.get("/user/{user}", VueComponent("user-view"))
55+
config.routes.ws("/rate-limit-status") { ws ->
5156
ws.onConnect { GhService.registerClient(it) }
5257
ws.onClose { GhService.unregisterClient(it) }
5358
ws.onError { GhService.unregisterClient(it) }
5459
}
55-
after { it.cookie("gtm-id", Config.getGtmId() ?: "") } // what is this?
56-
}.exception(Exception::class.java) { e, ctx ->
57-
log.warn("Uncaught exception", e)
58-
ctx.status(500)
59-
}.error(404, "html") {
60-
it.redirect("/search")
61-
}.start()
60+
config.routes.after { it.cookie("gtm-id", Config.getGtmId() ?: "") } // what is this?
61+
62+
// Exception and error handlers
63+
config.routes.exception(Exception::class.java) { e, ctx ->
64+
log.warn("Uncaught exception", e)
65+
ctx.status(500)
66+
}
67+
config.routes.error(404, "html") {
68+
it.redirect("/search")
69+
}
70+
}
6271

6372
UserService.syncWatchers()
6473

src/main/kotlin/app/UserService.kt

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,16 @@ object UserService {
2525
private fun remainingRequests(): Int = GhService.remainingRequests
2626
private fun hasFreeRemainingRequests(): Boolean = remainingRequests() > (freeRequestCutoff ?: remainingRequests())
2727

28+
// Lightweight check that doesn't consume GitHub API requests
29+
// Used by /api/can-load to check before showing the spinner
30+
// This is a permissive check - the actual strict check happens in getUserIfCanLoad
31+
fun canLoadUserQuick(user: String): Boolean {
32+
val userCacheJson = CacheService.selectJsonFromDb(user)
33+
return Config.unrestricted()
34+
|| (userCacheJson != null)
35+
|| remainingRequests() > 0 // Just check if ANY requests are available
36+
}
37+
2838
fun canLoadUser(user: String): Boolean {
2939
val userCacheJson = CacheService.selectJsonFromDb(user)
3040
return Config.unrestricted()
@@ -52,7 +62,7 @@ object UserService {
5262
}
5363

5464
private fun hasStarredRepo(username: String): Boolean {
55-
val login = username.toLowerCase()
65+
val login = username.lowercase()
5666
if (watchers.contains(login)) return true
5767
syncWatchers()
5868
return watchers.contains(login)
@@ -71,7 +81,7 @@ object UserService {
7181
}
7282

7383
private fun addAllWatchers(pageNumber: Int) = try {
74-
GhService.watchers.pageWatchers(repo, pageNumber, pageSize).first().forEach { watchers.add(it.login.toLowerCase()) }
84+
GhService.watchers.pageWatchers(repo, pageNumber, pageSize).first().forEach { watchers.add(it.login.lowercase()) }
7585
} catch (e: Exception) {
7686
log.info("Exception while adding watchers", e)
7787
}

src/main/resources/logback.xml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<configuration>
3+
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
4+
<encoder>
5+
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
6+
</encoder>
7+
</appender>
8+
9+
<root level="INFO">
10+
<appender-ref ref="STDOUT" />
11+
</root>
12+
13+
<!-- Set Javalin to INFO level -->
14+
<logger name="io.javalin" level="INFO" />
15+
16+
<!-- Set Jetty to WARN level to reduce noise -->
17+
<logger name="org.eclipse.jetty" level="WARN" />
18+
</configuration>
19+

src/main/resources/vue/components/app-frame.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<slot :requests-left="requestsLeft"></slot>
55
</main>
66
<footer>
7-
GitHub profile summary is built with <a href="https://javalin.io">Javalin 5.0.0</a> <small>(kotlin web framework)</small> and
7+
GitHub profile summary is built with <a href="https://javalin.io">Javalin 7</a> <small>(kotlin web framework)</small> and
88
<a href="http://www.chartjs.org/docs/latest/" target="_blank">chart.js</a> <small>(visualization)</small>.
99
Source is on <a href="https://github.com/tipsy/profile-summary-for-github" target="_blank">GitHub</a>.
1010
</footer>

system.properties

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
java.runtime.version=17
2+
maven.version=3.9.4
3+

0 commit comments

Comments
 (0)