packages feed

wikimusic-ssr-0.6.0.0: 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

server :: Env -> Server WikiMusicSSRServant
server env =
  fallbackRoute
    :<|> artistsRoute env
    :<|> artistRoute env
    :<|> artistCreateRoute env
    :<|> artistCreateFormRoute env
    :<|> artistLikeRoute env
    :<|> artistDislikeRoute env
    :<|> genresRoute env
    :<|> genreRoute env
    :<|> genreCreateRoute env
    :<|> songsRoute env
    :<|> songRoute env
    :<|> songCreateRoute env
    :<|> setLanguageRoute env
    :<|> setArtistSortingRoute env
    :<|> setGenreSortingRoute env
    :<|> setSongSortingRoute env
    :<|> setDarkModeRoute env
    :<|> setSongAsciiSizeRoute env
    :<|> setPaletteRoute env
    :<|> loginFormRoute env
    :<|> submitLoginRoute 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
        }