5import Control.Parallel (parTraverse)
6import Data.Array ((..))
7import Data.Either (Either(..))
8import Data.Int (fromString)
9import Data.Maybe (Maybe, fromMaybe)
10import Data.Newtype (un)
11import Data.Time.Duration (Seconds(..), fromDuration)
12import Data.Traversable (for)
14import Effect.Aff (Aff, launchAff_)
15import Effect.Aff.Class (liftAff)
16import Effect.Class (class MonadEffect, liftEffect)
17import Effect.Class.Console (info, log)
18import Foreign (unsafeToForeign)
19import JobQueue (EnqueueResult(..), NewJob(..), Queue, ResourcePool(..))
21import Middleware.JsonBodyParser (jsonBodyParser)
22import Node.Buffer (Buffer)
23import Node.Buffer as Buffer
24import Node.ChildProcess (ExecResult)
25import Node.Encoding (Encoding(..))
26import Node.Express.App (App, listenHttp, listenHttps, use, useExternal)
27import Node.Express.App as E
28import Node.Express.Handler (HandlerM(..), Handler)
29import Node.Express.Middleware.Static (static)
30import Node.Express.Request (getBody')
31import Node.Express.Response (setStatus)
32import Node.Express.Response as Response
33import Node.Express.Types (Request, Response)
34import Node.FS.Aff (readTextFile)
35import Node.HTTP (Server)
36import Node.OS (numCpus)
37import Node.Process (lookupEnv)
38import Playground.Playground (Folder(..), copy)
39import PscIdeClient (PscIdeConnection, compileCode, getFolder, mkConnection, execCommand)
40import Shared.Json (readAff)
41import Shared.Models.Body (CompileRequest, RunResult, CompileResult)
42import Shared.Models.Body as Body
43import Simple.JSON (readJSON, read_, write)
45toBody ∷ ∀ r m. MonadEffect m => { stdout ∷ Buffer, stderr ∷ Buffer | r } -> m RunResult
49 stdout <- Buffer.toString UTF8 result.stdout
50 stderr <- Buffer.toString UTF8 result.stderr
52 (code ∷ Maybe Int) = asErrorWithCode result >>= _.code
53 in { code, stdout, stderr } ∷ RunResult
56 = { code ∷ Maybe Int }
58asErrorWithCode ∷ ∀ a. a -> Maybe ErrorWithCode
59asErrorWithCode = read_ <<< unsafeToForeign
61runCode ∷ Folder -> Aff ExecResult
62runCode folder = execCommand folder "node run.js"
64compileAndRunJob ∷ CompileRequest -> (Handler -> Aff Unit) -> NewJob PscIdeConnection
65compileAndRunJob json handle =
66 NewJob \jobId conn -> do
67 responseString <- compileCode json.code conn
68 case ((readJSON responseString) :: _ _ CompileResult) of
69 Right res | res.resultType == "error" -> do
70 handle $ setStatus 422
71 handle $ Response.send $ write res
73 runResult <- runCode (getFolder conn)
74 resultBody <- toBody runResult
75 handle $ Response.send $ write (resultBody ∷ Body.RunResult)
77 handle $ setStatus 500
78 log $ "Could not decode: " <> responseString <> "\nErrors: " <> show errs
79 handle $ Response.send $ write {}
81compileAndRunHandler ∷ Queue PscIdeConnection -> Handler
82compileAndRunHandler queue = do
84 json <- readAff body # liftAff
85 HandlerM \req res next -> do
87 handle = unHandler req res next
89 runJob = compileAndRunJob json handle
90 queueRes <- Q.enqueue runJob queue # liftEffect
92 Enqueued _ -> pure unit
96 Response.send $ write { error: "Queue full" }
98unHandler ∷ ∀ a. Request -> Response -> Effect Unit -> HandlerM a -> Aff a
99unHandler req res next (HandlerM h) = h req res next
102srcFolder = "../playground"
105destFolder = "../playgrounds/"
110 cpus <- numCpus # liftEffect
112 poolSize = max 2 (cpus / 2) -- use at least 2
114 mkFolder = Folder <<< (destFolder <> _) <<< show
116 connections <- (1 .. poolSize) # parTraverse \n -> do
117 let folder = mkFolder n
119 log $ "Copying to folder " <> (un Folder folder)
120 copy srcFolder (un Folder folder)
121 mkConnection folder port
123 let pool = ResourcePool connections
127 , timeout: 10.0 # Seconds # fromDuration
130 serverSetup (makeApp q)
132makeApp ∷ Queue PscIdeConnection -> App
134 use $ static "assets"
135 useExternal jsonBodyParser
136 E.post "/compileAndRun" (compileAndRunHandler q)
138data CertificatesToUse
139 = UseLocalCertificates
140 | DoNotUseLocalCertificates
142parseCertificatesToUse ∷ String -> CertificatesToUse
143parseCertificatesToUse = case _ of
144 "LOCAL" -> UseLocalCertificates
145 _ -> DoNotUseLocalCertificates
147serverSetup ∷ App -> Aff Server
149 maybePortString <- lookupEnv "PORT" # liftEffect
151 port = maybePortString >>= fromString # fromMaybe 14188
152 useLocalCertificates <- fromMaybe DoNotUseLocalCertificates <<< map parseCertificatesToUse <$> lookupEnv "CERTS" # liftEffect
153 listen <- case useLocalCertificates of
154 UseLocalCertificates -> do
155 httpsOptions <- makeHttpsOptions
156 pure $ listenHttps app (port ∷ Int) httpsOptions
157 DoNotUseLocalCertificates -> pure $ listenHttp app port
158 liftEffect $ listen \_ -> info $ "psfp server started on port " <> show port
160 makeHttpsOptions = do
161 key <- readTextFile UTF8 "server.key"
162 cert <- readTextFile UTF8 "server.cert"
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list