packages feed

wikimusic-ssr-0.6.0.1: src/WikiMusic/SSR/Servant/ApiSetup.hs

{-# LANGUAGE OverloadedLabels #-}

module WikiMusic.SSR.Servant.ApiSetup (mkApp) where

import Data.Text qualified as T
import Data.Time
import Network.HTTP.Client (newManager)
import Network.HTTP.Client.TLS (tlsManagerSettings)
import Network.Wai
import Network.Wai.Middleware.Cors
import Optics
import Relude
import Servant
import Servant.Client
import WikiMusic.SSR.Backend.Rest ()
import WikiMusic.SSR.Model.Config
import WikiMusic.SSR.Model.Env
import WikiMusic.SSR.Servant.ApiSpec
import WikiMusic.SSR.Servant.ArtistRoutes
import WikiMusic.SSR.Servant.GenreRoutes
import WikiMusic.SSR.Servant.LoginRoutes
import WikiMusic.SSR.Servant.PreferenceRoutes
import WikiMusic.SSR.Servant.SongRoutes
import WikiMusic.SSR.View.Html ()

newClientEnv :: (MonadIO m) => m ClientEnv
newClientEnv = do
  manager <- liftIO $ newManager tlsManagerSettings
  pure $ clientEnv manager Nothing
  where
    baseUrl' =
      BaseUrl
        { baseUrlScheme = Https,
          baseUrlHost = "api.wikimusic.jointhefreeworld.org",
          baseUrlPort = 443,
          baseUrlPath = ""
        }
    clientEnv manager cookieJar =
      ClientEnv
        { manager = manager,
          baseUrl = baseUrl',
          cookieJar = cookieJar,
          makeClientRequest = defaultMakeClientRequest
        }

mkApp :: AppConfig -> IO Application
mkApp cfg = do
  let apiCfg = EmptyContext
  now <- getZonedTime
  mainCss <- liftIO (readFileBS "resources/css/main.css")
  lightCss <- liftIO (readFileBS "resources/css/light.css")
  darkCss <- liftIO (readFileBS "resources/css/dark.css")
  greenPaletteCss <- liftIO (readFileBS "resources/css/palettes/green.css")
  mauvePaletteCss <- liftIO (readFileBS "resources/css/palettes/mauve.css")
  clientEnv <- newClientEnv
  let env =
        Env
          { cfg = cfg,
            processStartedAt = now,
            reportedVersion = cfg ^. #dev % #reportedVersion,
            mainCss = prepareCSS mainCss,
            darkCss = prepareCSS darkCss,
            lightCss = prepareCSS lightCss,
            clientEnv = clientEnv,
            palettes =
              PalettesCss
                { green = prepareCSS greenPaletteCss,
                  mauve = prepareCSS mauvePaletteCss
                }
          }
  pure
    . myCors (cfg ^. #cors)
    $ serveWithContext wikimusicSSRServant apiCfg (server env)
  where
    prepareCSS = T.filter (\x -> x /= '\n' && x /= '\t') . decodeUtf8

artistBaseEntityRoutes :: Env -> Server BaseEntityRoutes
artistBaseEntityRoutes env =
  artistsRoute env
    :<|> artistRoute env
    :<|> artistCreateRoute env
    :<|> artistCreateFormRoute env
    :<|> artistLikeRoute env
    :<|> artistDislikeRoute env
    :<|> artistEditRoute env

genreBaseEntityRoutes :: Env -> Server BaseEntityRoutes
genreBaseEntityRoutes env =
  genresRoute env
    :<|> genreRoute env
    :<|> genreCreateRoute env
    :<|> genreCreateFormRoute env
    :<|> genreLikeRoute env
    :<|> genreDislikeRoute env
    :<|> genreEditRoute env

songBaseEntityRoutes :: Env -> Server BaseEntityRoutes
songBaseEntityRoutes env =
  songsRoute env
    :<|> songRoute env
    :<|> songCreateRoute env
    :<|> songCreateFormRoute env
    :<|> songLikeRoute env
    :<|> songDislikeRoute env
    :<|> songEditRoute env

preferenceRoutes :: Env -> Server PreferenceRoutes
preferenceRoutes env =
  setLanguageRoute env
    :<|> setArtistSortingRoute env
    :<|> setGenreSortingRoute env
    :<|> setSongSortingRoute env
    :<|> setDarkModeRoute env
    :<|> setSongAsciiSizeRoute env
    :<|> setPaletteRoute env

loginRoutes :: Env -> Server LoginRoutes
loginRoutes env =
  loginFormRoute env
    :<|> submitLoginRoute env

server :: Env -> Server WikiMusicSSRServant
server env =
  fallbackRoute
    :<|> artistBaseEntityRoutes env
    :<|> genreBaseEntityRoutes env
    :<|> songBaseEntityRoutes env
    :<|> preferenceRoutes env
    :<|> loginRoutes env

fallbackRoute :: Handler a
fallbackRoute =
  do
    throwError
    $ ServerError
      { errHTTPCode = 302,
        errReasonPhrase = "Found",
        errBody = "",
        errHeaders = [("Location", "/songs")]
      }

wikimusicSSRServant :: Proxy WikiMusicSSRServant
wikimusicSSRServant = Proxy

myCors :: CorsConfig -> Middleware
myCors cfg = cors (const $ Just policy)
  where
    policy =
      CorsResourcePolicy
        { corsOrigins = Just (map (fromString . T.unpack) (cfg ^. #origins), True),
          corsMethods = map (fromString . T.unpack) (cfg ^. #methods),
          corsRequestHeaders = map (fromString . T.unpack) (cfg ^. #requestHeaders),
          corsExposedHeaders =
            Just
              [ "content-type",
                "date",
                "content-length",
                "access-control-allow-origin",
                "access-control-allow-methods",
                "access-control-allow-headers",
                "access-control-request-method",
                "access-control-request-headers"
              ],
          corsMaxAge = Nothing,
          corsVaryOrigin = False,
          corsRequireOrigin = False,
          corsIgnoreFailures = False
        }