@persistica/flux-mesh 0.0.2 → 0.0.4-1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -1
- package/index.js +6 -0
- package/package.json +22 -21
- package/src/_classes/web-rtc-client-interface.class.d.ts +20 -0
- package/src/_routes/auth/network-authority.post.route.d.ts +7 -0
- package/src/_routes/auth/network-client.post.route.d.ts +12 -0
- package/src/_routes/options.route.d.ts +6 -0
- package/src/auth/auth.d.ts +22 -0
- package/src/business-logic/channels/channel-manager.class.d.ts +69 -0
- package/src/connected-client-socket.types.d.ts +21 -0
- package/src/index.d.ts +1 -0
- package/src/main.d.ts +25 -0
- package/src/orchestrators/facilitate-webrtc-connection.class.d.ts +2 -0
- package/src/register/network-client-manager.class.d.ts +38 -0
- package/src/register/register-network-authority.class.d.ts +34 -0
- package/src/routing/addressing.utils.d.ts +9 -0
- package/src/routing/global-channel/global-channel-pubsub.class.d.ts +34 -0
- package/src/routing/incomming-message-router.class.d.ts +9 -0
- package/src/routing/outgoing-message-router.class.d.ts +19 -0
- package/src/routing/process-message-router.class.d.ts +28 -0
- package/src/routing/redis/hash/network-agent.redis.sorted-set.d.ts +26 -0
- package/src/routing/redis/network-agent.redis.d.ts +43 -0
- package/src/routing/redis/redis-connection.class.d.ts +65 -0
- package/src/routing/rpc/core/global-rpc-client.class.d.ts +57 -0
- package/src/test.spec.d.ts +1 -0
- package/build.js +0 -21
- package/message-routing.excalidraw +0 -2433
- package/project.json +0 -79
- package/src/_classes/web-rtc-client-interface.class.ts +0 -79
- package/src/_routes/auth/network-authority.post.route.ts +0 -74
- package/src/_routes/auth/network-client.post.route.ts +0 -169
- package/src/_routes/options.route.ts +0 -9
- package/src/auth/auth.ts +0 -29
- package/src/business-logic/channels/channel-manager.class.ts +0 -209
- package/src/index.ts +0 -3
- package/src/main.ts +0 -680
- package/src/orchestrators/facilitate-webrtc-connection.class.ts +0 -23
- package/src/register/network-client-manager.class.ts +0 -175
- package/src/register/register-network-authority.class.ts +0 -123
- package/src/routing/addressing.utils.ts +0 -34
- package/src/routing/global-channel/global-channel-pubsub.class.ts +0 -76
- package/src/routing/incomming-message-router.class.ts +0 -47
- package/src/routing/outgoing-message-router.class.ts +0 -72
- package/src/routing/process-message-router.class.ts +0 -85
- package/src/routing/redis/hash/network-agent.redis.sorted-set.ts +0 -60
- package/src/routing/redis/network-agent.redis.ts +0 -135
- package/src/routing/redis/redis-connection.class.ts +0 -261
- package/src/routing/rpc/core/global-rpc-client.class.ts +0 -215
- package/src/test.spec.ts +0 -7
- package/tsconfig.json +0 -23
- package/tsconfig.lib.json +0 -20
package/project.json
DELETED
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "persistica-flux-mesh",
|
|
3
|
-
"$schema": "../../../node_modules/nx/schemas/project-schema.json",
|
|
4
|
-
"sourceRoot": "packages/flux/mesh/src",
|
|
5
|
-
"projectType": "library",
|
|
6
|
-
"tags": [
|
|
7
|
-
"type:npm-package",
|
|
8
|
-
"scope:any"
|
|
9
|
-
],
|
|
10
|
-
"targets": {
|
|
11
|
-
"bun-serve": {
|
|
12
|
-
"executor": "nx:run-commands",
|
|
13
|
-
"options": {
|
|
14
|
-
"command": "bun --watch run ./packages/flux/mesh/src/main.ts"
|
|
15
|
-
}
|
|
16
|
-
},
|
|
17
|
-
"build": {
|
|
18
|
-
"executor": "nx:run-commands",
|
|
19
|
-
"options": {
|
|
20
|
-
"command": "bun run {projectRoot}/build.js",
|
|
21
|
-
"args": [
|
|
22
|
-
"--projectRoot={projectRoot}"
|
|
23
|
-
]
|
|
24
|
-
}
|
|
25
|
-
},
|
|
26
|
-
"serve": {
|
|
27
|
-
"executor": "@nx/js:node",
|
|
28
|
-
"defaultConfiguration": "development",
|
|
29
|
-
"dependsOn": [
|
|
30
|
-
"build"
|
|
31
|
-
],
|
|
32
|
-
"options": {
|
|
33
|
-
"buildTarget": "persistica-flux-mesh:build",
|
|
34
|
-
"runBuildTargetDependencies": false
|
|
35
|
-
},
|
|
36
|
-
"configurations": {
|
|
37
|
-
"development": {
|
|
38
|
-
"buildTarget": "persistica-flux-mesh:build:development"
|
|
39
|
-
},
|
|
40
|
-
"production": {
|
|
41
|
-
"buildTarget": "persistica-flux-mesh:build:production"
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
},
|
|
45
|
-
"nx-release-publish": {
|
|
46
|
-
"options": {
|
|
47
|
-
"packageRoot": "dist/{projectRoot}"
|
|
48
|
-
}
|
|
49
|
-
},
|
|
50
|
-
"npm-publish": {
|
|
51
|
-
"executor": "nx:run-commands",
|
|
52
|
-
"options": {
|
|
53
|
-
"commands": [
|
|
54
|
-
"npm login",
|
|
55
|
-
"npm publish --access public"
|
|
56
|
-
]
|
|
57
|
-
},
|
|
58
|
-
"dependsOn": [
|
|
59
|
-
"build"
|
|
60
|
-
]
|
|
61
|
-
},
|
|
62
|
-
"bun-test": {
|
|
63
|
-
"executor": "nx:run-commands",
|
|
64
|
-
"options": {
|
|
65
|
-
"command": "bun test {projectRoot}/ --env-file=.env.e2e"
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
},
|
|
69
|
-
"release": {
|
|
70
|
-
"version": {
|
|
71
|
-
"currentVersionResolver": "git-tag",
|
|
72
|
-
"fallbackCurrentVersionResolver": "disk",
|
|
73
|
-
"preserveLocalDependencyProtocols": false,
|
|
74
|
-
"manifestRootsToUpdate": [
|
|
75
|
-
"dist/{projectRoot}"
|
|
76
|
-
]
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
}
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
TProcessAddress,
|
|
3
|
-
} from '@flux/shared/types';
|
|
4
|
-
import { GlobalRPCClient2 } from '../routing/rpc/core/global-rpc-client.class';
|
|
5
|
-
import {
|
|
6
|
-
type TRPCResponseCallbackFunction,
|
|
7
|
-
RPCClient,
|
|
8
|
-
} from '@flux/shared/ws';
|
|
9
|
-
|
|
10
|
-
export class WebRTCClient extends RPCClient<
|
|
11
|
-
'createOffer' | 'acceptOffer' | 'acceptAnswer' | 'answerAcceptedByInitiator'
|
|
12
|
-
> {
|
|
13
|
-
constructor(
|
|
14
|
-
private readonly _originProcessAddress: TProcessAddress,
|
|
15
|
-
private readonly _sendToRPCServer: (
|
|
16
|
-
data: any, // ! Use 'unknown' or Bun.BufferSource (does not work)
|
|
17
|
-
compress?: boolean | undefined
|
|
18
|
-
) => number,
|
|
19
|
-
|
|
20
|
-
// Handle the response from the RPC server
|
|
21
|
-
private readonly _handleResponseMessage: (
|
|
22
|
-
fn: TRPCResponseCallbackFunction,
|
|
23
|
-
) => void
|
|
24
|
-
) {
|
|
25
|
-
super(_sendToRPCServer, _handleResponseMessage);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
public createOffer(): Promise<unknown> {
|
|
29
|
-
// TODO TYPE OFFER
|
|
30
|
-
return super.call(this._originProcessAddress, 'createOffer');
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
public acceptOfferAndCreateAnswer(
|
|
34
|
-
offer: unknown // TODO TYPE OFFER
|
|
35
|
-
): Promise<unknown> {
|
|
36
|
-
// TODO TYOPE answer
|
|
37
|
-
return super.call(this._originProcessAddress, 'acceptOffer', offer);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
public acceptAnswer(
|
|
41
|
-
answer: unknown // TODO TYPE answer
|
|
42
|
-
): Promise<boolean> {
|
|
43
|
-
return super.call(this._originProcessAddress, 'acceptAnswer', answer);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
public answerWasAccepted(answerAccepted: boolean): Promise<boolean> {
|
|
47
|
-
return super.call(
|
|
48
|
-
this._originProcessAddress,
|
|
49
|
-
'answerAcceptedByInitiator',
|
|
50
|
-
answerAccepted
|
|
51
|
-
);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export class GlobalWebRTCClient extends GlobalRPCClient2<
|
|
56
|
-
'createOffer' | 'acceptOffer' | 'acceptAnswer' | 'answerAcceptedByInitiator'
|
|
57
|
-
> {
|
|
58
|
-
public createOffer(): Promise<unknown> {
|
|
59
|
-
// TODO TYPE OFFER
|
|
60
|
-
return super.call('createOffer');
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
public acceptOfferAndCreateAnswer(
|
|
64
|
-
offer: unknown // TODO TYPE OFFER
|
|
65
|
-
): Promise<unknown> {
|
|
66
|
-
// TODO TYOPE answer
|
|
67
|
-
return super.call('acceptOffer', offer);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
public acceptAnswer(
|
|
71
|
-
answer: unknown // TODO TYPE answer
|
|
72
|
-
): Promise<boolean> {
|
|
73
|
-
return super.call('acceptAnswer', answer);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
public answerWasAccepted(answerAccepted: boolean): Promise<boolean> {
|
|
77
|
-
return super.call('answerAcceptedByInitiator', answerAccepted);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
import type * as Bun from 'bun';
|
|
2
|
-
import * as nodeURL from 'node:url';
|
|
3
|
-
import { PicoLogger } from '@utils/pico-logger';
|
|
4
|
-
import { generateToken } from '../../auth/auth';
|
|
5
|
-
import {
|
|
6
|
-
type TNetworkId_S,
|
|
7
|
-
validateNetworkIdOrThrow,
|
|
8
|
-
} from '@flux/shared/types';
|
|
9
|
-
import {
|
|
10
|
-
type TFluxClientUID,
|
|
11
|
-
validateMachineUID,
|
|
12
|
-
} from '@flux/shared/utils';
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* This route is used to authorize a network authority.
|
|
16
|
-
*
|
|
17
|
-
* @returns { Promise<Response> }
|
|
18
|
-
*/
|
|
19
|
-
export const authorizeNetworkAuthority = async (
|
|
20
|
-
request: Bun.BunRequest,
|
|
21
|
-
): Promise<Response> => {
|
|
22
|
-
const urlWithParsedQuery: nodeURL.UrlWithParsedQuery = nodeURL.parse(request.url, true);
|
|
23
|
-
|
|
24
|
-
// Find the network authority to authenticate with
|
|
25
|
-
const networkIdString: string | string[] | undefined = urlWithParsedQuery.query['networkId'];
|
|
26
|
-
|
|
27
|
-
try {
|
|
28
|
-
validateNetworkIdOrThrow(networkIdString ?? '');
|
|
29
|
-
} catch (error) {
|
|
30
|
-
return new Response(
|
|
31
|
-
error instanceof Error ? error.message : 'Unknown error validating network ID',
|
|
32
|
-
{
|
|
33
|
-
status: 500,
|
|
34
|
-
},
|
|
35
|
-
);
|
|
36
|
-
}
|
|
37
|
-
const networkId: TNetworkId_S = networkIdString as TNetworkId_S;
|
|
38
|
-
const text = await request.text();
|
|
39
|
-
|
|
40
|
-
PicoLogger.log(`Received password:${text}${networkId}`, 'authorize');
|
|
41
|
-
|
|
42
|
-
console.error('TODOD: CHECK PASSWORD');
|
|
43
|
-
|
|
44
|
-
const cookies = request.cookies;
|
|
45
|
-
|
|
46
|
-
// Set a cookie with various options
|
|
47
|
-
cookies.set('user_id', '12345', {
|
|
48
|
-
maxAge: 60 * 60 * 24 * 7, // 1 week
|
|
49
|
-
httpOnly: true,
|
|
50
|
-
secure: true,
|
|
51
|
-
path: '/',
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
const machineUIDString: string | string[] | undefined = urlWithParsedQuery.query['machineUID'];
|
|
55
|
-
let machineUID: TFluxClientUID | undefined;
|
|
56
|
-
if (machineUIDString && validateMachineUID(machineUIDString)) {
|
|
57
|
-
machineUID = machineUIDString;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
return new Response(
|
|
61
|
-
generateToken({
|
|
62
|
-
networkId,
|
|
63
|
-
isAuthority: true,
|
|
64
|
-
machineUID: machineUID,
|
|
65
|
-
}),
|
|
66
|
-
{
|
|
67
|
-
headers: {
|
|
68
|
-
'Content-Type': 'text/plain',
|
|
69
|
-
'Access-Control-Allow-Origin': '*',
|
|
70
|
-
'Set-Cookie': 'sessionId=abc123; HttpOnly; Max-Age=3600',
|
|
71
|
-
},
|
|
72
|
-
}
|
|
73
|
-
);
|
|
74
|
-
};
|
|
@@ -1,169 +0,0 @@
|
|
|
1
|
-
import type { BunRequest } from 'bun';
|
|
2
|
-
import * as nodeURL from 'node:url';
|
|
3
|
-
import {
|
|
4
|
-
type TAddress,
|
|
5
|
-
type TNetworkId_S,
|
|
6
|
-
GlobalRPCTimeoutError,
|
|
7
|
-
UnknownClientError,
|
|
8
|
-
validateAgentUIDOrThrow,
|
|
9
|
-
validateNetworkIdOrThrow,
|
|
10
|
-
} from '@flux/shared/types';
|
|
11
|
-
import { generateToken } from '../../auth/auth';
|
|
12
|
-
import type { GlobalRPCClient } from '../../routing/rpc/core/global-rpc-client.class';
|
|
13
|
-
import type { NetworkAuthorityManager } from '../../register/register-network-authority.class';
|
|
14
|
-
import { retry } from '@flux/shared/utils';
|
|
15
|
-
import {
|
|
16
|
-
type TFluxClientUID,
|
|
17
|
-
validateMachineUID,
|
|
18
|
-
} from '@flux/shared/utils';
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
*
|
|
22
|
-
* @param { BunRequest } request
|
|
23
|
-
* @param { NetworkAuthorityManager } networkAuthorityManager
|
|
24
|
-
* @param { GlobalRPCClient<'authorize'> } globalRPCClient
|
|
25
|
-
*
|
|
26
|
-
* @returns { void }
|
|
27
|
-
*/
|
|
28
|
-
export const authorizeNetworkAgent = async (
|
|
29
|
-
request: BunRequest,
|
|
30
|
-
networkAuthorityManager: NetworkAuthorityManager,
|
|
31
|
-
globalRPCClient: GlobalRPCClient<'authorize'>,
|
|
32
|
-
) => {
|
|
33
|
-
const urlWithParsedQuery: nodeURL.UrlWithParsedQuery = nodeURL.parse(request.url, true);
|
|
34
|
-
|
|
35
|
-
// Find the network authority to authenticate with
|
|
36
|
-
const networkIdString: string | string[] | undefined = urlWithParsedQuery.query['networkId'];
|
|
37
|
-
|
|
38
|
-
try {
|
|
39
|
-
validateNetworkIdOrThrow(networkIdString ?? '');
|
|
40
|
-
} catch (error) {
|
|
41
|
-
return new Response(
|
|
42
|
-
error instanceof Error ? error.message : 'Unknown error validating network ID',
|
|
43
|
-
{
|
|
44
|
-
status: 500,
|
|
45
|
-
},
|
|
46
|
-
);
|
|
47
|
-
}
|
|
48
|
-
const networkId: TNetworkId_S = networkIdString as TNetworkId_S;
|
|
49
|
-
|
|
50
|
-
if (!networkId) {
|
|
51
|
-
return new Response('Missing networkId', {
|
|
52
|
-
status: 500,
|
|
53
|
-
headers: {
|
|
54
|
-
// 'Access-Control-Allow-Origin': '*',
|
|
55
|
-
},
|
|
56
|
-
});
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const text = await request.text();
|
|
60
|
-
|
|
61
|
-
const contentType = request.headers
|
|
62
|
-
.get('x-flux-content-type')
|
|
63
|
-
?.toLowerCase();
|
|
64
|
-
|
|
65
|
-
try {
|
|
66
|
-
let networkAuthorityAddress: TAddress;
|
|
67
|
-
|
|
68
|
-
const authorizedJWT: string = await retry<string>(
|
|
69
|
-
async () => {
|
|
70
|
-
networkAuthorityAddress = await networkAuthorityManager
|
|
71
|
-
.resolveNetworkAuthorityAddressOrThrow(
|
|
72
|
-
networkId,
|
|
73
|
-
);
|
|
74
|
-
|
|
75
|
-
return globalRPCClient.call(
|
|
76
|
-
networkAuthorityAddress,
|
|
77
|
-
'authorize',
|
|
78
|
-
`${contentType === 'application/json' ? 'json' : 'text'}:${text}`
|
|
79
|
-
);
|
|
80
|
-
},
|
|
81
|
-
(error: unknown) => {
|
|
82
|
-
if (
|
|
83
|
-
(error instanceof UnknownClientError) ||
|
|
84
|
-
(error instanceof GlobalRPCTimeoutError)
|
|
85
|
-
) {
|
|
86
|
-
networkAuthorityManager.removeUnresponsiveClient(
|
|
87
|
-
networkId,
|
|
88
|
-
networkAuthorityAddress,
|
|
89
|
-
);
|
|
90
|
-
|
|
91
|
-
return true;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
return false;
|
|
95
|
-
},
|
|
96
|
-
{
|
|
97
|
-
retries: 10,
|
|
98
|
-
delayMs: 50,
|
|
99
|
-
onRetry: (
|
|
100
|
-
attempt: number,
|
|
101
|
-
retries: number,
|
|
102
|
-
) => {
|
|
103
|
-
console.log(`[authorizeNetworkAgent] Retrying... (attempt: ${attempt} of ${retries})`);
|
|
104
|
-
},
|
|
105
|
-
},
|
|
106
|
-
);
|
|
107
|
-
|
|
108
|
-
// const authorizedJWT: string = await globalRPCClient.call(
|
|
109
|
-
// networkAuthorityAddress,
|
|
110
|
-
// 'authorize',
|
|
111
|
-
// `${contentType === 'application/json' ? 'json' : 'text'}:${text}`
|
|
112
|
-
// );
|
|
113
|
-
|
|
114
|
-
if (authorizedJWT) {
|
|
115
|
-
const requestedAgentUidString: string | string[] | undefined = urlWithParsedQuery.query['requestedAgentUid'];
|
|
116
|
-
|
|
117
|
-
if (requestedAgentUidString) {
|
|
118
|
-
try {
|
|
119
|
-
validateAgentUIDOrThrow(requestedAgentUidString);
|
|
120
|
-
} catch (error) {
|
|
121
|
-
return new Response(
|
|
122
|
-
error instanceof Error ? error.message : 'The requested agent ID is not valid',
|
|
123
|
-
{
|
|
124
|
-
status: 500,
|
|
125
|
-
},
|
|
126
|
-
);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
const cookies = request.cookies;
|
|
131
|
-
|
|
132
|
-
// Set a cookie with various options
|
|
133
|
-
cookies.set('claim', authorizedJWT, {
|
|
134
|
-
maxAge: 60 * 60 * 24 * 7, // 1 week
|
|
135
|
-
httpOnly: true,
|
|
136
|
-
secure: true,
|
|
137
|
-
path: '/',
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
const machineUIDString: string | string[] | undefined = urlWithParsedQuery.query['machineUID'];
|
|
141
|
-
let machineUID: TFluxClientUID | undefined;
|
|
142
|
-
if (machineUIDString && validateMachineUID(machineUIDString)) {
|
|
143
|
-
machineUID = machineUIDString;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
return new Response(
|
|
147
|
-
generateToken({
|
|
148
|
-
networkId,
|
|
149
|
-
claim: authorizedJWT,
|
|
150
|
-
agentUID: requestedAgentUidString,
|
|
151
|
-
machineUID: machineUID,
|
|
152
|
-
}),
|
|
153
|
-
{
|
|
154
|
-
headers: {
|
|
155
|
-
'Content-Type': 'text/plain',
|
|
156
|
-
'Access-Control-Allow-Origin': '*',
|
|
157
|
-
// 'Set-Cookie': `sessionId=abc123; HttpOnly; Max-Age=3600; signed=true; Path=/;`,
|
|
158
|
-
},
|
|
159
|
-
}
|
|
160
|
-
);
|
|
161
|
-
}
|
|
162
|
-
} catch (error) {
|
|
163
|
-
console.error('Error authorizing:', error);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
return new Response('Unauthorized', {
|
|
167
|
-
status: 500,
|
|
168
|
-
});
|
|
169
|
-
};
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
export const OPTIONS_RESPONSE = () =>
|
|
2
|
-
new Response(null, {
|
|
3
|
-
status: 204,
|
|
4
|
-
headers: {
|
|
5
|
-
'Access-Control-Allow-Origin': '*',
|
|
6
|
-
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
|
|
7
|
-
'Access-Control-Allow-Headers': 'Content-Type, x-flux-content-type, Set-Cookie',
|
|
8
|
-
},
|
|
9
|
-
});
|
package/src/auth/auth.ts
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import jwt from 'jsonwebtoken';
|
|
2
|
-
|
|
3
|
-
const secret = 'your-very-secure-secret'; // keep this secret safe!
|
|
4
|
-
|
|
5
|
-
export const generateToken = (
|
|
6
|
-
payload: object,
|
|
7
|
-
expiresIn = 60_000 //! TODOD CHANGE TO LOW VALUE AGAINGA.. FOR DEVE.. Live for 60 seconds
|
|
8
|
-
): string => {
|
|
9
|
-
return jwt.sign(payload, secret, { expiresIn });
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
export const verifyTokenOrThrow = (
|
|
13
|
-
token: unknown,
|
|
14
|
-
// callback?: VerifyCallback<JwtPayload | string>,
|
|
15
|
-
): object => {
|
|
16
|
-
if (typeof token !== 'string') {
|
|
17
|
-
throw new Error('Token must be a string');
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
try {
|
|
21
|
-
const decoded = jwt.verify(token, secret);
|
|
22
|
-
|
|
23
|
-
return decoded as any;
|
|
24
|
-
} catch (err) {
|
|
25
|
-
console.error('Invalid or expired token', (<any>err).message);
|
|
26
|
-
|
|
27
|
-
throw new Error('Invalid or expired token');
|
|
28
|
-
}
|
|
29
|
-
};
|
|
@@ -1,209 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
type TAddress,
|
|
3
|
-
type TChannelName,
|
|
4
|
-
type TNetworkId_S,
|
|
5
|
-
AUTHORITY_ON_CREATE_CHANNEL,
|
|
6
|
-
AUTHORITY_ON_EMPTY_CHANNEL,
|
|
7
|
-
} from '@flux/shared/types';
|
|
8
|
-
import { NetworkChannelHash } from '@flux/mesh/store/redis/network-channel';
|
|
9
|
-
import {
|
|
10
|
-
type RedisConnection,
|
|
11
|
-
getMeshRedisConnection,
|
|
12
|
-
} from '../../routing/redis/redis-connection.class';
|
|
13
|
-
import type { GlobalChannelPubsub } from '../../routing/global-channel/global-channel-pubsub.class';
|
|
14
|
-
|
|
15
|
-
// ! TODO Hardcoded for now, take from network config in the future
|
|
16
|
-
const MAX_CHANNEL_MEMBERS = 25;
|
|
17
|
-
|
|
18
|
-
interface IUsageCache {
|
|
19
|
-
networkId: TNetworkId_S;
|
|
20
|
-
usage: number;
|
|
21
|
-
};
|
|
22
|
-
export class NetworkChannelManager {
|
|
23
|
-
private readonly channelUsageCount: Map<TChannelName, IUsageCache> = new Map();
|
|
24
|
-
private readonly redisConnection: RedisConnection = getMeshRedisConnection();
|
|
25
|
-
private readonly networkChannelHash: NetworkChannelHash = new NetworkChannelHash(
|
|
26
|
-
this.redisConnection,
|
|
27
|
-
);
|
|
28
|
-
|
|
29
|
-
constructor(
|
|
30
|
-
private readonly _globalChannelPubsub: GlobalChannelPubsub,
|
|
31
|
-
) {
|
|
32
|
-
setInterval(() => {
|
|
33
|
-
for (const [channelName, usage] of this.channelUsageCount) {
|
|
34
|
-
|
|
35
|
-
if (usage.usage > 0) {
|
|
36
|
-
this.networkChannelHash
|
|
37
|
-
.incrementUsage(
|
|
38
|
-
usage.networkId,
|
|
39
|
-
channelName,
|
|
40
|
-
usage.usage,
|
|
41
|
-
);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
this.channelUsageCount.delete(channelName);
|
|
45
|
-
}
|
|
46
|
-
}, 5_000);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Checks if a channel can have more members.
|
|
51
|
-
*
|
|
52
|
-
* ! TODO Don't query the database repeatedly, implement local synced cache.
|
|
53
|
-
*
|
|
54
|
-
* @param { TNetworkId_S } networkId
|
|
55
|
-
* @param { TChannelName } channelName
|
|
56
|
-
*
|
|
57
|
-
* @returns { Promise<boolean> }
|
|
58
|
-
*/
|
|
59
|
-
public async canHaveMembers(
|
|
60
|
-
networkId: TNetworkId_S,
|
|
61
|
-
channelName: TChannelName,
|
|
62
|
-
): Promise<boolean> {
|
|
63
|
-
const count: number = await this.networkChannelHash.readNetworkMemberCount(
|
|
64
|
-
networkId,
|
|
65
|
-
channelName,
|
|
66
|
-
);
|
|
67
|
-
|
|
68
|
-
return Promise.resolve(count < MAX_CHANNEL_MEMBERS);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Joins a network channel.
|
|
73
|
-
*
|
|
74
|
-
* @param { TNetworkId_S } networkId
|
|
75
|
-
* @param { TChannelName } channelName
|
|
76
|
-
* @param { TAddress } clientAddress
|
|
77
|
-
*
|
|
78
|
-
* @returns { void }
|
|
79
|
-
*/
|
|
80
|
-
public joinNetworkChannel(
|
|
81
|
-
networkId: TNetworkId_S,
|
|
82
|
-
channelName: TChannelName,
|
|
83
|
-
clientAddress: TAddress,
|
|
84
|
-
): void {
|
|
85
|
-
this.createNetworkChannelIfNotExist(
|
|
86
|
-
networkId,
|
|
87
|
-
channelName,
|
|
88
|
-
);
|
|
89
|
-
|
|
90
|
-
this.networkChannelHash.joinNetworkChannel(
|
|
91
|
-
networkId,
|
|
92
|
-
channelName,
|
|
93
|
-
clientAddress,
|
|
94
|
-
);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Leaves a network channel.
|
|
99
|
-
*
|
|
100
|
-
* @param { TNetworkId_S } networkId
|
|
101
|
-
* @param { TChannelName } channelName
|
|
102
|
-
* @param { TAddress } clientAddress
|
|
103
|
-
*
|
|
104
|
-
* @returns { Promise<void> }
|
|
105
|
-
*/
|
|
106
|
-
public async leaveNetworkChannel(
|
|
107
|
-
networkId: TNetworkId_S,
|
|
108
|
-
channelName: TChannelName,
|
|
109
|
-
clientAddress: TAddress,
|
|
110
|
-
): Promise<void> {
|
|
111
|
-
const membersLeft: number = await this.networkChannelHash
|
|
112
|
-
.leaveNetworkChannel(
|
|
113
|
-
networkId,
|
|
114
|
-
channelName,
|
|
115
|
-
clientAddress,
|
|
116
|
-
);
|
|
117
|
-
|
|
118
|
-
if (membersLeft === 0) {
|
|
119
|
-
this._globalChannelPubsub
|
|
120
|
-
.publish(
|
|
121
|
-
`~/networks/${networkId}/channel-empty`,
|
|
122
|
-
`${AUTHORITY_ON_EMPTY_CHANNEL}:${channelName}`,
|
|
123
|
-
);
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Leaves all network channels.
|
|
129
|
-
*
|
|
130
|
-
* @param { TNetworkId_S } networkId
|
|
131
|
-
* @param { TAddress } clientAddress
|
|
132
|
-
* @param { Set<TChannelName> } channelNames
|
|
133
|
-
*
|
|
134
|
-
* @returns { void }
|
|
135
|
-
*/
|
|
136
|
-
public leaveAllNetworkChannels(
|
|
137
|
-
networkId: TNetworkId_S,
|
|
138
|
-
clientAddress: TAddress,
|
|
139
|
-
channelNames: Set<TChannelName>,
|
|
140
|
-
): void {
|
|
141
|
-
this.networkChannelHash.leaveAllNetworkChannels(
|
|
142
|
-
networkId,
|
|
143
|
-
clientAddress,
|
|
144
|
-
channelNames,
|
|
145
|
-
);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* Creates a network channel if it does not exist.
|
|
150
|
-
*
|
|
151
|
-
* @param { TNetworkId_S } networkId
|
|
152
|
-
* @param { TChannelName } channelName
|
|
153
|
-
*
|
|
154
|
-
* @returns { void }
|
|
155
|
-
*/
|
|
156
|
-
private async createNetworkChannelIfNotExist(
|
|
157
|
-
networkId: TNetworkId_S,
|
|
158
|
-
channelName: TChannelName,
|
|
159
|
-
): Promise<void> {
|
|
160
|
-
const wasCreated: boolean = await this.networkChannelHash
|
|
161
|
-
.createNetworkChannelIfNotExist(
|
|
162
|
-
networkId,
|
|
163
|
-
channelName,
|
|
164
|
-
);
|
|
165
|
-
|
|
166
|
-
if (wasCreated) {
|
|
167
|
-
this._globalChannelPubsub
|
|
168
|
-
.publish(
|
|
169
|
-
`~/networks/${networkId}/channel-created`,
|
|
170
|
-
`${AUTHORITY_ON_CREATE_CHANNEL}:${channelName}`,
|
|
171
|
-
);
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
// ****************************************************************************
|
|
176
|
-
// *** Update
|
|
177
|
-
// ****************************************************************************
|
|
178
|
-
|
|
179
|
-
/**
|
|
180
|
-
* Increases the usage count of a channel.
|
|
181
|
-
*
|
|
182
|
-
* @param { TNetworkId_S } networkId
|
|
183
|
-
* @param { TChannelName } channelName
|
|
184
|
-
* @param { number } usage
|
|
185
|
-
*
|
|
186
|
-
* @returns { void }
|
|
187
|
-
*/
|
|
188
|
-
public increaseUsageCount(
|
|
189
|
-
networkId: TNetworkId_S,
|
|
190
|
-
channelName: TChannelName,
|
|
191
|
-
usage: number,
|
|
192
|
-
): void {
|
|
193
|
-
const channelUsageCache: {
|
|
194
|
-
networkId: TNetworkId_S;
|
|
195
|
-
usage: number;
|
|
196
|
-
} | undefined = this.channelUsageCount.get(channelName);
|
|
197
|
-
|
|
198
|
-
this.channelUsageCount.set(
|
|
199
|
-
channelName,
|
|
200
|
-
channelUsageCache ? {
|
|
201
|
-
...channelUsageCache,
|
|
202
|
-
usage: channelUsageCache.usage + usage,
|
|
203
|
-
} : {
|
|
204
|
-
networkId,
|
|
205
|
-
usage: usage,
|
|
206
|
-
},
|
|
207
|
-
);
|
|
208
|
-
}
|
|
209
|
-
}
|
package/src/index.ts
DELETED