content: {-# LANGUAGE DataKinds              #-}
  {-# LANGUAGE DeriveGeneric          #-}
  {-# LANGUAGE FlexibleContexts       #-}
  {-# LANGUAGE FlexibleInstances      #-}
  {-# LANGUAGE FunctionalDependencies #-}
  {-# LANGUAGE MultiParamTypeClasses  #-}
  {-# LANGUAGE OverloadedStrings      #-}
  {-# LANGUAGE RankNTypes             #-}
  {-# LANGUAGE RecordWildCards        #-}
  {-# LANGUAGE TypeOperators          #-}
  
  {-|
    Production specific execution lives in this module.
  -}
  module Utopia.Web.Executors.Production where
  
  import           Control.Monad.Free
  import           Control.Monad.RWS.Strict
  import           Data.IORef
  import           Data.Pool
  import           Data.Time.Clock
  import           Network.HTTP.Client            (Manager, newManager)
  import           Network.HTTP.Client.TLS
  import           Protolude                      hiding (Handler)
  import           Servant
  import           System.Environment
  import           System.Log.FastLogger
  import           URI.ByteString
  import           Utopia.Web.Assets
  import           Utopia.Web.Auth
  import           Utopia.Web.Auth.Github
  import           Utopia.Web.Auth.Session
  import           Utopia.Web.Auth.Types
  import qualified Utopia.Web.Database            as DB
  import           Utopia.Web.Database.Migrations
  import           Utopia.Web.Database.Types
  import           Utopia.Web.Editor.Branches
  import           Utopia.Web.Endpoints
  import           Utopia.Web.Executors.Common
  import           Utopia.Web.Github
  import           Utopia.Web.Liveblocks
  import           Utopia.Web.Liveblocks.Types
  import           Utopia.Web.Logging
  import           Utopia.Web.Metrics
  import           Utopia.Web.Packager.Locking
  import           Utopia.Web.Packager.NPM
  import           Utopia.Web.ServiceTypes
  import           Utopia.Web.Types
  import           Utopia.Web.Utils.Files
  
  {-|
    Any long living resources like database pools live in here.
  -}
  data ProductionServerResources = ProductionServerResources
                                 { _commitHash              :: Text
                                 , _projectPool             :: DBPool
                                 , _auth0Resources          :: Auth0Resources
                                 , _awsResources            :: AWSResources
                                 , _githubResources         :: GithubAuthResources
                                 , _sessionState            :: SessionState
                                 , _serverPort              :: Int
                                 , _storeForMetrics         :: Store
                                 , _databaseMetrics         :: DB.DatabaseMetrics
                                 , _npmMetrics              :: NPMMetrics
                                 , _registryManager         :: Manager
                                 , _assetsCaches            :: AssetsCaches
                                 , _nodeSemaphore           :: QSem
                                 , _githubSemaphore         :: QSem
                                 , _locksRef                :: PackageVersionLocksRef
                                 , _siteHost                :: Text
                                 , _branchDownloads         :: Maybe BranchDownloads
                                 , _matchingVersionsCache   :: MatchingVersionsCache
                                 , _cdnHost                 :: Text
                                 , _logger                  :: FastLogger
                                 , _loggerShutdown          :: IO ()
                                 , _liveblocksResources     :: Maybe LiveblocksResources
                                 , _shouldUseFakeUser       :: Bool
                                 }
  
  type ProductionProcessMonad a = ServerProcessMonad ProductionServerResources a
  
  dummyUserAlice :: UserDetails
  dummyUserAlice = UserDetails { userId  = "ab9401d8-f6f0-4642-8239-2435656bf0b2"
                               , email   = Just "team1@utopia.app"
                               , name    = Just "A real human being"
                               , picture = Just "/editor/avatars/utopino3.png"
                               }
  
  dummyUserBob :: UserDetails
  dummyUserBob = UserDetails { userId  = "231f5f05-cac7-4910-8006-d7645c44051c"
                              , email   = Just "team1@utopia.app"
                              , name    = Just "Also a real human being"
                              , picture = Just "/editor/avatars/utopino2.png"
                              }
  
  
  {-|
    Interpretor for a service call, which converts it into side effecting calls ready to be invoked.
  -}
  innerServerExecutor :: MonadExecutor ProductionServerResources a
  innerServerExecutor NotFound = do
    throwError err404
  innerServerExecutor BadRequest = do
    throwError err400
  innerServerExecutor NotAuthenticated = do
    throwError err401
  innerServerExecutor (TempRedirect uri) = do
    throwError err302 { errHeaders = [("Location", serializeURIRef' uri)]}
  innerServerExecutor NotModified = do
    throwError err304
  innerServerExecutor (CheckAuthCode authCode action) = do
    auth0 <- fmap _auth0Resources ask
    sessionStore <- fmap _sessionState ask
    pool <- fmap _projectPool ask
    metrics <- fmap _databaseMetrics ask
    canUseFakeUser <- fmap _shouldUseFakeUser ask
    case (canUseFakeUser, authCode) of
      (True, "alice") -> successfulAuthCheck metrics pool sessionStore action dummyUserAlice
      (True, "bob") -> successfulAuthCheck metrics pool sessionStore action dummyUserBob
      _ -> auth0CodeCheck metrics pool sessionStore auth0 authCode action
  innerServerExecutor (Logout cookie pageContents action) = do
    sessionStore <- fmap _sessionState ask
    logoutOfSession sessionStore cookie pageContents action
  innerServerExecutor (ValidateAuth cookie action) = do
    sessionStore <- fmap _sessionState ask
    liftIO $ validateAuthCookie sessionStore cookie action
  innerServerExecutor (UserForId userIdToGet action) = do
    pool <- fmap _projectPool ask
    metrics <- fmap _databaseMetrics ask
    getUserWithDBPool metrics pool userIdToGet action
  innerServerExecutor (DebugLog logContent next) = do
    loggerToUse <- fmap _logger ask
    liftIO $ loggerLn loggerToUse $ toLogStr logContent
    return next
  innerServerExecutor (GetProjectMetadata projectID action) = do
    pool <- fmap _projectPool ask
    metrics <- fmap _databaseMetrics ask
    metadata <- liftIO $ getProjectDetailsWithDBPool metrics pool projectID
    return $ action metadata
  innerServerExecutor (LoadProject projectID action) = do
    pool <- fmap _projectPool ask
    metrics <- fmap _databaseMetrics ask
    loadProjectWithDBPool metrics pool projectID action
  innerServerExecutor (CreateProject action) = do
    pool <- fmap _projectPool ask
    metrics <- fmap _databaseMetrics ask
    createProjectWithDBPool metrics pool action
  innerServerExecutor (SaveProject sessionUser projectID possibleTitle possibleProjectContents next) = do
    pool <- fmap _projectPool ask
    metrics <- fmap _databaseMetrics ask
    saveProjectWithDBPool metrics pool sessionUser projectID possibleTitle possibleProjectContents
    return next
  innerServerExecutor (DeleteProject sessionUser projectID next) = do
    pool <- fmap _projectPool ask
    metrics <- fmap _databaseMetrics ask
    deleteProjectWithDBPool metrics pool sessionUser projectID
    return next
  innerServerExecutor (GetProjectsForUser user action) = do
    pool <- fmap _projectPool ask
    metrics <- fmap _databaseMetrics ask
    getUserProjectsWithDBPool metrics pool user action
  innerServerExecutor (GetShowcaseProjects action) = do
    pool <- fmap _projectPool ask
    metrics <- fmap _databaseMetrics ask
    getShowcaseProjectsWithDBPool metrics pool action
  innerServerExecutor (SetShowcaseProjects showcaseProjects next) = do
    pool <- fmap _projectPool ask
    metrics <- fmap _databaseMetrics ask
    setShowcaseProjectsWithDBPool metrics pool showcaseProjects next
  innerServerExecutor (LoadProjectAsset path possibleETag action) = do
    awsResource <- fmap _awsResources ask
    possibleAsset <- liftIO $ loadAsset (Just awsResource) path possibleETag
    application <- loadProjectAssetWithAsset path possibleAsset
    return $ action application
  innerServerExecutor (SaveProjectAsset user projectID path action) = do
    pool <- fmap _projectPool ask
    awsResource <- fmap _awsResources ask
    metrics <- fmap _databaseMetrics ask
    application <- saveProjectAssetWithCall metrics pool user projectID path $ saveAsset $ Just awsResource
    return $ action application
  innerServerExecutor (RenameProjectAsset user projectID oldPath newPath next) = do
    awsResource <- fmap _awsResources ask
    pool <- fmap _projectPool ask
    metrics <- fmap _databaseMetrics ask
    liftIO $ renameProjectAssetWithCall metrics pool user projectID oldPath newPath (renameProjectAssetOnS3 awsResource)
    return next
  innerServerExecutor (DeleteProjectAsset user projectID path next) = do
    awsResource <- fmap _awsResources ask
    pool <- fmap _projectPool ask
    metrics <- fmap _databaseMetrics ask
    liftIO $ deleteProjectAssetWithCall metrics pool user projectID path (deleteProjectAssetOnS3 awsResource)
    return next
  innerServerExecutor (LoadProjectThumbnail projectID possibleETag action) = do
    awsResource <- fmap _awsResources ask
    application <- loadProjectThumbnailWithCall (loadProjectThumbnailFromS3 awsResource) projectID possibleETag
    return $ action application
  innerServerExecutor (SaveProjectThumbnail user projectID thumbnail next) = do
    pool <- fmap _projectPool ask
    awsResource <- fmap _awsResources ask
    metrics <- fmap _databaseMetrics ask
    saveProjectThumbnailWithCall metrics pool user projectID thumbnail $ saveProjectThumbnailToS3 awsResource
    return next
  innerServerExecutor (GetProxyManager action) = do
    return $ action Nothing
  innerServerExecutor (GetGithubProject owner repo action) = do
    zipball <- liftIO $ fetchRepoArchive owner repo
    return $ action zipball
  innerServerExecutor (GetMetrics action) = do
    store <- fmap _storeForMetrics ask
    storeJSON <- liftIO $ sampleAsJSON store
    return $ action storeJSON
  innerServerExecutor (GetPackageJSON javascriptPackageName maybeJavascriptPackageVersion action) = do
    manager <- fmap _registryManager ask
    let qualifiedPackageName = maybe javascriptPackageName (\v -> javascriptPackageName <> "/" <> v) maybeJavascriptPackageVersion
    packageMetadata <- liftIO $ lookupPackageJSON manager qualifiedPackageName
    return $ action packageMetadata
  innerServerExecutor (GetPackageVersionJSON javascriptPackageName maybeJavascriptPackageVersion action) = do
    semaphore <- fmap _nodeSemaphore ask
    versionsCache <- fmap _matchingVersionsCache ask
    npmMetrics <- fmap _npmMetrics ask
    appLogger <- fmap _logger ask
    packageMetadata <- liftIO $ findMatchingVersions appLogger npmMetrics semaphore versionsCache javascriptPackageName maybeJavascriptPackageVersion
    return $ action packageMetadata
  innerServerExecutor (GetCommitHash action) = do
    hashToUse <- fmap _commitHash ask
    return $ action hashToUse
  innerServerExecutor (GetEditorTextContent branchName fileName action) = do
    downloads <- fmap _branchDownloads ask
    indexHtml <- liftIO $ readEditorContentFromDisk downloads branchName fileName
    return $ action indexHtml
  innerServerExecutor (GetHashedAssetPaths action) = do
    AssetsCaches{..} <- fmap _assetsCaches ask
    AssetResultCache{..} <- liftIO $ readIORef _assetResultCache
    return $ action _editorMappings
  innerServerExecutor (GetPackagePackagerContent versionedPackageName action) = do
    semaphore <- fmap _nodeSemaphore ask
    packagerLocksRef <- fmap _locksRef ask
    npmMetrics <- fmap _npmMetrics ask
    appLogger <- fmap _logger ask
    packagerContent <- liftIO $ getPackagerContent appLogger npmMetrics semaphore packagerLocksRef versionedPackageName
    return $ action packagerContent
  innerServerExecutor (AccessControlAllowOrigin _ action) = do
    return $ action $ Just "*"
  innerServerExecutor (GetSiteRoot action) = do
    hostOfServer <- fmap _siteHost ask
    let siteRoot = "https://" <> hostOfServer
    return $ action siteRoot
  innerServerExecutor (GetCDNRoot action) = do
    hostOfCDN <- fmap _cdnHost ask
    let cdnRoot = "https://" <> hostOfCDN
    return $ action cdnRoot
  innerServerExecutor (GetPathToServe defaultPathToServe possibleBranchName action) = do
    possibleDownloads <- fmap _branchDownloads ask
    pathToServe <- case (defaultPathToServe, possibleBranchName, possibleDownloads) of
                     ("./editor", Just branchName, Just downloads)  -> liftIO $ getBranchBundleFolder downloads branchName
                     _                                                  -> return defaultPathToServe
    return $ action pathToServe
  innerServerExecutor (GetVSCodeAssetRoot action) = do
    return $ action "./vscode/"
  innerServerExecutor (GetUserConfiguration user action) = do
    pool <- fmap _projectPool ask
    metrics <- fmap _databaseMetrics ask
    getUserConfigurationWithDBPool metrics pool user action
  innerServerExecutor (SaveUserConfiguration user possibleShortcutConfig possibleTheme action) = do
    pool <- fmap _projectPool ask
    metrics <- fmap _databaseMetrics ask
    saveUserConfigurationWithDBPool metrics pool user possibleShortcutConfig possibleTheme
    return action
  innerServerExecutor (ClearBranchCache branchName action) = do
    possibleDownloads <- fmap _branchDownloads ask
    liftIO $ traverse_ (\d -> deleteBranchCache d branchName) possibleDownloads
    return action
  innerServerExecutor (GetDownloadBranchFolders action) = do
    downloads <- fmap _branchDownloads ask
    folders <- liftIO $ maybe (pure []) getDownloadedLocalFolders downloads
    pure $ action folders
  innerServerExecutor (GetGithubAuthorizationURI action) = do
    githubResources <- fmap _githubResources ask
    let uri = getAuthorizationURI githubResources
    pure $ action uri
  innerServerExecutor (GetGithubAccessToken user authCode action) = do
    githubResources <- fmap _githubResources ask
    metrics <- fmap _databaseMetrics ask
    logger <- fmap _logger ask
    pool <- fmap _projectPool ask
    result <- getAndHandleGithubAccessToken githubResources logger metrics pool user authCode
    pure $ action result
  innerServerExecutor (GetGithubAuthentication user action) = do
    metrics <- fmap _databaseMetrics ask
    pool <- fmap _projectPool ask
    result <- liftIO $ DB.lookupGithubAuthenticationDetails metrics pool user
    pure $ action result
  innerServerExecutor (SaveToGithubRepo user projectID possibleBranchName possibleCommitMessage model action) = do
    githubSemaphore <- fmap _githubSemaphore ask
    githubResources <- fmap _githubResources ask
    awsResource <- fmap _awsResources ask
    metrics <- fmap _databaseMetrics ask
    logger <- fmap _logger ask
    pool <- fmap _projectPool ask
    result <- createTreeAndSaveToGithub githubSemaphore githubResources (Just awsResource) logger metrics pool user projectID possibleBranchName possibleCommitMessage model
    pure $ action result
  innerServerExecutor (GetBranchesFromGithubRepo user owner repository action) = do
    githubSemaphore <- fmap _githubSemaphore ask
    githubResources <- fmap _githubResources ask
    metrics <- fmap _databaseMetrics ask
    logger <- fmap _logger ask
    pool <- fmap _projectPool ask
    result <- getGithubBranches githubSemaphore githubResources logger metrics pool user owner repository
    pure $ action result
  innerServerExecutor (GetBranchContent user owner repository branchName possibleCommitSha possiblePreviousCommitSha action) = do
    githubSemaphore <- fmap _githubSemaphore ask
    githubResources <- fmap _githubResources ask
    metrics <- fmap _databaseMetrics ask
    logger <- fmap _logger ask
    pool <- fmap _projectPool ask
    result <- getGithubBranch githubSemaphore githubResources logger metrics pool user owner repository branchName possibleCommitSha possiblePreviousCommitSha
    pure $ action result
  innerServerExecutor (GetDefaultBranchContent user owner repository possibleCommitSha possiblePreviousCommitSha action) = do
    githubSemaphore <- fmap _githubSemaphore ask
    githubResources <- fmap _githubResources ask
    metrics <- fmap _databaseMetrics ask
    logger <- fmap _logger ask
    pool <- fmap _projectPool ask
    result <- getDefaultGithubBranch githubSemaphore githubResources logger metrics pool user owner repository possibleCommitSha possiblePreviousCommitSha
    pure $ action result
  innerServerExecutor (GetUsersRepositories user action) = do
    githubSemaphore <- fmap _githubSemaphore ask
    githubResources <- fmap _githubResources ask
    metrics <- fmap _databaseMetrics ask
    logger <- fmap _logger ask
    pool <- fmap _projectPool ask
    result <- getGithubUsersPublicRepositories githubSemaphore githubResources logger metrics pool user
    pure $ action result
  innerServerExecutor (SaveGithubAsset user owner repository assetSha projectID assetPath action) = do
    githubSemaphore <- fmap _githubSemaphore ask
    githubResources <- fmap _githubResources ask
    awsResource <- fmap _awsResources ask
    metrics <- fmap _databaseMetrics ask
    logger <- fmap _logger ask
    pool <- fmap _projectPool ask
    result <- saveGithubAssetToProject githubSemaphore githubResources (Just awsResource) logger metrics pool user owner repository assetSha projectID assetPath
    pure $ action result
  innerServerExecutor (GetPullRequestForBranch user owner repository branchName action) = do
    githubSemaphore <- fmap _githubSemaphore ask
    githubResources <- fmap _githubResources ask
    metrics <- fmap _databaseMetrics ask
    logger <- fmap _logger ask
    pool <- fmap _projectPool ask
    result <- getBranchPullRequest githubSemaphore githubResources logger metrics pool user owner repository branchName
    pure $ action result
  innerServerExecutor (GetGithubUserDetails user action) = do
    githubSemaphore <- fmap _githubSemaphore ask
    githubResources <- fmap _githubResources ask
    metrics <- fmap _databaseMetrics ask
    logger <- fmap _logger ask
    pool <- fmap _projectPool ask
    result <- getDetailsOfGithubUser githubSemaphore githubResources logger metrics pool user
    pure $ action result
  innerServerExecutor (AuthLiveblocksUser user roomID action) = do
    possibleLiveblocksResources <- fmap _liveblocksResources ask
    metrics <- fmap _databaseMetrics ask
    pool <- fmap _projectPool ask
    case possibleLiveblocksResources of
      Nothing -> throwError err501
      Just liveblocksResources -> do
        errorOrToken <- liftIO $ authorizeUserForLiveblocksProjectRoom liveblocksResources metrics pool user roomID
        case errorOrToken of
          Left errorMessage -> putStrLn errorMessage >> throwError err500
          Right token       -> pure $ action token
  innerServerExecutor (IsLiveblocksEnabled action) = do
    possibleLiveblocksResources <- fmap _liveblocksResources ask
    pure $ action $ isJust possibleLiveblocksResources
  innerServerExecutor (ClaimCollaborationControl user projectID collaborationEditor action) = do
    metrics <- fmap _databaseMetrics ask
    pool <- fmap _projectPool ask
    projectOwnershipResult <- liftIO $ DB.checkIfProjectOwner metrics pool user projectID
    unless projectOwnershipResult $ throwError err400
    ownershipResult <- liftIO $ DB.maybeClaimCollaborationControl metrics pool getCurrentTime user projectID collaborationEditor
    pure $ action ownershipResult
  innerServerExecutor (SnatchCollaborationControl user projectID collaborationEditor action) = do
    metrics <- fmap _databaseMetrics ask
    pool <- fmap _projectPool ask
    projectOwnershipResult <- liftIO $ DB.checkIfProjectOwner metrics pool user projectID
    unless projectOwnershipResult $ throwError err400
    liftIO $ DB.forceClaimCollaborationControl metrics pool getCurrentTime user projectID collaborationEditor
    pure action
  innerServerExecutor (ReleaseCollaborationControl user projectID collaborationEditor action) = do
    metrics <- fmap _databaseMetrics ask
    pool <- fmap _projectPool ask
    liftIO $ DB.releaseCollaborationControl metrics pool user projectID collaborationEditor
    pure action
  innerServerExecutor (ClearCollaboratorOwnership user collaborationEditor action) = do
    metrics <- fmap _databaseMetrics ask
    pool <- fmap _projectPool ask
    liftIO $ DB.deleteCollaborationControlByCollaborator metrics pool user collaborationEditor
    pure action
  
  readEditorContentFromDisk :: Maybe BranchDownloads -> Maybe Text -> Text -> IO Text
  readEditorContentFromDisk (Just downloads) (Just branchName) fileName = do
    readBranchHTMLContent downloads branchName fileName
  readEditorContentFromDisk _ _ fileName = readFile ("./editor/" <> toS fileName)
  
  {-|
    Invokes a service call using the supplied resources.
  -}
  serverExecutor :: forall a. ProductionServerResources -> ServiceCallsF a -> Handler a
  serverExecutor serverResources serviceCalls = do
    result <- evalRWST (innerServerExecutor serviceCalls) serverResources ()
    return $ fst result
  
  {-|
    Folds over the server monad, computing the full result of an endpoint call.
  -}
  serverMonadToHandler :: ProductionServerResources -> (forall a. ServerMonad a -> Handler a)
  serverMonadToHandler resources = foldFree (serverExecutor resources)
  
  {-|
    Glue to pull together the free monad computation and turn it into an HTTP service.
  -}
  serverAPI :: ProductionServerResources -> Server API
  serverAPI resources = hoistServer apiProxy (serverMonadToHandler resources) server
  
  assetPathsAndBuilders :: [PathAndBuilders]
  assetPathsAndBuilders =
    [ simplePathAndBuilders "/server/editor/icons" "/server" "" "/server" ""
    ]
  
  shouldUseFakeUser :: Maybe Text -> Bool
  shouldUseFakeUser (Just "USE_FAKE_USER") = True
  shouldUseFakeUser _                      = False
  
  initialiseResources :: IO ProductionServerResources
  initialiseResources = do
    _commitHash <- toS <$> getEnv "UTOPIA_SHA"
    _projectPool <- DB.createDatabasePoolFromEnvironment
    maybeAuth0Resources <- getAuth0Environment
    _auth0Resources <- maybe (panic "No Auth0 environment configured.") pure maybeAuth0Resources
    maybeAws <- getAmazonResourcesFromEnvironment
    _awsResources <- maybe (panic "No AWS environment configured.") pure maybeAws
    maybeGithubAuthResources <- getGithubAuthResources
    _githubResources <- maybe (panic "No Github resources configured.") pure maybeGithubAuthResources
    _sessionState <- createSessionState _projectPool
    _serverPort <- portFromEnvironment
    _storeForMetrics <- newStore
    _databaseMetrics <- DB.createDatabaseMetrics _storeForMetrics
    _npmMetrics <- createNPMMetrics _storeForMetrics
    _registryManager <- newManager tlsManagerSettings
    _assetsCaches <- emptyAssetsCaches assetPathsAndBuilders
    _nodeSemaphore <- newQSem 1
    _githubSemaphore <- newQSem 5
    _siteHost <- toS <$> getEnv "SITE_HOST"
    _cdnHost <- toS <$> getEnv "CDN_HOST"
    _branchDownloads <- createBranchDownloads
    _locksRef <- newIORef mempty
    _matchingVersionsCache <- newMatchingVersionsCache
    (_logger, _loggerShutdown) <- newFastLogger (LogStdout defaultBufSize)
    _liveblocksResources <- makeLiveblocksResources
    _shouldUseFakeUser <- shouldUseFakeUser <$> (fmap toS) <$> lookupEnv "FAKE_USER_SETTING"
    return $ ProductionServerResources{..}
  
  startup :: ProductionServerResources -> IO Stop
  startup ProductionServerResources{..} = do
    migrateDatabase True False _projectPool
    DB.cleanupCollaborationControl _databaseMetrics _projectPool getCurrentTime
    preloadNPMDependencies _logger _npmMetrics _nodeSemaphore _locksRef
    hashedFilenamesThread <- forkIO $ watchFilenamesWithHashes (_hashCache _assetsCaches) (_assetResultCache _assetsCaches) assetPathsAndBuilders
    return $ do
          killThread hashedFilenamesThread
          destroyAllResources _projectPool
  
  serverPortFromResources :: ProductionServerResources -> Int
  serverPortFromResources resources = _serverPort resources
  
  productionEnvironmentRuntime :: EnvironmentRuntime ProductionServerResources
  productionEnvironmentRuntime = EnvironmentRuntime
    { _initialiseResources = initialiseResources
    , _startup = startup
    , _envServerPort = serverPortFromResources
    , _serverAPI = serverAPI
    , _startupLogging = const True
    , _getLogger = _logger
    , _metricsStore = _storeForMetrics
    , _cacheForAssets = (\r -> readIORef $ _assetResultCache $ _assetsCaches r)
    , _forceSSL = const False
    }
  
  