Skip to content

Commit bdbc409

Browse files
authored
Merge pull request #38 from diogob/lts-12.14
Lts 12.14
2 parents 384298c + 5e2245c commit bdbc409

File tree

12 files changed

+59
-135
lines changed

12 files changed

+59
-135
lines changed

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Use Alpine Linux as base image
2-
FROM alpine:3.6
2+
FROM alpine:3.8
33

44
# Install libpq and gmp dependencies (dynamic libraries required by the project)
55
RUN apk update && apk add libpq gmp libffi

Dockerfile.build

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM alpine:3.6
1+
FROM alpine:3.8
22

33
RUN echo "@testing http://nl.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories
44
RUN apk -U add shadow@testing

README.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,17 @@ If you have any problems processing any Postgres related library on a Mac, try i
3838

3939
After the build you should be able to run the server using `~/.local/bin/postgres-websockets` (you can add `~/.local/bin` to your PATH variable):
4040

41-
To run the example bellow you will need a PostgreSQL server running on port 5432 of your localhost. You can also change the database connection string editting the `sample.conf` file.
41+
To run the example bellow you will need a PostgreSQL server running on port 5432 of your localhost.
4242
```bash
43-
~/.local/bin/postgres-websockets sample.conf
43+
PGWS_DB_URI="postgres://localhost:5432/postgres" PGWS_JWT_SECRET="auwhfdnskjhewfi34uwehdlaehsfkuaeiskjnfduierhfsiweskjcnzeiluwhskdewishdnpwe" ~/.local/bin/postgres-websockets
44+
postgres-websockets <version> / Connects websockets to PostgreSQL asynchronous notifications.
4445
Listening on port 3000
4546
```
4647

48+
You can also use the provided [sample-env](./sample-env) file to export the needed variables:
49+
```bash
50+
source sample-env && ~/.local/bin/postgres-websockets
51+
```
4752
After running the above command, open your browser on http://localhost:3000 to see an example of usage.
4853

