Skip to content

Commit f2cd560

Browse files
committed
web day 8
1 parent f98ff7f commit f2cd560

File tree

23 files changed

+402
-65
lines changed

23 files changed

+402
-65
lines changed

build.gradle

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,8 @@ dependencies {
6262
implementation include("io.ktor:ktor-server-sessions:${project.ktor_version}")
6363
implementation include("io.ktor:ktor-serialization-kotlinx-json:${project.ktor_version}")
6464
implementation include("io.ktor:ktor-server-content-negotiation:${project.ktor_version}")
65-
implementation include("io.ktor:ktor-server-status-pages:$ktor_version")
66-
implementation include("io.ktor:ktor-server-cors:$ktor_version")
65+
implementation include("io.ktor:ktor-server-status-pages:${project.ktor_version}")
66+
implementation include("io.ktor:ktor-server-cors:${project.ktor_version}")
6767
include "io.ktor:ktor-events:${project.ktor_version}"
6868
include "io.ktor:ktor-utils:${project.ktor_version}"
6969
include "io.ktor:ktor-network-tls:${project.ktor_version}"
@@ -76,6 +76,11 @@ dependencies {
7676
include "io.netty:netty-transport-classes-kqueue:${project.netty_version}"
7777
include "io.netty:netty-codec-http2:${project.netty_version}"
7878
include "io.netty:netty-codec-http:${project.netty_version}"
79+
80+
// ktor client
81+
implementation include("io.ktor:ktor-client-core:${project.ktor_version}")
82+
implementation include("io.ktor:ktor-client-cio:${project.ktor_version}")
83+
implementation include("io.ktor:ktor-client-websockets:${project.ktor_version}")
7984
// already launched ktor
8085

8186
implementation include("com.typesafe:config:${project.hocon_config_version}")
@@ -136,6 +141,10 @@ tasks.register("preparePythonClient") {
136141
def pyBuildDir = file("$buildDir/python")
137142
def outputZip = file("$buildDir/resources/main/files/iwtcms_client.zip")
138143

144+
doFirst {
145+
outputZip.delete()
146+
}
147+
139148
copy {
140149
from(clientDir)
141150
into(pyBuildDir)
@@ -150,33 +159,39 @@ tasks.register("preparePythonClient") {
150159
}
151160
}
152161

162+
tasks.register("cleanWebFolder") {
163+
group = "build"
164+
165+
doFirst {
166+
file("$buildDir/resources/main/web").deleteDir()
167+
}
168+
}
169+
153170
tasks.register("compileTypeScript", NpxTask) {
154171
group = "build"
172+
dependsOn tasks.cleanWebFolder
155173
dependsOn tasks.npmInstall
156174
command = "tsc"
157175
}
158176

159177
tasks.register("compileSASS", NpxTask) {
160178
group = "build"
179+
dependsOn tasks.cleanWebFolder
161180
dependsOn tasks.npmInstall
162181
command = "sass"
163-
args = ["$buildDir/../src/main/web/.", "$buildDir/resources/main/web/."]
182+
args = ["$buildDir/../src/main/web/.:$buildDir/resources/main/web/.",]
164183
}
165184

166185
tasks.register("compileWeb"){
167186
group = "build"
168187
dependsOn tasks.compileTypeScript
169188
dependsOn tasks.compileSASS
170-
copy {
171-
from(file("src/main/web"))
172-
into(file("$buildDir/resources/main/web"))
173-
}
174189

175190
doLast {
176-
// By default web content in dev mode, to disable it we need to add dev.txt with "false" in root of web folder
177-
def file = new File("$buildDir/resources/main/web/dev.txt")
178-
file.createNewFile()
179-
file.write("false")
191+
copy {
192+
from(file("src/main/web"))
193+
into(file("$buildDir/resources/main/web"))
194+
}
180195
}
181196
}
182197

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package ua.pp.lumivoid.iwtcms.ktor.api.dev
2+
3+
import io.ktor.client.HttpClient
4+
import io.ktor.client.request.get
5+
import io.ktor.client.statement.HttpResponse
6+
import io.ktor.client.statement.readRawBytes
7+
import io.ktor.http.contentType
8+
import io.ktor.server.request.uri
9+
import io.ktor.server.response.respondBytes
10+
import io.ktor.server.routing.Routing
11+
import io.ktor.server.routing.get
12+
import ua.pp.lumivoid.iwtcms.Constants
13+
import ua.pp.lumivoid.iwtcms.ktor.api.requests.Request
14+
import ua.pp.lumivoid.iwtcms.util.Config
15+
16+
17+
object DevRequests: Request() {
18+
override val logger = Constants.EMBEDDED_SERVER_LOGGER
19+
override val PATH = "/{...}"
20+
21+
// TODO crashing if launching with built jar, needs to add includes
22+
23+
override val request: Routing.() -> Unit = {
24+
logger.info("Initializing $PATH request")
25+
get(PATH) {
26+
val httpClient = HttpClient()
27+
val response: HttpResponse = httpClient.get("${Config.readConfig().proxyUrl}${call.request.uri}")
28+
call.respondBytes(
29+
bytes = response.readRawBytes(),
30+
contentType = response.contentType(),
31+
status = response.status
32+
)
33+
}
34+
}
35+
}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
package ua.pp.lumivoid.iwtcms.ktor.api.dev
2+
3+
import io.ktor.client.HttpClient
4+
import io.ktor.client.engine.cio.CIO
5+
import io.ktor.client.plugins.websocket.WebSockets
6+
import io.ktor.client.plugins.websocket.webSocket
7+
import io.ktor.http.HttpMethod
8+
import io.ktor.server.routing.Routing
9+
import io.ktor.server.websocket.webSocket
10+
import io.ktor.websocket.CloseReason
11+
import io.ktor.websocket.Frame
12+
import io.ktor.websocket.close
13+
import io.ktor.websocket.readText
14+
import kotlinx.coroutines.CoroutineScope
15+
import kotlinx.coroutines.Dispatchers
16+
import kotlinx.coroutines.channels.consumeEach
17+
import kotlinx.coroutines.launch
18+
import kotlinx.coroutines.runBlocking
19+
import ua.pp.lumivoid.iwtcms.Constants
20+
import ua.pp.lumivoid.iwtcms.ktor.api.websockets.WS
21+
import ua.pp.lumivoid.iwtcms.ktor.api.websockets.WebSocketBaseInterface
22+
import ua.pp.lumivoid.iwtcms.util.Config
23+
24+
object DevWS: WS() {
25+
override val logger = Constants.EMBEDDED_SERVER_LOGGER
26+
override var WSinterface: WebSocketBaseInterface? = null
27+
override val PATH = "/{...}"
28+
29+
override val ws: Routing.() -> Unit = {
30+
logger.info("Initializing $PATH websocket")
31+
32+
webSocket(PATH) {
33+
var running = true
34+
35+
val client = Client(this@DevWS)
36+
37+
WSinterface = object : WebSocketBaseInterface {
38+
override fun sendMessage(message: String) {
39+
runBlocking { send(Frame.Text(message)) }
40+
}
41+
override fun shutdown() {
42+
running = false
43+
client.asWs()?.shutdown()
44+
runBlocking { close(CloseReason(CloseReason.Codes.NORMAL, "Client requested shutdown")) }
45+
}
46+
}
47+
48+
runCatching {
49+
incoming.consumeEach { frame ->
50+
if (frame is Frame.Text) {
51+
client.asWs()?.sendMessage(frame.readText())
52+
}
53+
}
54+
}.onFailure { exception ->
55+
logger.error("WebSocket exception: $exception")
56+
}
57+
58+
}
59+
}
60+
61+
override fun asWs(): WebSocketBaseInterface? = WSinterface
62+
63+
private class Client(parent: DevWS) {
64+
private var WSinterface: WebSocketBaseInterface? = null
65+
66+
init {
67+
CoroutineScope(Dispatchers.Default).launch {
68+
val client = HttpClient(CIO) {
69+
install(WebSockets)
70+
}
71+
72+
client.webSocket(
73+
method = HttpMethod.Get,
74+
host = Config.readConfig().proxyWsUrl,
75+
port = Config.readConfig().proxyWsPort,
76+
path = "/ws"
77+
) {
78+
var running = true
79+
80+
WSinterface = object : WebSocketBaseInterface {
81+
override fun sendMessage(message: String) {
82+
runBlocking { send(Frame.Text(message)) }
83+
}
84+
override fun shutdown() {
85+
running = false
86+
runBlocking { close(CloseReason(CloseReason.Codes.NORMAL, "Client requested shutdown")) }
87+
}
88+
}
89+
90+
while (running) {
91+
val message = incoming.receive() as? Frame.Text
92+
parent.asWs()?.sendMessage(message?.readText() ?: "")
93+
}
94+
}
95+
}
96+
}
97+
98+
fun asWs(): WebSocketBaseInterface? = WSinterface
99+
}
100+
}
101+
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
This directory created for dev features, for example proxy to vscode live server
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package ua.pp.lumivoid.iwtcms.ktor.api.requests
2+
3+
import io.ktor.http.HttpStatusCode
4+
import io.ktor.server.response.respondText
5+
import io.ktor.server.routing.Routing
6+
import io.ktor.server.routing.get
7+
import io.ktor.server.sessions.get
8+
import io.ktor.server.sessions.sessions
9+
import ua.pp.lumivoid.iwtcms.Constants
10+
import ua.pp.lumivoid.iwtcms.ktor.api.requests.ApiListGET.registerAPI
11+
import ua.pp.lumivoid.iwtcms.ktor.cookie.UserSession
12+
import ua.pp.lumivoid.iwtcms.util.Config
13+
14+
object CheckLoginGET: Request() {
15+
override val logger = Constants.EMBEDDED_SERVER_LOGGER
16+
override val PATH = "/api/checkLogin"
17+
18+
override val request: Routing.() -> Unit = {
19+
logger.info("Initializing $PATH request")
20+
registerAPI("CheckLoginGET", PATH)
21+
22+
get(PATH) {
23+
val session = call.sessions.get<UserSession>()
24+
if (session == null) {
25+
call.respondText("Not logged in", status = HttpStatusCode.Unauthorized)
26+
}
27+
28+
if (Config.readConfig().users.find { it.username == session?.name && it.id == session.id } != null) {
29+
call.respondText(session!!.name)
30+
} else {
31+
call.respondText("Not logged in", status = HttpStatusCode.Unauthorized)
32+
}
33+
}
34+
}
35+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package ua.pp.lumivoid.iwtcms.ktor.api.requests
2+
3+
import io.ktor.http.ContentType
4+
import io.ktor.server.response.respondText
5+
import io.ktor.server.routing.Routing
6+
import io.ktor.server.routing.get
7+
import ua.pp.lumivoid.iwtcms.Constants
8+
import ua.pp.lumivoid.iwtcms.ktor.api.requests.ApiListGET.registerAPI
9+
import ua.pp.lumivoid.iwtcms.util.Config
10+
11+
object IsDevEnabledGET: Request() {
12+
override val logger = Constants.EMBEDDED_SERVER_LOGGER
13+
override val PATH = "/api/isDevEnabled"
14+
15+
override val request: Routing.() -> Unit = {
16+
logger.info("Initializing $PATH request")
17+
registerAPI("IsDevEnabledGET", PATH)
18+
19+
get(PATH) {
20+
call.respondText(Config.readConfig().devMode.toString(), ContentType.Text.Plain)
21+
}
22+
}
23+
}

src/main/kotlin/ua/pp/lumivoid/iwtcms/ktor/api/requests/MainGET.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,11 @@ object MainGET: Request() {
1010
override val logger = Constants.EMBEDDED_SERVER_LOGGER
1111
override val PATH = "/"
1212

13+
private var devMode = true
14+
1315
override val request: Routing.() -> Unit = {
1416
logger.info("Initializing $PATH request")
1517
registerAPI("MainGET", PATH)
16-
1718
if (Config.readConfig().enableIWTCMSControlPanel) {
1819
staticResources(PATH, "web", index = "index.html")
1920
} else {

src/main/kotlin/ua/pp/lumivoid/iwtcms/ktor/api/websockets/ServerStatsWS.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ object ServerStatsWS: WS() {
7676
runCatching {
7777
incoming.consumeEach { frame ->
7878
if (frame is Frame.Text) {
79-
// send on avery message
79+
// send on every message
8080
val stats = ServerStats.getServerStats()
8181
val statsJson = json.encodeToString(stats)
8282
send(Frame.Text(statsJson))

src/main/kotlin/ua/pp/lumivoid/iwtcms/ktor/plugins/Routing.kt

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,13 @@ import io.ktor.server.plugins.statuspages.statusFile
1111
import io.ktor.server.routing.*
1212
import io.ktor.server.sessions.*
1313
import io.ktor.server.websocket.*
14+
import ua.pp.lumivoid.iwtcms.ktor.api.dev.DevRequests
15+
import ua.pp.lumivoid.iwtcms.ktor.api.dev.DevWS
1416
import ua.pp.lumivoid.iwtcms.ktor.api.requests.ApiListGET
17+
import ua.pp.lumivoid.iwtcms.ktor.api.requests.CheckLoginGET
1518
import ua.pp.lumivoid.iwtcms.ktor.api.requests.FilesGET
1619
import ua.pp.lumivoid.iwtcms.ktor.api.requests.IsAuthEnabledGET
20+
import ua.pp.lumivoid.iwtcms.ktor.api.requests.IsDevEnabledGET
1721
import ua.pp.lumivoid.iwtcms.ktor.api.requests.LoginPOST
1822
import ua.pp.lumivoid.iwtcms.ktor.api.requests.LogsHistoryGET
1923
import ua.pp.lumivoid.iwtcms.ktor.api.requests.MainGET
@@ -22,6 +26,7 @@ import ua.pp.lumivoid.iwtcms.ktor.api.requests.VersionGET
2226
import ua.pp.lumivoid.iwtcms.ktor.api.websockets.ConsoleWS
2327
import ua.pp.lumivoid.iwtcms.ktor.api.websockets.ServerStatsWS
2428
import ua.pp.lumivoid.iwtcms.ktor.cookie.UserSession
29+
import ua.pp.lumivoid.iwtcms.util.Config
2530
import kotlin.time.Duration.Companion.seconds
2631

2732
fun Application.configureRouting() {
@@ -36,6 +41,7 @@ fun Application.configureRouting() {
3641
cookie<UserSession>("USER_SESSION") {
3742
cookie.httpOnly = true
3843
cookie.secure = true
44+
cookie.sameSite = "None"
3945
}
4046
}
4147

@@ -50,20 +56,29 @@ fun Application.configureRouting() {
5056
install(CORS) {
5157
anyHost()
5258
allowHeader(HttpHeaders.ContentType)
59+
allowCredentials = true
5360
}
5461

5562
val r = routing {
5663
}
5764

58-
MainGET.request.invoke(r)
5965
LogsHistoryGET.request.invoke(r)
6066
LoginPOST.request.invoke(r)
6167
ApiListGET.request.invoke(r)
6268
PermitsGET.request.invoke(r)
6369
IsAuthEnabledGET.request.invoke(r)
6470
FilesGET.request.invoke(r)
6571
VersionGET.request.invoke(r)
72+
CheckLoginGET.request.invoke(r)
73+
IsDevEnabledGET.request.invoke(r)
6674

6775
ConsoleWS.ws.invoke(r)
6876
ServerStatsWS.ws.invoke(r)
77+
78+
if (!Config.readConfig().devMode) {
79+
MainGET.request.invoke(r)
80+
} else {
81+
DevRequests.request.invoke(r)
82+
DevWS.ws.invoke(r)
83+
}
6984
}

0 commit comments

Comments
 (0)