Compare commits

...
Sign in to create a new pull request.

1 commit

Author SHA1 Message Date
Joey Yakimowich-Payne
8b39358dd6 Allow passing of url between jester and frontend 2020-02-22 15:23:51 -07:00
5 changed files with 56 additions and 13 deletions

View file

@ -15,6 +15,7 @@
<!-- Global site tag (gtag.js) - Google Analytics --> <!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=$ga"></script> <script async src="https://www.googletagmanager.com/gtag/js?id=$ga"></script>
<script> <script>
window.SERVER_URL = "$server_url";
window.dataLayer = window.dataLayer || []; window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);} function gtag(){dataLayer.push(arguments);}
gtag('js', new Date()); gtag('js', new Date());

View file

@ -267,7 +267,9 @@ proc initialise() =
{ {
"title": config.title, "title": config.title,
"timestamp": encodeUrl(CompileDate & CompileTime), "timestamp": encodeUrl(CompileDate & CompileTime),
"ga": config.ga "ga": config.ga,
# An ugly hack, maybe there's a better way?
"server_url": "$server_url"
}.newStringTable() }.newStringTable()
@ -1554,4 +1556,4 @@ routes:
get re"/(.*)": get re"/(.*)":
cond request.matches[0].splitFile.ext == "" cond request.matches[0].splitFile.ext == ""
resp karaxHtml resp karaxHtml % {"server_url": request.url.jsEscape}.newStringTable

View file

