@casual-simulation/aux-records 3.4.6-alpha.14668890889 → 3.5.0-alpha.15119114602
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/AIController.js +835 -890
- package/AIController.js.map +1 -1
- package/AIHumeInterface.js +43 -54
- package/AIHumeInterface.js.map +1 -1
- package/AIOpenAIRealtimeInterface.js +60 -71
- package/AIOpenAIRealtimeInterface.js.map +1 -1
- package/AnthropicAIChatInterface.js +96 -142
- package/AnthropicAIChatInterface.js.map +1 -1
- package/AuthController.d.ts +3 -2
- package/AuthController.js +1907 -1933
- package/AuthController.js.map +1 -1
- package/AuthStore.d.ts +1 -10
- package/BlockadeLabsGenerateSkyboxInterface.js +57 -72
- package/BlockadeLabsGenerateSkyboxInterface.js.map +1 -1
- package/CachingConfigStore.js +30 -45
- package/CachingConfigStore.js.map +1 -1
- package/CachingPolicyStore.d.ts +8 -2
- package/CachingPolicyStore.js +108 -135
- package/CachingPolicyStore.js.map +1 -1
- package/ComIdConfig.d.ts +18 -18
- package/ComIdConfig.js.map +1 -1
- package/ConsoleAuthMessenger.js +7 -20
- package/ConsoleAuthMessenger.js.map +1 -1
- package/DataRecordsController.d.ts +2 -2
- package/DataRecordsController.js +369 -377
- package/DataRecordsController.js.map +1 -1
- package/DataRecordsStore.d.ts +1 -1
- package/DataRecordsStore.js +1 -1
- package/DataRecordsStore.js.map +1 -1
- package/EventRecordsController.js +226 -240
- package/EventRecordsController.js.map +1 -1
- package/FileRecordsController.d.ts +13 -2
- package/FileRecordsController.js +458 -450
- package/FileRecordsController.js.map +1 -1
- package/GoogleAIChatInterface.js +133 -179
- package/GoogleAIChatInterface.js.map +1 -1
- package/LivekitController.js +43 -54
- package/LivekitController.js.map +1 -1
- package/LoomController.js +64 -75
- package/LoomController.js.map +1 -1
- package/MemoryAuthMessenger.js +10 -23
- package/MemoryAuthMessenger.js.map +1 -1
- package/MemoryCache.js +18 -35
- package/MemoryCache.js.map +1 -1
- package/MemoryFileRecordsLookup.js +105 -125
- package/MemoryFileRecordsLookup.js.map +1 -1
- package/MemoryModerationJobProvider.js +17 -30
- package/MemoryModerationJobProvider.js.map +1 -1
- package/MemoryRateLimiter.js +12 -27
- package/MemoryRateLimiter.js.map +1 -1
- package/MemoryStore.d.ts +18 -6
- package/MemoryStore.js +1879 -1997
- package/MemoryStore.js.map +1 -1
- package/MetricsStore.d.ts +2 -2
- package/ModerationController.js +186 -200
- package/ModerationController.js.map +1 -1
- package/OpenAIChatInterface.js +105 -135
- package/OpenAIChatInterface.js.map +1 -1
- package/OpenAIImageInterface.js +57 -51
- package/OpenAIImageInterface.js.map +1 -1
- package/PolicyController.d.ts +150 -10
- package/PolicyController.js +1546 -1299
- package/PolicyController.js.map +1 -1
- package/PolicyStore.d.ts +110 -2
- package/PolicyStore.js +36 -1
- package/PolicyStore.js.map +1 -1
- package/PrivoClient.js +398 -435
- package/PrivoClient.js.map +1 -1
- package/RateLimitController.js +25 -36
- package/RateLimitController.js.map +1 -1
- package/RecordsClient.js +51 -74
- package/RecordsClient.js.map +1 -1
- package/RecordsController.d.ts +2 -42
- package/RecordsController.js +1026 -1182
- package/RecordsController.js.map +1 -1
- package/RecordsServer.d.ts +196 -27
- package/RecordsServer.js +1701 -1343
- package/RecordsServer.js.map +1 -1
- package/RecordsStore.d.ts +1 -10
- package/RecordsStore.js.map +1 -1
- package/ServerConfig.d.ts +339 -195
- package/ServerConfig.js +13 -0
- package/ServerConfig.js.map +1 -1
- package/SloydInterface.js +62 -75
- package/SloydInterface.js.map +1 -1
- package/StabilityAIImageInterface.js +150 -167
- package/StabilityAIImageInterface.js.map +1 -1
- package/SubscriptionConfigBuilder.d.ts +6 -1
- package/SubscriptionConfigBuilder.js +22 -0
- package/SubscriptionConfigBuilder.js.map +1 -1
- package/SubscriptionConfiguration.d.ts +266 -169
- package/SubscriptionConfiguration.js +101 -79
- package/SubscriptionConfiguration.js.map +1 -1
- package/SubscriptionController.d.ts +2 -1
- package/SubscriptionController.js +643 -650
- package/SubscriptionController.js.map +1 -1
- package/SystemNotificationMessenger.d.ts +21 -4
- package/SystemNotificationMessenger.js +36 -30
- package/SystemNotificationMessenger.js.map +1 -1
- package/TestUtils.d.ts +9 -1
- package/TestUtils.js +105 -129
- package/TestUtils.js.map +1 -1
- package/Utils.d.ts +2 -16
- package/Utils.js +21 -22
- package/Utils.js.map +1 -1
- package/crud/CrudHelpers.js +17 -26
- package/crud/CrudHelpers.js.map +1 -1
- package/crud/CrudRecordsController.d.ts +1 -1
- package/crud/CrudRecordsController.js +259 -267
- package/crud/CrudRecordsController.js.map +1 -1
- package/crud/CrudRecordsControllerTests.js +174 -185
- package/crud/CrudRecordsControllerTests.js.map +1 -1
- package/crud/CrudRecordsStore.d.ts +7 -3
- package/crud/MemoryCrudRecordsStore.d.ts +4 -4
- package/crud/MemoryCrudRecordsStore.js +98 -118
- package/crud/MemoryCrudRecordsStore.js.map +1 -1
- package/crud/sub/MemorySubCrudRecordsStore.d.ts +24 -0
- package/crud/sub/MemorySubCrudRecordsStore.js +146 -0
- package/crud/sub/MemorySubCrudRecordsStore.js.map +1 -0
- package/crud/sub/SubCrudRecordsController.d.ts +182 -0
- package/crud/sub/SubCrudRecordsController.js +360 -0
- package/crud/sub/SubCrudRecordsController.js.map +1 -0
- package/crud/sub/SubCrudRecordsControllerTests.d.ts +39 -0
- package/crud/sub/SubCrudRecordsControllerTests.js +821 -0
- package/crud/sub/SubCrudRecordsControllerTests.js.map +1 -0
- package/crud/sub/SubCrudRecordsStore.d.ts +95 -0
- package/{forms/index.js → crud/sub/SubCrudRecordsStore.js} +2 -2
- package/crud/sub/SubCrudRecordsStore.js.map +1 -0
- package/crud/sub/index.d.ts +3 -0
- package/crud/sub/index.js +20 -0
- package/{forms → crud/sub}/index.js.map +1 -1
- package/index.d.ts +1 -1
- package/index.js +1 -1
- package/index.js.map +1 -1
- package/notifications/MemoryNotificationRecordsStore.js +189 -198
- package/notifications/MemoryNotificationRecordsStore.js.map +1 -1
- package/notifications/NotificationRecordsController.js +438 -460
- package/notifications/NotificationRecordsController.js.map +1 -1
- package/notifications/NotificationRecordsStore.d.ts +2 -1
- package/notifications/WebPushInterface.d.ts +0 -1
- package/notifications/WebPushInterface.js +0 -1
- package/notifications/WebPushInterface.js.map +1 -1
- package/package.json +6 -6
- package/packages/MemoryPackageRecordsStore.d.ts +10 -0
- package/packages/MemoryPackageRecordsStore.js +38 -0
- package/packages/MemoryPackageRecordsStore.js.map +1 -0
- package/packages/PackageRecordsController.d.ts +26 -0
- package/packages/PackageRecordsController.js +49 -0
- package/packages/PackageRecordsController.js.map +1 -0
- package/packages/PackageRecordsStore.d.ts +32 -0
- package/packages/PackageRecordsStore.js +19 -0
- package/packages/PackageRecordsStore.js.map +1 -0
- package/packages/index.d.ts +4 -0
- package/packages/index.js +21 -0
- package/packages/index.js.map +1 -0
- package/packages/version/MemoryPackageVersionRecordsStore.d.ts +21 -0
- package/packages/version/MemoryPackageVersionRecordsStore.js +177 -0
- package/packages/version/MemoryPackageVersionRecordsStore.js.map +1 -0
- package/packages/version/PackageVersionRecordsController.d.ts +144 -0
- package/packages/version/PackageVersionRecordsController.js +656 -0
- package/packages/version/PackageVersionRecordsController.js.map +1 -0
- package/packages/version/PackageVersionRecordsStore.d.ts +342 -0
- package/packages/version/PackageVersionRecordsStore.js +126 -0
- package/packages/version/PackageVersionRecordsStore.js.map +1 -0
- package/packages/version/index.d.ts +4 -0
- package/packages/version/index.js +21 -0
- package/packages/version/index.js.map +1 -0
- package/tracing/TracingDecorators.js +31 -40
- package/tracing/TracingDecorators.js.map +1 -1
- package/webhooks/MemoryWebhookRecordsStore.js +56 -72
- package/webhooks/MemoryWebhookRecordsStore.js.map +1 -1
- package/webhooks/WebhookEnvironment.d.ts +3 -3
- package/webhooks/WebhookRecordsController.d.ts +2 -1
- package/webhooks/WebhookRecordsController.js +389 -382
- package/webhooks/WebhookRecordsController.js.map +1 -1
- package/webhooks/WebhookRecordsStore.d.ts +2 -1
- package/websockets/InstRecordsStore.d.ts +50 -0
- package/websockets/InstRecordsStore.js +17 -0
- package/websockets/InstRecordsStore.js.map +1 -1
- package/websockets/MemoryTempInstRecordsStore.d.ts +5 -0
- package/websockets/MemoryTempInstRecordsStore.js +168 -179
- package/websockets/MemoryTempInstRecordsStore.js.map +1 -1
- package/websockets/MemoryWebsocketConnectionStore.js +98 -135
- package/websockets/MemoryWebsocketConnectionStore.js.map +1 -1
- package/websockets/MemoryWebsocketMessenger.js +29 -48
- package/websockets/MemoryWebsocketMessenger.js.map +1 -1
- package/websockets/SplitInstRecordsStore.d.ts +4 -1
- package/websockets/SplitInstRecordsStore.js +167 -185
- package/websockets/SplitInstRecordsStore.js.map +1 -1
- package/websockets/TemporaryInstRecordsStore.d.ts +19 -1
- package/websockets/TemporaryInstRecordsStore.js +17 -0
- package/websockets/TemporaryInstRecordsStore.js.map +1 -1
- package/websockets/WebsocketController.d.ts +147 -3
- package/websockets/WebsocketController.js +1735 -1391
- package/websockets/WebsocketController.js.map +1 -1
- package/websockets/index.d.ts +0 -1
- package/websockets/index.js +0 -1
- package/websockets/index.js.map +1 -1
- package/AAGUID.d.ts +0 -11
- package/AAGUID.js +0 -116
- package/AAGUID.js.map +0 -1
- package/AuthUtils.d.ts +0 -162
- package/AuthUtils.js +0 -327
- package/AuthUtils.js.map +0 -1
- package/forms/FormError.d.ts +0 -43
- package/forms/FormError.js +0 -56
- package/forms/FormError.js.map +0 -1
- package/forms/index.d.ts +0 -2
- package/websockets/Utils.d.ts +0 -33
- package/websockets/Utils.js +0 -82
- package/websockets/Utils.js.map +0 -1
|
@@ -4,40 +4,23 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
4
4
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
5
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
6
|
};
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
10
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
11
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
12
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
13
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
14
|
-
});
|
|
15
|
-
};
|
|
16
|
-
var __rest = (this && this.__rest) || function (s, e) {
|
|
17
|
-
var t = {};
|
|
18
|
-
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
19
|
-
t[p] = s[p];
|
|
20
|
-
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
21
|
-
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
22
|
-
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
23
|
-
t[p[i]] = s[p[i]];
|
|
24
|
-
}
|
|
25
|
-
return t;
|
|
26
|
-
};
|
|
27
|
-
import { action, ON_WEBHOOK_ACTION_NAME, } from '@casual-simulation/aux-common/bots';
|
|
28
|
-
import { YjsPartitionImpl } from '@casual-simulation/aux-common/partitions';
|
|
7
|
+
import { action, createInitializationUpdate, ON_WEBHOOK_ACTION_NAME, } from '@casual-simulation/aux-common/bots';
|
|
8
|
+
import { constructInitializationUpdate, YjsPartitionImpl, } from '@casual-simulation/aux-common/partitions';
|
|
29
9
|
import { device, deviceError, deviceResult, } from '@casual-simulation/aux-common/common/RemoteActions';
|
|
30
10
|
import { fromByteArray, toByteArray } from 'base64-js';
|
|
31
11
|
import { applyUpdate, Doc, encodeStateAsUpdate } from 'yjs';
|
|
32
|
-
import { WebsocketEventTypes
|
|
12
|
+
import { WebsocketEventTypes } from '@casual-simulation/aux-common/websockets/WebsocketEvents';
|
|
33
13
|
import { sumBy } from 'lodash';
|
|
34
|
-
import { PRIVATE_MARKER, ACCOUNT_MARKER, DEFAULT_BRANCH_NAME, } from '@casual-simulation/aux-common';
|
|
14
|
+
import { PRIVATE_MARKER, ACCOUNT_MARKER, DEFAULT_BRANCH_NAME, tryParseJson, } from '@casual-simulation/aux-common';
|
|
35
15
|
import { SplitInstRecordsStore } from './SplitInstRecordsStore';
|
|
36
|
-
import { v4 as uuid } from 'uuid';
|
|
16
|
+
import { v4 as uuid, v7 as uuidv7 } from 'uuid';
|
|
37
17
|
import { getSubscriptionFeatures } from '../SubscriptionConfiguration';
|
|
38
18
|
import { traced } from '../tracing/TracingDecorators';
|
|
39
19
|
import { trace } from '@opentelemetry/api';
|
|
40
20
|
import { SEMATTRS_ENDUSER_ID } from '@opentelemetry/semantic-conventions';
|
|
21
|
+
import { formatVersionSpecifier, } from '../packages/version';
|
|
22
|
+
import { STORED_AUX_SCHEMA } from '../webhooks';
|
|
23
|
+
import { formatInstId } from '@casual-simulation/aux-common';
|
|
41
24
|
const TRACE_NAME = 'WebsocketController';
|
|
42
25
|
export const SAVE_PERMANENT_BRANCHES_LOCK = 'savePermanentBranches';
|
|
43
26
|
/**
|
|
@@ -47,7 +30,7 @@ export class WebsocketController {
|
|
|
47
30
|
get messenger() {
|
|
48
31
|
return this._messenger;
|
|
49
32
|
}
|
|
50
|
-
constructor(connectionStore, messenger, instStore, temporaryInstStore, auth, policies, config, metrics, authStore) {
|
|
33
|
+
constructor(connectionStore, messenger, instStore, temporaryInstStore, auth, policies, config, metrics, authStore, packageVersions = null) {
|
|
51
34
|
this.mergeUpdatesOnMaxSizeExceeded = false;
|
|
52
35
|
this._connectionStore = connectionStore;
|
|
53
36
|
this._messenger = messenger;
|
|
@@ -58,6 +41,7 @@ export class WebsocketController {
|
|
|
58
41
|
this._policies = policies;
|
|
59
42
|
this._config = config;
|
|
60
43
|
this._metrics = metrics;
|
|
44
|
+
this._packageVersions = packageVersions;
|
|
61
45
|
}
|
|
62
46
|
/**
|
|
63
47
|
* Attempts to log the given connection in.
|
|
@@ -65,987 +49,1171 @@ export class WebsocketController {
|
|
|
65
49
|
* @param requestId The ID of the request.
|
|
66
50
|
* @param message The login message.
|
|
67
51
|
*/
|
|
68
|
-
login(connectionId, requestId, message) {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
if (!message.
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
errorMessage: 'A connection ID must be specified when logging in without a connection token.',
|
|
81
|
-
});
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
84
|
-
yield this._connectionStore.saveConnection({
|
|
85
|
-
serverConnectionId: connectionId,
|
|
86
|
-
clientConnectionId: message.connectionId,
|
|
87
|
-
userId: null,
|
|
88
|
-
sessionId: null,
|
|
89
|
-
token: null,
|
|
90
|
-
});
|
|
91
|
-
clientConnectionId = message.connectionId;
|
|
92
|
-
}
|
|
93
|
-
else {
|
|
94
|
-
const validationResult = yield this._auth.validateConnectionToken(message.connectionToken);
|
|
95
|
-
if (validationResult.success === false) {
|
|
96
|
-
yield this._messenger.sendMessage([connectionId], {
|
|
97
|
-
type: 'login_result',
|
|
98
|
-
success: false,
|
|
99
|
-
errorCode: validationResult.errorCode,
|
|
100
|
-
errorMessage: validationResult.errorMessage,
|
|
101
|
-
});
|
|
102
|
-
return;
|
|
103
|
-
}
|
|
104
|
-
yield this._connectionStore.saveConnection({
|
|
105
|
-
serverConnectionId: connectionId,
|
|
106
|
-
userId: validationResult.userId,
|
|
107
|
-
sessionId: validationResult.sessionId,
|
|
108
|
-
clientConnectionId: validationResult.connectionId,
|
|
109
|
-
token: message.connectionToken,
|
|
52
|
+
async login(connectionId, requestId, message) {
|
|
53
|
+
try {
|
|
54
|
+
let userId = null;
|
|
55
|
+
let sessionId = null;
|
|
56
|
+
let clientConnectionId;
|
|
57
|
+
if (!message.connectionToken) {
|
|
58
|
+
if (!message.connectionId) {
|
|
59
|
+
await this._messenger.sendMessage([connectionId], {
|
|
60
|
+
type: 'login_result',
|
|
61
|
+
success: false,
|
|
62
|
+
errorCode: 'unacceptable_connection_id',
|
|
63
|
+
errorMessage: 'A connection ID must be specified when logging in without a connection token.',
|
|
110
64
|
});
|
|
111
|
-
|
|
112
|
-
userId = validationResult.userId;
|
|
113
|
-
sessionId = validationResult.sessionId;
|
|
114
|
-
clientConnectionId = validationResult.connectionId;
|
|
115
|
-
const span = trace.getActiveSpan();
|
|
116
|
-
if (span) {
|
|
117
|
-
span.setAttributes({
|
|
118
|
-
[SEMATTRS_ENDUSER_ID]: userId,
|
|
119
|
-
['request.userId']: userId,
|
|
120
|
-
['request.sessionId']: sessionId,
|
|
121
|
-
['request.subscriptionId']: validationResult.subscriptionId,
|
|
122
|
-
['request.subscriptionTier']: validationResult.subscriptionTier,
|
|
123
|
-
});
|
|
124
|
-
}
|
|
65
|
+
return;
|
|
125
66
|
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
connectionId: clientConnectionId,
|
|
133
|
-
},
|
|
67
|
+
await this._connectionStore.saveConnection({
|
|
68
|
+
serverConnectionId: connectionId,
|
|
69
|
+
clientConnectionId: message.connectionId,
|
|
70
|
+
userId: null,
|
|
71
|
+
sessionId: null,
|
|
72
|
+
token: null,
|
|
134
73
|
});
|
|
74
|
+
clientConnectionId = message.connectionId;
|
|
135
75
|
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
76
|
+
else {
|
|
77
|
+
const validationResult = await this._auth.validateConnectionToken(message.connectionToken);
|
|
78
|
+
if (validationResult.success === false) {
|
|
79
|
+
await this._messenger.sendMessage([connectionId], {
|
|
80
|
+
type: 'login_result',
|
|
81
|
+
success: false,
|
|
82
|
+
errorCode: validationResult.errorCode,
|
|
83
|
+
errorMessage: validationResult.errorMessage,
|
|
84
|
+
});
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
await this._connectionStore.saveConnection({
|
|
88
|
+
serverConnectionId: connectionId,
|
|
89
|
+
userId: validationResult.userId,
|
|
90
|
+
sessionId: validationResult.sessionId,
|
|
91
|
+
clientConnectionId: validationResult.connectionId,
|
|
92
|
+
token: message.connectionToken,
|
|
142
93
|
});
|
|
94
|
+
await this._connectionStore.saveAuthorizedInst(connectionId, validationResult.recordName, validationResult.inst, 'token');
|
|
95
|
+
userId = validationResult.userId;
|
|
96
|
+
sessionId = validationResult.sessionId;
|
|
97
|
+
clientConnectionId = validationResult.connectionId;
|
|
98
|
+
const span = trace.getActiveSpan();
|
|
99
|
+
if (span) {
|
|
100
|
+
span.setAttributes({
|
|
101
|
+
[SEMATTRS_ENDUSER_ID]: userId,
|
|
102
|
+
['request.userId']: userId,
|
|
103
|
+
['request.sessionId']: sessionId,
|
|
104
|
+
['request.subscriptionId']: validationResult.subscriptionId,
|
|
105
|
+
['request.subscriptionTier']: validationResult.subscriptionTier,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
143
108
|
}
|
|
144
|
-
|
|
109
|
+
await this._messenger.sendMessage([connectionId], {
|
|
110
|
+
type: 'login_result',
|
|
111
|
+
success: true,
|
|
112
|
+
info: {
|
|
113
|
+
userId,
|
|
114
|
+
sessionId,
|
|
115
|
+
connectionId: clientConnectionId,
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
catch (err) {
|
|
120
|
+
console.error('[WebsocketController] [login] Error while logging in.', err);
|
|
121
|
+
await this.sendError(connectionId, requestId, {
|
|
122
|
+
success: false,
|
|
123
|
+
errorCode: 'server_error',
|
|
124
|
+
errorMessage: 'A server error occurred while logging in.',
|
|
125
|
+
});
|
|
126
|
+
}
|
|
145
127
|
}
|
|
146
|
-
disconnect(connectionId) {
|
|
128
|
+
async disconnect(connectionId) {
|
|
147
129
|
var _a;
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
if (connection.
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
yield this._instStore.deleteBranch(connection.recordName, connection.inst, connection.branch);
|
|
161
|
-
}
|
|
130
|
+
const loadedConnections = await this._connectionStore.getConnections(connectionId);
|
|
131
|
+
await this._connectionStore.clearConnection(connectionId);
|
|
132
|
+
for (let connection of loadedConnections) {
|
|
133
|
+
if (connection.mode === 'branch') {
|
|
134
|
+
if (connection.temporary) {
|
|
135
|
+
const count = await this._connectionStore.countConnectionsByBranch(connection.mode, connection.recordName, connection.inst, connection.branch);
|
|
136
|
+
if (count <= 0) {
|
|
137
|
+
const branch = (_a = (await this._instStore.getBranchByName(connection.recordName, connection.inst, connection.branch))) !== null && _a !== void 0 ? _a : (await this._temporaryStore.getBranchByName(connection.recordName, connection.inst, connection.branch));
|
|
138
|
+
if (branch.temporary) {
|
|
139
|
+
console.log('[WebsocketController] Deleting temporary branch', connection.recordName, connection.inst, connection.branch);
|
|
140
|
+
await this._temporaryStore.deleteBranch(connection.recordName, connection.inst, connection.branch);
|
|
141
|
+
await this._instStore.deleteBranch(connection.recordName, connection.inst, connection.branch);
|
|
162
142
|
}
|
|
163
143
|
}
|
|
164
|
-
const watchingDevices = yield this._connectionStore.getConnectionsByBranch('watch_branch', connection.recordName, connection.inst, connection.branch);
|
|
165
|
-
yield this._messenger.sendMessage(watchingDevices.map((d) => d.serverConnectionId), {
|
|
166
|
-
type: 'repo/disconnected_from_branch',
|
|
167
|
-
broadcast: false,
|
|
168
|
-
recordName: connection.recordName,
|
|
169
|
-
inst: connection.inst,
|
|
170
|
-
branch: connection.branch,
|
|
171
|
-
connection: connectionInfo(connection),
|
|
172
|
-
});
|
|
173
144
|
}
|
|
145
|
+
const watchingDevices = await this._connectionStore.getConnectionsByBranch('watch_branch', connection.recordName, connection.inst, connection.branch);
|
|
146
|
+
await this._messenger.sendMessage(watchingDevices.map((d) => d.serverConnectionId), {
|
|
147
|
+
type: 'repo/disconnected_from_branch',
|
|
148
|
+
broadcast: false,
|
|
149
|
+
recordName: connection.recordName,
|
|
150
|
+
inst: connection.inst,
|
|
151
|
+
branch: connection.branch,
|
|
152
|
+
connection: connectionInfo(connection),
|
|
153
|
+
});
|
|
174
154
|
}
|
|
175
|
-
}
|
|
155
|
+
}
|
|
176
156
|
}
|
|
177
|
-
watchBranch(connectionId, event) {
|
|
157
|
+
async watchBranch(connectionId, event) {
|
|
178
158
|
var _a, _b, _c, _d;
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
159
|
+
if (!event) {
|
|
160
|
+
console.warn('[CasualRepoServer] Trying to watch branch with a null event!');
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
console.log(`[WebsocketController] [namespace: ${event.recordName}/${event.inst}/${event.branch}, ${connectionId}] Watch`);
|
|
164
|
+
const connection = await this._connectionStore.getConnection(connectionId);
|
|
165
|
+
if (!connection) {
|
|
166
|
+
console.error(`[WebsocketController] [namespace: ${event.recordName}/${event.inst}/${event.branch}, connectionId: ${connectionId}] Unable to watch branch. Connection not found!`);
|
|
167
|
+
await this.sendError(connectionId, -1, {
|
|
168
|
+
success: false,
|
|
169
|
+
errorCode: 'invalid_connection_state',
|
|
170
|
+
errorMessage: `A server error occurred. (namespace: ${event.recordName}/${event.inst}/${event.branch}, connectionId: ${connectionId})`,
|
|
171
|
+
recordName: event.recordName,
|
|
172
|
+
inst: event.inst,
|
|
173
|
+
branch: event.branch,
|
|
174
|
+
});
|
|
175
|
+
await this.messenger.disconnect(connectionId);
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
if (connection.token && event.recordName) {
|
|
179
|
+
const authorized = await this._connectionStore.isAuthorizedInst(connectionId, event.recordName, event.inst, 'token');
|
|
180
|
+
if (!authorized) {
|
|
181
|
+
await this.messenger.sendMessage([connectionId], {
|
|
182
|
+
type: 'repo/watch_branch_result',
|
|
189
183
|
success: false,
|
|
190
|
-
errorCode: '
|
|
191
|
-
errorMessage:
|
|
184
|
+
errorCode: 'not_authorized',
|
|
185
|
+
errorMessage: 'You are not authorized to access this inst.',
|
|
192
186
|
recordName: event.recordName,
|
|
193
187
|
inst: event.inst,
|
|
194
188
|
branch: event.branch,
|
|
189
|
+
reason: {
|
|
190
|
+
type: 'invalid_token',
|
|
191
|
+
},
|
|
195
192
|
});
|
|
196
|
-
yield this.messenger.disconnect(connectionId);
|
|
197
|
-
return;
|
|
198
|
-
}
|
|
199
|
-
if (connection.token && event.recordName) {
|
|
200
|
-
const authorized = yield this._connectionStore.isAuthorizedInst(connectionId, event.recordName, event.inst, 'token');
|
|
201
|
-
if (!authorized) {
|
|
202
|
-
yield this.messenger.sendMessage([connectionId], {
|
|
203
|
-
type: 'repo/watch_branch_result',
|
|
204
|
-
success: false,
|
|
205
|
-
errorCode: 'not_authorized',
|
|
206
|
-
errorMessage: 'You are not authorized to access this inst.',
|
|
207
|
-
recordName: event.recordName,
|
|
208
|
-
inst: event.inst,
|
|
209
|
-
branch: event.branch,
|
|
210
|
-
reason: {
|
|
211
|
-
type: 'invalid_token',
|
|
212
|
-
},
|
|
213
|
-
});
|
|
214
|
-
return;
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
const config = yield this._config.getSubscriptionConfiguration();
|
|
218
|
-
if (!event.recordName) {
|
|
219
|
-
if (((_b = (_a = config === null || config === void 0 ? void 0 : config.defaultFeatures) === null || _a === void 0 ? void 0 : _a.publicInsts) === null || _b === void 0 ? void 0 : _b.allowed) === false) {
|
|
220
|
-
yield this.messenger.sendMessage([connectionId], {
|
|
221
|
-
type: 'repo/watch_branch_result',
|
|
222
|
-
success: false,
|
|
223
|
-
errorCode: 'not_authorized',
|
|
224
|
-
errorMessage: 'Temporary insts are not allowed.',
|
|
225
|
-
recordName: event.recordName,
|
|
226
|
-
inst: event.inst,
|
|
227
|
-
branch: event.branch,
|
|
228
|
-
});
|
|
229
|
-
return;
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
const instResult = yield this._getOrCreateInst(event.recordName, event.inst, connection.userId, config);
|
|
233
|
-
if (instResult.success === false) {
|
|
234
|
-
yield this.messenger.sendMessage([connectionId], Object.assign(Object.assign({}, instResult), { type: 'repo/watch_branch_result', recordName: event.recordName, inst: event.inst, branch: event.branch }));
|
|
235
193
|
return;
|
|
236
194
|
}
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
if (
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
maxConnections =
|
|
247
|
-
config.defaultFeatures.publicInsts.maxActiveConnectionsPerInst;
|
|
248
|
-
}
|
|
249
|
-
if (maxConnections) {
|
|
250
|
-
const count = yield this._connectionStore.countConnectionsByBranch('branch', event.recordName, event.inst, event.branch);
|
|
251
|
-
if (count >= maxConnections) {
|
|
252
|
-
yield this.messenger.sendMessage([connectionId], {
|
|
253
|
-
type: 'repo/watch_branch_result',
|
|
254
|
-
success: false,
|
|
255
|
-
errorCode: features
|
|
256
|
-
? 'subscription_limit_reached'
|
|
257
|
-
: 'not_authorized',
|
|
258
|
-
errorMessage: 'The maximum number of active connections to this inst has been reached.',
|
|
259
|
-
recordName: event.recordName,
|
|
260
|
-
inst: event.inst,
|
|
261
|
-
branch: event.branch,
|
|
262
|
-
});
|
|
263
|
-
return;
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
yield this._connectionStore.saveBranchConnection(Object.assign(Object.assign({}, connection), { serverConnectionId: connectionId, mode: 'branch', recordName: event.recordName, inst: event.inst, branch: event.branch, temporary: event.temporary || false }));
|
|
267
|
-
const branch = yield this._getOrCreateBranch(event.recordName, event.inst, event.branch, event.temporary, inst);
|
|
268
|
-
let updates;
|
|
269
|
-
if (branch.temporary) {
|
|
270
|
-
// Temporary branches use a temporary inst data store.
|
|
271
|
-
// This is because temporary branches are never persisted to disk.
|
|
272
|
-
updates = yield this._temporaryStore.getUpdates(event.recordName, event.inst, event.branch);
|
|
273
|
-
}
|
|
274
|
-
else {
|
|
275
|
-
updates = yield this._instStore.getCurrentUpdates(event.recordName, event.inst, event.branch);
|
|
276
|
-
}
|
|
277
|
-
if (!updates) {
|
|
278
|
-
// branch info exists, but no updates for them exist yet.
|
|
279
|
-
updates = {
|
|
280
|
-
updates: [],
|
|
281
|
-
timestamps: [],
|
|
282
|
-
instSizeInBytes: 0,
|
|
283
|
-
};
|
|
284
|
-
}
|
|
285
|
-
const watchingDevices = yield this._connectionStore.getConnectionsByBranch('watch_branch', event.recordName, event.inst, event.branch);
|
|
286
|
-
console.log(`[CausalRepoServer] [namespace: ${event.recordName}/${event.inst}/${event.branch}, ${connectionId}] Connected.`);
|
|
287
|
-
const promises = [
|
|
288
|
-
this._messenger.sendMessage(watchingDevices.map((d) => d.serverConnectionId), {
|
|
289
|
-
type: 'repo/connected_to_branch',
|
|
290
|
-
broadcast: false,
|
|
291
|
-
branch: event,
|
|
292
|
-
connection: connectionInfo(connection),
|
|
293
|
-
}),
|
|
294
|
-
this._messenger.sendMessage([connection.serverConnectionId], {
|
|
295
|
-
type: 'repo/add_updates',
|
|
195
|
+
}
|
|
196
|
+
const config = await this._config.getSubscriptionConfiguration();
|
|
197
|
+
if (!event.recordName) {
|
|
198
|
+
if (((_b = (_a = config === null || config === void 0 ? void 0 : config.defaultFeatures) === null || _a === void 0 ? void 0 : _a.publicInsts) === null || _b === void 0 ? void 0 : _b.allowed) === false) {
|
|
199
|
+
await this.messenger.sendMessage([connectionId], {
|
|
200
|
+
type: 'repo/watch_branch_result',
|
|
201
|
+
success: false,
|
|
202
|
+
errorCode: 'not_authorized',
|
|
203
|
+
errorMessage: 'Temporary insts are not allowed.',
|
|
296
204
|
recordName: event.recordName,
|
|
297
205
|
inst: event.inst,
|
|
298
206
|
branch: event.branch,
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
207
|
+
});
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
const instResult = await this._getOrCreateInst(event.recordName, event.inst, connection.userId, config);
|
|
212
|
+
if (instResult.success === false) {
|
|
213
|
+
await this.messenger.sendMessage([connectionId], {
|
|
214
|
+
...instResult,
|
|
215
|
+
type: 'repo/watch_branch_result',
|
|
216
|
+
recordName: event.recordName,
|
|
217
|
+
inst: event.inst,
|
|
218
|
+
branch: event.branch,
|
|
219
|
+
});
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
const inst = instResult.inst;
|
|
223
|
+
const features = instResult.features;
|
|
224
|
+
let maxConnections = null;
|
|
225
|
+
if (features &&
|
|
226
|
+
typeof features.insts.maxActiveConnectionsPerInst === 'number') {
|
|
227
|
+
maxConnections = features.insts.maxActiveConnectionsPerInst;
|
|
228
|
+
}
|
|
229
|
+
else if (!event.recordName &&
|
|
230
|
+
typeof ((_d = (_c = config === null || config === void 0 ? void 0 : config.defaultFeatures) === null || _c === void 0 ? void 0 : _c.publicInsts) === null || _d === void 0 ? void 0 : _d.maxActiveConnectionsPerInst) === 'number') {
|
|
231
|
+
maxConnections =
|
|
232
|
+
config.defaultFeatures.publicInsts.maxActiveConnectionsPerInst;
|
|
233
|
+
}
|
|
234
|
+
if (maxConnections) {
|
|
235
|
+
const count = await this._connectionStore.countConnectionsByBranch('branch', event.recordName, event.inst, event.branch);
|
|
236
|
+
if (count >= maxConnections) {
|
|
237
|
+
await this.messenger.sendMessage([connectionId], {
|
|
303
238
|
type: 'repo/watch_branch_result',
|
|
239
|
+
success: false,
|
|
240
|
+
errorCode: features
|
|
241
|
+
? 'subscription_limit_reached'
|
|
242
|
+
: 'not_authorized',
|
|
243
|
+
errorMessage: 'The maximum number of active connections to this inst has been reached.',
|
|
304
244
|
recordName: event.recordName,
|
|
305
245
|
inst: event.inst,
|
|
306
246
|
branch: event.branch,
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
247
|
+
});
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
await this._connectionStore.saveBranchConnection({
|
|
252
|
+
...connection,
|
|
253
|
+
serverConnectionId: connectionId,
|
|
254
|
+
mode: 'branch',
|
|
255
|
+
recordName: event.recordName,
|
|
256
|
+
inst: event.inst,
|
|
257
|
+
branch: event.branch,
|
|
258
|
+
temporary: event.temporary || false,
|
|
311
259
|
});
|
|
260
|
+
const branch = await this._getOrCreateBranch(event.recordName, event.inst, event.branch, event.temporary, inst);
|
|
261
|
+
let updates;
|
|
262
|
+
if (branch.temporary) {
|
|
263
|
+
// Temporary branches use a temporary inst data store.
|
|
264
|
+
// This is because temporary branches are never persisted to disk.
|
|
265
|
+
updates = await this._temporaryStore.getUpdates(event.recordName, event.inst, event.branch);
|
|
266
|
+
}
|
|
267
|
+
else {
|
|
268
|
+
updates = await this._instStore.getCurrentUpdates(event.recordName, event.inst, event.branch);
|
|
269
|
+
}
|
|
270
|
+
if (!updates) {
|
|
271
|
+
// branch info exists, but no updates for them exist yet.
|
|
272
|
+
updates = {
|
|
273
|
+
updates: [],
|
|
274
|
+
timestamps: [],
|
|
275
|
+
instSizeInBytes: 0,
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
const watchingDevices = await this._connectionStore.getConnectionsByBranch('watch_branch', event.recordName, event.inst, event.branch);
|
|
279
|
+
console.log(`[WebsocketController] [namespace: ${event.recordName}/${event.inst}/${event.branch}, ${connectionId}] Connected.`);
|
|
280
|
+
const promises = [
|
|
281
|
+
this._messenger.sendMessage(watchingDevices.map((d) => d.serverConnectionId), {
|
|
282
|
+
type: 'repo/connected_to_branch',
|
|
283
|
+
broadcast: false,
|
|
284
|
+
branch: event,
|
|
285
|
+
connection: connectionInfo(connection),
|
|
286
|
+
}),
|
|
287
|
+
this._messenger.sendMessage([connection.serverConnectionId], {
|
|
288
|
+
type: 'repo/add_updates',
|
|
289
|
+
recordName: event.recordName,
|
|
290
|
+
inst: event.inst,
|
|
291
|
+
branch: event.branch,
|
|
292
|
+
updates: updates.updates,
|
|
293
|
+
initial: true,
|
|
294
|
+
}),
|
|
295
|
+
this._messenger.sendMessage([connection.serverConnectionId], {
|
|
296
|
+
type: 'repo/watch_branch_result',
|
|
297
|
+
recordName: event.recordName,
|
|
298
|
+
inst: event.inst,
|
|
299
|
+
branch: event.branch,
|
|
300
|
+
success: true,
|
|
301
|
+
}),
|
|
302
|
+
];
|
|
303
|
+
await Promise.all(promises);
|
|
312
304
|
}
|
|
313
|
-
unwatchBranch(connectionId, recordName, inst, branch) {
|
|
305
|
+
async unwatchBranch(connectionId, recordName, inst, branch) {
|
|
314
306
|
var _a;
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
yield this._instStore.deleteBranch(connection.recordName, connection.inst, connection.branch);
|
|
333
|
-
}
|
|
307
|
+
if (!branch) {
|
|
308
|
+
console.warn('[CasualRepoServer] Trying to unwatch branch with a null event!');
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
console.log(`[WebsocketController] [namespace: ${recordName}/${inst}/${branch}, ${connectionId}] Unwatch`);
|
|
312
|
+
const connection = await this._connectionStore.getBranchConnection(connectionId, 'branch', recordName, inst, branch);
|
|
313
|
+
if (connection) {
|
|
314
|
+
await this._connectionStore.deleteBranchConnection(connectionId, 'branch', recordName, inst, branch);
|
|
315
|
+
if (connection.temporary) {
|
|
316
|
+
const count = await this._connectionStore.countConnectionsByBranch('branch', recordName, inst, branch);
|
|
317
|
+
if (count <= 0) {
|
|
318
|
+
const branch = (_a = (await this._instStore.getBranchByName(connection.recordName, connection.inst, connection.branch))) !== null && _a !== void 0 ? _a : (await this._temporaryStore.getBranchByName(connection.recordName, connection.inst, connection.branch));
|
|
319
|
+
if (branch === null || branch === void 0 ? void 0 : branch.temporary) {
|
|
320
|
+
await this._temporaryStore.deleteBranch(connection.recordName, connection.inst, connection.branch);
|
|
321
|
+
// Delete the branch from the permentent store even if it is temporary
|
|
322
|
+
// because it is possible that the branch was created before temporary branches were only stored in the temporary store.
|
|
323
|
+
await this._instStore.deleteBranch(connection.recordName, connection.inst, connection.branch);
|
|
334
324
|
}
|
|
335
325
|
}
|
|
336
|
-
const watchingDevices = yield this._connectionStore.getConnectionsByBranch('watch_branch', recordName, inst, branch);
|
|
337
|
-
yield this._messenger.sendMessage(watchingDevices.map((d) => d.serverConnectionId), {
|
|
338
|
-
type: 'repo/disconnected_from_branch',
|
|
339
|
-
broadcast: false,
|
|
340
|
-
recordName,
|
|
341
|
-
inst,
|
|
342
|
-
branch: branch,
|
|
343
|
-
connection: connectionInfo(connection),
|
|
344
|
-
});
|
|
345
326
|
}
|
|
346
|
-
|
|
327
|
+
const watchingDevices = await this._connectionStore.getConnectionsByBranch('watch_branch', recordName, inst, branch);
|
|
328
|
+
await this._messenger.sendMessage(watchingDevices.map((d) => d.serverConnectionId), {
|
|
329
|
+
type: 'repo/disconnected_from_branch',
|
|
330
|
+
broadcast: false,
|
|
331
|
+
recordName,
|
|
332
|
+
inst,
|
|
333
|
+
branch: branch,
|
|
334
|
+
connection: connectionInfo(connection),
|
|
335
|
+
});
|
|
336
|
+
}
|
|
347
337
|
}
|
|
348
|
-
addUpdates(connectionId, event) {
|
|
338
|
+
async addUpdates(connectionId, event) {
|
|
349
339
|
var _a, _b, _c, _d, _e;
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
340
|
+
if (!event) {
|
|
341
|
+
console.warn('[CasualRepoServer] Trying to add atoms with a null event!');
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
console.log(`[WebsocketController] [namespace: ${event.recordName}/${event.inst}/${event.branch}, connectionId: ${connectionId}] Add Updates`);
|
|
345
|
+
const connection = await this._connectionStore.getConnection(connectionId);
|
|
346
|
+
if (!connection) {
|
|
347
|
+
console.error(`[WebsocketController] [namespace: ${event.recordName}/${event.inst}/${event.branch}, connectionId: ${connectionId}] Unable to add updates. Connection not found!`);
|
|
348
|
+
await this.sendError(connectionId, -1, {
|
|
349
|
+
success: false,
|
|
350
|
+
errorCode: 'invalid_connection_state',
|
|
351
|
+
errorMessage: `A server error occurred. (namespace: ${event.recordName}/${event.inst}/${event.branch}, connectionId: ${connectionId})`,
|
|
352
|
+
recordName: event.recordName,
|
|
353
|
+
inst: event.inst,
|
|
354
|
+
branch: event.branch,
|
|
355
|
+
});
|
|
356
|
+
await this.messenger.disconnect(connectionId);
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
if (connection.token && event.recordName) {
|
|
360
|
+
const authorized = await this._connectionStore.isAuthorizedInst(connectionId, event.recordName, event.inst, 'token');
|
|
361
|
+
if (!authorized) {
|
|
362
|
+
await this.sendError(connectionId, -1, {
|
|
360
363
|
success: false,
|
|
361
|
-
errorCode: '
|
|
362
|
-
errorMessage:
|
|
364
|
+
errorCode: 'not_authorized',
|
|
365
|
+
errorMessage: 'You are not authorized to access this inst.',
|
|
363
366
|
recordName: event.recordName,
|
|
364
367
|
inst: event.inst,
|
|
365
368
|
branch: event.branch,
|
|
369
|
+
reason: {
|
|
370
|
+
type: 'invalid_token',
|
|
371
|
+
},
|
|
366
372
|
});
|
|
367
|
-
yield this.messenger.disconnect(connectionId);
|
|
368
373
|
return;
|
|
369
374
|
}
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
375
|
+
}
|
|
376
|
+
if (event.updates) {
|
|
377
|
+
let branch = (_a = (await this._instStore.getBranchByName(event.recordName, event.inst, event.branch))) !== null && _a !== void 0 ? _a : (await this._temporaryStore.getBranchByName(event.recordName, event.inst, event.branch));
|
|
378
|
+
const updateSize = sumBy(event.updates, (u) => Buffer.byteLength(u, 'utf8'));
|
|
379
|
+
const config = await this._config.getSubscriptionConfiguration();
|
|
380
|
+
let features = null;
|
|
381
|
+
if (!event.recordName) {
|
|
382
|
+
if (((_c = (_b = config === null || config === void 0 ? void 0 : config.defaultFeatures) === null || _b === void 0 ? void 0 : _b.publicInsts) === null || _c === void 0 ? void 0 : _c.allowed) === false) {
|
|
383
|
+
await this.sendError(connectionId, -1, {
|
|
374
384
|
success: false,
|
|
375
385
|
errorCode: 'not_authorized',
|
|
376
|
-
errorMessage: '
|
|
386
|
+
errorMessage: 'Temporary insts are not allowed.',
|
|
377
387
|
recordName: event.recordName,
|
|
378
388
|
inst: event.inst,
|
|
379
389
|
branch: event.branch,
|
|
380
|
-
reason: {
|
|
381
|
-
type: 'invalid_token',
|
|
382
|
-
},
|
|
383
390
|
});
|
|
384
391
|
return;
|
|
385
392
|
}
|
|
386
393
|
}
|
|
387
|
-
if (
|
|
388
|
-
|
|
389
|
-
const
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
if (((_c = (_b = config === null || config === void 0 ? void 0 : config.defaultFeatures) === null || _b === void 0 ? void 0 : _b.publicInsts) === null || _c === void 0 ? void 0 : _c.allowed) === false) {
|
|
394
|
-
yield this.sendError(connectionId, -1, {
|
|
395
|
-
success: false,
|
|
396
|
-
errorCode: 'not_authorized',
|
|
397
|
-
errorMessage: 'Temporary insts are not allowed.',
|
|
398
|
-
recordName: event.recordName,
|
|
399
|
-
inst: event.inst,
|
|
400
|
-
branch: event.branch,
|
|
401
|
-
});
|
|
402
|
-
return;
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
if (!branch) {
|
|
406
|
-
console.log(`[CausalRepoServer] [namespace: ${event.recordName}/${event.inst}/${event.branch}, connectionId: ${connectionId}] Branch not found!`);
|
|
407
|
-
const instResult = yield this._getOrCreateInst(event.recordName, event.inst, connection.userId, config);
|
|
408
|
-
if (instResult.success === false) {
|
|
409
|
-
yield this.sendError(connectionId, -1, Object.assign(Object.assign({}, instResult), { recordName: event.recordName, inst: event.inst, branch: event.branch }));
|
|
410
|
-
return;
|
|
411
|
-
}
|
|
412
|
-
else if (event.recordName) {
|
|
413
|
-
const authorizeResult = yield this._policies.authorizeUserAndInstances(instResult.context, {
|
|
414
|
-
resourceKind: 'inst',
|
|
415
|
-
resourceId: event.inst,
|
|
416
|
-
action: 'updateData',
|
|
417
|
-
userId: connection.userId,
|
|
418
|
-
markers: instResult.inst.markers,
|
|
419
|
-
instances: [],
|
|
420
|
-
});
|
|
421
|
-
if (authorizeResult.success === false) {
|
|
422
|
-
yield this.sendError(connectionId, -1, Object.assign(Object.assign({}, authorizeResult), { recordName: event.recordName, inst: event.inst, branch: event.branch }));
|
|
423
|
-
return;
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
features = instResult.features;
|
|
427
|
-
const branchResult = yield this._instStore.saveBranch({
|
|
428
|
-
branch: event.branch,
|
|
429
|
-
inst: event.inst,
|
|
394
|
+
if (!branch) {
|
|
395
|
+
console.log(`[WebsocketController] [namespace: ${event.recordName}/${event.inst}/${event.branch}, connectionId: ${connectionId}] Branch not found!`);
|
|
396
|
+
const instResult = await this._getOrCreateInst(event.recordName, event.inst, connection.userId, config);
|
|
397
|
+
if (instResult.success === false) {
|
|
398
|
+
await this.sendError(connectionId, -1, {
|
|
399
|
+
...instResult,
|
|
430
400
|
recordName: event.recordName,
|
|
431
|
-
|
|
401
|
+
inst: event.inst,
|
|
402
|
+
branch: event.branch,
|
|
432
403
|
});
|
|
433
|
-
|
|
434
|
-
yield this.sendError(connectionId, -1, Object.assign(Object.assign({}, branchResult), { recordName: event.recordName, inst: event.inst, branch: event.branch }));
|
|
435
|
-
return;
|
|
436
|
-
}
|
|
437
|
-
branch = yield this._instStore.getBranchByName(event.recordName, event.inst, event.branch);
|
|
404
|
+
return;
|
|
438
405
|
}
|
|
439
406
|
else if (event.recordName) {
|
|
440
|
-
const
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
});
|
|
452
|
-
return;
|
|
453
|
-
}
|
|
454
|
-
const contextResult = yield this._policies.constructAuthorizationContext({
|
|
455
|
-
recordKeyOrRecordName: event.recordName,
|
|
456
|
-
userId: connection.userId,
|
|
457
|
-
});
|
|
458
|
-
if (contextResult.success === false) {
|
|
459
|
-
yield this.sendError(connectionId, -1, Object.assign(Object.assign({}, contextResult), { recordName: event.recordName, inst: event.inst, branch: event.branch }));
|
|
460
|
-
return;
|
|
461
|
-
}
|
|
462
|
-
const authorizeResult = yield this._policies.authorizeUserAndInstancesForResources(contextResult.context, {
|
|
463
|
-
userId: connection.userId,
|
|
464
|
-
instances: [],
|
|
465
|
-
resources: [
|
|
466
|
-
{
|
|
467
|
-
resourceKind: 'inst',
|
|
468
|
-
resourceId: event.inst,
|
|
469
|
-
action: 'read',
|
|
470
|
-
markers: branch.linkedInst.markers,
|
|
471
|
-
},
|
|
472
|
-
{
|
|
473
|
-
resourceKind: 'inst',
|
|
474
|
-
resourceId: event.inst,
|
|
475
|
-
action: 'updateData',
|
|
476
|
-
markers: branch.linkedInst.markers,
|
|
477
|
-
},
|
|
478
|
-
],
|
|
479
|
-
});
|
|
480
|
-
// const authorizeReadResult =
|
|
481
|
-
// await this._policies.authorizeRequestUsingContext(
|
|
482
|
-
// contextResult.context,
|
|
483
|
-
// {
|
|
484
|
-
// action: 'inst.read',
|
|
485
|
-
// inst: event.inst,
|
|
486
|
-
// recordKeyOrRecordName: event.recordName,
|
|
487
|
-
// resourceMarkers: branch.linkedInst.markers,
|
|
488
|
-
// userId: connection.userId,
|
|
489
|
-
// }
|
|
490
|
-
// );
|
|
491
|
-
if (authorizeResult.success === false) {
|
|
492
|
-
yield this.sendError(connectionId, -1, Object.assign(Object.assign({}, authorizeResult), { recordName: event.recordName, inst: event.inst, branch: event.branch }));
|
|
493
|
-
return;
|
|
494
|
-
}
|
|
495
|
-
// const authorizeUpdateResult =
|
|
496
|
-
// await this._policies.authorizeUserAndInstances(
|
|
497
|
-
// contextResult.context,
|
|
498
|
-
// {
|
|
499
|
-
// resourceKind: 'inst',
|
|
500
|
-
// resourceId: event.inst,
|
|
501
|
-
// action: 'updateData',
|
|
502
|
-
// markers: branch.linkedInst.markers,
|
|
503
|
-
// userId: connection.userId,
|
|
504
|
-
// instances: [],
|
|
505
|
-
// }
|
|
506
|
-
// );
|
|
507
|
-
// if (authorizeUpdateResult.success === false) {
|
|
508
|
-
// await this.sendError(connectionId, -1, {
|
|
509
|
-
// ...authorizeUpdateResult,
|
|
510
|
-
// recordName: event.recordName,
|
|
511
|
-
// inst: event.inst,
|
|
512
|
-
// branch: event.branch,
|
|
513
|
-
// });
|
|
514
|
-
// return;
|
|
515
|
-
// }
|
|
516
|
-
yield this._connectionStore.saveAuthorizedInst(connectionId, event.recordName, event.inst, 'updateData');
|
|
517
|
-
}
|
|
518
|
-
}
|
|
519
|
-
if (!features && branch.linkedInst) {
|
|
520
|
-
features = getSubscriptionFeatures(config, branch.linkedInst.subscriptionStatus, branch.linkedInst.subscriptionId, branch.linkedInst.subscriptionType);
|
|
521
|
-
}
|
|
522
|
-
let maxInstSize = null;
|
|
523
|
-
if (features &&
|
|
524
|
-
typeof features.insts.maxBytesPerInst === 'number') {
|
|
525
|
-
maxInstSize = features.insts.maxBytesPerInst;
|
|
526
|
-
}
|
|
527
|
-
else if (!event.recordName &&
|
|
528
|
-
typeof ((_e = (_d = config === null || config === void 0 ? void 0 : config.defaultFeatures) === null || _d === void 0 ? void 0 : _d.publicInsts) === null || _e === void 0 ? void 0 : _e.maxBytesPerInst) ===
|
|
529
|
-
'number') {
|
|
530
|
-
maxInstSize =
|
|
531
|
-
config.defaultFeatures.publicInsts.maxBytesPerInst;
|
|
532
|
-
}
|
|
533
|
-
if (maxInstSize) {
|
|
534
|
-
const currentSize = branch.temporary
|
|
535
|
-
? yield this._temporaryStore.getInstSize(event.recordName, event.inst)
|
|
536
|
-
: yield this._instStore.getInstSize(event.recordName, event.inst);
|
|
537
|
-
const neededSizeInBytes = currentSize + updateSize;
|
|
538
|
-
if (neededSizeInBytes > maxInstSize) {
|
|
539
|
-
yield this._messenger.sendMessage([connectionId], {
|
|
540
|
-
type: 'repo/updates_received',
|
|
407
|
+
const authorizeResult = await this._policies.authorizeUserAndInstances(instResult.context, {
|
|
408
|
+
resourceKind: 'inst',
|
|
409
|
+
resourceId: event.inst,
|
|
410
|
+
action: 'updateData',
|
|
411
|
+
userId: connection.userId,
|
|
412
|
+
markers: instResult.inst.markers,
|
|
413
|
+
instances: [],
|
|
414
|
+
});
|
|
415
|
+
if (authorizeResult.success === false) {
|
|
416
|
+
await this.sendError(connectionId, -1, {
|
|
417
|
+
...authorizeResult,
|
|
541
418
|
recordName: event.recordName,
|
|
542
419
|
inst: event.inst,
|
|
543
420
|
branch: event.branch,
|
|
544
|
-
updateId: event.updateId,
|
|
545
|
-
errorCode: 'max_size_reached',
|
|
546
|
-
maxBranchSizeInBytes: maxInstSize,
|
|
547
|
-
neededBranchSizeInBytes: neededSizeInBytes,
|
|
548
421
|
});
|
|
549
422
|
return;
|
|
550
423
|
}
|
|
551
424
|
}
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
// This is because temporary branches are never persisted to disk.
|
|
555
|
-
yield this._temporaryStore.addUpdates(event.recordName, event.inst, event.branch, event.updates, updateSize);
|
|
556
|
-
}
|
|
557
|
-
else {
|
|
558
|
-
const result = yield this._instStore.addUpdates(event.recordName, event.inst, event.branch, event.updates, updateSize);
|
|
559
|
-
if (result.success === false) {
|
|
560
|
-
console.log(`[CausalRepoServer] [namespace: ${event.recordName}/${event.inst}/${event.branch}, connectionId: ${connectionId}] Failed to add updates`, result);
|
|
561
|
-
if (result.errorCode === 'max_size_reached') {
|
|
562
|
-
if (result.success === false) {
|
|
563
|
-
if ('updateId' in event) {
|
|
564
|
-
let { success, branch } = result, rest = __rest(result, ["success", "branch"]);
|
|
565
|
-
yield this._messenger.sendMessage([connectionId], Object.assign({ type: 'repo/updates_received', recordName: event.recordName, inst: event.inst, branch: event.branch, updateId: event.updateId }, rest));
|
|
566
|
-
}
|
|
567
|
-
return;
|
|
568
|
-
}
|
|
569
|
-
}
|
|
570
|
-
}
|
|
571
|
-
else {
|
|
572
|
-
if (event.recordName &&
|
|
573
|
-
this._instStore instanceof SplitInstRecordsStore) {
|
|
574
|
-
this._instStore.temp.markBranchAsDirty({
|
|
575
|
-
recordName: event.recordName,
|
|
576
|
-
inst: event.inst,
|
|
577
|
-
branch: event.branch,
|
|
578
|
-
});
|
|
579
|
-
}
|
|
580
|
-
}
|
|
581
|
-
}
|
|
582
|
-
}
|
|
583
|
-
const hasUpdates = event.updates && event.updates.length > 0;
|
|
584
|
-
if (hasUpdates) {
|
|
585
|
-
const connectedDevices = yield this._connectionStore.getConnectionsByBranch('branch', event.recordName, event.inst, event.branch);
|
|
586
|
-
let ret = {
|
|
587
|
-
type: 'repo/add_updates',
|
|
588
|
-
recordName: event.recordName,
|
|
589
|
-
inst: event.inst,
|
|
425
|
+
features = instResult.features;
|
|
426
|
+
const branchResult = await this._instStore.saveBranch({
|
|
590
427
|
branch: event.branch,
|
|
591
|
-
updates: event.updates,
|
|
592
|
-
};
|
|
593
|
-
yield this._messenger.sendMessage(connectedDevices.map((c) => c.serverConnectionId), ret, connectionId);
|
|
594
|
-
}
|
|
595
|
-
if ('updateId' in event) {
|
|
596
|
-
yield this._messenger.sendMessage([connectionId], {
|
|
597
|
-
type: 'repo/updates_received',
|
|
598
|
-
recordName: event.recordName,
|
|
599
428
|
inst: event.inst,
|
|
600
|
-
|
|
601
|
-
|
|
429
|
+
recordName: event.recordName,
|
|
430
|
+
temporary: false,
|
|
602
431
|
});
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
sendAction(connectionId, event) {
|
|
607
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
608
|
-
if (!event) {
|
|
609
|
-
console.warn('[CasualRepoServer] Trying to send event with a null event!');
|
|
610
|
-
return;
|
|
611
|
-
}
|
|
612
|
-
// const namespace = branchNamespace(event.recordName, event.inst, event.branch);
|
|
613
|
-
const connectedDevices = yield this._connectionStore.getConnectionsByBranch('branch', event.recordName, event.inst, event.branch);
|
|
614
|
-
if (event.action.type === 'remote') {
|
|
615
|
-
const action = event.action.event;
|
|
616
|
-
}
|
|
617
|
-
let finalAction;
|
|
618
|
-
if (event.action.sessionId ||
|
|
619
|
-
event.action.connectionId ||
|
|
620
|
-
event.action.userId ||
|
|
621
|
-
(typeof event.action.broadcast !== 'undefined' &&
|
|
622
|
-
event.action.broadcast !== null)) {
|
|
623
|
-
finalAction = event.action;
|
|
624
|
-
}
|
|
625
|
-
else {
|
|
626
|
-
// TODO: Replace with system that selects target devices with better uniformity
|
|
627
|
-
// than Math.random().
|
|
628
|
-
const randomDeviceIndex = Math.min(connectedDevices.length - 1, Math.max(Math.floor(Math.random() * connectedDevices.length), 0));
|
|
629
|
-
const randomDevice = connectedDevices[randomDeviceIndex];
|
|
630
|
-
if (randomDevice) {
|
|
631
|
-
finalAction = Object.assign(Object.assign({}, event.action), { connectionId: randomDevice.clientConnectionId });
|
|
632
|
-
}
|
|
633
|
-
}
|
|
634
|
-
if (!finalAction) {
|
|
635
|
-
return;
|
|
636
|
-
}
|
|
637
|
-
const currentConnection = yield this._connectionStore.getConnection(connectionId);
|
|
638
|
-
if (currentConnection.token && event.recordName) {
|
|
639
|
-
const authorized = yield this._connectionStore.isAuthorizedInst(connectionId, event.recordName, event.inst, 'token');
|
|
640
|
-
if (!authorized) {
|
|
641
|
-
yield this.sendError(connectionId, -1, {
|
|
642
|
-
success: false,
|
|
643
|
-
errorCode: 'not_authorized',
|
|
644
|
-
errorMessage: 'You are not authorized to access this inst.',
|
|
432
|
+
if (branchResult.success === false) {
|
|
433
|
+
await this.sendError(connectionId, -1, {
|
|
434
|
+
...branchResult,
|
|
645
435
|
recordName: event.recordName,
|
|
646
436
|
inst: event.inst,
|
|
647
437
|
branch: event.branch,
|
|
648
|
-
reason: {
|
|
649
|
-
type: 'invalid_token',
|
|
650
|
-
},
|
|
651
438
|
});
|
|
652
439
|
return;
|
|
653
440
|
}
|
|
441
|
+
branch = await this._instStore.getBranchByName(event.recordName, event.inst, event.branch);
|
|
654
442
|
}
|
|
655
|
-
if (event.recordName) {
|
|
656
|
-
const
|
|
657
|
-
const instResult = yield this._getInst(event.recordName, event.inst, currentConnection.userId, config);
|
|
658
|
-
if (instResult.success === false) {
|
|
659
|
-
yield this.sendError(connectionId, -1, instResult);
|
|
660
|
-
return;
|
|
661
|
-
}
|
|
662
|
-
else if (!instResult.inst) {
|
|
663
|
-
yield this.sendError(connectionId, -1, {
|
|
664
|
-
success: false,
|
|
665
|
-
errorCode: 'inst_not_found',
|
|
666
|
-
errorMessage: 'The inst was not found.',
|
|
667
|
-
});
|
|
668
|
-
return;
|
|
669
|
-
}
|
|
670
|
-
const authorizeResult = yield this._policies.authorizeUserAndInstances(instResult.context, {
|
|
671
|
-
resourceKind: 'inst',
|
|
672
|
-
resourceId: event.inst,
|
|
673
|
-
action: 'sendAction',
|
|
674
|
-
markers: instResult.inst.markers,
|
|
675
|
-
userId: currentConnection.userId,
|
|
676
|
-
instances: [],
|
|
677
|
-
});
|
|
678
|
-
if (authorizeResult.success === false) {
|
|
679
|
-
yield this.sendError(connectionId, -1, authorizeResult);
|
|
680
|
-
return;
|
|
681
|
-
}
|
|
682
|
-
}
|
|
683
|
-
const targetedDevices = connectedDevices.filter((d) => isEventForDevice(finalAction, d));
|
|
684
|
-
const dEvent = finalAction.type === 'remote'
|
|
685
|
-
? device(connectionInfo(currentConnection), finalAction.event, finalAction.taskId)
|
|
686
|
-
: finalAction.type === 'remote_result'
|
|
687
|
-
? deviceResult(connectionInfo(currentConnection), finalAction.result, finalAction.taskId)
|
|
688
|
-
: deviceError(connectionInfo(currentConnection), finalAction.error, finalAction.taskId);
|
|
689
|
-
yield this._messenger.sendMessage(targetedDevices.map((c) => c.serverConnectionId), {
|
|
690
|
-
type: 'repo/receive_action',
|
|
691
|
-
recordName: event.recordName,
|
|
692
|
-
inst: event.inst,
|
|
693
|
-
branch: event.branch,
|
|
694
|
-
action: dEvent,
|
|
695
|
-
});
|
|
696
|
-
});
|
|
697
|
-
}
|
|
698
|
-
watchBranchDevices(connectionId, recordName, inst, branch) {
|
|
699
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
700
|
-
console.log(`[CausalRepoServer] [namespace: ${recordName}/${inst}/${branch}, connectionId: ${connectionId}] Watch devices for branch`);
|
|
701
|
-
const connection = yield this._connectionStore.getConnection(connectionId);
|
|
702
|
-
if (!connection) {
|
|
703
|
-
console.error(`[CausalRepoServer] [namespace: ${recordName}/${inst}/${branch}, connectionId: ${connectionId}] Unable to watch_branch_devices. Connection not found!`);
|
|
704
|
-
yield this.sendError(connectionId, -1, {
|
|
705
|
-
success: false,
|
|
706
|
-
errorCode: 'invalid_connection_state',
|
|
707
|
-
errorMessage: `A server error occurred. (namespace: ${recordName}/${inst}/${branch}, connectionId: ${connectionId})`,
|
|
708
|
-
recordName: recordName,
|
|
709
|
-
inst: inst,
|
|
710
|
-
branch: branch,
|
|
711
|
-
});
|
|
712
|
-
yield this.messenger.disconnect(connectionId);
|
|
713
|
-
return;
|
|
714
|
-
}
|
|
715
|
-
if (connection.token && recordName) {
|
|
716
|
-
const authorized = yield this._connectionStore.isAuthorizedInst(connectionId, recordName, inst, 'token');
|
|
443
|
+
else if (event.recordName) {
|
|
444
|
+
const authorized = await this._connectionStore.isAuthorizedInst(connectionId, event.recordName, event.inst, 'updateData');
|
|
717
445
|
if (!authorized) {
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
}
|
|
446
|
+
if (!branch.linkedInst) {
|
|
447
|
+
console.error('[WebsocketController] The inst was not found even though the branch was found and exists in a record!');
|
|
448
|
+
await this.sendError(connectionId, -1, {
|
|
449
|
+
success: false,
|
|
450
|
+
errorCode: 'inst_not_found',
|
|
451
|
+
errorMessage: 'The inst was not found.',
|
|
452
|
+
recordName: event.recordName,
|
|
453
|
+
inst: event.inst,
|
|
454
|
+
branch: event.branch,
|
|
455
|
+
});
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
const contextResult = await this._policies.constructAuthorizationContext({
|
|
459
|
+
recordKeyOrRecordName: event.recordName,
|
|
460
|
+
userId: connection.userId,
|
|
728
461
|
});
|
|
729
|
-
|
|
462
|
+
if (contextResult.success === false) {
|
|
463
|
+
await this.sendError(connectionId, -1, {
|
|
464
|
+
...contextResult,
|
|
465
|
+
recordName: event.recordName,
|
|
466
|
+
inst: event.inst,
|
|
467
|
+
branch: event.branch,
|
|
468
|
+
});
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
471
|
+
const authorizeResult = await this._policies.authorizeUserAndInstancesForResources(contextResult.context, {
|
|
472
|
+
userId: connection.userId,
|
|
473
|
+
instances: [],
|
|
474
|
+
resources: [
|
|
475
|
+
{
|
|
476
|
+
resourceKind: 'inst',
|
|
477
|
+
resourceId: event.inst,
|
|
478
|
+
action: 'read',
|
|
479
|
+
markers: branch.linkedInst.markers,
|
|
480
|
+
},
|
|
481
|
+
{
|
|
482
|
+
resourceKind: 'inst',
|
|
483
|
+
resourceId: event.inst,
|
|
484
|
+
action: 'updateData',
|
|
485
|
+
markers: branch.linkedInst.markers,
|
|
486
|
+
},
|
|
487
|
+
],
|
|
488
|
+
});
|
|
489
|
+
// const authorizeReadResult =
|
|
490
|
+
// await this._policies.authorizeRequestUsingContext(
|
|
491
|
+
// contextResult.context,
|
|
492
|
+
// {
|
|
493
|
+
// action: 'inst.read',
|
|
494
|
+
// inst: event.inst,
|
|
495
|
+
// recordKeyOrRecordName: event.recordName,
|
|
496
|
+
// resourceMarkers: branch.linkedInst.markers,
|
|
497
|
+
// userId: connection.userId,
|
|
498
|
+
// }
|
|
499
|
+
// );
|
|
500
|
+
if (authorizeResult.success === false) {
|
|
501
|
+
await this.sendError(connectionId, -1, {
|
|
502
|
+
...authorizeResult,
|
|
503
|
+
recordName: event.recordName,
|
|
504
|
+
inst: event.inst,
|
|
505
|
+
branch: event.branch,
|
|
506
|
+
});
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
509
|
+
// const authorizeUpdateResult =
|
|
510
|
+
// await this._policies.authorizeUserAndInstances(
|
|
511
|
+
// contextResult.context,
|
|
512
|
+
// {
|
|
513
|
+
// resourceKind: 'inst',
|
|
514
|
+
// resourceId: event.inst,
|
|
515
|
+
// action: 'updateData',
|
|
516
|
+
// markers: branch.linkedInst.markers,
|
|
517
|
+
// userId: connection.userId,
|
|
518
|
+
// instances: [],
|
|
519
|
+
// }
|
|
520
|
+
// );
|
|
521
|
+
// if (authorizeUpdateResult.success === false) {
|
|
522
|
+
// await this.sendError(connectionId, -1, {
|
|
523
|
+
// ...authorizeUpdateResult,
|
|
524
|
+
// recordName: event.recordName,
|
|
525
|
+
// inst: event.inst,
|
|
526
|
+
// branch: event.branch,
|
|
527
|
+
// });
|
|
528
|
+
// return;
|
|
529
|
+
// }
|
|
530
|
+
await this._connectionStore.saveAuthorizedInst(connectionId, event.recordName, event.inst, 'updateData');
|
|
730
531
|
}
|
|
731
532
|
}
|
|
732
|
-
if (
|
|
733
|
-
|
|
734
|
-
const instResult = yield this._getInst(recordName, inst, connection.userId, config);
|
|
735
|
-
if (instResult.success === false) {
|
|
736
|
-
yield this.sendError(connectionId, -1, instResult);
|
|
737
|
-
return;
|
|
738
|
-
}
|
|
533
|
+
if (!features && branch.linkedInst) {
|
|
534
|
+
features = getSubscriptionFeatures(config, branch.linkedInst.subscriptionStatus, branch.linkedInst.subscriptionId, branch.linkedInst.subscriptionType);
|
|
739
535
|
}
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
const count = typeof branch !== 'undefined' && branch !== null
|
|
767
|
-
? yield this._connectionStore.countConnectionsByBranch('branch', recordName, inst, branch)
|
|
768
|
-
: yield this._connectionStore.countConnections();
|
|
769
|
-
const currentConnection = yield this._connectionStore.getConnection(connectionId);
|
|
770
|
-
if (recordName && (currentConnection === null || currentConnection === void 0 ? void 0 : currentConnection.token)) {
|
|
771
|
-
const authorized = yield this._connectionStore.isAuthorizedInst(connectionId, recordName, inst, 'token');
|
|
772
|
-
if (!authorized) {
|
|
773
|
-
yield this.sendError(connectionId, -1, {
|
|
774
|
-
success: false,
|
|
775
|
-
errorCode: 'not_authorized',
|
|
776
|
-
errorMessage: 'You are not authorized to access this inst.',
|
|
777
|
-
recordName,
|
|
778
|
-
inst,
|
|
779
|
-
branch,
|
|
780
|
-
reason: {
|
|
781
|
-
type: 'invalid_token',
|
|
782
|
-
},
|
|
536
|
+
let maxInstSize = null;
|
|
537
|
+
if (features &&
|
|
538
|
+
typeof features.insts.maxBytesPerInst === 'number') {
|
|
539
|
+
maxInstSize = features.insts.maxBytesPerInst;
|
|
540
|
+
}
|
|
541
|
+
else if (!event.recordName &&
|
|
542
|
+
typeof ((_e = (_d = config === null || config === void 0 ? void 0 : config.defaultFeatures) === null || _d === void 0 ? void 0 : _d.publicInsts) === null || _e === void 0 ? void 0 : _e.maxBytesPerInst) ===
|
|
543
|
+
'number') {
|
|
544
|
+
maxInstSize =
|
|
545
|
+
config.defaultFeatures.publicInsts.maxBytesPerInst;
|
|
546
|
+
}
|
|
547
|
+
if (maxInstSize) {
|
|
548
|
+
const currentSize = branch.temporary
|
|
549
|
+
? await this._temporaryStore.getInstSize(event.recordName, event.inst)
|
|
550
|
+
: await this._instStore.getInstSize(event.recordName, event.inst);
|
|
551
|
+
const neededSizeInBytes = currentSize + updateSize;
|
|
552
|
+
if (neededSizeInBytes > maxInstSize) {
|
|
553
|
+
await this._messenger.sendMessage([connectionId], {
|
|
554
|
+
type: 'repo/updates_received',
|
|
555
|
+
recordName: event.recordName,
|
|
556
|
+
inst: event.inst,
|
|
557
|
+
branch: event.branch,
|
|
558
|
+
updateId: event.updateId,
|
|
559
|
+
errorCode: 'max_size_reached',
|
|
560
|
+
maxBranchSizeInBytes: maxInstSize,
|
|
561
|
+
neededBranchSizeInBytes: neededSizeInBytes,
|
|
783
562
|
});
|
|
784
563
|
return;
|
|
785
564
|
}
|
|
786
565
|
}
|
|
787
|
-
if (
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
566
|
+
if (branch.temporary) {
|
|
567
|
+
// Temporary branches use a temporary inst data store.
|
|
568
|
+
// This is because temporary branches are never persisted to disk.
|
|
569
|
+
await this._temporaryStore.addUpdates(event.recordName, event.inst, event.branch, event.updates, updateSize);
|
|
570
|
+
}
|
|
571
|
+
else {
|
|
572
|
+
const result = await this._instStore.addUpdates(event.recordName, event.inst, event.branch, event.updates, updateSize);
|
|
573
|
+
if (result.success === false) {
|
|
574
|
+
console.log(`[WebsocketController] [namespace: ${event.recordName}/${event.inst}/${event.branch}, connectionId: ${connectionId}] Failed to add updates`, result);
|
|
575
|
+
if (result.errorCode === 'max_size_reached') {
|
|
576
|
+
if (result.success === false) {
|
|
577
|
+
if ('updateId' in event) {
|
|
578
|
+
let { success, branch, ...rest } = result;
|
|
579
|
+
await this._messenger.sendMessage([connectionId], {
|
|
580
|
+
type: 'repo/updates_received',
|
|
581
|
+
recordName: event.recordName,
|
|
582
|
+
inst: event.inst,
|
|
583
|
+
branch: event.branch,
|
|
584
|
+
updateId: event.updateId,
|
|
585
|
+
...rest,
|
|
586
|
+
});
|
|
587
|
+
}
|
|
588
|
+
return;
|
|
589
|
+
}
|
|
590
|
+
}
|
|
793
591
|
}
|
|
794
|
-
else
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
592
|
+
else {
|
|
593
|
+
if (event.recordName &&
|
|
594
|
+
this._instStore instanceof SplitInstRecordsStore) {
|
|
595
|
+
this._instStore.temp.markBranchAsDirty({
|
|
596
|
+
recordName: event.recordName,
|
|
597
|
+
inst: event.inst,
|
|
598
|
+
branch: event.branch,
|
|
599
|
+
});
|
|
600
|
+
}
|
|
801
601
|
}
|
|
802
602
|
}
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
603
|
+
}
|
|
604
|
+
const hasUpdates = event.updates && event.updates.length > 0;
|
|
605
|
+
if (hasUpdates) {
|
|
606
|
+
const connectedDevices = await this._connectionStore.getConnectionsByBranch('branch', event.recordName, event.inst, event.branch);
|
|
607
|
+
let ret = {
|
|
608
|
+
type: 'repo/add_updates',
|
|
609
|
+
recordName: event.recordName,
|
|
610
|
+
inst: event.inst,
|
|
611
|
+
branch: event.branch,
|
|
612
|
+
updates: event.updates,
|
|
613
|
+
};
|
|
614
|
+
await this._messenger.sendMessage(connectedDevices.map((c) => c.serverConnectionId), ret, connectionId);
|
|
615
|
+
}
|
|
616
|
+
if ('updateId' in event) {
|
|
617
|
+
await this._messenger.sendMessage([connectionId], {
|
|
618
|
+
type: 'repo/updates_received',
|
|
619
|
+
recordName: event.recordName,
|
|
620
|
+
inst: event.inst,
|
|
621
|
+
branch: event.branch,
|
|
622
|
+
updateId: event.updateId,
|
|
809
623
|
});
|
|
810
|
-
}
|
|
624
|
+
}
|
|
811
625
|
}
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
823
|
-
console.log(`[CausalRepoServer] [namespace: ${recordName}/${inst}/${branch}] Get Data`);
|
|
824
|
-
if (recordName) {
|
|
825
|
-
const config = yield this._config.getSubscriptionConfiguration();
|
|
826
|
-
const instResult = yield this._getInst(recordName, inst, userId, config);
|
|
827
|
-
if (instResult.success === false) {
|
|
828
|
-
return instResult;
|
|
829
|
-
}
|
|
830
|
-
else if (!instResult.inst) {
|
|
626
|
+
async addUserUpdates(request) {
|
|
627
|
+
var _a, _b, _c, _d, _e;
|
|
628
|
+
console.log(`[CausalRepoServer] [namespace: ${request.recordName}/${request.inst}/${request.branch}, userId: ${request.userId}] Add Updates`);
|
|
629
|
+
if (request.updates) {
|
|
630
|
+
let branch = (_a = (await this._instStore.getBranchByName(request.recordName, request.inst, request.branch))) !== null && _a !== void 0 ? _a : (await this._temporaryStore.getBranchByName(request.recordName, request.inst, request.branch));
|
|
631
|
+
const updateSize = sumBy(request.updates, (u) => Buffer.byteLength(u, 'utf8'));
|
|
632
|
+
const config = await this._config.getSubscriptionConfiguration();
|
|
633
|
+
let features = null;
|
|
634
|
+
if (!request.recordName) {
|
|
635
|
+
if (((_c = (_b = config === null || config === void 0 ? void 0 : config.defaultFeatures) === null || _b === void 0 ? void 0 : _b.publicInsts) === null || _c === void 0 ? void 0 : _c.allowed) === false) {
|
|
831
636
|
return {
|
|
832
637
|
success: false,
|
|
833
|
-
errorCode: '
|
|
834
|
-
errorMessage: '
|
|
638
|
+
errorCode: 'not_authorized',
|
|
639
|
+
errorMessage: 'Temporary insts are not allowed.',
|
|
835
640
|
};
|
|
836
641
|
}
|
|
837
642
|
}
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
if (auxVersion === 1) {
|
|
844
|
-
const partition = new YjsPartitionImpl({ type: 'yjs' });
|
|
845
|
-
for (let updateBase64 of updates.updates) {
|
|
846
|
-
const update = toByteArray(updateBase64);
|
|
847
|
-
applyUpdate(partition.doc, update);
|
|
643
|
+
if (!branch) {
|
|
644
|
+
console.log(`[CausalRepoServer] [namespace: ${request.recordName}/${request.inst}/${request.branch}, userId: ${request.userId}] Branch not found!`);
|
|
645
|
+
const instResult = await this._getOrCreateInst(request.recordName, request.inst, request.userId, config);
|
|
646
|
+
if (instResult.success === false) {
|
|
647
|
+
return instResult;
|
|
848
648
|
}
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
else {
|
|
858
|
-
let stored = [];
|
|
859
|
-
for (let i = 0; i < updates.updates.length; i++) {
|
|
860
|
-
stored.push({
|
|
861
|
-
id: i,
|
|
862
|
-
update: updates.updates[i],
|
|
863
|
-
timestamp: updates.timestamps[i],
|
|
649
|
+
else if (request.recordName) {
|
|
650
|
+
const authorizeResult = await this._policies.authorizeUserAndInstances(instResult.context, {
|
|
651
|
+
resourceKind: 'inst',
|
|
652
|
+
resourceId: request.inst,
|
|
653
|
+
action: 'updateData',
|
|
654
|
+
userId: request.userId,
|
|
655
|
+
markers: instResult.inst.markers,
|
|
656
|
+
instances: [],
|
|
864
657
|
});
|
|
658
|
+
if (authorizeResult.success === false) {
|
|
659
|
+
return authorizeResult;
|
|
660
|
+
}
|
|
865
661
|
}
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
};
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
}
|
|
876
|
-
listInsts(recordName, userId, startingInst) {
|
|
877
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
878
|
-
try {
|
|
879
|
-
if (!recordName) {
|
|
880
|
-
return {
|
|
881
|
-
success: true,
|
|
882
|
-
insts: [],
|
|
883
|
-
totalCount: 0,
|
|
884
|
-
};
|
|
662
|
+
features = instResult.features;
|
|
663
|
+
const branchResult = await this._instStore.saveBranch({
|
|
664
|
+
branch: request.branch,
|
|
665
|
+
inst: request.inst,
|
|
666
|
+
recordName: request.recordName,
|
|
667
|
+
temporary: false,
|
|
668
|
+
});
|
|
669
|
+
if (branchResult.success === false) {
|
|
670
|
+
return branchResult;
|
|
885
671
|
}
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
672
|
+
branch = await this._instStore.getBranchByName(request.recordName, request.inst, request.branch);
|
|
673
|
+
}
|
|
674
|
+
else if (request.recordName) {
|
|
675
|
+
const contextResult = await this._policies.constructAuthorizationContext({
|
|
676
|
+
recordKeyOrRecordName: request.recordName,
|
|
677
|
+
userId: request.userId,
|
|
889
678
|
});
|
|
890
679
|
if (contextResult.success === false) {
|
|
891
680
|
return contextResult;
|
|
892
681
|
}
|
|
893
|
-
const
|
|
894
|
-
|
|
895
|
-
resourceKind: 'inst',
|
|
896
|
-
action: 'list',
|
|
897
|
-
userId,
|
|
898
|
-
markers: [PRIVATE_MARKER],
|
|
682
|
+
const authorizeResult = await this._policies.authorizeUserAndInstancesForResources(contextResult.context, {
|
|
683
|
+
userId: request.userId,
|
|
899
684
|
instances: [],
|
|
685
|
+
resources: [
|
|
686
|
+
{
|
|
687
|
+
resourceKind: 'inst',
|
|
688
|
+
resourceId: request.inst,
|
|
689
|
+
action: 'read',
|
|
690
|
+
markers: branch.linkedInst.markers,
|
|
691
|
+
},
|
|
692
|
+
{
|
|
693
|
+
resourceKind: 'inst',
|
|
694
|
+
resourceId: request.inst,
|
|
695
|
+
action: 'updateData',
|
|
696
|
+
markers: branch.linkedInst.markers,
|
|
697
|
+
},
|
|
698
|
+
],
|
|
900
699
|
});
|
|
901
700
|
if (authorizeResult.success === false) {
|
|
902
701
|
return authorizeResult;
|
|
903
702
|
}
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
703
|
+
}
|
|
704
|
+
if (!features && branch.linkedInst) {
|
|
705
|
+
features = getSubscriptionFeatures(config, branch.linkedInst.subscriptionStatus, branch.linkedInst.subscriptionId, branch.linkedInst.subscriptionType);
|
|
706
|
+
}
|
|
707
|
+
let maxInstSize = null;
|
|
708
|
+
if (features &&
|
|
709
|
+
typeof features.insts.maxBytesPerInst === 'number') {
|
|
710
|
+
maxInstSize = features.insts.maxBytesPerInst;
|
|
711
|
+
}
|
|
712
|
+
else if (!request.recordName &&
|
|
713
|
+
typeof ((_e = (_d = config === null || config === void 0 ? void 0 : config.defaultFeatures) === null || _d === void 0 ? void 0 : _d.publicInsts) === null || _e === void 0 ? void 0 : _e.maxBytesPerInst) ===
|
|
714
|
+
'number') {
|
|
715
|
+
maxInstSize =
|
|
716
|
+
config.defaultFeatures.publicInsts.maxBytesPerInst;
|
|
717
|
+
}
|
|
718
|
+
if (maxInstSize) {
|
|
719
|
+
const currentSize = branch.temporary
|
|
720
|
+
? await this._temporaryStore.getInstSize(request.recordName, request.inst)
|
|
721
|
+
: await this._instStore.getInstSize(request.recordName, request.inst);
|
|
722
|
+
const neededSizeInBytes = currentSize + updateSize;
|
|
723
|
+
if (neededSizeInBytes > maxInstSize) {
|
|
908
724
|
return {
|
|
909
725
|
success: false,
|
|
910
|
-
errorCode: '
|
|
911
|
-
errorMessage: '
|
|
726
|
+
errorCode: 'subscription_limit_reached',
|
|
727
|
+
errorMessage: 'The inst has reached its maximum size.',
|
|
912
728
|
};
|
|
913
729
|
}
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
730
|
+
}
|
|
731
|
+
if (branch.temporary) {
|
|
732
|
+
// Temporary branches use a temporary inst data store.
|
|
733
|
+
// This is because temporary branches are never persisted to disk.
|
|
734
|
+
await this._temporaryStore.addUpdates(request.recordName, request.inst, request.branch, request.updates, updateSize);
|
|
735
|
+
}
|
|
736
|
+
else {
|
|
737
|
+
const result = await this._instStore.addUpdates(request.recordName, request.inst, request.branch, request.updates, updateSize);
|
|
738
|
+
if (result.success === false) {
|
|
739
|
+
console.log(`[CausalRepoServer] [namespace: ${request.recordName}/${request.inst}/${request.branch}, userId: ${request.userId}] Failed to add updates`, result);
|
|
740
|
+
if (result.errorCode === 'max_size_reached') {
|
|
741
|
+
if (result.success === false) {
|
|
742
|
+
return {
|
|
743
|
+
success: false,
|
|
744
|
+
errorCode: 'subscription_limit_reached',
|
|
745
|
+
errorMessage: 'The inst has reached its maximum size.',
|
|
746
|
+
};
|
|
747
|
+
}
|
|
748
|
+
}
|
|
917
749
|
}
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
750
|
+
else {
|
|
751
|
+
if (request.recordName &&
|
|
752
|
+
this._instStore instanceof SplitInstRecordsStore) {
|
|
753
|
+
this._instStore.temp.markBranchAsDirty({
|
|
754
|
+
recordName: request.recordName,
|
|
755
|
+
inst: request.inst,
|
|
756
|
+
branch: request.branch,
|
|
757
|
+
});
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
const hasUpdates = request.updates && request.updates.length > 0;
|
|
763
|
+
if (hasUpdates) {
|
|
764
|
+
const connectedDevices = await this._connectionStore.getConnectionsByBranch('branch', request.recordName, request.inst, request.branch);
|
|
765
|
+
let ret = {
|
|
766
|
+
type: 'repo/add_updates',
|
|
767
|
+
recordName: request.recordName,
|
|
768
|
+
inst: request.inst,
|
|
769
|
+
branch: request.branch,
|
|
770
|
+
updates: request.updates,
|
|
771
|
+
};
|
|
772
|
+
await this._messenger.sendMessage(connectedDevices.map((c) => c.serverConnectionId), ret);
|
|
773
|
+
}
|
|
774
|
+
return {
|
|
775
|
+
success: true,
|
|
776
|
+
};
|
|
777
|
+
}
|
|
778
|
+
async sendAction(connectionId, event) {
|
|
779
|
+
if (!event) {
|
|
780
|
+
console.warn('[CasualRepoServer] Trying to send event with a null event!');
|
|
781
|
+
return;
|
|
782
|
+
}
|
|
783
|
+
// const namespace = branchNamespace(event.recordName, event.inst, event.branch);
|
|
784
|
+
const connectedDevices = await this._connectionStore.getConnectionsByBranch('branch', event.recordName, event.inst, event.branch);
|
|
785
|
+
if (event.action.type === 'remote') {
|
|
786
|
+
const action = event.action.event;
|
|
787
|
+
}
|
|
788
|
+
let finalAction;
|
|
789
|
+
if (event.action.sessionId ||
|
|
790
|
+
event.action.connectionId ||
|
|
791
|
+
event.action.userId ||
|
|
792
|
+
(typeof event.action.broadcast !== 'undefined' &&
|
|
793
|
+
event.action.broadcast !== null)) {
|
|
794
|
+
finalAction = event.action;
|
|
795
|
+
}
|
|
796
|
+
else {
|
|
797
|
+
// TODO: Replace with system that selects target devices with better uniformity
|
|
798
|
+
// than Math.random().
|
|
799
|
+
const randomDeviceIndex = Math.min(connectedDevices.length - 1, Math.max(Math.floor(Math.random() * connectedDevices.length), 0));
|
|
800
|
+
const randomDevice = connectedDevices[randomDeviceIndex];
|
|
801
|
+
if (randomDevice) {
|
|
802
|
+
finalAction = {
|
|
803
|
+
...event.action,
|
|
804
|
+
connectionId: randomDevice.clientConnectionId,
|
|
922
805
|
};
|
|
923
806
|
}
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
807
|
+
}
|
|
808
|
+
if (!finalAction) {
|
|
809
|
+
return;
|
|
810
|
+
}
|
|
811
|
+
const currentConnection = await this._connectionStore.getConnection(connectionId);
|
|
812
|
+
if (currentConnection.token && event.recordName) {
|
|
813
|
+
const authorized = await this._connectionStore.isAuthorizedInst(connectionId, event.recordName, event.inst, 'token');
|
|
814
|
+
if (!authorized) {
|
|
815
|
+
await this.sendError(connectionId, -1, {
|
|
927
816
|
success: false,
|
|
928
|
-
errorCode: '
|
|
929
|
-
errorMessage: '
|
|
930
|
-
|
|
817
|
+
errorCode: 'not_authorized',
|
|
818
|
+
errorMessage: 'You are not authorized to access this inst.',
|
|
819
|
+
recordName: event.recordName,
|
|
820
|
+
inst: event.inst,
|
|
821
|
+
branch: event.branch,
|
|
822
|
+
reason: {
|
|
823
|
+
type: 'invalid_token',
|
|
824
|
+
},
|
|
825
|
+
});
|
|
826
|
+
return;
|
|
931
827
|
}
|
|
932
|
-
}
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
console.warn('[CasualRepoServer] Trying to get branch with a null branch!');
|
|
828
|
+
}
|
|
829
|
+
if (event.recordName) {
|
|
830
|
+
const config = await this._config.getSubscriptionConfiguration();
|
|
831
|
+
const instResult = await this._getInst(event.recordName, event.inst, currentConnection.userId, config);
|
|
832
|
+
if (instResult.success === false) {
|
|
833
|
+
await this.sendError(connectionId, -1, instResult);
|
|
939
834
|
return;
|
|
940
835
|
}
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
console.error(`[CausalRepoServer] [namespace: ${recordName}/${inst}/${branch}, connectionId: ${connectionId}] Unable to get_updates. Connection not found!`);
|
|
944
|
-
yield this.sendError(connectionId, -1, {
|
|
836
|
+
else if (!instResult.inst) {
|
|
837
|
+
await this.sendError(connectionId, -1, {
|
|
945
838
|
success: false,
|
|
946
|
-
errorCode: '
|
|
947
|
-
errorMessage:
|
|
948
|
-
recordName: recordName,
|
|
949
|
-
inst: inst,
|
|
950
|
-
branch: branch,
|
|
839
|
+
errorCode: 'inst_not_found',
|
|
840
|
+
errorMessage: 'The inst was not found.',
|
|
951
841
|
});
|
|
952
|
-
yield this.messenger.disconnect(connectionId);
|
|
953
842
|
return;
|
|
954
843
|
}
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
844
|
+
const authorizeResult = await this._policies.authorizeUserAndInstances(instResult.context, {
|
|
845
|
+
resourceKind: 'inst',
|
|
846
|
+
resourceId: event.inst,
|
|
847
|
+
action: 'sendAction',
|
|
848
|
+
markers: instResult.inst.markers,
|
|
849
|
+
userId: currentConnection.userId,
|
|
850
|
+
instances: [],
|
|
851
|
+
});
|
|
852
|
+
if (authorizeResult.success === false) {
|
|
853
|
+
await this.sendError(connectionId, -1, authorizeResult);
|
|
854
|
+
return;
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
const targetedDevices = connectedDevices.filter((d) => isEventForDevice(finalAction, d));
|
|
858
|
+
const dEvent = finalAction.type === 'remote'
|
|
859
|
+
? device(connectionInfo(currentConnection), finalAction.event, finalAction.taskId)
|
|
860
|
+
: finalAction.type === 'remote_result'
|
|
861
|
+
? deviceResult(connectionInfo(currentConnection), finalAction.result, finalAction.taskId)
|
|
862
|
+
: deviceError(connectionInfo(currentConnection), finalAction.error, finalAction.taskId);
|
|
863
|
+
await this._messenger.sendMessage(targetedDevices.map((c) => c.serverConnectionId), {
|
|
864
|
+
type: 'repo/receive_action',
|
|
865
|
+
recordName: event.recordName,
|
|
866
|
+
inst: event.inst,
|
|
867
|
+
branch: event.branch,
|
|
868
|
+
action: dEvent,
|
|
869
|
+
});
|
|
870
|
+
}
|
|
871
|
+
async watchBranchDevices(connectionId, recordName, inst, branch) {
|
|
872
|
+
console.log(`[WebsocketController] [namespace: ${recordName}/${inst}/${branch}, connectionId: ${connectionId}] Watch devices for branch`);
|
|
873
|
+
const connection = await this._connectionStore.getConnection(connectionId);
|
|
874
|
+
if (!connection) {
|
|
875
|
+
console.error(`[WebsocketController] [namespace: ${recordName}/${inst}/${branch}, connectionId: ${connectionId}] Unable to watch_branch_devices. Connection not found!`);
|
|
876
|
+
await this.sendError(connectionId, -1, {
|
|
877
|
+
success: false,
|
|
878
|
+
errorCode: 'invalid_connection_state',
|
|
879
|
+
errorMessage: `A server error occurred. (namespace: ${recordName}/${inst}/${branch}, connectionId: ${connectionId})`,
|
|
880
|
+
recordName: recordName,
|
|
881
|
+
inst: inst,
|
|
882
|
+
branch: branch,
|
|
883
|
+
});
|
|
884
|
+
await this.messenger.disconnect(connectionId);
|
|
885
|
+
return;
|
|
886
|
+
}
|
|
887
|
+
if (connection.token && recordName) {
|
|
888
|
+
const authorized = await this._connectionStore.isAuthorizedInst(connectionId, recordName, inst, 'token');
|
|
889
|
+
if (!authorized) {
|
|
890
|
+
await this.sendError(connectionId, -1, {
|
|
891
|
+
success: false,
|
|
892
|
+
errorCode: 'not_authorized',
|
|
893
|
+
errorMessage: 'You are not authorized to access this inst.',
|
|
894
|
+
recordName,
|
|
895
|
+
inst,
|
|
896
|
+
branch,
|
|
897
|
+
reason: {
|
|
898
|
+
type: 'invalid_token',
|
|
899
|
+
},
|
|
900
|
+
});
|
|
901
|
+
return;
|
|
971
902
|
}
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
const config =
|
|
975
|
-
const instResult =
|
|
903
|
+
}
|
|
904
|
+
if (recordName) {
|
|
905
|
+
const config = await this._config.getSubscriptionConfiguration();
|
|
906
|
+
const instResult = await this._getInst(recordName, inst, connection.userId, config);
|
|
976
907
|
if (instResult.success === false) {
|
|
977
|
-
|
|
908
|
+
await this.sendError(connectionId, -1, instResult);
|
|
909
|
+
return;
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
await this._connectionStore.saveBranchConnection({
|
|
913
|
+
...connection,
|
|
914
|
+
mode: 'watch_branch',
|
|
915
|
+
serverConnectionId: connectionId,
|
|
916
|
+
recordName,
|
|
917
|
+
inst,
|
|
918
|
+
branch,
|
|
919
|
+
temporary: true,
|
|
920
|
+
});
|
|
921
|
+
const currentDevices = await this._connectionStore.getConnectionsByBranch('branch', recordName, inst, branch);
|
|
922
|
+
const promises = currentDevices.map((device) => this._messenger.sendMessage([connectionId], {
|
|
923
|
+
type: 'repo/connected_to_branch',
|
|
924
|
+
broadcast: false,
|
|
925
|
+
connection: connectionInfo(device),
|
|
926
|
+
branch: {
|
|
927
|
+
type: 'repo/watch_branch',
|
|
928
|
+
recordName: recordName,
|
|
929
|
+
inst: inst,
|
|
930
|
+
branch: branch,
|
|
931
|
+
temporary: device.temporary,
|
|
932
|
+
},
|
|
933
|
+
}));
|
|
934
|
+
await Promise.all(promises);
|
|
935
|
+
}
|
|
936
|
+
async unwatchBranchDevices(connectionId, recordName, inst, branch) {
|
|
937
|
+
await this._connectionStore.deleteBranchConnection(connectionId, 'watch_branch', recordName, inst, branch);
|
|
938
|
+
}
|
|
939
|
+
async deviceCount(connectionId, recordName, inst, branch) {
|
|
940
|
+
const count = typeof branch !== 'undefined' && branch !== null
|
|
941
|
+
? await this._connectionStore.countConnectionsByBranch('branch', recordName, inst, branch)
|
|
942
|
+
: await this._connectionStore.countConnections();
|
|
943
|
+
const currentConnection = await this._connectionStore.getConnection(connectionId);
|
|
944
|
+
if (recordName && (currentConnection === null || currentConnection === void 0 ? void 0 : currentConnection.token)) {
|
|
945
|
+
const authorized = await this._connectionStore.isAuthorizedInst(connectionId, recordName, inst, 'token');
|
|
946
|
+
if (!authorized) {
|
|
947
|
+
await this.sendError(connectionId, -1, {
|
|
948
|
+
success: false,
|
|
949
|
+
errorCode: 'not_authorized',
|
|
950
|
+
errorMessage: 'You are not authorized to access this inst.',
|
|
951
|
+
recordName,
|
|
978
952
|
inst,
|
|
979
|
-
branch
|
|
953
|
+
branch,
|
|
954
|
+
reason: {
|
|
955
|
+
type: 'invalid_token',
|
|
956
|
+
},
|
|
957
|
+
});
|
|
980
958
|
return;
|
|
981
959
|
}
|
|
982
|
-
|
|
983
|
-
|
|
960
|
+
}
|
|
961
|
+
if (recordName) {
|
|
962
|
+
const config = await this._config.getSubscriptionConfiguration();
|
|
963
|
+
const instResult = await this._getInst(recordName, inst, currentConnection === null || currentConnection === void 0 ? void 0 : currentConnection.userId, config);
|
|
964
|
+
if (instResult.success === false) {
|
|
965
|
+
await this.sendError(connectionId, -1, instResult);
|
|
966
|
+
return;
|
|
967
|
+
}
|
|
968
|
+
else if (!instResult.inst) {
|
|
969
|
+
await this.sendError(connectionId, -1, {
|
|
984
970
|
success: false,
|
|
985
971
|
errorCode: 'inst_not_found',
|
|
986
972
|
errorMessage: 'The inst was not found.',
|
|
973
|
+
});
|
|
974
|
+
return;
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
await this._messenger.sendMessage([connectionId], {
|
|
978
|
+
type: 'repo/connection_count',
|
|
979
|
+
recordName,
|
|
980
|
+
inst,
|
|
981
|
+
branch,
|
|
982
|
+
count: count,
|
|
983
|
+
});
|
|
984
|
+
}
|
|
985
|
+
/**
|
|
986
|
+
* Gets the data from the given branch in the given inst.
|
|
987
|
+
* @param userId The ID of the user that is currently logged in.
|
|
988
|
+
* @param recordName The name of the record that the inst is in. Null if accessing a public inst.
|
|
989
|
+
* @param inst The name of the inst.
|
|
990
|
+
* @param branch The name of the branch in the inst.
|
|
991
|
+
* @param auxVersion The AUX version to return.
|
|
992
|
+
*/
|
|
993
|
+
async getBranchData(userId, recordName, inst, branch, auxVersion = 1) {
|
|
994
|
+
var _a;
|
|
995
|
+
console.log(`[WebsocketController] [namespace: ${recordName}/${inst}/${branch}] Get Data`);
|
|
996
|
+
if (recordName) {
|
|
997
|
+
const config = await this._config.getSubscriptionConfiguration();
|
|
998
|
+
const instResult = await this._getInst(recordName, inst, userId, config);
|
|
999
|
+
if (instResult.success === false) {
|
|
1000
|
+
return instResult;
|
|
1001
|
+
}
|
|
1002
|
+
else if (!instResult.inst) {
|
|
1003
|
+
return {
|
|
1004
|
+
success: false,
|
|
1005
|
+
errorCode: 'inst_not_found',
|
|
1006
|
+
errorMessage: 'The inst was not found.',
|
|
1007
|
+
};
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
const updates = (_a = (await this._instStore.getCurrentUpdates(recordName, inst, branch))) !== null && _a !== void 0 ? _a : {
|
|
1011
|
+
updates: [],
|
|
1012
|
+
timestamps: [],
|
|
1013
|
+
instSizeInBytes: 0,
|
|
1014
|
+
};
|
|
1015
|
+
if (auxVersion === 1) {
|
|
1016
|
+
const partition = new YjsPartitionImpl({ type: 'yjs' });
|
|
1017
|
+
for (let updateBase64 of updates.updates) {
|
|
1018
|
+
const update = toByteArray(updateBase64);
|
|
1019
|
+
applyUpdate(partition.doc, update);
|
|
1020
|
+
}
|
|
1021
|
+
return {
|
|
1022
|
+
success: true,
|
|
1023
|
+
data: {
|
|
1024
|
+
version: 1,
|
|
1025
|
+
state: partition.state,
|
|
1026
|
+
},
|
|
1027
|
+
};
|
|
1028
|
+
}
|
|
1029
|
+
else {
|
|
1030
|
+
let stored = [];
|
|
1031
|
+
for (let i = 0; i < updates.updates.length; i++) {
|
|
1032
|
+
stored.push({
|
|
1033
|
+
id: i,
|
|
1034
|
+
update: updates.updates[i],
|
|
1035
|
+
timestamp: updates.timestamps[i],
|
|
1036
|
+
});
|
|
1037
|
+
}
|
|
1038
|
+
return {
|
|
1039
|
+
success: true,
|
|
1040
|
+
data: {
|
|
1041
|
+
version: 2,
|
|
1042
|
+
updates: stored,
|
|
1043
|
+
},
|
|
1044
|
+
};
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
async listInsts(recordName, userId, startingInst) {
|
|
1048
|
+
try {
|
|
1049
|
+
if (!recordName) {
|
|
1050
|
+
return {
|
|
1051
|
+
success: true,
|
|
1052
|
+
insts: [],
|
|
1053
|
+
totalCount: 0,
|
|
1054
|
+
};
|
|
1055
|
+
}
|
|
1056
|
+
const contextResult = await this._policies.constructAuthorizationContext({
|
|
1057
|
+
recordKeyOrRecordName: recordName,
|
|
1058
|
+
userId,
|
|
1059
|
+
});
|
|
1060
|
+
if (contextResult.success === false) {
|
|
1061
|
+
return contextResult;
|
|
1062
|
+
}
|
|
1063
|
+
const context = contextResult.context;
|
|
1064
|
+
const authorizeResult = await this._policies.authorizeUserAndInstances(context, {
|
|
1065
|
+
resourceKind: 'inst',
|
|
1066
|
+
action: 'list',
|
|
1067
|
+
userId,
|
|
1068
|
+
markers: [PRIVATE_MARKER],
|
|
1069
|
+
instances: [],
|
|
1070
|
+
});
|
|
1071
|
+
if (authorizeResult.success === false) {
|
|
1072
|
+
return authorizeResult;
|
|
1073
|
+
}
|
|
1074
|
+
const metricsResult = await this._metrics.getSubscriptionInstMetricsByRecordName(recordName);
|
|
1075
|
+
const config = await this._config.getSubscriptionConfiguration();
|
|
1076
|
+
const features = getSubscriptionFeatures(config, metricsResult.subscriptionStatus, metricsResult.subscriptionId, metricsResult.subscriptionType);
|
|
1077
|
+
if (!features.insts.allowed) {
|
|
1078
|
+
return {
|
|
1079
|
+
success: false,
|
|
1080
|
+
errorCode: 'not_authorized',
|
|
1081
|
+
errorMessage: 'Insts are not allowed for this subscription.',
|
|
1082
|
+
};
|
|
1083
|
+
}
|
|
1084
|
+
const instsResult = await this._instStore.listInstsByRecord(recordName, startingInst);
|
|
1085
|
+
if (!instsResult.success) {
|
|
1086
|
+
return instsResult;
|
|
1087
|
+
}
|
|
1088
|
+
return {
|
|
1089
|
+
success: true,
|
|
1090
|
+
insts: instsResult.insts,
|
|
1091
|
+
totalCount: instsResult.totalCount,
|
|
1092
|
+
};
|
|
1093
|
+
}
|
|
1094
|
+
catch (err) {
|
|
1095
|
+
console.error(`[WebsocketController] A server error occurred while listing insts:`, err);
|
|
1096
|
+
return {
|
|
1097
|
+
success: false,
|
|
1098
|
+
errorCode: 'server_error',
|
|
1099
|
+
errorMessage: 'A server error occurred.',
|
|
1100
|
+
};
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
async getUpdates(connectionId, recordName, inst, branch) {
|
|
1104
|
+
var _a;
|
|
1105
|
+
if (!branch) {
|
|
1106
|
+
console.warn('[CasualRepoServer] Trying to get branch with a null branch!');
|
|
1107
|
+
return;
|
|
1108
|
+
}
|
|
1109
|
+
const connection = await this._connectionStore.getConnection(connectionId);
|
|
1110
|
+
if (!connection) {
|
|
1111
|
+
console.error(`[WebsocketController] [namespace: ${recordName}/${inst}/${branch}, connectionId: ${connectionId}] Unable to get_updates. Connection not found!`);
|
|
1112
|
+
await this.sendError(connectionId, -1, {
|
|
1113
|
+
success: false,
|
|
1114
|
+
errorCode: 'invalid_connection_state',
|
|
1115
|
+
errorMessage: `A server error occurred. (namespace: ${recordName}/${inst}/${branch}, connectionId: ${connectionId})`,
|
|
1116
|
+
recordName: recordName,
|
|
1117
|
+
inst: inst,
|
|
1118
|
+
branch: branch,
|
|
1119
|
+
});
|
|
1120
|
+
await this.messenger.disconnect(connectionId);
|
|
1121
|
+
return;
|
|
1122
|
+
}
|
|
1123
|
+
if (connection.token && recordName) {
|
|
1124
|
+
const authorized = await this._connectionStore.isAuthorizedInst(connectionId, recordName, inst, 'token');
|
|
1125
|
+
if (!authorized) {
|
|
1126
|
+
await this.sendError(connectionId, -1, {
|
|
1127
|
+
success: false,
|
|
1128
|
+
errorCode: 'not_authorized',
|
|
1129
|
+
errorMessage: 'You are not authorized to access this inst.',
|
|
987
1130
|
recordName,
|
|
988
1131
|
inst,
|
|
989
1132
|
branch,
|
|
1133
|
+
reason: {
|
|
1134
|
+
type: 'invalid_token',
|
|
1135
|
+
},
|
|
990
1136
|
});
|
|
991
1137
|
return;
|
|
992
1138
|
}
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
1139
|
+
}
|
|
1140
|
+
// const namespace = branchNamespace(recordName, inst, branch);
|
|
1141
|
+
console.log(`[WebsocketController] [namespace: ${recordName}/${inst}/${branch}, connectionId: ${connectionId}] Get Updates`);
|
|
1142
|
+
const config = await this._config.getSubscriptionConfiguration();
|
|
1143
|
+
const instResult = await this._getInst(recordName, inst, connection.userId, config);
|
|
1144
|
+
if (instResult.success === false) {
|
|
1145
|
+
await this.sendError(connectionId, -1, {
|
|
1146
|
+
...instResult,
|
|
999
1147
|
recordName,
|
|
1000
1148
|
inst,
|
|
1001
|
-
branch
|
|
1002
|
-
|
|
1003
|
-
|
|
1149
|
+
branch,
|
|
1150
|
+
});
|
|
1151
|
+
return;
|
|
1152
|
+
}
|
|
1153
|
+
else if (recordName && !instResult.inst) {
|
|
1154
|
+
await this.sendError(connectionId, -1, {
|
|
1155
|
+
success: false,
|
|
1156
|
+
errorCode: 'inst_not_found',
|
|
1157
|
+
errorMessage: 'The inst was not found.',
|
|
1158
|
+
recordName,
|
|
1159
|
+
inst,
|
|
1160
|
+
branch,
|
|
1004
1161
|
});
|
|
1162
|
+
return;
|
|
1163
|
+
}
|
|
1164
|
+
const updates = (_a = (await this._instStore.getAllUpdates(recordName, inst, branch))) !== null && _a !== void 0 ? _a : {
|
|
1165
|
+
updates: [],
|
|
1166
|
+
timestamps: [],
|
|
1167
|
+
};
|
|
1168
|
+
await this._messenger.sendMessage([connection.serverConnectionId], {
|
|
1169
|
+
type: 'repo/add_updates',
|
|
1170
|
+
recordName,
|
|
1171
|
+
inst,
|
|
1172
|
+
branch: branch,
|
|
1173
|
+
updates: updates.updates,
|
|
1174
|
+
timestamps: updates.timestamps,
|
|
1005
1175
|
});
|
|
1006
1176
|
}
|
|
1007
|
-
eraseInst(recordKeyOrName, inst, userId) {
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
if (!storedInst) {
|
|
1020
|
-
return {
|
|
1021
|
-
success: true,
|
|
1022
|
-
};
|
|
1023
|
-
}
|
|
1024
|
-
const authResult = yield this._policies.authorizeUserAndInstances(context.context, {
|
|
1025
|
-
resourceKind: 'inst',
|
|
1026
|
-
resourceId: inst,
|
|
1027
|
-
action: 'delete',
|
|
1028
|
-
markers: storedInst.markers,
|
|
1029
|
-
userId,
|
|
1030
|
-
instances: [],
|
|
1031
|
-
});
|
|
1032
|
-
if (authResult.success === false) {
|
|
1033
|
-
return authResult;
|
|
1034
|
-
}
|
|
1035
|
-
yield this._instStore.deleteInst(recordName, inst);
|
|
1177
|
+
async eraseInst(recordKeyOrName, inst, userId) {
|
|
1178
|
+
try {
|
|
1179
|
+
const context = await this._policies.constructAuthorizationContext({
|
|
1180
|
+
recordKeyOrRecordName: recordKeyOrName,
|
|
1181
|
+
userId,
|
|
1182
|
+
});
|
|
1183
|
+
if (context.success === false) {
|
|
1184
|
+
return context;
|
|
1185
|
+
}
|
|
1186
|
+
const recordName = context.context.recordName;
|
|
1187
|
+
const storedInst = await this._instStore.getInstByName(recordName, inst);
|
|
1188
|
+
if (!storedInst) {
|
|
1036
1189
|
return {
|
|
1037
1190
|
success: true,
|
|
1038
1191
|
};
|
|
1039
1192
|
}
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1193
|
+
const authResult = await this._policies.authorizeUserAndInstances(context.context, {
|
|
1194
|
+
resourceKind: 'inst',
|
|
1195
|
+
resourceId: inst,
|
|
1196
|
+
action: 'delete',
|
|
1197
|
+
markers: storedInst.markers,
|
|
1198
|
+
userId,
|
|
1199
|
+
instances: [],
|
|
1200
|
+
});
|
|
1201
|
+
if (authResult.success === false) {
|
|
1202
|
+
return authResult;
|
|
1047
1203
|
}
|
|
1048
|
-
|
|
1204
|
+
await this._instStore.deleteInst(recordName, inst);
|
|
1205
|
+
return {
|
|
1206
|
+
success: true,
|
|
1207
|
+
};
|
|
1208
|
+
}
|
|
1209
|
+
catch (err) {
|
|
1210
|
+
console.error('[WebsocketController] [eraseInst] Error while erasing inst.', err);
|
|
1211
|
+
return {
|
|
1212
|
+
success: false,
|
|
1213
|
+
errorCode: 'server_error',
|
|
1214
|
+
errorMessage: 'A server error occurred.',
|
|
1215
|
+
};
|
|
1216
|
+
}
|
|
1049
1217
|
}
|
|
1050
1218
|
/**
|
|
1051
1219
|
* Processes a webhook and returns the status code that should be returned.
|
|
@@ -1055,182 +1223,369 @@ export class WebsocketController {
|
|
|
1055
1223
|
* @param headers The headers that were included in the request.
|
|
1056
1224
|
* @param data The data included in the request.
|
|
1057
1225
|
*/
|
|
1058
|
-
webhook(recordName, inst, branch, method, url, headers, data) {
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
action: a,
|
|
1095
|
-
});
|
|
1096
|
-
return 200;
|
|
1226
|
+
async webhook(recordName, inst, branch, method, url, headers, data) {
|
|
1227
|
+
// TODO: Change webhooks to be records.
|
|
1228
|
+
if (recordName) {
|
|
1229
|
+
return;
|
|
1230
|
+
}
|
|
1231
|
+
// const namespace = branchNamespace(recordName, inst, branch);
|
|
1232
|
+
const b = await this._instStore.getBranchByName(recordName, inst, branch);
|
|
1233
|
+
if (!b) {
|
|
1234
|
+
return 404;
|
|
1235
|
+
}
|
|
1236
|
+
const connectedDevices = await this._connectionStore.getConnectionsByBranch('branch', recordName, inst, branch);
|
|
1237
|
+
if (connectedDevices.some((d) => !d)) {
|
|
1238
|
+
return 500;
|
|
1239
|
+
}
|
|
1240
|
+
if (connectedDevices.length <= 0) {
|
|
1241
|
+
return 404;
|
|
1242
|
+
}
|
|
1243
|
+
// TODO: Replace with system that selects target devices with better uniformity
|
|
1244
|
+
// than Math.random().
|
|
1245
|
+
const randomDeviceIndex = Math.min(connectedDevices.length - 1, Math.max(Math.floor(Math.random() * connectedDevices.length), 0));
|
|
1246
|
+
const randomDevice = connectedDevices[randomDeviceIndex];
|
|
1247
|
+
if (!randomDevice) {
|
|
1248
|
+
return 500;
|
|
1249
|
+
}
|
|
1250
|
+
const a = action(ON_WEBHOOK_ACTION_NAME, null, null, {
|
|
1251
|
+
method,
|
|
1252
|
+
url,
|
|
1253
|
+
headers,
|
|
1254
|
+
data,
|
|
1255
|
+
});
|
|
1256
|
+
await this._messenger.sendMessage([randomDevice.serverConnectionId], {
|
|
1257
|
+
type: 'repo/receive_action',
|
|
1258
|
+
recordName,
|
|
1259
|
+
inst,
|
|
1260
|
+
branch,
|
|
1261
|
+
action: a,
|
|
1097
1262
|
});
|
|
1263
|
+
return 200;
|
|
1098
1264
|
}
|
|
1099
1265
|
/**
|
|
1100
1266
|
* Requests that the user be given permission to access the given resource.
|
|
1101
1267
|
* @param connectionId The ID of the connection that is making the request.
|
|
1102
1268
|
* @param event The request missing permission event.
|
|
1103
1269
|
*/
|
|
1104
|
-
requestMissingPermission(connectionId, event) {
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
console.error('[WebsocketController] [requestMissingPermission] Error while getting user info.', userInfoResult);
|
|
1166
|
-
}
|
|
1167
|
-
else {
|
|
1168
|
-
userInfo = userInfoResult.user;
|
|
1169
|
-
}
|
|
1170
|
-
const inst = `${event.reason.resourceKind}/${event.reason.resourceId}`;
|
|
1171
|
-
const branch = `${event.reason.subjectType}/${event.reason.subjectId}`;
|
|
1172
|
-
yield this._connectionStore.saveBranchConnection(Object.assign(Object.assign({}, connection), { serverConnectionId: connectionId, mode: 'missing_permission', recordName: event.reason.recordName, inst: inst, branch: branch, temporary: true }));
|
|
1173
|
-
yield this._messenger.sendMessage(connections.map((c) => c.serverConnectionId), {
|
|
1174
|
-
type: 'permission/request/missing',
|
|
1175
|
-
reason: event.reason,
|
|
1176
|
-
connection: connectionInfo(connection),
|
|
1177
|
-
user: userInfo,
|
|
1178
|
-
});
|
|
1270
|
+
async requestMissingPermission(connectionId, event) {
|
|
1271
|
+
const connection = await this._connectionStore.getConnection(connectionId);
|
|
1272
|
+
if (!connection) {
|
|
1273
|
+
console.error(`[WebsocketController] [connectionId: ${connectionId}] Unable to request permission. Connection not found!`);
|
|
1274
|
+
await this.sendError(connectionId, -1, {
|
|
1275
|
+
success: false,
|
|
1276
|
+
errorCode: 'invalid_connection_state',
|
|
1277
|
+
errorMessage: `A server error occurred. (connectionId: ${connectionId})`,
|
|
1278
|
+
});
|
|
1279
|
+
await this.messenger.disconnect(connectionId);
|
|
1280
|
+
return;
|
|
1281
|
+
}
|
|
1282
|
+
if (event.reason.type !== 'missing_permission') {
|
|
1283
|
+
await this._messenger.sendMessage([connectionId], {
|
|
1284
|
+
type: 'permission/request/missing/response',
|
|
1285
|
+
success: false,
|
|
1286
|
+
recordName: event.reason.recordName,
|
|
1287
|
+
resourceKind: event.reason.resourceKind,
|
|
1288
|
+
resourceId: event.reason.resourceId,
|
|
1289
|
+
subjectType: event.reason.subjectType,
|
|
1290
|
+
subjectId: event.reason.subjectId,
|
|
1291
|
+
errorCode: 'unacceptable_request',
|
|
1292
|
+
errorMessage: 'It is only possible to request missing permissions.',
|
|
1293
|
+
});
|
|
1294
|
+
return;
|
|
1295
|
+
}
|
|
1296
|
+
else if (event.reason.resourceKind !== 'inst') {
|
|
1297
|
+
await this._messenger.sendMessage([connectionId], {
|
|
1298
|
+
type: 'permission/request/missing/response',
|
|
1299
|
+
success: false,
|
|
1300
|
+
recordName: event.reason.recordName,
|
|
1301
|
+
resourceKind: event.reason.resourceKind,
|
|
1302
|
+
resourceId: event.reason.resourceId,
|
|
1303
|
+
subjectType: event.reason.subjectType,
|
|
1304
|
+
subjectId: event.reason.subjectId,
|
|
1305
|
+
errorCode: 'unacceptable_request',
|
|
1306
|
+
errorMessage: 'Permissions can only be requested to access insts.',
|
|
1307
|
+
});
|
|
1308
|
+
return;
|
|
1309
|
+
}
|
|
1310
|
+
else if (event.reason.subjectType !== 'user' ||
|
|
1311
|
+
event.reason.subjectId !== connection.userId) {
|
|
1312
|
+
await this._messenger.sendMessage([connectionId], {
|
|
1313
|
+
type: 'permission/request/missing/response',
|
|
1314
|
+
success: false,
|
|
1315
|
+
recordName: event.reason.recordName,
|
|
1316
|
+
resourceKind: event.reason.resourceKind,
|
|
1317
|
+
resourceId: event.reason.resourceId,
|
|
1318
|
+
subjectType: event.reason.subjectType,
|
|
1319
|
+
subjectId: event.reason.subjectId,
|
|
1320
|
+
errorCode: 'unacceptable_request',
|
|
1321
|
+
errorMessage: 'Permissions can only be requested for the current user.',
|
|
1322
|
+
});
|
|
1323
|
+
return;
|
|
1324
|
+
}
|
|
1325
|
+
const connections = await this._connectionStore.getConnectionsByBranch('branch', event.reason.recordName, event.reason.resourceId, DEFAULT_BRANCH_NAME);
|
|
1326
|
+
if (connections.length > 0) {
|
|
1327
|
+
const userInfoResult = await this._auth.getPublicUserInfo(connection.userId);
|
|
1328
|
+
let userInfo = null;
|
|
1329
|
+
if (userInfoResult.success === false) {
|
|
1330
|
+
console.error('[WebsocketController] [requestMissingPermission] Error while getting user info.', userInfoResult);
|
|
1179
1331
|
}
|
|
1180
1332
|
else {
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
}
|
|
1333
|
+
userInfo = userInfoResult.user;
|
|
1334
|
+
}
|
|
1335
|
+
const inst = `${event.reason.resourceKind}/${event.reason.resourceId}`;
|
|
1336
|
+
const branch = `${event.reason.subjectType}/${event.reason.subjectId}`;
|
|
1337
|
+
await this._connectionStore.saveBranchConnection({
|
|
1338
|
+
...connection,
|
|
1339
|
+
serverConnectionId: connectionId,
|
|
1340
|
+
mode: 'missing_permission',
|
|
1341
|
+
recordName: event.reason.recordName,
|
|
1342
|
+
inst: inst,
|
|
1343
|
+
branch: branch,
|
|
1344
|
+
temporary: true,
|
|
1345
|
+
});
|
|
1346
|
+
await this._messenger.sendMessage(connections.map((c) => c.serverConnectionId), {
|
|
1347
|
+
type: 'permission/request/missing',
|
|
1348
|
+
reason: event.reason,
|
|
1349
|
+
connection: connectionInfo(connection),
|
|
1350
|
+
user: userInfo,
|
|
1351
|
+
});
|
|
1352
|
+
}
|
|
1353
|
+
else {
|
|
1354
|
+
await this._messenger.sendMessage([connectionId], {
|
|
1355
|
+
type: 'permission/request/missing/response',
|
|
1356
|
+
success: false,
|
|
1357
|
+
recordName: event.reason.recordName,
|
|
1358
|
+
resourceKind: event.reason.resourceKind,
|
|
1359
|
+
resourceId: event.reason.resourceId,
|
|
1360
|
+
subjectType: event.reason.subjectType,
|
|
1361
|
+
subjectId: event.reason.subjectId,
|
|
1362
|
+
errorCode: 'unacceptable_request',
|
|
1363
|
+
errorMessage: 'There are no currently no users available that can grant access to the inst.',
|
|
1364
|
+
});
|
|
1365
|
+
return;
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1368
|
+
/**
|
|
1369
|
+
* Responds to a missing permission request.
|
|
1370
|
+
* @param connectionId The ID of the connection that is responding to the request.
|
|
1371
|
+
* @param event The response to the missing permission request.
|
|
1372
|
+
*/
|
|
1373
|
+
async respondToPermissionRequest(connectionId, event) {
|
|
1374
|
+
const connection = await this._connectionStore.getConnection(connectionId);
|
|
1375
|
+
if (!connection) {
|
|
1376
|
+
console.error(`[WebsocketController] [connectionId: ${connectionId}] Unable to respond to permission request. Connection not found!`);
|
|
1377
|
+
await this.sendError(connectionId, -1, {
|
|
1378
|
+
success: false,
|
|
1379
|
+
errorCode: 'invalid_connection_state',
|
|
1380
|
+
errorMessage: `A server error occurred. (connectionId: ${connectionId})`,
|
|
1381
|
+
});
|
|
1382
|
+
await this.messenger.disconnect(connectionId);
|
|
1383
|
+
return;
|
|
1384
|
+
}
|
|
1385
|
+
const inst = `${event.resourceKind}/${event.resourceId}`;
|
|
1386
|
+
const branch = `${event.subjectType}/${event.subjectId}`;
|
|
1387
|
+
const otherConnections = await this._connectionStore.getConnectionsByBranch('missing_permission', event.recordName, inst, branch);
|
|
1388
|
+
if (otherConnections.length > 0) {
|
|
1389
|
+
for (let c of otherConnections) {
|
|
1390
|
+
await this._connectionStore.deleteBranchConnection(c.serverConnectionId, 'missing_permission', event.recordName, inst, branch);
|
|
1391
|
+
}
|
|
1392
|
+
await this._messenger.sendMessage(otherConnections.map((c) => c.serverConnectionId), {
|
|
1393
|
+
type: 'permission/request/missing/response',
|
|
1394
|
+
...event,
|
|
1395
|
+
connection: connectionInfo(connection),
|
|
1396
|
+
});
|
|
1397
|
+
}
|
|
1398
|
+
}
|
|
1399
|
+
/**
|
|
1400
|
+
* Attempts to install a package into an inst.
|
|
1401
|
+
* @param request The request to load the package.
|
|
1402
|
+
*/
|
|
1403
|
+
async installPackage(request) {
|
|
1404
|
+
var _a, _b;
|
|
1405
|
+
if (!this._packageVersions) {
|
|
1406
|
+
return {
|
|
1407
|
+
success: false,
|
|
1408
|
+
errorCode: 'not_supported',
|
|
1409
|
+
errorMessage: 'Package loading is not supported.',
|
|
1410
|
+
};
|
|
1411
|
+
}
|
|
1412
|
+
const userId = request.userId;
|
|
1413
|
+
const key = (_a = request.package.key) !== null && _a !== void 0 ? _a : {};
|
|
1414
|
+
console.log(`[CausalRepoServer] [namespace: ${request.recordName}/${request.inst}, ${userId}, package: ${request.package.recordName}/${request.package.address}@${formatVersionSpecifier(key)}] Install Package`);
|
|
1415
|
+
const p = await this._packageVersions.getItem({
|
|
1416
|
+
recordName: request.package.recordName,
|
|
1417
|
+
address: request.package.address,
|
|
1418
|
+
key,
|
|
1419
|
+
userId: userId,
|
|
1420
|
+
instances: request.instances,
|
|
1421
|
+
});
|
|
1422
|
+
if (p.success === false) {
|
|
1423
|
+
console.error(`[CausalRepoServer] [userId: ${userId}] Unable to load package.`, p);
|
|
1424
|
+
return p;
|
|
1425
|
+
}
|
|
1426
|
+
if (p.auxFile.success === false) {
|
|
1427
|
+
console.error(`[CausalRepoServer] [userId: ${userId}] Unable to load package file.`, p.auxFile);
|
|
1428
|
+
return p.auxFile;
|
|
1429
|
+
}
|
|
1430
|
+
const loadedPackageStore = this._instStore;
|
|
1431
|
+
const loadedPackage = await loadedPackageStore.isPackageLoaded(request.recordName, request.inst, p.item.packageId);
|
|
1432
|
+
if (loadedPackage) {
|
|
1433
|
+
// Already loaded
|
|
1434
|
+
console.log(`[CausalRepoServer] [userId: ${userId}] Package already loaded.`);
|
|
1435
|
+
return {
|
|
1436
|
+
success: true,
|
|
1437
|
+
packageLoadId: loadedPackage.id,
|
|
1438
|
+
package: p.item,
|
|
1439
|
+
};
|
|
1440
|
+
}
|
|
1441
|
+
// check that the user and target inst has the ability to run the package
|
|
1442
|
+
const context = await this._policies.constructAuthorizationContext({
|
|
1443
|
+
recordKeyOrRecordName: request.package.recordName,
|
|
1444
|
+
userId,
|
|
1445
|
+
});
|
|
1446
|
+
if (context.success === false) {
|
|
1447
|
+
return context;
|
|
1448
|
+
}
|
|
1449
|
+
const authorization = await this._policies.authorizeUserAndInstances(context.context, {
|
|
1450
|
+
userId,
|
|
1451
|
+
instances: [formatInstId(request.recordName, request.inst)],
|
|
1452
|
+
resourceKind: 'package.version',
|
|
1453
|
+
resourceId: p.item.address,
|
|
1454
|
+
action: 'run',
|
|
1455
|
+
markers: p.item.markers,
|
|
1456
|
+
});
|
|
1457
|
+
if (authorization.success === false) {
|
|
1458
|
+
return authorization;
|
|
1459
|
+
}
|
|
1460
|
+
const fileResponse = await fetch(p.auxFile.requestUrl, {
|
|
1461
|
+
method: p.auxFile.requestMethod,
|
|
1462
|
+
headers: new Headers(p.auxFile.requestHeaders),
|
|
1194
1463
|
});
|
|
1464
|
+
if (fileResponse.status >= 300) {
|
|
1465
|
+
console.error(`[CausalRepoServer] [userId: ${userId}] Unable to load package file.`);
|
|
1466
|
+
// Failed
|
|
1467
|
+
return {
|
|
1468
|
+
success: false,
|
|
1469
|
+
errorCode: 'invalid_file_data',
|
|
1470
|
+
errorMessage: 'The package file could not be loaded.',
|
|
1471
|
+
};
|
|
1472
|
+
}
|
|
1473
|
+
const json = await fileResponse.text();
|
|
1474
|
+
const packageData = tryParseJson(json);
|
|
1475
|
+
if (packageData.success === false) {
|
|
1476
|
+
console.error(`[CausalRepoServer] [userId: ${userId}] Unable to parse package file.`, packageData);
|
|
1477
|
+
return {
|
|
1478
|
+
success: false,
|
|
1479
|
+
errorCode: 'invalid_file_data',
|
|
1480
|
+
errorMessage: 'The package file could not be parsed. It must be valid JSON.',
|
|
1481
|
+
};
|
|
1482
|
+
}
|
|
1483
|
+
const parsed = STORED_AUX_SCHEMA.safeParse(packageData.value);
|
|
1484
|
+
if (parsed.success === false) {
|
|
1485
|
+
console.error(`[CausalRepoServer] [userId: ${userId}] Unable to parse package file.`, packageData);
|
|
1486
|
+
return {
|
|
1487
|
+
success: false,
|
|
1488
|
+
errorCode: 'invalid_file_data',
|
|
1489
|
+
errorMessage: 'The package file could not be parsed.',
|
|
1490
|
+
issues: parsed.error.issues,
|
|
1491
|
+
};
|
|
1492
|
+
}
|
|
1493
|
+
const updates = parsed.data.version === 2
|
|
1494
|
+
? parsed.data.updates.map((u) => u.update)
|
|
1495
|
+
: [
|
|
1496
|
+
constructInitializationUpdate(createInitializationUpdate(Object.values(parsed.data.state))).update,
|
|
1497
|
+
];
|
|
1498
|
+
const timestamps = parsed.data.version === 2
|
|
1499
|
+
? parsed.data.updates.map((u) => u.timestamp)
|
|
1500
|
+
: [0];
|
|
1501
|
+
const branch = (_b = request.branch) !== null && _b !== void 0 ? _b : DEFAULT_BRANCH_NAME;
|
|
1502
|
+
const result = await this.addUserUpdates({
|
|
1503
|
+
userId: request.userId,
|
|
1504
|
+
userRole: request.userRole,
|
|
1505
|
+
recordName: request.recordName,
|
|
1506
|
+
inst: request.inst,
|
|
1507
|
+
branch,
|
|
1508
|
+
updates: updates,
|
|
1509
|
+
timestamps: timestamps,
|
|
1510
|
+
});
|
|
1511
|
+
if (result.success === false) {
|
|
1512
|
+
return result;
|
|
1513
|
+
}
|
|
1514
|
+
const loadedPackageId = uuidv7();
|
|
1515
|
+
await loadedPackageStore.saveLoadedPackage({
|
|
1516
|
+
id: loadedPackageId,
|
|
1517
|
+
recordName: request.recordName,
|
|
1518
|
+
inst: request.inst,
|
|
1519
|
+
branch,
|
|
1520
|
+
packageId: p.item.packageId,
|
|
1521
|
+
packageVersionId: p.item.id,
|
|
1522
|
+
userId: userId,
|
|
1523
|
+
});
|
|
1524
|
+
return {
|
|
1525
|
+
success: true,
|
|
1526
|
+
packageLoadId: loadedPackageId,
|
|
1527
|
+
package: p.item,
|
|
1528
|
+
};
|
|
1195
1529
|
}
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1530
|
+
async listInstalledPackages(request) {
|
|
1531
|
+
if (!this._packageVersions) {
|
|
1532
|
+
return {
|
|
1533
|
+
success: false,
|
|
1534
|
+
errorCode: 'not_supported',
|
|
1535
|
+
errorMessage: 'Packages are not supported.',
|
|
1536
|
+
};
|
|
1537
|
+
}
|
|
1538
|
+
if (request.recordName) {
|
|
1539
|
+
const context = await this._policies.constructAuthorizationContext({
|
|
1540
|
+
recordKeyOrRecordName: request.recordName,
|
|
1541
|
+
userId: request.userId,
|
|
1542
|
+
});
|
|
1543
|
+
if (context.success === false) {
|
|
1544
|
+
return context;
|
|
1545
|
+
}
|
|
1546
|
+
const recordName = context.context.recordName;
|
|
1547
|
+
const instName = request.inst;
|
|
1548
|
+
const savedInst = await this._instStore.getInstByName(recordName, instName);
|
|
1549
|
+
if (!savedInst) {
|
|
1550
|
+
return {
|
|
1207
1551
|
success: false,
|
|
1208
|
-
errorCode: '
|
|
1209
|
-
errorMessage:
|
|
1210
|
-
}
|
|
1211
|
-
yield this.messenger.disconnect(connectionId);
|
|
1212
|
-
return;
|
|
1552
|
+
errorCode: 'inst_not_found',
|
|
1553
|
+
errorMessage: 'The inst was not found.',
|
|
1554
|
+
};
|
|
1213
1555
|
}
|
|
1214
|
-
const
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1556
|
+
const authResult = await this._policies.authorizeUserAndInstances(context.context, {
|
|
1557
|
+
resourceKind: 'inst',
|
|
1558
|
+
resourceId: instName,
|
|
1559
|
+
action: 'read',
|
|
1560
|
+
markers: savedInst.markers,
|
|
1561
|
+
userId: request.userId,
|
|
1562
|
+
instances: request.instances,
|
|
1563
|
+
});
|
|
1564
|
+
if (authResult.success === false) {
|
|
1565
|
+
return authResult;
|
|
1222
1566
|
}
|
|
1223
|
-
}
|
|
1567
|
+
}
|
|
1568
|
+
const packages = await this._instStore.listLoadedPackages(request.recordName, request.inst);
|
|
1569
|
+
return {
|
|
1570
|
+
success: true,
|
|
1571
|
+
packages: packages.map((p) => ({
|
|
1572
|
+
id: p.id,
|
|
1573
|
+
recordName: p.recordName,
|
|
1574
|
+
inst: p.inst,
|
|
1575
|
+
branch: p.branch,
|
|
1576
|
+
packageId: p.packageId,
|
|
1577
|
+
packageVersionId: p.packageVersionId,
|
|
1578
|
+
userId: p.userId,
|
|
1579
|
+
})),
|
|
1580
|
+
};
|
|
1224
1581
|
}
|
|
1225
|
-
syncTime(connectionId, event, requestTime) {
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
serverTransmitTime: Date.now(),
|
|
1233
|
-
});
|
|
1582
|
+
async syncTime(connectionId, event, requestTime) {
|
|
1583
|
+
await this._messenger.sendMessage([connectionId], {
|
|
1584
|
+
type: 'sync/time/response',
|
|
1585
|
+
id: event.id,
|
|
1586
|
+
clientRequestTime: event.clientRequestTime,
|
|
1587
|
+
serverReceiveTime: requestTime,
|
|
1588
|
+
serverTransmitTime: Date.now(),
|
|
1234
1589
|
});
|
|
1235
1590
|
}
|
|
1236
1591
|
/**
|
|
@@ -1240,127 +1595,119 @@ export class WebsocketController {
|
|
|
1240
1595
|
* @param totalHits The total number of hits by the connection.
|
|
1241
1596
|
* @param timeMs The current time in unix time in miliseconds.
|
|
1242
1597
|
*/
|
|
1243
|
-
rateLimitExceeded(connectionId, retryAfter, totalHits, timeMs) {
|
|
1598
|
+
async rateLimitExceeded(connectionId, retryAfter, totalHits, timeMs) {
|
|
1244
1599
|
var _a;
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
}
|
|
1256
|
-
});
|
|
1600
|
+
const lastHit = (_a = (await this._connectionStore.getConnectionRateLimitExceededTime(connectionId))) !== null && _a !== void 0 ? _a : -Infinity;
|
|
1601
|
+
const difference = timeMs - lastHit;
|
|
1602
|
+
await this._connectionStore.setConnectionRateLimitExceededTime(connectionId, timeMs);
|
|
1603
|
+
if (difference >= 1000) {
|
|
1604
|
+
await this._messenger.sendMessage([connectionId], {
|
|
1605
|
+
type: 'rate_limit_exceeded',
|
|
1606
|
+
retryAfter,
|
|
1607
|
+
totalHits,
|
|
1608
|
+
});
|
|
1609
|
+
}
|
|
1257
1610
|
}
|
|
1258
1611
|
/**
|
|
1259
1612
|
* Processes the given upload request.
|
|
1260
1613
|
* @param connectionId The ID of the connection that is requesting the upload.
|
|
1261
1614
|
* @param requestId The ID of the request.
|
|
1262
1615
|
*/
|
|
1263
|
-
uploadRequest(connectionId, requestId) {
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
yield this.sendError(connectionId, requestId, {
|
|
1270
|
-
success: false,
|
|
1271
|
-
errorCode: 'not_supported',
|
|
1272
|
-
errorMessage: 'Upload requests are not supported.',
|
|
1273
|
-
});
|
|
1274
|
-
return;
|
|
1275
|
-
}
|
|
1276
|
-
yield this.sendEvent(connectionId, [
|
|
1277
|
-
WebsocketEventTypes.UploadResponse,
|
|
1278
|
-
requestId,
|
|
1279
|
-
result.uploadUrl,
|
|
1280
|
-
result.uploadMethod,
|
|
1281
|
-
result.uploadHeaders,
|
|
1282
|
-
]);
|
|
1283
|
-
}
|
|
1284
|
-
catch (err) {
|
|
1285
|
-
console.error('[WebsocketController] [uploadRequest] Error while processing upload request.', err);
|
|
1286
|
-
yield this.sendError(connectionId, requestId, {
|
|
1616
|
+
async uploadRequest(connectionId, requestId) {
|
|
1617
|
+
try {
|
|
1618
|
+
const result = await this._messenger.presignMessageUpload();
|
|
1619
|
+
if (result.success === false) {
|
|
1620
|
+
console.log(`[WebsocketController] [uploadRequest] Upload requests are not supported!`);
|
|
1621
|
+
await this.sendError(connectionId, requestId, {
|
|
1287
1622
|
success: false,
|
|
1288
|
-
errorCode: '
|
|
1289
|
-
errorMessage: '
|
|
1623
|
+
errorCode: 'not_supported',
|
|
1624
|
+
errorMessage: 'Upload requests are not supported.',
|
|
1290
1625
|
});
|
|
1626
|
+
return;
|
|
1291
1627
|
}
|
|
1292
|
-
|
|
1628
|
+
await this.sendEvent(connectionId, [
|
|
1629
|
+
WebsocketEventTypes.UploadResponse,
|
|
1630
|
+
requestId,
|
|
1631
|
+
result.uploadUrl,
|
|
1632
|
+
result.uploadMethod,
|
|
1633
|
+
result.uploadHeaders,
|
|
1634
|
+
]);
|
|
1635
|
+
}
|
|
1636
|
+
catch (err) {
|
|
1637
|
+
console.error('[WebsocketController] [uploadRequest] Error while processing upload request.', err);
|
|
1638
|
+
await this.sendError(connectionId, requestId, {
|
|
1639
|
+
success: false,
|
|
1640
|
+
errorCode: 'server_error',
|
|
1641
|
+
errorMessage: 'Error while processing upload request.',
|
|
1642
|
+
});
|
|
1643
|
+
}
|
|
1293
1644
|
}
|
|
1294
|
-
downloadRequest(connectionId, requestId, url, method, headers) {
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
};
|
|
1305
|
-
}
|
|
1306
|
-
else if (message === null) {
|
|
1307
|
-
return {
|
|
1308
|
-
success: false,
|
|
1309
|
-
errorCode: 'message_not_found',
|
|
1310
|
-
errorMessage: 'Message not found.',
|
|
1311
|
-
};
|
|
1312
|
-
}
|
|
1313
|
-
else {
|
|
1314
|
-
return {
|
|
1315
|
-
success: true,
|
|
1316
|
-
requestId,
|
|
1317
|
-
message,
|
|
1318
|
-
};
|
|
1319
|
-
}
|
|
1645
|
+
async downloadRequest(connectionId, requestId, url, method, headers) {
|
|
1646
|
+
try {
|
|
1647
|
+
const message = await this._messenger.downloadMessage(url, method, headers);
|
|
1648
|
+
if (message === undefined) {
|
|
1649
|
+
console.log(`[WebsocketController] [downloadRequest] Download requests are not supported!`);
|
|
1650
|
+
return {
|
|
1651
|
+
success: false,
|
|
1652
|
+
errorCode: 'not_supported',
|
|
1653
|
+
errorMessage: 'Download requests are not supported.',
|
|
1654
|
+
};
|
|
1320
1655
|
}
|
|
1321
|
-
|
|
1322
|
-
console.error('[WebsocketController] [downloadRequest] Error while processing download request.', err);
|
|
1656
|
+
else if (message === null) {
|
|
1323
1657
|
return {
|
|
1324
1658
|
success: false,
|
|
1325
|
-
errorCode: '
|
|
1326
|
-
errorMessage: '
|
|
1659
|
+
errorCode: 'message_not_found',
|
|
1660
|
+
errorMessage: 'Message not found.',
|
|
1327
1661
|
};
|
|
1328
1662
|
}
|
|
1329
|
-
|
|
1663
|
+
else {
|
|
1664
|
+
return {
|
|
1665
|
+
success: true,
|
|
1666
|
+
requestId,
|
|
1667
|
+
message,
|
|
1668
|
+
};
|
|
1669
|
+
}
|
|
1670
|
+
}
|
|
1671
|
+
catch (err) {
|
|
1672
|
+
console.error('[WebsocketController] [downloadRequest] Error while processing download request.', err);
|
|
1673
|
+
return {
|
|
1674
|
+
success: false,
|
|
1675
|
+
errorCode: 'server_error',
|
|
1676
|
+
errorMessage: 'Error while processing download request.',
|
|
1677
|
+
};
|
|
1678
|
+
}
|
|
1330
1679
|
}
|
|
1331
1680
|
/**
|
|
1332
1681
|
* Saves all of the permanent branches that are currently in memory.
|
|
1333
1682
|
* @param timeout The timeout for the operation. Defaults to 30 seconds.
|
|
1334
1683
|
*/
|
|
1335
|
-
savePermanentBranches(timeout = 30000) {
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
continue;
|
|
1352
|
-
}
|
|
1353
|
-
yield this._saveBranchUpdates(store, branch);
|
|
1684
|
+
async savePermanentBranches(timeout = 30000) {
|
|
1685
|
+
const store = this._instStore;
|
|
1686
|
+
if (store instanceof SplitInstRecordsStore) {
|
|
1687
|
+
const unlock = await store.temp.acquireLock(SAVE_PERMANENT_BRANCHES_LOCK, timeout);
|
|
1688
|
+
if (!unlock) {
|
|
1689
|
+
console.log(`[WebsocketController] [savePermanentBranches] Unable to acquire lock.`);
|
|
1690
|
+
return;
|
|
1691
|
+
}
|
|
1692
|
+
console.log('[WebsocketController] [savePermanentBranches] Lock acquired.');
|
|
1693
|
+
try {
|
|
1694
|
+
const generation = await store.temp.getDirtyBranchGeneration();
|
|
1695
|
+
store.temp.setDirtyBranchGeneration(uuid());
|
|
1696
|
+
const branches = await store.temp.listDirtyBranches(generation);
|
|
1697
|
+
for (let branch of branches) {
|
|
1698
|
+
if (!branch.recordName) {
|
|
1699
|
+
continue;
|
|
1354
1700
|
}
|
|
1355
|
-
|
|
1356
|
-
console.log(`[WebsocketController] [savePermanentBranches] Saved.`);
|
|
1357
|
-
}
|
|
1358
|
-
finally {
|
|
1359
|
-
unlock();
|
|
1360
|
-
console.log('[WebsocketController] [savePermanentBranches] Released lock.');
|
|
1701
|
+
await this._saveBranchUpdates(store, branch);
|
|
1361
1702
|
}
|
|
1703
|
+
await store.temp.clearDirtyBranches(generation);
|
|
1704
|
+
console.log(`[WebsocketController] [savePermanentBranches] Saved.`);
|
|
1362
1705
|
}
|
|
1363
|
-
|
|
1706
|
+
finally {
|
|
1707
|
+
unlock();
|
|
1708
|
+
console.log('[WebsocketController] [savePermanentBranches] Released lock.');
|
|
1709
|
+
}
|
|
1710
|
+
}
|
|
1364
1711
|
}
|
|
1365
1712
|
/**
|
|
1366
1713
|
* Gets or creates the inst with the given name in the given record.
|
|
@@ -1373,147 +1720,135 @@ export class WebsocketController {
|
|
|
1373
1720
|
* @param userId The ID of the user that is trying to access the inst.
|
|
1374
1721
|
* @param context The authorization context.
|
|
1375
1722
|
*/
|
|
1376
|
-
_getOrCreateInst(recordName, instName, userId, config, context = null) {
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1723
|
+
async _getOrCreateInst(recordName, instName, userId, config, context = null) {
|
|
1724
|
+
let inst = null;
|
|
1725
|
+
let features = null;
|
|
1726
|
+
if (recordName) {
|
|
1727
|
+
const getInstResult = await this._getInst(recordName, instName, userId, config, context);
|
|
1728
|
+
if (getInstResult.success === false) {
|
|
1729
|
+
return getInstResult;
|
|
1730
|
+
}
|
|
1731
|
+
const savedInst = getInstResult.inst;
|
|
1732
|
+
if (!context) {
|
|
1733
|
+
context = getInstResult.context;
|
|
1734
|
+
}
|
|
1735
|
+
if (!features) {
|
|
1736
|
+
features = getInstResult.features;
|
|
1737
|
+
}
|
|
1738
|
+
if (!savedInst) {
|
|
1386
1739
|
if (!context) {
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
features = getInstResult.features;
|
|
1391
|
-
}
|
|
1392
|
-
if (!savedInst) {
|
|
1393
|
-
if (!context) {
|
|
1394
|
-
const contextResult = yield this._policies.constructAuthorizationContext({
|
|
1395
|
-
recordKeyOrRecordName: recordName,
|
|
1396
|
-
userId,
|
|
1397
|
-
});
|
|
1398
|
-
if (contextResult.success === false) {
|
|
1399
|
-
return contextResult;
|
|
1400
|
-
}
|
|
1401
|
-
context = contextResult.context;
|
|
1402
|
-
}
|
|
1403
|
-
const authorizationResult = yield this._policies.authorizeUserAndInstancesForResources(context, {
|
|
1404
|
-
userId: userId,
|
|
1405
|
-
instances: [],
|
|
1406
|
-
resources: [
|
|
1407
|
-
{
|
|
1408
|
-
resourceKind: 'inst',
|
|
1409
|
-
resourceId: instName,
|
|
1410
|
-
action: 'create',
|
|
1411
|
-
markers: [PRIVATE_MARKER],
|
|
1412
|
-
},
|
|
1413
|
-
{
|
|
1414
|
-
resourceKind: 'marker',
|
|
1415
|
-
resourceId: PRIVATE_MARKER,
|
|
1416
|
-
action: 'assign',
|
|
1417
|
-
markers: [ACCOUNT_MARKER],
|
|
1418
|
-
},
|
|
1419
|
-
{
|
|
1420
|
-
resourceKind: 'inst',
|
|
1421
|
-
resourceId: instName,
|
|
1422
|
-
action: 'read',
|
|
1423
|
-
markers: [PRIVATE_MARKER],
|
|
1424
|
-
},
|
|
1425
|
-
],
|
|
1740
|
+
const contextResult = await this._policies.constructAuthorizationContext({
|
|
1741
|
+
recordKeyOrRecordName: recordName,
|
|
1742
|
+
userId,
|
|
1426
1743
|
});
|
|
1427
|
-
if (
|
|
1428
|
-
|
|
1429
|
-
return authorizationResult;
|
|
1430
|
-
}
|
|
1431
|
-
// const authorizeCreateResult =
|
|
1432
|
-
// await this._policies.authorizeUserAndInstances(context, {
|
|
1433
|
-
// resourceKind: 'inst',
|
|
1434
|
-
// resourceId: instName,
|
|
1435
|
-
// action: 'create',
|
|
1436
|
-
// markers: [PRIVATE_MARKER],
|
|
1437
|
-
// userId,
|
|
1438
|
-
// instances: [],
|
|
1439
|
-
// });
|
|
1440
|
-
// if (authorizeCreateResult.success === false) {
|
|
1441
|
-
// console.log(
|
|
1442
|
-
// '[WebsocketController] Unable to authorize inst creation.',
|
|
1443
|
-
// authorizeCreateResult
|
|
1444
|
-
// );
|
|
1445
|
-
// return authorizeCreateResult;
|
|
1446
|
-
// }
|
|
1447
|
-
// const authorizeReadResult =
|
|
1448
|
-
// await this._policies.authorizeUserAndInstances(context, {
|
|
1449
|
-
// resourceKind: 'inst',
|
|
1450
|
-
// resourceId: instName,
|
|
1451
|
-
// action: 'read',
|
|
1452
|
-
// markers: [PRIVATE_MARKER],
|
|
1453
|
-
// userId,
|
|
1454
|
-
// instances: [],
|
|
1455
|
-
// });
|
|
1456
|
-
// if (authorizeReadResult.success === false) {
|
|
1457
|
-
// console.log(
|
|
1458
|
-
// '[WebsocketController] Unable to authorize inst creation.',
|
|
1459
|
-
// authorizeReadResult
|
|
1460
|
-
// );
|
|
1461
|
-
// return authorizeReadResult;
|
|
1462
|
-
// }
|
|
1463
|
-
const instMetrics = yield this._metrics.getSubscriptionInstMetricsByRecordName(recordName);
|
|
1464
|
-
features = getSubscriptionFeatures(config, instMetrics.subscriptionStatus, instMetrics.subscriptionId, instMetrics.subscriptionType);
|
|
1465
|
-
if (!features.insts.allowed) {
|
|
1466
|
-
return {
|
|
1467
|
-
success: false,
|
|
1468
|
-
errorCode: 'not_authorized',
|
|
1469
|
-
errorMessage: 'Insts are not allowed for this subscription.',
|
|
1470
|
-
};
|
|
1471
|
-
}
|
|
1472
|
-
else if (typeof features.insts.maxInsts === 'number' &&
|
|
1473
|
-
instMetrics.totalInsts >= features.insts.maxInsts) {
|
|
1474
|
-
return {
|
|
1475
|
-
success: false,
|
|
1476
|
-
errorCode: 'subscription_limit_reached',
|
|
1477
|
-
errorMessage: 'The maximum number of insts has been reached.',
|
|
1478
|
-
};
|
|
1744
|
+
if (contextResult.success === false) {
|
|
1745
|
+
return contextResult;
|
|
1479
1746
|
}
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1747
|
+
context = contextResult.context;
|
|
1748
|
+
}
|
|
1749
|
+
const authorizationResult = await this._policies.authorizeUserAndInstancesForResources(context, {
|
|
1750
|
+
userId: userId,
|
|
1751
|
+
instances: [],
|
|
1752
|
+
resources: [
|
|
1753
|
+
{
|
|
1754
|
+
resourceKind: 'inst',
|
|
1755
|
+
resourceId: instName,
|
|
1756
|
+
action: 'create',
|
|
1757
|
+
markers: [PRIVATE_MARKER],
|
|
1758
|
+
},
|
|
1759
|
+
{
|
|
1760
|
+
resourceKind: 'marker',
|
|
1761
|
+
resourceId: PRIVATE_MARKER,
|
|
1762
|
+
action: 'assign',
|
|
1763
|
+
markers: [ACCOUNT_MARKER],
|
|
1764
|
+
},
|
|
1765
|
+
{
|
|
1766
|
+
resourceKind: 'inst',
|
|
1767
|
+
resourceId: instName,
|
|
1768
|
+
action: 'read',
|
|
1769
|
+
markers: [PRIVATE_MARKER],
|
|
1770
|
+
},
|
|
1771
|
+
],
|
|
1772
|
+
});
|
|
1773
|
+
if (authorizationResult.success === false) {
|
|
1774
|
+
console.log('[WebsocketController] Unable to authorize inst creation.', authorizationResult);
|
|
1775
|
+
return authorizationResult;
|
|
1776
|
+
}
|
|
1777
|
+
// const authorizeCreateResult =
|
|
1778
|
+
// await this._policies.authorizeUserAndInstances(context, {
|
|
1779
|
+
// resourceKind: 'inst',
|
|
1780
|
+
// resourceId: instName,
|
|
1781
|
+
// action: 'create',
|
|
1782
|
+
// markers: [PRIVATE_MARKER],
|
|
1783
|
+
// userId,
|
|
1784
|
+
// instances: [],
|
|
1785
|
+
// });
|
|
1786
|
+
// if (authorizeCreateResult.success === false) {
|
|
1787
|
+
// console.log(
|
|
1788
|
+
// '[WebsocketController] Unable to authorize inst creation.',
|
|
1789
|
+
// authorizeCreateResult
|
|
1790
|
+
// );
|
|
1791
|
+
// return authorizeCreateResult;
|
|
1792
|
+
// }
|
|
1793
|
+
// const authorizeReadResult =
|
|
1794
|
+
// await this._policies.authorizeUserAndInstances(context, {
|
|
1795
|
+
// resourceKind: 'inst',
|
|
1796
|
+
// resourceId: instName,
|
|
1797
|
+
// action: 'read',
|
|
1798
|
+
// markers: [PRIVATE_MARKER],
|
|
1799
|
+
// userId,
|
|
1800
|
+
// instances: [],
|
|
1801
|
+
// });
|
|
1802
|
+
// if (authorizeReadResult.success === false) {
|
|
1803
|
+
// console.log(
|
|
1804
|
+
// '[WebsocketController] Unable to authorize inst creation.',
|
|
1805
|
+
// authorizeReadResult
|
|
1806
|
+
// );
|
|
1807
|
+
// return authorizeReadResult;
|
|
1808
|
+
// }
|
|
1809
|
+
const instMetrics = await this._metrics.getSubscriptionInstMetricsByRecordName(recordName);
|
|
1810
|
+
features = getSubscriptionFeatures(config, instMetrics.subscriptionStatus, instMetrics.subscriptionId, instMetrics.subscriptionType);
|
|
1811
|
+
if (!features.insts.allowed) {
|
|
1812
|
+
return {
|
|
1813
|
+
success: false,
|
|
1814
|
+
errorCode: 'not_authorized',
|
|
1815
|
+
errorMessage: 'Insts are not allowed for this subscription.',
|
|
1488
1816
|
};
|
|
1489
|
-
const result = yield this._instStore.saveInst(inst);
|
|
1490
|
-
if (result.success === false) {
|
|
1491
|
-
console.log('[WebsocketController] Unable to save inst.', result);
|
|
1492
|
-
return result;
|
|
1493
|
-
}
|
|
1494
1817
|
}
|
|
1495
|
-
else
|
|
1496
|
-
|
|
1818
|
+
else if (typeof features.insts.maxInsts === 'number' &&
|
|
1819
|
+
instMetrics.totalInsts >= features.insts.maxInsts) {
|
|
1820
|
+
return {
|
|
1821
|
+
success: false,
|
|
1822
|
+
errorCode: 'subscription_limit_reached',
|
|
1823
|
+
errorMessage: 'The maximum number of insts has been reached.',
|
|
1824
|
+
};
|
|
1825
|
+
}
|
|
1826
|
+
// Create the inst
|
|
1827
|
+
inst = {
|
|
1828
|
+
recordName: recordName,
|
|
1829
|
+
inst: instName,
|
|
1830
|
+
markers: [PRIVATE_MARKER],
|
|
1831
|
+
subscriptionId: instMetrics.subscriptionId,
|
|
1832
|
+
subscriptionStatus: instMetrics.subscriptionStatus,
|
|
1833
|
+
subscriptionType: instMetrics.subscriptionType,
|
|
1834
|
+
};
|
|
1835
|
+
const result = await this._instStore.saveInst(inst);
|
|
1836
|
+
if (result.success === false) {
|
|
1837
|
+
console.log('[WebsocketController] Unable to save inst.', result);
|
|
1838
|
+
return result;
|
|
1497
1839
|
}
|
|
1498
1840
|
}
|
|
1499
1841
|
else {
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
};
|
|
1511
|
-
}
|
|
1512
|
-
}
|
|
1513
|
-
}
|
|
1514
|
-
else {
|
|
1515
|
-
const privoConfig = yield this._config.getPrivoConfiguration();
|
|
1516
|
-
if (privoConfig) {
|
|
1842
|
+
inst = savedInst;
|
|
1843
|
+
}
|
|
1844
|
+
}
|
|
1845
|
+
else {
|
|
1846
|
+
// null record name means public temporary inst
|
|
1847
|
+
const userInfo = await this._authStore.findUser(userId);
|
|
1848
|
+
if (userInfo) {
|
|
1849
|
+
const userPrivacyFeatures = userInfo.privacyFeatures;
|
|
1850
|
+
if (userPrivacyFeatures) {
|
|
1851
|
+
if (!userPrivacyFeatures.allowPublicInsts) {
|
|
1517
1852
|
return {
|
|
1518
1853
|
success: false,
|
|
1519
1854
|
errorCode: 'not_authorized',
|
|
@@ -1522,13 +1857,23 @@ export class WebsocketController {
|
|
|
1522
1857
|
}
|
|
1523
1858
|
}
|
|
1524
1859
|
}
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1860
|
+
else {
|
|
1861
|
+
const privoConfig = await this._config.getPrivoConfiguration();
|
|
1862
|
+
if (privoConfig) {
|
|
1863
|
+
return {
|
|
1864
|
+
success: false,
|
|
1865
|
+
errorCode: 'not_authorized',
|
|
1866
|
+
errorMessage: 'Public insts are not allowed.',
|
|
1867
|
+
};
|
|
1868
|
+
}
|
|
1869
|
+
}
|
|
1870
|
+
}
|
|
1871
|
+
return {
|
|
1872
|
+
success: true,
|
|
1873
|
+
inst,
|
|
1874
|
+
context,
|
|
1875
|
+
features,
|
|
1876
|
+
};
|
|
1532
1877
|
}
|
|
1533
1878
|
/**
|
|
1534
1879
|
* Gets the inst with the given name in the given record.
|
|
@@ -1540,154 +1885,144 @@ export class WebsocketController {
|
|
|
1540
1885
|
* @param instName The name of the inst.
|
|
1541
1886
|
* @param userId The ID of the user that is trying to access the inst.
|
|
1542
1887
|
*/
|
|
1543
|
-
_getInst(recordName, instName, userId, config, context = null) {
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
recordKeyOrRecordName: recordName,
|
|
1561
|
-
userId,
|
|
1562
|
-
});
|
|
1563
|
-
if (contextResult.success === false) {
|
|
1564
|
-
return contextResult;
|
|
1565
|
-
}
|
|
1566
|
-
context = contextResult.context;
|
|
1567
|
-
}
|
|
1568
|
-
const authorizeResult = yield this._policies.authorizeUserAndInstances(context, {
|
|
1569
|
-
resourceKind: 'inst',
|
|
1570
|
-
resourceId: instName,
|
|
1571
|
-
action: 'read',
|
|
1572
|
-
markers: savedInst.markers,
|
|
1888
|
+
async _getInst(recordName, instName, userId, config, context = null) {
|
|
1889
|
+
let inst = null;
|
|
1890
|
+
let features = null;
|
|
1891
|
+
if (recordName) {
|
|
1892
|
+
const savedInst = await this._instStore.getInstByName(recordName, instName);
|
|
1893
|
+
if (savedInst) {
|
|
1894
|
+
features = getSubscriptionFeatures(config, savedInst.subscriptionStatus, savedInst.subscriptionId, savedInst.subscriptionType);
|
|
1895
|
+
if (!features.insts.allowed) {
|
|
1896
|
+
return {
|
|
1897
|
+
success: false,
|
|
1898
|
+
errorCode: 'not_authorized',
|
|
1899
|
+
errorMessage: 'Insts are not allowed for this subscription.',
|
|
1900
|
+
};
|
|
1901
|
+
}
|
|
1902
|
+
if (!context) {
|
|
1903
|
+
const contextResult = await this._policies.constructAuthorizationContext({
|
|
1904
|
+
recordKeyOrRecordName: recordName,
|
|
1573
1905
|
userId,
|
|
1574
|
-
instances: [],
|
|
1575
1906
|
});
|
|
1576
|
-
if (
|
|
1577
|
-
|
|
1578
|
-
return authorizeResult;
|
|
1907
|
+
if (contextResult.success === false) {
|
|
1908
|
+
return contextResult;
|
|
1579
1909
|
}
|
|
1910
|
+
context = contextResult.context;
|
|
1911
|
+
}
|
|
1912
|
+
const authorizeResult = await this._policies.authorizeUserAndInstances(context, {
|
|
1913
|
+
resourceKind: 'inst',
|
|
1914
|
+
resourceId: instName,
|
|
1915
|
+
action: 'read',
|
|
1916
|
+
markers: savedInst.markers,
|
|
1917
|
+
userId,
|
|
1918
|
+
instances: [],
|
|
1919
|
+
});
|
|
1920
|
+
if (authorizeResult.success === false) {
|
|
1921
|
+
console.log('[WebsocketController] Unable to authorize inst read.', authorizeResult);
|
|
1922
|
+
return authorizeResult;
|
|
1580
1923
|
}
|
|
1581
|
-
return {
|
|
1582
|
-
success: true,
|
|
1583
|
-
inst: savedInst,
|
|
1584
|
-
context,
|
|
1585
|
-
features,
|
|
1586
|
-
};
|
|
1587
1924
|
}
|
|
1588
1925
|
return {
|
|
1589
1926
|
success: true,
|
|
1590
|
-
inst,
|
|
1927
|
+
inst: savedInst,
|
|
1591
1928
|
context,
|
|
1592
1929
|
features,
|
|
1593
1930
|
};
|
|
1594
|
-
}
|
|
1931
|
+
}
|
|
1932
|
+
return {
|
|
1933
|
+
success: true,
|
|
1934
|
+
inst,
|
|
1935
|
+
context,
|
|
1936
|
+
features,
|
|
1937
|
+
};
|
|
1595
1938
|
}
|
|
1596
|
-
_getOrCreateBranch(recordName, inst, branch, temporary, linkedInst) {
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
b = yield this._temporaryStore.getBranchByName(recordName, inst, branch);
|
|
1610
|
-
}
|
|
1611
|
-
return b;
|
|
1939
|
+
async _getOrCreateBranch(recordName, inst, branch, temporary, linkedInst) {
|
|
1940
|
+
if (temporary) {
|
|
1941
|
+
let b = await this._temporaryStore.getBranchByName(recordName, inst, branch);
|
|
1942
|
+
if (!b) {
|
|
1943
|
+
// Save the branch to the temp store
|
|
1944
|
+
await this._temporaryStore.saveBranchInfo({
|
|
1945
|
+
recordName: recordName,
|
|
1946
|
+
inst: inst,
|
|
1947
|
+
branch: branch,
|
|
1948
|
+
temporary: true,
|
|
1949
|
+
linkedInst: linkedInst,
|
|
1950
|
+
});
|
|
1951
|
+
b = await this._temporaryStore.getBranchByName(recordName, inst, branch);
|
|
1612
1952
|
}
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
}
|
|
1625
|
-
|
|
1953
|
+
return b;
|
|
1954
|
+
}
|
|
1955
|
+
else {
|
|
1956
|
+
let b = await this._instStore.getBranchByName(recordName, inst, branch);
|
|
1957
|
+
if (!b) {
|
|
1958
|
+
// Save the branch to the inst store
|
|
1959
|
+
await this._instStore.saveBranch({
|
|
1960
|
+
branch: branch,
|
|
1961
|
+
inst: inst,
|
|
1962
|
+
recordName: recordName,
|
|
1963
|
+
temporary: temporary || false,
|
|
1964
|
+
});
|
|
1965
|
+
b = await this._instStore.getBranchByName(recordName, inst, branch);
|
|
1626
1966
|
}
|
|
1627
|
-
|
|
1967
|
+
return b;
|
|
1968
|
+
}
|
|
1628
1969
|
}
|
|
1629
|
-
_saveBranchUpdates(store, branch) {
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1970
|
+
async _saveBranchUpdates(store, branch) {
|
|
1971
|
+
console.log(`[WebsocketController] Saving branch updates for ${branch.recordName}/${branch.inst}/${branch.branch}`);
|
|
1972
|
+
let [updateCount, size] = await Promise.all([
|
|
1973
|
+
store.temp.countBranchUpdates(branch.recordName, branch.inst, branch.branch),
|
|
1974
|
+
store.temp.getBranchSize(branch.recordName, branch.inst, branch.branch),
|
|
1975
|
+
]);
|
|
1976
|
+
if (updateCount <= 0) {
|
|
1977
|
+
console.log(`[WebsocketController] Branch has no updates to save.`);
|
|
1978
|
+
return;
|
|
1979
|
+
}
|
|
1980
|
+
const branchInfo = await store.getBranchByName(branch.recordName, branch.inst, branch.branch);
|
|
1981
|
+
if (!branchInfo) {
|
|
1982
|
+
console.log(`[WebsocketController] Branch info not found.`);
|
|
1983
|
+
return;
|
|
1984
|
+
}
|
|
1985
|
+
if (branchInfo.temporary) {
|
|
1986
|
+
console.log(`[WebsocketController] Branch is temporary.`);
|
|
1987
|
+
return;
|
|
1988
|
+
}
|
|
1989
|
+
const updates = await store.temp.getUpdates(branch.recordName, branch.inst, branch.branch);
|
|
1990
|
+
if (updates) {
|
|
1991
|
+
const doc = new Doc({
|
|
1992
|
+
gc: true,
|
|
1993
|
+
});
|
|
1994
|
+
for (let update of updates.updates) {
|
|
1995
|
+
const bytes = toByteArray(update);
|
|
1996
|
+
applyUpdate(doc, bytes);
|
|
1997
|
+
}
|
|
1998
|
+
const mergedBytes = encodeStateAsUpdate(doc);
|
|
1999
|
+
const mergedBase64 = fromByteArray(mergedBytes);
|
|
2000
|
+
const permanentReplaceResult = await store.perm.replaceCurrentUpdates(branch.recordName, branch.inst, branch.branch, mergedBase64, mergedBase64.length);
|
|
2001
|
+
if (permanentReplaceResult.success === false) {
|
|
2002
|
+
console.error(`[WebsocketController] Failed to replace permanent updates.`, permanentReplaceResult);
|
|
1647
2003
|
return;
|
|
1648
2004
|
}
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
for (let update of updates.updates) {
|
|
1655
|
-
const bytes = toByteArray(update);
|
|
1656
|
-
applyUpdate(doc, bytes);
|
|
1657
|
-
}
|
|
1658
|
-
const mergedBytes = encodeStateAsUpdate(doc);
|
|
1659
|
-
const mergedBase64 = fromByteArray(mergedBytes);
|
|
1660
|
-
const permanentReplaceResult = yield store.perm.replaceCurrentUpdates(branch.recordName, branch.inst, branch.branch, mergedBase64, mergedBase64.length);
|
|
1661
|
-
if (permanentReplaceResult.success === false) {
|
|
1662
|
-
console.error(`[WebsocketController] Failed to replace permanent updates.`, permanentReplaceResult);
|
|
1663
|
-
return;
|
|
1664
|
-
}
|
|
1665
|
-
yield store.temp.addUpdates(branch.recordName, branch.inst, branch.branch, [mergedBase64], mergedBase64.length);
|
|
1666
|
-
yield store.temp.trimUpdates(branch.recordName, branch.inst, branch.branch, updateCount);
|
|
1667
|
-
yield Promise.all([
|
|
1668
|
-
store.temp.addBranchSize(branch.recordName, branch.inst, branch.branch, -size),
|
|
1669
|
-
store.temp.addInstSize(branch.recordName, branch.inst, -size),
|
|
1670
|
-
]);
|
|
1671
|
-
}
|
|
1672
|
-
else {
|
|
1673
|
-
console.log(`[WebsocketController] No updates found.`);
|
|
1674
|
-
}
|
|
1675
|
-
console.log(`[WebsocketController] Updates complete.`);
|
|
1676
|
-
});
|
|
1677
|
-
}
|
|
1678
|
-
sendError(connectionId, requestId, info) {
|
|
1679
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
1680
|
-
yield this.sendEvent(connectionId, [
|
|
1681
|
-
WebsocketEventTypes.Error,
|
|
1682
|
-
requestId,
|
|
1683
|
-
info,
|
|
2005
|
+
await store.temp.addUpdates(branch.recordName, branch.inst, branch.branch, [mergedBase64], mergedBase64.length);
|
|
2006
|
+
await store.temp.trimUpdates(branch.recordName, branch.inst, branch.branch, updateCount);
|
|
2007
|
+
await Promise.all([
|
|
2008
|
+
store.temp.addBranchSize(branch.recordName, branch.inst, branch.branch, -size),
|
|
2009
|
+
store.temp.addInstSize(branch.recordName, branch.inst, -size),
|
|
1684
2010
|
]);
|
|
1685
|
-
}
|
|
2011
|
+
}
|
|
2012
|
+
else {
|
|
2013
|
+
console.log(`[WebsocketController] No updates found.`);
|
|
2014
|
+
}
|
|
2015
|
+
console.log(`[WebsocketController] Updates complete.`);
|
|
1686
2016
|
}
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
2017
|
+
async sendError(connectionId, requestId, info) {
|
|
2018
|
+
await this.sendEvent(connectionId, [
|
|
2019
|
+
WebsocketEventTypes.Error,
|
|
2020
|
+
requestId,
|
|
2021
|
+
info,
|
|
2022
|
+
]);
|
|
2023
|
+
}
|
|
2024
|
+
async sendEvent(connectionId, event) {
|
|
2025
|
+
await this._messenger.sendEvent(connectionId, event);
|
|
1691
2026
|
}
|
|
1692
2027
|
}
|
|
1693
2028
|
__decorate([
|
|
@@ -1705,6 +2040,9 @@ __decorate([
|
|
|
1705
2040
|
__decorate([
|
|
1706
2041
|
traced(TRACE_NAME)
|
|
1707
2042
|
], WebsocketController.prototype, "addUpdates", null);
|
|
2043
|
+
__decorate([
|
|
2044
|
+
traced(TRACE_NAME)
|
|
2045
|
+
], WebsocketController.prototype, "addUserUpdates", null);
|
|
1708
2046
|
__decorate([
|
|
1709
2047
|
traced(TRACE_NAME)
|
|
1710
2048
|
], WebsocketController.prototype, "sendAction", null);
|
|
@@ -1738,6 +2076,12 @@ __decorate([
|
|
|
1738
2076
|
__decorate([
|
|
1739
2077
|
traced(TRACE_NAME)
|
|
1740
2078
|
], WebsocketController.prototype, "respondToPermissionRequest", null);
|
|
2079
|
+
__decorate([
|
|
2080
|
+
traced(TRACE_NAME)
|
|
2081
|
+
], WebsocketController.prototype, "installPackage", null);
|
|
2082
|
+
__decorate([
|
|
2083
|
+
traced(TRACE_NAME)
|
|
2084
|
+
], WebsocketController.prototype, "listInstalledPackages", null);
|
|
1741
2085
|
__decorate([
|
|
1742
2086
|
traced(TRACE_NAME)
|
|
1743
2087
|
], WebsocketController.prototype, "syncTime", null);
|