@@ -1,9 +1,10 @@
1module Main where
1module Main where
3import Prelude
3import Prelude
4−
4+import Auth.Handler (authHandler, readAllowedTokens)
5+import Auth.Types (Token(..))
5import Control.Parallel (parOneOf, parTraverse)
6import Control.Parallel (parOneOf, parTraverse)
6−import Data.Array ((..))
7+import Data.Array (elem, (..))
7import Data.Bifunctor (lmap)
8import Data.Bifunctor (lmap)
8import Data.Either (Either(..))
9import Data.Either (Either(..))
9import Data.Int (fromString)
10import Data.Int (fromString)
@@ -25,10 +26,10 @@ import Node.ChildProcess (ExecResult)
25import Node.Encoding (Encoding(..))
26import Node.Encoding (Encoding(..))
26import Node.Express.App (App, listenHttp, listenHttps, use, useExternal)
27import Node.Express.App (App, listenHttp, listenHttps, use, useExternal)
27import Node.Express.App as E
28import Node.Express.App as E
28−import Node.Express.Handler (HandlerM(..), Handler)
29+import Node.Express.Handler (Handler, HandlerM(..), next)
29import Node.Express.Middleware.Static (static)
30import Node.Express.Middleware.Static (static)
30−import Node.Express.Request (getBody')
31+import Node.Express.Request (getBody', getRequestHeader)
31−import Node.Express.Response (setStatus)
32+import Node.Express.Response (end, setStatus)
32import Node.Express.Response as Response
33import Node.Express.Response as Response
33import Node.Express.Types (Request, Response)
34import Node.Express.Types (Request, Response)
34import Node.FS.Aff (readTextFile)
35import Node.FS.Aff (readTextFile)
@@ -48,40 +49,41 @@ toBody result =
49 stdout <- Buffer.toString UTF8 result.stdout
50 stdout <- Buffer.toString UTF8 result.stdout
50 stderr <- Buffer.toString UTF8 result.stderr
51 stderr <- Buffer.toString UTF8 result.stderr
52− (code ∷ Maybe Int) = asErrorWithCode result >>= _.code
52+ let (code ∷ Maybe Int) = asErrorWithCode result >>= _.code
53 in { code, stdout, stderr } ∷ RunResult
53 in { code, stdout, stderr } ∷ RunResult
55−type ErrorWithCode
55+type ErrorWithCode =
56− = { code ∷ Maybe Int }
56+ { code ∷ Maybe Int }
58asErrorWithCode ∷ ∀ a. a -> Maybe ErrorWithCode
58asErrorWithCode ∷ ∀ a. a -> Maybe ErrorWithCode
59asErrorWithCode = read_ <<< unsafeToForeign
59asErrorWithCode = read_ <<< unsafeToForeign
61runCode ∷ ∀ d. Duration d => d -> Folder -> Aff (Maybe ExecResult)
61runCode ∷ ∀ d. Duration d => d -> Folder -> Aff (Maybe ExecResult)
62−runCode timeout folder = parOneOf
62+runCode timeout folder =
63− [ Just <$> execCommand folder "exec node run.js"
64+ [ Just <$> execCommand folder "exec node run.js"
64− , Nothing <$ delay (timeout # fromDuration)
65+ , Nothing <$ delay (timeout # fromDuration)
67compileAndRunJob ∷ CompileRequest -> (Handler -> Aff Unit) -> NewJob PscIdeConnection
68compileAndRunJob ∷ CompileRequest -> (Handler -> Aff Unit) -> NewJob PscIdeConnection
68compileAndRunJob json handle =
69compileAndRunJob json handle =
69 NewJob \jobId conn -> do
70 NewJob \jobId conn -> do
70 stringOrErr <- attempt $ compileCode json.code conn
71 stringOrErr <- attempt $ compileCode json.code conn
71− case ((readJSON <$> stringOrErr) ∷ _ _ (_ _ CompileResult)) of
72+ case ((readJSON <$> stringOrErr) ∷ _ _ (_ _ CompileResult)) of
72 Left e -> do
73 Left e -> do
73 handle $ setStatus 500
74 handle $ setStatus 500
74 log $ "Aff failed with " <> message e
75 log $ "Aff failed with " <> message e
75 handle $ Response.send $ write {}
76 handle $ Response.send $ write {}
76− Right (Right res) | res.resultType == "error" -> do
78+ | res.resultType == "error" -> do
77− handle $ setStatus 422
79+ handle $ setStatus 422
78− handle $ Response.send $ write res
80+ handle $ Response.send $ write res
79 Right (Right res) -> do
81 Right (Right res) -> do
80− runResult <- runCode timeout (getFolder conn)
82+ runResult <- runCode timeout (getFolder conn)
81 case runResult of
83 case runResult of
82 Nothing -> do
84 Nothing -> do
83 handle $ setStatus 408
85 handle $ setStatus 408
84− handle $ Response.send $ write { error: "Timed out after running for " <> show timeout}
86+ handle $ Response.send $ write { error: "Timed out after running for " <> show timeout }
85 Just rr -> do
87 Just rr -> do
86 resultBody <- toBody rr
88 resultBody <- toBody rr
87 handle $ Response.send $ write (resultBody ∷ Body.RunResult)
89 handle $ Response.send $ write (resultBody ∷ Body.RunResult)
@@ -89,8 +91,8 @@ compileAndRunJob json handle =
89 handle $ setStatus 500
91 handle $ setStatus 500
90 log $ "Could not decode: " <> show (lmap (const "no way") stringOrErr) <> "\nErrors: " <> show errs
92 log $ "Could not decode: " <> show (lmap (const "no way") stringOrErr) <> "\nErrors: " <> show errs
91 handle $ Response.send $ write {}
93 handle $ Response.send $ write {}
93− timeout = 1.0 # Seconds
95+ timeout = 1.0 # Seconds
95compileAndRunHandler ∷ Queue PscIdeConnection -> Handler
97compileAndRunHandler ∷ Queue PscIdeConnection -> Handler
96compileAndRunHandler queue = do
98compileAndRunHandler queue = do
@@ -99,7 +101,6 @@ compileAndRunHandler queue = do
99 HandlerM \req res next -> do
101 HandlerM \req res next -> do
101 handle = unHandler req res next
103 handle = unHandler req res next
103 runJob = compileAndRunJob json handle
104 runJob = compileAndRunJob json handle
104 queueRes <- Q.enqueue runJob queue # liftEffect
105 queueRes <- Q.enqueue runJob queue # liftEffect
105 case queueRes of
106 case queueRes of
@@ -121,19 +122,20 @@ destFolder = "../playgrounds/"
121main ∷ Effect Unit
122main ∷ Effect Unit
123 launchAff_ do
124 launchAff_ do
125+ tokens <- readAllowedTokens
124 cpus <- numCpus # liftEffect
126 cpus <- numCpus # liftEffect
126 poolSize = max 2 (cpus / 2) -- use at least 2
128 poolSize = max 2 (cpus / 2) -- use at least 2
128 mkFolder = Folder <<< (destFolder <> _) <<< show
129 mkFolder = Folder <<< (destFolder <> _) <<< show
130− connections <- (1 .. poolSize) # parTraverse \n -> do
132+ # parTraverse \n -> do
131− let folder = mkFolder n
134+ folder = mkFolder n
132− port = 14100 + n
135+ port = 14100 + n
133− log $ "Copying to folder " <> (un Folder folder)
136+ log $ "Copying to folder " <> (un Folder folder)
134− copy srcFolder (un Folder folder)
137+ copy srcFolder (un Folder folder)
135− mkConnection folder port
138+ mkConnection folder port
137 let pool = ResourcePool connections
139 let pool = ResourcePool connections
139 Q.mkQueue
141 Q.mkQueue
@@ -141,38 +143,22 @@ main = do
141 , timeout: 10.0 # Seconds # fromDuration
143 , timeout: 10.0 # Seconds # fromDuration
144− serverSetup (makeApp q)
146+ serverSetup (makeApp tokens q)
146−makeApp ∷ Queue PscIdeConnection -> App
148+makeApp ∷ Array Token -> Queue PscIdeConnection -> App
147−makeApp q = do
149+makeApp tokens q = do
148 use $ static "assets"
150 use $ static "assets"
149 useExternal jsonBodyParser
151 useExternal jsonBodyParser
152+ use (authHandler tokens)
150 E.post "/compileAndRun" (compileAndRunHandler q)
153 E.post "/compileAndRun" (compileAndRunHandler q)
152−data CertificatesToUse
153− = UseLocalCertificates
154− | DoNotUseLocalCertificates
156−parseCertificatesToUse ∷ String -> CertificatesToUse
157−parseCertificatesToUse = case _ of
158− "LOCAL" -> UseLocalCertificates
159− _ -> DoNotUseLocalCertificates
161serverSetup ∷ App -> Aff Server
155serverSetup ∷ App -> Aff Server
162serverSetup app = do
156serverSetup app = do
163 maybePortString <- lookupEnv "PORT" # liftEffect
157 maybePortString <- lookupEnv "PORT" # liftEffect
165− port = maybePortString >>= fromString # fromMaybe 14188
158+ let port = maybePortString >>= fromString # fromMaybe 14188
166− useLocalCertificates <- fromMaybe DoNotUseLocalCertificates <<< map parseCertificatesToUse <$> lookupEnv "CERTS" # liftEffect
167− listen <- case useLocalCertificates of
168− UseLocalCertificates -> do
169− httpsOptions <- makeHttpsOptions
170− pure $ listenHttps app (port ∷ Int) httpsOptions
171− DoNotUseLocalCertificates -> pure $ listenHttp app port
172− liftEffect $ listen \_ -> info $ "psfp server started on port " <> show port
159+ liftEffect $ (listenHttp app port) \_ -> info $ "psfp server started on port " <> show port
174− makeHttpsOptions = do
161+ makeHttpsOptions = do
175− key <- readTextFile UTF8 "server.key"
162+ key <- readTextFile UTF8 "server.key"
176− cert <- readTextFile UTF8 "server.cert"
163+ cert <- readTextFile UTF8 "server.cert"
177− pure { key, cert }
164+ pure { key, cert }