content: {-# LANGUAGE CPP               #-}
  {-# LANGUAGE NoImplicitPrelude #-}
  {-# LANGUAGE OverloadedStrings #-}
  {-# LANGUAGE RecordWildCards   #-}
  {-# LANGUAGE TemplateHaskell   #-}
  -- | Settings are centralized, as much as possible, into this file. This
  -- includes database connection settings, static file locations, etc.
  -- In addition, you can configure a number of different aspects of Yesod
  -- by overriding methods in the Yesod typeclass. That instance is
  -- declared in the Foundation.hs file.
  module Settings where
  
  import ClassyPrelude.Yesod
  import qualified Control.Exception as Exception
  import Data.Aeson                  (Result (..), fromJSON, withObject, (.!=),
                                      (.:?))
  import Data.FileEmbed              (embedFile)
  import Data.Yaml                   (decodeEither')
  import Database.Persist.Sqlite     (SqliteConf)
  import Network.Wai.Handler.Warp    (HostPreference)
  import Yesod.Default.Config2       (applyEnvValue, configSettingsYml)
  
  -- | Runtime settings to configure this application. These settings can be
  -- loaded from various sources: defaults, environment variables, config files,
  -- theoretically even a database.
  data AppSettings = AppSettings
      { appDatabaseConf           :: SqliteConf
      -- ^ Configuration settings for accessing the database.
      , appRoot                   :: Maybe Text
      -- ^ Base for all generated URLs. If @Nothing@, determined
      -- from the request headers.
      , appHost                   :: HostPreference
      -- ^ Host/interface the server should bind to.
      , appPort                   :: Int
      -- ^ Port to listen on
      , appIpFromHeader           :: Bool
      -- ^ Get the IP address from the header when logging. Useful when sitting
      -- behind a reverse proxy.
      , appDetailedRequestLogging :: Bool
      -- ^ Use detailed request logging system
      , appShouldLogAll           :: Bool
      -- ^ Should all log messages be displayed?
      , appReloadTemplates        :: Bool
      -- ^ Use the reload version of templates
      , appMutableStatic          :: Bool
      -- ^ Assume that files in the static dir may change after compilation
      , appSkipCombining          :: Bool
      -- ^ Perform no stylesheet/script combining
      , appJwtSecret              :: Text
      -- ^ JWT secret key
      , appCorsOriginWhitelist    :: [Text]
      -- ^ Origins allowed to perform CORS requests
      }
  
  instance FromJSON AppSettings where
      parseJSON = withObject "AppSettings" $ \o -> do
          let defaultDev =
  #ifdef DEVELOPMENT
                  True
  #else
                  False
  #endif
          appDatabaseConf           <- o .: "database"
          appRoot                   <- o .:? "approot"
          appHost                   <- fromString <$> o .: "host"
          appPort                   <- o .: "port"
          appIpFromHeader           <- o .: "ip-from-header"
  
          dev                       <- o .:? "development"      .!= defaultDev
  
          appDetailedRequestLogging <- o .:? "detailed-logging" .!= dev
          appShouldLogAll           <- o .:? "should-log-all"   .!= dev
          appReloadTemplates        <- o .:? "reload-templates" .!= dev
          appMutableStatic          <- o .:? "mutable-static"   .!= dev
          appSkipCombining          <- o .:? "skip-combining"   .!= dev
  
          appJwtSecret              <- o .:  "jwt-secret"
          appCorsOriginWhitelist    <- o .:  "cors-origin-whitelist"
  
          return AppSettings {..}
  
  -- | Raw bytes at compile time of @config/settings.yml@
  configSettingsYmlBS :: ByteString
  configSettingsYmlBS = $(embedFile configSettingsYml)
  
  -- | @config/settings.yml@, parsed to a @Value@.
  configSettingsYmlValue :: Value
  configSettingsYmlValue = either Exception.throw id
                         $ decodeEither' configSettingsYmlBS
  
  -- | A version of @AppSettings@ parsed at compile time from @config/settings.yml@.
  compileTimeAppSettings :: AppSettings
  compileTimeAppSettings =
      case fromJSON $ applyEnvValue False mempty configSettingsYmlValue of
          Error e -> error e
          Success settings -> settings
  