Skip to content
This repository has been archived by the owner on Jun 26, 2024. It is now read-only.

Commit

Permalink
add blog RSS
Browse files Browse the repository at this point in the history
  • Loading branch information
sanity committed Oct 29, 2023
1 parent 5620746 commit 260afc5
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 6 deletions.
11 changes: 10 additions & 1 deletion src/main/kotlin/org/freenet/website/main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import org.freenet.website.pages.developers.PivotalTracker
import org.freenet.website.pages.developers.developersPage
import org.freenet.website.pages.homePage
import org.freenet.website.pages.faq.faqPage
import org.freenet.website.util.BlogRssPlugin
import org.freenet.website.util.HealthCheckPlugin
import org.freenet.website.util.UrlToPathSegmentsRF
import org.freenet.website.util.recordVisit
Expand Down Expand Up @@ -47,6 +48,7 @@ suspend fun main() {
port = 8080,
debug = isLocalTestingMode,
plugins = listOf(
BlogRssPlugin(),
HealthCheckPlugin,
StaticFilesPlugin(ResourceFolder("static"), "/static",)
),
Expand Down Expand Up @@ -136,7 +138,14 @@ private fun HeadComponent.configureHead(title : KVal<String>) {
meta["name"] = "viewport"
}

// element("script")["src"] = "https://js.stripe.com/v3/"
element("link") {
it["rel"] = "alternate"
it["type"] = "application/rss+xml"
it["title"] = "Freenet Blog RSS Feed"
it["href"] = "https://freenet.org/blog.rss"
}

// element("script")["src"] = "https://js.stripe.com/v3/"
// element("script")["src"] = "/static/checkout.js"
}

Expand Down
4 changes: 2 additions & 2 deletions src/main/kotlin/org/freenet/website/pages/blog/blogPage.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ fun Component.blogPage(number: Int? = null) {
h3 { it.text("Loading, please refresh page.") }
} else {
discussions.discussions.forEach { discussion ->
a(href = "/blog/${discussion.number}/${formatForUrl(discussion.title)}.html") {
a(href = discussion.freenetUrlPath) {
it["style"] = "color: #000000;"
div {
it.classes("box")
Expand Down Expand Up @@ -69,7 +69,7 @@ fun Component.blogPage(number: Int? = null) {
}
}

private fun formatForUrl(title: String): String {
fun formatForUrl(title: String): String {
return title.lowercase(Locale.getDefault())
.replace(Regex("\\s"), "-")
.replace(Regex("[^a-z0-9-]"), "")
Expand Down
17 changes: 14 additions & 3 deletions src/main/kotlin/org/freenet/website/pages/blog/github.kt
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,22 @@ object GitHubDiscussions {

@Volatile var discussions: DiscussionStore? = null

data class DiscussionStore(val discussions : List<Discussion>) {
data class DiscussionStore(val discussions : List<Discussion>, val generated : Instant = Instant.now()) {
val discussionsByNumber = discussions.associateBy { it.number }
}

init {
scope.launch(Dispatchers.IO) {
while(true) {
logger.info { "Retrieving discussions from GitHub" }
discussions = DiscussionStore(getDiscussions())
val newDiscussions = getDiscussions()
val curDiscussions = discussions
if (curDiscussions != null && newDiscussions == curDiscussions.discussions) {
logger.debug { "Discussions unchanged" }
} else {
logger.info { "Discussions updated" }
discussions = DiscussionStore(newDiscussions)
}
logger.info { "Github discussions retrieved" }
delay(Duration.ofHours(1))
}
Expand Down Expand Up @@ -130,7 +137,11 @@ object GitHubDiscussions {
val url: String,
val createdAt: Instant,
val bodyHTML: String,
)
) {
val freenetUrlPath : String by lazy {
"/blog/${number}/${formatForUrl(title)}.html"
}
}

@Serializable
data class GraphQLRequest(
Expand Down
77 changes: 77 additions & 0 deletions src/main/kotlin/org/freenet/website/util/BlogRssPlugin.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package org.freenet.website.util

import io.ktor.http.*
import io.ktor.http.HttpStatusCode.Companion.OK
import io.ktor.server.application.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import kweb.plugins.KwebPlugin
import org.freenet.website.pages.blog.GitHubDiscussions
import java.time.Instant

class BlogRssPlugin : KwebPlugin() {
data class CachedRSS(val rss: String, val generated: Instant)

@Volatile var cachedRss: CachedRSS? = null

private fun getRss(): CachedRSS {
val discussionsGenerated = GitHubDiscussions.discussions?.generated ?: Instant.now()
val cached = cachedRss
if (cached == null || cached.generated.isBefore(discussionsGenerated)) {
val generatedRss = generateRss()
cachedRss = CachedRSS(generatedRss, discussionsGenerated)
}
return cachedRss!!
}

private fun generateRss() : String {
val rss = StringBuilder()
rss.append("""
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
<channel>
<title>Freenet Blog</title>
<link>https://freenet.org/blog</link>
<description>News and updates from the Freenet Project</description>
<language>en-us</language>
<ttl>1440</ttl>
""".trimIndent())
val discussions = GitHubDiscussions.discussions
if (discussions != null) {
for (blog in discussions.discussions.take(20)) {
rss.append(
"""
<item>
<title>${blog.title}</title>
<link>https://freenet.org/blog/${blog.number}/${blog.title}</link>
<pubDate>${blog.createdAt}</pubDate>
</item> """.trimIndent()
)
}
}
rss.append("""
</channel>
</rss>
""".trimIndent())

return rss.toString()
}

override fun appServerConfigurator(routeHandler: Routing) {
routeHandler.get("/blog.rss") {
val rssCache = getRss()
val etag = rssCache.generated.hashCode().toString()
val etagHeader = call.request.headers[HttpHeaders.IfNoneMatch]
if (etagHeader == etag) {
call.respond(HttpStatusCode.NotModified)
} else {
call.response.header(HttpHeaders.ETag, etag)
call.respondText(
text = rssCache.rss,
contentType = ContentType.Application.Xml,
status = OK
)
}
}
}
}

0 comments on commit 260afc5

Please sign in to comment.