Application.hs 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. {-# LANGUAGE NoImplicitPrelude #-}
  2. {-# LANGUAGE OverloadedStrings #-}
  3. {-# LANGUAGE TemplateHaskell #-}
  4. {-# LANGUAGE MultiParamTypeClasses #-}
  5. {-# LANGUAGE TypeFamilies #-}
  6. {-# LANGUAGE ViewPatterns #-}
  7. {-# LANGUAGE RecordWildCards #-}
  8. {-# OPTIONS_GHC -fno-warn-orphans #-}
  9. module Application
  10. ( getApplicationDev
  11. , appMain
  12. , develMain
  13. , makeFoundation
  14. , makeLogWare
  15. -- * for DevelMain
  16. , getApplicationRepl
  17. , shutdownApp
  18. -- * for GHCI
  19. , handler
  20. , db
  21. ) where
  22. import Control.Monad.Logger (liftLoc, runLoggingT)
  23. import Database.Persist.Postgresql (createPostgresqlPool, pgConnStr,
  24. pgPoolSize, runSqlPool)
  25. import Import
  26. import Language.Haskell.TH.Syntax (qLocation)
  27. import Network.HTTP.Client.TLS (getGlobalManager)
  28. import Network.Wai (Middleware)
  29. import Network.Wai.Handler.Warp (Settings, defaultSettings,
  30. defaultShouldDisplayException,
  31. runSettings, setHost,
  32. setOnException, setPort, getPort)
  33. import Network.Wai.Middleware.RequestLogger (Destination (Logger),
  34. IPAddrSource (..),
  35. OutputFormat (..), destination,
  36. mkRequestLogger, outputFormat)
  37. import System.Log.FastLogger (defaultBufSize, newStdoutLoggerSet,
  38. toLogStr)
  39. -- Import all relevant handler modules here.
  40. -- Don't forget to add new modules to your cabal file!
  41. import Handler.Common
  42. import Handler.Home
  43. import Handler.Comment
  44. import Handler.Profile
  45. import Handler.PostNew
  46. import Handler.PostDetails
  47. -- This line actually creates our YesodDispatch instance. It is the second half
  48. -- of the call to mkYesodData which occurs in Foundation.hs. Please see the
  49. -- comments there for more details.
  50. mkYesodDispatch "App" resourcesApp
  51. -- | This function allocates resources (such as a database connection pool),
  52. -- performs initialization and returns a foundation datatype value. This is also
  53. -- the place to put your migrate statements to have automatic database
  54. -- migrations handled by Yesod.
  55. makeFoundation :: AppSettings -> IO App
  56. makeFoundation appSettings = do
  57. -- Some basic initializations: HTTP connection manager, logger, and static
  58. -- subsite.
  59. appHttpManager <- getGlobalManager
  60. appLogger <- newStdoutLoggerSet defaultBufSize >>= makeYesodLogger
  61. appStatic <-
  62. (if appMutableStatic appSettings then staticDevel else static)
  63. (appStaticDir appSettings)
  64. -- We need a log function to create a connection pool. We need a connection
  65. -- pool to create our foundation. And we need our foundation to get a
  66. -- logging function. To get out of this loop, we initially create a
  67. -- temporary foundation without a real connection pool, get a log function
  68. -- from there, and then create the real foundation.
  69. let mkFoundation appConnPool = App {..}
  70. -- The App {..} syntax is an example of record wild cards. For more
  71. -- information, see:
  72. -- https://ocharles.org.uk/blog/posts/2014-12-04-record-wildcards.html
  73. tempFoundation = mkFoundation $ error "connPool forced in tempFoundation"
  74. logFunc = messageLoggerSource tempFoundation appLogger
  75. -- Create the database connection pool
  76. pool <- flip runLoggingT logFunc $ createPostgresqlPool
  77. (pgConnStr $ appDatabaseConf appSettings)
  78. (pgPoolSize $ appDatabaseConf appSettings)
  79. -- Perform database migration using our application's logging settings.
  80. runLoggingT (runSqlPool (runMigration migrateAll) pool) logFunc
  81. -- Return the foundation
  82. return $ mkFoundation pool
  83. -- | Convert our foundation to a WAI Application by calling @toWaiAppPlain@ and
  84. -- applying some additional middlewares.
  85. makeApplication :: App -> IO Application
  86. makeApplication foundation = do
  87. logWare <- makeLogWare foundation
  88. -- Create the WAI application and apply middlewares
  89. appPlain <- toWaiAppPlain foundation
  90. return $ logWare $ defaultMiddlewaresNoLogging appPlain
  91. makeLogWare :: App -> IO Middleware
  92. makeLogWare foundation =
  93. mkRequestLogger def
  94. { outputFormat =
  95. if appDetailedRequestLogging $ appSettings foundation
  96. then Detailed True
  97. else Apache
  98. (if appIpFromHeader $ appSettings foundation
  99. then FromFallback
  100. else FromSocket)
  101. , destination = Logger $ loggerSet $ appLogger foundation
  102. }
  103. -- | Warp settings for the given foundation value.
  104. warpSettings :: App -> Settings
  105. warpSettings foundation =
  106. setPort (appPort $ appSettings foundation)
  107. $ setHost (appHost $ appSettings foundation)
  108. $ setOnException (\_req e ->
  109. when (defaultShouldDisplayException e) $ messageLoggerSource
  110. foundation
  111. (appLogger foundation)
  112. $(qLocation >>= liftLoc)
  113. "yesod"
  114. LevelError
  115. (toLogStr $ "Exception from Warp: " ++ show e))
  116. defaultSettings
  117. -- | For yesod devel, return the Warp settings and WAI Application.
  118. getApplicationDev :: IO (Settings, Application)
  119. getApplicationDev = do
  120. settings <- getAppSettings
  121. foundation <- makeFoundation settings
  122. wsettings <- getDevSettings $ warpSettings foundation
  123. app <- makeApplication foundation
  124. return (wsettings, app)
  125. getAppSettings :: IO AppSettings
  126. getAppSettings = loadYamlSettings [configSettingsYml] [] useEnv
  127. -- | main function for use by yesod devel
  128. develMain :: IO ()
  129. develMain = develMainHelper getApplicationDev
  130. -- | The @main@ function for an executable running this site.
  131. appMain :: IO ()
  132. appMain = do
  133. -- Get the settings from all relevant sources
  134. settings <- loadYamlSettingsArgs
  135. -- fall back to compile-time values, set to [] to require values at runtime
  136. [configSettingsYmlValue]
  137. -- allow environment variables to override
  138. useEnv
  139. -- Generate the foundation from the settings
  140. foundation <- makeFoundation settings
  141. -- Generate a WAI Application from the foundation
  142. app <- makeApplication foundation
  143. -- Run the application with Warp
  144. runSettings (warpSettings foundation) app
  145. --------------------------------------------------------------
  146. -- Functions for DevelMain.hs (a way to run the app from GHCi)
  147. --------------------------------------------------------------
  148. getApplicationRepl :: IO (Int, App, Application)
  149. getApplicationRepl = do
  150. settings <- getAppSettings
  151. foundation <- makeFoundation settings
  152. wsettings <- getDevSettings $ warpSettings foundation
  153. app1 <- makeApplication foundation
  154. return (getPort wsettings, foundation, app1)
  155. shutdownApp :: App -> IO ()
  156. shutdownApp _ = return ()
  157. ---------------------------------------------
  158. -- Functions for use in development with GHCi
  159. ---------------------------------------------
  160. -- | Run a handler
  161. handler :: Handler a -> IO a
  162. handler h = getAppSettings >>= makeFoundation >>= flip unsafeHandler h
  163. -- | Run DB queries
  164. db :: ReaderT SqlBackend Handler a -> IO a
  165. db = handler . runDB