app :: FilePath -> IO Application
app jobFile = do
  cmdQ <- atomically $ (SIO.queueMap :: STM (SIO.ActionQueueMap Command))
  ownerMap <- atomically $ emptyOwnerMap

  scottyApp $ do
    middleware simpleCors

    get "/" $ json =<< liftIO jobs

    -- this call should be available only for admin users
    post "/concurrent/:name" $ run sourceRunner source

    get "/queue/:name" $ run ((\comm -> atomically $ readTVar cmdQ >>= dumpTQueue . fromJust . M.lookup comm) <=< command) json

    post "/enqueue/:name" $ dispatch cmdQ ownerMap chanSource
    post "/qplain/:name" $ dispatch cmdQ ownerMap chanText
    get "/attach/:name" $ setHeader "Cache-Control" "no-cache" >> run attachRunner chanSource

  where
    jobs :: IO JobsSpec
    jobs = readJobs jobFile

    command :: JobName -> IO Command
    command name = jobs >>= return . fromJust . M.lookup name

    sourceRunner :: JobName -> IO ChunkSource
    sourceRunner = runCommandS <=< command

    attachRunner :: JobName -> IO ChunkChan
    attachRunner = fmap fromJust . atomically . SIO.attach <=< command

    dispatch :: SIO.SerializableIO Command result => SIO.ActionQueueMap Command -> OwnerMap -> (TMChan result -> ActionM ()) -> ActionM ()
    dispatch cmdQ = runAccounted $ command >=> SIO.dispatchAction cmdQ
app :: FilePath -> IO Application
app jobFile = do
  cmdQ <- atomically $ (SIO.queueMap :: STM (SIO.ActionQueueMap Command))
  ownerMap <- atomically $ emptyOwnerMap

  scottyApp $ do
    middleware simpleCors

    get "/" $ json =<< liftIO jobs

    -- this call should be available only for admin users
    post "/concurrent/:name" $ run sourceRunner source

    get "/queue/:name" $ run ((\comm -> atomically $ readTVar cmdQ >>= dumpTQueue . fromJust . M.lookup comm) <=< command) json

    post "/enqueue/:name" $ dispatch cmdQ ownerMap chanSource
    post "/qplain/:name" $ dispatch cmdQ ownerMap chanText
    get "/attach/:name" $ setHeader "Cache-Control" "no-cache" >> run attachRunner chanSource

  where
    jobs :: IO JobsSpec
    jobs = readJobs jobFile

    command :: JobName -> IO Command
    command name = jobs >>= return . fromJust . M.lookup name

    sourceRunner :: JobName -> IO ChunkSource
    sourceRunner = runCommandS <=< command

    attachRunner :: JobName -> IO ChunkChan
    attachRunner = fmap fromJust . atomically . SIO.attach <=< command

    dispatch :: SIO.SerializableIO Command result => SIO.ActionQueueMap Command -> OwnerMap -> (TMChan result -> ActionM ()) -> ActionM ()
    dispatch cmdQ = runAccounted $ command >=> SIO.dispatchAction cmdQ
app jobFile = do
  cmdQ <- atomically $ (SIO.queueMap :: STM (SIO.ActionQueueMap Command))
  ownerMap <- atomically $ emptyOwnerMap

  scottyApp $ do
    middleware simpleCors

    get "/" $ json =<< liftIO jobs

    -- this call should be available only for admin users
    post "/concurrent/:name" $ run sourceRunner source

    get "/queue/:name" $ run ((\comm -> atomically $ readTVar cmdQ >>= dumpTQueue . fromJust . M.lookup comm) <=< command) json

    post "/enqueue/:name" $ dispatch cmdQ ownerMap chanSource
    post "/qplain/:name" $ dispatch cmdQ ownerMap chanText
    get "/attach/:name" $ setHeader "Cache-Control" "no-cache" >> run attachRunner chanSource

  where
    jobs :: IO JobsSpec
    jobs = readJobs jobFile

    command :: JobName -> IO Command
    command name = jobs >>= return . fromJust . M.lookup name

    sourceRunner :: JobName -> IO ChunkSource
    sourceRunner = runCommandS <=< command

    attachRunner :: JobName -> IO ChunkChan
    attachRunner = fmap fromJust . atomically . SIO.attach <=< command

    dispatch :: SIO.SerializableIO Command result => SIO.ActionQueueMap Command -> OwnerMap -> (TMChan result -> ActionM ()) -> ActionM ()
    dispatch cmdQ = runAccounted $ command >=> SIO.dispatchAction cmdQ