|
1 |
| -{-# LANGUAGE OverloadedStrings #-} |
2 |
| -{-# LANGUAGE TypeOperators #-} |
| 1 | +{-# LANGUAGE DeriveDataTypeable #-} |
| 2 | +{-# LANGUAGE TypeOperators #-} |
3 | 3 | module Main where
|
4 | 4 |
|
| 5 | +import Control.Concurrent |
| 6 | +import Control.Exception |
5 | 7 | import Control.Monad
|
6 |
| -import Control.Monad.State |
7 | 8 |
|
8 |
| -import qualified Data.ByteString.Char8 as B |
9 | 9 | import Data.Maybe
|
10 |
| -import qualified Data.Text as T |
11 |
| -import Data.Time |
| 10 | +import Data.Typeable |
| 11 | + |
| 12 | +import Filesystem.Path.CurrentOS |
12 | 13 |
|
13 | 14 | import Happstack.Server
|
14 | 15 |
|
15 |
| -import System.Environment |
| 16 | +import Network.Socket |
16 | 17 |
|
17 |
| -import Web.Routes |
18 |
| -import Web.Routes.Boomerang |
19 |
| -import Web.Routes.Happstack |
| 18 | +import System.Argv0 |
| 19 | +import System.Environment |
| 20 | +import System.Posix.Process |
| 21 | +import System.Posix.Signals |
20 | 22 |
|
21 | 23 | import App
|
22 |
| -import Language |
23 |
| -import Models |
24 |
| -import Routes |
25 |
| -import Utils |
26 |
| -import Views |
27 | 24 |
|
28 |
| -type AppPart a = RouteT Sitemap (ServerPartT App) a |
| 25 | +data Reload = Reload |
| 26 | + deriving (Show, Typeable) |
29 | 27 |
|
30 |
| -handler :: Sitemap -> AppPart Response |
31 |
| -handler route = case route of |
32 |
| - Index -> index |
33 |
| - Yearly y -> yearlyIndex y |
34 |
| - Monthly y m -> monthlyIndex y m |
35 |
| - Daily d -> dailyIndex d |
36 |
| - ArticleView d s -> article d s |
37 |
| - MetaView s -> meta s |
| 28 | +instance Exception Reload |
38 | 29 |
|
39 |
| -site :: Site Sitemap (ServerPartT App Response) |
40 |
| -site = boomerangSiteRouteT handler sitemap |
| 30 | +-- Run the site, handling SIGHUP |
| 31 | +main :: IO () |
| 32 | +main = do |
| 33 | + mainThread <- myThreadId |
| 34 | + _ <- installHandler lostConnection |
| 35 | + (CatchOnce $ reloadExecutable mainThread) Nothing |
| 36 | + runSite |
| 37 | + |
| 38 | +-- Replace the process with a (possibly updated) executable |
| 39 | +-- Throw a "Reload" exception to the main thread so it releases |
| 40 | +-- its socket first |
| 41 | +reloadExecutable :: ThreadId -> IO () |
| 42 | +reloadExecutable mainThread = do |
| 43 | + throwTo mainThread Reload |
| 44 | + ownPath <- liftM encodeString getArgv0 |
| 45 | + executeFile ownPath False [] Nothing |
41 | 46 |
|
42 | 47 | siteAddress :: IO String
|
43 | 48 | siteAddress = do
|
44 | 49 | addr <- lookupEnv "SITE_URL"
|
45 | 50 | return $ fromMaybe "http://localhost:8000" addr
|
46 | 51 |
|
47 |
| -main :: IO () |
48 |
| -main = do |
49 |
| - address <- siteAddress |
50 |
| - listenPort <- lookupEnv "LISTEN_PORT" |
51 |
| - let conf = nullConf { port = read $ fromMaybe "8000" listenPort } |
52 |
| - simpleHTTP' runApp conf $ implSite (T.pack address) "" site |
53 |
| - |
54 |
| -index :: AppPart Response |
55 |
| -index = articleList $ const True |
| 52 | +listenPort :: IO Int |
| 53 | +listenPort = liftM (read . fromMaybe "8000") (lookupEnv "LISTEN_PORT") |
56 | 54 |
|
57 |
| -yearlyIndex :: Integer -> AppPart Response |
58 |
| -yearlyIndex = articleList . byYear |
59 |
| - |
60 |
| -monthlyIndex :: Integer -> Int -> AppPart Response |
61 |
| -monthlyIndex year month = articleList $ byYearMonth year month |
62 |
| - |
63 |
| -dailyIndex :: Day -> AppPart Response |
64 |
| -dailyIndex date = articleList $ byDate date |
65 |
| - |
66 |
| -languageHeaderM :: AppPart LanguagePreference |
67 |
| -languageHeaderM = do |
68 |
| - request <- askRq |
69 |
| - let header = getHeader "Accept-Language" request |
70 |
| - return $ languageHeader $ liftM B.unpack header |
71 |
| - |
72 |
| -html :: ToMessage a => a -> AppPart Response |
73 |
| -html = ok . toResponse |
74 |
| - |
75 |
| -article :: Day -> String -> AppPart Response |
76 |
| -article date slug = do |
77 |
| - language <- languageHeaderM |
78 |
| - -- TODO: onlyOne |
79 |
| - a <- onlyOne $ lift $ getFiltered $ byDateSlug date slug |
80 |
| - articleDisplay language a >>= html |
81 |
| - |
82 |
| -articleList :: (Article -> Bool) -> AppPart Response |
83 |
| -articleList articleFilter = do |
84 |
| - articles <- lift $ getFiltered articleFilter |
85 |
| - language <- languageHeaderM |
86 |
| - articleListDisplay language articles >>= html |
87 |
| - |
88 |
| -meta :: String -> AppPart Response |
89 |
| -meta slug = do |
90 |
| - language <- languageHeaderM |
91 |
| - m <- getMeta slug |
92 |
| - metaDisplay language m >>= html |
| 55 | +-- Run the actual site |
| 56 | +runSite :: IO () |
| 57 | +runSite = do |
| 58 | + address <- siteAddress |
| 59 | + lport <- listenPort |
| 60 | + let conf = nullConf { port = lport } |
| 61 | + -- Manually bind the socket to close it on exception |
| 62 | + bracket |
| 63 | + (bindPort conf) |
| 64 | + close |
| 65 | + (\sock -> simpleHTTPWithSocket' runApp sock conf $ siteHandler address) |
0 commit comments