зеркало из
1
0
Форкнуть 0

Add SignalR extensions sample for JS V4 model (#252)

This commit is contained in:
yzt 2024-02-05 16:09:42 +08:00 коммит произвёл GitHub
Родитель d688eb9693
Коммит 41f8ef74c6
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
9 изменённых файлов: 966 добавлений и 0 удалений

Просмотреть файл

@ -0,0 +1,10 @@
*.js.map
*.ts
.git*
.vscode
local.settings.json
test
getting_started.md
node_modules/@types/
node_modules/azure-functions-core-tools/
node_modules/typescript/

48
samples/BidirectionChat/js/.gitignore поставляемый Normal file
Просмотреть файл

@ -0,0 +1,48 @@
bin
obj
csx
.vs
edge
Publish
*.user
*.suo
*.cscfg
*.Cache
project.lock.json
/packages
/TestResults
/tools/NuGet.exe
/App_Data
/secrets
/data
.secrets
appsettings.json
local.settings.json
node_modules
dist
# Local python packages
.python_packages/
# Python Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# Azurite artifacts
__blobstorage__
__queuestorage__
__azurite_db*__.json

Просмотреть файл

@ -0,0 +1,18 @@
{
"version": "2.0",
"logging": {
"applicationInsights": {
"samplingSettings": {
"isEnabled": true,
"excludedTypes": "Request"
}
},
"logLevel": {
"default": "Debug"
}
},
"extensionBundle": {
"id": "Microsoft.Azure.Functions.ExtensionBundle",
"version": "[4.*, 5.0.0)"
}
}

Просмотреть файл

@ -0,0 +1,213 @@
<html>
<head>
<title>Serverless Chat</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.1.3/dist/css/bootstrap.min.css">
<script>
window.apiBaseUrl = window.location.origin;
</script>
<style>
.slide-fade-enter-active,
.slide-fade-leave-active {
transition: all 1s ease;
}
.slide-fade-enter,
.slide-fade-leave-to {
height: 0px;
overflow-y: hidden;
opacity: 0;
}
</style>
</head>
<body>
<p>&nbsp;</p>
<div id="app" class="container">
<h3>Serverless chat</h3>
<div class="row" v-if="ready">
<div class="signalr-demo col-sm">
<hr />
<div id='groupchecked'>
<input type="checkbox" id="checkbox" v-model="checked">
<label for="checkbox">Send To Default Group: {{ this.defaultgroup }}</label>
</div>
<form v-on:submit.prevent="sendNewMessage(checked)">
<input type="text" v-model="newMessage" id="message-box" class="form-control"
placeholder="Type message here..." />
</form>
</div>
</div>
<div class="row" v-if="!ready">
<div class="col-sm">
<div>Loading...</div>
</div>
</div>
<div v-if="ready">
<transition-group name="slide-fade" tag="div">
<div class="row" v-for="message in messages" v-bind:key="message.id">
<div class="col-sm">
<hr />
<div>
<div style="display: inline-block; padding-left: 12px;">
<div>
<a href="#" v-on:click.prevent="sendPrivateMessage(message.Sender)">
<span class="text-info small">
<strong>{{ message.Sender || message.sender }}</strong>
</span>
</a>
<span v-if="message.ConnectionId || message.connectionId">
<a href="#" v-on:click.prevent="sendToConnection(message.ConnectionId || message.connectionId)">
<span class="badge badge-primary">Connection: {{ message.ConnectionId || message.connectionId
}}</span>
</a>
<a href="#" v-on:click.prevent="addConnectionToGroup(message.ConnectionId || message.connectionId)">
<span v-if="message.ConnectionId || message.connectionId"
class="badge badge-primary">AddConnectionToGroup</span>
</a>
<a href="#"
v-on:click.prevent="removeConnectionIdFromGroup(message.ConnectionId || message.connectionId)">
<span v-if="message.ConnectionId || message.connectionId"
class="badge badge-primary">RemoveConnectionFromGroup</span>
</a>
</span>
<span v-if="message.Sender || message.sender">
<a href="#" v-on:click.prevent="addUserToGroup(message.Sender || message.sender)">
<span class="badge badge-primary">AddUserToGroup</span>
</a>
<a href="#" v-on:click.prevent="removeUserFromGroup(message.Sender || message.sender)">
<span class="badge badge-primary">RemoveUserFromGroup</span>
</a>
</span>
<span v-if="message.IsPrivate || message.isPrivate" class="badge badge-secondary">private message
</span>
</div>
<div>
{{ message.Text || message.text }}
</div>
</div>
</div>
</div>
</div>
</transition-group>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@aspnet/signalr@1.0.3/dist/browser/signalr.js"></script>
<script src="https://cdn.jsdelivr.net/npm/axios@0.18.0/dist/axios.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/crypto-js@3.1.9-1/crypto-js.js"></script>
<script src="https://cdn.jsdelivr.net/npm/crypto-js@3.1.9-1/enc-base64.js"></script>
<script>
const data = {
username: '',
defaultgroup: 'AzureSignalR',
checked: false,
newMessage: '',
messages: [],
myConnectionId: '',
ready: false
};
const app = new Vue({
el: '#app',
data: data,
methods: {
sendNewMessage: function (isToGroup) {
if (isToGroup) {
connection.invoke("sendToGroup", this.defaultgroup, this.newMessage);
}
else {
connection.invoke("broadcast", this.newMessage);
}
this.newMessage = '';
},
sendPrivateMessage: function (user) {
const messageText = prompt('Send private message to ' + user);
if (messageText) {
connection.invoke("sendToUser", user, messageText);
}
},
sendToConnection: function (connectionId) {
const messageText = prompt('Send private message to connection ' + connectionId);
if (messageText) {
connection.invoke("sendToConnection", connectionId, messageText);
}
},
addConnectionToGroup: function (connectionId) {
confirm('Add connection ' + connectionId + ' to group: ' + this.defaultgroup);
connection.invoke("joinGroup", connectionId, this.defaultgroup);
},
addUserToGroup: function (user) {
r = confirm('Add user ' + user + ' to group: ' + this.defaultgroup);
connection.invoke("joinUserToGroup", user, this.defaultgroup);
},
removeConnectionIdFromGroup: function (connectionId) {
confirm('Remove connection ' + connectionId + ' from group: ' + this.defaultgroup);
connection.invoke("leaveGroup", connectionId, this.defaultgroup);
},
removeUserFromGroup: function (user) {
confirm('Remove user ' + user + ' from group: ' + this.defaultgroup);
connection.invoke("leaveUserFromGroup", user, this.defaultgroup);
}
}
});
const apiBaseUrl = window.location.origin;
data.username = prompt("Enter your username");
if (!data.username) {
alert("No username entered. Reload page and try again.");
throw "No username entered";
}
const connection = new signalR.HubConnectionBuilder()
.withUrl(apiBaseUrl + `/api?x-ms-signalr-userid=${data.username}`)
.configureLogging(signalR.LogLevel.Information)
.build();
connection.on('newMessage', onNewMessage);
connection.on('newConnection', onNewConnection)
connection.onclose(() => console.log('disconnected'));
console.log('connecting...');
connection.start()
.then(() => {
data.ready = true;
console.log('connected!');
})
.catch(console.error);
function getAxiosConfig() {
const config = {
headers: {
'x-ms-signalr-user-id': data.username,
}
};
return config;
}
let counter = 0;
function onNewMessage(message) {
message.id = counter++; // vue transitions need an id
data.messages.unshift(message);
};
function onNewConnection(message) {
data.myConnectionId = message.ConnectionId;
newConnectionMessage = {
id: counter++,
text: `${message.ConnectionId} has connected.`
};
data.messages.unshift(newConnectionMessage);
}
function base64url(source) {
// Encode in classical base64
encodedSource = CryptoJS.enc.Base64.stringify(source);
// Remove padding equal characters
encodedSource = encodedSource.replace(/=+$/, '');
// Replace characters according to base64url specifications
encodedSource = encodedSource.replace(/\+/g, '-');
encodedSource = encodedSource.replace(/\//g, '_');
return encodedSource;
}
</script>
</body>
</html>

473
samples/BidirectionChat/js/package-lock.json сгенерированный Normal file
Просмотреть файл

@ -0,0 +1,473 @@
{
"name": "js",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"version": "1.0.0",
"dependencies": {
"@azure/functions": "^4.0.0"
},
"devDependencies": {
"azure-functions-core-tools": "^4.x"
}
},
"node_modules/@azure/functions": {
"version": "4.1.0",
"resolved": "https://msazure.pkgs.visualstudio.com/_packaging/AzurePortal/npm/registry/@azure/functions/-/functions-4.1.0.tgz",
"integrity": "sha1-+KGZb84rRk+2mw9lBqhe4GThkHU=",
"license": "MIT",
"dependencies": {
"long": "^4.0.0",
"undici": "^5.13.0"
},
"engines": {
"node": ">=18.0"
}
},
"node_modules/@fastify/busboy": {
"version": "2.1.0",
"resolved": "https://msazure.pkgs.visualstudio.com/_packaging/AzurePortal/npm/registry/@fastify/busboy/-/busboy-2.1.0.tgz",
"integrity": "sha1-Bwnp9MslI1HGCcbm2NZ3mo0l7f8=",
"license": "MIT",
"engines": {
"node": ">=14"
}
},
"node_modules/@types/node": {
"version": "20.11.5",
"resolved": "https://msazure.pkgs.visualstudio.com/_packaging/AzurePortal/npm/registry/@types/node/-/node-20.11.5.tgz",
"integrity": "sha1-vhDGIsp/yqPPImz4AWarwxOJ2G4=",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"undici-types": "~5.26.4"
}
},
"node_modules/@types/yauzl": {
"version": "2.10.3",
"resolved": "https://msazure.pkgs.visualstudio.com/_packaging/AzurePortal/npm/registry/@types/yauzl/-/yauzl-2.10.3.tgz",
"integrity": "sha1-6bKAi08QlQSgPNqVglmHb2EBeZk=",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"@types/node": "*"
}
},
"node_modules/agent-base": {
"version": "6.0.2",
"resolved": "https://msazure.pkgs.visualstudio.com/_packaging/AzurePortal/npm/registry/agent-base/-/agent-base-6.0.2.tgz",
"integrity": "sha1-Sf/1hXfP7j83F2/qtMIuAPhtf3c=",
"dev": true,
"license": "MIT",
"dependencies": {
"debug": "4"
},
"engines": {
"node": ">= 6.0.0"
}
},
"node_modules/ansi-styles": {
"version": "4.3.0",
"resolved": "https://msazure.pkgs.visualstudio.com/_packaging/AzurePortal/npm/registry/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha1-7dgDYornHATIWuegkG7a00tkiTc=",
"dev": true,
"license": "MIT",
"dependencies": {
"color-convert": "^2.0.1"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
"node_modules/azure-functions-core-tools": {
"version": "4.0.5455",
"resolved": "https://msazure.pkgs.visualstudio.com/_packaging/AzurePortal/npm/registry/azure-functions-core-tools/-/azure-functions-core-tools-4.0.5455.tgz",
"integrity": "sha1-N69rKRXOUPV/di0oShQcyh8e0WY=",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"os": [
"win32",
"darwin",
"linux"
],
"dependencies": {
"chalk": "3.0.0",
"extract-zip": "^2.0.1",
"https-proxy-agent": "5.0.0",
"progress": "2.0.3",
"rimraf": "3.0.2"
},
"bin": {
"azfun": "lib/main.js",
"azurefunctions": "lib/main.js",
"func": "lib/main.js"
},
"engines": {
"node": ">=6.9.1"
}
},
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://msazure.pkgs.visualstudio.com/_packaging/AzurePortal/npm/registry/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha1-6D46fj8wCzTLnYf2FfoMvzV2kO4=",
"dev": true,
"license": "MIT"
},
"node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://msazure.pkgs.visualstudio.com/_packaging/AzurePortal/npm/registry/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha1-PH/L9SnYcibz0vUrlm/1Jx60Qd0=",
"dev": true,
"license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
}
},
"node_modules/buffer-crc32": {
"version": "0.2.13",
"resolved": "https://msazure.pkgs.visualstudio.com/_packaging/AzurePortal/npm/registry/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
"integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=",
"dev": true,
"license": "MIT",
"engines": {
"node": "*"
}
},
"node_modules/chalk": {
"version": "3.0.0",
"resolved": "https://msazure.pkgs.visualstudio.com/_packaging/AzurePortal/npm/registry/chalk/-/chalk-3.0.0.tgz",
"integrity": "sha1-P3PCv1JlkfV0zEksUeJFY0n4ROQ=",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://msazure.pkgs.visualstudio.com/_packaging/AzurePortal/npm/registry/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha1-ctOmjVmMm9s68q0ehPIdiWq9TeM=",
"dev": true,
"license": "MIT",
"dependencies": {
"color-name": "~1.1.4"
},
"engines": {
"node": ">=7.0.0"
}
},
"node_modules/color-name": {
"version": "1.1.4",
"resolved": "https://msazure.pkgs.visualstudio.com/_packaging/AzurePortal/npm/registry/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha1-wqCah6y95pVD3m9j+jmVyCbFNqI=",
"dev": true,
"license": "MIT"
},
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://msazure.pkgs.visualstudio.com/_packaging/AzurePortal/npm/registry/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
"dev": true,
"license": "MIT"
},
"node_modules/debug": {
"version": "4.3.4",
"resolved": "https://msazure.pkgs.visualstudio.com/_packaging/AzurePortal/npm/registry/debug/-/debug-4.3.4.tgz",
"integrity": "sha1-Exn2V5NX8jONMzfSzdSRS7XcyGU=",
"dev": true,
"license": "MIT",
"dependencies": {
"ms": "2.1.2"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/end-of-stream": {
"version": "1.4.4",
"resolved": "https://msazure.pkgs.visualstudio.com/_packaging/AzurePortal/npm/registry/end-of-stream/-/end-of-stream-1.4.4.tgz",
"integrity": "sha1-WuZKX0UFe682JuwU2gyl5LJDHrA=",
"dev": true,
"license": "MIT",
"dependencies": {
"once": "^1.4.0"
}
},
"node_modules/extract-zip": {
"version": "2.0.1",
"resolved": "https://msazure.pkgs.visualstudio.com/_packaging/AzurePortal/npm/registry/extract-zip/-/extract-zip-2.0.1.tgz",
"integrity": "sha1-Zj3KVv5G34kNXxMe9KBtIruLoTo=",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
"debug": "^4.1.1",
"get-stream": "^5.1.0",
"yauzl": "^2.10.0"
},
"bin": {
"extract-zip": "cli.js"
},
"engines": {
"node": ">= 10.17.0"
},
"optionalDependencies": {
"@types/yauzl": "^2.9.1"
}
},
"node_modules/fd-slicer": {
"version": "1.1.0",
"resolved": "https://msazure.pkgs.visualstudio.com/_packaging/AzurePortal/npm/registry/fd-slicer/-/fd-slicer-1.1.0.tgz",
"integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=",
"dev": true,
"license": "MIT",
"dependencies": {
"pend": "~1.2.0"
}
},
"node_modules/fs.realpath": {
"version": "1.0.0",
"resolved": "https://msazure.pkgs.visualstudio.com/_packaging/AzurePortal/npm/registry/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
"dev": true,
"license": "ISC"
},
"node_modules/get-stream": {
"version": "5.2.0",
"resolved": "https://msazure.pkgs.visualstudio.com/_packaging/AzurePortal/npm/registry/get-stream/-/get-stream-5.2.0.tgz",
"integrity": "sha1-SWaheV7lrOZecGxLe+txJX1uItM=",
"dev": true,
"license": "MIT",
"dependencies": {
"pump": "^3.0.0"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/glob": {
"version": "7.2.3",
"resolved": "https://msazure.pkgs.visualstudio.com/_packaging/AzurePortal/npm/registry/glob/-/glob-7.2.3.tgz",
"integrity": "sha1-uN8PuAK7+o6JvR2Ti04WV47UTys=",
"dev": true,
"license": "ISC",
"dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.1.1",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
},
"engines": {
"node": "*"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/has-flag": {
"version": "4.0.0",
"resolved": "https://msazure.pkgs.visualstudio.com/_packaging/AzurePortal/npm/registry/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha1-lEdx/ZyByBJlxNaUGGDaBrtZR5s=",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/https-proxy-agent": {
"version": "5.0.0",
"resolved": "https://msazure.pkgs.visualstudio.com/_packaging/AzurePortal/npm/registry/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz",
"integrity": "sha1-4qkFQqu2inYuCghQ9sntrf2FBrI=",
"dev": true,
"license": "MIT",
"dependencies": {
"agent-base": "6",
"debug": "4"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/inflight": {
"version": "1.0.6",
"resolved": "https://msazure.pkgs.visualstudio.com/_packaging/AzurePortal/npm/registry/inflight/-/inflight-1.0.6.tgz",
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
"dev": true,
"license": "ISC",
"dependencies": {
"once": "^1.3.0",
"wrappy": "1"
}
},
"node_modules/inherits": {
"version": "2.0.4",
"resolved": "https://msazure.pkgs.visualstudio.com/_packaging/AzurePortal/npm/registry/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha1-D6LGT5MpF8NDOg3tVTY6rjdBa3w=",
"dev": true,
"license": "ISC"
},
"node_modules/long": {
"version": "4.0.0",
"resolved": "https://msazure.pkgs.visualstudio.com/_packaging/AzurePortal/npm/registry/long/-/long-4.0.0.tgz",
"integrity": "sha1-mntxz7fTYaGU6lVSQckvdGjVvyg=",
"license": "Apache-2.0"
},
"node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://msazure.pkgs.visualstudio.com/_packaging/AzurePortal/npm/registry/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha1-Gc0ZS/0+Qo8EmnCBfAONiatL41s=",
"dev": true,
"license": "ISC",
"dependencies": {
"brace-expansion": "^1.1.7"
},
"engines": {
"node": "*"
}
},
"node_modules/ms": {
"version": "2.1.2",
"resolved": "https://msazure.pkgs.visualstudio.com/_packaging/AzurePortal/npm/registry/ms/-/ms-2.1.2.tgz",
"integrity": "sha1-0J0fNXtEP0kzgqjrPM0YOHKuYAk=",
"dev": true,
"license": "MIT"
},
"node_modules/once": {
"version": "1.4.0",
"resolved": "https://msazure.pkgs.visualstudio.com/_packaging/AzurePortal/npm/registry/once/-/once-1.4.0.tgz",
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"dev": true,
"license": "ISC",
"dependencies": {
"wrappy": "1"
}
},
"node_modules/path-is-absolute": {
"version": "1.0.1",
"resolved": "https://msazure.pkgs.visualstudio.com/_packaging/AzurePortal/npm/registry/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/pend": {
"version": "1.2.0",
"resolved": "https://msazure.pkgs.visualstudio.com/_packaging/AzurePortal/npm/registry/pend/-/pend-1.2.0.tgz",
"integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=",
"dev": true,
"license": "MIT"
},
"node_modules/progress": {
"version": "2.0.3",
"resolved": "https://msazure.pkgs.visualstudio.com/_packaging/AzurePortal/npm/registry/progress/-/progress-2.0.3.tgz",
"integrity": "sha1-foz42PW48jnBvGi+tOt4Vn1XLvg=",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/pump": {
"version": "3.0.0",
"resolved": "https://msazure.pkgs.visualstudio.com/_packaging/AzurePortal/npm/registry/pump/-/pump-3.0.0.tgz",
"integrity": "sha1-tKIRaBW94vTh6mAjVOjHVWUQemQ=",
"dev": true,
"license": "MIT",
"dependencies": {
"end-of-stream": "^1.1.0",
"once": "^1.3.1"
}
},
"node_modules/rimraf": {
"version": "3.0.2",
"resolved": "https://msazure.pkgs.visualstudio.com/_packaging/AzurePortal/npm/registry/rimraf/-/rimraf-3.0.2.tgz",
"integrity": "sha1-8aVAK6YiCtUswSgrrBrjqkn9Bho=",
"dev": true,
"license": "ISC",
"dependencies": {
"glob": "^7.1.3"
},
"bin": {
"rimraf": "bin.js"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/supports-color": {
"version": "7.2.0",
"resolved": "https://msazure.pkgs.visualstudio.com/_packaging/AzurePortal/npm/registry/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha1-G33NyzK4E4gBs+R4umpRyqiWSNo=",
"dev": true,
"license": "MIT",
"dependencies": {
"has-flag": "^4.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/undici": {
"version": "5.28.2",
"resolved": "https://msazure.pkgs.visualstudio.com/_packaging/AzurePortal/npm/registry/undici/-/undici-5.28.2.tgz",
"integrity": "sha1-/qIA6sZfx+yv+AoCPRoFQ0I7TJE=",
"license": "MIT",
"dependencies": {
"@fastify/busboy": "^2.0.0"
},
"engines": {
"node": ">=14.0"
}
},
"node_modules/undici-types": {
"version": "5.26.5",
"resolved": "https://msazure.pkgs.visualstudio.com/_packaging/AzurePortal/npm/registry/undici-types/-/undici-types-5.26.5.tgz",
"integrity": "sha1-vNU5iT0AtW6WT9JlekhmsiGmVhc=",
"dev": true,
"license": "MIT",
"optional": true
},
"node_modules/wrappy": {
"version": "1.0.2",
"resolved": "https://msazure.pkgs.visualstudio.com/_packaging/AzurePortal/npm/registry/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
"dev": true,
"license": "ISC"
},
"node_modules/yauzl": {
"version": "2.10.0",
"resolved": "https://msazure.pkgs.visualstudio.com/_packaging/AzurePortal/npm/registry/yauzl/-/yauzl-2.10.0.tgz",
"integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=",
"dev": true,
"license": "MIT",
"dependencies": {
"buffer-crc32": "~0.2.3",
"fd-slicer": "~1.1.0"
}
}
}
}

Просмотреть файл

@ -0,0 +1,16 @@
{
"name": "",
"version": "1.0.0",
"description": "",
"main": "src/functions/*.js",
"scripts": {
"start": "func start",
"test": "echo \"No tests yet...\""
},
"dependencies": {
"@azure/functions": "^4.0.0"
},
"devDependencies": {
"azure-functions-core-tools": "^4.x"
}
}

Просмотреть файл

@ -0,0 +1,23 @@
const { app } = require('@azure/functions');
const { readFile } = require('fs/promises');
app.http('index', {
methods: ['GET', 'POST'],
authLevel: 'anonymous',
handler: async (context) => {
const content = await readFile('index.html', 'utf8', (err, data) => {
if (err) {
context.err(err)
return
}
});
return {
status: 200,
headers: {
'Content-Type': 'text/html'
},
body: content,
};
}
});

Просмотреть файл

@ -0,0 +1,147 @@
const { app, output } = require('@azure/functions');
const newMessageTarget = "newMessage";
const signalR = output.generic({
type: 'signalR',
name: 'signalR',
hubName: 'hub',
connectionStringSetting: 'AzureSignalRConnectionString',
});
app.generic("connected",
{
trigger: { "type": "signalRTrigger", "name": "connected", "direction": "in", "hubName": "hub", "connectionStringSetting": "AzureSignalRConnectionString", "event": "connected", "category": "connections" },
extraOutputs: [signalR],
handler: (triggerInput, context) => {
context.log(`Connection ${triggerInput.ConnectionId} is connected.`)
context.extraOutputs.set(signalR, {
"target": "newConnection",
"arguments": [new NewConnection(triggerInput.ConnectionId, "text")],
});
}
})
app.generic("disconnected",
{
trigger: { "type": "signalRTrigger", "name": "disconnected", "direction": "in", "hubName": "hub", "connectionStringSetting": "AzureSignalRConnectionString", "event": "disconnected", "category": "connections" },
handler: (triggerInput, context) => { context.log(`Connection ${triggerInput.ConnectionId} is disconnected.`) }
})
class NewConnection {
constructor(connectionId, auth) {
this.ConnectionId = connectionId;
this.auth = auth;
}
}
app.generic("broadcast",
{
trigger: { "type": "signalRTrigger", "name": "broadcast", "direction": "in", "hubName": "hub", "connectionStringSetting": "AzureSignalRConnectionString", "event": "broadcast", "category": "messages" },
extraOutputs: [signalR],
handler: (triggerInput, context) => {
context.extraOutputs.set(signalR, {
"target": newMessageTarget,
"arguments": [new NewMessage(triggerInput, triggerInput.Arguments[0])]
});
}
})
app.generic("sendToGroup",
{
trigger: { "type": "signalRTrigger", "name": "sendToGroup", "direction": "in", "hubName": "hub", "connectionStringSetting": "AzureSignalRConnectionString", "event": "sendToGroup", "category": "messages" },
extraOutputs: [signalR],
handler: (triggerInput, context) => {
context.extraOutputs.set(signalR, {
"target": newMessageTarget,
"groupName": triggerInput.Arguments[0],
"arguments": [new NewMessage(triggerInput, triggerInput.Arguments[1])],
});
}
})
app.generic("sendToUser",
{
trigger: { "type": "signalRTrigger", "name": "sendToUser", "direction": "in", "hubName": "hub", "connectionStringSetting": "AzureSignalRConnectionString", "event": "sendToUser", "category": "messages" },
extraOutputs: [signalR],
handler: (triggerInput, context) => {
context.extraOutputs.set(signalR, {
"target": newMessageTarget,
"userId": triggerInput.Arguments[0],
"arguments": [new NewMessage(triggerInput, triggerInput.Arguments[1])],
});
}
})
app.generic("sendToConnection",
{
trigger: { "type": "signalRTrigger", "name": "sendToConnection", "direction": "in", "hubName": "hub", "connectionStringSetting": "AzureSignalRConnectionString", "event": "sendToConnection", "category": "messages" },
extraOutputs: [signalR],
handler: (triggerInput, context) => {
context.extraOutputs.set(signalR, {
"target": newMessageTarget,
"connectionId": triggerInput.Arguments[0],
"arguments": [new NewMessage(triggerInput, triggerInput.Arguments[1])],
});
}
})
app.generic("joinGroup",
{
trigger: { "type": "signalRTrigger", "name": "joinGroup", "direction": "in", "hubName": "hub", "connectionStringSetting": "AzureSignalRConnectionString", "event": "joinGroup", "category": "messages" },
extraOutputs: [signalR],
handler: (triggerInput, context) => {
context.extraOutputs.set(signalR, {
"connectionId": triggerInput.Arguments[0],
"groupName": triggerInput.Arguments[1],
"action": "add",
});
}
})
app.generic("leaveGroup",
{
trigger: { "type": "signalRTrigger", "name": "leaveGroup", "direction": "in", "hubName": "hub", "connectionStringSetting": "AzureSignalRConnectionString", "event": "leaveGroup", "category": "messages" },
extraOutputs: [signalR],
handler: (triggerInput, context) => {
context.extraOutputs.set(signalR, {
"connectionId": triggerInput.Arguments[0],
"groupName": triggerInput.Arguments[1],
"action": "remove",
});
}
})
app.generic("joinUserToGroup",
{
trigger: { "type": "signalRTrigger", "name": "joinUserToGroup", "direction": "in", "hubName": "hub", "connectionStringSetting": "AzureSignalRConnectionString", "event": "joinUserToGroup", "category": "messages" },
extraOutputs: [signalR],
handler: (triggerInput, context) => {
context.extraOutputs.set(signalR, {
"userId": triggerInput.Arguments[0],
"groupName": triggerInput.Arguments[1],
"action": "add",
});
}
})
app.generic("leaveUserFromGroup",
{
trigger: { "type": "signalRTrigger", "name": "leaveUserFromGroup", "direction": "in", "hubName": "hub", "connectionStringSetting": "AzureSignalRConnectionString", "event": "leaveUserFromGroup", "category": "messages" },
extraOutputs: [signalR],
handler: (triggerInput, context) => {
context.extraOutputs.set(signalR, {
"userId": triggerInput.Arguments[0],
"groupName": triggerInput.Arguments[1],
"action": "remove",
});
}
})
class NewMessage {
constructor(invocationContext, message) {
this.Sender = invocationContext.UserId ? invocationContext.UserId : '';
this.ConnectionId = invocationContext.ConnectionId;
this.Text = message;
}
}

Просмотреть файл

@ -0,0 +1,18 @@
const { app, input } = require('@azure/functions');
const inputSignalR = input.generic({
type: 'signalRConnectionInfo',
name: 'connectionInfo',
hubName: 'hub',
connectionStringSetting: 'AzureSignalRConnectionString',
userId: '{query.x-ms-signalr-userid}'
});
app.post('negotiate', {
authLevel: 'function',
handler: (request, context) => {
return { body: JSON.stringify(context.extraInputs.get(inputSignalR)) }
},
route: 'negotiate',
extraInputs: [inputSignalR],
});