Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Chat app using Azure Web PubSub, Static Web Apps and other Azure services

License

NotificationsYou must be signed in to change notification settings

benc-uk/chatr

Repository files navigation

This is a demonstration & sample application designed to be a simple multi-user web based chat system.
It provides persistent group chats, user to user private chats, a user list, idle (away from keyboard) detection and several other features.

It is built on several Azure technologies, including:Web PubSub, Functions, Static Web Apps andTable Storage

👀 Note. This was created as a personal project, created to aid learning while building something interesting. The code comes with all the caveats you might expect from such a project.

Goals:

  • Learn about using websockets
  • Write a 'fun' thing
  • Try out the newAzure Web PubSub service
  • Use the authentication features ofAzure Static Web Apps
  • Deploy everything usingAzure Bicep
  • No connection strings, managed identity for everything

Use cases & key features:

  • Sign-in with Microsoft, Twitter or GitHub accounts
  • Realtime chat with users
  • Shared group chats, only the creator can remove the chat
  • Detects where users are idle and away from keyboard (default is one minute)
  • Private 'user to user' chats, with notifications and popups

🌐 Live Version

Try the application out here:chatr.benco.io

🖼️ Screenshot

🗺️ Architecture

🧑‍💻 Client / Frontend

This is the main web frontend as used by end users via the browser.

The source for this is found inclient/ and consists of a static standalone pure ES6 JS application, no bundling or Node.js is required. It is written usingVue.js as a supporting framework, andBulma as a CSS framework.

Some notes:

  • Vue.js is used as a browser side library loaded from CDN as a ESM module, this is an elegant & lightweight approach supported by modern browsers, rather than the usual style of SPA app which requires Node and webpack etc.
  • No bundling/build. ES6 modules are used so the various JS files can use import/export without the need to bundle.
  • client/js/app.js shows how to create a Vue.js app with child components using this approach. The majority of client logic is here.
  • client/js/components/chat.js is a Vue.js component used to host each chat tab in the application
  • The special.auth/ endpoint provided by Static Web Apps is used to sign users in and fetch their user details, such as userId.

⛽ Server

This is the backend, handling websocket events to and from Azure Web PubSub, and providing REST API for some operations.

