Ver código fonte

Refactor routing code

Zed 7 anos atrás
pai
commit
014f01bf88

+ 11 - 201
src/nitter.nim

@@ -1,83 +1,18 @@
-import asyncdispatch, asyncfile, httpclient, uri, os
-import sequtils, strformat, strutils
+import asyncdispatch
 from net import Port
 
-import jester, regex
+import jester
 
-import api, utils, types, cache, formatters, search, config, prefs, agents
-import views/[general, profile, status, preferences]
+import types, config, prefs
+import views/general
+import routes/[preferences, timeline, media]
 
 const configPath {.strdefine.} = "./nitter.conf"
 let cfg = getConfig(configPath)
 
-proc showSingleTimeline(name, after, agent: string; query: Option[Query];
-                        prefs: Prefs; path: string): Future[string] {.async.} =
-  let railFut = getPhotoRail(name, agent)
-
-  var timeline: Timeline
-  var profile: Profile
-  var cachedProfile = hasCachedProfile(name)
-
-  if cachedProfile.isSome:
-    profile = get(cachedProfile)
-
-  if query.isNone:
-    if cachedProfile.isSome:
-      timeline = await getTimeline(name, after, agent)
-    else:
-      (profile, timeline) = await getProfileAndTimeline(name, agent, after)
-      cache(profile)
-  else:
-    var timelineFut = getTimelineSearch(get(query), after, agent)
-    if cachedProfile.isNone:
-      profile = await getCachedProfile(name, agent)
-    timeline = await timelineFut
-
-  if profile.username.len == 0:
-    return ""
-
-  let profileHtml = renderProfile(profile, timeline, await railFut, prefs, path)
-  return renderMain(profileHtml, prefs, cfg.title, pageTitle(profile),
-                    pageDesc(profile), path)
-
-proc showMultiTimeline(names: seq[string]; after, agent: string; query: Option[Query];
-                       prefs: Prefs; path: string): Future[string] {.async.} =
-  var q = query
-  if q.isSome:
-    get(q).fromUser = names
-  else:
-    q = some(Query(kind: multi, fromUser: names, excludes: @["replies"]))
-
-  var timeline = renderMulti(await getTimelineSearch(get(q), after, agent),
-                             names.join(","), prefs, path)
-
-  return renderMain(timeline, prefs, cfg.title, "Multi")
-
-proc showTimeline(name, after: string; query: Option[Query];
-                  prefs: Prefs; path: string): Future[string] {.async.} =
-  let agent = getAgent()
-  let names = name.strip(chars={'/'}).split(",").filterIt(it.len > 0)
-
-  if names.len == 1:
-    return await showSingleTimeline(names[0], after, agent, query, prefs, path)
-  else:
-    return await showMultiTimeline(names, after, agent, query, prefs, path)
-
-template respTimeline(timeline: typed) =
-  if timeline.len == 0:
-    resp Http404, showError("User \"" & @"name" & "\" not found", cfg.title)
-  resp timeline
-
-template cookiePrefs(): untyped {.dirty.} =
-  getPrefs(request.cookies.getOrDefault("preferences"))
-
-template getPath(): untyped {.dirty.} =
-  $(parseUri(request.path) ? filterParams(request.params))
-
-template refPath(): untyped {.dirty.} =
-  if @"referer".len > 0: @"referer" else: "/"
-
-setProfileCacheTime(cfg.profileCacheTime)
+createPrefRouter(cfg)
+createTimelineRouter(cfg)
+createMediaRouter(cfg)
 
 settings:
   port = Port(cfg.port)
@@ -93,133 +28,8 @@ routes:
       resp Http404, showError("Please enter a username.", cfg.title)
     redirect("/" & @"query")
 
