From 05b6dd2a43a1672f6eadb601193f4ddfcf3804b8 Mon Sep 17 00:00:00 2001 From: Zed Date: Wed, 11 Feb 2026 23:49:50 +0100 Subject: [PATCH] Add config options to enable subset of RSS feeds Fixes #1363 --- nitter.example.conf | 33 +++++++++++++++++++-------------- src/config.nim | 8 +++++++- src/routes/list.nim | 2 +- src/routes/rss.nim | 20 +++++++++++++++----- src/routes/search.nim | 2 +- src/routes/timeline.nim | 11 ++++++++++- src/types.nim | 6 +++++- src/views/general.nim | 4 ++-- 8 files changed, 60 insertions(+), 26 deletions(-) diff --git a/nitter.example.conf b/nitter.example.conf index 4a6a026..5263ebf 100644 --- a/nitter.example.conf +++ b/nitter.example.conf @@ -1,34 +1,39 @@ [Server] -hostname = "nitter.net" # for generating links, change this to your own domain/ip +hostname = "nitter.net" # for generating links, change this to your own domain/ip title = "nitter" address = "0.0.0.0" port = 8080 -https = false # disable to enable cookies when not using https +https = false # disable to enable cookies when not using https httpMaxConnections = 100 staticDir = "./public" [Cache] -listMinutes = 240 # how long to cache list info (not the tweets, so keep it high) -rssMinutes = 10 # how long to cache rss queries -redisHost = "localhost" # Change to "nitter-redis" if using docker-compose +listMinutes = 240 # how long to cache list info (not the tweets, so keep it high) +rssMinutes = 10 # how long to cache rss queries +redisHost = "localhost" # Change to "nitter-redis" if using docker-compose redisPort = 6379 redisPassword = "" -redisConnections = 20 # minimum open connections in pool +redisConnections = 20 # minimum open connections in pool redisMaxConnections = 30 # new connections are opened when none are available, but if the pool size # goes above this, they're closed when released. don't worry about this unless # you receive tons of requests per second [Config] -hmacKey = "secretkey" # random key for cryptographic signing of video urls -base64Media = false # use base64 encoding for proxied media urls -enableRSS = true # set this to false to disable RSS feeds -enableDebug = false # enable request logs and debug endpoints (/.sessions) -proxy = "" # http/https url, SOCKS proxies are not supported +hmacKey = "secretkey" # random key for cryptographic signing of video urls +base64Media = false # use base64 encoding for proxied media urls +enableRSS = true # master switch, set to false to disable all RSS feeds +enableRSSUserTweets = true # /@user/rss +enableRSSUserReplies = true # /@user/with_replies/rss +enableRSSUserMedia = true # /@user/media/rss +enableRSSSearch = true # /search/rss and /@user/search/rss +enableRSSList = true # list RSS feeds +enableDebug = false # enable request logs and debug endpoints (/.sessions) +proxy = "" # http/https url, SOCKS proxies are not supported proxyAuth = "" -apiProxy = "" # nitter-proxy host, e.g. localhost:7000 -disableTid = false # enable this if cookie-based auth is failing -maxConcurrentReqs = 2 # max requests at a time per session to avoid race conditions +apiProxy = "" # nitter-proxy host, e.g. localhost:7000 +disableTid = false # enable this if cookie-based auth is failing +maxConcurrentReqs = 2 # max requests at a time per session to avoid race conditions # Change default preferences here, see src/prefs_impl.nim for a complete list [Preferences] diff --git a/src/config.nim b/src/config.nim index 8cb334a..343b301 100644 --- a/src/config.nim +++ b/src/config.nim @@ -13,6 +13,8 @@ proc get*[T](config: parseCfg.Config; section, key: string; default: T): T = proc getConfig*(path: string): (Config, parseCfg.Config) = var cfg = loadConfig(path) + let masterRss = cfg.get("Config", "enableRSS", true) + let conf = Config( # Server address: cfg.get("Server", "address", "0.0.0.0"), @@ -37,7 +39,11 @@ proc getConfig*(path: string): (Config, parseCfg.Config) = hmacKey: cfg.get("Config", "hmacKey", "secretkey"), base64Media: cfg.get("Config", "base64Media", false), minTokens: cfg.get("Config", "tokenCount", 10), - enableRss: cfg.get("Config", "enableRSS", true), + enableRSSUserTweets: masterRss and cfg.get("Config", "enableRSSUserTweets", true), + enableRSSUserReplies: masterRss and cfg.get("Config", "enableRSSUserReplies", true), + enableRSSUserMedia: masterRss and cfg.get("Config", "enableRSSUserMedia", true), + enableRSSSearch: masterRss and cfg.get("Config", "enableRSSSearch", true), + enableRSSList: masterRss and cfg.get("Config", "enableRSSList", true), enableDebug: cfg.get("Config", "enableDebug", false), proxy: cfg.get("Config", "proxy", ""), proxyAuth: cfg.get("Config", "proxyAuth", ""), diff --git a/src/routes/list.nim b/src/routes/list.nim index 7dadc22..b4ab091 100644 --- a/src/routes/list.nim +++ b/src/routes/list.nim @@ -13,7 +13,7 @@ template respList*(list, timeline, title, vnode: typed) = let html = renderList(vnode, timeline.query, list) - rss = &"""/i/lists/{@"id"}/rss""" + rss = if cfg.enableRSSList: &"""/i/lists/{@"id"}/rss""" else: "" resp renderMain(html, request, cfg, prefs, titleText=title, rss=rss, banner=list.banner) diff --git a/src/routes/rss.nim b/src/routes/rss.nim index 6902001..444dc70 100644 --- a/src/routes/rss.nim +++ b/src/routes/rss.nim @@ -60,7 +60,8 @@ template respRss*(rss, page) = proc createRssRouter*(cfg: Config) = router rss: get "/search/rss": - cond cfg.enableRss + if not cfg.enableRSSSearch: + resp Http403, showError("RSS feed is disabled", cfg) if @"q".len > 200: resp Http400, showError("Search input too long.", cfg) @@ -86,8 +87,9 @@ proc createRssRouter*(cfg: Config) = respRss(rss, "Search") get "/@name/rss": - cond cfg.enableRss cond '.' notin @"name" + if not cfg.enableRSSUserTweets: + resp Http403, showError("RSS feed is disabled", cfg) let prefs = requestPrefs() name = @"name" @@ -103,9 +105,15 @@ proc createRssRouter*(cfg: Config) = respRss(rss, "User") get "/@name/@tab/rss": - cond cfg.enableRss cond '.' notin @"name" cond @"tab" in ["with_replies", "media", "search"] + let rssEnabled = case @"tab" + of "with_replies": cfg.enableRSSUserReplies + of "media": cfg.enableRSSUserMedia + of "search": cfg.enableRSSSearch + else: false + if not rssEnabled: + resp Http403, showError("RSS feed is disabled", cfg) let prefs = requestPrefs() name = @"name" @@ -132,8 +140,9 @@ proc createRssRouter*(cfg: Config) = respRss(rss, "User") get "/@name/lists/@slug/rss": - cond cfg.enableRss cond @"name" != "i" + if not cfg.enableRSSList: + resp Http403, showError("RSS feed is disabled", cfg) let slug = decodeUrl(@"slug") list = await getCachedList(@"name", slug) @@ -149,7 +158,8 @@ proc createRssRouter*(cfg: Config) = redirect(url) get "/i/lists/@id/rss": - cond cfg.enableRss + if not cfg.enableRSSList: + resp Http403, showError("RSS feed is disabled", cfg) let prefs = requestPrefs() id = @"id" diff --git a/src/routes/search.nim b/src/routes/search.nim index 86677c9..7d72f34 100644 --- a/src/routes/search.nim +++ b/src/routes/search.nim @@ -36,7 +36,7 @@ proc createSearchRouter*(cfg: Config) = of tweets: let tweets = await getGraphTweetSearch(query, getCursor()) - rss = "/search/rss?" & genQueryUrl(query) + rss = if cfg.enableRSSSearch: "/search/rss?" & genQueryUrl(query) else: "" resp renderMain(renderTweetSearch(tweets, prefs, getPath()), request, cfg, prefs, title, rss=rss) else: diff --git a/src/routes/timeline.nim b/src/routes/timeline.nim index d6d8c21..379281a 100644 --- a/src/routes/timeline.nim +++ b/src/routes/timeline.nim @@ -138,8 +138,17 @@ proc createTimelineRouter*(cfg: Config) = profile.tweets.beginning = true resp $renderTimelineTweets(profile.tweets, prefs, getPath()) + let rssEnabled = + if @"tab".len == 0: cfg.enableRSSUserTweets + elif @"tab" == "with_replies": cfg.enableRSSUserReplies + elif @"tab" == "media": cfg.enableRSSUserMedia + elif @"tab" == "search": cfg.enableRSSSearch + else: false + let rss = - if @"tab".len == 0: + if not rssEnabled: + "" + elif @"tab".len == 0: "/$1/rss" % @"name" elif @"tab" == "search": "/$1/search/rss?$2" % [@"name", genQueryUrl(query)] diff --git a/src/types.nim b/src/types.nim index c90e789..33298d2 100644 --- a/src/types.nim +++ b/src/types.nim @@ -275,7 +275,11 @@ type hmacKey*: string base64Media*: bool minTokens*: int - enableRss*: bool + enableRSSUserTweets*: bool + enableRSSUserReplies*: bool + enableRSSUserMedia*: bool + enableRSSSearch*: bool + enableRSSList*: bool enableDebug*: bool proxy*: string proxyAuth*: string diff --git a/src/views/general.nim b/src/views/general.nim index 27fa103..32e53bd 100644 --- a/src/views/general.nim +++ b/src/views/general.nim @@ -29,7 +29,7 @@ proc renderNavbar(cfg: Config; req: Request; rss, canonical: string): VNode = tdiv(class="nav-item right"): icon "search", title="Search", href="/search" - if cfg.enableRss and rss.len > 0: + if rss.len > 0: icon "rss", title="RSS Feed", href=rss icon "bird", title="Open in X", href=canonical a(href="https://liberapay.com/zedeus"): verbatim lp @@ -67,7 +67,7 @@ proc renderHead*(prefs: Prefs; cfg: Config; req: Request; titleText=""; desc=""; if alternate.len > 0: link(rel="alternate", href=alternate, title="View on X") - if cfg.enableRss and rss.len > 0: + if rss.len > 0: link(rel="alternate", type="application/rss+xml", href=rss, title="RSS feed") if prefs.hlsPlayback: