Skip to content

Commit

Permalink
solve #180 and #181, update API docs reference
Browse files Browse the repository at this point in the history
  • Loading branch information
Ethosa committed Nov 21, 2023
1 parent 6feac8a commit 8029235
Show file tree
Hide file tree
Showing 7 changed files with 268 additions and 29 deletions.
1 change: 1 addition & 0 deletions src/happyx.nim
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,7 @@
##
## - [routing](happyx/routing/routing.html) provides powerful routing and `pathParams` macro.
## - [mounting](happyx/routing/mounting.html) provides powerful mounting.
## - [decorators](happyx/routing/decorators.html) provides powerful decorators.
##
## ### Syntax Sugar ✨
##
Expand Down
41 changes: 23 additions & 18 deletions src/happyx/core/constants.nim
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
## # Constants ✨
## > Provides HappyX constants
##
## | Flag | Description | Need Value |
## | :---: | :---: | :--: |
## | `-d:httpx` | enables Httpx as alternative HTTP Server ⚡ | ❌ |
## | `-d:beast` | enables HttpBeast as alternative HTTP Server ⚡ | ❌ |
## | `-d:micro` | enables MicroAsyncHttpServer as alternative HTTP Server ⚡ | ❌ |
## | `-d:translate` | enables automatic translate for returns 🌐 | ❌ |
## | `-d:debug` | enables debug logging 💻 | ❌ |
## | `-d:oldRenderer` | enables old renderer for SPA 🍍 | ❌ |
## | `-d:enableUi` | enables built-in UI components 🎴 | ❌ |
## | `-d:cryptoMethod` | choose crypto method for `generate_password` methods 🔐 | ✅ |
## | `-d:numThreads` | choose number of threads (httpx/httpbeast) ⌛ | ✅ |
## | `-d:sessionIdLength` | choose length of session ID ✍ | ✅ |
## | `-d:disableApiDoc` | disables built-in API documentation 📕 | ❌ |
## | `-d:disableORM` | disables built-in ORM 📑 | ❌ |
## | `-d:appName` | choose name of application (SSR/SSG) 📕 | ✅ |
## | `-d:apiDocsPath` | choose path for API documentation 📕 | ✅ |
## | Flag | Description | Need Value |
## | :---: | :---: | :--: |
## | `-d:httpx` | enables Httpx as alternative HTTP Server ⚡ | ❌ |
## | `-d:beast` | enables HttpBeast as alternative HTTP Server ⚡ | ❌ |
## | `-d:micro` | enables MicroAsyncHttpServer as alternative HTTP Server ⚡ | ❌ |
## | `-d:translate` | enables automatic translate for returns 🌐 | ❌ |
## | `-d:debug` | enables debug logging 💻 | ❌ |
## | `-d:oldRenderer` | enables old renderer for SPA 🍍 | ❌ |
## | `-d:enableUi` | enables built-in UI components 🎴 | ❌ |
## | `-d:cryptoMethod` | choose crypto method for `generate_password` methods 🔐 | ✅ |
## | `-d:numThreads` | choose number of threads (httpx/httpbeast) ⌛ | ✅ |
## | `-d:sessionIdLength` | choose length of session ID ✍ | ✅ |
## | `-d:disableApiDoc` | disables built-in API documentation 📕 | ❌ |
## | `-d:disableORM` | disables built-in ORM 📑 | ❌ |
## | `-d:appName` | choose name of application (SSR/SSG) 📕 | ✅ |
## | `-d:apiDocsPath` | choose path for API documentation 📕 | ✅ |
## | `-d:noliveviews` | Disables LiveViews at SSR/SSG (It helpful for components) 📕 | ❌ |
## | `-d:safeRequests` | Enables requests safety (On error returns 500 with err msg) 📕| ❌ |
##
## ## Dev Consts 👨‍💻
##
Expand All @@ -41,6 +43,10 @@ const
enableHttpx* = defined(httpx) or defined(happyxHttpx) or defined(hpxHttpx)
enableMicro* = defined(micro) or defined(happyxMicro) or defined(hpxMicro)
enableHttpBeast* = defined(beast) or defined(happyxBeast) or defined(hpxBeast)
# LiveViews
enableLiveViews* = not (defined(noLiveviews) or defined(hpxNoLiveviews) or defined(happyxNoLiveviews))
# Safe Requests
enableSafeRequests* = defined(safeRequests) or defined(hpxSafeRequests) or defined(happyxSafeRequests)
# Auto translation in routing
enableAutoTranslate* = defined(translate) or defined(happyxTranslate) or defined(hpxTranslate)
# Debug mode
Expand All @@ -51,7 +57,6 @@ const
enableUseCompDebugMacro* = defined(useCompDebug) or defined(happyxUseCompDebug) or defined(hpxUseCompDebug)
enableRequestModelDebugMacro* = defined(reqModelDebug) or defined(happyxReqModelDebug) or defined(hpxReqModelDebug)
enableRoutingDebugMacro* = defined(routingDebug) or defined(happyxRoutingDebug) or defined(hpxRoutingDebug)
enableLiveViews* = not (defined(noLiveviews) or defined(hpxNoLiveviews) or defined(happyxNoLiveviews))
componentDebugTarget* {.strdefine.} = ""
reqModelDebugTarget* {.strdefine.} = ""
# Language bindings
Expand Down Expand Up @@ -91,7 +96,7 @@ const
nim_2_0_0* = (NimMajor, NimMinor, NimPatch) >= (2, 0, 0)
# Framework version
HpxMajor* = 3
HpxMinor* = 3
HpxMinor* = 4
HpxPatch* = 0
HpxVersion* = $HpxMajor & "." & $HpxMinor & "." & $HpxPatch

