Optimize usage of cookie preferences
This commit is contained in:
		
							parent
							
								
									310c5e936d
								
							
						
					
					
						commit
						db45433ec4
					
				| 
						 | 
				
			
			@ -7,7 +7,7 @@ import types, config, prefs, formatters, redis_cache, tokens
 | 
			
		|||
import views/[general, about]
 | 
			
		||||
import routes/[
 | 
			
		||||
  preferences, timeline, status, media, search, rss, list,
 | 
			
		||||
  unsupported, embed, resolver]
 | 
			
		||||
  unsupported, embed, resolver, router_utils]
 | 
			
		||||
 | 
			
		||||
const configPath {.strdefine.} = "./nitter.conf"
 | 
			
		||||
let (cfg, fullCfg) = getConfig(configPath)
 | 
			
		||||
| 
						 | 
				
			
			@ -49,10 +49,10 @@ settings:
 | 
			
		|||
 | 
			
		||||
routes:
 | 
			
		||||
  get "/":
 | 
			
		||||
    resp renderMain(renderSearch(), request, cfg)
 | 
			
		||||
    resp renderMain(renderSearch(), request, cfg, themePrefs())
 | 
			
		||||
 | 
			
		||||
  get "/about":
 | 
			
		||||
    resp renderMain(renderAbout(), request, cfg)
 | 
			
		||||
    resp renderMain(renderAbout(), request, cfg, themePrefs())
 | 
			
		||||
 | 
			
		||||
  get "/explore":
 | 
			
		||||
    redirect("/about")
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -12,4 +12,10 @@ proc updateDefaultPrefs*(cfg: parsecfg.Config) =
 | 
			
		|||
 | 
			
		||||
proc getPrefs*(cookies: Table[string, string]): Prefs =
 | 
			
		||||
  result = defaultPrefs
 | 
			
		||||
  genCookiePrefs()
 | 
			
		||||
  genCookiePrefs(cookies)
 | 
			
		||||
 | 
			
		||||
template getPref*(cookies: Table[string, string], pref): untyped =
 | 
			
		||||
  bind genCookiePref
 | 
			
		||||
  var res = defaultPrefs.`pref`
 | 
			
		||||
  genCookiePref(cookies, pref, res)
 | 
			
		||||
  res
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -120,9 +120,8 @@ macro genDefaultPrefs*(): untyped =
 | 
			
		|||
    result.add quote do:
 | 
			
		||||
      defaultPrefs.`ident` = cfg.get("Preferences", `name`, `default`)
 | 
			
		||||
 | 
			
		||||
macro genCookiePrefs*(): untyped =
 | 
			
		||||
macro genCookiePrefs*(cookies): untyped =
 | 
			
		||||
  result = nnkStmtList.newTree()
 | 
			
		||||
  let cookies = ident("cookies")
 | 
			
		||||
  for pref in allPrefs():
 | 
			
		||||
    let
 | 
			
		||||
      name = pref.name
 | 
			
		||||
| 
						 | 
				
			
			@ -132,14 +131,36 @@ macro genCookiePrefs*(): untyped =
 | 
			
		|||
 | 
			
		||||
    result.add quote do:
 | 
			
		||||
      if `name` in `cookies`:
 | 
			
		||||
        let value = `cookies`[`name`]
 | 
			
		||||
        when `kind` == input or `name` == "theme":
 | 
			
		||||
          result.`ident` = value
 | 
			
		||||
          result.`ident` = `cookies`[`name`]
 | 
			
		||||
        elif `kind` == checkbox:
 | 
			
		||||
          result.`ident` = value == "on"
 | 
			
		||||
          result.`ident` = `cookies`[`name`] == "on"
 | 
			
		||||
        else:
 | 
			
		||||
          let value = `cookies`[`name`]
 | 
			
		||||
          if value in `options`: result.`ident` = value
 | 
			
		||||
 | 
			
		||||
