143 lines
3.3 KiB
Nim
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()
|