packages feed

ema-0.12.0.0: src/Ema/Server/Common.hs

{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TemplateHaskell #-}

module Ema.Server.Common where

import Control.Monad.Logger
import Data.FileEmbed
import Data.Text qualified as T
import Ema.Asset (
  Asset (AssetGenerated),
  Format (Html),
 )
import Ema.Route.Class (IsRoute (RouteModel, routePrism))
import Ema.Route.Prism (
  checkRoutePrismGivenFilePath,
  fromPrism_,
 )
import Ema.Route.Url (urlToFilePath)
import Ema.Site (EmaSite (siteOutput), EmaStaticSite)
import UnliftIO (MonadUnliftIO)
import UnliftIO.Exception (catch)

renderCatchingErrors ::
  forall r m.
  ( MonadLoggerIO m
  , MonadUnliftIO m
  , EmaStaticSite r
  ) =>
  RouteModel r ->
  r ->
  m (Asset LByteString)
renderCatchingErrors m r =
  catch (siteOutput (fromPrism_ $ routePrism m) m r) $ \(err :: SomeException) -> do
    -- Log the error first.
    logErrorNS "App" $ show @Text err
    pure
      $ AssetGenerated Html
        . mkHtmlErrorMsg
      $ show @Text err

-- Decode an URL path into a route
--
-- This function is used only in live server. If the route is not
-- isomoprhic, this returns a Left, with the mismatched encoding.
decodeUrlRoute ::
  forall r.
  (Eq r, Show r, IsRoute r) =>
  RouteModel r ->
  Text ->
  Either (BadRouteEncoding r) (Maybe r)
decodeUrlRoute m (urlToFilePath -> s) = do
  case checkRoutePrismGivenFilePath routePrism m s of
    Left (r, log) -> Left $ BadRouteEncoding s r log
    Right mr -> Right mr

-- | A basic error response for displaying in the browser
emaErrorHtmlResponse :: Text -> LByteString
emaErrorHtmlResponse err =
  mkHtmlErrorMsg err <> toLazy wsClientHtml

mkHtmlErrorMsg :: Text -> LByteString
mkHtmlErrorMsg s =
  encodeUtf8 . T.replace "MESSAGE" s . decodeUtf8 $ emaErrorHtml

decodeRouteNothingMsg :: Text
decodeRouteNothingMsg = "Ema: 404 (route decoding returned Nothing)"

data BadRouteEncoding r = BadRouteEncoding
  { _bre_urlFilePath :: FilePath
  , _bre_decodedRoute :: r
  , _bre_checkLog :: [(FilePath, Text)]
  }
  deriving stock (Show)

badRouteEncodingMsg :: (Show r) => BadRouteEncoding r -> Text
badRouteEncodingMsg BadRouteEncoding {..} =
  toText $
    "A route Prism' is unlawful.\n\nThe URL '"
      <> toText _bre_urlFilePath
      <> "' decodes to route '"
      <> show _bre_decodedRoute
      <> "', but it is not isomporphic on any of the allowed candidates: \n\n"
      <> T.intercalate
        "\n\n"
        ( _bre_checkLog <&> \(candidate, log) ->
            "## Candidate '" <> toText candidate <> "':\n" <> log
        )
      <> " \n\nYou should make the relevant routePrism lawful to fix this issue."

emaErrorHtml :: ByteString
emaErrorHtml = $(embedFile "www/ema-error.html")

wsClientHtml :: ByteString
wsClientHtml = $(embedFile "www/ema-indicator.html")

wsClientJSShim :: Text
wsClientJSShim = decodeUtf8 $(embedFile "www/ema-shim.js")