macro genCookiePref*(cookies, prefName, res): untyped =
 | 
			
		||||
  result = nnkStmtList.newTree()
 | 
			
		||||
  for pref in allPrefs():
 | 
			
		||||
    let ident = ident(pref.name)
 | 
			
		||||
    if ident != prefName:
 | 
			
		||||
      continue
 | 
			
		||||
 | 
			
		||||
    let
 | 
			
		||||
      name = pref.name
 | 
			
		||||
      kind = newLit(pref.kind)
 | 
			
		||||
      options = pref.options
 | 
			
		||||
 | 
			
		||||
    result.add quote do:
 | 
			
		||||
      if `name` in `cookies`:
 | 
			
		||||
        when `kind` == input or `name` == "theme":
 | 
			
		||||
          `res` = `cookies`[`name`]
 | 
			
		||||
        elif `kind` == checkbox:
 | 
			
		||||
          `res` = `cookies`[`name`] == "on"
 | 
			
		||||
        else:
 | 
			
		||||
          let value = `cookies`[`name`]
 | 
			
		||||
          if value in `options`: `res` = value
 | 
			
		||||
 | 
			
		||||
macro genUpdatePrefs*(): untyped =
 | 
			
		||||
  result = nnkStmtList.newTree()
 | 
			
		||||
  let req = ident("request")
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,7 +15,7 @@ template respList*(list, timeline, vnode: typed) =
 | 
			
		|||
    html = renderList(vnode, timeline.query, list)
 | 
			
		||||
    rss = "/$1/lists/$2/rss" % [@"name", @"list"]
 | 
			
		||||
 | 
			
		||||
  resp renderMain(html, request, cfg, rss=rss)
 | 
			
		||||
  resp renderMain(html, request, cfg, prefs, rss=rss)
 | 
			
		||||
 | 
			
		||||
proc createListRouter*(cfg: Config) =
 | 
			
		||||
  router list:
 | 
			
		||||
| 
						 | 
				
			
			@ -23,18 +23,20 @@ proc createListRouter*(cfg: Config) =
 | 
			
		|||
      cond '.' notin @"name"
 | 
			
		||||
      cond @"name" != "i"
 | 
			
		||||
      let
 | 
			
		||||
        prefs = cookiePrefs()
 | 
			
		||||
        list = await getCachedList(@"name", @"list")
 | 
			
		||||
        timeline = await getListTimeline(list.id, getCursor())
 | 
			
		||||
        vnode = renderTimelineTweets(timeline, cookiePrefs(), request.path)
 | 
			
		||||
        vnode = renderTimelineTweets(timeline, prefs, request.path)
 | 
			
		||||
      respList(list, timeline, vnode)
 | 
			
		||||
 | 
			
		||||
    get "/@name/lists/@list/members":
 | 
			
		||||
      cond '.' notin @"name"
 | 
			
		||||
      cond @"name" != "i"
 | 
			
		||||
      let
 | 
			
		||||
        prefs = cookiePrefs()
 | 
			
		||||
        list = await getCachedList(@"name", @"list")
 | 
			
		||||
        members = await getListMembers(list)
 | 
			
		||||
      respList(list, members, renderTimelineUsers(members, cookiePrefs(), request.path))
 | 
			
		||||
      respList(list, members, renderTimelineUsers(members, prefs, request.path))
 | 
			
		||||
 | 
			
		||||
    get "/i/lists/@id/?":
 | 
			
		||||
      cond '.' notin @"id"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,7 +5,6 @@ import jester
 | 
			
		|||
 | 
			
		||||
import router_utils
 | 
			
		||||
import ".."/[types, formatters, agents, utils]
 | 
			
		||||
import ../views/general
 | 
			
		||||
 | 
			
		||||
export asynchttpserver, asyncstreams, asyncfile, asyncnet
 | 
			
		||||
export httpclient, os, strutils, asyncstreams, base64, re
 | 
			
		||||
| 
						 | 
				
			
			@ -16,6 +15,12 @@ const
 | 
			
		|||
 | 
			
		||||
let mediaAgent* = getAgent()
 | 
			
		||||
 | 
			
		||||
