blob: 86a828073ab897bf3a9f56a5ec81c559d8253fe2 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
|
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE NoImplicitPrelude #-}
-- | Web server for Ava trace viewer.
--
-- Serves the trace viewer UI for debugging tool executions.
--
-- : out omni-ava-web
-- : dep warp
-- : dep wai
-- : dep http-types
-- : dep aeson
-- : dep text
-- : dep bytestring
-- : dep sqlite-simple
module Omni.Ava.Web
( startWebServer,
app,
defaultPort,
)
where
import Alpha
import qualified Data.Aeson as Aeson
import qualified Data.ByteString.Lazy as LBS
import qualified Data.Text as Text
import qualified Data.Text.Encoding as Encoding
import qualified Data.Text.IO as TextIO
import qualified Database.SQLite.Simple as SQL
import qualified Network.HTTP.Types as HTTP
import qualified Network.Wai as Wai
import qualified Network.Wai.Handler.Warp as Warp
import qualified Omni.Ava.Trace as Trace
import qualified System.Environment as Environment
defaultPort :: Int
defaultPort = 8079
startWebServer :: Int -> FilePath -> IO ()
startWebServer port dbPath = do
putText <| "Starting Ava web server on port " <> tshow port
Warp.run port (app dbPath)
app :: FilePath -> Wai.Application
app dbPath request respond = do
case Wai.pathInfo request of
["trace", tid] -> serveTracePage dbPath tid request respond
["api", "trace", tid] -> serveTraceJson dbPath tid request respond
["health"] -> respond <| Wai.responseLBS HTTP.status200 [(HTTP.hContentType, "text/plain")] "ok"
_ -> respond <| Wai.responseLBS HTTP.status404 [(HTTP.hContentType, "text/plain")] "Not found"
serveTracePage :: FilePath -> Text -> Wai.Request -> (Wai.Response -> IO Wai.ResponseReceived) -> IO Wai.ResponseReceived
serveTracePage dbPath tid _req respond = do
SQL.withConnection dbPath <| \conn -> do
maybeRec <- Trace.getTrace conn tid
case maybeRec of
Nothing -> respond <| Wai.responseLBS HTTP.status404 [(HTTP.hContentType, "text/plain")] "Trace not found"
Just rec -> do
html <- renderTraceHtml rec
respond <| Wai.responseLBS HTTP.status200 [(HTTP.hContentType, "text/html; charset=utf-8")] (LBS.fromStrict <| Encoding.encodeUtf8 html)
serveTraceJson :: FilePath -> Text -> Wai.Request -> (Wai.Response -> IO Wai.ResponseReceived) -> IO Wai.ResponseReceived
serveTraceJson dbPath tid _req respond = do
SQL.withConnection dbPath <| \conn -> do
maybeRec <- Trace.getTrace conn tid
case maybeRec of
Nothing -> respond <| Wai.responseLBS HTTP.status404 [(HTTP.hContentType, "application/json")] "{\"error\":\"not found\"}"
Just rec -> do
let json = Aeson.encode rec
respond <| Wai.responseLBS HTTP.status200 [(HTTP.hContentType, "application/json")] json
renderTraceHtml :: Trace.TraceRecord -> IO Text
renderTraceHtml rec = do
coderoot <- Environment.getEnv "CODEROOT"
let templatePath = coderoot <> "/Omni/Ava/Web/trace.html"
template <- TextIO.readFile templatePath
pure
<| Text.replace "{{trace_id}}" (Trace.trcId rec)
<| Text.replace "{{tool_name}}" (Trace.trcToolName rec)
<| Text.replace "{{created_at}}" (Trace.trcCreatedAt rec)
<| Text.replace "{{duration_ms}}" (tshow <| Trace.trcDurationMs rec)
<| Text.replace "{{input_json}}" (Trace.trcInput rec)
<| Text.replace "{{output_json}}" (Trace.trcOutput rec)
<| template
|