Files
org-parser/src/server/server.nim
2022-05-30 20:57:45 +02:00

143 lines
3.3 KiB
Nim

import system/[
io,
]
import std/[
asyncdispatch,
asynchttpserver,
mimetypes,
os,
strformat,
strutils,
sugar,
]
import fp/[
map,
option,
maybe,
resultM,
]
import fusion/matching
import ./env
import ./errors
import ./utils
# (GET, (scheme: "", username: "", password: "", hostname: "", port: "", path: "/", query: "", anchor: "", opaque: false, isIpv6: false), {"accept": @["*/*"], "host": @["localhost:45201"], "user-agent": @["curl/7.82.0"]})
{.experimental: "caseStmtMacros".}
type
NimHttpResponse* = tuple[
code: HttpCode,
content: string,
headers: HttpHeaders
]
proc sendNotFound(env: Env, path: seq[string]): NimHttpResponse =
(
code: Http404,
content: "Not Found",
headers: newHttpHeaders({
"Content-Type": "text/html"
}),
)
proc sendStaticFile(env: Env, path: seq[string]): NimHttpResponse =
let mimetype = path
.last()
.map((x: string) => x.splitFile.ext)
.notEmpty()
.map((x: string) => env.mimeTypes.getMimetype(x))
.getOrElse("text/plain")
let filePath = env.htmlRoot.joinPath(path.join("/"))
let file = filePath.readFile()
(
code: Http200,
content: file,
headers: newHttpHeaders({
"Content-Type": mimetype
})
)
proc sendOrgFile(env: Env, id: orgId, path: seq[string]): NimHttpResponse =
type errT = tuple[err: orgEnvErr, id: orgId, path: string]
type StringResult = Result[orgPath, errT]
type FileResult = Result[string, errT]
let content = env.orgEnv
.get(id)
.fold(
() => StringResult.err((invalidWorkspaceId, id, "")),
(x: orgPath) => StringResult.ok(x),
)
.map((x: orgPath) => $x.joinPath(path.join("/")))
.flatMap((path: orgPath) => (
catch(readFile(path)).fold(
_ => FileResult.err((fileNotFound, id, path)),
(content: string) => FileResult.ok(content),
)
))
.tapErr((err: errT) => logErr(env, err[0], &"{err[1]}, path: {err[2]}"))
content.fold(
(e: errT) => (
code: Http404,
content: "File not Found",
headers: newHttpHeaders({
"Content-Type": "application/json"
})
),
(content: string) => (
code: Http200,
content: content,
headers: newHttpHeaders({
"Content-Type": "text/plain"
})
)
)
proc sendIndex(env: Env): auto =
sendStaticFile(env, path = @["index.html"])
proc handleRoute(env: Env, req: Request): NimHttpResponse =
# Handle main route
if req.url.path == "/":
return sendIndex(env)
var path = req.url.path.split("/")
path.delete(0)
# Router
return (
case (req.reqMethod, path):
of (HttpGet, ["dist", .._]):
sendStaticFile(env, path)
of (HttpGet, ["api", "org", @id, all @rest]):
sendOrgFile(env, id, rest)
else:
sendIndex(env)
)
proc main {.async.} =
let env = initEnv();
proc cb(req: Request) {.async.} =
echo (req.reqMethod, req.url, req.headers)
let response = handleRoute(env, req)
await req.respond(response.code, response.content , response.headers)
var server = newAsyncHttpServer()
server.listen(Port(env.port))
echo &"Starting server at {env.port}"
while true:
if server.shouldAcceptRequest():
await server.acceptRequest(cb)
else:
# too many concurrent connections, `maxFDs` exceeded wait 500ms for FDs to be closed
await sleepAsync(500)
waitFor main()