Преглед на файлове

Store preferences in cookies, add config defaults

Zed преди 6 години
родител
ревизия
312ff78628
променени са 9 файла, в които са добавени 112 реда и са изтрити 112 реда
  1. 10 1
      nitter.conf
  2. 6 5
      src/config.nim
  3. 3 1
      src/nitter.nim
  4. 11 50
      src/prefs.nim
  5. 67 32
      src/prefs_impl.nim
  6. 2 18
      src/routes/preferences.nim
  7. 7 1
      src/routes/router_utils.nim
  8. 2 2
      src/types.nim
  9. 4 2
      src/views/general.nim

+ 10 - 1
nitter.conf

@@ -11,5 +11,14 @@ directory = "./tmp"
 profileMinutes = 10  # how long to cache profiles
 
 [Config]
-defaultTheme = "Nitter"
 hmacKey = "secretkey" # for signing video urls
+
+# Change default preferences here, see src/prefs_impl.nim for a complete list
+[Preferences]
+theme = "Nitter"
+replaceTwitter = "nitter.net"
+replaceYouTube = "invidio.us"
+replaceInstagram = ""
+proxyVideos = true
+hlsPlayback = false
+infiniteScroll = false

+ 6 - 5
src/config.nim

@@ -1,7 +1,7 @@
 import parsecfg except Config
-import net, types, strutils
+import types, strutils
 
-proc get[T](config: parseCfg.Config; s, v: string; default: T): T =
+proc get*[T](config: parseCfg.Config; s, v: string; default: T): T =
   let val = config.getSectionValue(s, v)
   if val.len == 0: return default
 
@@ -9,10 +9,10 @@ proc get[T](config: parseCfg.Config; s, v: string; default: T): T =
   elif T is bool: parseBool(val)
   elif T is string: val
 
-proc getConfig*(path: string): Config =
+proc getConfig*(path: string): (Config, parseCfg.Config) =
   var cfg = loadConfig(path)
 
