From 0bc680c7ae90e0267bb79fc4a518ecd01ed1ef95 Mon Sep 17 00:00:00 2001 From: Filip Gralinski Date: Sat, 27 Feb 2021 18:38:38 +0100 Subject: [PATCH] Add experimental handling of outputs via Web Sockets --- Foundation.hs | 4 ++ Handler/Shared.hs | 84 ++++++++++++++++++++++++++++++++++++++++ Handler/ShowChallenge.hs | 13 +++++++ config/routes | 2 + gonito.cabal | 2 + 5 files changed, 105 insertions(+) diff --git a/Foundation.hs b/Foundation.hs index 93b4a0e..34ec1db 100644 --- a/Foundation.hs +++ b/Foundation.hs @@ -225,8 +225,12 @@ instance Yesod App where isAuthorized (CompareFormR _ _) _ = regularAuthorization + isAuthorized (TestProgressR _) _ = isTrustedAuthorized + isAuthorized SwaggerR _ = return Authorized + isAuthorized (ViewProgressWithWebSocketsR _) _ = isTrustedAuthorized + -- Default to Authorized for now. isAuthorized _ _ = isTrustedAuthorized diff --git a/Handler/Shared.hs b/Handler/Shared.hs index 03fe643..41f2e92 100644 --- a/Handler/Shared.hs +++ b/Handler/Shared.hs @@ -7,6 +7,8 @@ import Import import qualified Data.IntMap as IntMap +import Yesod.WebSockets + import Handler.Runner import System.Exit @@ -99,6 +101,88 @@ runViewProgress = runViewProgress' ViewProgressR runOpenViewProgress :: (Channel -> Handler ()) -> Handler TypedContent runOpenViewProgress = runViewProgress' OpenViewProgressR +runViewProgressWithWebSockets :: (Channel -> Handler ()) -> Handler TypedContent +runViewProgressWithWebSockets = runViewProgress' ViewProgressWithWebSocketsR + +consoleApp :: Int -> WebSocketsT Handler () +consoleApp jobId = do + App {..} <- getYesod + mchan <- liftIO $ atom $ do + m <- readTVar jobs + case IntMap.lookup jobId m of + Nothing -> return Nothing + Just chan -> fmap Just $ dupTChan chan + case mchan of + Nothing -> do + sendTextData ("CANNOT FIND THE OUTPUT (ALREADY SHOWN??)" :: Text) + sendCloseE ("" :: Text) + return () + Just chan -> do + let loop = do + mtext <- liftIO $ atom $ readTChan chan + case mtext of + Nothing -> sendCloseE ("" :: Text) + Just text -> do + sendTextData text + loop + loop + return () + + +getViewProgressWithWebSocketsR :: Int -> Handler Html +getViewProgressWithWebSocketsR jobId = do + webSockets $ consoleApp jobId + defaultLayout $ do + [whamlet| +
+
+
+ ... PLEASE WAIT ... + |] + toWidget [lucius| + #outwindow { + border: 1px solid black; + margin-bottom: 1em; + color: white; + background-color: black; + padding: 10pt; + } + #wait { + animation: blink 1s linear infinite; + } + @keyframes blink { + 0% { + opacity: 0; + } + 50% { + opacity: .5; + } + 100% { + opacity: 1; + } + } + |] + toWidget [julius| + var url = document.URL, + output = document.getElementById("output"), + wait = document.getElementById("wait"), + conn; + + url = url.replace("http:", "ws:").replace("https:", "wss:"); + conn = new WebSocket(url); + + conn.onmessage = function(e) { + var p = document.createElement("p"); + p.appendChild(document.createTextNode(e.data)); + output.appendChild(p); + }; + + conn.onclose = function(e) { + wait.parentNode.removeChild(wait); + }; + |] + + runViewProgressAsynchronously :: (Channel -> Handler ()) -> Handler Value runViewProgressAsynchronously action = runViewProgressGeneralized getJobIdAsJson action -- where getJobIdAsJson jobId = return $ Number (scientific (toInteger jobId) 0) diff --git a/Handler/ShowChallenge.hs b/Handler/ShowChallenge.hs index 951a9ca..ac6d001 100644 --- a/Handler/ShowChallenge.hs +++ b/Handler/ShowChallenge.hs @@ -15,6 +15,8 @@ import qualified Data.HashMap.Strict as HMS import qualified Yesod.Table as Table +import Control.Concurrent.Lifted (threadDelay) + import Handler.Extract import Handler.Shared import Handler.Runner @@ -1330,3 +1332,14 @@ challengeLayout withHeader challenge widget = do defaultLayout $ do setTitle "Challenge" $(widgetFile "challenge") + +getTestProgressR :: Int -> Handler TypedContent +getTestProgressR m = runViewProgressWithWebSockets $ doTestProgress m + +doTestProgress :: Int -> Channel -> Handler () +doTestProgress m chan = do + forM [1..m] $ (\i -> do + msg chan $ (Data.Text.pack $ show i) + liftIO $ threadDelay 1000000 + return ()) + return () diff --git a/config/routes b/config/routes index a2b9e86..85d0100 100644 --- a/config/routes +++ b/config/routes @@ -9,6 +9,8 @@ /create-challenge CreateChallengeR GET POST /view-progress/#Int ViewProgressR GET /open-view-progress/#Int OpenViewProgressR GET +/view-progress-with-web-sockets/#Int ViewProgressWithWebSocketsR GET +/test-progress/#Int TestProgressR GET /list-challenges ListChallengesR GET /api/list-challenges ListChallengesJsonR GET diff --git a/gonito.cabal b/gonito.cabal index 59edae1..60dec32 100644 --- a/gonito.cabal +++ b/gonito.cabal @@ -160,6 +160,7 @@ library , swagger2 , lens , insert-ordered-containers + , yesod-websockets executable gonito if flag(library-only) @@ -223,3 +224,4 @@ test-suite test , wai , containers , unordered-containers + , yesod-websockets \ No newline at end of file