Initial version.
This commit is contained in:
@@ -0,0 +1,2 @@
|
|||||||
|
.*.sw?
|
||||||
|
/short_url
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"mappings": {
|
||||||
|
"/hn": "https://news.ycombinator.com",
|
||||||
|
"/([^/]+)/latest": "https://app.abbey.com/discussions/$1?order=timestamp,desc"
|
||||||
|
},
|
||||||
|
"port": "8081"
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# Package
|
||||||
|
|
||||||
|
version = "1.0.0"
|
||||||
|
author = "Jonathan Bernard"
|
||||||
|
description = "URL shortener/expander for Probatem at https://pbtm.us"
|
||||||
|
license = "GPL3"
|
||||||
|
srcDir = "src"
|
||||||
|
bin = @["short_url"]
|
||||||
|
|
||||||
|
|
||||||
|
# Dependencies
|
||||||
|
|
||||||
|
requires "nim >= 2.2.4"
|
||||||
|
requires @["cliutils", "mummy", "namespaced_logging >= 2.1.2", "webby"]
|
||||||
|
|
||||||
|
task updateVersion, "Update the version of this package.":
|
||||||
|
exec "update_version interactive src/short_url.nim"
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
import std/[json, nre, sequtils, strutils]
|
||||||
|
import cliutils, docopt, mummy, namespaced_logging
|
||||||
|
import webby/httpheaders
|
||||||
|
|
||||||
|
const USAGE = """
|
||||||
|
Usage:
|
||||||
|
short_url serve [options]
|
||||||
|
|
||||||
|
Options:
|
||||||
|
|
||||||
|
-c, --config <cfgFile> Use the given config file (defaults to ./short-url.cfg.json)
|
||||||
|
--debug Enable debug-level logging.
|
||||||
|
--trace Enable trace-level logging (takes precedence over --debug).
|
||||||
|
"""
|
||||||
|
|
||||||
|
const VERSION = "1.0.0"
|
||||||
|
const CT_TXT = "text/plain"
|
||||||
|
|
||||||
|
type
|
||||||
|
ShortUrlsConfig = object
|
||||||
|
mappings: seq[tuple[src, dst: string]]
|
||||||
|
port: int
|
||||||
|
cfg: CombinedConfig
|
||||||
|
|
||||||
|
Context = object
|
||||||
|
mappings: seq[tuple[src:string, regex: Regex, tmpl: string]]
|
||||||
|
logger: Logger
|
||||||
|
|
||||||
|
proc loadConfig(args: Table[string, Value]): ShortUrlsConfig =
|
||||||
|
let cfgFile =
|
||||||
|
if args["--config"]: $args["--config"]
|
||||||
|
else: findConfigFile("short-url.cfg.json")
|
||||||
|
|
||||||
|
result.cfg = initCombinedConfig(filename = cfgFile, docopt = args)
|
||||||
|
result.port = parseInt(result.cfg.getVal("port", "80"))
|
||||||
|
|
||||||
|
result.mappings = @[]
|
||||||
|
for (k, v) in pairs(result.cfg.getJson("mappings")):
|
||||||
|
result.mappings.add((k, v.getStr))
|
||||||
|
|
||||||
|
proc initMappings(m: seq[tuple[src, dst: string]]):
|
||||||
|
seq[tuple[src: string, regex: Regex, tmpl: string]] =
|
||||||
|
result = m.mapIt((it.src, re(it.src), it.dst))
|
||||||
|
|
||||||
|
proc makeHandler(mappings: seq[tuple[src,dst: string]], logSvc: LogService): RequestHandler =
|
||||||
|
let ctx = Context(
|
||||||
|
mappings: initMappings(mappings),
|
||||||
|
logger: threadLocalRef(logSvc).getLogger("pbm/short_url/response"))
|
||||||
|
|
||||||
|
return proc(req: Request) {.gcsafe.} =
|
||||||
|
|
||||||
|
if req.path == "/internal/health":
|
||||||
|
ctx.logger.info("Successful health check")
|
||||||
|
req.respond(200, @[("Content-Type", CT_TXT)], "healthy and happy")
|
||||||
|
return
|
||||||
|
|
||||||
|
for (src, regex, tmpl) in ctx.mappings:
|
||||||
|
if match(req.uri, regex).isSome:
|
||||||
|
ctx.logger.debug(%*{
|
||||||
|
"msg": "found match",
|
||||||
|
"matching_pattern": %src,
|
||||||
|
"uri": %req.uri,
|
||||||
|
"tmpl": %tmpl })
|
||||||
|
|
||||||
|
req.respond(302,
|
||||||
|
toWebby(@[("Location", replace(req.uri, regex, tmpl))]),
|
||||||
|
"")
|
||||||
|
return
|
||||||
|
|
||||||
|
ctx.logger.debug(%*{
|
||||||
|
"msg": "no match found",
|
||||||
|
"uri": %req.uri,
|
||||||
|
"patterns": %mapIt(mappings, it.src) })
|
||||||
|
|
||||||
|
req.respond(404, @[("Content-Type", CT_TXT)], "not found")
|
||||||
|
|
||||||
|
when isMainModule:
|
||||||
|
var logSvc = initLogService()
|
||||||
|
try:
|
||||||
|
|
||||||
|
let llsvc = threadLocalRef(logSvc)
|
||||||
|
llsvc.addAppender(initConsoleLogAppender(formatter = formatJsonStructuredLog))
|
||||||
|
llsvc.setRootThreshold(lvlInfo)
|
||||||
|
let logger = llsvc.getLogger("pbm/short_url")
|
||||||
|
|
||||||
|
let args = docopt(USAGE, version = VERSION)
|
||||||
|
let cfg = loadConfig(args)
|
||||||
|
|
||||||
|
if args["--trace"]: logSvc.setRootThreshold(lvlAll)
|
||||||
|
elif args["--debug"]: logSvc.setRootThreshold(lvlDebug)
|
||||||
|
|
||||||
|
let server = newServer(makeHandler(cfg.mappings, logSvc))
|
||||||
|
logger.info("short_url v$# listening for requests on $#" %
|
||||||
|
[ VERSION, $cfg.port ])
|
||||||
|
server.serve(Port(cfg.port), address = "0.0.0.0")
|
||||||
|
|
||||||
|
except Exception:
|
||||||
|
let ex = getCurrentException()
|
||||||
|
threadLocalRef(logSvc).getLogger("pbm/short_url")
|
||||||
|
.fatal(msg = ex.msg, err = ex)
|
||||||
Reference in New Issue
Block a user