L'objectif est de créer une application de chat avec un ensemble de fonctionnalités complet en utilisant le temps réel en combinaison avec d'autres services pour stocker, manipuler et partager des données.
Si vous avez des questions, des idées ou vous souhaitez contribuer, veuillez soulever un problème ou nous contacter.
Node 16 installé
Les fonctions Azure fonctionnent à partir de NPM. Pour installer cette course:
npm install -g azure-functions-core-tools@4
un fichier .env dans ./API:
JWT_SIGNING_KEY=key used to sign tokens for users. Can be anything you decide it to be
# Ably
ABLY_API_KEY=YOURKEY:HERE
APP_ID=[YOUR ABLY APP ID](https://faqs.ably.com/how-do-i-find-my-app-id)
CONTROL_KEY=[YOUR ABLY CONTROL KEY](https://ably.com/documentation/control-api#authentication)
# Azure
COSMOS_ENDPOINT=https://yourcosomsdb.documents.azure.com
COSMOS_KEY=ASK FOR THIS OR MAKE YOUR OWN
COSMOS_DATABASE_ID=metadata
AZURE_STORAGE_CONNECTION_STRING=your string here
AZURE_STORAGE_CONTAINER_NAME=container name here
# Auth0
AUTH0_DOMAIN=yourdomain.auth0.com
AUTH0_CLIENTID=yourclientid
AUTH0_REDIRECT_URI=http://localhost:8080/auth0-landing
# from root folder
npm run init # installs node modules for api & integrations
npm run start # runs the dev serverComme le montre le fichier .env, il existe quelques services que vous devrez vous inscrire pour utiliser le projet dans sa forme actuelle.
Afin d'obtenir les informations d'identification habilement, vous devrez d'abord vous inscrire à un compte librement gratuit. Une fois que vous avez un compte habilement, vous pouvez accéder à l'application par défaut générée et obtenir la clé API racine pour elle. Ceci sera utilisé pour l'environnement ABLY_API_KEY . La variable APP_ID ENV doit être définie dans la première partie de votre clé API, avant le Fullstop. Si votre clé API est 12345.jh40fj23jkd0-,32c3-j- , vous devez la régler à 12345 .
Pour le CONTROL_KEY , qui est utilisé pour contrôler la création de clés API, d'applications et de manière plus programmée, vous devrez vous rendre au jeton d'accès en tant qu'utilisateur connecté, et cliquez sur «Créer un nouveau jeton d'accès». Donnez-lui un nom, à partir de la liste déroulante «Compte», sélectionnez le compte qui propose l'application pour laquelle vous disposez d'une clé API et assurez-vous de sélectionner la read:key et write:key des autorisations. Créez le jeton et utilisez sa valeur comme CONTROL_KEY .
COSMOSDB est utilisé pour stocker des données pour cette application. Pour obtenir un compte Azure et créer une ressource COSMOSDB, suivez les étapes du tutoriel de configuration d'Azure. Vous devez définir le COSMOS_ENDPOINT pour être l'URI fourni dans votre nouveau sous-compte. Le COSMOS_KEY est la clé principale du compte COSMOSDB, auquel vous pouvez accéder comme décrit par Azure.
COSMOS_DATABASE_ID est le nom du conteneur que vous utiliserez dans votre compte cosmosdb.
En utilisant le même compte Azure créé pour COSMOSDB, créez un nouveau conteneur de stockage de données. Une fois que vous avez cette configuration, accédez à la section 'Access Keys' dans la barre latérale pour obtenir une connection string pour AZURE_STORAGE_CONNECTION_STRING , puis définissez AZURE_STORAGE_CONTAINER_NAME à tout ce que vous avez nommé le conteneur.
Si vous souhaitez inclure Auth0 comme méthode d'authentification, vous devrez créer un compte AUTH0. Une fois que vous avez un compte Auth0, créez une application avec des «applications Web régulières» sélectionnées comme type d'application. Dans cette application, vous pouvez ensuite copier le domaine pour AUTH0_DOMAIN et le clientId pour l' AUTH0_CLIENTID . L' AUTH0_REDIRECT_URI doit pointer vers l'URI approprié qu'AUTH0, après l'authentification d'un utilisateur, doit rediriger vers. La valeur par défaut devrait fonctionner pour l'exécution localement.
Dans l'onglet Paramètres de votre application AUTH0, faites défiler vers le bas vers la section appelée «Application Uris». Dans ce document, vous devriez voir un champ pour «URL de rappel autorisé» et «URL de déconnexion autorisée». Pour le contexte, le flux d'une page Web utilisant Auth0 est:
Votre site relie un utilisateur à la page de connexion de votre application AUTH0, où il se connecte à la page AUTH0 redirige l'utilisateur vers la page `` rappeler '' de votre site Web lorsque l'utilisateur souhaite se connecter, il est dirigé vers la page de déconnexion de l'application Auth0, puis redirigé vers la page spécifiée dans la requête `` retourner '' passée à la page de connexion
Pour éviter une mauvaise utilisation et abus potentiels, vous devez spécifier les URL que Auth0 peut rediriger. Lorsque vous hébergez cette application de chat localement, elle est hébergée sur localhost: 8080, donc définissez les URL de rappel autorisées sur 'http: // localhost: 8080 / auth0-landing'. Lorsqu'un utilisateur se déconnecte, nous aurons l'utilisateur redirigé vers notre page principale, donc définir les URL de déconnexion autorisées sur 'http: // localhost: 8080 /'.
L'application de chat est composée des éléments suivants:
Archive API pour recevoir des événements de Reactor Ably et maintenir une histoire de chatChat Archive . L'application React est une application par défaut, une seule page. Il utilise un mélange de react-router-dom et un AppProvider personnalisé pour fournir le contexte de sécurité de l'application.
L'application utilise @ Ably-Babs / React-Hooks pour interagir avec les canaux habilement , et l'application est composée de composants fonctionnels React modernes.
Snowpack est le serveur de développement, qui créera de manière transparente le code ES6 pour la production.
Le BFF est une API spécifique à l'application qui contient toute la logique de serveurs pour l'application de chat. Parce qu'il est hébergé sur les applications Web statiques Azure , nous pouvons utiliser les azure-functions-core-tools exécuter le serveur API.
En plus de cela, le runtime des applications Web Azure Static Host -hébergera automatiquement les API pour nous - nous n'avons donc pas à nous soucier de la configuration de l'hébergement. Le BFF est exécuté sur Infrastrucutre sans serveur, et Azure SWA l'a automatiquement à l'échelle pour répondre à la demande.
Pour ajouter de nouveaux points de terminaison API, vous devrez ajouter un nouveau répertoire au dossier api .
Tout d'abord, créez un répertoire pour la nouvelle API - par exemple, api/messages . Ensuite, créez un fichier function.json dans le nouveau répertoire.
{
"bindings" : [
{
"route" : " messages " ,
"authLevel" : " function " ,
"type" : " httpTrigger " ,
"direction" : " in " ,
"name" : " req " ,
"methods" : [ " get " , " post " ]
},
{
"type" : " http " ,
"direction" : " out " ,
"name" : " res "
}
],
"scriptFile" : " ../dist/messages/index.js "
} Ensuite, vous devrez créer votre API TypeScript :
import "../startup" ;
import { Context , HttpRequest } from "@azure/functions" ;
export default async function ( context : Context , req : HttpRequest ) : Promise < void > {
context . res = { status : 200 , body : "I'm an API" } ;
} Cette API sera désormais montée sur http://localhost:8080/api/messages .
Et c'est tout! L'outillage et le SDK détecteront automatiquement votre code lorsque vous le modifiez et reconstruisez vos fonctions pour vous.
L'application utilise l'authentification du jeton JWT entre l'application Web et le BFF. Nous stockons les informations d'identification des utilisateurs et Salted, des mots de passe hachés à un moyen (réalisés avec BCrypt) dans la base de données COSMOSDB.
Lorsqu'un utilisateur authentifie, l'application signe un jeton JWT avec l'ID et le nom d'utilisateur de l'utilisateur qui est ensuite envoyé au BFF dans les demandes ultérieures de données authentifiées. Cela signifie qu'avec une petite quantité de code dans les API, nous pouvons nous assurer que l'utilisateur est ce qu'ils prétendent être et qu'ils ont le droit d'accéder aux données de l'API.
Nous pouvons étendre ce modèle pour inclure une collection de roles pour l'authentification basée sur les réclamations aux ressources de l'application.
Authenticated User Only Nous pouvons créer un appel API authentifié JWT token en utilisant les méthodes de commodité suivantes dans l' BFF API .
import "../startup" ;
import { Context , HttpRequest } from "@azure/functions" ;
import { authorized , ApiRequestContext } from "../common/ApiRequestContext" ;
export default async function ( context : Context , req : HttpRequest ) : Promise < void > {
await authorized ( context , req , ( ) => {
// This code will only run if the user is authenticated
context . res = {
status : 200 ,
body : JSON . stringify ( "I am validated and authenticated" )
} ;
} ) ;
}Si vous souhaitez accéder aux informations des utilisateurs authentifiés dans le cadre de l'un de ces appels d'API, vous pouvez effectuer ce qui suit:
import "../startup" ;
import { Context , HttpRequest } from "@azure/functions" ;
import { authorized , ApiRequestContext } from "../common/ApiRequestContext" ;
export default async function ( context : Context , req : HttpRequest ) : Promise < void > {
await authorized (
context ,
req ,
( { user } : ApiRequestContext ) => {
// user is the userDetails object retrieved from CosmosDb
context . res = {
status : 200 ,
body : JSON . stringify ( "I am validated and authenticated" )
} ;
} ,
true
) ; // <- true to include the userDetails object in the ApiRequestContext
} L'authentification in-app est implémentée dans AppProviders.jsx .
Il fournit un crochet React qui renverra l'objet userDetails si l'utilisateur est authentifié (ainsi que de s'assurer que l'utilisateur est authentifié du tout). Si un utilisateur donné n'est pas authentifié, il sera redirigé vers la page de connexion dans tous les cas.
Étant donné que l' AppProvider s'occupe de l'authentification, vous devrez utiliser des crochets pour accéder aux données des utilisateurs et pour passer des appels API authentifiés dans tous les composants.
Voici un exemple d'accès à l'objet userDetails de l'utilisateur actuellement authentifié.
import { useAuth } from "../../AppProviders" ;
const MyComponent = ( ) => {
const { user } = useAuth ( ) ;
return < div > { user . username } </ div > ;
} ;
export default MyComponent ; Vous pouvez également accéder à une instance de la classe BffApiClient , qui vous permettra de passer des appels API authentifiés et contient déjà le JWT token actuellement connecté dans les utilisateurs.
import { useAuth } from "../../AppProviders" ;
const MyComponent = ( ) => {
const { api } = useAuth ( ) ;
const [ channels , setChannels ] = useState ( [ ] ) ;
useEffect ( ( ) => {
const fetchChannels = async ( ) => {
const response = await api . listChannels ( ) ;
setChannels ( response . channels ) ;
} ;
fetchChannels ( ) ;
} , [ ] ) ;
return < div > ... bind channel data here </ div > ;
} ;
export default MyComponent ; L'exemple ci-dessus utilise le crochet useEffect pour récupérer les canaux lorsque le composant monte - la demande de l'API est faite à l'aide de l'instance api , fournie par le crochet useAuth .
C'est la seule façon de passer des appels API au BFF à partir d'un composant, car il garantira que le jeton JWT est valide et présent.
Si vous ajoutez de nouvelles BFF APIs à l'application, vous devrez implémenter une nouvelle fonction dans /app/src/sdk/BffApiClient.js pour la mettre à la disposition de vos composants.
Ces appels BffApiClient sont simples et ressemblent à ceci:
async listChannels ( ) {
const result = await this . get ( "/api/channels" ) ;
return await result . json ( ) ;
} Un code d'utilité dans le client s'assurera que le JWT token correct est présent lorsque la demande est faite.
Nous utilisons COSMOSDB pour stocker nos métadonnées d'application, car il s'agit d'une base de données évolutive, hautement disponible et gérée que nous n'avons pas à nous administrer. Il peut s'exécuter en mode pré-provisoire ou sans serveur, ce qui contribue à maintenir les coûts bas lorsque l'application n'est pas utilisée (au prix de certaines performances).
Nous utilisons une seule base de données COSMOSDB pour stocker toutes les métadonnées, et à l'intérieur, nous avons créé une collection pour chaque type d'entité que nous stockons.
Par exemple: la collection User , stocke nos enregistrements d'utilisateurs - et peut être interrogé à l'aide de la syntaxe de type SQL. COSMOSDB le rend facile en indexant automatiquement les documents JSON.
Chacune des entités de métadonnées stockées a un champ id et un type , et nous utilisons une classe generic repository ( /api/common/dataaccess/CosmosDbMetadataRepository ) pour charger et enregistrer ces entités.
Pour le développement local, vous pouvez soit utiliser la version hébergée de cloud de COSMOS, soit utiliser l'une des docker container images disponibles pour exécuter une copie locale de la base de données.
Nous utilisons Ably channels pour stocker nos messages de chat et pour pousser les événements vers notre application React. Chaque utilisateur connecté recevra des messages pour les canaux qu'il consulte activement en temps réel, et nous utilisons Channel rewind pour remplir les messages les récemment envoyés.
Les messages peuvent être corrected de manière asyncronique après avoir été reçue - par exemple, pour appliquer le filtrage des blasphèmes ou pour corriger les erreurs d'orthographe. Ces messages de correction feront partie du flux et appliqués rétroactivement dans l'application React. (Développement ultérieur à ce sujet dans les épopées ultérieures)
Cette conception nous permet de défendre des API supplémentaires qui consomment ces événements et de publier leurs propres élaborations sur les canaux auxquels les clients peuvent répondre.
Parce que les événements habilement disparaîtront au fil du temps, nous allons stocker des copies des événements entrants sur chaque chaîne dans nos Chat Archive via l' Archive API .
L' Archive API recevra des messages de réacteur pour tous nos canaux et les ajoutera aux Azure Storage Blobs spécifiques aux canaux. L'API ajoutera un seul fichier jusqu'à ce qu'il atteigne un seuil de taille (~ 500 Ko), puis créera un nouveau fichier pour les messages ultérieurs.
L' Archive API conservera un enregistrement du fichier d'archive actuellement actif dans la Metadata database pour chaque canal.
L' Archive API sera en mesure de mettre à jour un index de recherche au fur et à mesure que les messages sont reçus et archivés pour les exposer plus tard dans la recherche.
Les tests sont écrits en jest avec ts-jest utilisé pour exécuter les tests TypeScript API.