Skip to content

Commit cd5ee7f

Browse files
committed
feat: add option to enforce strict folder IDs for default folders
1 parent ef6893a commit cd5ee7f

9 files changed

Lines changed: 92 additions & 280 deletions

File tree

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,11 @@ object Constants {
44
const val SERVER_VERSION = "0.2.1"
55
const val KEY_STORE_ALIAS = "linkora-sync-server-cert"
66
const val DEFAULT_USER_AGENT = "Twitterbot/1.0"
7+
8+
const val ALL_LINKS_ID: Long = -1
9+
const val SAVED_LINKS_ID: Long = -2
10+
const val IMPORTANT_LINKS_ID: Long = -3
11+
const val ARCHIVE_ID: Long = -4
12+
const val HISTORY_ID: Long = -5
13+
const val DEFAULT_PANELS_ID: Long = -6
714
}

src/main/kotlin/com/sakethh/linkora/data/repository/FoldersRepoImpl.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import org.jetbrains.exposed.v1.jdbc.selectAll
2828
import org.jetbrains.exposed.v1.jdbc.transactions.transaction
2929
import org.jetbrains.exposed.v1.jdbc.update
3030

31-
class FoldersRepoImpl(private val panelsRepo: PanelsRepo) : FoldersRepo {
31+
class FoldersRepoImpl : FoldersRepo {
3232

3333
override suspend fun createFolder(addFolderDTO: AddFolderDTO): Result<NewItemResponseDTO> {
3434
return try {

src/main/kotlin/com/sakethh/linkora/data/repository/LinksRepoImpl.kt

Lines changed: 60 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,13 @@ import kotlinx.coroutines.withContext
2525
import kotlinx.serialization.encodeToString
2626
import kotlinx.serialization.json.Json
2727
import kotlinx.serialization.json.encodeToJsonElement
28+
import org.jetbrains.exposed.v1.core.LongColumnType
29+
import org.jetbrains.exposed.v1.core.SqlExpressionBuilder.case
2830
import org.jetbrains.exposed.v1.core.SqlExpressionBuilder.eq
2931
import org.jetbrains.exposed.v1.core.SqlExpressionBuilder.inList
3032
import org.jetbrains.exposed.v1.core.and
33+
import org.jetbrains.exposed.v1.core.castTo
34+
import org.jetbrains.exposed.v1.core.longLiteral
3135
import org.jetbrains.exposed.v1.jdbc.*
3236
import org.jetbrains.exposed.v1.jdbc.transactions.transaction
3337
import org.jsoup.Jsoup
@@ -47,9 +51,8 @@ class LinksRepoImpl : LinksRepo {
4751
Jsoup.connect(
4852
"http" + linkUrl.substringAfter("http").substringBefore(" ").trim()
4953
).userAgent(userAgent).followRedirects(true).header("Accept", "text/html")
50-
.header("Accept-Encoding", "gzip,deflate").header("Accept-Language", "en;q=1.0")
51-
.ignoreContentType(true).maxBodySize(0)
52-
.ignoreHttpErrors(true).get()
54+
.header("Accept-Encoding", "gzip,deflate").header("Accept-Language", "en;q=1.0").ignoreContentType(true)
55+
.maxBodySize(0).ignoreHttpErrors(true).get()
5356
}.toString()
5457

5558
val document = Jsoup.parse(rawHTML)
@@ -100,26 +103,24 @@ class LinksRepoImpl : LinksRepo {
100103
HttpRequest.newBuilder(URI("https://api.vxtwitter.com/${tweetURL.substringAfter(".com/")}")).build()
101104

102105
val vxTwitterResponseBody = withContext(Dispatchers.IO) {
103-
HttpClient.newHttpClient().send(httpRequest, HttpResponse.BodyHandlers.ofString())
104-
.body().run {
105-
Json.decodeFromString<TwitterMetaDataDTO>(this)
106-
}
106+
HttpClient.newHttpClient().send(httpRequest, HttpResponse.BodyHandlers.ofString()).body().run {
107+
Json.decodeFromString<TwitterMetaDataDTO>(this)
108+
}
107109
}
108110

109111
return ScrapedLinkInfo(
110112
title = vxTwitterResponseBody.text,
111113
imgUrl = vxTwitterResponseBody.media.takeIf { vxTwitterResponseBody.hasMedia && it.isNotEmpty() }
112114
?.find { it.type in listOf("image", "video", "gif") }
113-
?.let { if (it.type == "image") it.url else it.thumbnailUrl }
114-
?: vxTwitterResponseBody.userPfp,
115+
?.let { if (it.type == "image") it.url else it.thumbnailUrl } ?: vxTwitterResponseBody.userPfp,
115116
mediaType = if (vxTwitterResponseBody.media.isNotEmpty() && vxTwitterResponseBody.media.first().type == "video") MediaType.VIDEO else MediaType.IMAGE)
116117
}
117118

118119
override suspend fun createANewLink(addLinkDTO: AddLinkDTO): Result<NewItemResponseDTO> {
119120
return try {
120121
val eventTimestamp = getSystemEpochSeconds()
121122

122-
val addLinkDTO = if (addLinkDTO.forceRetrieveOGMetaInfo) {
123+
val addLinkDTO = (if (addLinkDTO.forceRetrieveOGMetaInfo) {
123124
try {
124125
val ogMetaInfo = if (addLinkDTO.url.isATwitterUrl()) {
125126
retrieveFromVxTwitterApi(addLinkDTO.url)
@@ -140,6 +141,8 @@ class LinksRepoImpl : LinksRepo {
140141
}
141142
} else {
142143
addLinkDTO
144+
}).run {
145+
copy(idOfLinkedFolder = linkType.defaultFolderId() ?: idOfLinkedFolder)
143146
}
144147

145148
val idOfNewlyAddedLink = transaction {
@@ -150,9 +153,9 @@ class LinksRepoImpl : LinksRepo {
150153
historyLinkMatch
151154
}.toList()
152155
TombstoneTable.batchInsert(historyLinkRecords) {
153-
it[TombstoneTable.deletedAt] = eventTimestamp
154-
it[TombstoneTable.operation] = Route.DELETE_A_LINK.name
155-
it[TombstoneTable.payload] = Json.encodeToString(
156+
this[TombstoneTable.deletedAt] = eventTimestamp
157+
this[TombstoneTable.operation] = Route.DELETE_A_LINK.name
158+
this[TombstoneTable.payload] = Json.encodeToString(
156159
IDBasedDTO(
157160
id = it[LinksTable.id].value,
158161
correlation = addLinkDTO.correlation,
@@ -374,6 +377,7 @@ class LinksRepoImpl : LinksRepo {
374377
}) {
375378
it[lastModified] = eventTimestamp
376379
it[linkType] = LinkType.ARCHIVE_LINK.name
380+
it[idOfLinkedFolder] = Constants.ARCHIVE_ID
377381
}
378382
}
379383
Result.Success(
@@ -401,6 +405,7 @@ class LinksRepoImpl : LinksRepo {
401405
}) {
402406
it[lastModified] = eventTimestamp
403407
it[linkType] = LinkType.SAVED_LINK.name
408+
it[idOfLinkedFolder] = Constants.SAVED_LINKS_ID
404409
}
405410
}
406411
Result.Success(
@@ -489,7 +494,7 @@ class LinksRepoImpl : LinksRepo {
489494
it[baseURL] = linkDTO.baseURL
490495
it[imgURL] = linkDTO.imgURL
491496
it[note] = linkDTO.note
492-
it[idOfLinkedFolder] = linkDTO.idOfLinkedFolder
497+
it[idOfLinkedFolder] = linkDTO.linkType.defaultFolderId() ?: linkDTO.idOfLinkedFolder
493498
it[userAgent] = linkDTO.userAgent
494499
it[mediaType] = linkDTO.mediaType.name
495500
it[markedAsImportant] = linkDTO.markedAsImportant
@@ -556,4 +561,45 @@ class LinksRepoImpl : LinksRepo {
556561
Result.Failure(e)
557562
}
558563
}
564+
565+
override suspend fun forceSetDefaultFolderToInternalIds(): Result<TimeStampBasedResponse> {
566+
val eventTimestamp = getSystemEpochSeconds()
567+
return try {
568+
val timeStampResponse = TimeStampBasedResponse(
569+
eventTimestamp = eventTimestamp, message = "Set Default folders to internal IDs."
570+
)
571+
572+
transaction {
573+
LinksTable.update(where = {
574+
LinksTable.linkType.inList(
575+
listOf(
576+
LinkType.SAVED_LINK.name,
577+
LinkType.IMPORTANT_LINK.name,
578+
LinkType.HISTORY_LINK.name,
579+
LinkType.ARCHIVE_LINK.name
580+
)
581+
)
582+
}) {
583+
it[idOfLinkedFolder] =
584+
case().When(linkType.eq(LinkType.SAVED_LINK.name), longLiteral(Constants.SAVED_LINKS_ID))
585+
.When(linkType.eq(LinkType.IMPORTANT_LINK.name), longLiteral(Constants.IMPORTANT_LINKS_ID))
586+
.When(linkType.eq(LinkType.HISTORY_LINK.name), longLiteral(Constants.HISTORY_ID))
587+
.When(linkType.eq(LinkType.ARCHIVE_LINK.name), longLiteral(Constants.ARCHIVE_ID))
588+
.Else(idOfLinkedFolder.castTo(LongColumnType()))
589+
590+
it[lastModified] = eventTimestamp
591+
}
592+
}
593+
594+
Result.Success(
595+
response = timeStampResponse, webSocketEvent = WebSocketEvent(
596+
operation = Route.FORCE_SET_DEFAULT_FOLDER_TO_INTERNAL_IDS.name,
597+
payload = Json.encodeToJsonElement(timeStampResponse)
598+
)
599+
)
600+
} catch (e: Exception) {
601+
Result.Failure(e)
602+
}
603+
}
604+
559605
}

src/main/kotlin/com/sakethh/linkora/domain/Route.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,5 +46,6 @@ enum class Route {
4646
CREATE_TAG,
4747
DELETE_TAG,
4848
RENAME_TAG,
49-
GET_TAGS
49+
GET_TAGS,
50+
FORCE_SET_DEFAULT_FOLDER_TO_INTERNAL_IDS
5051
}

src/main/kotlin/com/sakethh/linkora/domain/repository/LinksRepo.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,5 @@ interface LinksRepo {
1919
suspend fun markALinkAsNonImp(idBasedDTO: IDBasedDTO): Result<TimeStampBasedResponse>
2020
suspend fun updateLink(linkDTO: LinkDTO): Result<TimeStampBasedResponse>
2121
suspend fun deleteDuplicateLinks(deleteDuplicateLinksDTO: DeleteDuplicateLinksDTO): Result<TimeStampBasedResponse>
22+
suspend fun forceSetDefaultFolderToInternalIds(): Result<TimeStampBasedResponse>
2223
}

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ fun Application.configureRouting(serverConfig: ServerConfig) {
246246
}
247247
val linksRepo: LinksRepo = LinksRepoImpl()
248248
val panelsRepo: PanelsRepo = PanelsRepoImpl()
249-
val foldersRepo: FoldersRepo = FoldersRepoImpl(panelsRepo)
249+
val foldersRepo: FoldersRepo = FoldersRepoImpl()
250250
val syncRepo: SyncRepo = SyncRepoImpl()
251251
val multiActionRepo: MultiActionRepo = MultiActionRepoImpl(foldersRepo)
252252
val tagsRepo: TagsRepo = TagsRepoImpl()
@@ -256,6 +256,5 @@ fun Application.configureRouting(serverConfig: ServerConfig) {
256256
syncRouting(syncRepo)
257257
multiActionRouting(multiActionRepo)
258258
tagsRouting(tagsRepo)
259-
browserExtensionRouting(foldersRepo)
260259
}
261260
}

0 commit comments

Comments
 (0)