proc safeFetch*(url, agent: string): Future[string] {.async.} =
 | 
			
		||||
  let client = newAsyncHttpClient(userAgent=agent)
 | 
			
		||||
  try: result = await client.getContent(url)
 | 
			
		||||
  except: discard
 | 
			
		||||
  finally: client.close()
 | 
			
		||||
 | 
			
		||||
template respond*(req: asynchttpserver.Request; headers) =
 | 
			
		||||
  var msg = "HTTP/1.1 200 OK\c\L"
 | 
			
		||||
  for k, v in headers:
 | 
			
		||||
| 
						 | 
				
			
			@ -57,7 +62,7 @@ proc proxyMedia*(req: jester.Request; url: string): Future[HttpCode] {.async.} =
 | 
			
		|||
  except HttpRequestError, ProtocolError, OSError:
 | 
			
		||||
    result = Http404
 | 
			
		||||
  finally:
 | 
			
		||||
    client.safeClose()
 | 
			
		||||
    client.close()
 | 
			
		||||
 | 
			
		||||
template check*(code): untyped =
 | 
			
		||||
  if code != Http200:
 | 
			
		||||
| 
						 | 
				
			
			@ -111,9 +116,7 @@ proc createMediaRouter*(cfg: Config) =
 | 
			
		|||
          resp Http404
 | 
			
		||||
 | 
			
		||||
      if ".m3u8" in url:
 | 
			
		||||
        let
 | 
			
		||||
          vid = await safeFetch(url, mediaAgent)
 | 
			
		||||
          prefs = cookiePrefs()
 | 
			
		||||
        content = proxifyVideo(vid, prefs.proxyVideos)
 | 
			
		||||
        let vid = await safeFetch(url, mediaAgent)
 | 
			
		||||
        content = proxifyVideo(vid, cookiePref(proxyVideos))
 | 
			
		||||
 | 
			
		||||
      resp content, m3u8Mime
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,8 +17,10 @@ proc findThemes*(dir: string): seq[string] =
 | 
			
		|||
proc createPrefRouter*(cfg: Config) =
 | 
			
		||||
  router preferences:
 | 
			
		||||
    get "/settings":
 | 
			
		||||
      let html = renderPreferences(cookiePrefs(), refPath(), findThemes(cfg.staticDir))
 | 
			
		||||
      resp renderMain(html, request, cfg, "Preferences")
 | 
			
		||||
      let
 | 
			
		||||
        prefs = cookiePrefs()
 | 
			
		||||
        html = renderPreferences(prefs, refPath(), findThemes(cfg.staticDir))
 | 
			
		||||
      resp renderMain(html, request, cfg, prefs, "Preferences")
 | 
			
		||||
 | 
			
		||||
    get "/settings/@i?":
 | 
			
		||||
      redirect("/settings")
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,7 +1,9 @@
 | 
			
		|||
import strutils, sequtils, asyncdispatch, httpclient, uri
 | 
			
		||||
from jester import Request
 | 
			
		||||
import ".."/[utils, prefs]
 | 
			
		||||
export utils, prefs
 | 
			
		||||
import strutils, sequtils, uri, tables
 | 
			
		||||
from jester import Request, cookies
 | 
			
		||||
 | 
			
		||||
import ../views/general
 | 
			
		||||
import ".."/[utils, prefs, types]
 | 
			
		||||
export utils, prefs, types
 | 
			
		||||
 | 
			
		||||
template savePref*(pref, value: string; req: Request; expire=false) =
 | 
			
		||||
  if not expire or pref in cookies(req):
 | 
			
		||||
| 
						 | 
				
			
			@ -11,6 +13,17 @@ template savePref*(pref, value: string; req: Request; expire=false) =
 | 
			
		|||
template cookiePrefs*(): untyped {.dirty.} =
 | 
			
		||||
  getPrefs(cookies(request))
 | 
			
		||||
 | 
			
		||||
template cookiePref*(pref): untyped {.dirty.} =
 | 
			
		||||
  getPref(cookies(request), pref)
 | 
			
		||||
 | 
			
		||||
