Ziel ist es, eine Chat -App mit einer vollständigen Funktion zu erstellen, die in Kombination mit anderen Diensten mithilfe von telefonischem Echtzeit eingestellt ist, um Daten zu speichern, zu manipulieren und zu teilen.
Wenn Sie Fragen, Ideen haben oder einen Beitrag leisten möchten, stellen Sie bitte ein Problem an oder wenden Sie sich an uns.
Knoten 16 installiert
Die Azure fungiert Laufzeit von NPM. So installieren Sie diesen Lauf:
npm install -g azure-functions-core-tools@4
an .env Datei in ./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 serverWie in der .EnV -Datei zu sehen ist, müssen Sie einige Dienste anmelden, mit denen Sie das Projekt in seinem aktuellen Formular verwenden müssen.
Um die schändlichen Anmeldeinformationen zu erhalten, müssen Sie sich zunächst für ein kostenloses Konto anmelden. Sobald Sie ein geschicktes Konto haben, können Sie zur generierten Standard -App wechseln und den Stamm -API -Schlüssel dafür abrufen. Dies wird für die Umgebung ABLY_API_KEY verwendet. Die Variable APP_ID env sollte auf den ersten Teil Ihrer API -Schlüssel vor dem FullStop eingestellt werden. Wenn Ihr API-Schlüssel 12345.jh40fj23jkd0-,32c3-j- ist, sollten Sie sie auf 12345 einstellen.
Für den CONTROL_KEY , der zur Steuerung der Erstellung von API -Schlüssel, Apps und mehr programmatisch verwendet wird, müssen Sie als angemeldete Benutzer zum Access -Token wechseln und auf "Neue Zugriffs -Token erstellen" klicken. Geben Sie ihm einen Namen aus der Dropdown "Konto" aus. Wählen Sie das Konto aus, das die App enthält, für die Sie einen API -Schlüssel haben, und vergewissern Sie sich, dass Sie den read:key und write:key . Erstellen Sie das Token und verwenden Sie seinen Wert als CONTROL_KEY .
CosmosDB wird zum Speichern von Daten für diese App verwendet. Befolgen Sie die Schritte im Setup -Tutorial von Azure, um ein Azure -Konto zu erhalten und eine CosmosDB -Ressource zu erstellen. Sie sollten den COSMOS_ENDPOINT so einstellen, dass Sie in Ihrem neuen Unterrichtungskonsum die URI enthalten sind. Der COSMOS_KEY ist der Hauptschlüssel für das CosmosDB -Konto, auf das Sie wie von Azure beschrieben zugreifen können.
COSMOS_DATABASE_ID ist der Name des Containers, den Sie in Ihrem CosmosDB -Konto verwenden werden.
Erstellen Sie mit dem gleichen Azure -Konto, das für CosmosDB erstellt wurde, einen neuen Datenspeichercontainer. Sobald Sie dieses Setup haben, wechseln Sie in den Abschnitt "Zugriffsschlüssel" in der Seitenleiste, um eine connection string für AZURE_STORAGE_CONNECTION_STRING zu erhalten, und setzen Sie dann AZURE_STORAGE_CONTAINER_NAME auf das, was Sie den Container genannt haben.
Wenn Sie Auth0 als Authentifizierungsmethode einfügen möchten, müssen Sie ein Auth0 -Konto erstellen. Sobald Sie ein Auth0 -Konto haben, erstellen Sie eine Anwendung mit "regulären Webanwendungen", die als Anwendungstyp ausgewählt wurden. In dieser App können Sie dann die Domain für AUTH0_DOMAIN und die ClientID für das AUTH0_CLIENTID kopieren. Das AUTH0_REDIRECT_URI sollte auf den entsprechenden URI verweisen, dass Auth0 nach der Authentifizierung eines Benutzers umleiten sollte. Der Standardwert sollte für das lokale Ausführen funktionieren.
Scrollen Sie auf der Registerkarte "Einstellungen" Ihrer Auth0 -App zum Abschnitt "Anwendungs -URIs" nach unten. Darin sollten Sie ein Feld für "erlaubte Callback -URLs" und "zugelassene Abmelde -URLs" sehen. Für den Kontext lautet der Fluss einer Webseite mit Auth0:
Ihre Site verlinkt einen Benutzer mit der Anmeldeseite Ihrer Auth0 -App, auf der sie sich auf der Auth0 -Seite anmelden, um den Benutzer auf die "Rückruf -Seite" Ihrer Website zurückzutreten, wenn sich der Benutzer anmelden möchte. Sie werden auf die Anmeldeseite der Auth0 -App gerichtet und dann zur Seite "Rückkehr" zurückgeleitet, die auf der Abmeldeseite übergeben wurde, um die Seite "zurückzukehren" weitergeleitet, um auf der Abmeldeseite übergeben zu werden.
Um potenziellem Missbrauch und Missbrauch zu vermeiden, müssen Sie angeben, auf welche URLS -Auth0 sich umleiten kann. Beim lokalen Hosting dieser Chat-App wird sie auf Localhost: 8080 gehostet. Setzen Sie also die zulässigen Callback-URLs auf "http: // localhost: 8080/auth0-landing". Wenn sich ein Benutzer anmeldet, wird der Benutzer zu unserer Hauptseite zurückgeleitet. Setzen Sie also die zulässigen Abmelden -URLs auf 'http: // localhost: 8080/'.
Die Chat -App besteht aus Folgendes:
Archive API um Ereignisse aus dem heiligen Reaktor zu erhalten und einen Chat -Historie aufrechtzuerhaltenChat Archive . Die React -Anwendung ist eine Standardanwendung für einseitige. Es verwendet eine Mischung aus react-router-dom und einem benutzerdefinierten AppProvider , um den Sicherheitskontext für die Anwendung bereitzustellen.
Die App verwendet @form-Labs/React-Hooks, um mit qualifizierten Kanälen zu interagieren, und die Anwendung besteht aus modernen React-funktionellen Komponenten .
Snowpack ist der Entwicklungsserver, der den ES6 -Code für die Produktion transparent erstellt.
Die BFF ist eine anwendungsspezifische API, die die gesamte Serverside -Logik für die Chat -App enthält. Da es in Azure statischen Web-Apps gehostet wird, können wir den azure-functions-core-tools den API-Server verwenden.
Darüber hinaus wird die Laufzeit von Azure Static Web Apps die APIs für uns automatisch veranstalten - daher müssen wir uns keine Sorgen um das Konfigurieren von Hosting machen. Die BFF wird auf serverloser Infrastrucutre ausgeführt, und Azure SWA wird es automatisch skalieren, um die Nachfrage zu befriedigen.
Um neue API -Endpunkte hinzuzufügen, müssen Sie dem api -Ordner ein neues Verzeichnis hinzufügen.
Erstellen Sie zunächst ein Verzeichnis für die neue API - z. B. api/messages . Erstellen Sie dann eine function.json -Datei im neuen Verzeichnis.
{
"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 "
} Als nächstes müssen Sie Ihre TypeScript -API erstellen:
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" } ;
} Diese API wird nun unter http://localhost:8080/api/messages montiert.
Und das war's! Das Werkzeug und SDK erkennen Ihren Code automatisch, während Sie ihn ändern und Ihre Funktionen für Sie neu aufbauen.
Die App verwendet die JWT -Token -Authentifizierung zwischen der Webanwendung und der BFF. Wir speichern Benutzeranmeldeinformationen und salzen, in der CosmosDB -Datenbank eine Wege -Hash -Passwörter (mit BCrypt).
Wenn sich ein Benutzer authentifiziert, unterzeichnet die App ein JWT -Token mit der ID und dem Benutzernamen des Benutzers, der dann in nachfolgenden Anfragen nach authentifizierten Daten an die BFF gesendet wird. Dies bedeutet, dass wir mit einer geringen Menge an Code in der APIs sicherstellen können, dass der Benutzer, der er behauptet, zu sein und dass er Anspruch auf Zugriff auf API -Daten hat.
Wir können dieses Modell um eine Sammlung von roles für die anspruchsbasierte Authentifizierung in den Ressourcen in der Anwendung erweitern.
Authenticated User Only -API -Anrufs Wir können einen JWT token authentifizierten API -Aufruf erstellen, indem wir die folgenden Convenience -Methoden in der BFF API verwenden.
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" )
} ;
} ) ;
}Wenn Sie als Teil eines dieser API -Anrufe auf die authentifizierten Benutzerinformationen zugreifen möchten, können Sie Folgendes ausführen:
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
} Die In-App-Authentifizierung wird in AppProviders.jsx implementiert.
Es bietet einen React -Hook, der das Objekt userDetails zurückgibt, wenn der Benutzer authentifiziert ist (zusammen mit der Sicherstellung, dass der Benutzer überhaupt authentifiziert ist). Wenn ein bestimmter Benutzer nicht authentifiziert ist, werden er in allen Fällen auf die Anmeldeseite umgeleitet.
Da der AppProvider die Authentifizierung kümmert, müssen Sie Hooks verwenden, um auf Benutzerdaten zuzugreifen und authentifizierte API -Aufrufe in Komponenten zu tätigen.
Hier ist ein Beispiel für den Zugriff auf das Objekt des userDetails des aktuell authentifizierten Benutzer.
import { useAuth } from "../../AppProviders" ;
const MyComponent = ( ) => {
const { user } = useAuth ( ) ;
return < div > { user . username } </ div > ;
} ;
export default MyComponent ; Sie können auch auf eine Instanz der BffApiClient -Klasse zugreifen, mit der Sie authentifizierte API -Anrufe tätigen und bereits die derzeit angemeldeten JWT token von Benutzern enthalten.
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 ; Das obige Beispiel verwendet den useEffect -Hook, um die Kanäle zu holen, wenn die Komponenten montiert werden. Die API -Anforderung wird mit der api -Instanz erstellt, die vom useAuth -Hook bereitgestellt wird.
Dies ist der einzige Weg, wie Sie API -Anrufe von einer Komponente an die BFF tätigen sollten, da das JWT -Token gültig und anwesend ist.
Wenn Sie der Anwendung neue BFF APIs hinzufügen, müssen Sie eine neue Funktion in /app/src/sdk/BffApiClient.js implementieren, um es Ihren Komponenten zur Verfügung zu stellen.
Diese BffApiClient -Anrufe sind einfach und sehen so aus:
async listChannels ( ) {
const result = await this . get ( "/api/channels" ) ;
return await result . json ( ) ;
} Ein Versorgungscode im Client sorgt dafür, dass das richtige JWT token bei der Anforderung vorhanden ist.
Wir verwenden CosmosDB, um unsere Anwendungsmetadaten zu speichern, da es sich um eine skalierbare, hoch verfügbare, verwaltete Datenbank handelt, die wir nicht selbst verwalten müssen. Es kann in einem vorbereiteten oder serverlosen Modus ausgeführt werden, wodurch die Kosten niedrig bleiben, wenn die Anwendung nicht verwendet wird (auf Kosten einer Leistung).
Wir verwenden eine einzelne CosmosDB -Datenbank, um alle Metadaten zu speichern, und im Inneren haben wir für jede Art von Entität, die wir speichern, eine Sammlung erstellt.
Zum Beispiel: Die User speichert unsere Benutzerdatensätze - und kann mithilfe der SQL -ähnlichen Syntax abgefragt werden. CosmosDB erleichtert dies durch automatische Indizierung von JSON -Dokumenten.
Jede der gespeicherten Metadaten -Entitäten verfügen über eine id und ein type . Wir verwenden eine generic repository -Klasse ( /api/common/dataaccess/CosmosDbMetadataRepository ), um diese Entitäten zu laden und zu speichern.
Für die lokale Entwicklung können Sie entweder die Cloud -gehostete Version von Cosmos verwenden oder eines der verfügbaren docker container images verwenden, um eine lokale Kopie der Datenbank auszuführen.
Wir verwenden Ably channels um unsere Chat -Nachrichten zu speichern und Ereignisse in unsere React -Anwendung zu übertragen. Jeder vernetzte Benutzer empfängt Nachrichten für Kanäle, die er aktiv in Echtzeit anzeigt, und wir verwenden Channel rewind , um die zuletzt gesendeten Nachrichten zu bevölkern.
Nachrichten können asynchron corrected werden, nachdem sie empfangen wurden - zum Beispiel, um die Filterung von Obszönitäten anzuwenden oder Rechtschreibfehler zu korrigieren. Diese Korrekturnachrichten sind Teil des Streams und werden rückwirkend in der React -Anwendung angewendet. (Weiterentwicklung dazu in späteren Epen)
Mit diesem Design können wir zusätzliche APIs aufstehen, die diese Ereignisse konsumieren und ihre eigenen Ausgaben auf den Kanälen veröffentlichen, auf die Kunden reagieren können.
Da schädliche Ereignisse im Laufe der Zeit verschwinden, werden wir über die Archive API Kopien von Inbound -Events auf jedem Kanal in unser Chat Archive speichern.
Die Archive API empfängt Reaktornachrichten für alle unsere Kanäle und hängt sie an kanalspezifische Azure Storage Blobs an. Die API wird einer einzelnen Datei angehängt, bis sie einen Größenschwellenwert (~ 500 KB) erreicht und dann eine neue Datei für nachfolgende Nachrichten erstellt.
Die Archive API führt einen Datensatz der aktuell aktiven Archivdatei in der Metadata database für jeden Kanal bei.
Die Archive API kann einen Suchindex aktualisieren, wenn Nachrichten empfangen und archiviert werden, um sie später in der Suche aufzudecken.
Tests werden im jest mit ts-jest geschrieben, mit dem die APIS- TypeScript -Tests ausgeführt werden.