nitter.nim 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. # SPDX-License-Identifier: AGPL-3.0-only
  2. import asyncdispatch, strformat, logging
  3. from net import Port
  4. from htmlgen import a
  5. from os import getEnv, normalizedPath
  6. import jester
  7. import types, config, prefs, formatters, redis_cache, http_pool, auth, apiutils
  8. import views/[general, about]
  9. import routes/[
  10. preferences, timeline, status, media, search, rss, list, community, debug,
  11. unsupported, embed, resolver, broadcast, space, article, router_utils]
  12. const instancesUrl = "https://github.com/zedeus/nitter/wiki/Instances"
  13. const issuesUrl = "https://github.com/zedeus/nitter/issues"
  14. let
  15. configPath = getEnv("NITTER_CONF_FILE", "./nitter.conf")
  16. (cfg, fullCfg) = getConfig(configPath)
  17. sessionsPath = getEnv("NITTER_SESSIONS_FILE", "./sessions.jsonl")
  18. initSessionPool(cfg, sessionsPath)
  19. if not cfg.enableDebug:
  20. # Silence Jester's query warning
  21. addHandler(newConsoleLogger())
  22. setLogFilter(lvlError)
  23. stdout.write &"Starting Nitter at {getUrlPrefix(cfg)}\n"
  24. stdout.flushFile
  25. updateDefaultPrefs(fullCfg)
  26. setCacheTimes(cfg)
  27. setHmacKey(cfg.hmacKey)
  28. if cfg.hmacKey.len == 0 or cfg.hmacKey == "secretkey":
  29. stderr.write "WARNING: insecure default 'hmacKey' in nitter.conf; " &
  30. "set a unique random value to stop media URL signatures being forgeable.\n"
  31. stderr.flushFile
  32. setProxyEncoding(cfg.base64Media)
  33. setMaxHttpConns(cfg.httpMaxConns)
  34. setHttpProxy(cfg.proxy, cfg.proxyAuth)
  35. setApiProxy(cfg.apiProxy)
  36. setDisableTid(cfg.disableTid)
  37. setMaxConcurrentReqs(cfg.maxConcurrentReqs)
  38. setMaxRetries(cfg.maxRetries)
  39. setRetryDelayMs(cfg.retryDelayMs)
  40. initAboutPage(cfg.staticDir)
  41. waitFor initRedisPool(cfg)
  42. stdout.write &"Connected to Redis at {cfg.redisHost}:{cfg.redisPort}\n"
  43. stdout.flushFile
  44. createArticleRouter(cfg)
  45. createUnsupportedRouter(cfg)
  46. createResolverRouter(cfg)
  47. createPrefRouter(cfg)
  48. createTimelineRouter(cfg)
  49. createListRouter(cfg)
  50. createCommunityRouter(cfg)
  51. createStatusRouter(cfg)
  52. createSearchRouter(cfg)
  53. createMediaRouter(cfg)
  54. createEmbedRouter(cfg)
  55. createRssRouter(cfg)
  56. createBroadcastRouter(cfg)
  57. createSpaceRouter(cfg)
  58. createDebugRouter(cfg)
  59. settings:
  60. port = Port(cfg.port)
  61. staticDir = normalizedPath(cfg.staticDir)
  62. bindAddr = cfg.address
  63. reusePort = true
  64. maxBody = 64 * 1024
  65. routes:
  66. before:
  67. # Reject malformed paths
  68. if request.path.len == 0 or request.path[0] != '/':
  69. halt Http400
  70. # skip all file URLs
  71. cond "." notin request.path
  72. applyUrlPrefs()
  73. get "/":
  74. resp renderMain(renderSearch(), request, cfg, requestPrefs())
  75. get "/about":
  76. resp renderMain(renderAbout(), request, cfg, requestPrefs())
  77. get "/explore":
  78. redirect("/about")
  79. get "/help":
  80. redirect("/about")
  81. get "/i/redirect":
  82. let url = decodeUrl(@"url")
  83. if url.len == 0: resp Http404
  84. redirect(replaceUrls(url, requestPrefs()))
  85. error Http404:
  86. resp Http404, showError("Page not found", cfg)
  87. error InternalError:
  88. echo error.exc.name, ": ", error.exc.msg
  89. const link = a("open a GitHub issue", href = issuesUrl)
  90. resp Http500, showError(
  91. &"An error occurred, please {link} with the URL you tried to visit.", cfg)
  92. error BadClientError:
  93. echo error.exc.name, ": ", error.exc.msg
  94. resp Http500, showError("Network error occurred, please try again.", cfg)
  95. error RateLimitError:
  96. const link = a("another instance", href = instancesUrl)
  97. resp Http429, showError(
  98. &"Instance has been rate limited.<br>Use {link} or try again later.", cfg)
  99. error NoSessionsError:
  100. const link = a("another instance", href = instancesUrl)
  101. resp Http429, showError(
  102. &"Instance has no auth tokens, or is fully rate limited.<br>Use {link} or try again later.", cfg)
  103. extend articleRoute, ""
  104. extend rss, ""
  105. extend status, ""
  106. extend search, ""
  107. extend timeline, ""
  108. extend media, ""
  109. extend list, ""
  110. extend community, ""
  111. extend preferences, ""
  112. extend resolver, ""
  113. extend embed, ""
  114. extend broadcastRoute, ""
  115. extend spaceRoute, ""
  116. extend debug, ""
  117. extend unsupported, ""