4954
The sample config file provided in the [sample.conf](https://github.com/diogob/postgres-websockets/tree/master/sample.conf) file comes with a jwt secret just for testing and is used in the sample client.

app/Config.hs

Lines changed: 13 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -20,22 +20,10 @@ module Config ( prettyVersion
2020
)
2121
where
2222

23-
import System.IO.Error (IOError)
24-
import Control.Applicative
25-
import qualified Data.Configurator as C
26-
import qualified Data.Configurator.Parser as C
27-
import qualified Data.Configurator.Types as C
28-
import Data.Monoid
29-
import Data.Scientific (floatingOrInteger)
30-
import Data.Text (intercalate, lines)
31-
import Data.Text.Encoding (encodeUtf8)
23+
import Env
24+
import Data.Text (intercalate)
3225
import Data.Version (versionBranch)
33-
import Options.Applicative hiding (str)
3426
import Paths_postgres_websockets (version)
35-
import System.IO (hPrint)
36-
import Text.Heredoc
37-
import Text.PrettyPrint.ANSI.Leijen hiding ((<>), (<$>))
38-
import qualified Text.PrettyPrint.ANSI.Leijen as L
3927
import Protolude hiding (intercalate, (<>))
4028

4129
-- | Config file settings for the server
@@ -54,91 +42,18 @@ data AppConfig = AppConfig {
5442
prettyVersion :: Text
5543
prettyVersion = intercalate "." $ map show $ versionBranch version
5644

57-
-- | Function to read and parse options from the command line
45+
-- | Function to read and parse options from the environment
5846
readOptions :: IO AppConfig
59-
readOptions = do
60-
-- First read the config file path from command line
61-
cfgPath <- customExecParser parserPrefs opts
62-
-- Now read the actual config file
63-
conf <- catch
64-
(C.readConfig =<< C.load [C.Required cfgPath])
65-
configNotfoundHint
66-
67-
let (mAppConf, errs) = flip C.runParserM conf $ do
68-
-- db ----------------
69-
cDbUri <- C.key "db-uri"
70-
cPool <- fromMaybe 10 . join . fmap coerceInt <$> C.key "db-pool"
71-
-- server ------------
72-
cPath <- C.key "server-root"
73-
cHost <- fromMaybe "*4" . mfilter (/= "") <$> C.key "server-host"
74-
cPort <- fromMaybe 3000 . join . fmap coerceInt <$> C.key "server-port"
75-
cAuditC <- C.key "audit-channel"
76-
cChannel <- case cAuditC of
77-
Just c -> fromMaybe c . mfilter (/= "") <$> C.key "listen-channel"
78-
Nothing -> C.key "listen-channel"
79-
-- jwt ---------------
80-
cJwtSec <- C.key "jwt-secret"
81-
cJwtB64 <- fromMaybe False <$> C.key "secret-is-base64"
82-
83-
return $ AppConfig cDbUri cPath cHost cPort cChannel (encodeUtf8 cJwtSec) cJwtB64 cPool
84-
85-
case mAppConf of
86-
Nothing -> do
87-
forM_ errs $ hPrint stderr
88-
exitFailure
89-
Just appConf ->
90-
return appConf
91-
92-
where
93-
coerceInt :: (Read i, Integral i) => C.Value -> Maybe i
94-
coerceInt (C.Number x) = rightToMaybe $ floatingOrInteger x
95-
coerceInt (C.String x) = readMaybe $ toS x
96-
coerceInt _ = Nothing
97-
98-
opts = info (helper <*> pathParser) $
99-
fullDesc
100-
<> progDesc (
101-
"postgres-websockets "
102-
<> toS prettyVersion
103-
<> " / Connects websockets to PostgreSQL asynchronous notifications."
104-
)
105-
<> footerDoc (Just $
106-
text "Example Config File:"
107-
L.<> nest 2 (hardline L.<> exampleCfg)
108-
)
109-
110-
parserPrefs = prefs showHelpOnError
111-
112-
configNotfoundHint :: IOError -> IO a
113-
configNotfoundHint e = die $ "Cannot open config file:\n\t" <> show e
114-
115-
missingKeyHint :: C.KeyError -> IO a
116-
missingKeyHint (C.KeyError n) =
117-
die $
118-
"Required config parameter \"" <> n <> "\" is missing or of wrong type.\n"
119-
120-
exampleCfg :: Doc
121-
exampleCfg = vsep . map (text . toS) . lines $
122-
[str|db-uri = "postgres://user:pass@localhost:5432/dbname"
123-
|db-pool = 10
124-
|
125-
|server-root = "./client-example"
126-
|server-host = "*4"
127-
|server-port = 3000
128-
|listen-channel = "postgres-websockets-listener"
129-
|
130-
|## choose a secret to enable JWT auth
131-
|## (use "@filename" to load from separate file)
132-
|# jwt-secret = "foo"
133-
|# secret-is-base64 = false
134-
|]
135-
136-
137-
pathParser :: Parser FilePath
138-
pathParser =
139-
strArgument $
140-
metavar "FILENAME" <>
141-
help "Path to configuration file"
47+
readOptions =
48+
Env.parse (header "You need to configure some environment variables to start the service.") $
49+
AppConfig <$> var (str <=< nonempty) "PGWS_DB_URI" (help "String to connect to PostgreSQL")
50+
<*> var str "PGWS_ROOT_PATH" (def "./" <> helpDef show <> help "Root path to serve static files")
51+
<*> var str "PGWS_HOST" (def "*4" <> helpDef show <> help "Address the server will listen for websocket connections")
52+
<*> var auto "PGWS_PORT" (def 3000 <> helpDef show <> help "Port the server will listen for websocket connections")
53+
<*> var str "PGWS_LISTEN_CHANNEL" (def "postgres-websockets-listener" <> helpDef show <> help "Master channel used in the database to send or read messages in any notification channel")
54+
<*> var str "PGWS_JWT_SECRET" (help "Secret used to sign JWT tokens used to open communications channels")
55+
<*> var auto "PGWS_JWT_SECRET_BASE64" (def False <> helpDef show <> help "Indicate whether the JWT secret should be decoded from a base64 encoded string")
56+
<*> var auto "PGWS_POOL_SIZE" (def 10 <> helpDef show <> help "How many connection to the database should be used by the connection pool")
14257

14358
data PgVersion = PgVersion {
14459
pgvNum :: Int32

app/Main.hs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import qualified Data.ByteString.Base64 as B64
1313
import Data.String (IsString (..))
1414
import Data.Text (pack, replace, strip, stripPrefix)
1515
import Data.Text.Encoding (encodeUtf8, decodeUtf8)
16-
import qualified Hasql.Query as H
16+
import qualified Hasql.Statement as H
1717
import qualified Hasql.Session as H
1818
import qualified Hasql.Decoders as HD
1919
import qualified Hasql.Encoders as HE
@@ -27,19 +27,23 @@ import System.IO (BufferMode (..),
2727

2828
isServerVersionSupported :: H.Session Bool
2929
isServerVersionSupported = do
30-
ver <- H.query () pgVersion
30+
ver <- H.statement () pgVersion
3131
return $ ver >= pgvNum minimumPgVersion
3232
where
3333
pgVersion =
34-
H.statement "SELECT current_setting('server_version_num')::integer"
35-
HE.unit (HD.singleRow $ HD.value HD.int4) False
34+
H.Statement "SELECT current_setting('server_version_num')::integer"
35+
HE.unit (HD.singleRow $ HD.column HD.int4) False
3636

3737
main :: IO ()
3838
main = do
3939
hSetBuffering stdout LineBuffering
4040
hSetBuffering stdin LineBuffering
4141
hSetBuffering stderr NoBuffering
4242

43+
putStrLn $ ("postgres-websockets " :: Text)
44+
<> prettyVersion
45+
<> " / Connects websockets to PostgreSQL asynchronous notifications."
46+
4347
conf <- loadSecretFile =<< readOptions
4448
let host = configHost conf
4549
port = configPort conf

circle.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ dependencies:
33
- "~/.stack"
44
- ".stack-work"
55
pre:
6-
- curl -L https://github.com/commercialhaskell/stack/releases/download/v1.1.2/stack-1.1.2-linux-x86_64.tar.gz | tar zx -C /tmp
7-
- sudo mv /tmp/stack-1.1.2-linux-x86_64/stack /usr/bin
6+
- curl -L https://github.com/commercialhaskell/stack/releases/download/v1.9.1/stack-1.9.1-linux-x86_64.tar.gz | tar zx -C /tmp
7+
- sudo mv /tmp/stack-1.9.1-linux-x86_64/stack /usr/bin
88
override:
99
- stack setup
1010
- rm -fr $(stack path --dist-dir) $(stack path --local-install-root)

postgres-websockets.cabal

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: postgres-websockets
2-
version: 0.4.2.1
2+
version: 0.5.0.0
33
synopsis: Middleware to map LISTEN/NOTIFY messages to Websockets
44
description: Please see README.md
55
homepage: https://github.com/diogob/postgres-websockets#readme
@@ -22,10 +22,10 @@ library
2222
, PostgresWebsockets.HasqlBroadcast
2323
, PostgresWebsockets.Claims
2424
build-depends: base >= 4.7 && < 5
25-
, hasql-pool >= 0.4 && < 0.5
25+
, hasql-pool >= 0.4 && < 0.6
2626
, text >= 1.2 && < 2
2727
, wai >= 3.2 && < 4
28-
, websockets >= 0.9 && < 0.11
28+
, websockets >= 0.9 && < 0.13
2929
, wai-websockets >= 3.0 && < 4
3030
, http-types >= 0.9
3131
, bytestring >= 0.10
@@ -53,6 +53,7 @@ executable postgres-websockets
5353
hs-source-dirs: app
5454
main-is: Main.hs
5555
other-modules: Config
56+
, Paths_postgres_websockets
5657
ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N
5758
build-depends: base >= 4.7 && < 5
5859
, transformers >= 0.4 && < 0.6
@@ -63,16 +64,16 @@ executable postgres-websockets
6364
, protolude >= 0.2
6465
, base64-bytestring
6566
, bytestring
66-
, configurator-ng >= 0.0.0.1
67+
, configurator
6768
, optparse-applicative
68-
, scientific >= 0.3.5.0
6969
, text
7070
, time
7171
, wai
7272
, wai-extra
7373
, wai-app-static
7474
, heredoc
7575
, ansi-wl-pprint
76+
, envparse
7677
default-language: Haskell2010
7778
default-extensions: OverloadedStrings, NoImplicitPrelude, QuasiQuotes
7879

sample.conf renamed to sample-env

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
11
## PostgreSQL URI where the server will connect to issue NOTIFY and LISTEN commands
2-
db-uri = "postgres://localhost:5432/postgres"
2+
export PGWS_DB_URI="postgres://localhost:5432/postgres"
33

44
## Size of connection pool used to issue notify commands (LISTEN commands are always issued on the same connection that is not part of the pool).
5-
db-pool = 10
5+
export PGWS_POOL_SIZE=10
66

7-
## server-root can be used to serve some static files for convenience when testing.
8-
server-root = "./client-example"
7+
## Root path can be used to serve some static files for convenience when testing.
8+
export PGWS_ROOT_PATH="./client-example"
99

1010
## Sends a copy of every message received from websocket clients to the channel specified bellow as an aditional NOTIFY command.
11-
listen-channel = "postgres-websockets-listener"
11+
export PGWS_LISTEN_CHANNEL="postgres-websockets-listener"
1212

1313
## Host and port on which the websockets server (and the static files server) will be listening.
14-
server-host = "*4"
15-
server-port = 3000
14+
export PGWS_HOST="*4"
15+
export PGWS_PORT=3000
1616

1717
## choose a secret to enable JWT auth
1818
## (use "@filename" to load from separate file)
19-
jwt-secret = "auwhfdnskjhewfi34uwehdlaehsfkuaeiskjnfduierhfsiweskjcnzeiluwhskdewishdnpwe"
20-
secret-is-base64 = false
19+
export PGWS_JWT_SECRET="auwhfdnskjhewfi34uwehdlaehsfkuaeiskjnfduierhfsiweskjcnzeiluwhskdewishdnpwe"
20+
export PGWS_JWT_SECRET_BASE64=False

src/PostgresWebsockets/Claims.hs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,8 @@ claims2map = val2map . toJSON
8989
hs256jwk :: ByteString -> JWK
9090
hs256jwk key =
9191
fromKeyMaterial km
92-
& jwkUse .~ Just Sig
93-
& jwkAlg .~ (Just $ JWSAlg HS256)
92+
& jwkUse ?~ Sig
93+
& jwkAlg ?~ JWSAlg HS256
9494
where
9595
km = OctKeyMaterial (OctKeyParameters (JOSE.Types.Base64Octets key))
9696

src/PostgresWebsockets/Database.hs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ module PostgresWebsockets.Database
1212

1313
import Protolude hiding (replace)
1414
import Hasql.Pool (Pool, UsageError, use)
15-
import Hasql.Session (sql, run, query)
15+
import Hasql.Session (sql, run, statement)
1616
import qualified Hasql.Session as S
17-
import Hasql.Query (statement)
17+
import qualified Hasql.Statement as HST
1818
import Hasql.Connection (Connection, withLibPQConnection)
1919
import qualified Hasql.Decoders as HD
2020
import qualified Hasql.Encoders as HE
@@ -45,19 +45,19 @@ toPgIdentifier x = PgIdentifier $ "\"" <> strictlyReplaceQuotes (trimNullChars x
4545
-- | Given a Hasql Pool, a channel and a message sends a notify command to the database
4646
notifyPool :: Pool -> ByteString -> ByteString -> IO (Either Error ())
4747
notifyPool pool channel mesg =
48-
mapError <$> use pool (query (toS channel, toS mesg) callStatement)
48+
mapError <$> use pool (statement (toS channel, toS mesg) callStatement)
4949
where
5050
mapError :: Either UsageError () -> Either Error ()
5151
mapError = mapLeft (NotifyError . show)
52-
callStatement = statement ("SELECT pg_notify" <> "($1, $2)") encoder HD.unit False
53-
encoder = contramap fst (HE.value HE.text) <> contramap snd (HE.value HE.text)
52+
callStatement = HST.Statement ("SELECT pg_notify" <> "($1, $2)") encoder HD.unit False
53+
encoder = contramap fst (HE.param HE.text) <> contramap snd (HE.param HE.text)
5454

5555
-- | Given a Hasql Connection, a channel and a message sends a notify command to the database
5656
notify :: Connection -> PgIdentifier -> ByteString -> IO (Either Error ())
5757
notify con channel mesg =
5858
mapError <$> run (sql ("NOTIFY " <> fromPgIdentifier channel <> ", '" <> mesg <> "'")) con
5959
where
60-
mapError :: Either S.Error () -> Either Error ()
60+
mapError :: Either S.QueryError () -> Either Error ()
6161
mapError = mapLeft (NotifyError . show)
6262

6363
-- | Given a Hasql Connection and a channel sends a listen command to the database

0 commit comments

Comments
 (0)