-  get "/settings":
-    let prefs = cookiePrefs()
-    let path = refPath()
-    resp renderMain(renderPreferences(prefs, path), prefs, cfg.title,
-                    "Preferences", path)
-
-  post "/saveprefs":
-    var prefs = cookiePrefs()
-    genUpdatePrefs()
-    setCookie("preferences", $prefs.id, daysForward(360), httpOnly=true, secure=cfg.useHttps)
-    redirect(refPath())
-
-  post "/resetprefs":
-    var prefs = cookiePrefs()
-    resetPrefs(prefs)
-    setCookie("preferences", $prefs.id, daysForward(360), httpOnly=true, secure=cfg.useHttps)
-    redirect($(parseUri("/settings") ? filterParams(request.params)))
-
-  post "/enablehls":
-    var prefs = cookiePrefs()
-    prefs.hlsPlayback = true
-    cache(prefs)
-    setCookie("preferences", $prefs.id, daysForward(360), httpOnly=true, secure=cfg.useHttps)
-    redirect(refPath())
-
-  get "/@name/?":
-    cond '.' notin @"name"
-    respTimeline(await showTimeline(@"name", @"after", none(Query),
-                                    cookiePrefs(), getPath()))
-
-  get "/@name/search":
-    cond '.' notin @"name"
-    let prefs = cookiePrefs()
-    let query = initQuery(@"filter", @"include", @"not", @"sep", @"name")
-    respTimeline(await showTimeline(@"name", @"after", some(query),
-                                    cookiePrefs(), getPath()))
-
-  get "/@name/replies":
-    cond '.' notin @"name"
-    let prefs = cookiePrefs()
-    respTimeline(await showTimeline(@"name", @"after", some(getReplyQuery(@"name")),
-                                    cookiePrefs(), getPath()))
-
-  get "/@name/media":
-    cond '.' notin @"name"
-    let prefs = cookiePrefs()
-    respTimeline(await showTimeline(@"name", @"after", some(getMediaQuery(@"name")),
-                                    cookiePrefs(), getPath()))
-
-  get "/@name/status/@id":
-    cond '.' notin @"name"
-    let prefs = cookiePrefs()
-
-    let conversation = await getTweet(@"name", @"id", getAgent())
-    if conversation == nil or conversation.tweet.id.len == 0:
-      resp Http404, showError("Tweet not found", cfg.title)
-
-    let path = getPath()
-    let title = pageTitle(conversation.tweet.profile)
-    let desc = conversation.tweet.text
-    let html = renderConversation(conversation, prefs, path)
-
-    if conversation.tweet.video.isSome():
-      let thumb = get(conversation.tweet.video).thumb
-      let vidUrl = getVideoEmbed(conversation.tweet.id)
-      resp renderMain(html, prefs, cfg.title, title, desc, path, images = @[thumb],
-                      `type`="video", video=vidUrl)
-    elif conversation.tweet.gif.isSome():
-      let thumb = get(conversation.tweet.gif).thumb
-      let vidUrl = getVideoEmbed(conversation.tweet.id)
-      resp renderMain(html, prefs, cfg.title, title, desc, path, images = @[thumb],
-                      `type`="video", video=vidUrl)
-    else:
-      resp renderMain(html, prefs, cfg.title, title, desc, path, images=conversation.tweet.photos)
-
-  get "/i/web/status/@id":
-    redirect("/i/status/" & @"id")
-
-  get "/pic/@sig/@url":
-    cond "http" in @"url"
-    cond "twimg" in @"url"
-    let
-      uri = parseUri(decodeUrl(@"url"))
-      path = uri.path.split("/")[2 .. ^1].join("/")
-      filename = cfg.cacheDir / cleanFilename(path & uri.query)
-
-    if getHmac($uri) != @"sig":
-      resp showError("Failed to verify signature", cfg.title)
-
-    if not existsDir(cfg.cacheDir):
-      createDir(cfg.cacheDir)
-
-    if not existsFile(filename):
-      let client = newAsyncHttpClient()
-      await client.downloadFile($uri, filename)
-      client.close()
-
-    if not existsFile(filename):
-      resp Http404
-
-    let file = openAsync(filename)
-    let buf = await readAll(file)
-    file.close()
-
-    resp buf, mimetype(filename)
-
-  get "/video/@sig/@url":
-    cond "http" in @"url"
-    var url = decodeUrl(@"url")
-    let prefs = cookiePrefs()
-
-    if getHmac(url) != @"sig":
-      resp showError("Failed to verify signature", cfg.title)
-
-    let client = newAsyncHttpClient()
-    var content = await client.getContent(url)
-
-    if ".vmap" in url:
-      var m: RegexMatch
-      discard content.find(re"""url="(.+.m3u8)"""", m)
-      url = decodeUrl(content[m.group(0)[0]])
-      content = await client.getContent(url)
-
-    if ".m3u8" in url:
-      content = proxifyVideo(content, prefs.proxyVideos)
-
-    client.close()
-    resp content, mimetype(url)
+  extend preferences, ""
+  extend timeline, ""
+  extend media, ""
 
 runForever()

+ 64 - 0
src/routes/media.nim

@@ -0,0 +1,64 @@
+import asyncfile, uri, strutils, httpclient, os
+
+import jester, regex
+
+import router_utils
+import ".."/[types, formatters, utils, prefs]
+import ../views/general
+
+export asyncfile, httpclient, os, strutils
+export regex
+export utils
+
+proc createMediaRouter*(cfg: Config) =
+  router media:
+    get "/pic/@sig/@url":
+      cond "http" in @"url"
+      cond "twimg" in @"url"
+      let
+        uri = parseUri(decodeUrl(@"url"))
+        path = uri.path.split("/")[2 .. ^1].join("/")
+        filename = cfg.cacheDir / cleanFilename(path & uri.query)
+
+      if getHmac($uri) != @"sig":
+        resp showError("Failed to verify signature", cfg.title)
+
+      if not existsDir(cfg.cacheDir):
+        createDir(cfg.cacheDir)
+
+      if not existsFile(filename):
+        let client = newAsyncHttpClient()
+        await client.downloadFile($uri, filename)
+        client.close()
+
+      if not existsFile(filename):
+        resp Http404
+
+      let file = openAsync(filename)
+      let buf = await readAll(file)
+      file.close()
+
+      resp buf, mimetype(filename)
+
+    get "/video/@sig/@url":
+      cond "http" in @"url"
+      var url = decodeUrl(@"url")
+      let prefs = cookiePrefs()
+
+      if getHmac(url) != @"sig":
+        resp showError("Failed to verify signature", cfg.title)
+
+      let client = newAsyncHttpClient()
+      var content = await client.getContent(url)
+
+      if ".vmap" in url:
+        var m: RegexMatch
+        discard content.find(re"""url="(.+.m3u8)"""", m)
+        url = decodeUrl(content[m.group(0)[0]])
+        content = await client.getContent(url)
+
+      if ".m3u8" in url:
+        content = proxifyVideo(content, prefs.proxyVideos)
+
+      client.close()
+      resp content, mimetype(url)

+ 38 - 0
src/routes/preferences.nim

@@ -0,0 +1,38 @@
+import strutils, uri
+
+import jester
+
+import router_utils
+import ".."/[prefs, types, utils]
+import ../views/[general, preferences]
+
+export preferences
+
+proc createPrefRouter*(cfg: Config) =
+  router preferences:
+    template savePrefs(): untyped =
+      setCookie("preferences", $prefs.id, daysForward(360), httpOnly=true, secure=cfg.useHttps)
+
+    get "/settings":
+      let prefs = cookiePrefs()
+      let path = refPath()
+      resp renderMain(renderPreferences(prefs, path), prefs, cfg.title, "Preferences", path)
+
+    post "/saveprefs":
+      var prefs = cookiePrefs()
+      genUpdatePrefs()
+      savePrefs()
+      redirect(refPath())
+
+    post "/resetprefs":
+      var prefs = cookiePrefs()
+      resetPrefs(prefs)
+      savePrefs()
+      redirect($(parseUri("/settings") ? filterParams(request.params)))
+
+    post "/enablehls":
+      var prefs = cookiePrefs()
+      prefs.hlsPlayback = true
+      cache(prefs)
+      savePrefs()
+      redirect(refPath())

+ 11 - 0
src/routes/router_utils.nim

@@ -0,0 +1,11 @@
+import ../utils
+
+template cookiePrefs*(): untyped {.dirty.} =
+  getPrefs(request.cookies.getOrDefault("preferences"))
+
+template getPath*(): untyped {.dirty.} =
+  $(parseUri(request.path) ? filterParams(request.params))
+
+template refPath*(): untyped {.dirty.} =
+  if @"referer".len > 0: @"referer" else: "/"
+

+ 127 - 0
src/routes/timeline.nim

@@ -0,0 +1,127 @@
+import asyncdispatch, strutils, sequtils, uri
+
+import jester
+
+import router_utils
+import ".."/[api, prefs, types, utils, cache, formatters, agents, search]
+import ../views/[general, profile, timeline, status]
+
+export uri, sequtils
+export router_utils
+export api, cache, formatters, search, agents
+export profile, timeline, status
+
+proc showSingleTimeline(name, after, agent: string; query: Option[Query];
+                        prefs: Prefs; path, title: string): Future[string] {.async.} =
+  let railFut = getPhotoRail(name, agent)
+
+  var timeline: Timeline
+  var profile: Profile
+  var cachedProfile = hasCachedProfile(name)
+
+  if cachedProfile.isSome:
+    profile = get(cachedProfile)
+
+  if query.isNone:
+    if cachedProfile.isSome:
+      timeline = await getTimeline(name, after, agent)
+    else:
+      (profile, timeline) = await getProfileAndTimeline(name, agent, after)
+      cache(profile)
+  else:
+    var timelineFut = getTimelineSearch(get(query), after, agent)
+    if cachedProfile.isNone:
+      profile = await getCachedProfile(name, agent)
+    timeline = await timelineFut
+
+  if profile.username.len == 0:
+    return ""
+
+  let profileHtml = renderProfile(profile, timeline, await railFut, prefs, path)
+  return renderMain(profileHtml, prefs, title, pageTitle(profile),
+                    pageDesc(profile), path)
+
+proc showMultiTimeline(names: seq[string]; after, agent: string; query: Option[Query];
+                       prefs: Prefs; path, title: string): Future[string] {.async.} =
+  var q = query
+  if q.isSome:
+    get(q).fromUser = names
+  else:
+    q = some(Query(kind: multi, fromUser: names, excludes: @["replies"]))
+
+  var timeline = renderMulti(await getTimelineSearch(get(q), after, agent),
+                             names.join(","), prefs, path)
+
+  return renderMain(timeline, prefs, title, "Multi")
+
+proc showTimeline*(name, after: string; query: Option[Query];
+                  prefs: Prefs; path, title: string): Future[string] {.async.} =
+  let agent = getAgent()
+  let names = name.strip(chars={'/'}).split(",").filterIt(it.len > 0)
+
+  if names.len == 1:
+    return await showSingleTimeline(names[0], after, agent, query, prefs, path, title)
+  else:
+    return await showMultiTimeline(names, after, agent, query, prefs, path, title)
+
+template respTimeline*(timeline: typed) =
+  if timeline.len == 0:
+    resp Http404, showError("User \"" & @"name" & "\" not found", cfg.title)
+  resp timeline
+
+proc createTimelineRouter*(cfg: Config) =
+  setProfileCacheTime(cfg.profileCacheTime)
+
+  router timeline:
+    get "/@name/?":
+      cond '.' notin @"name"
+      respTimeline(await showTimeline(@"name", @"after", none(Query),
+                                      cookiePrefs(), getPath(), cfg.title))
+
+    get "/@name/search":
+      cond '.' notin @"name"
+      let prefs = cookiePrefs()
+      let query = initQuery(@"filter", @"include", @"not", @"sep", @"name")
+      respTimeline(await showTimeline(@"name", @"after", some(query),
+                                      cookiePrefs(), getPath(), cfg.title))
+
+    get "/@name/replies":
+      cond '.' notin @"name"
+      let prefs = cookiePrefs()
+      respTimeline(await showTimeline(@"name", @"after", some(getReplyQuery(@"name")),
+                                      cookiePrefs(), getPath(), cfg.title))
+
+    get "/@name/media":
+      cond '.' notin @"name"
+      let prefs = cookiePrefs()
+      respTimeline(await showTimeline(@"name", @"after", some(getMediaQuery(@"name")),
+                                      cookiePrefs(), getPath(), cfg.title))
+
+    get "/@name/status/@id":
+      cond '.' notin @"name"
+      let prefs = cookiePrefs()
+
+      let conversation = await getTweet(@"name", @"id", getAgent())
+      if conversation == nil or conversation.tweet.id.len == 0:
+        resp Http404, showError("Tweet not found", cfg.title)
+
+      let path = getPath()
+      let title = pageTitle(conversation.tweet.profile)
+      let desc = conversation.tweet.text
+      let html = renderConversation(conversation, prefs, path)
+
+      if conversation.tweet.video.isSome():
+        let thumb = get(conversation.tweet.video).thumb
+        let vidUrl = getVideoEmbed(conversation.tweet.id)
+        resp renderMain(html, prefs, cfg.title, title, desc, path, images = @[thumb],
+                        `type`="video", video=vidUrl)
+      elif conversation.tweet.gif.isSome():
+        let thumb = get(conversation.tweet.gif).thumb
+        let vidUrl = getVideoEmbed(conversation.tweet.id)
+        resp renderMain(html, prefs, cfg.title, title, desc, path, images = @[thumb],
+                        `type`="video", video=vidUrl)
+      else:
+        resp renderMain(html, prefs, cfg.title, title, desc, path, images=conversation.tweet.photos)
+
+    get "/i/web/status/@id":
+      redirect("/i/status/" & @"id")

+ 1 - 1
src/views/profile.nim

@@ -2,7 +2,7 @@ import strutils, strformat
 import karax/[karaxdsl, vdom, vstyles]
 
 import tweet, timeline, renderutils
-import ../types, ../utils, ../formatters
+import ".."/[types, utils, formatters]
 
 proc renderStat(num, class: string; text=""): VNode =
   let t = if text.len > 0: text else: class

+ 1 - 1
src/views/tweet.nim

@@ -2,7 +2,7 @@ import strutils, sequtils
 import karax/[karaxdsl, vdom, vstyles]
 
 import renderutils
-import ../types, ../utils, ../formatters
+import ".."/[types, utils, formatters]
 
 proc renderHeader(tweet: Tweet): VNode =
   buildHtml(tdiv):