Skip to content

Commit 0867910

Browse files
committed
feat: Add SERVER_IS_CONFIGURED route and its content to provide information about the server.
1 parent c6b6282 commit 0867910

9 files changed

Lines changed: 117 additions & 10 deletions

File tree

build.gradle.kts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ dependencies {
4949
implementation("com.oracle.database.jdbc:ojdbc8:12.2.0.1")
5050
implementation("org.postgresql:postgresql:42.7.5")
5151
implementation("com.microsoft.sqlserver:mssql-jdbc:9.4.1.jre8")
52+
53+
implementation("org.jetbrains:markdown:0.7.3")
5254
}
5355

5456
tasks.named<JavaExec>("run") {

src/main/kotlin/com/sakethh/linkora/Application.kt

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
package com.sakethh.linkora
22

33
import com.sakethh.linkora.data.configureDatabase
4+
import com.sakethh.linkora.data.repository.MarkdownManagerRepoImpl
45
import com.sakethh.linkora.domain.model.ServerConfig
5-
import com.sakethh.linkora.presentation.routing.websocket.configureEventsWebSocket
6+
import com.sakethh.linkora.domain.repository.MarkdownManagerRepo
67
import com.sakethh.linkora.presentation.routing.configureRouting
8+
import com.sakethh.linkora.presentation.routing.websocket.configureEventsWebSocket
79
import com.sakethh.linkora.utils.SysEnvKey
810
import com.sakethh.linkora.utils.useSysEnvValues
911
import io.ktor.server.application.*
@@ -116,7 +118,8 @@ fun Application.module() {
116118
configureDatabase()
117119
configureSecurity()
118120
configureSerialization()
119-
configureRouting()
121+
val mdManagerRepo: MarkdownManagerRepo = MarkdownManagerRepoImpl()
122+
configureRouting(serverConfig = ServerConfiguration.readConfig(), markdownManagerRepo = mdManagerRepo)
120123
install(WebSockets) {
121124
pingPeriod = 15.seconds
122125
timeout = 15.seconds

src/main/kotlin/com/sakethh/linkora/data/Database.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ fun configureDatabase() {
2727
println("Linkora database is operational and accessible.")
2828
} catch (e: Exception) {
2929

30-
if (e.message != "Unknown database 'linkora'") {
30+
if (e.message.toString().contains("Unknown database").not()) {
3131
database?.connector?.invoke()?.close()
3232
}
3333

@@ -39,7 +39,7 @@ fun configureDatabase() {
3939
}
4040
return
4141
}
42-
if (e.message == "Unknown database 'linkora'") {
42+
if (e.message.toString().contains("Unknown database")) {
4343
println("Linkora database does not exist; proceeding with creation.")
4444
Database.connect(
4545
url = serverConfig.databaseUrl.substringBefore("/linkora"),
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package com.sakethh.linkora.data.repository
2+
3+
import com.sakethh.linkora.domain.PlaceHolder
4+
import com.sakethh.linkora.domain.PlaceHolderValue
5+
import com.sakethh.linkora.domain.repository.MarkdownManagerRepo
6+
import org.intellij.markdown.flavours.commonmark.CommonMarkFlavourDescriptor
7+
import org.intellij.markdown.html.HtmlGenerator
8+
import org.intellij.markdown.parser.MarkdownParser
9+
10+
11+
class MarkdownManagerRepoImpl : MarkdownManagerRepo {
12+
13+
private val markdownFlavour = CommonMarkFlavourDescriptor()
14+
private val mdParser = MarkdownParser(markdownFlavour)
15+
16+
override fun getRawHtmlBasedOnMD(fileLocation: String, placeHolder: Pair<PlaceHolder, PlaceHolderValue>): String {
17+
val file = this::class.java.getResourceAsStream(fileLocation)
18+
val rawMDText = file.use { it?.bufferedReader()?.readText().toString() }
19+
.replace(placeHolder.first, placeHolder.second)
20+
val parseTree = mdParser.buildMarkdownTreeFromString(rawMDText)
21+
return HtmlGenerator(markdownText = rawMDText, parseTree, markdownFlavour).generateHtml()
22+
}
23+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package com.sakethh.linkora.domain
2+
3+
typealias PlaceHolder = String
4+
5+
typealias PlaceHolderValue = String
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.sakethh.linkora.domain.repository
2+
3+
import com.sakethh.linkora.domain.PlaceHolder
4+
import com.sakethh.linkora.domain.PlaceHolderValue
5+
6+
interface MarkdownManagerRepo {
7+
fun getRawHtmlBasedOnMD(fileLocation: String, placeHolder: Pair<PlaceHolder, PlaceHolderValue>): String
8+
}

src/main/kotlin/com/sakethh/linkora/domain/routes/SyncRoute.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@ enum class SyncRoute {
44
TEST_BEARER,
55
GET_UPDATES,
66
GET_TOMBSTONES,
7-
DELETE_EVERYTHING
7+
DELETE_EVERYTHING,
8+
SERVER_IS_CONFIGURED
89
}

src/main/kotlin/com/sakethh/linkora/presentation/routing/Routing.kt

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,26 +5,50 @@ import com.sakethh.linkora.data.repository.FoldersImplementation
55
import com.sakethh.linkora.data.repository.LinksImplementation
66
import com.sakethh.linkora.data.repository.PanelsRepoImpl
77
import com.sakethh.linkora.data.repository.SyncRepoImpl
8-
import com.sakethh.linkora.domain.repository.FoldersRepository
9-
import com.sakethh.linkora.domain.repository.LinksRepository
10-
import com.sakethh.linkora.domain.repository.PanelsRepository
11-
import com.sakethh.linkora.domain.repository.SyncRepo
8+
import com.sakethh.linkora.domain.model.ServerConfig
9+
import com.sakethh.linkora.domain.repository.*
10+
import com.sakethh.linkora.domain.routes.SyncRoute
1211
import com.sakethh.linkora.presentation.routing.http.foldersRouting
1312
import com.sakethh.linkora.presentation.routing.http.linksRouting
1413
import com.sakethh.linkora.presentation.routing.http.panelsRouting
1514
import com.sakethh.linkora.presentation.routing.http.syncRouting
15+
import com.sakethh.linkora.utils.SysEnvKey
16+
import com.sakethh.linkora.utils.useSysEnvValues
1617
import io.ktor.http.*
1718
import io.ktor.server.application.*
1819
import io.ktor.server.auth.*
1920
import io.ktor.server.response.*
2021
import io.ktor.server.routing.*
22+
import java.net.InetAddress
2123

22-
fun Application.configureRouting() {
24+
fun Application.configureRouting(serverConfig: ServerConfig, markdownManagerRepo: MarkdownManagerRepo) {
2325
routing {
2426
authenticate(Security.BEARER.name) {
2527
get("/") {
2628
call.respond(message = HttpStatusCode.OK, status = HttpStatusCode.OK)
2729
}
30+
get(SyncRoute.TEST_BEARER.name) {
31+
call.respond(message = HttpStatusCode.OK, status = HttpStatusCode.OK)
32+
}
33+
}
34+
get(SyncRoute.SERVER_IS_CONFIGURED.name) {
35+
val placeHolderValue =
36+
if ((useSysEnvValues().not() && serverConfig.hostAddress != InetAddress.getLocalHost().hostAddress) || (useSysEnvValues() && System.getenv(
37+
SysEnvKey.LINKORA_HOST_ADDRESS.name
38+
) != InetAddress.getLocalHost().hostAddress)
39+
) {
40+
"""### **Local Hosting & IPv4 Address**
41+
- If you're **hosting locally**, ensure you're using an **IPv4 address** (${InetAddress.getLocalHost().hostAddress}) as `serverHost`.
42+
- If using environment variables, set `${SysEnvKey.LINKORA_HOST_ADDRESS.name}` to `${InetAddress.getLocalHost().hostAddress}`.
43+
- Otherwise, update `serverHost` in `linkoraConfig.json` so the Android app can connect.
44+
- If you're **only using Linkora on Desktop**, no changes are needed."""
45+
} else {
46+
""
47+
}
48+
val requiredHtml = markdownManagerRepo.getRawHtmlBasedOnMD(
49+
fileLocation = "/raw/SERVER_IS_CONFIGURED.md", placeHolder = "#{PLACEHOLDER_1}" to placeHolderValue
50+
)
51+
call.respondText(contentType = ContentType.Text.Html, text = requiredHtml)
2852
}
2953
}
3054
val linksRepository: LinksRepository = LinksImplementation()
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
It seems you've correctly configured the sync server.
2+
3+
Before anything, check out this video where I explained how to connect the app and the server. That should clear up most things.
4+
5+
This server is only meant to communicate with the **Linkora** apps that are available on both **Android** and **Desktop**. You can't use this server with them as there's no frontend client on web yet. Well, you can if you set up your own thing from scratch that connects to this server and does what it's supposed to do.
6+
7+
## Important Notes
8+
9+
#{PLACEHOLDER_1}
10+
11+
### **HTTPS Support**
12+
- If you're **hosting locally**, you **can't connect** via **HTTPS** right now.
13+
- HTTPS requires a **trusted certificate** from a recognized Certificate Authority (CA), which isn’t typically applicable for local setups.
14+
- The other way is to manually generate a self-signed certificate and configure it, which may be added in future versions.
15+
- If you're **hosting on a cloud service**, you can use HTTPS by setting up an SSL certificate through your provider.
16+
17+
### **Database & Data Syncing**
18+
- Apps have their **own databases**.
19+
- This server is **only meant to store your data** in the local system that you're currently running on.
20+
- If, for some reason, data gets deleted (maybe uninstalled or whatever), you can **connect to this server**, and the app will **pull everything** from the database that’s currently on your machine to its local database.
21+
- **i.e.,**
22+
- The app has its **own database**.
23+
- This server is now **connected to a database**.
24+
- Linkora **app and server sync data** between the app and the database.
25+
26+
### **Deletion Behavior**
27+
- If something gets **deleted from the Linkora app(s)** while you have a server saved on clients, even if the server is **not up**, it will be **deleted as soon as the server is up** and you open the Linkora app.
28+
- **It's gone, that’s it. You can't undo this.**
29+
- (*I should probably add a trash mechanism instead of deleting permanently, but for now, this can't be undone.*)
30+
31+
### **LWW (Last-Write-Wins) Implementation**
32+
- This server follows **LWW (Last-Write-Wins)**.
33+
- Even if your clients have a saved connection, if the **server is offline**, the **most recently edited data** (from any client) will be updated in the database on your machine once the server is back online and the client apps reconnect.
34+
35+
### **Blank Pages on Other Routes**
36+
- If you're seeing blank pages for other routes, that's completely normal since there's no UI with this server except for this page.
37+
38+
## Troubleshooting & Additional Help
39+
- If something isn’t covered here, check out the **YouTube video** where I explained how to connect the app and the server.
40+
- Also, go through **GitHub issues**; you might find the solution there.
41+
- If not, **create an issue on GitHub**, and I'll fix it when I get some time.

0 commit comments

Comments
 (0)