目的是使用Ably Realtime與其他服務結合使用以存儲,操縱和共享數據的完整功能集構建聊天應用程序。
如果您有任何疑問,想法或想做出貢獻,請提出問題或與我們聯繫。
節點16安裝
NPM的Azure功能運行時。安裝此運行:
npm install -g azure-functions-core-tools@4
.env文件./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 server如.env文件所示,您需要註冊一些服務以使用其當前形式使用該項目。
為了獲得明智的憑據,您首先需要註冊一個免費的帳戶。擁有一個很好的帳戶後,您可以轉到生成的默認應用程序並獲取root API鍵。這將用於ABLY_API_KEY環境。 APP_ID env變量應設置為API鍵的第一部分,然後在全速度之前。如果您的API鍵為12345.jh40fj23jkd0-,32c3-j- ,則應將其設置為12345 。
對於CONTROL_KEY ,用於控制API密鑰,應用程序等的Control_Key,您將需要作為登錄用戶進行訪問令牌,然後單擊“創建新的訪問令牌”。從“帳戶”下拉列表中給它一個名稱,選擇具有您擁有API鍵的應用程序的帳戶,並確保選擇read:key和write:key權限”的帳戶。創建令牌,並將其值用作CONTROL_KEY 。
COSMOSDB用於存儲此應用程序的數據。要獲取一個Azure帳戶並創建COSMOSDB資源,請按照Azure的設置教程中的步驟進行操作。您應該將COSMOS_ENDPOINT設置為新子帳戶中提供的URI。 COSMOS_KEY是COSMOSDB帳戶的主要鍵,您可以按Azure所述訪問。
COSMOS_DATABASE_ID是您將在COSMOSDB帳戶中使用的容器的名稱。
使用為COSMOSDB創建的相同的Azure帳戶,創建一個新的數據存儲容器。設置該設置後,請轉到側邊欄中的“訪問鍵”部分,以獲取AZURE_STORAGE_CONNECTION_STRING的connection string ,然後將AZURE_STORAGE_CONTAINER_NAME設置為您命名容器的任何內容。
如果要將auth0作為身份驗證方法包括在內,則需要創建一個auth0帳戶。擁有一個Auth0帳戶後,創建一個應用程序,將“常規Web應用程序”作為應用程序類型。然後,您可以將AUTH0_DOMAIN的域複製為AUTH0_CLIENTID的客戶端。 AUTH0_REDIRECT_URI應該指出適當的uri,在對用戶進行身份驗證後,應重定向到。默認值應適用於本地運行。
在Auth0應用程序的“設置”選項卡上,向下滾動到稱為“應用程序URI”的部分。在其中,您應該看到一個“允許回調URL”和“允許註銷URL”的字段。對於上下文,使用auth0的網頁流量為:
您的網站將用戶鏈接到您的auth0應用程序的登錄頁面,在該頁面中,他們在auth0頁面中籤名將用戶重定向到您的網站的“回調”頁面時,當用戶要註銷時,它們被定向到auth0應用程序的登錄頁面,然後重定向到“返回”查詢頁面中指定的頁面,
為了避免潛在的濫用和濫用,您需要指定URLS auth0可以重定向到哪些。在本地託管此聊天應用程序時,它託管在Localhost:8080上,因此將允許的回調URL設置為'http:// localhost:8080/auth0-landing'。當用戶註銷時,我們將將用戶重定向回我們的主頁,因此將允許的註銷URL設置為'http:// localhost:8080/'。
聊天應用程序由以下內容組成:
Archive API接收來自反應堆的活動並保持聊天歷史記錄Chat Archive存儲庫的存儲存儲桶。 React應用程序是默認的單頁應用程序。它使用react-router-dom和自定義AppProvider的混合物為應用程序提供安全上下文。
該應用程序使用 @ably-labs/react-hooks與通道相互作用,該應用程序由現代反應功能組件組成。
Snowpack是開發服務器,它將透明地構建生產的ES6代碼。
BFF是一個特定應用程序的API,其中包含聊天應用程序的所有Serveride邏輯。由於它託管在Azure靜態Web應用程序上,因此我們可以使用azure-functions-core-tools運行API服務器。
除此之外, Azure靜態Web應用程序運行時將為我們自動使用API-因此我們不必擔心配置託管。 BFF在無服務器的基礎上執行,而Azure SWA將自動規模以滿足需求。
要添加新的API端點,您將需要在api文件夾中添加新目錄。
首先,為新API創建一個目錄 - 例如, api/messages 。然後,在新目錄中創建一個function.json文件。
{
"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 "
}接下來,您需要創建您的TypeScript API:
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" } ;
}現在,此API將安裝在http://localhost:8080/api/messages 。
就是這樣!更改代碼並為您重建功能時,該工具和SDK將自動檢測您的代碼。
該應用在Web應用程序和BFF之間使用JWT令牌身份驗證。我們在COSMOSDB數據庫中存儲用戶憑據並鹽分,單方法密碼(使用BCRypt完成)。
當用戶進行身份驗證時,該應用將簽名使用用戶的ID和用戶名的JWT令牌,然後在隨後的認證數據請求中將其發送到BFF。這意味著,在API中使用少量代碼,我們可以確保用戶是他們聲稱是誰,並且有權訪問API數據。
我們可以將此模型擴展到包括針對應用程序中資源的基於索賠的身份驗證的roles集合。
Authenticated User Only API調用我們可以通過使用BFF API中的以下便利方法來創建JWT token身份驗證的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" )
} ;
} ) ;
}如果要作為這些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 ,
( { 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
} 應用內身份驗證在AppProviders.jsx中實現。
它提供了一個React Hook,如果用戶經過身份驗證,則將返回userDetails對象(並確保完全驗證用戶)。如果未對給定的用戶進行身份驗證,則在所有情況下,他們都會將其重定向到登錄頁面。
由於AppProvider負責身份驗證,因此您需要使用掛鉤訪問用戶數據,並在任何組件中進行身份驗證的API調用。
這是訪問當前身份驗證用戶的userDetails訪問對象的示例。
import { useAuth } from "../../AppProviders" ;
const MyComponent = ( ) => {
const { user } = useAuth ( ) ;
return < div > { user . username } </ div > ;
} ;
export default MyComponent ;您還可以訪問BffApiClient類的實例,該類別將允許您進行身份驗證的API調用,並且已經包含當前已登錄的用戶JWT token 。
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 ;上面的示例使用useEffect掛鉤在組件安裝時獲取通道 - API請求是使用useAuth Hook提供的api實例進行的。
這是您應該從組件對BFF進行API調用的唯一方法,因為它可以確保JWT令牌有效且存在。
如果您將新的BFF APIs添加到應用程序中,則需要在/app/src/sdk/BffApiClient.js中實現一個新功能,以使其可用於組件。
這些BffApiClient調用很簡單,看起來像這樣:
async listChannels ( ) {
const result = await this . get ( "/api/channels" ) ;
return await result . json ( ) ;
}客戶端中的某些實用代碼將確保在提出請求時存在正確的JWT token 。
我們使用COSMOSDB來存儲我們的應用程序元數據,因為它是一個可擴展的,高度可用的託管數據庫,我們不必管理自己。它可以以預先處理或無服務器模式運行,在不使用應用程序時(以某些性能為代價),有助於將成本降低。
我們使用單個COSMOSDB數據庫存儲所有元數據,在內部,我們為我們存儲的每種實體創建了一個集合。
例如: User集合,存儲我們的用戶記錄 - 可以使用類似SQL的語法查詢。 COSMOSDB通過自動索引JSON文檔來簡單。
每個存儲的元數據實體都有一個id和一個type字段,我們正在使用generic repository類( /api/common/dataaccess/CosmosDbMetadataRepository )來加載和保存這些實體。
對於本地開發,您可以使用雲託管版的宇宙版本,也可以使用可用的docker container images之一運行數據庫的本地副本。
我們正在使用Ably channels來存儲我們的聊天消息,並將事件推向我們的React應用程序。每個連接的用戶將接收他們正在實時積極查看的渠道的消息,並且我們使用Channel rewind帶來填充最近發送的消息。
收到消息後,可能會corrected這些消息 - 例如,應用褻瀆過濾或糾正拼寫錯誤。這些校正消息將是流的一部分,並在React應用程序中追溯應用。 (在後來的史詩中進一步發展)
這種設計使我們能夠忍受消耗這些事件的額外API,並在渠道上發布自己的詳細說明,以供客戶做出回應。
由於事件隨著時間的流逝而消失,我們將通過Archive API將每個頻道上的入站事件的副本存儲到我們的Chat Archive中。
Archive API將接收我們所有頻道的反應堆消息,並將其附加到特定於頻道的Azure Storage Blobs 。 API將附加到一個文件,直到達到大小閾值(〜500KB),然後創建一個新文件以進行後續消息。
Archive API將在每個通道的Metadata database中維護當前活動存檔文件的記錄。
Archive API將能夠在接收消息並存檔以後將其顯示在搜索中時更新搜索索引。
測試用jest編寫,用ts-jest用來執行APIS TypeScript測試。