-  Config(
+  let conf = Config(
     address: cfg.get("Server", "address", "0.0.0.0"),
     port: cfg.get("Server", "port", 8080),
     useHttps: cfg.get("Server", "https", true),
@@ -23,6 +23,7 @@ proc getConfig*(path: string): Config =
     cacheDir: cfg.get("Cache", "directory", "/tmp/nitter"),
     profileCacheTime: cfg.get("Cache", "profileMinutes", 10),
 
-    defaultTheme: cfg.get("Config", "defaultTheme", "Nitter"),
     hmacKey: cfg.get("Config", "hmacKey", "secretkey")
   )
+
+  return (conf, cfg)

+ 3 - 1
src/nitter.nim

@@ -10,7 +10,9 @@ import routes/[
   unsupported, embed, resolver]
 
 const configPath {.strdefine.} = "./nitter.conf"
-let cfg = getConfig(configPath)
+let (cfg, fullCfg) = getConfig(configPath)
+
+updateDefaultPrefs(fullCfg)
 
 setHmacKey(cfg.hmacKey)
 

+ 11 - 50
src/prefs.nim

@@ -1,54 +1,15 @@
-import strutils, sequtils, macros
-import norm/sqlite
+import tables
+import types, prefs_impl
+from config import get
+from parsecfg import nil
 
-import prefs_impl, types
-export genUpdatePrefs
+export genUpdatePrefs, genResetPrefs
 
-template safeAddColumn(field: typedesc): untyped =
-  try: field.addColumn
-  except DbError: discard
+var defaultPrefs*: Prefs
 
-dbFromTypes("prefs.db", "", "", "", [Prefs])
+proc updateDefaultPrefs*(cfg: parsecfg.Config) =
+  genDefaultPrefs()
 
-withDb:
-  try:
-    createTables()
-  except DbError:
-    discard
-  safeAddColumn Prefs.theme
-  safeAddColumn Prefs.hidePins
-  safeAddColumn Prefs.hideReplies
-  safeAddColumn Prefs.infiniteScroll
-  safeAddColumn Prefs.replaceInstagram
-
-proc getDefaultPrefs(cfg: Config): Prefs =
-  result = genDefaultPrefs()
-  result.replaceTwitter = cfg.hostname
-  result.theme = cfg.defaultTheme
-
-proc cache*(prefs: var Prefs) =
-  withDb:
-    try:
-      doAssert prefs.id != 0
-      discard Prefs.getOne("id = ?", prefs.id)
-      prefs.update()
-    except AssertionError, KeyError:
-      prefs.insert()
-
-proc getPrefs*(id: string; cfg: Config): Prefs =
-  if id.len == 0:
-    return getDefaultPrefs(cfg)
-
-  withDb:
-    try:
-      result.getOne("id = ?", id)
-      if result.theme.len == 0:
-        result.theme = cfg.defaultTheme
-    except KeyError:
-      result = getDefaultPrefs(cfg)
-
-proc resetPrefs*(prefs: var Prefs; cfg: Config) =
-  var defPrefs = getDefaultPrefs(cfg)
-  defPrefs.id = prefs.id
-  cache(defPrefs)
-  prefs = defPrefs
+proc getPrefs*(cookies: Table[string, string]): Prefs =
+  result = defaultPrefs
+  genCookiePrefs()

+ 67 - 32
src/prefs_impl.nim

@@ -14,7 +14,9 @@ type
     defaultOption*: string
     defaultInput*: string
 
-macro genPrefs(prefDsl: untyped) =
+  PrefList* = OrderedTable[string, seq[Pref]]
+
+macro genPrefs*(prefDsl: untyped) =
   var table = nnkTableConstr.newTree()
   for category in prefDsl:
     table.add nnkExprColonExpr.newTree(newLit($category[0]))
@@ -41,7 +43,7 @@ macro genPrefs(prefDsl: untyped) =
 
   let name = ident("prefList")
   result = quote do:
-    const `name`* = toOrderedTable(`table`)
+    const `name`*: PrefList = toOrderedTable(`table`)
 
 genPrefs:
   Privacy:
@@ -101,46 +103,79 @@ iterator allPrefs*(): Pref =
       yield pref
 
 macro genDefaultPrefs*(): untyped =
-  result = nnkObjConstr.newTree(ident("Prefs"))
-
+  result = nnkStmtList.newTree()
   for pref in allPrefs():
-    let default =
-      case pref.kind
-      of checkbox: newLit(pref.defaultState)
-      of select: newLit(pref.defaultOption)
-      of input: newLit(pref.defaultInput)
-
-    result.add nnkExprColonExpr.newTree(ident(pref.name), default)
+    let
+      ident = ident(pref.name)
+      name = newLit(pref.name)
+      default =
+        case pref.kind
+        of checkbox: newLit(pref.defaultState)
+        of select: newLit(pref.defaultOption)
+        of input: newLit(pref.defaultInput)
+
+    result.add quote do:
+      defaultPrefs.`ident` = cfg.get("Preferences", `name`, `default`)
+
+macro genCookiePrefs*(): untyped =
+  result = nnkStmtList.newTree()
+  let cookies = ident("cookies")
+  for pref in allPrefs():
+    let
+      name = pref.name
+      ident = ident(pref.name)
+      kind = newLit(pref.kind)
+      options = pref.options
+
+    result.add quote do:
+      if `name` in `cookies`:
+        let value = `cookies`[`name`]
+        when `kind` == input or `name` == "theme":
+          result.`ident` = value
+        elif `kind` == checkbox:
+          result.`ident` = value == "on"
+        else:
+          if value in `options`: result.`ident` = value
 
 macro genUpdatePrefs*(): untyped =
   result = nnkStmtList.newTree()
-
+  let req = ident("request")
+  for pref in allPrefs():
+    let
+      name = newLit(pref.name)
+      kind = newLit(pref.kind)
+      options = newLit(pref.options)
+      default = nnkDotExpr.newTree(ident("defaultPrefs"), ident(pref.name))
+
+    result.add quote do:
+      let val = @`name`
+      let isDefault =
+        when `kind` == input or `name` == "theme":
+          if `default`.len != val.len: false
+          else: val == `default`
+        elif `kind` == checkbox:
+          (val == "on") == `default`
+        else:
+          val notin `options` or val == `default`
+
+      if isDefault:
+        savePref(`name`, "", `req`, expire=true)
+      else:
+        savePref(`name`, val, `req`)
+
+macro genResetPrefs*(): untyped =
+  result = nnkStmtList.newTree()
+  let req = ident("request")
   for pref in allPrefs():
-    let ident = ident(pref.name)
-    let value = nnkPrefix.newTree(ident("@"), newLit(pref.name))
-
-    case pref.kind
-    of checkbox:
-      result.add quote do: prefs.`ident` = `value` == "on"
-    of input:
-      result.add quote do: prefs.`ident` = xmltree.escape(strip(`value`))
-    of select:
-      let name = pref.name
-      let options = pref.options
-      let default = pref.defaultOption
-      result.add quote do:
-        if `name` == "theme": prefs.`ident` = `value`
-        elif `value` in `options`: prefs.`ident` = `value`
-        else: prefs.`ident` = `default`
-
-  result.add quote do:
-    cache(prefs)
+    let name = newLit(pref.name)
+    result.add quote do:
+      savePref(`name`, "", `req`, expire=true)
 
 macro genPrefsType*(): untyped =
   let name = nnkPostfix.newTree(ident("*"), ident("Prefs"))
   result = quote do:
     type `name` = object
-      id* {.pk, ro.}: int
+      discard
 
   for pref in allPrefs():
     result[0][2][2].add nnkIdentDefs.newTree(

+ 2 - 18
src/routes/preferences.nim

@@ -16,9 +16,6 @@ proc findThemes*(dir: string): seq[string] =
 
 proc createPrefRouter*(cfg: Config) =
   router preferences:
-    template savePrefs(): untyped =
-      setCookie("preferences", $prefs.id, daysForward(360), httpOnly=true, secure=cfg.useHttps)
-
     get "/settings":
       let html = renderPreferences(cookiePrefs(), refPath(), findThemes(cfg.staticDir))
       resp renderMain(html, request, cfg, "Preferences")
@@ -27,27 +24,14 @@ proc createPrefRouter*(cfg: Config) =
       redirect("/settings")
 
     post "/saveprefs":
-      var prefs = cookiePrefs()
       genUpdatePrefs()
-      savePrefs()
       redirect(refPath())
 
     post "/resetprefs":
-      var prefs = cookiePrefs()
-      resetPrefs(prefs, cfg)
-      savePrefs()
+      genResetPrefs()
       redirect($(parseUri("/settings") ? filterParams(request.params)))
 
     post "/enablehls":
-      var prefs = cookiePrefs()
-      prefs.hlsPlayback = true
-      cache(prefs)
-      savePrefs()
+      savePref("hlsPlayback", "on", request)
       redirect(refPath())
 
-    before:
-      if @"theme".len > 0:
-        var prefs = cookiePrefs()
-        prefs.theme = @"theme".capitalizeAscii.replace("_", " ")
-        cache(prefs)
-        savePrefs()

+ 7 - 1
src/routes/router_utils.nim

@@ -1,9 +1,15 @@
 import strutils, sequtils, asyncdispatch, httpclient
+from jester import Request
 import ../utils, ../prefs
 export utils, prefs
 
+template savePref*(pref, value: string; req: Request; expire=false): typed =
+  if not expire or pref in cookies(req):
+    setCookie(pref, value, daysForward(when expire: -10 else: 360),
+              httpOnly=true, secure=cfg.useHttps)
+
 template cookiePrefs*(): untyped {.dirty.} =
-  getPrefs(request.cookies.getOrDefault("preferences"), cfg)
+  getPrefs(cookies(request))
 
 template getPath*(): untyped {.dirty.} =
   $(parseUri(request.path) ? filterParams(request.params))

+ 2 - 2
src/types.nim

@@ -3,6 +3,8 @@ import norm/sqlite
 
 import prefs_impl
 
+genPrefsType()
+
 type
   VideoType* = enum
     vmap, m3u8, mp4
@@ -59,7 +61,6 @@ dbTypes:
           formatIt: dbValue(getTime().toUnix())
         .}: Time
 
-genPrefsType()
 
 type
   QueryKind* = enum
@@ -187,7 +188,6 @@ type
     hostname*: string
     cacheDir*: string
     profileCacheTime*: int
-    defaultTheme*: string
     hmacKey*: string
 
 proc contains*(thread: Chain; tweet: Tweet): bool =

+ 4 - 2
src/views/general.nim

@@ -82,8 +82,10 @@ proc renderHead*(prefs: Prefs; cfg: Config; titleText=""; desc=""; video="";
 
 proc renderMain*(body: VNode; req: Request; cfg: Config; titleText=""; desc="";
                  rss=""; video=""; images: seq[string] = @[]; ogTitle=""): string =
-  let prefs = getPrefs(req.cookies.getOrDefault("preferences"), cfg)
-  let theme = toLowerAscii(prefs.theme).replace(" ", "_")
+  let prefs = getPrefs(req.cookies)
+  var theme = toLowerAscii(prefs.theme).replace(" ", "_")
+  if "theme" in req.params:
+    theme = toLowerAscii(req.params["theme"]).replace(" ", "_")
 
   let node = buildHtml(html(lang="en")):
     renderHead(prefs, cfg, titleText, desc, video, images, ogTitle):