Expand Down
11 changes: 11 additions & 0 deletions src/happyx/private/macro_utils.nim
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,17 @@ proc bracket*(node: varargs[NimNode]): NimNode =
result.add(i)


proc bracket*(node: seq[NimNode] | seq[string]): NimNode =
when node is seq[NimNode]:
result = newNimNode(nnkBracket)
for i in node:
result.add(i)
else:
result = newNimNode(nnkBracket)
for i in node:
result.add(newLit(i))


proc pragmaBlock*(pragmas: openArray[NimNode], statementList: NimNode): NimNode =
result = newNimNode(nnkPragmaBlock).add(
newNimNode(nnkPragma),
Expand Down
29 changes: 29 additions & 0 deletions src/happyx/routing/decorators.nim
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,35 @@
##
## Provides convenient wrapper to create route decorators
##
## > It can be used for plugins also
##
## ## Decorator Usage Example ✨
##
##
## .. code-block:: nim
## serve ...:
## @AuthBasic
## get "/":
## # password and username takes from header "Authorization"
## # Authorization: Bearer BASE64
## echo username
## echo password
##
##
## ## Own decorators
##
##
## .. code-block:: nim
## proc myDecorator(httpMethods: seq[string], routePath: string, statementList: NimNode) =
## statementList.insert(0, newCall("echo", newLit"My own decorator"))
## # Register decorator
## static:
## regDecorator("MyDecorator", myDecorator)
## # Use it
## serve ...:
## @MyDecorator
## get "/":
##
import
macros,
tables,
Expand Down
176 changes: 165 additions & 11 deletions src/happyx/ssr/server.nim
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,7 @@ macro `.`*(obj: JsonNode, field: untyped): JsonNode =

template answer*(
req: Request,
message: string,
message: string | int | float | bool | char,
code: HttpCode = Http200,
headers: HttpHeaders = newHttpHeaders([
("Content-Type", "text/plain; charset=utf-8")
Expand Down Expand Up @@ -388,23 +388,23 @@ template answer*(
headersArr.add(cookie)
when declared(statusCode):
when statusCode is int:
req.send(statusCode.HttpCode, message, headersArr.join("\r\n"))
req.send(statusCode.HttpCode, $message, headersArr.join("\r\n"))
else:
req.send(code, message, headersArr.join("\r\n"))
req.send(code, $message, headersArr.join("\r\n"))
else:
req.send(code, message, headersArr.join("\r\n"))
req.send(code, $message, headersArr.join("\r\n"))
else:
when declared(outCookies):
for cookie in outCookies:
let data = cookie.split(":", 1)
h.add("Set-Cookie", data[1].strip())
when declared(statusCode):
when statusCode is int:
await req.respond(statusCode.HttpCode, message, h)
await req.respond(statusCode.HttpCode, $message, h)
else:
await req.respond(code, message, h)
await req.respond(code, $message, h)
else:
await req.respond(code, message, h)
await req.respond(code, $message, h)


when enableHttpBeast:
Expand Down Expand Up @@ -638,7 +638,19 @@ macro routes*(server: Server, body: untyped = newStmtList()): untyped =
procStmt = newProc(
ident"handleRequest",
[newEmptyNode(), newIdentDefs(ident"req", ident"Request")],
stmtList
when enableSafeRequests:
newNimNode(nnkTryStmt).add(
stmtList,
newNimNode(nnkExceptBranch).add(
newCall(
ident"answer", ident"req",
newCall("fmt", newLit"Internal Server Error: {getCurrentExceptionMsg()}"),
ident"Http500"
)
)
)
else:
stmtList,
)
caseRequestMethodsStmt = newNimNode(nnkCaseStmt)
methodTable = newTable[string, NimNode]()
Expand Down Expand Up @@ -916,6 +928,121 @@ socketToSsr.onmessage=function(m){
for route in nextRouteDecorators:
decorators[route](@[$statement[0]], $statement[1], statement[2])
if name == "STATICDIR":
# Just path
var
staticPath = ""
directory = ""
extensions: seq[string] = @[]

# staticDir "/directory"
if statement[1].kind in [nnkStrLit, nnkTripleStrLit]:
staticPath = $statement[1]
directory = $statement[1]
ifStmt.insert(
0, newNimNode(nnkElifBranch).add(
newCall(
"and",
newCall(
"or",
newCall("startsWith", pathIdent, statement[1]),
newCall("startsWith", pathIdent, newStrLitNode("/" & $statement[1])),
), newCall(
"fileExists",
directoryFromPath
)
),
newStmtList(
newCall("await", newCall("answerFile", ident"req", directoryFromPath))
)
)
)
# staticDir "/path" -> "directory" ~ "js,html"
elif statement[1].kind == nnkInfix and statement[1][0] == ident"->" and statement[1][2].kind == nnkInfix and statement[1][2][0] == ident"~":
staticPath = $statement[1][1]
directory = $statement[1][2][1]
extensions = ($statement[1][2][2]).split(",")
# staticDir "/directory" ~ "js,html"
elif statement[1].kind == nnkInfix and statement[1][0] == ident"~":
staticPath = $statement[1][1]
directory = $statement[1][1]
extensions = ($statement[1][2]).split(",")
# staticDir "/directory" -> "js,html"
elif statement[1].kind == nnkInfix and statement[1][0] == ident"->":
staticPath = $statement[1][1]
directory = $statement[1][2]

if directory == staticPath:
let answerStatic =
if extensions.len > 0:
newNimNode(nnkIfStmt).add(newNimNode(nnkElifBranch).add(
newCall(
"contains",
bracket(extensions),
newCall("[]", newCall("split", directoryFromPath, newLit"."), newCall("^", newLit(1)))
), newStmtList(
newCall("await", newCall("answerFile", ident"req", directoryFromPath))
)
), newNimNode(nnkElse).add(
newCall(ident"answer", ident"req", newLit"Not found", ident"Http404")
))
else:
newCall("await", newCall("answerFile", ident"req", directoryFromPath))
ifStmt.insert(
0, newNimNode(nnkElifBranch).add(
newCall(
"and",
newCall(
"or",
newCall("startsWith", pathIdent, newLit(staticPath)),
newCall("startsWith", pathIdent, newStrLitNode("/" & $staticPath)),
), newCall(
"fileExists",
directoryFromPath
)
),
answerStatic
)
)
else:
let
route = if staticPath == "/": newStrLitNode("") else: newLit(staticPath)
path = if $staticPath == "/": newStrLitNode(directory & "/") else: newLit(directory)
let dirFromPath = newCall(
"&",
newCall("&", newStrLitNode("."), newLit("/")),
newCall(
"replace",
newCall("replace", pathIdent, newLit(staticPath), path),
newLit('/'), ident"DirSep"
)
)
let answerStatic =
if extensions.len > 0:
newNimNode(nnkIfStmt).add(newNimNode(nnkElifBranch).add(
newCall(
"contains",
bracket(extensions),
newCall("[]", newCall("split", dirFromPath, newLit"."), newCall("^", newLit(1)))
), newStmtList(
newCall("await", newCall("answerFile", ident"req", dirFromPath))
)
), newNimNode(nnkElse).add(
newCall(ident"answer", ident"req", newLit"Not found", ident"Http404")
))
else:
newCall("await", newCall("answerFile", ident"req", dirFromPath))
ifStmt.insert(
0, newNimNode(nnkElifBranch).add(
newCall(
"and",
newCall("startsWith", pathIdent, route),
newCall("fileExists", dirFromPath)
),
answerStatic
)
)
continue

if statement[1].kind in [nnkStrLit, nnkTripleStrLit]:
ifStmt.insert(
0, newNimNode(nnkElifBranch).add(
Expand All @@ -935,7 +1062,34 @@ socketToSsr.onmessage=function(m){
)
)
)
elif statement[1].kind == nnkInfix and statement[1][^1].kind == nnkInfix and statement[1][0] == ident"->" and statement[1][^1][0] == ident"~":
# Path -> directory ~ extensions
let
route = if $statement[1][1] == "/": newStrLitNode("") else: statement[1][1]
path = if $statement[1][1] == "/": newStrLitNode($statement[1][2] & "/") else: statement[1][2]
let dirFromPath = newCall(
"&",
newCall("&", newStrLitNode("."), newLit("/")),
newCall(
"replace",
newCall("replace", pathIdent, statement[1][1], path),
newLit('/'), ident"DirSep"
)
)
ifStmt.insert(
0, newNimNode(nnkElifBranch).add(
newCall(
"and",
newCall("startsWith", pathIdent, route),
newCall("fileExists", dirFromPath)
),
newStmtList(
newCall("await", newCall("answerFile", ident"req", dirFromPath))
)
)
)
else:
# Path -> directory
let
route = if $statement[1][1] == "/": newStrLitNode("") else: statement[1][1]
path = if $statement[1][1] == "/": newStrLitNode($statement[1][2] & "/") else: statement[1][2]
Expand Down Expand Up @@ -1212,12 +1366,12 @@ socketToSsr.onmessage=function(m){
"warn",
newCall(
"fgColored",
newCall("fmt", newStrLitNode("{urlPath} is not found.")), ident"fgYellow"
newCall("fmt", newLit("{urlPath} is not found.")), ident"fgYellow"
)
)
)
elseStmtList.add(
newCall(ident"answer", ident"req", newStrLitNode("Not found"), ident"Http404")
newCall(ident"answer", ident"req", newLit"Not found", ident"Http404")
)
else:
ifStmt.add(newNimNode(nnkElse).add(notFoundNode))
Expand All @@ -1233,7 +1387,7 @@ socketToSsr.onmessage=function(m){
# )
# ))
stmtList.add(
newCall(ident"answer", ident"req", newStrLitNode("Not found"), ident"Http404")
newCall(ident"answer", ident"req", newLit"Not found", ident"Http404")
)
else:
stmtList.add(notFoundNode)
Expand Down
Loading

0 comments on commit 8029235

Please sign in to comment.