A src/lib/auth.nim => src/lib/auth.nim +21 -0
@@ 0,0 1,21 @@
+import jester
+import karax/vdom
+import ../views/[common, unauthorized]
+
+proc isAuthOK(req: Request, token: string): bool =
+ if token == "":
+ return true
+
+ try:
+ var t = req.cookies["TOKEN"]
+ if t == token:
+ return true
+ except KeyError:
+ discard
+ return false
+
+proc renderWithAuth*(req: Request, token: string, body: VNode): string =
+ if isAuthOK(req, token):
+ result = renderPage(body)
+ else:
+ result = renderPage(renderUnauthorized())
M src/lib/server.nim => src/lib/server.nim +32 -20
@@ 1,28 1,40 @@
import std/strutils
from os import getEnv
import jester
-import ../routes/[edit, home, new, tags], ../views/[common, forms]
+import ../lib/auth, ../routes/[edit, home, new, tags], ../views/[forms]
-router hutRouter:
- get "/":
- resp renderPage(renderHome())
- get "/new":
- resp renderPage(renderNew())
- get "/edit/@id":
- resp renderPage(renderEdit(@"id"))
- get "/tags":
- resp renderPage(renderTagsList())
- get "/tags/@name":
- resp renderPage(renderTag(@"name"))
- post "/api/v1/url":
- saveUrl(request)
- redirect("/")
- post "/api/v1/url/delete":
- removeUrl(request)
- redirect("/")
-
-proc startServer*() =
+proc startServer*(token: string) =
let port = getEnv("PORT", "3000")
let settings = newSettings(port = Port(parseInt(port)))
+
+ router hutRouter:
+ get "/":
+ resp renderWithAuth(request, token, renderHome())
+ get "/new":
+ resp renderWithAuth(request, token, renderNew())
+ get "/edit/@id":
+ resp renderWithAuth(request, token, renderEdit(@"id"))
+ get "/tags":
+ resp renderWithAuth(request, token, renderTagsList())
+ get "/tags/@name":
+ resp renderWithAuth(request, token, renderTag(@"name"))
+ post "/api/v1/url":
+ saveUrl(request)
+ redirect("/")
+ post "/api/v1/url/delete":
+ removeUrl(request)
+ redirect("/")
+ post "/api/v1/token":
+ try:
+ let t = request.formData.getOrDefault("token").body
+ if t == token:
+ setCookie("TOKEN", value = t, path = "/", secure = true,
+ httpOnly = true)
+ except KeyError:
+ # TODO show error message
+ discard
+ # TODO redirect to requested page.
+ redirect("/")
+
var jester = initJester(hutRouter, settings = settings)
jester.serve()
A src/routes/token.nim => src/routes/token.nim +1 -0
M src/urlhut.nim => src/urlhut.nim +7 -3
@@ 1,13 1,18 @@
# This is just an example to get you started. A typical binary package
# uses this file as the main entry point of the application.
import std/logging
+from os import getEnv
import dotenv
import ./lib/[db, server]
proc ctrlc() {.noconv.} =
+ info("🛖 is stopping")
closeDb()
+ quit()
when isMainModule:
+ info("🛖 is starting")
+
addHandler(newConsoleLogger())
setLogFilter(lvlError)
@@ 15,6 20,5 @@ when isMainModule:
load()
initDb()
- info("🛖 is starting")
-
- startServer()
+ let token = getEnv("TOKEN", "")
+ startServer(token)
A src/views/unauthorized.nim => src/views/unauthorized.nim +14 -0
@@ 0,0 1,14 @@
+import karax/[karaxdsl, vdom]
+
+proc renderUnauthorized*(): VNode =
+ result = buildHtml(tdiv):
+ p: text "Error 401: unauthorized"
+ p: text "Do you have a token?"
+ form(`method` = "post", action = "/api/v1/token",
+ enctype = "multipart/form-data"):
+ label(`for` = "token"):
+ text "Token"
+ input(`type` = "text", name = "token", id = "token", autofocus = "",
+ placeholder = "Enter your token...")
+ button(`type` = "submit"):
+ text "submit"
M urlhut.nimble => urlhut.nimble +1 -1
@@ 10,7 10,7 @@ bin = @["urlhut"]
# Dependencies
-requires "nim >= 1.6.0"
+requires "nim >= 1.6.6"
requires "jester >= 0.5.0"
requires "karax#fa4a2dc"
requires "dotenv >= 2.0.0"