@ -1,7 +1,8 @@
import options, tables, sugar, httpcore import options, tables, sugar, httpcore, uri
from dom import window, Location, document, decodeURI from dom import window, Location, document, decodeURI
include karax/prelude include karax/prelude
import karax/[kdom]
import jester/[patterns] import jester/[patterns]
import threadlist, postlist, header, profile, newthread, error, about import threadlist, postlist, header, profile, newthread, error, about
@ -12,6 +13,7 @@ type
State = ref object State = ref object
originalTitle: cstring originalTitle: cstring
url: Location url: Location
serverUri: Uri
profile: ProfileState profile: ProfileState
newThread: NewThread newThread: NewThread
about: About about: About
@ -36,6 +38,7 @@ proc newState(): State =
State( State(
originalTitle: document.title, originalTitle: document.title,
url: copyLocation(window.location), url: copyLocation(window.location),
serverUri: SERVER_URI,
profile: newProfileState(), profile: newProfileState(),
newThread: newNewThread(), newThread: newNewThread(),
about: newAbout(), about: newAbout(),
@ -56,6 +59,7 @@ proc onPopState(event: dom.Event) =
state.url = copyLocation(window.location) state.url = copyLocation(window.location)
redraw() redraw()
window.location.reload()
type Params = Table[string, string] type Params = Table[string, string]
type type
@ -66,12 +70,12 @@ type
proc r(n: string, p: proc (params: Params): VNode): Route = Route(n: n, p: p) proc r(n: string, p: proc (params: Params): VNode): Route = Route(n: n, p: p)
proc route(routes: openarray[Route]): VNode = proc route(routes: openarray[Route]): VNode =
let path = let path =
if state.url.pathname.len == 0: "/" else: $state.url.pathname if state.serverUri.path.len == 0: "/" else: state.serverUri.path
let prefix = if appName == "/": "" else: appName let prefix = if appName == "/": "" else: appName
for route in routes: for route in routes:
let pattern = (prefix & route.n).parsePattern() let pattern = (prefix & route.n).parsePattern()
var (matched, params) = pattern.match(path) var (matched, params) = pattern.match(path)
parseUrlQuery($state.url.search, params) parseUrlQuery($state.serverUri.query, params)
if matched: if matched:
return route.p(params) return route.p(params)

View file

@ -1,4 +1,4 @@
import strutils, strformat, parseutils, tables import strutils, strformat, parseutils, tables, uri
proc parseIntSafe*(s: string, value: var int) {.noSideEffect.} = proc parseIntSafe*(s: string, value: var int) {.noSideEffect.} =
## parses `s` into an integer in the range `validRange`. If successful, ## parses `s` into an integer in the range `validRange`. If successful,
@ -31,6 +31,10 @@ when defined(js):
const appName* = "/" const appName* = "/"
# Get the server URL that we set in the backend
var SERVER_URL* {.importc: "SERVER_URL".}: cstring
let SERVER_URI* = parseUri($SERVER_URL)
proc class*(classes: varargs[tuple[name: string, present: bool]], proc class*(classes: varargs[tuple[name: string, present: bool]],
defaultClasses: string = ""): string = defaultClasses: string = ""): string =
result = defaultClasses & " " result = defaultClasses & " "
@ -42,11 +46,13 @@ when defined(js):
## Concatenates ``relative`` to the current URL in a way that is ## Concatenates ``relative`` to the current URL in a way that is
## (possibly) sane. ## (possibly) sane.
var relative = relative var relative = relative
assert appName in $window.location.pathname assert appName in SERVER_URI.path
if relative[0] == '/': relative = relative[1..^1] if relative[0] == '/': relative = relative[1..^1]
return $window.location.protocol & "//" & let port = if SERVER_URI.port.len > 0: ":" & SERVER_URI.port else: ""
$window.location.host &
return SERVER_URI.scheme & "://" &
SERVER_URI.hostname & port &
appName & appName &
relative & relative &
search & search &
@ -62,7 +68,7 @@ when defined(js):
query.add(param[0] & "=" & param[1]) query.add(param[0] & "=" & param[1])
if query.len > 0: if query.len > 0:
var search = if reuseSearch: $window.location.search else: "" var search = if reuseSearch: SERVER_URI.query else: ""
if search.len != 0: search.add("&") if search.len != 0: search.add("&")
search.add(query) search.add(query)
if search[0] != '?': search = "?" & search if search[0] != '?': search = "?" & search
@ -86,6 +92,7 @@ when defined(js):
let url = n.getAttr("href") let url = n.getAttr("href")
navigateTo(url) navigateTo(url)
window.location.href = url
proc newFormData*(form: dom.Element): FormData proc newFormData*(form: dom.Element): FormData
{.importcpp: "new FormData(@)", constructor.} {.importcpp: "new FormData(@)", constructor.}

View file

@ -1,13 +1,21 @@
import asyncdispatch, smtp, strutils, json, os, rst, rstgen, xmltree, strtabs, import asyncdispatch, strutils, json, os, rst, rstgen, xmltree, strtabs,
htmlparser, streams, parseutils, options, logging htmlparser, streams, parseutils, logging, uri, options, nre, strformat
from times import getTime, getGMTime, format from times import getTime, getGMTime, format
import jester
import jester/private/utils as jesterutils
when useHttpBeast:
import httpbeast except Settings, Request
else:
import asynchttpserver
# Used to be: # Used to be:
# {'A'..'Z', 'a'..'z', '0'..'9', '_', '\128'..'\255'} # {'A'..'Z', 'a'..'z', '0'..'9', '_', '\128'..'\255'}
let let
UsernameIdent* = IdentChars # TODO: Double check that everyone follows this. UsernameIdent* = IdentChars # TODO: Double check that everyone follows this.
import frontend/[karaxutils, error] import frontend/[error]
export parseInt export parseInt
type type
@ -181,3 +189,24 @@ proc rstToHtml*(content: string): string =
add(result, node, indWidth=0, addNewLines=false) add(result, node, indWidth=0, addNewLines=false)
except: except:
warn("Could not parse rst html.") warn("Could not parse rst html.")
proc jsEscape*(str: string): string =
## Escapes a string to be stored in a JS variable. This prevents
## XSS attacks. Taken from here: https://portswigger.net/web-security/cross-site-scripting/preventing
str.replace(
re"[^\w. ]",
proc(match: string): string =
let code = match[0].ord
fmt"\u{code:04x}"
)
proc url*(request: Request): string =
## Get the full URL of the request, including the query params
let nativeRequest = request.getNativeReq
when useHttpBeast:
let query = nativeRequest.path.get("").parseUri().query
else:
let query = nativeRequest.url.query
let proto = if request.secure: "https" else: "http"
result = proto & "://" & request.host & request.path & query