The source for this is found inapi/ and consists of a Node.js Azure Function App using the v4 programming model. It connects to Azure Table Storage to persist group chat and user data (Table Storage was picked as it's simple & cheap). This is not hosted in a standalone Azure Function App but insteaddeployed into the Static Web App as part of it's serverless API support

There are four HTTP functions all served from the default/api/ path

  • eventHandler - Webhook receiver for "upstream" events sent from Azure Web PubSub service, contains the majority of application logic. Not called directly by the client, only Azure WebPub Sub.
  • getToken - Called by the client to get an access token and URL to connect via WebSockets to the Azure Web PubSub service. Must be called with userId in the URL query, e.g. GET/api/getToken?userId={user}
  • users - Returns a list of signed in users from state
  • chats - Returns a list of active group chats from state

State is handled withstate.js which is an ES6 module exporting functions supporting state CRUD for users and chats. This module carries out all the interaction with Azure Tables, and provides a relatively transparent interface, so a different storage backend could be swapped in.

WebSocket & API Message Flows

There is two way message flow between clients and the server viaAzure Web PubSub and event handlers

The json.webpubsub.azure.v1 subprotocol is used rather than basic WebSockets, this provides a number of features: users can be added to groups, clients can send custom events (usingtype: event), and also send messages direct to other clients without going via the server (usingtype: sendToGroup)

Notes:

  • Chat IDs are simply randomly generated GUIDs, and these correspond 1:1 with "groups" in the subprotocol.
  • Private chats are a special case, they are not persisted in state, and they do not triggerchatCreated events. Also the user doesn't issue ajoinChat event to join them, that is handled by the server as a kind of "push" to the clients.
  • User IDs are simply strings which are considered to be unique, this could be improved, e.g. with prefixing.

Client Messaging

Events & chat are sent using thejson.webpubsub.azure.v1 subprotocol

Chat messages sent from the client usesendToGroup and a custom JSON payload with three fieldsmessage,fromUserId &fromUserName, these messages are relayed client to client by Azure Web PubSub, the server is never notified of them:

{type: 'sendToGroup',group:<chatId>,dataType: 'json',data: {message:<messagetext>,fromUserId:<userId>,fromUserName:<userName>,  },}

Events destined for the backend server are sent as WebSocket messages from the client via the same subprotocol with theevent type, and an application specific sub-type, e.g.

{type: 'event',event: 'joinChat',dataType: 'text',data:<chatId>,}

The types of events are:

  • createChat - Request the server you want to create a group chat
  • createPrivateChat - Request the server you want to create a private chat
  • joinChat - To join a chat, the server will add user to the group for that chatId
  • leaveChat - To leave a group chat
  • deleteChat - Called from a chat owner to delete a chat
  • userEnterIdle - Let the server know user is now idle
  • userExitIdle - Let the server know user is no longer idle

The backend APIeventHandler function has cases for each of these user events, along with handlers for connection & disconnection system events.

Server Messaging

Messages sent from the server have a custom Chatr app specific payload as follows:

{chatEvent:<eventType>,data:<JSONobjecttypedependant>}

WhereeventType is one of:

  • chatCreated - Let all users know a new group chat has been created
  • chatDeleted - Let all users know a group chat has been removed
  • userOnline - Let all users know a user has come online
  • userOffline - Let all users know a user has left
  • joinPrivateChat - Sent to both the initiator and recipient of a private chat
  • userIsIdle - Sent to all users when a user enters idle state
  • userNotIdle - Sent to all users when a user exits idle state

The client code inclient/js/app.js handles these messages as they are received by the client, and reacts accordingly.

✨ Some Notes on Design and Service Choice

The plan of this project was to useAzure Web PubSub andAzure Static Web Apps, and to host the server side component as a set of serverless HTTP functions inAzure Functions.Azure Static Web Apps was selected because it hasamazing support for codeless and config-less user sign-in and auth, which I wanted to leverage.

Some comments on this approach:

  • New: Originally the fully managed API support in Static Web Apps was used, however as the project switched to using managed identity for everything, a side effect of this was a need to move to a standalone external Function App,as managed identity is not supported in managed mode.
  • A decision was made to create a HTTP function to act as a webhook event handler instead of using the providedwebPubSubConnection binding. This is partly historical now (see above bullet), but it sill remains a valid approach. For sending messages back to Web PubSub, the server SDK can simply be used within the function code rather than using thewebPubSub output binding.
  • Table Storage was picked for persisting state as it has a good JS SDK (the new SDK in @azure/data-table was used), it's extremely lightweight and cheap and was good enough for this project, see details below

📚 State & Entity Design

State in Azure Tables consists of two tables (collections) namedchats andusers

Chats Table

As each chat contains nested objects inside the members field, each chat is stored as a JSON string in a field calleddata. The PartitionKey is not used and hardcoded to a string "chatr". The RowKey and the id field inside the data object are the same.

  • PartitionKey: "chatr"
  • RowKey: The chatId (random GUID created client side)
  • data: JSON stringified chat entity

Example of a chat data entity

{"id":"eab4b030-1a3d-499a-bd89-191578395910","name":"This is a group chat","members": {"0987654321": {"userId":"0987654321","userName":"Another Guy"    },"1234567890": {"userId":"1234567890","userName":"Ben"    }  },"owner":"1234567890"}

Users Table

Users are stored as entities with the fields (columns) described below. As there are no nested fields, there is no need to encode as a JSON string. Again the PartitionKey is not used and hardcoded to a string "chatr".

  • PartitionKey: "chatr"
  • RowKey: TheuserId field returned from Static Web Apps auth endpoint
  • userName: The username (could be email address or handle) of the user
  • userProvider: Which auth provided the user signed in withtwitter,aad orgithub
  • idle: Boolean, indicating if the user us currently idle

🛠️ Running and Deploying the App

Working Locally

See makefile

$ makehelp                 💬 This help messagelint                 🔎 Lint & format, will not fix but sets exit code on errorlint-fix             📜 Lint & format, will try to fix errors and modify coderun                  🏃 Run server locally using SWA CLIclean                🧹 Clean up projectdeploy-infra         🚀 Deploy required infra in Azure using Bicepdeploy-api           🚀 Deploy API to Azure using Function Core Toolsdeploy-client        🚀 Deploy client to Azure using SWA CLIdeploy               🚀 Deploy everything!tunnel               🚇 Start AWPS local tunnel tool for local development

Deploying to Azure

Deployment is slightly complex due to the number of components and the configuration between them. The makefile targetdeploy should deploy everything for you in a single step using Bicep templates in thedeploy/ folder

See readme in deploy folder for details and instructions

Running Locally

This requires a little effort as the Azure Web PubSub service needs to be able call the HTTP endpoint on your local machine, plus several role assignments & configs need to be setup, see below. The fabulous Azure Web PubSub local tunnel tool does a great job of providing a way to to tunnel the Azure Web PubSub service to your local machine, so you can run the app locally and test it.

When running locally the Static Web Apps CLI is used and this provides a nice fake user authentication endpoint for us and will also run the API.

Pre-Reqs

If these pre-reqs look a little daunting, don't worry, just use the dev container in the repo, this has everything you need to run and deploy the app.

Summary

A short summary of the steps to getting it running:

  • Deploy anAzure Storage account, ensure it has public network access.
  • Deploy anAzure Web Pub Sub instance into the same resource group, also ensure it has public access.
  • Role assignments:
    • Assign yourself the 'Web PubSub Service Owner' role on the Web Pub Sub resource
    • Assign yourself the 'Storage Table Data Contributor' role on the Storage Account
  • Copyapi/local.settings.sample.json toapi/local.settings.json and edit the required settings values.
  • InAzure Web Pub Sub settings. Go into the 'Settings' section
    • Add a hub named"chat" (don't pick a different name, and don't include the quotes)
    • Add an event handler, by clicking 'Add':
      • In 'URL Template Type' select "Tunnel traffic to local"
      • For the URL template set it to"api/eventHandler" (no quotes)
      • Under system events tickconnected anddisconnected
      • Leave everything else alone :)
  • Check the values at the top of themakefile
    • AZURE_PREFIX should be the name of theAzure Web Pub Sub resource
    • AZURE_RESGRP should be the resource group you deployed into
    • Rather than edit themakefile you can pass these values in after the make command or set them as env vars.
  • Make sure you have the Azure CLI logged in to your account, and the correct subscription selected.
  • Runmake run
  • Open a second terminal/shell and runmake tunnel
  • Openhttp://localhost:4280/

🥲 Known Issues

  • Won't run in Firefox as top level await is not yet supported

About

Chat app using Azure Web PubSub, Static Web Apps and other Azure services

Topics

Resources

License

Stars

Watchers

Forks

Contributors4

  •  
  •  
  •  
  •  

[8]ページ先頭

©2009-2025 Movatter.jp