1
1
module Main where
2
2
3
- import Protolude hiding (replace )
4
- import PostgresWebsockets
5
- import Config (AppConfig (.. ),
6
- PgVersion (.. ),
7
- minimumPgVersion ,
8
- prettyVersion ,
9
- readOptions )
3
+ import Protolude
4
+ import PostgresWebsockets
10
5
11
- import qualified Data.ByteString as BS
12
- import qualified Data.ByteString.Base64 as B64
13
- import Data.String (IsString (.. ))
14
- import Data.Text (pack , replace , strip , stripPrefix )
15
- import Data.Text.Encoding (encodeUtf8 , decodeUtf8 )
16
- import qualified Hasql.Statement as H
17
- import qualified Hasql.Session as H
18
- import qualified Hasql.Decoders as HD
19
- import qualified Hasql.Encoders as HE
20
- import qualified Hasql.Pool as P
21
- import Network.Wai.Application.Static
22
- import Data.Time.Clock (UTCTime , getCurrentTime )
23
- import Control.AutoUpdate ( defaultUpdateSettings
24
- , mkAutoUpdate
25
- , updateAction
26
- )
27
-
28
- import Network.Wai (Application , responseLBS )
29
- import Network.HTTP.Types (status200 )
30
- import Network.Wai.Handler.Warp
31
- import Network.Wai.Middleware.RequestLogger (logStdout )
32
- import System.IO (BufferMode (.. ),
33
- hSetBuffering )
34
-
35
- isServerVersionSupported :: H. Session Bool
36
- isServerVersionSupported = do
37
- ver <- H. statement () pgVersion
38
- return $ ver >= pgvNum minimumPgVersion
39
- where
40
- pgVersion =
41
- H. Statement " SELECT current_setting('server_version_num')::integer"
42
- HE. noParams (HD. singleRow $ HD. column $ HD. nonNullable HD. int4) False
6
+ import System.IO (BufferMode (.. ), hSetBuffering )
43
7
44
8
main :: IO ()
45
9
main = do
@@ -51,67 +15,5 @@ main = do
51
15
<> prettyVersion
52
16
<> " / Connects websockets to PostgreSQL asynchronous notifications."
53
17
54
- conf <- loadSecretFile =<< readOptions
55
- shutdownSignal <- newEmptyMVar
56
- let host = configHost conf
57
- port = configPort conf
58
- listenChannel = toS $ configListenChannel conf
59
- pgSettings = toS (configDatabase conf)
60
- waitForShutdown cl = void $ forkIO (takeMVar shutdownSignal >> cl >> die " Shutting server down..." )
61
-
62
- appSettings = setHost ((fromString . toS) host)
63
- . setPort port
64
- . setServerName (toS $ " postgres-websockets/" <> prettyVersion)
65
- . setTimeout 3600
66
- . setInstallShutdownHandler waitForShutdown
67
- . setGracefulShutdownTimeout (Just 5 )
68
- $ defaultSettings
69
-
70
- putStrLn $ (" Listening on port " :: Text ) <> show (configPort conf)
71
-
72
- let shutdown = putErrLn (" Broadcaster connection is dead" :: Text ) >> putMVar shutdownSignal ()
73
- pool <- P. acquire (configPool conf, 10 , pgSettings)
74
- multi <- newHasqlBroadcaster shutdown listenChannel pgSettings
75
- getTime <- mkGetTime
76
-
77
- runSettings appSettings $
78
- postgresWsMiddleware getTime listenChannel (configJwtSecret conf) pool multi $
79
- logStdout $ maybe dummyApp staticApp' (configPath conf)
80
-
81
- where
82
- mkGetTime :: IO (IO UTCTime )
83
- mkGetTime = mkAutoUpdate defaultUpdateSettings {updateAction = getCurrentTime}
84
- staticApp' :: Text -> Application
85
- staticApp' = staticApp . defaultFileServerSettings . toS
86
- dummyApp :: Application
87
- dummyApp _ respond =
88
- respond $ responseLBS status200 [(" Content-Type" , " text/plain" )] " Hello, Web!"
89
-
90
- loadSecretFile :: AppConfig -> IO AppConfig
91
- loadSecretFile conf = extractAndTransform secret
92
- where
93
- secret = decodeUtf8 $ configJwtSecret conf
94
- isB64 = configJwtSecretIsBase64 conf
95
-
96
- extractAndTransform :: Text -> IO AppConfig
97
- extractAndTransform s =
98
- fmap setSecret $ transformString isB64 =<<
99
- case stripPrefix " @" s of
100
- Nothing -> return . encodeUtf8 $ s
101
- Just filename -> chomp <$> BS. readFile (toS filename)
102
- where
103
- chomp bs = fromMaybe bs (BS. stripSuffix " \n " bs)
104
-
105
- -- Turns the Base64url encoded JWT into Base64
106
- transformString :: Bool -> ByteString -> IO ByteString
107
- transformString False t = return t
108
- transformString True t =
109
- case B64. decode $ encodeUtf8 $ strip $ replaceUrlChars $ decodeUtf8 t of
110
- Left errMsg -> panic $ pack errMsg
111
- Right bs -> return bs
112
-
113
- setSecret bs = conf {configJwtSecret = bs}
114
-
115
- -- replace: Replace every occurrence of one substring with another
116
- replaceUrlChars =
117
- replace " _" " /" . replace " -" " +" . replace " ." " ="
18
+ conf <- loadConfig
19
+ void $ serve conf
0 commit comments