Skip to content

Commit 995683e

Browse files
authored
Merge pull request #67 from diogob/metadata-channel
Add a configuration option to open a channel for metadata
2 parents 9f2cfc4 + 444fd89 commit 995683e

File tree

14 files changed

+186
-115
lines changed

14 files changed

+186
-115
lines changed

CHANGELOG.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
11
# CHANGELOG
22

3+
## 0.10.0.0
4+
5+
- Add `PGWS_META_CHANNEL` to configure optional metadata channel to send events from the server. Initially the oply event is `ConnectionOpen`.
6+
- Add property `event` to message JSON. Two possible values so far: `ConnectionOpen` and `WebsocketMessage`.
7+
- Breaking change: the property `channel` is not appended to claims anymore. If `channel` is in the original token claims it will still be present.
8+
39
## 0.9.0.0
410

511
- Add @filename semantics to PGWS_DB_URI configiration variable to allow secret management to use a file instead of an environment variable.
6-
- Add PGWS_RETRIES to limit the amount of times the server tries to open a database connection upon startup (defaults to 5). This breaks backward compatibility if you rely on the behaviour of the server to try infitite times.
12+
- Add `PGWS_RETRIES` to limit the amount of times the server tries to open a database connection upon startup (defaults to 5). This breaks backward compatibility if you rely on the behaviour of the server to try infitite times.
713

814
## 0.8.0.1
915

README.md

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,9 +90,10 @@ To use a secure socket (`wss://`) you will need a proxy server like nginx to han
9090
Every message received from the browser will be in JSON format as:
9191
```javascript
9292
{
93-
"claims": { "message_delivered_at": 0.0, "a_custom_claim_from_the_jwt": "your_custom_value" },
93+
"event": "WebsocketMessage",
9494
"channel": "destination_channel",
95-
"payload": "message content"
95+
"payload": "message content",
96+
"claims": { "message_delivered_at": 0.0, "a_custom_claim_from_the_jwt": "your_custom_value" }
9697
}
9798
```
9899

@@ -109,8 +110,19 @@ To send a message to a particular channel on the browser one should notify the p
109110
```sql
110111
SELECT pg_notify(
111112
'postgres-websockets-listener',
112-
json_build_object('channel', 'chat', 'payload', 'test')::text
113+
json_build_object('event', 'WebsocketMessage', 'channel', 'chat', 'payload', 'test')::text
113114
);
114115
```
115116

116117
Where `postgres-websockets-listener` is the database channel used by your instance of postgres-websockets and `chat` is the channel where the browser is connected (the same issued in the JWT used to connect).
118+
119+
## Monitoring Connections
120+
121+
To monitor connection opening one should set the variable `PGWS_META_CHANNEL` which will enable the meta-data messages generation in the server on the channel name specified.
122+
For instamce, if we use the configuration in the [sample-env](./sample-env) we will see messages like the one bellow each time a connection is estabilished (only after the JWT is validated).
123+
124+
```javascript
125+
{"event":"ConnectionOpen","channel":"server-info","payload":"server-info","claims":{"mode":"rw","message_delivered_at":1.602719440727465893e9}}
126+
```
127+
128+
You can monitor these messages on another websocket connection with a proper read token for the channel `server-info` or also having an additional database listener on the `PGWS_LISTEN_CHANNEL`.

client-example/client.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ function jwt() {
6464
}
6565