template themePrefs*(): Prefs =
 | 
			
		||||
  var res = defaultPrefs
 | 
			
		||||
  res.theme = cookiePref(theme)
 | 
			
		||||
  res
 | 
			
		||||
 | 
			
		||||
template showError*(error: string; cfg: Config): string =
 | 
			
		||||
  renderMain(renderError(error), request, cfg, themePrefs(), "Error")
 | 
			
		||||
 | 
			
		||||
template getPath*(): untyped {.dirty.} =
 | 
			
		||||
  $(parseUri(request.path) ? filterParams(request.params))
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -28,13 +41,3 @@ template getCursor*(req: Request): string =
 | 
			
		|||
 | 
			
		||||
proc getNames*(name: string): seq[string] =
 | 
			
		||||
  name.strip(chars={'/'}).split(",").filterIt(it.len > 0)
 | 
			
		||||
 | 
			
		||||
proc safeClose*(client: AsyncHttpClient) =
 | 
			
		||||
  try: client.close()
 | 
			
		||||
  except: discard
 | 
			
		||||
 | 
			
		||||
proc safeFetch*(url, agent: string): Future[string] {.async.} =
 | 
			
		||||
  let client = newAsyncHttpClient(userAgent=agent)
 | 
			
		||||
  try: result = await client.getContent(url)
 | 
			
		||||
  except: discard
 | 
			
		||||
  finally: client.safeClose()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,7 +3,7 @@ import asyncdispatch, strutils, tables, times, sequtils, hashes, snappy
 | 
			
		|||
import jester
 | 
			
		||||
 | 
			
		||||
import router_utils, timeline
 | 
			
		||||
import ../query, ../views/general
 | 
			
		||||
import ../query
 | 
			
		||||
 | 
			
		||||
