stackage-server/Application.hs

173 lines
6.5 KiB
Haskell
Raw Normal View History

2014-04-09 07:52:04 +00:00
{-# OPTIONS_GHC -fno-warn-orphans #-}
module Application
( makeApplication
, getApplicationDev
, makeFoundation
) where
2016-05-02 11:16:11 +00:00
import Control.Concurrent (forkIO)
2014-11-18 13:46:24 +00:00
import Control.Exception (catch)
2014-12-09 12:01:38 +00:00
import Data.WebsiteContent
import Import hiding (catch)
import Language.Haskell.TH.Syntax (Loc(..))
import Network.Wai (Middleware, responseLBS, rawPathInfo)
import Network.Wai.Logger (clockDateCacher)
2016-02-02 07:56:30 +00:00
import Network.Wai.Middleware.ForceSSL (forceSSL)
import Network.Wai.Middleware.RequestLogger
2014-04-09 07:52:04 +00:00
( mkRequestLogger, outputFormat, OutputFormat (..), IPAddrSource (..), destination
)
import qualified Network.Wai.Middleware.RequestLogger as RequestLogger
2015-03-16 13:54:58 +00:00
import System.Log.FastLogger (newStdoutLoggerSet, newFileLoggerSet, defaultBufSize, fromLogStr)
2014-04-09 11:38:54 +00:00
import qualified System.Random.MWC as MWC
import Yesod.Core.Types (loggerSet, Logger (Logger))
import Yesod.Default.Config
import Yesod.Default.Handlers
import Yesod.Default.Main
2014-12-09 13:38:36 +00:00
import Yesod.GitRepo
2014-12-09 12:01:38 +00:00
import System.Process (rawSystem)
import Stackage.Database.Cron (loadFromS3)
import Control.AutoUpdate
import qualified Echo
2014-04-09 07:52:04 +00:00
-- Import all relevant handler modules here.
-- Don't forget to add new modules to your cabal file!
import Handler.Home
2014-06-01 11:35:10 +00:00
import Handler.Snapshots
import Handler.StackageHome
import Handler.StackageIndex
import Handler.StackageSdist
import Handler.System
2014-10-20 11:46:57 +00:00
import Handler.Haddock
2014-10-22 23:29:21 +00:00
import Handler.Package
2014-10-22 23:45:32 +00:00
import Handler.PackageList
import Handler.Hoogle
2015-01-06 08:10:47 +00:00
import Handler.BuildVersion
2015-03-23 19:02:05 +00:00
import Handler.Sitemap
2015-03-26 15:34:58 +00:00
import Handler.BuildPlan
import Handler.Download
import Handler.OldLinks
2015-10-11 11:16:10 +00:00
import Handler.Feed
import Handler.DownloadStack
2014-04-09 07:52:04 +00:00
-- This line actually creates our YesodDispatch instance. It is the second half
-- of the call to mkYesodData which occurs in Foundation.hs. Please see the
-- comments there for more details.
mkYesodDispatch "App" resourcesApp
-- This function allocates resources (such as a database connection pool),
-- performs initialization and creates a WAI application. This is also the
-- place to put your migrate statements to have automatic database
-- migrations handled by Yesod.
makeApplication :: Bool -- ^ Use Echo.
-> AppConfig DefaultEnv Extra -> IO (Application, LogFunc)
makeApplication echo@True conf = do
foundation <- makeFoundation echo conf
app <- toWaiAppPlain foundation
logWare <- mkRequestLogger def
{ destination = RequestLogger.Callback (const (return ()))
}
Echo.clear
2016-02-02 07:56:30 +00:00
return (forceSSL' conf $ logWare (defaultMiddlewaresNoLogging app),logFunc)
2014-07-30 09:10:45 +00:00
where logFunc (Loc filename' _pkg _mod (line,_) _) source level str =
Echo.write (filename',line) (show source ++ ": " ++ show level ++ ": " ++ toStr str)
toStr = unpack . decodeUtf8 . fromLogStr
makeApplication echo@False conf = do
foundation <- makeFoundation echo conf
2014-04-09 07:52:04 +00:00
-- Initialize the logging middleware
logWare <- mkRequestLogger def
{ outputFormat =
if development
then Detailed True
else Apache FromFallback
2014-04-09 07:52:04 +00:00
, destination = RequestLogger.Logger $ loggerSet $ appLogger foundation
}
-- Create the WAI application and apply middlewares
app <- toWaiAppPlain foundation
let logFunc = messageLoggerSource foundation (appLogger foundation)
2016-02-02 07:56:30 +00:00
middleware = forceSSL' conf . nicerExceptions . logWare . defaultMiddlewaresNoLogging
2014-04-09 11:38:54 +00:00
return (middleware app, logFunc)
2016-02-02 07:56:30 +00:00
forceSSL' :: AppConfig DefaultEnv Extra -> Middleware
forceSSL' ac app
| extraForceSsl $ appExtra ac = \req send ->
-- Don't force SSL for tarballs, to provide 00-index.tar.gz and package
-- tarball access for cabal-install
if ".tar.gz" `isSuffixOf` rawPathInfo req
then app req send
else forceSSL app req send
| otherwise = app
2016-02-02 07:56:30 +00:00
2014-11-18 13:46:24 +00:00
nicerExceptions :: Middleware
nicerExceptions app req send = catch (app req send) $ \e -> do
let text = "Exception thrown to Warp: " ++ tshow (e :: SomeException)
putStrLn text
send $ responseLBS status500 [("Content-Type", "text/plain")] $
fromStrict $ encodeUtf8 text
2014-04-09 07:52:04 +00:00
-- | Loads up any necessary settings, creates your foundation datatype, and
-- performs some initialization.
makeFoundation :: Bool -> AppConfig DefaultEnv Extra -> IO App
makeFoundation useEcho conf = do
2015-10-06 04:14:29 +00:00
let extra = appExtra conf
2014-04-09 07:52:04 +00:00
manager <- newManager
s <- staticSite
loggerSet' <- if useEcho
then newFileLoggerSet defaultBufSize "/dev/null"
else newStdoutLoggerSet defaultBufSize
2015-03-15 17:03:45 +00:00
(getter, _) <- clockDateCacher
2014-04-09 07:52:04 +00:00
2014-04-09 11:38:54 +00:00
gen <- MWC.createSystemRandom
2015-10-06 04:14:29 +00:00
websiteContent' <- if extraDevDownload extra
2014-12-09 13:38:36 +00:00
then do
void $ rawSystem "git"
[ "clone"
, "https://github.com/fpco/stackage-content.git"
]
gitRepoDev "stackage-content" loadWebsiteContent
else gitRepo
"https://github.com/fpco/stackage-content.git"
"master"
loadWebsiteContent
2014-12-09 12:01:38 +00:00
2015-10-06 04:14:29 +00:00
(stackageDatabase', refreshDB) <- loadFromS3 (extraDevDownload extra) manager
2015-04-01 05:02:56 +00:00
-- Temporary workaround to force content updates regularly, until
-- distribution of webhooks is handled via consul
void $ forkIO $ forever $ void $ do
threadDelay $ 1000 * 1000 * 60 * 5
handleAny print refreshDB
handleAny print $ grRefresh websiteContent'
2015-05-11 17:23:09 +00:00
latestStackMatcher' <- mkAutoUpdate defaultUpdateSettings
2015-10-15 18:42:04 +00:00
{ updateFreq = 1000 * 1000 * 60 * 30 -- update every thirty minutes
, updateAction = getLatestMatcher manager
}
2016-05-08 08:39:19 +00:00
hoogleLock <- newMVar ()
2014-04-09 07:52:04 +00:00
let logger = Yesod.Core.Types.Logger loggerSet' getter
foundation = App
2014-04-09 11:38:54 +00:00
{ settings = conf
, getStatic = s
, httpManager = manager
, appLogger = logger
, genIO = gen
2014-12-09 12:01:38 +00:00
, websiteContent = websiteContent'
2015-05-11 17:23:09 +00:00
, stackageDatabase = stackageDatabase'
, latestStackMatcher = latestStackMatcher'
2016-05-08 08:39:19 +00:00
, appHoogleLock = hoogleLock
2014-04-09 11:38:54 +00:00
}
2014-04-09 07:52:04 +00:00
return foundation
2014-05-14 05:28:00 +00:00
2014-04-09 07:52:04 +00:00
-- for yesod devel
getApplicationDev :: Bool -> IO (Int, Application)
getApplicationDev useEcho =
defaultDevelApp loader (fmap fst . makeApplication useEcho)
2014-04-09 07:52:04 +00:00
where
loader = Yesod.Default.Config.loadConfig (configSettings Development)
{ csParseExtra = parseExtra
}