6666
$(document).ready(function () {
67-
var ws = null;
67+
var ws = null, meta = null;
6868

6969
$('#channel').keyup(updateJWT);
7070
updateJWT();
@@ -74,6 +74,10 @@ $(document).ready(function () {
7474
if(ws === null){
7575
var jwt = $('#jwt').val();
7676
var channel = $('#channel').val();
77+
78+
meta = createWebSocket('/server-info/' + jwt);
79+
meta.onmessage = onMessage('#meta-messages');
80+
7781
if(channel == ""){
7882
ws = createWebSocket('/' + jwt);
7983
} else {

client-example/index.html

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ <h2>Chat</h2>
3434
<h2>Messages sent to chat channel</h2>
3535
<div id="messages">
3636
</div>
37+
<h2>Messages sent to meta channel</h2>
38+
<div id="meta-messages">
39+
</div>
3740
</div>
3841
</div>
3942
</body>

postgres-websockets.cabal

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: postgres-websockets
2-
version: 0.9.0.0
2+
version: 0.10.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
@@ -25,6 +25,7 @@ library
2525
other-modules: Paths_postgres_websockets
2626
, PostgresWebsockets.Server
2727
, PostgresWebsockets.Middleware
28+
, PostgresWebsockets.Context
2829
build-depends: base >= 4.7 && < 5
2930
, hasql-pool >= 0.5 && < 0.6
3031
, text >= 1.2 && < 1.3

sample-env

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ export PGWS_ROOT_PATH="./client-example"
1010
## Sends a copy of every message received from websocket clients to the channel specified bellow as an aditional NOTIFY command.
1111
export PGWS_LISTEN_CHANNEL="postgres-websockets-listener"
1212

13+
## Send postgres-websockets server events to this channel (will be sent both to the database and the connected websocket clients)
14+
export PGWS_META_CHANNEL="server-info"
15+
1316
## Host and port on which the websockets server (and the static files server) will be listening.
1417
export PGWS_HOST="*4"
1518
export PGWS_PORT=3000

src/PostgresWebsockets.hs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,6 @@ module PostgresWebsockets
1111
, postgresWsMiddleware
1212
) where
1313

14-
import PostgresWebsockets.Middleware
15-
import PostgresWebsockets.Server
16-
import PostgresWebsockets.Config
14+
import PostgresWebsockets.Middleware ( postgresWsMiddleware )
15+
import PostgresWebsockets.Server ( serve )
16+
import PostgresWebsockets.Config ( prettyVersion, loadConfig )

src/PostgresWebsockets/Claims.hs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,16 @@ module PostgresWebsockets.Claims
1111
( ConnectionInfo,validateClaims
1212
) where
1313

14-
import Control.Lens
15-
import qualified Crypto.JOSE.Types as JOSE.Types
16-
import Crypto.JWT
17-
import qualified Data.HashMap.Strict as M
18-
import Protolude
14+
import Protolude
15+
import Control.Lens
16+
import Crypto.JWT
1917
import Data.List
2018
import Data.Time.Clock (UTCTime)
19+
import qualified Crypto.JOSE.Types as JOSE.Types
20+
import qualified Data.HashMap.Strict as M
2121
import qualified Data.Aeson as JSON
2222

23+
2324
type Claims = M.HashMap Text JSON.Value
2425
type ConnectionInfo = ([ByteString], ByteString, Claims)
2526

src/PostgresWebsockets/Config.hs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ data AppConfig = AppConfig {
3131
, configHost :: Text
3232
, configPort :: Int
3333
, configListenChannel :: Text
34+
, configMetaChannel :: Maybe Text
3435
, configJwtSecret :: ByteString
3536
, configJwtSecretIsBase64 :: Bool
3637
, configPool :: Int
@@ -68,6 +69,7 @@ readOptions =
6869
<*> var str "PGWS_HOST" (def "*4" <> helpDef show <> help "Address the server will listen for websocket connections")
6970
<*> var auto "PGWS_PORT" (def 3000 <> helpDef show <> help "Port the server will listen for websocket connections")
7071
<*> 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")
72+
<*> optional (var str "PGWS_META_CHANNEL" (help "Websockets channel used to send events about the server state changes."))
7173
<*> var str "PGWS_JWT_SECRET" (help "Secret used to sign JWT tokens used to open communications channels")
7274
<*> var auto "PGWS_JWT_SECRET_BASE64" (def False <> helpDef show <> help "Indicate whether the JWT secret should be decoded from a base64 encoded string")
7375
<*> var auto "PGWS_POOL_SIZE" (def 10 <> helpDef show <> help "How many connection to the database should be used by the connection pool")

src/PostgresWebsockets/Context.hs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
{-|
2+
Module : PostgresWebsockets.Context
3+
Description : Produce a context capable of running postgres-websockets sessions
4+
-}
5+
module PostgresWebsockets.Context
6+
( Context (..)
7+
, mkContext
8+
) where
9+
10+
import Protolude
11+
import Data.Time.Clock (UTCTime, getCurrentTime)
12+
import Control.AutoUpdate ( defaultUpdateSettings
13+
, mkAutoUpdate
14+
, updateAction
15+
)
16+
import qualified Hasql.Pool as P
17+
18+
import PostgresWebsockets.Config ( AppConfig(..) )
19+
import PostgresWebsockets.HasqlBroadcast (newHasqlBroadcaster)
20+
import PostgresWebsockets.Broadcast (Multiplexer)
21+
22+
data Context = Context {
23+
ctxConfig :: AppConfig
24+
, ctxPool :: P.Pool
25+
, ctxMulti :: Multiplexer
26+
, ctxGetTime :: IO UTCTime
27+
}
28+
29+
-- | Given a configuration and a shutdown action (performed when the Multiplexer's listen connection dies) produces the context necessary to run sessions
30+
mkContext :: AppConfig -> IO () -> IO Context
31+
mkContext conf@AppConfig{..} shutdown = do
32+
Context conf
33+
<$> P.acquire (configPool, 10, pgSettings)
34+
<*> newHasqlBroadcaster shutdown (toS configListenChannel) configRetries pgSettings
35+
<*> mkGetTime
36+
where
37+
mkGetTime :: IO (IO UTCTime)
38+
mkGetTime = mkAutoUpdate defaultUpdateSettings {updateAction = getCurrentTime}
39+
pgSettings = toS configDatabase

0 commit comments

Comments
 (0)