include "../views/rss.nimf"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -16,20 +16,22 @@ proc createSearchRouter*(cfg: Config) =
 | 
			
		|||
      if @"q".len > 200:
 | 
			
		||||
        resp Http400, showError("Search input too long.", cfg)
 | 
			
		||||
 | 
			
		||||
      let prefs = cookiePrefs()
 | 
			
		||||
      let query = initQuery(params(request))
 | 
			
		||||
      let
 | 
			
		||||
        prefs = cookiePrefs()
 | 
			
		||||
        query = initQuery(params(request))
 | 
			
		||||
 | 
			
		||||
      case query.kind
 | 
			
		||||
      of users:
 | 
			
		||||
        if "," in @"q":
 | 
			
		||||
          redirect("/" & @"q")
 | 
			
		||||
        let users = await getSearch[Profile](query, getCursor())
 | 
			
		||||
        resp renderMain(renderUserSearch(users, prefs), request, cfg)
 | 
			
		||||
        resp renderMain(renderUserSearch(users, prefs), request, cfg, prefs)
 | 
			
		||||
      of tweets:
 | 
			
		||||
        let tweets = await getSearch[Tweet](query, getCursor())
 | 
			
		||||
        let rss = "/search/rss?" & genQueryUrl(query)
 | 
			
		||||
        let
 | 
			
		||||
          tweets = await getSearch[Tweet](query, getCursor())
 | 
			
		||||
          rss = "/search/rss?" & genQueryUrl(query)
 | 
			
		||||
        resp renderMain(renderTweetSearch(tweets, prefs, getPath()),
 | 
			
		||||
                        request, cfg, rss=rss)
 | 
			
		||||
                        request, cfg, prefs, rss=rss)
 | 
			
		||||
      else:
 | 
			
		||||
        resp Http404, showError("Invalid search", cfg)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -48,8 +48,8 @@ proc createStatusRouter*(cfg: Config) =
 | 
			
		|||
        video = getPicUrl(get(conv.tweet.gif).url)
 | 
			
		||||
 | 
			
		||||
      let html = renderConversation(conv, prefs, getPath() & "#m")
 | 
			
		||||
      resp renderMain(html, request, cfg, title, desc,
 | 
			
		||||
                      images=images, video=video, ogTitle=ogTitle)
 | 
			
		||||
      resp renderMain(html, request, cfg, prefs, title, desc, ogTitle,
 | 
			
		||||
                      images=images, video=video)
 | 
			
		||||
 | 
			
		||||
    get "/@name/@s/@id/@m/?@i?":
 | 
			
		||||
      cond @"s" in ["status", "statuses"]
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -78,7 +78,7 @@ proc showTimeline*(request: Request; query: Query; cfg: Config; prefs: Prefs;
 | 
			
		|||
    let
 | 
			
		||||
      timeline = await getSearch[Tweet](query, after)
 | 
			
		||||
      html = renderTweetSearch(timeline, prefs, getPath())
 | 
			
		||||
    return renderMain(html, request, cfg, "Multi", rss=rss)
 | 
			
		||||
    return renderMain(html, request, cfg, prefs, "Multi", rss=rss)
 | 
			
		||||
 | 
			
		||||
  var (p, t, r) = await fetchSingleTimeline(after, query)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -86,7 +86,7 @@ proc showTimeline*(request: Request; query: Query; cfg: Config; prefs: Prefs;
 | 
			
		|||
  if p.id.len == 0: return
 | 
			
		||||
 | 
			
		||||
  let pHtml = renderProfile(p, t, r, prefs, getPath())
 | 
			
		||||
  result = renderMain(pHtml, request, cfg, pageTitle(p), pageDesc(p),
 | 
			
		||||
  result = renderMain(pHtml, request, cfg, prefs, pageTitle(p), pageDesc(p),
 | 
			
		||||
                      rss=rss, images = @[p.getUserpic("_200x200")])
 | 
			
		||||
 | 
			
		||||
template respTimeline*(timeline: typed) =
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,7 +7,7 @@ import ../views/[general, about]
 | 
			
		|||
proc createUnsupportedRouter*(cfg: Config) =
 | 
			
		||||
  router unsupported:
 | 
			
		||||
    template feature {.dirty.} =
 | 
			
		||||
      resp renderMain(renderFeature(), request, cfg)
 | 
			
		||||
      resp renderMain(renderFeature(), request, cfg, themePrefs())
 | 
			
		||||
 | 
			
		||||
    get "/about/feature": feature()
 | 
			
		||||
    get "/intent/?@i?": feature()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -80,9 +80,9 @@ proc renderHead*(prefs: Prefs; cfg: Config; titleText=""; desc=""; video="";
 | 
			
		|||
      meta(property="og:video:secure_url", content=video)
 | 
			
		||||
      meta(property="og:video:type", content="text/html")
 | 
			
		||||
 | 
			
		||||
proc renderMain*(body: VNode; req: Request; cfg: Config; titleText=""; desc="";
 | 
			
		||||
                 rss=""; video=""; images: seq[string] = @[]; ogTitle=""): string =
 | 
			
		||||
  let prefs = getPrefs(req.cookies)
 | 
			
		||||
proc renderMain*(body: VNode; req: Request; cfg: Config; prefs=defaultPrefs;
 | 
			
		||||
                 titleText=""; desc=""; ogTitle=""; rss=""; video="";
 | 
			
		||||
                 images: seq[string] = @[]): string =
 | 
			
		||||
  var theme = toLowerAscii(prefs.theme).replace(" ", "_")
 | 
			
		||||
  if "theme" in req.params:
 | 
			
		||||
    theme = toLowerAscii(req.params["theme"]).replace(" ", "_")
 | 
			
		||||
| 
						 | 
				
			
			@ -107,6 +107,3 @@ proc renderError*(error: string): VNode =
 | 
			
		|||
  buildHtml(tdiv(class="panel-container")):
 | 
			
		||||
    tdiv(class="error-panel"):
 | 
			
		||||
      span: text error
 | 
			
		||||
 | 
			
		||||
template showError*(error: string; cfg: Config): string =
 | 
			
		||||
  renderMain(renderError(error), request, cfg, "Error")
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue