Add WIP initial multi-user websocket implementation for sheets
This commit is contained in:
parent
01867f5736
commit
36c953443f
19 changed files with 431 additions and 122 deletions
|
@ -15,12 +15,12 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
const goToBuilder = () => {
|
const goToBuilder = () => {
|
||||||
if (app.lockedOther) {
|
// if (app.lockedOther) {
|
||||||
notifications.error(
|
// notifications.error(
|
||||||
`App locked by ${app.lockedBy.email}. Please allow lock to expire or have them unlock this app.`
|
// `App locked by ${app.lockedBy.email}. Please allow lock to expire or have them unlock this app.`
|
||||||
)
|
// )
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
$goto(`../../app/${app.devId}`)
|
$goto(`../../app/${app.devId}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,9 +59,7 @@
|
||||||
<div class="app-row-actions">
|
<div class="app-row-actions">
|
||||||
<AppLockModal {app} buttonSize="M" />
|
<AppLockModal {app} buttonSize="M" />
|
||||||
<Button size="S" secondary on:click={goToOverview}>Manage</Button>
|
<Button size="S" secondary on:click={goToOverview}>Manage</Button>
|
||||||
<Button size="S" primary disabled={app.lockedOther} on:click={goToBuilder}>
|
<Button size="S" primary on:click={goToBuilder}>Edit</Button>
|
||||||
Edit
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -89,6 +89,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
|
return
|
||||||
if (!hasSynced && application) {
|
if (!hasSynced && application) {
|
||||||
try {
|
try {
|
||||||
await API.syncApp(application)
|
await API.syncApp(application)
|
||||||
|
|
|
@ -1467,6 +1467,11 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
"@sinonjs/commons" "^1.7.0"
|
"@sinonjs/commons" "^1.7.0"
|
||||||
|
|
||||||
|
"@socket.io/component-emitter@~3.1.0":
|
||||||
|
version "3.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz#96116f2a912e0c02817345b3c10751069920d553"
|
||||||
|
integrity sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==
|
||||||
|
|
||||||
"@spectrum-css/accordion@^3.0.24":
|
"@spectrum-css/accordion@^3.0.24":
|
||||||
version "3.0.30"
|
version "3.0.30"
|
||||||
resolved "https://registry.yarnpkg.com/@spectrum-css/accordion/-/accordion-3.0.30.tgz#0893a6db28bab984bf5adaf7e1ba194e741db615"
|
resolved "https://registry.yarnpkg.com/@spectrum-css/accordion/-/accordion-3.0.30.tgz#0893a6db28bab984bf5adaf7e1ba194e741db615"
|
||||||
|
@ -2581,7 +2586,7 @@ dayjs@^1.10.4, dayjs@^1.11.2:
|
||||||
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.7.tgz#4b296922642f70999544d1144a2c25730fce63e2"
|
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.7.tgz#4b296922642f70999544d1144a2c25730fce63e2"
|
||||||
integrity sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ==
|
integrity sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ==
|
||||||
|
|
||||||
debug@4, debug@4.3.4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4:
|
debug@4, debug@4.3.4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4, debug@~4.3.1, debug@~4.3.2:
|
||||||
version "4.3.4"
|
version "4.3.4"
|
||||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
|
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
|
||||||
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
|
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
|
||||||
|
@ -2774,6 +2779,22 @@ end-of-stream@^1.1.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
once "^1.4.0"
|
once "^1.4.0"
|
||||||
|
|
||||||
|
engine.io-client@~6.4.0:
|
||||||
|
version "6.4.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-6.4.0.tgz#88cd3082609ca86d7d3c12f0e746d12db4f47c91"
|
||||||
|
integrity sha512-GyKPDyoEha+XZ7iEqam49vz6auPnNJ9ZBfy89f+rMMas8AuiMWOZ9PVzu8xb9ZC6rafUqiGHSCfu22ih66E+1g==
|
||||||
|
dependencies:
|
||||||
|
"@socket.io/component-emitter" "~3.1.0"
|
||||||
|
debug "~4.3.1"
|
||||||
|
engine.io-parser "~5.0.3"
|
||||||
|
ws "~8.11.0"
|
||||||
|
xmlhttprequest-ssl "~2.0.0"
|
||||||
|
|
||||||
|
engine.io-parser@~5.0.3:
|
||||||
|
version "5.0.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-5.0.6.tgz#7811244af173e157295dec9b2718dfe42a64ef45"
|
||||||
|
integrity sha512-tjuoZDMAdEhVnSFleYPCtdL2GXwVTGtNjoeJd9IhIG3C1xs9uwxqRNEu5WpnDZCaozwVlK/nuQhpodhXSIMaxw==
|
||||||
|
|
||||||
enquirer@^2.3.6:
|
enquirer@^2.3.6:
|
||||||
version "2.3.6"
|
version "2.3.6"
|
||||||
resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d"
|
resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d"
|
||||||
|
@ -5896,6 +5917,24 @@ snapdragon@^0.8.1:
|
||||||
source-map-resolve "^0.5.0"
|
source-map-resolve "^0.5.0"
|
||||||
use "^3.1.0"
|
use "^3.1.0"
|
||||||
|
|
||||||
|
socket.io-client@^4.6.1:
|
||||||
|
version "4.6.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-4.6.1.tgz#80d97d5eb0feca448a0fb6d69a7b222d3d547eab"
|
||||||
|
integrity sha512-5UswCV6hpaRsNg5kkEHVcbBIXEYoVbMQaHJBXJCyEQ+CiFPV1NIOY0XOFWG4XR4GZcB8Kn6AsRs/9cy9TbqVMQ==
|
||||||
|
dependencies:
|
||||||
|
"@socket.io/component-emitter" "~3.1.0"
|
||||||
|
debug "~4.3.2"
|
||||||
|
engine.io-client "~6.4.0"
|
||||||
|
socket.io-parser "~4.2.1"
|
||||||
|
|
||||||
|
socket.io-parser@~4.2.1:
|
||||||
|
version "4.2.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-4.2.2.tgz#1dd384019e25b7a3d374877f492ab34f2ad0d206"
|
||||||
|
integrity sha512-DJtziuKypFkMMHCm2uIshOYC7QaylbtzQwiMYDuCKy3OPkjLzu4B2vAhTlqipRHHzrI0NJeBAizTK7X+6m1jVw==
|
||||||
|
dependencies:
|
||||||
|
"@socket.io/component-emitter" "~3.1.0"
|
||||||
|
debug "~4.3.1"
|
||||||
|
|
||||||
source-map-js@^1.0.2:
|
source-map-js@^1.0.2:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
|
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
|
||||||
|
@ -6713,6 +6752,11 @@ ws@^7.4.6:
|
||||||
resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591"
|
resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591"
|
||||||
integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==
|
integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==
|
||||||
|
|
||||||
|
ws@~8.11.0:
|
||||||
|
version "8.11.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/ws/-/ws-8.11.0.tgz#6a0d36b8edfd9f96d8b25683db2f8d7de6e8e143"
|
||||||
|
integrity sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==
|
||||||
|
|
||||||
xml-name-validator@^3.0.0:
|
xml-name-validator@^3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a"
|
resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a"
|
||||||
|
@ -6723,6 +6767,11 @@ xmlchars@^2.2.0:
|
||||||
resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb"
|
resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb"
|
||||||
integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==
|
integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==
|
||||||
|
|
||||||
|
xmlhttprequest-ssl@~2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz#91360c86b914e67f44dce769180027c0da618c67"
|
||||||
|
integrity sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==
|
||||||
|
|
||||||
y18n@^4.0.0:
|
y18n@^4.0.0:
|
||||||
version "4.0.3"
|
version "4.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf"
|
resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf"
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/bbui": "2.3.18-alpha.15",
|
"@budibase/bbui": "2.3.18-alpha.15",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
|
"socket.io-client": "^4.6.1",
|
||||||
"svelte": "^3.46.2"
|
"svelte": "^3.46.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
import { setContext, createEventDispatcher } from "svelte"
|
import { setContext, createEventDispatcher, onMount } from "svelte"
|
||||||
import { writable } from "svelte/store"
|
import { writable } from "svelte/store"
|
||||||
import { createReorderStores } from "./stores/reorder"
|
import { createReorderStores } from "./stores/reorder"
|
||||||
import { createViewportStores } from "./stores/viewport"
|
import { createViewportStores } from "./stores/viewport"
|
||||||
|
@ -15,6 +15,7 @@
|
||||||
import { createAPIClient } from "../../api"
|
import { createAPIClient } from "../../api"
|
||||||
import ScrollOverlay from "./ScrollOverlay.svelte"
|
import ScrollOverlay from "./ScrollOverlay.svelte"
|
||||||
import StickyColumn from "./StickyColumn.svelte"
|
import StickyColumn from "./StickyColumn.svelte"
|
||||||
|
import { createWebsocket } from "./websocket"
|
||||||
|
|
||||||
export let API
|
export let API
|
||||||
export let tableId
|
export let tableId
|
||||||
|
@ -62,6 +63,11 @@
|
||||||
|
|
||||||
// Set context for children to consume
|
// Set context for children to consume
|
||||||
setContext("spreadsheet", context)
|
setContext("spreadsheet", context)
|
||||||
|
|
||||||
|
// Initialise websocket for multi-user
|
||||||
|
onMount(() => {
|
||||||
|
return createWebsocket(context)
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="sheet" style="--cell-height:{cellHeight}px;" id="sheet-{rand}">
|
<div class="sheet" style="--cell-height:{cellHeight}px;" id="sheet-{rand}">
|
||||||
|
|
|
@ -72,31 +72,6 @@ export const createRowsStore = context => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
// Local handler to process new rows inside the fetch, and append any new
|
|
||||||
// rows to state that we haven't encountered before
|
|
||||||
const handleNewRows = newRows => {
|
|
||||||
let rowsToAppend = []
|
|
||||||
let newRow
|
|
||||||
for (let i = 0; i < newRows.length; i++) {
|
|
||||||
newRow = newRows[i]
|
|
||||||
if (!rowCacheMap[newRow._id]) {
|
|
||||||
rowCacheMap[newRow._id] = true
|
|
||||||
rowsToAppend.push(newRow)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (rowsToAppend.length) {
|
|
||||||
rows.update($rows => {
|
|
||||||
return [
|
|
||||||
...$rows,
|
|
||||||
...rowsToAppend.map((row, idx) => ({
|
|
||||||
...row,
|
|
||||||
__idx: $rows.length + idx,
|
|
||||||
})),
|
|
||||||
]
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Adds a new empty row
|
// Adds a new empty row
|
||||||
const addRow = async () => {
|
const addRow = async () => {
|
||||||
try {
|
try {
|
||||||
|
@ -127,6 +102,43 @@ export const createRowsStore = context => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Refreshes a specific row, handling updates, addition or deletion
|
||||||
|
const refreshRow = async id => {
|
||||||
|
// Get index of row to check if it exists
|
||||||
|
const $rows = get(rows)
|
||||||
|
const index = $rows.findIndex(row => row._id === id)
|
||||||
|
|
||||||
|
// Fetch row from the server again
|
||||||
|
const res = await API.searchTable({
|
||||||
|
tableId: get(tableId),
|
||||||
|
limit: 1,
|
||||||
|
query: {
|
||||||
|
equal: {
|
||||||
|
_id: id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
paginate: false,
|
||||||
|
})
|
||||||
|
let newRow = res?.rows?.[0]
|
||||||
|
|
||||||
|
// Process as either an update, addition or deletion
|
||||||
|
if (newRow) {
|
||||||
|
if (index !== -1) {
|
||||||
|
// An existing row was updated
|
||||||
|
rows.update(state => {
|
||||||
|
state[index] = { ...newRow, __idx: index }
|
||||||
|
return state
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// A new row was created
|
||||||
|
handleNewRows([newRow])
|
||||||
|
}
|
||||||
|
} else if (index !== -1) {
|
||||||
|
// A row was removed
|
||||||
|
handleRemoveRows([$rows[index]])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Updates a value of a row
|
// Updates a value of a row
|
||||||
const updateRow = async (rowId, column, value) => {
|
const updateRow = async (rowId, column, value) => {
|
||||||
const $rows = get(rows)
|
const $rows = get(rows)
|
||||||
|
@ -151,35 +163,11 @@ export const createRowsStore = context => {
|
||||||
notifications.error(`Error saving row: ${error?.message}`)
|
notifications.error(`Error saving row: ${error?.message}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch row from the server again
|
return await refreshRow(row._id)
|
||||||
const res = await API.searchTable({
|
|
||||||
tableId: get(tableId),
|
|
||||||
limit: 1,
|
|
||||||
query: {
|
|
||||||
equal: {
|
|
||||||
_id: row._id,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
paginate: false,
|
|
||||||
})
|
|
||||||
if (res?.rows?.[0]) {
|
|
||||||
newRow = res.rows[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update state again with this row
|
|
||||||
newRow = { ...newRow, __idx: row.__idx }
|
|
||||||
rows.update(state => {
|
|
||||||
state[index] = newRow
|
|
||||||
return state
|
|
||||||
})
|
|
||||||
|
|
||||||
return newRow
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deletes an array of rows
|
// Deletes an array of rows
|
||||||
const deleteRows = async rowsToDelete => {
|
const deleteRows = async rowsToDelete => {
|
||||||
const deletedIds = rowsToDelete.map(row => row._id)
|
|
||||||
|
|
||||||
// Actually delete rows
|
// Actually delete rows
|
||||||
rowsToDelete.forEach(row => {
|
rowsToDelete.forEach(row => {
|
||||||
delete row.__idx
|
delete row.__idx
|
||||||
|
@ -190,6 +178,38 @@ export const createRowsStore = context => {
|
||||||
})
|
})
|
||||||
|
|
||||||
// Update state
|
// Update state
|
||||||
|
handleRemoveRows(rowsToDelete)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Local handler to process new rows inside the fetch, and append any new
|
||||||
|
// rows to state that we haven't encountered before
|
||||||
|
const handleNewRows = newRows => {
|
||||||
|
let rowsToAppend = []
|
||||||
|
let newRow
|
||||||
|
for (let i = 0; i < newRows.length; i++) {
|
||||||
|
newRow = newRows[i]
|
||||||
|
if (!rowCacheMap[newRow._id]) {
|
||||||
|
rowCacheMap[newRow._id] = true
|
||||||
|
rowsToAppend.push(newRow)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (rowsToAppend.length) {
|
||||||
|
rows.update($rows => {
|
||||||
|
return [
|
||||||
|
...$rows,
|
||||||
|
...rowsToAppend.map((row, idx) => ({
|
||||||
|
...row,
|
||||||
|
__idx: $rows.length + idx,
|
||||||
|
})),
|
||||||
|
]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Local handler to remove rows from state
|
||||||
|
const handleRemoveRows = rowsToRemove => {
|
||||||
|
const deletedIds = rowsToRemove.map(row => row._id)
|
||||||
|
|
||||||
// We deliberately do not remove IDs from the cache map as the data may
|
// We deliberately do not remove IDs from the cache map as the data may
|
||||||
// still exist inside the fetch, but we don't want to add it again
|
// still exist inside the fetch, but we don't want to add it again
|
||||||
rows.update(state => {
|
rows.update(state => {
|
||||||
|
@ -217,6 +237,7 @@ export const createRowsStore = context => {
|
||||||
updateRow,
|
updateRow,
|
||||||
deleteRows,
|
deleteRows,
|
||||||
loadNextPage,
|
loadNextPage,
|
||||||
|
refreshRow,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
schema,
|
schema,
|
||||||
|
|
57
packages/frontend-core/src/components/sheet/websocket.js
Normal file
57
packages/frontend-core/src/components/sheet/websocket.js
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
import { derived, get } from "svelte/store"
|
||||||
|
import { io } from "socket.io-client"
|
||||||
|
|
||||||
|
export const createWebsocket = context => {
|
||||||
|
const { rows, config } = context
|
||||||
|
const tableId = derived(config, $config => $config.tableId)
|
||||||
|
|
||||||
|
// Determine connection info
|
||||||
|
const tls = location.protocol === "https:"
|
||||||
|
const proto = tls ? "wss:" : "ws:"
|
||||||
|
const host = location.hostname
|
||||||
|
const port = location.port || (tls ? 443 : 80)
|
||||||
|
const socket = io(`${proto}//${host}:${port}`, {
|
||||||
|
path: "/socket/dataspace",
|
||||||
|
// Cap reconnection attempts to 3 (total of 15 seconds before giving up)
|
||||||
|
reconnectionAttempts: 3,
|
||||||
|
// Delay reconnection attempt by 5 seconds
|
||||||
|
reconnectionDelay: 5000,
|
||||||
|
reconnectionDelayMax: 5000,
|
||||||
|
// Timeout after 4 seconds so we never stack requests
|
||||||
|
timeout: 4000,
|
||||||
|
})
|
||||||
|
|
||||||
|
const connectToDataspace = tableId => {
|
||||||
|
if (!socket.connected) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
console.log("Idenifying dataspace", tableId)
|
||||||
|
|
||||||
|
// Identify which dataspace we are editing
|
||||||
|
socket.emit("identify", tableId, response => {
|
||||||
|
// handle initial connection info
|
||||||
|
console.log("response", response)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Event handlers
|
||||||
|
socket.on("connect", () => {
|
||||||
|
connectToDataspace(get(tableId))
|
||||||
|
})
|
||||||
|
|
||||||
|
socket.on("row-update", data => {
|
||||||
|
if (data.id) {
|
||||||
|
rows.actions.refreshRow(data.id)
|
||||||
|
}
|
||||||
|
console.log(data)
|
||||||
|
})
|
||||||
|
|
||||||
|
socket.on("connect_error", err => {
|
||||||
|
console.log("Failed to connect to websocket:", err.message)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Change websocket connection when dataspace changes
|
||||||
|
tableId.subscribe(connectToDataspace)
|
||||||
|
|
||||||
|
return () => socket?.disconnect()
|
||||||
|
}
|
|
@ -2,12 +2,73 @@
|
||||||
# yarn lockfile v1
|
# yarn lockfile v1
|
||||||
|
|
||||||
|
|
||||||
|
"@socket.io/component-emitter@~3.1.0":
|
||||||
|
version "3.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz#96116f2a912e0c02817345b3c10751069920d553"
|
||||||
|
integrity sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==
|
||||||
|
|
||||||
|
debug@~4.3.1, debug@~4.3.2:
|
||||||
|
version "4.3.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
|
||||||
|
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
|
||||||
|
dependencies:
|
||||||
|
ms "2.1.2"
|
||||||
|
|
||||||
|
engine.io-client@~6.4.0:
|
||||||
|
version "6.4.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-6.4.0.tgz#88cd3082609ca86d7d3c12f0e746d12db4f47c91"
|
||||||
|
integrity sha512-GyKPDyoEha+XZ7iEqam49vz6auPnNJ9ZBfy89f+rMMas8AuiMWOZ9PVzu8xb9ZC6rafUqiGHSCfu22ih66E+1g==
|
||||||
|
dependencies:
|
||||||
|
"@socket.io/component-emitter" "~3.1.0"
|
||||||
|
debug "~4.3.1"
|
||||||
|
engine.io-parser "~5.0.3"
|
||||||
|
ws "~8.11.0"
|
||||||
|
xmlhttprequest-ssl "~2.0.0"
|
||||||
|
|
||||||
|
engine.io-parser@~5.0.3:
|
||||||
|
version "5.0.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-5.0.6.tgz#7811244af173e157295dec9b2718dfe42a64ef45"
|
||||||
|
integrity sha512-tjuoZDMAdEhVnSFleYPCtdL2GXwVTGtNjoeJd9IhIG3C1xs9uwxqRNEu5WpnDZCaozwVlK/nuQhpodhXSIMaxw==
|
||||||
|
|
||||||
lodash@^4.17.21:
|
lodash@^4.17.21:
|
||||||
version "4.17.21"
|
version "4.17.21"
|
||||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
|
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
|
||||||
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
|
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
|
||||||
|
|
||||||
|
ms@2.1.2:
|
||||||
|
version "2.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
|
||||||
|
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
|
||||||
|
|
||||||
|
socket.io-client@^4.6.1:
|
||||||
|
version "4.6.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-4.6.1.tgz#80d97d5eb0feca448a0fb6d69a7b222d3d547eab"
|
||||||
|
integrity sha512-5UswCV6hpaRsNg5kkEHVcbBIXEYoVbMQaHJBXJCyEQ+CiFPV1NIOY0XOFWG4XR4GZcB8Kn6AsRs/9cy9TbqVMQ==
|
||||||
|
dependencies:
|
||||||
|
"@socket.io/component-emitter" "~3.1.0"
|
||||||
|
debug "~4.3.2"
|
||||||
|
engine.io-client "~6.4.0"
|
||||||
|
socket.io-parser "~4.2.1"
|
||||||
|
|
||||||
|
socket.io-parser@~4.2.1:
|
||||||
|
version "4.2.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-4.2.2.tgz#1dd384019e25b7a3d374877f492ab34f2ad0d206"
|
||||||
|
integrity sha512-DJtziuKypFkMMHCm2uIshOYC7QaylbtzQwiMYDuCKy3OPkjLzu4B2vAhTlqipRHHzrI0NJeBAizTK7X+6m1jVw==
|
||||||
|
dependencies:
|
||||||
|
"@socket.io/component-emitter" "~3.1.0"
|
||||||
|
debug "~4.3.1"
|
||||||
|
|
||||||
svelte@^3.46.2:
|
svelte@^3.46.2:
|
||||||
version "3.49.0"
|
version "3.49.0"
|
||||||
resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.49.0.tgz#5baee3c672306de1070c3b7888fc2204e36a4029"
|
resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.49.0.tgz#5baee3c672306de1070c3b7888fc2204e36a4029"
|
||||||
integrity sha512-+lmjic1pApJWDfPCpUUTc1m8azDqYCG1JN9YEngrx/hUyIcFJo6VZhj0A1Ai0wqoHcEIuQy+e9tk+4uDgdtsFA==
|
integrity sha512-+lmjic1pApJWDfPCpUUTc1m8azDqYCG1JN9YEngrx/hUyIcFJo6VZhj0A1Ai0wqoHcEIuQy+e9tk+4uDgdtsFA==
|
||||||
|
|
||||||
|
ws@~8.11.0:
|
||||||
|
version "8.11.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/ws/-/ws-8.11.0.tgz#6a0d36b8edfd9f96d8b25683db2f8d7de6e8e143"
|
||||||
|
integrity sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==
|
||||||
|
|
||||||
|
xmlhttprequest-ssl@~2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz#91360c86b914e67f44dce769180027c0da618c67"
|
||||||
|
integrity sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==
|
||||||
|
|
|
@ -62,6 +62,7 @@
|
||||||
"bull": "4.10.1",
|
"bull": "4.10.1",
|
||||||
"chmodr": "1.2.0",
|
"chmodr": "1.2.0",
|
||||||
"chokidar": "3.5.3",
|
"chokidar": "3.5.3",
|
||||||
|
"cookies": "0.8.0",
|
||||||
"csvtojson": "2.0.10",
|
"csvtojson": "2.0.10",
|
||||||
"curlconverter": "3.21.0",
|
"curlconverter": "3.21.0",
|
||||||
"dd-trace": "3.13.2",
|
"dd-trace": "3.13.2",
|
||||||
|
@ -107,7 +108,7 @@
|
||||||
"redis": "4",
|
"redis": "4",
|
||||||
"server-destroy": "1.0.1",
|
"server-destroy": "1.0.1",
|
||||||
"snowflake-promise": "^4.5.0",
|
"snowflake-promise": "^4.5.0",
|
||||||
"socket.io": "^4.5.1",
|
"socket.io": "4.6.1",
|
||||||
"svelte": "3.49.0",
|
"svelte": "3.49.0",
|
||||||
"swagger-parser": "10.0.3",
|
"swagger-parser": "10.0.3",
|
||||||
"tar": "6.1.11",
|
"tar": "6.1.11",
|
||||||
|
|
|
@ -7,7 +7,7 @@ import {
|
||||||
} from "@budibase/backend-core"
|
} from "@budibase/backend-core"
|
||||||
import { PluginType, FileType, PluginSource, Plugin } from "@budibase/types"
|
import { PluginType, FileType, PluginSource, Plugin } from "@budibase/types"
|
||||||
import env from "../../../environment"
|
import env from "../../../environment"
|
||||||
import { ClientAppSocket } from "../../../websocket"
|
import { clientAppSocket } from "../../../websockets"
|
||||||
import { sdk as pro } from "@budibase/pro"
|
import { sdk as pro } from "@budibase/pro"
|
||||||
|
|
||||||
export async function getPlugins(type?: PluginType) {
|
export async function getPlugins(type?: PluginType) {
|
||||||
|
@ -91,7 +91,7 @@ export async function create(ctx: any) {
|
||||||
|
|
||||||
const doc = await pro.plugins.storePlugin(metadata, directory, source)
|
const doc = await pro.plugins.storePlugin(metadata, directory, source)
|
||||||
|
|
||||||
ClientAppSocket.emit("plugins-update", { name, hash: doc.hash })
|
clientAppSocket.emit("plugins-update", { name, hash: doc.hash })
|
||||||
ctx.body = {
|
ctx.body = {
|
||||||
message: "Plugin uploaded successfully",
|
message: "Plugin uploaded successfully",
|
||||||
plugins: [doc],
|
plugins: [doc],
|
||||||
|
@ -133,6 +133,6 @@ export async function processUploadedPlugin(
|
||||||
}
|
}
|
||||||
|
|
||||||
const doc = await pro.plugins.storePlugin(metadata, directory, source)
|
const doc = await pro.plugins.storePlugin(metadata, directory, source)
|
||||||
ClientAppSocket.emit("plugin-update", { name: doc.name, hash: doc.hash })
|
clientAppSocket.emit("plugin-update", { name: doc.name, hash: doc.hash })
|
||||||
return doc
|
return doc
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { quotas } from "@budibase/pro"
|
||||||
import * as internal from "./internal"
|
import * as internal from "./internal"
|
||||||
import * as external from "./external"
|
import * as external from "./external"
|
||||||
import { isExternalTable } from "../../../integrations/utils"
|
import { isExternalTable } from "../../../integrations/utils"
|
||||||
|
import { dataspaceSocket } from "../../../websockets"
|
||||||
|
|
||||||
function pickApi(tableId: any) {
|
function pickApi(tableId: any) {
|
||||||
if (isExternalTable(tableId)) {
|
if (isExternalTable(tableId)) {
|
||||||
|
@ -45,6 +46,9 @@ export async function patch(ctx: any): Promise<any> {
|
||||||
ctx.eventEmitter.emitRow(`row:update`, appId, row, table)
|
ctx.eventEmitter.emitRow(`row:update`, appId, row, table)
|
||||||
ctx.message = `${table.name} updated successfully.`
|
ctx.message = `${table.name} updated successfully.`
|
||||||
ctx.body = row
|
ctx.body = row
|
||||||
|
|
||||||
|
// Notify websocket change
|
||||||
|
dataspaceSocket.emit("row-update", { id: row._id })
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
ctx.throw(400, err)
|
ctx.throw(400, err)
|
||||||
}
|
}
|
||||||
|
@ -67,6 +71,9 @@ export const save = async (ctx: any) => {
|
||||||
ctx.eventEmitter && ctx.eventEmitter.emitRow(`row:save`, appId, row, table)
|
ctx.eventEmitter && ctx.eventEmitter.emitRow(`row:save`, appId, row, table)
|
||||||
ctx.message = `${table.name} saved successfully`
|
ctx.message = `${table.name} saved successfully`
|
||||||
ctx.body = row
|
ctx.body = row
|
||||||
|
|
||||||
|
// Notify websocket change
|
||||||
|
dataspaceSocket.emit("row-update", { id: row._id })
|
||||||
}
|
}
|
||||||
export async function fetchView(ctx: any) {
|
export async function fetchView(ctx: any) {
|
||||||
const tableId = getTableId(ctx)
|
const tableId = getTableId(ctx)
|
||||||
|
@ -105,6 +112,8 @@ export async function destroy(ctx: any) {
|
||||||
response = rows
|
response = rows
|
||||||
for (let row of rows) {
|
for (let row of rows) {
|
||||||
ctx.eventEmitter && ctx.eventEmitter.emitRow(`row:delete`, appId, row)
|
ctx.eventEmitter && ctx.eventEmitter.emitRow(`row:delete`, appId, row)
|
||||||
|
// Notify websocket change
|
||||||
|
dataspaceSocket.emit("row-update", { id: row._id })
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let resp = await quotas.addQuery(() => pickApi(tableId).destroy(ctx), {
|
let resp = await quotas.addQuery(() => pickApi(tableId).destroy(ctx), {
|
||||||
|
@ -114,6 +123,8 @@ export async function destroy(ctx: any) {
|
||||||
response = resp.response
|
response = resp.response
|
||||||
row = resp.row
|
row = resp.row
|
||||||
ctx.eventEmitter && ctx.eventEmitter.emitRow(`row:delete`, appId, row)
|
ctx.eventEmitter && ctx.eventEmitter.emitRow(`row:delete`, appId, row)
|
||||||
|
// Notify websocket change
|
||||||
|
dataspaceSocket.emit("row-update", { id: row._id })
|
||||||
}
|
}
|
||||||
ctx.status = 200
|
ctx.status = 200
|
||||||
// for automations include the row that was deleted
|
// for automations include the row that was deleted
|
||||||
|
|
|
@ -28,7 +28,7 @@ import * as automations from "./automations"
|
||||||
import { Thread } from "./threads"
|
import { Thread } from "./threads"
|
||||||
import * as redis from "./utilities/redis"
|
import * as redis from "./utilities/redis"
|
||||||
import { events, logging, middleware } from "@budibase/backend-core"
|
import { events, logging, middleware } from "@budibase/backend-core"
|
||||||
import { initialise as initialiseWebsockets } from "./websocket"
|
import { initialise as initialiseWebsockets } from "./websockets"
|
||||||
import { startup } from "./startup"
|
import { startup } from "./startup"
|
||||||
const Sentry = require("@sentry/node")
|
const Sentry = require("@sentry/node")
|
||||||
const destroyable = require("server-destroy")
|
const destroyable = require("server-destroy")
|
||||||
|
@ -72,7 +72,7 @@ if (env.isProd()) {
|
||||||
|
|
||||||
const server = http.createServer(app.callback())
|
const server = http.createServer(app.callback())
|
||||||
destroyable(server)
|
destroyable(server)
|
||||||
initialiseWebsockets(server)
|
initialiseWebsockets(app, server)
|
||||||
|
|
||||||
let shuttingDown = false,
|
let shuttingDown = false,
|
||||||
errCode = 0
|
errCode = 0
|
||||||
|
|
|
@ -35,12 +35,12 @@ async function checkDevAppLocks(ctx: BBContext) {
|
||||||
if (!appId || !appId.startsWith(APP_DEV_PREFIX)) {
|
if (!appId || !appId.startsWith(APP_DEV_PREFIX)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (!(await doesUserHaveLock(appId, ctx.user))) {
|
// if (!(await doesUserHaveLock(appId, ctx.user))) {
|
||||||
ctx.throw(400, "User does not hold app lock.")
|
// ctx.throw(400, "User does not hold app lock.")
|
||||||
}
|
// }
|
||||||
|
|
||||||
// they do have lock, update it
|
// they do have lock, update it
|
||||||
await updateLock(appId, ctx.user)
|
// await updateLock(appId, ctx.user)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function updateAppUpdatedAt(ctx: BBContext) {
|
async function updateAppUpdatedAt(ctx: BBContext) {
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
import { Server } from "socket.io"
|
|
||||||
import http from "http"
|
|
||||||
|
|
||||||
class Websocket {
|
|
||||||
socketServer: Server
|
|
||||||
|
|
||||||
constructor(server: http.Server, path: string) {
|
|
||||||
this.socketServer = new Server(server, {
|
|
||||||
path,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Emit an event to all sockets
|
|
||||||
emit(event: string, payload: any) {
|
|
||||||
this.socketServer.sockets.emit(event, payload)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Likely to be more socket instances in future
|
|
||||||
let ClientAppSocket: Websocket
|
|
||||||
|
|
||||||
export const initialise = (server: http.Server) => {
|
|
||||||
ClientAppSocket = new Websocket(server, "/socket/client")
|
|
||||||
}
|
|
||||||
|
|
||||||
export { ClientAppSocket }
|
|
11
packages/server/src/websockets/client.ts
Normal file
11
packages/server/src/websockets/client.ts
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import Socket from "./websocket"
|
||||||
|
import authorized from "../middleware/authorized"
|
||||||
|
import http from "http"
|
||||||
|
import Koa from "koa"
|
||||||
|
import { permissions } from "@budibase/backend-core"
|
||||||
|
|
||||||
|
export default class ClientAppWebsocket extends Socket {
|
||||||
|
constructor(app: Koa, server: http.Server) {
|
||||||
|
super(app, server, "/socket/client", [authorized(permissions.BUILDER)])
|
||||||
|
}
|
||||||
|
}
|
29
packages/server/src/websockets/dataspace.ts
Normal file
29
packages/server/src/websockets/dataspace.ts
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
import authorized from "../middleware/authorized"
|
||||||
|
import Socket from "./websocket"
|
||||||
|
import { permissions } from "@budibase/backend-core"
|
||||||
|
import http from "http"
|
||||||
|
import Koa from "koa"
|
||||||
|
|
||||||
|
export default class DataspaceSocket extends Socket {
|
||||||
|
constructor(app: Koa, server: http.Server) {
|
||||||
|
super(app, server, "/socket/dataspace", [authorized(permissions.BUILDER)])
|
||||||
|
|
||||||
|
this.io.on("connection", socket => {
|
||||||
|
const user = socket.data.user
|
||||||
|
console.log(`Dataspace user connected: ${user?._id}`)
|
||||||
|
|
||||||
|
// Initial identification of conneted dataspace
|
||||||
|
socket.on("identify", async (tableId, callback) => {
|
||||||
|
socket.join(tableId)
|
||||||
|
|
||||||
|
const sockets = await this.io.in(tableId).fetchSockets()
|
||||||
|
callback(sockets.map(socket => socket.data.user))
|
||||||
|
})
|
||||||
|
|
||||||
|
// Disconnection cleanup
|
||||||
|
socket.on("disconnect", reason => {
|
||||||
|
console.log(`Disconnecting ${user.email} because of ${reason}`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
14
packages/server/src/websockets/index.ts
Normal file
14
packages/server/src/websockets/index.ts
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
import http from "http"
|
||||||
|
import Koa from "koa"
|
||||||
|
import DataspaceSocket from "./dataspace"
|
||||||
|
import ClientAppSocket from "./client"
|
||||||
|
|
||||||
|
let clientAppSocket: ClientAppSocket
|
||||||
|
let dataspaceSocket: DataspaceSocket
|
||||||
|
|
||||||
|
export const initialise = (app: Koa, server: http.Server) => {
|
||||||
|
clientAppSocket = new ClientAppSocket(app, server)
|
||||||
|
dataspaceSocket = new DataspaceSocket(app, server)
|
||||||
|
}
|
||||||
|
|
||||||
|
export { clientAppSocket, dataspaceSocket }
|
73
packages/server/src/websockets/websocket.ts
Normal file
73
packages/server/src/websockets/websocket.ts
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
import { Server } from "socket.io"
|
||||||
|
import http from "http"
|
||||||
|
import Koa from "koa"
|
||||||
|
import Cookies from "cookies"
|
||||||
|
import { userAgent } from "koa-useragent"
|
||||||
|
import { auth } from "@budibase/backend-core"
|
||||||
|
|
||||||
|
export default class Socket {
|
||||||
|
io: Server
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
app: Koa,
|
||||||
|
server: http.Server,
|
||||||
|
path: string,
|
||||||
|
additionalMiddlewares?: any[]
|
||||||
|
) {
|
||||||
|
this.io = new Server(server, {
|
||||||
|
path,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Attach default middlewares
|
||||||
|
const authenticate = auth.buildAuthMiddleware([], {
|
||||||
|
publicAllowed: true,
|
||||||
|
})
|
||||||
|
const middlewares = [
|
||||||
|
userAgent,
|
||||||
|
authenticate,
|
||||||
|
...(additionalMiddlewares || []),
|
||||||
|
]
|
||||||
|
|
||||||
|
// Apply middlewares
|
||||||
|
this.io.use(async (socket, next) => {
|
||||||
|
// Build fake koa context
|
||||||
|
const res = new http.ServerResponse(socket.request)
|
||||||
|
const ctx: any = {
|
||||||
|
...app.createContext(socket.request, res),
|
||||||
|
|
||||||
|
// Additional overrides needed to make our middlewares work with this
|
||||||
|
// fake koa context
|
||||||
|
cookies: new Cookies(socket.request, res),
|
||||||
|
get: (field: string) => socket.request.headers[field],
|
||||||
|
throw: (code: number, message: string) => {
|
||||||
|
throw new Error(message)
|
||||||
|
},
|
||||||
|
|
||||||
|
// Needed for koa-useragent middleware
|
||||||
|
header: socket.request.headers,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run all koa middlewares
|
||||||
|
try {
|
||||||
|
for (let [idx, middleware] of middlewares.entries()) {
|
||||||
|
await middleware(ctx, () => {
|
||||||
|
if (idx === middlewares.length - 1) {
|
||||||
|
// Middlewares are finished.
|
||||||
|
// Extract some data from our enriched koa context to persist
|
||||||
|
// as metadata for the socket
|
||||||
|
socket.data.user = ctx.user
|
||||||
|
next()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emit an event to all sockets
|
||||||
|
emit(event: string, payload: any) {
|
||||||
|
this.io.sockets.emit(event, payload)
|
||||||
|
}
|
||||||
|
}
|
|
@ -5647,7 +5647,7 @@ cookiejar@^2.1.3:
|
||||||
resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.4.tgz#ee669c1fea2cf42dc31585469d193fef0d65771b"
|
resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.4.tgz#ee669c1fea2cf42dc31585469d193fef0d65771b"
|
||||||
integrity sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==
|
integrity sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==
|
||||||
|
|
||||||
cookies@~0.8.0:
|
cookies@0.8.0, cookies@~0.8.0:
|
||||||
version "0.8.0"
|
version "0.8.0"
|
||||||
resolved "https://registry.yarnpkg.com/cookies/-/cookies-0.8.0.tgz#1293ce4b391740a8406e3c9870e828c4b54f3f90"
|
resolved "https://registry.yarnpkg.com/cookies/-/cookies-0.8.0.tgz#1293ce4b391740a8406e3c9870e828c4b54f3f90"
|
||||||
integrity sha512-8aPsApQfebXnuI+537McwYsDtjVxGm8gTIzQI3FDW6t5t/DAhERxtnbEPN/8RX+uZthoz4eCOgloXaE5cYyNow==
|
integrity sha512-8aPsApQfebXnuI+537McwYsDtjVxGm8gTIzQI3FDW6t5t/DAhERxtnbEPN/8RX+uZthoz4eCOgloXaE5cYyNow==
|
||||||
|
@ -6462,10 +6462,10 @@ engine.io-parser@~5.0.3:
|
||||||
resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-5.0.4.tgz#0b13f704fa9271b3ec4f33112410d8f3f41d0fc0"
|
resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-5.0.4.tgz#0b13f704fa9271b3ec4f33112410d8f3f41d0fc0"
|
||||||
integrity sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg==
|
integrity sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg==
|
||||||
|
|
||||||
engine.io@~6.2.0:
|
engine.io@~6.4.1:
|
||||||
version "6.2.0"
|
version "6.4.1"
|
||||||
resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-6.2.0.tgz#003bec48f6815926f2b1b17873e576acd54f41d0"
|
resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-6.4.1.tgz#8056b4526a88e779f9c280d820422d4e3eeaaae5"
|
||||||
integrity sha512-4KzwW3F3bk+KlzSOY57fj/Jx6LyRQ1nbcyIadehl+AnXjKT7gDO0ORdRi/84ixvMKTym6ZKuxvbzN62HDDU1Lg==
|
integrity sha512-JFYQurD/nbsA5BSPmbaOSLa3tSVj8L6o4srSwXXY3NqE+gGUNmmPTbhn8tjzcCtSqhFgIeqef81ngny8JM25hw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/cookie" "^0.4.1"
|
"@types/cookie" "^0.4.1"
|
||||||
"@types/cors" "^2.8.12"
|
"@types/cors" "^2.8.12"
|
||||||
|
@ -6476,7 +6476,7 @@ engine.io@~6.2.0:
|
||||||
cors "~2.8.5"
|
cors "~2.8.5"
|
||||||
debug "~4.3.1"
|
debug "~4.3.1"
|
||||||
engine.io-parser "~5.0.3"
|
engine.io-parser "~5.0.3"
|
||||||
ws "~8.2.3"
|
ws "~8.11.0"
|
||||||
|
|
||||||
enhanced-resolve@^5.9.3:
|
enhanced-resolve@^5.9.3:
|
||||||
version "5.9.3"
|
version "5.9.3"
|
||||||
|
@ -13943,30 +13943,32 @@ snowflake-sdk@^1.6.0:
|
||||||
uuid "^3.3.2"
|
uuid "^3.3.2"
|
||||||
winston "^3.1.0"
|
winston "^3.1.0"
|
||||||
|
|
||||||
socket.io-adapter@~2.4.0:
|
socket.io-adapter@~2.5.2:
|
||||||
version "2.4.0"
|
version "2.5.2"
|
||||||
resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-2.4.0.tgz#b50a4a9ecdd00c34d4c8c808224daa1a786152a6"
|
resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-2.5.2.tgz#5de9477c9182fdc171cd8c8364b9a8894ec75d12"
|
||||||
integrity sha512-W4N+o69rkMEGVuk2D/cvca3uYsvGlMwsySWV447y99gUPghxq42BxqLNMndb+a1mm/5/7NeXVQS7RLa2XyXvYg==
|
integrity sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA==
|
||||||
|
dependencies:
|
||||||
|
ws "~8.11.0"
|
||||||
|
|
||||||
socket.io-parser@~4.2.0:
|
socket.io-parser@~4.2.1:
|
||||||
version "4.2.1"
|
version "4.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-4.2.1.tgz#01c96efa11ded938dcb21cbe590c26af5eff65e5"
|
resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-4.2.2.tgz#1dd384019e25b7a3d374877f492ab34f2ad0d206"
|
||||||
integrity sha512-V4GrkLy+HeF1F/en3SpUaM+7XxYXpuMUWLGde1kSSh5nQMN4hLrbPIkD+otwh6q9R6NOQBN4AMaOZ2zVjui82g==
|
integrity sha512-DJtziuKypFkMMHCm2uIshOYC7QaylbtzQwiMYDuCKy3OPkjLzu4B2vAhTlqipRHHzrI0NJeBAizTK7X+6m1jVw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@socket.io/component-emitter" "~3.1.0"
|
"@socket.io/component-emitter" "~3.1.0"
|
||||||
debug "~4.3.1"
|
debug "~4.3.1"
|
||||||
|
|
||||||
socket.io@^4.5.1:
|
socket.io@4.6.1:
|
||||||
version "4.5.2"
|
version "4.6.1"
|
||||||
resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-4.5.2.tgz#1eb25fd380ab3d63470aa8279f8e48d922d443ac"
|
resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-4.6.1.tgz#62ec117e5fce0692fa50498da9347cfb52c3bc70"
|
||||||
integrity sha512-6fCnk4ARMPZN448+SQcnn1u8OHUC72puJcNtSgg2xS34Cu7br1gQ09YKkO1PFfDn/wyUE9ZgMAwosJed003+NQ==
|
integrity sha512-KMcaAi4l/8+xEjkRICl6ak8ySoxsYG+gG6/XfRCPJPQ/haCRIJBTL4wIl8YCsmtaBovcAXGLOShyVWQ/FG8GZA==
|
||||||
dependencies:
|
dependencies:
|
||||||
accepts "~1.3.4"
|
accepts "~1.3.4"
|
||||||
base64id "~2.0.0"
|
base64id "~2.0.0"
|
||||||
debug "~4.3.2"
|
debug "~4.3.2"
|
||||||
engine.io "~6.2.0"
|
engine.io "~6.4.1"
|
||||||
socket.io-adapter "~2.4.0"
|
socket.io-adapter "~2.5.2"
|
||||||
socket.io-parser "~4.2.0"
|
socket.io-parser "~4.2.1"
|
||||||
|
|
||||||
socks@^2.7.0:
|
socks@^2.7.0:
|
||||||
version "2.7.0"
|
version "2.7.0"
|
||||||
|
@ -15936,10 +15938,10 @@ ws@^5.2.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
async-limiter "~1.0.0"
|
async-limiter "~1.0.0"
|
||||||
|
|
||||||
ws@~8.2.3:
|
ws@~8.11.0:
|
||||||
version "8.2.3"
|
version "8.11.0"
|
||||||
resolved "https://registry.yarnpkg.com/ws/-/ws-8.2.3.tgz#63a56456db1b04367d0b721a0b80cae6d8becbba"
|
resolved "https://registry.yarnpkg.com/ws/-/ws-8.11.0.tgz#6a0d36b8edfd9f96d8b25683db2f8d7de6e8e143"
|
||||||
integrity sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==
|
integrity sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==
|
||||||
|
|
||||||
x3-linkedlist@1.2.0:
|
x3-linkedlist@1.2.0:
|
||||||
version "1.2.0"
|
version "1.2.0"
|
||||||
|
|
Loading…
Reference in a new issue