+1
darkfeed/build.gradle.kts
+1
darkfeed/build.gradle.kts
+3
-1
darkfeed/src/main/kotlin/Main.kt
+3
-1
darkfeed/src/main/kotlin/Main.kt
+46
-2
darkfeed/src/main/kotlin/api/AtpIdentityClient.kt
+46
-2
darkfeed/src/main/kotlin/api/AtpIdentityClient.kt
···
1
package rs.averyrive.darkfeed.api
2
3
import io.ktor.client.*
4
import io.ktor.client.call.*
5
import io.ktor.client.engine.cio.*
···
14
import kotlinx.serialization.json.Json
15
import org.slf4j.LoggerFactory
16
import rs.averyrive.darkfeed.api.model.*
17
18
/** Client that handles identity related requests for ATProto accounts. */
19
class AtpIdentityClient {
···
32
install(Logging)
33
}
34
35
/**
36
* Resolve a handle's DID.
37
*
···
46
*/
47
suspend fun resolveDidFromHandle(handle: String): Did {
48
log.debug("Resolving DID for '{}'...", handle)
49
50
@Serializable
51
data class ResolveHandleResponse(val did: String)
···
101
else -> throw RuntimeException("Received unexpected response: ${resolveHandleResponse.bodyAsText()}")
102
}
103
104
-
log.debug("Resolved DID for '{}': {}", handle, did.toString())
105
106
return did
107
}
···
120
*/
121
suspend fun resolvePdsFromDid(did: Did): String {
122
log.debug("Resolving PDS for '{}'...", did.toString())
123
124
// Get the DID document, using either `plc.directory` or
125
// `/.well-known/did.json` depending on the type of DID.
···
166
?.serviceEndpoint
167
?: throw PdsNotFoundException(did)
168
169
-
log.debug("Resolved PDS for '{}': {}", did.toString(), pdsHost)
170
171
return pdsHost
172
}
173
}
174
···
1
package rs.averyrive.darkfeed.api
2
3
+
import com.mayakapps.kache.InMemoryKache
4
import io.ktor.client.*
5
import io.ktor.client.call.*
6
import io.ktor.client.engine.cio.*
···
15
import kotlinx.serialization.json.Json
16
import org.slf4j.LoggerFactory
17
import rs.averyrive.darkfeed.api.model.*
18
+
import kotlin.time.Duration.Companion.hours
19
+
20
+
// Specify size of each cache
21
+
const val CACHE_SIZE: Long = 1 * 1024 * 1024 // 1 MB
22
23
/** Client that handles identity related requests for ATProto accounts. */
24
class AtpIdentityClient {
···
37
install(Logging)
38
}
39
40
+
/** Cache of handle to DID relationships. */
41
+
private val handleDidCache = InMemoryKache<String, Did>(CACHE_SIZE) {
42
+
expireAfterWriteDuration = 1.hours
43
+
}
44
+
45
+
/** Cache of DID to PDS host relationships. */
46
+
private val didPdsCache = InMemoryKache<Did, String>(CACHE_SIZE) {
47
+
expireAfterWriteDuration = 1.hours
48
+
}
49
+
50
/**
51
* Resolve a handle's DID.
52
*
···
61
*/
62
suspend fun resolveDidFromHandle(handle: String): Did {
63
log.debug("Resolving DID for '{}'...", handle)
64
+
65
+
// Return cached DID if it exists.
66
+
handleDidCache.get(handle)?.let { did ->
67
+
log.debug("Returning cached DID for '{}': {}", handle, did.toString())
68
+
return did
69
+
}
70
71
@Serializable
72
data class ResolveHandleResponse(val did: String)
···
122
else -> throw RuntimeException("Received unexpected response: ${resolveHandleResponse.bodyAsText()}")
123
}
124
125
+
// Cache the DID for this handle.
126
+
handleDidCache.put(handle, did)
127
+
log.debug("Resolved and cached DID for '{}': {}", handle, did.toString())
128
129
return did
130
}
···
143
*/
144
suspend fun resolvePdsFromDid(did: Did): String {
145
log.debug("Resolving PDS for '{}'...", did.toString())
146
+
147
+
// Return cached PDS host if it exists.
148
+
didPdsCache.get(did)?.let { pdsHost ->
149
+
log.debug("Returning cached PDS host for '{}': {}", did.toString(), pdsHost)
150
+
return pdsHost
151
+
}
152
153
// Get the DID document, using either `plc.directory` or
154
// `/.well-known/did.json` depending on the type of DID.
···
195
?.serviceEndpoint
196
?: throw PdsNotFoundException(did)
197
198
+
// Cache the PDS for this DID.
199
+
didPdsCache.put(did, pdsHost)
200
+
log.debug("Resolved and cached PDS for '{}': {}", did.toString(), pdsHost)
201
202
return pdsHost
203
+
}
204
+
205
+
/**
206
+
* Invalidate the PDS associated with the given DID.
207
+
*
208
+
* This should be used when a call to the PDS fails due to the PDS no longer
209
+
* being available. This may indicate that the PDS has moved and should be
210
+
* resolved again.
211
+
*
212
+
* @param did DID to invalidate.
213
+
*/
214
+
suspend fun invalidateCachedPds(did: Did) {
215
+
didPdsCache.remove(did)
216
}
217
}
218
+2
darkfeed/src/main/kotlin/api/BskyApi.kt
+2
darkfeed/src/main/kotlin/api/BskyApi.kt
···
92
val record: Generator,
93
)
94
95
val ownerPdsHost = atpIdentityClient.resolvePdsFromDid(authManager.authAccountDid.toDid())
96
97
val response = httpClient.post {
···
116
@Serializable
117
data class Response(val cursor: String?, val records: List<LikeRef>)
118
119
val requestorPdsHost = atpIdentityClient.resolvePdsFromDid(actor.toDid())
120
121
val response = httpClient.get {
···
92
val record: Generator,
93
)
94
95
+
// TODO: If this PDS is not accessible, invalidate cache and retry.
96
val ownerPdsHost = atpIdentityClient.resolvePdsFromDid(authManager.authAccountDid.toDid())
97
98
val response = httpClient.post {
···
117
@Serializable
118
data class Response(val cursor: String?, val records: List<LikeRef>)
119
120
+
// TODO: If this PDS is not accessible, invalidate cache and retry.
121
val requestorPdsHost = atpIdentityClient.resolvePdsFromDid(actor.toDid())
122
123
val response = httpClient.get {
+2
gradle/libs.versions.toml
+2
gradle/libs.versions.toml
···
4
ktor = "3.0.1"
5
auth0 = "4.4.0"
6
clikt = "5.0.1"
7
8
[libraries]
9
logback-classic = { group = "ch.qos.logback", name = "logback-classic", version.ref = "logback" }
···
18
ktor-serialization-kotlinx-json = { group = "io.ktor", name = "ktor-serialization-kotlinx-json", version.ref = "ktor" }
19
java-jwt = { group = "com.auth0", name = "java-jwt", version.ref = "auth0" }
20
clikt = { group = "com.github.ajalt.clikt", name = "clikt", version.ref = "clikt" }
21
22
[plugins]
23
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
···
4
ktor = "3.0.1"
5
auth0 = "4.4.0"
6
clikt = "5.0.1"
7
+
kache = "2.1.1"
8
9
[libraries]
10
logback-classic = { group = "ch.qos.logback", name = "logback-classic", version.ref = "logback" }
···
19
ktor-serialization-kotlinx-json = { group = "io.ktor", name = "ktor-serialization-kotlinx-json", version.ref = "ktor" }
20
java-jwt = { group = "com.auth0", name = "java-jwt", version.ref = "auth0" }
21
clikt = { group = "com.github.ajalt.clikt", name = "clikt", version.ref = "clikt" }
22
+
kache = { group = "com.mayakapps.kache", name = "kache", version.ref = "kache" }
23
24
[plugins]
25
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }