Add server config file
This commit is contained in:
parent
b10d894a11
commit
6a9d182249
8 changed files with 53 additions and 23 deletions
|
@ -28,7 +28,6 @@ is on implementing missing features.
|
|||
- Search (images/videos, hashtags, etc.)
|
||||
- Custom timeline filter
|
||||
- Nitter link previews
|
||||
- Server configuration
|
||||
- More caching (waiting for [moigagoo/norm#19](https://github.com/moigagoo/norm/pull/19))
|
||||
- Simple account system with customizable feed
|
||||
- Video support with hls.js
|
||||
|
|
9
nitter.conf
Normal file
9
nitter.conf
Normal file
|
@ -0,0 +1,9 @@
|
|||
[Server]
|
||||
address = "0.0.0.0"
|
||||
port = 8080
|
||||
title = "nitter"
|
||||
staticDir = "./public"
|
||||
|
||||
[Cache]
|
||||
directory = "/tmp/niter"
|
||||
profileMinutes = 10 # how long to cache profiles
|
|
@ -450,6 +450,7 @@ video {
|
|||
text-overflow: ellipsis;
|
||||
max-width: 100%;
|
||||
font-weight: bold;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
.profile-card-username {
|
||||
|
|
|
@ -26,3 +26,6 @@ proc getCachedProfile*(username: string; force=false): Future[Profile] {.async.}
|
|||
result = await getProfile(username)
|
||||
if result.username.len > 0:
|
||||
result.insert()
|
||||
|
||||
proc setProfileCacheTime*(minutes: int) =
|
||||
profileCacheTime = initDuration(minutes=minutes)
|
||||
|
|
|
@ -69,10 +69,7 @@ proc getUserpic*(profile: Profile; style=""): string =
|
|||
getUserPic(profile.userpic, style)
|
||||
|
||||
proc pageTitle*(profile: Profile): string =
|
||||
&"{profile.fullname} (@{profile.username}) | Nitter"
|
||||
|
||||
proc pageTitle*(page: string): string =
|
||||
&"{page} | Nitter"
|
||||
&"{profile.fullname} (@{profile.username})"
|
||||
|
||||
proc getTime*(tweet: Tweet): string =
|
||||
tweet.time.format("d/M/yyyy', ' HH:mm:ss")
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
import asyncdispatch, asyncfile, httpclient, strutils, strformat, uri, os
|
||||
from net import Port
|
||||
|
||||
import jester, regex
|
||||
|
||||
import api, utils, types, cache, formatters, search
|
||||
|
||||
import api, utils, types, cache, formatters, search, config
|
||||
import views/[general, profile, status]
|
||||
|
||||
const cacheDir {.strdefine.} = "/tmp/nitter"
|
||||
const configPath {.strdefine.} = "./nitter.conf"
|
||||
let cfg = getConfig(configPath)
|
||||
|
||||
proc showTimeline(name, after: string; query: Option[Query]): Future[string] {.async.} =
|
||||
let
|
||||
|
@ -24,20 +26,27 @@ proc showTimeline(name, after: string; query: Option[Query]): Future[string] {.a
|
|||
return ""
|
||||
|
||||
let profileHtml = renderProfile(profile, await timelineFut, await railFut)
|
||||
return renderMain(profileHtml, title=pageTitle(profile))
|
||||
return renderMain(profileHtml, title=cfg.title, titleText=pageTitle(profile))
|
||||
|
||||
template respTimeline(timeline: typed) =
|
||||
if timeline.len == 0:
|
||||
resp Http404, showError("User \"" & @"name" & "\" not found")
|
||||
resp Http404, showError("User \"" & @"name" & "\" not found", cfg.title)
|
||||
resp timeline
|
||||
|
||||
setProfileCacheTime(cfg.profileCacheTime)
|
||||
|
||||
settings:
|
||||
port = Port(cfg.port)
|
||||
staticDir = cfg.staticDir
|
||||
bindAddr = cfg.address
|
||||
|
||||
routes:
|
||||
get "/":
|
||||
resp renderMain(renderSearch(), title=pageTitle("Search"))
|
||||
resp renderMain(renderSearch(), title=cfg.title, titleText="Search")
|
||||
|
||||
post "/search":
|
||||
if @"query".len == 0:
|
||||
resp Http404, showError("Please enter a username.")
|
||||
resp Http404, showError("Please enter a username.", cfg.title)
|
||||
redirect("/" & @"query")
|
||||
|
||||
get "/@name/?":
|
||||
|
@ -62,7 +71,7 @@ routes:
|
|||
|
||||
let conversation = await getTweet(@"name", @"id")
|
||||
if conversation == nil or conversation.tweet.id.len == 0:
|
||||
resp Http404, showError("Tweet not found")
|
||||
resp Http404, showError("Tweet not found", cfg.title)
|
||||
|
||||
let title = pageTitle(conversation.tweet.profile)
|
||||
resp renderMain(renderConversation(conversation), title=title)
|
||||
|
@ -74,13 +83,13 @@ routes:
|
|||
let
|
||||
uri = parseUri(decodeUrl(@"url"))
|
||||
path = uri.path.split("/")[2 .. ^1].join("/")
|
||||
filename = cacheDir / cleanFilename(path & uri.query)
|
||||
filename = cfg.cacheDir / cleanFilename(path & uri.query)
|
||||
|
||||
if getHmac($uri) != @"sig":
|
||||
resp showError("Failed to verify signature")
|
||||
resp showError("Failed to verify signature", cfg.title)
|
||||
|
||||
if not existsDir(cacheDir):
|
||||
createDir(cacheDir)
|
||||
if not existsDir(cfg.cacheDir):
|
||||
createDir(cfg.cacheDir)
|
||||
|
||||
if not existsFile(filename):
|
||||
let client = newAsyncHttpClient()
|
||||
|
@ -92,6 +101,7 @@ routes:
|
|||
|
||||
let file = openAsync(filename)
|
||||
defer: file.close()
|
||||
|
||||
resp await readAll(file), mimetype(filename)
|
||||
|
||||
get "/video/@sig/@url":
|
||||
|
@ -100,7 +110,7 @@ routes:
|
|||
let url = decodeUrl(@"url")
|
||||
|
||||
if getHmac(url) != @"sig":
|
||||
resp showError("Failed to verify signature")
|
||||
resp showError("Failed to verify signature", cfg.title)
|
||||
|
||||
let
|
||||
client = newAsyncHttpClient()
|
||||
|
|
|
@ -147,5 +147,13 @@ type
|
|||
beginning*: bool
|
||||
query*: Option[Query]
|
||||
|
||||
Config* = ref object
|
||||
address*: string
|
||||
port*: int
|
||||
title*: string
|
||||
staticDir*: string
|
||||
cacheDir*: string
|
||||
profileCacheTime*: int
|
||||
|
||||
proc contains*(thread: Thread; tweet: Tweet): bool =
|
||||
thread.tweets.anyIt(it.id == tweet.id)
|
||||
|
|
|
@ -2,17 +2,20 @@ import karax/[karaxdsl, vdom]
|
|||
|
||||
const doctype = "<!DOCTYPE html>\n"
|
||||
|
||||
proc renderMain*(body: VNode; title="Nitter"): string =
|
||||
proc renderMain*(body: VNode; title="Nitter"; titleText=""): string =
|
||||
let node = buildHtml(html(lang="en")):
|
||||
head:
|
||||
title: text title
|
||||
if titleText.len > 0:
|
||||
title: text titleText & " | " & title
|
||||
else:
|
||||
title: text title
|
||||
link(rel="stylesheet", `type`="text/css", href="/style.css")
|
||||
|
||||
body:
|
||||
nav(id="nav", class="nav-bar container"):
|
||||
tdiv(class="inner-nav"):
|
||||
tdiv(class="item"):
|
||||
a(href="/", class="site-name"): text "nitter"
|
||||
a(href="/", class="site-name"): text title
|
||||
|
||||
tdiv(id="content", class="container"):
|
||||
body
|
||||
|
@ -31,5 +34,5 @@ proc renderError*(error: string): VNode =
|
|||
tdiv(class="error-panel"):
|
||||
span: text error
|
||||
|
||||
proc showError*(error: string): string =
|
||||
renderMain(renderError(error), title = "Error | Nitter")
|
||||
proc showError*(error: string; title: string): string =
|
||||
renderMain(renderError(error), title=title, titleText="Error")
|
||||
|
|
Loading…
Reference in a new issue