Init
This commit is contained in:
119
src/luxtools.nim
Executable file
119
src/luxtools.nim
Executable file
@@ -0,0 +1,119 @@
|
||||
import std/[asynchttpserver, asyncdispatch, os, strformat, strutils, times]
|
||||
|
||||
const
|
||||
indexPage = staticRead("../templates/index.html")
|
||||
|
||||
var clickCount = 0
|
||||
|
||||
proc htmlHeaders(): HttpHeaders =
|
||||
result = newHttpHeaders()
|
||||
result.add("Content-Type", "text/html; charset=utf-8")
|
||||
|
||||
proc assetHeaders(contentType: string): HttpHeaders =
|
||||
result = newHttpHeaders()
|
||||
result.add("Content-Type", contentType)
|
||||
|
||||
proc renderCounter*(count: int): string =
|
||||
&"""
|
||||
<div id="counter" class="tui-panel tui-panel-inline">
|
||||
<p><strong>Clicks:</strong> {count}</p>
|
||||
<button class="tui-button" hx-post="/counter" hx-target="#counter" hx-swap="outerHTML">
|
||||
Increment
|
||||
</button>
|
||||
</div>
|
||||
"""
|
||||
|
||||
proc renderTime(): string =
|
||||
let now = now().format("yyyy-MM-dd HH:mm:ss")
|
||||
&"""
|
||||
<div id="server-time" class="tui-panel tui-panel-inline">
|
||||
<p><strong>Server time:</strong> {now}</p>
|
||||
</div>
|
||||
"""
|
||||
|
||||
proc sendHtml(req: Request, body: string) {.async, gcsafe.} =
|
||||
await req.respond(Http200, body, htmlHeaders())
|
||||
|
||||
proc sendHtml(req: Request, code: HttpCode, body: string) {.async, gcsafe.} =
|
||||
await req.respond(code, body, htmlHeaders())
|
||||
|
||||
proc sendAsset(req: Request, body: string, contentType: string) {.async, gcsafe.} =
|
||||
await req.respond(Http200, body, assetHeaders(contentType))
|
||||
|
||||
proc guessContentType(path: string): string =
|
||||
let ext = splitFile(path).ext.toLowerAscii()
|
||||
case ext
|
||||
of ".js":
|
||||
result = "application/javascript"
|
||||
of ".css":
|
||||
result = "text/css"
|
||||
of ".html":
|
||||
result = "text/html; charset=utf-8"
|
||||
of ".json":
|
||||
result = "application/json"
|
||||
of ".png":
|
||||
result = "image/png"
|
||||
of ".jpg", ".jpeg":
|
||||
result = "image/jpeg"
|
||||
of ".svg":
|
||||
result = "image/svg+xml"
|
||||
of ".gif":
|
||||
result = "image/gif"
|
||||
of ".ico":
|
||||
result = "image/x-icon"
|
||||
else:
|
||||
result = "application/octet-stream"
|
||||
|
||||
proc trySendAsset(req: Request): Future[bool] {.async, gcsafe.} =
|
||||
if req.reqMethod notin {HttpGet, HttpHead}:
|
||||
return false
|
||||
|
||||
let path = req.url.path
|
||||
if path.len <= 1 or path[0] != '/':
|
||||
return false
|
||||
|
||||
let relative = path[1..^1]
|
||||
if relative.len == 0 or relative.contains("..") or relative.contains('\\'):
|
||||
return false
|
||||
|
||||
let assetPath = joinPath(absolutePath(joinPath(getAppDir(), "..", "assets")), relative)
|
||||
if not fileExists(assetPath):
|
||||
return false
|
||||
|
||||
let contentType = guessContentType(assetPath)
|
||||
if req.reqMethod == HttpHead:
|
||||
await req.respond(Http200, "", assetHeaders(contentType))
|
||||
else:
|
||||
await sendAsset(req, readFile(assetPath), contentType)
|
||||
return true
|
||||
|
||||
proc handleRequest(req: Request) {.async, gcsafe.} =
|
||||
case req.url.path
|
||||
of "/":
|
||||
await sendHtml(req, indexPage)
|
||||
of "/counter":
|
||||
if req.reqMethod in {HttpPost, HttpPut}:
|
||||
inc clickCount
|
||||
await sendHtml(req, renderCounter(clickCount))
|
||||
of "/time":
|
||||
await sendHtml(req, renderTime())
|
||||
else:
|
||||
if await trySendAsset(req):
|
||||
return
|
||||
await sendHtml(req, Http404, """
|
||||
<div class="tui-window">
|
||||
<fieldset class="tui-fieldset">
|
||||
<legend>Error</legend>
|
||||
<p>Route """ & req.url.path & """ not found.</p>
|
||||
</fieldset>
|
||||
</div>
|
||||
""")
|
||||
|
||||
proc runServer() {.async.} =
|
||||
let server = newAsyncHttpServer()
|
||||
let port = Port(5000)
|
||||
echo &"Luxtools web server running on http://127.0.0.1:{port.int}"
|
||||
await server.serve(port, handleRequest)
|
||||
|
||||
when isMainModule:
|
||||
waitFor runServer()
|
||||
Reference in New Issue
Block a user