rxdb-server 15.4.3
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/LICENSE.txt +555 -0
- package/README.md +7 -0
- package/dist/cjs/index.js +10 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/plugins/client-rest/index.js +86 -0
- package/dist/cjs/plugins/client-rest/index.js.map +1 -0
- package/dist/cjs/plugins/client-rest/utils.js +19 -0
- package/dist/cjs/plugins/client-rest/utils.js.map +1 -0
- package/dist/cjs/plugins/replication-server/helpers.js +38 -0
- package/dist/cjs/plugins/replication-server/helpers.js.map +1 -0
- package/dist/cjs/plugins/replication-server/index.js +169 -0
- package/dist/cjs/plugins/replication-server/index.js.map +1 -0
- package/dist/cjs/plugins/replication-server/types.js +2 -0
- package/dist/cjs/plugins/replication-server/types.js.map +1 -0
- package/dist/cjs/plugins/server/endpoint-replication.js +162 -0
- package/dist/cjs/plugins/server/endpoint-replication.js.map +1 -0
- package/dist/cjs/plugins/server/endpoint-rest.js +219 -0
- package/dist/cjs/plugins/server/endpoint-rest.js.map +1 -0
- package/dist/cjs/plugins/server/helper.js +173 -0
- package/dist/cjs/plugins/server/helper.js.map +1 -0
- package/dist/cjs/plugins/server/index.js +84 -0
- package/dist/cjs/plugins/server/index.js.map +1 -0
- package/dist/cjs/plugins/server/rx-server.js +51 -0
- package/dist/cjs/plugins/server/rx-server.js.map +1 -0
- package/dist/cjs/plugins/server/types.js +37 -0
- package/dist/cjs/plugins/server/types.js.map +1 -0
- package/dist/esm/index.js +4 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/package.json +1 -0
- package/dist/esm/plugins/client-rest/index.js +64 -0
- package/dist/esm/plugins/client-rest/index.js.map +1 -0
- package/dist/esm/plugins/client-rest/utils.js +13 -0
- package/dist/esm/plugins/client-rest/utils.js.map +1 -0
- package/dist/esm/plugins/replication-server/helpers.js +32 -0
- package/dist/esm/plugins/replication-server/helpers.js.map +1 -0
- package/dist/esm/plugins/replication-server/index.js +146 -0
- package/dist/esm/plugins/replication-server/index.js.map +1 -0
- package/dist/esm/plugins/replication-server/types.js +3 -0
- package/dist/esm/plugins/replication-server/types.js.map +1 -0
- package/dist/esm/plugins/server/endpoint-replication.js +156 -0
- package/dist/esm/plugins/server/endpoint-replication.js.map +1 -0
- package/dist/esm/plugins/server/endpoint-rest.js +213 -0
- package/dist/esm/plugins/server/endpoint-rest.js.map +1 -0
- package/dist/esm/plugins/server/helper.js +157 -0
- package/dist/esm/plugins/server/helper.js.map +1 -0
- package/dist/esm/plugins/server/index.js +30 -0
- package/dist/esm/plugins/server/index.js.map +1 -0
- package/dist/esm/plugins/server/rx-server.js +45 -0
- package/dist/esm/plugins/server/rx-server.js.map +1 -0
- package/dist/esm/plugins/server/types.js +37 -0
- package/dist/esm/plugins/server/types.js.map +1 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/plugins/client-rest/index.d.ts +22 -0
- package/dist/types/plugins/client-rest/utils.d.ts +2 -0
- package/dist/types/plugins/replication-server/helpers.d.ts +2 -0
- package/dist/types/plugins/replication-server/index.d.ts +21 -0
- package/dist/types/plugins/replication-server/types.d.ts +17 -0
- package/dist/types/plugins/server/endpoint-replication.d.ts +20 -0
- package/dist/types/plugins/server/endpoint-rest.d.ts +16 -0
- package/dist/types/plugins/server/helper.d.ts +23 -0
- package/dist/types/plugins/server/index.d.ts +7 -0
- package/dist/types/plugins/server/rx-server.d.ts +44 -0
- package/dist/types/plugins/server/types.d.ts +66 -0
- package/package.json +139 -0
- package/plugins/client-rest/index.cjs +2 -0
- package/plugins/client-rest/index.d.cts +1 -0
- package/plugins/client-rest/index.d.mts +1 -0
- package/plugins/client-rest/index.mjs +1 -0
- package/plugins/client-rest/index.ts +1 -0
- package/plugins/client-rest/package.json +18 -0
- package/plugins/replication-server/index.cjs +2 -0
- package/plugins/replication-server/index.d.cts +1 -0
- package/plugins/replication-server/index.d.mts +1 -0
- package/plugins/replication-server/index.mjs +1 -0
- package/plugins/replication-server/index.ts +1 -0
- package/plugins/replication-server/package.json +18 -0
- package/plugins/server/index.cjs +2 -0
- package/plugins/server/index.d.cts +1 -0
- package/plugins/server/index.d.mts +1 -0
- package/plugins/server/index.mjs +1 -0
- package/plugins/server/index.ts +1 -0
- package/plugins/server/package.json +18 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Returns the auth state by the given request headers.
|
|
5
|
+
* Throws if auth not valid.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Modifies a given query in a way to limit the results
|
|
10
|
+
* to what the authenticated user is allowed to see.
|
|
11
|
+
* For example the query selector
|
|
12
|
+
* input: {
|
|
13
|
+
* selector: {
|
|
14
|
+
* myField: { $gt: 100 }
|
|
15
|
+
* }
|
|
16
|
+
* }
|
|
17
|
+
* could be modified to restrict the results to only return
|
|
18
|
+
* documents that are "owned" by the user
|
|
19
|
+
* return: {
|
|
20
|
+
* selector: {
|
|
21
|
+
* myField: { $gt: 100 },
|
|
22
|
+
* userId: { $eq: authData.userId }
|
|
23
|
+
* }
|
|
24
|
+
* }
|
|
25
|
+
*
|
|
26
|
+
*
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Validates if a given change is allowed to be performed on the server.
|
|
31
|
+
* Returns true if allowed, false if not.
|
|
32
|
+
* If a client tries to make a non-allowed change,
|
|
33
|
+
* the client will be disconnected.
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
;
|
|
37
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","names":[],"sources":["../../../../src/plugins/server/types.ts"],"sourcesContent":["import type {\n FilledMangoQuery,\n RxDatabase,\n RxReplicationWriteToMasterRow,\n MaybePromise,\n RxCollection,\n WithDeleted\n} from 'rxdb/plugins/core';\nimport { IncomingHttpHeaders } from 'http';\nimport { Express } from 'express';\n\nexport type RxServerOptions<AuthType> = {\n database: RxDatabase;\n authHandler?: RxServerAuthHandler<AuthType>;\n serverApp?: Express;\n appOptions?: any;\n /**\n * [default=localhost]\n */\n hostname?: 'localhost' | '0.0.0.0' | string;\n port: number;\n /**\n * Set a origin for allowed CORS requests.\n * Can be overwritten by the cors option of the endpoints.\n * [default='*']\n */\n cors?: '*' | string;\n};\n\nexport type RxServerAuthData<AuthType> = {\n data: AuthType;\n validUntil: number;\n};\n\n/**\n * Returns the auth state by the given request headers.\n * Throws if auth not valid.\n */\nexport type RxServerAuthHandler<AuthType> =\n (headers: IncomingHttpHeaders) => MaybePromise<RxServerAuthData<AuthType>>;\n\n/**\n * Modifies a given query in a way to limit the results\n * to what the authenticated user is allowed to see.\n * For example the query selector\n * input: {\n * selector: {\n * myField: { $gt: 100 }\n * }\n * }\n * could be modified to restrict the results to only return\n * documents that are \"owned\" by the user\n * return: {\n * selector: {\n * myField: { $gt: 100 },\n * userId: { $eq: authData.userId }\n * }\n * }\n * \n * \n */\nexport type RxServerQueryModifier<AuthType, RxDocType> = (\n authData: RxServerAuthData<AuthType>,\n query: FilledMangoQuery<RxDocType>\n) => FilledMangoQuery<RxDocType>;\n\n/**\n * Validates if a given change is allowed to be performed on the server.\n * Returns true if allowed, false if not.\n * If a client tries to make a non-allowed change,\n * the client will be disconnected.\n */\nexport type RxServerChangeValidator<AuthType, RxDocType> = (\n authData: RxServerAuthData<AuthType>,\n change: RxReplicationWriteToMasterRow<RxDocType>\n) => boolean;\n\n\nexport interface RxServerEndpoint<AuthType, RxDocType> {\n collection: RxCollection<RxDocType>;\n name: string;\n type: 'replication' | 'rest' | string;\n urlPath: string;\n queryModifier?: RxServerQueryModifier<AuthType, RxDocType>;\n changeValidator?: RxServerChangeValidator<AuthType, RxDocType>;\n};\n"],"mappings":";;AAkCA;AACA;AACA;AACA;;AAIA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAMA;AACA;AACA;AACA;AACA;AACA;;AAcC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":["wrongImport","Error"],"sources":["../../src/index.ts"],"sourcesContent":["export function wrongImport() {\n throw new Error('You should never import this file, only the server plugins');\n}\n"],"mappings":"AAAA,OAAO,SAASA,WAAWA,CAAA,EAAG;EAC1B,MAAM,IAAIC,KAAK,CAAC,4DAA4D,CAAC;AACjF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{ "type": "module", "sideEffects": false }
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { postRequest } from "./utils.js";
|
|
2
|
+
import { Subject } from 'rxjs';
|
|
3
|
+
import EventSource from 'eventsource';
|
|
4
|
+
export var RxRestClient = /*#__PURE__*/function () {
|
|
5
|
+
function RxRestClient(endpointUrl, headers = {}, eventSource = EventSource) {
|
|
6
|
+
this.endpointUrl = endpointUrl;
|
|
7
|
+
this.headers = headers;
|
|
8
|
+
this.eventSource = eventSource;
|
|
9
|
+
}
|
|
10
|
+
var _proto = RxRestClient.prototype;
|
|
11
|
+
_proto.setHeaders = function setHeaders(headers) {
|
|
12
|
+
this.headers = headers;
|
|
13
|
+
};
|
|
14
|
+
_proto.handleError = function handleError(response) {
|
|
15
|
+
if (response.error) {
|
|
16
|
+
throw new Error('Server returned an error ' + JSON.stringify(response));
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
_proto.query = async function query(_query) {
|
|
20
|
+
var response = await postRequest(this.endpointUrl + '/query', _query, this.headers);
|
|
21
|
+
this.handleError(response);
|
|
22
|
+
return response;
|
|
23
|
+
};
|
|
24
|
+
_proto.observeQuery = function observeQuery(query) {
|
|
25
|
+
var result = new Subject();
|
|
26
|
+
var queryAsBase64 = btoa(JSON.stringify(query));
|
|
27
|
+
var eventSource = new this.eventSource(this.endpointUrl + '/query/observe?query=' + queryAsBase64, {
|
|
28
|
+
withCredentials: true,
|
|
29
|
+
/**
|
|
30
|
+
* Sending headers is not supported by the Browser EventSource API,
|
|
31
|
+
* only by the npm module we use. In react-native you might have
|
|
32
|
+
* to set another EventSource implementation.
|
|
33
|
+
* @link https://www.npmjs.com/package/eventsource
|
|
34
|
+
*/
|
|
35
|
+
headers: this.headers
|
|
36
|
+
});
|
|
37
|
+
eventSource.onmessage = event => {
|
|
38
|
+
var eventData = JSON.parse(event.data);
|
|
39
|
+
result.next(eventData);
|
|
40
|
+
};
|
|
41
|
+
return result.asObservable();
|
|
42
|
+
};
|
|
43
|
+
_proto.get = function get(ids) {
|
|
44
|
+
var response = postRequest(this.endpointUrl + '/get', ids, this.headers);
|
|
45
|
+
this.handleError(response);
|
|
46
|
+
return response;
|
|
47
|
+
};
|
|
48
|
+
_proto.set = function set(docs) {
|
|
49
|
+
var response = postRequest(this.endpointUrl + '/set', docs, this.headers);
|
|
50
|
+
this.handleError(response);
|
|
51
|
+
return response;
|
|
52
|
+
};
|
|
53
|
+
_proto.delete = function _delete(ids) {
|
|
54
|
+
var response = postRequest(this.endpointUrl + '/delete', ids, this.headers);
|
|
55
|
+
this.handleError(response);
|
|
56
|
+
return response;
|
|
57
|
+
};
|
|
58
|
+
return RxRestClient;
|
|
59
|
+
}();
|
|
60
|
+
export function createRestClient(endpointUrl, headers, eventSource = EventSource) {
|
|
61
|
+
return new RxRestClient(endpointUrl, headers, eventSource);
|
|
62
|
+
}
|
|
63
|
+
export * from "./utils.js";
|
|
64
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":["postRequest","Subject","EventSource","RxRestClient","endpointUrl","headers","eventSource","_proto","prototype","setHeaders","handleError","response","error","Error","JSON","stringify","query","observeQuery","result","queryAsBase64","btoa","withCredentials","onmessage","event","eventData","parse","data","next","asObservable","get","ids","set","docs","delete","_delete","createRestClient"],"sources":["../../../../src/plugins/client-rest/index.ts"],"sourcesContent":["import { ById, MangoQuery, newRxError } from 'rxdb/plugins/core';\nimport { postRequest } from './utils.ts';\nimport { Observable, Subject } from 'rxjs';\nimport EventSource from 'eventsource';\n\nexport class RxRestClient<RxDocType> {\n constructor(\n public readonly endpointUrl: string,\n public headers: ById<string> = {},\n public readonly eventSource: typeof EventSource | any = EventSource\n ) { }\n\n setHeaders(headers: ById<string>) {\n this.headers = headers;\n }\n\n handleError(response: any) {\n if (response.error) {\n throw new Error('Server returned an error ' + JSON.stringify(response));\n }\n }\n\n async query(query: MangoQuery<RxDocType>): Promise<{ documents: RxDocType[] }> {\n const response = await postRequest(\n this.endpointUrl + '/query',\n query,\n this.headers\n );\n this.handleError(response);\n return response;\n }\n\n observeQuery(query: MangoQuery<RxDocType>): Observable<RxDocType[]> {\n const result = new Subject<RxDocType[]>;\n const queryAsBase64 = btoa(JSON.stringify(query));\n const eventSource: EventSource = new this.eventSource(\n this.endpointUrl + '/query/observe?query=' + queryAsBase64,\n {\n withCredentials: true,\n /**\n * Sending headers is not supported by the Browser EventSource API,\n * only by the npm module we use. In react-native you might have\n * to set another EventSource implementation.\n * @link https://www.npmjs.com/package/eventsource\n */\n headers: this.headers\n });\n eventSource.onmessage = event => {\n const eventData = JSON.parse(event.data);\n result.next(eventData);\n };\n return result.asObservable();\n }\n\n get(ids: string[]): Promise<{ documents: RxDocType[] }> {\n const response = postRequest(\n this.endpointUrl + '/get',\n ids,\n this.headers\n );\n this.handleError(response);\n return response;\n }\n\n set(docs: RxDocType[]) {\n const response = postRequest(\n this.endpointUrl + '/set',\n docs,\n this.headers\n );\n this.handleError(response);\n return response;\n }\n\n delete(ids: string[]) {\n const response = postRequest(\n this.endpointUrl + '/delete',\n ids,\n this.headers\n );\n this.handleError(response);\n return response;\n }\n}\n\nexport function createRestClient<RxDocType>(\n endpointUrl: string,\n headers: ById<string>,\n eventSource: typeof EventSource | any = EventSource\n) {\n\n return new RxRestClient<RxDocType>(\n endpointUrl,\n headers,\n eventSource\n );\n}\n\n\nexport * from './utils.ts';\n"],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export async function postRequest(url, body, headers = {}) {
|
|
2
|
+
var request = await fetch(url, {
|
|
3
|
+
method: 'POST',
|
|
4
|
+
headers: Object.assign({
|
|
5
|
+
'Accept': 'application/json',
|
|
6
|
+
'Content-Type': 'application/json'
|
|
7
|
+
}, headers),
|
|
8
|
+
body: JSON.stringify(body)
|
|
9
|
+
});
|
|
10
|
+
var response = await request.json();
|
|
11
|
+
return response;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","names":["postRequest","url","body","headers","request","fetch","method","Object","assign","JSON","stringify","response","json"],"sources":["../../../../src/plugins/client-rest/utils.ts"],"sourcesContent":["import { ById } from 'rxdb/plugins/core';\n\nexport async function postRequest(\n url: string,\n body: any,\n headers: ById<string> = {},\n) {\n const request = await fetch(url, {\n method: 'POST',\n headers: Object.assign({\n 'Accept': 'application/json',\n 'Content-Type': 'application/json'\n }, headers),\n body: JSON.stringify(body)\n });\n const response = await request.json();\n return response;\n}\n"],"mappings":"AAEA,OAAO,eAAeA,WAAWA,CAC7BC,GAAW,EACXC,IAAS,EACTC,OAAqB,GAAG,CAAC,CAAC,EAC5B;EACE,IAAMC,OAAO,GAAG,MAAMC,KAAK,CAACJ,GAAG,EAAE;IAC7BK,MAAM,EAAE,MAAM;IACdH,OAAO,EAAEI,MAAM,CAACC,MAAM,CAAC;MACnB,QAAQ,EAAE,kBAAkB;MAC5B,cAAc,EAAE;IACpB,CAAC,EAAEL,OAAO,CAAC;IACXD,IAAI,EAAEO,IAAI,CAACC,SAAS,CAACR,IAAI;EAC7B,CAAC,CAAC;EACF,IAAMS,QAAQ,GAAG,MAAMP,OAAO,CAACQ,IAAI,CAAC,CAAC;EACrC,OAAOD,QAAQ;AACnB"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { newRxError, nextTick } from 'rxdb/plugins/core';
|
|
2
|
+
export async function parseResponse(replicationState, fetchResponse) {
|
|
3
|
+
if (fetchResponse.status === 426) {
|
|
4
|
+
replicationState.outdatedClient$.next();
|
|
5
|
+
nextTick().then(() => replicationState.cancel());
|
|
6
|
+
throw newRxError('RC_OUTDATED', {
|
|
7
|
+
url: fetchResponse.url
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
if (fetchResponse.status === 401) {
|
|
11
|
+
replicationState.unauthorized$.next();
|
|
12
|
+
throw newRxError('RC_UNAUTHORIZED', {
|
|
13
|
+
url: fetchResponse.url
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
if (fetchResponse.status === 403) {
|
|
17
|
+
replicationState.forbidden$.next();
|
|
18
|
+
nextTick().then(() => replicationState.cancel());
|
|
19
|
+
throw newRxError('RC_FORBIDDEN', {
|
|
20
|
+
url: fetchResponse.url
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
var data = await fetchResponse.json();
|
|
24
|
+
if (data.error) {
|
|
25
|
+
// TODO
|
|
26
|
+
console.log('TODO handle parseResponse error');
|
|
27
|
+
console.dir(data);
|
|
28
|
+
throw data;
|
|
29
|
+
}
|
|
30
|
+
return data;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=helpers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"helpers.js","names":["newRxError","nextTick","parseResponse","replicationState","fetchResponse","status","outdatedClient$","next","then","cancel","url","unauthorized$","forbidden$","data","json","error","console","log","dir"],"sources":["../../../../src/plugins/replication-server/helpers.ts"],"sourcesContent":["import { newRxError, nextTick } from 'rxdb/plugins/core';\nimport type { RxServerReplicationState } from './index.ts';\n\nexport async function parseResponse(\n replicationState: RxServerReplicationState<any>,\n fetchResponse: Response\n) {\n if (fetchResponse.status === 426) {\n replicationState.outdatedClient$.next();\n nextTick().then(() => replicationState.cancel());\n throw newRxError('RC_OUTDATED', {\n url: fetchResponse.url\n });\n }\n if (fetchResponse.status === 401) {\n replicationState.unauthorized$.next();\n throw newRxError('RC_UNAUTHORIZED', {\n url: fetchResponse.url\n });\n }\n if (fetchResponse.status === 403) {\n replicationState.forbidden$.next();\n nextTick().then(() => replicationState.cancel());\n throw newRxError('RC_FORBIDDEN', {\n url: fetchResponse.url\n });\n }\n const data = await fetchResponse.json();\n\n if (data.error) {\n // TODO\n console.log('TODO handle parseResponse error');\n console.dir(data);\n throw data;\n }\n\n return data;\n}\n"],"mappings":"AAAA,SAASA,UAAU,EAAEC,QAAQ,QAAQ,mBAAmB;AAGxD,OAAO,eAAeC,aAAaA,CAC/BC,gBAA+C,EAC/CC,aAAuB,EACzB;EACE,IAAIA,aAAa,CAACC,MAAM,KAAK,GAAG,EAAE;IAC9BF,gBAAgB,CAACG,eAAe,CAACC,IAAI,CAAC,CAAC;IACvCN,QAAQ,CAAC,CAAC,CAACO,IAAI,CAAC,MAAML,gBAAgB,CAACM,MAAM,CAAC,CAAC,CAAC;IAChD,MAAMT,UAAU,CAAC,aAAa,EAAE;MAC5BU,GAAG,EAAEN,aAAa,CAACM;IACvB,CAAC,CAAC;EACN;EACA,IAAIN,aAAa,CAACC,MAAM,KAAK,GAAG,EAAE;IAC9BF,gBAAgB,CAACQ,aAAa,CAACJ,IAAI,CAAC,CAAC;IACrC,MAAMP,UAAU,CAAC,iBAAiB,EAAE;MAChCU,GAAG,EAAEN,aAAa,CAACM;IACvB,CAAC,CAAC;EACN;EACA,IAAIN,aAAa,CAACC,MAAM,KAAK,GAAG,EAAE;IAC9BF,gBAAgB,CAACS,UAAU,CAACL,IAAI,CAAC,CAAC;IAClCN,QAAQ,CAAC,CAAC,CAACO,IAAI,CAAC,MAAML,gBAAgB,CAACM,MAAM,CAAC,CAAC,CAAC;IAChD,MAAMT,UAAU,CAAC,cAAc,EAAE;MAC7BU,GAAG,EAAEN,aAAa,CAACM;IACvB,CAAC,CAAC;EACN;EACA,IAAMG,IAAI,GAAG,MAAMT,aAAa,CAACU,IAAI,CAAC,CAAC;EAEvC,IAAID,IAAI,CAACE,KAAK,EAAE;IACZ;IACAC,OAAO,CAACC,GAAG,CAAC,iCAAiC,CAAC;IAC9CD,OAAO,CAACE,GAAG,CAACL,IAAI,CAAC;IACjB,MAAMA,IAAI;EACd;EAEA,OAAOA,IAAI;AACf"}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import _inheritsLoose from "@babel/runtime/helpers/inheritsLoose";
|
|
2
|
+
import { ensureNotFalsy, flatClone, promiseWait, addRxPlugin, newRxError } from 'rxdb/plugins/core';
|
|
3
|
+
import { RxDBLeaderElectionPlugin } from 'rxdb/plugins/leader-election';
|
|
4
|
+
import { RxReplicationState, startReplicationOnLeaderShip } from 'rxdb/plugins/replication';
|
|
5
|
+
import { Subject } from 'rxjs';
|
|
6
|
+
import { parseResponse } from "./helpers.js";
|
|
7
|
+
import EventSource from 'eventsource';
|
|
8
|
+
export * from "./types.js";
|
|
9
|
+
export var RxServerReplicationState = /*#__PURE__*/function (_RxReplicationState) {
|
|
10
|
+
_inheritsLoose(RxServerReplicationState, _RxReplicationState);
|
|
11
|
+
function RxServerReplicationState(replicationIdentifier, collection, pull, push, live = true, retryTime = 1000 * 5, autoStart = true, headers = {}) {
|
|
12
|
+
var _this;
|
|
13
|
+
_this = _RxReplicationState.call(this, replicationIdentifier, collection, '_deleted', pull, push, live, retryTime, autoStart) || this;
|
|
14
|
+
_this.outdatedClient$ = new Subject();
|
|
15
|
+
_this.unauthorized$ = new Subject();
|
|
16
|
+
_this.forbidden$ = new Subject();
|
|
17
|
+
_this.replicationIdentifier = replicationIdentifier;
|
|
18
|
+
_this.collection = collection;
|
|
19
|
+
_this.pull = pull;
|
|
20
|
+
_this.push = push;
|
|
21
|
+
_this.live = live;
|
|
22
|
+
_this.retryTime = retryTime;
|
|
23
|
+
_this.autoStart = autoStart;
|
|
24
|
+
_this.headers = headers;
|
|
25
|
+
_this.onCancel.push(() => {
|
|
26
|
+
_this.outdatedClient$.complete();
|
|
27
|
+
_this.unauthorized$.complete();
|
|
28
|
+
_this.forbidden$.complete();
|
|
29
|
+
});
|
|
30
|
+
return _this;
|
|
31
|
+
}
|
|
32
|
+
var _proto = RxServerReplicationState.prototype;
|
|
33
|
+
_proto.setHeaders = function setHeaders(headers) {
|
|
34
|
+
this.headers = flatClone(headers);
|
|
35
|
+
};
|
|
36
|
+
return RxServerReplicationState;
|
|
37
|
+
}(RxReplicationState);
|
|
38
|
+
export function replicateServer(options) {
|
|
39
|
+
if (!options.pull && !options.push) {
|
|
40
|
+
throw newRxError('UT3', {
|
|
41
|
+
collection: options.collection.name,
|
|
42
|
+
args: {
|
|
43
|
+
replicationIdentifier: options.replicationIdentifier
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
options.live = typeof options.live === 'undefined' ? true : options.live;
|
|
48
|
+
options.waitForLeadership = typeof options.waitForLeadership === 'undefined' ? true : options.waitForLeadership;
|
|
49
|
+
var collection = options.collection;
|
|
50
|
+
addRxPlugin(RxDBLeaderElectionPlugin);
|
|
51
|
+
var pullStream$ = new Subject();
|
|
52
|
+
var replicationPrimitivesPull;
|
|
53
|
+
if (options.pull) {
|
|
54
|
+
replicationPrimitivesPull = {
|
|
55
|
+
async handler(checkpointOrNull, batchSize) {
|
|
56
|
+
var lwt = checkpointOrNull && checkpointOrNull.lwt ? checkpointOrNull.lwt : 0;
|
|
57
|
+
var id = checkpointOrNull && checkpointOrNull.id ? checkpointOrNull.id : '';
|
|
58
|
+
var url = options.url + ("/pull?lwt=" + lwt + "&id=" + id + "&limit=" + batchSize);
|
|
59
|
+
var response = await fetch(url, {
|
|
60
|
+
method: 'GET',
|
|
61
|
+
headers: Object.assign({
|
|
62
|
+
'Accept': 'application/json',
|
|
63
|
+
'Content-Type': 'application/json'
|
|
64
|
+
}, replicationState.headers)
|
|
65
|
+
});
|
|
66
|
+
var data = await await parseResponse(replicationState, response);
|
|
67
|
+
return {
|
|
68
|
+
documents: data.documents,
|
|
69
|
+
checkpoint: data.checkpoint
|
|
70
|
+
};
|
|
71
|
+
},
|
|
72
|
+
batchSize: ensureNotFalsy(options.pull).batchSize,
|
|
73
|
+
modifier: ensureNotFalsy(options.pull).modifier,
|
|
74
|
+
stream$: pullStream$.asObservable()
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
var replicationPrimitivesPush;
|
|
78
|
+
if (options.push) {
|
|
79
|
+
replicationPrimitivesPush = {
|
|
80
|
+
async handler(changeRows) {
|
|
81
|
+
var response = await fetch(options.url + '/push', {
|
|
82
|
+
method: 'POST',
|
|
83
|
+
headers: Object.assign({
|
|
84
|
+
'Accept': 'application/json',
|
|
85
|
+
'Content-Type': 'application/json'
|
|
86
|
+
}, replicationState.headers),
|
|
87
|
+
body: JSON.stringify(changeRows)
|
|
88
|
+
});
|
|
89
|
+
var conflictsArray = await parseResponse(replicationState, response);
|
|
90
|
+
return conflictsArray;
|
|
91
|
+
},
|
|
92
|
+
batchSize: options.push.batchSize,
|
|
93
|
+
modifier: options.push.modifier
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
var replicationState = new RxServerReplicationState(options.replicationIdentifier, collection, replicationPrimitivesPull, replicationPrimitivesPush, options.live, options.retryTime, options.autoStart, options.headers);
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Use long polling to get live changes for the pull.stream$
|
|
100
|
+
*/
|
|
101
|
+
if (options.live && options.pull) {
|
|
102
|
+
var startBefore = replicationState.start.bind(replicationState);
|
|
103
|
+
replicationState.start = async () => {
|
|
104
|
+
var useEventSource = options.eventSource ? options.eventSource : EventSource;
|
|
105
|
+
var eventSource;
|
|
106
|
+
var refreshEventSource = () => {
|
|
107
|
+
eventSource = new useEventSource(options.url + '/pullStream', {
|
|
108
|
+
withCredentials: true,
|
|
109
|
+
/**
|
|
110
|
+
* Sending headers is not supported by the Browser EventSource API,
|
|
111
|
+
* only by the npm module we use. In react-native you might have
|
|
112
|
+
* to set another EventSource implementation.
|
|
113
|
+
* @link https://www.npmjs.com/package/eventsource
|
|
114
|
+
*/
|
|
115
|
+
headers: replicationState.headers
|
|
116
|
+
});
|
|
117
|
+
// TODO check for 426 errors and handle them
|
|
118
|
+
eventSource.onerror = err => {
|
|
119
|
+
if (err.status === 401) {
|
|
120
|
+
replicationState.unauthorized$.next();
|
|
121
|
+
eventSource.close();
|
|
122
|
+
promiseWait(replicationState.retryTime).then(() => refreshEventSource());
|
|
123
|
+
} else {
|
|
124
|
+
pullStream$.next('RESYNC');
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
eventSource.onopen = x => {
|
|
128
|
+
pullStream$.next('RESYNC');
|
|
129
|
+
};
|
|
130
|
+
eventSource.onmessage = event => {
|
|
131
|
+
var eventData = JSON.parse(event.data);
|
|
132
|
+
pullStream$.next({
|
|
133
|
+
documents: eventData.documents,
|
|
134
|
+
checkpoint: eventData.checkpoint
|
|
135
|
+
});
|
|
136
|
+
};
|
|
137
|
+
};
|
|
138
|
+
refreshEventSource();
|
|
139
|
+
replicationState.onCancel.push(() => eventSource && eventSource.close());
|
|
140
|
+
return startBefore();
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
startReplicationOnLeaderShip(options.waitForLeadership, replicationState);
|
|
144
|
+
return replicationState;
|
|
145
|
+
}
|
|
146
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":["ensureNotFalsy","flatClone","promiseWait","addRxPlugin","newRxError","RxDBLeaderElectionPlugin","RxReplicationState","startReplicationOnLeaderShip","Subject","parseResponse","EventSource","RxServerReplicationState","_RxReplicationState","_inheritsLoose","replicationIdentifier","collection","pull","push","live","retryTime","autoStart","headers","_this","call","outdatedClient$","unauthorized$","forbidden$","onCancel","complete","_proto","prototype","setHeaders","replicateServer","options","name","args","waitForLeadership","pullStream$","replicationPrimitivesPull","handler","checkpointOrNull","batchSize","lwt","id","url","response","fetch","method","Object","assign","replicationState","data","documents","checkpoint","modifier","stream$","asObservable","replicationPrimitivesPush","changeRows","body","JSON","stringify","conflictsArray","startBefore","start","bind","useEventSource","eventSource","refreshEventSource","withCredentials","onerror","err","status","next","close","then","onopen","x","onmessage","event","eventData","parse"],"sources":["../../../../src/plugins/replication-server/index.ts"],"sourcesContent":["import {\n ensureNotFalsy,\n flatClone,\n promiseWait,\n RxCollection,\n ReplicationPullOptions,\n ReplicationPushOptions,\n RxReplicationPullStreamItem,\n RxStorageDefaultCheckpoint,\n ById,\n addRxPlugin, \n newRxError\n} from 'rxdb/plugins/core';\nimport { RxDBLeaderElectionPlugin } from 'rxdb/plugins/leader-election';\nimport {\n RxReplicationState,\n startReplicationOnLeaderShip\n} from 'rxdb/plugins/replication';\n\nimport { Subject } from 'rxjs';\nimport { ServerSyncOptions } from './types.ts';\nimport { parseResponse } from './helpers.ts';\nimport EventSource from 'eventsource';\n\nexport * from './types.ts';\n\nexport class RxServerReplicationState<RxDocType> extends RxReplicationState<RxDocType, RxStorageDefaultCheckpoint> {\n public readonly outdatedClient$ = new Subject<void>();\n public readonly unauthorized$ = new Subject<void>();\n public readonly forbidden$ = new Subject<void>();\n\n constructor(\n public readonly replicationIdentifier: string,\n public readonly collection: RxCollection<RxDocType>,\n public readonly pull?: ReplicationPullOptions<RxDocType, RxStorageDefaultCheckpoint>,\n public readonly push?: ReplicationPushOptions<RxDocType>,\n public readonly live: boolean = true,\n public retryTime: number = 1000 * 5,\n public autoStart: boolean = true,\n public headers: ById<string> = {}\n ) {\n super(\n replicationIdentifier,\n collection,\n '_deleted',\n pull,\n push,\n live,\n retryTime,\n autoStart\n );\n\n this.onCancel.push(() => {\n this.outdatedClient$.complete();\n this.unauthorized$.complete();\n this.forbidden$.complete();\n });\n }\n\n setHeaders(headers: ById<string>): void {\n this.headers = flatClone(headers);\n }\n}\n\nexport function replicateServer<RxDocType>(\n options: ServerSyncOptions<RxDocType>\n): RxServerReplicationState<RxDocType> {\n\n if (!options.pull && !options.push) {\n throw newRxError('UT3', {\n collection: options.collection.name,\n args: {\n replicationIdentifier: options.replicationIdentifier\n }\n });\n }\n\n options.live = typeof options.live === 'undefined' ? true : options.live;\n options.waitForLeadership = typeof options.waitForLeadership === 'undefined' ? true : options.waitForLeadership;\n\n const collection = options.collection;\n addRxPlugin(RxDBLeaderElectionPlugin);\n\n const pullStream$: Subject<RxReplicationPullStreamItem<RxDocType, RxStorageDefaultCheckpoint>> = new Subject();\n\n let replicationPrimitivesPull: ReplicationPullOptions<RxDocType, RxStorageDefaultCheckpoint> | undefined;\n if (options.pull) {\n replicationPrimitivesPull = {\n async handler(checkpointOrNull, batchSize) {\n const lwt = checkpointOrNull && checkpointOrNull.lwt ? checkpointOrNull.lwt : 0;\n const id = checkpointOrNull && checkpointOrNull.id ? checkpointOrNull.id : '';\n const url = options.url + `/pull?lwt=${lwt}&id=${id}&limit=${batchSize}`;\n const response = await fetch(url, {\n method: 'GET',\n headers: Object.assign({\n 'Accept': 'application/json',\n 'Content-Type': 'application/json'\n }, replicationState.headers),\n });\n const data = await await parseResponse(replicationState, response);\n return {\n documents: data.documents,\n checkpoint: data.checkpoint\n };\n },\n batchSize: ensureNotFalsy(options.pull).batchSize,\n modifier: ensureNotFalsy(options.pull).modifier,\n stream$: pullStream$.asObservable()\n };\n }\n\n let replicationPrimitivesPush: ReplicationPushOptions<RxDocType> | undefined;\n if (options.push) {\n replicationPrimitivesPush = {\n async handler(changeRows) {\n const response = await fetch(options.url + '/push', {\n method: 'POST',\n headers: Object.assign({\n 'Accept': 'application/json',\n 'Content-Type': 'application/json'\n }, replicationState.headers),\n body: JSON.stringify(changeRows)\n });\n const conflictsArray = await parseResponse(replicationState, response);\n return conflictsArray;\n },\n batchSize: options.push.batchSize,\n modifier: options.push.modifier\n };\n }\n\n const replicationState = new RxServerReplicationState<RxDocType>(\n options.replicationIdentifier,\n collection,\n replicationPrimitivesPull,\n replicationPrimitivesPush,\n options.live,\n options.retryTime,\n options.autoStart,\n options.headers\n );\n\n /**\n * Use long polling to get live changes for the pull.stream$\n */\n if (options.live && options.pull) {\n const startBefore = replicationState.start.bind(replicationState);\n replicationState.start = async () => {\n const useEventSource: typeof EventSource = options.eventSource ? options.eventSource : EventSource;\n let eventSource: EventSource;\n const refreshEventSource = () => {\n eventSource = new useEventSource(options.url + '/pullStream', {\n withCredentials: true,\n /**\n * Sending headers is not supported by the Browser EventSource API,\n * only by the npm module we use. In react-native you might have\n * to set another EventSource implementation.\n * @link https://www.npmjs.com/package/eventsource\n */\n headers: replicationState.headers\n });\n // TODO check for 426 errors and handle them\n eventSource.onerror = (err) => {\n if (err.status === 401) {\n replicationState.unauthorized$.next();\n eventSource.close();\n promiseWait(replicationState.retryTime).then(() => refreshEventSource());\n } else {\n pullStream$.next('RESYNC');\n }\n };\n eventSource.onopen = (x) => {\n pullStream$.next('RESYNC');\n }\n eventSource.onmessage = event => {\n const eventData = JSON.parse(event.data);\n pullStream$.next({\n documents: eventData.documents,\n checkpoint: eventData.checkpoint\n });\n };\n }\n refreshEventSource();\n\n replicationState.onCancel.push(() => eventSource && eventSource.close());\n return startBefore();\n };\n }\n\n startReplicationOnLeaderShip(options.waitForLeadership, replicationState);\n\n return replicationState;\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","names":[],"sources":["../../../../src/plugins/replication-server/types.ts"],"sourcesContent":["import type {\n MaybePromise,\n ReplicationOptions,\n ReplicationPullOptions,\n ReplicationPushOptions,\n RxStorageDefaultCheckpoint\n} from 'rxdb/plugins/core';\n\nexport type ServerSyncPullOptions<RxDocType> =\n Omit<ReplicationPullOptions<RxDocType, RxStorageDefaultCheckpoint>, 'handler' | 'stream$'>\n & {\n };\n\nexport type ServerSyncPushOptions<RxDocType> = Omit<ReplicationPushOptions<RxDocType>, 'handler'>\n & {\n};\n\nexport type ServerSyncOptions<RxDocType> = Omit<\n ReplicationOptions<RxDocType, any>,\n 'pull' | 'push'\n> & {\n url: string;\n headers?: { [k: string]: string };\n pull?: ServerSyncPullOptions<RxDocType>;\n push?: ServerSyncPushOptions<RxDocType>;\n\n /**\n * If the EventSource API is not available\n * on the runtime, pass an own implementation here.\n * Mostly used with the \"eventsource\" npm package on Node.js.\n */\n eventSource?: typeof EventSource | any\n};\n"],"mappings":""}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { prepareQuery, getChangedDocumentsSinceQuery } from 'rxdb/plugins/core';
|
|
2
|
+
import { getReplicationHandlerByCollection } from 'rxdb/plugins/replication-websocket';
|
|
3
|
+
import { filter, mergeMap } from 'rxjs';
|
|
4
|
+
import { ensureNotFalsy, getFromMapOrThrow, lastOfArray } from 'rxdb/plugins/utils';
|
|
5
|
+
import { addAuthMiddleware, blockPreviousVersionPaths, closeConnection, docContainsServerOnlyFields, doesContainRegexQuerySelector, getDocAllowedMatcher, mergeServerDocumentFieldsMonad, removeServerOnlyFieldsMonad, setCors, writeSSEHeaders } from "./helper.js";
|
|
6
|
+
export var RxServerReplicationEndpoint = function RxServerReplicationEndpoint(server, name, collection, queryModifier, changeValidator, serverOnlyFields, cors) {
|
|
7
|
+
this.type = 'replication';
|
|
8
|
+
this.server = server;
|
|
9
|
+
this.name = name;
|
|
10
|
+
this.collection = collection;
|
|
11
|
+
this.serverOnlyFields = serverOnlyFields;
|
|
12
|
+
this.cors = cors;
|
|
13
|
+
setCors(this.server, [this.name].join('/'), cors);
|
|
14
|
+
blockPreviousVersionPaths(this.server, [this.name].join('/'), collection.schema.version);
|
|
15
|
+
this.urlPath = [this.name, collection.schema.version].join('/');
|
|
16
|
+
var primaryPath = this.collection.schema.primaryPath;
|
|
17
|
+
var replicationHandler = getReplicationHandlerByCollection(this.server.database, collection.name);
|
|
18
|
+
var authDataByRequest = addAuthMiddleware(this.server, this.urlPath);
|
|
19
|
+
this.queryModifier = (authData, query) => {
|
|
20
|
+
if (doesContainRegexQuerySelector(query.selector)) {
|
|
21
|
+
throw new Error('$regex queries not allowed because of DOS-attacks');
|
|
22
|
+
}
|
|
23
|
+
return queryModifier(authData, query);
|
|
24
|
+
};
|
|
25
|
+
this.changeValidator = (authData, change) => {
|
|
26
|
+
if (change.assumedMasterState && docContainsServerOnlyFields(serverOnlyFields, change.assumedMasterState) || docContainsServerOnlyFields(serverOnlyFields, change.newDocumentState)) {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
return changeValidator(authData, change);
|
|
30
|
+
};
|
|
31
|
+
var removeServerOnlyFields = removeServerOnlyFieldsMonad(this.serverOnlyFields);
|
|
32
|
+
var mergeServerDocumentFields = mergeServerDocumentFieldsMonad(this.serverOnlyFields);
|
|
33
|
+
this.server.expressApp.get('/' + this.urlPath + '/pull', async (req, res) => {
|
|
34
|
+
var authData = getFromMapOrThrow(authDataByRequest, req);
|
|
35
|
+
var id = req.query.id ? req.query.id : '';
|
|
36
|
+
var lwt = req.query.lwt ? parseInt(req.query.lwt, 10) : 0;
|
|
37
|
+
var limit = req.query.limit ? parseInt(req.query.limit, 10) : 1;
|
|
38
|
+
var plainQuery = getChangedDocumentsSinceQuery(this.collection.storageInstance, limit, {
|
|
39
|
+
id,
|
|
40
|
+
lwt
|
|
41
|
+
});
|
|
42
|
+
var useQueryChanges = this.queryModifier(ensureNotFalsy(authData), plainQuery);
|
|
43
|
+
var prepared = prepareQuery(this.collection.schema.jsonSchema, useQueryChanges);
|
|
44
|
+
var result = await this.collection.storageInstance.query(prepared);
|
|
45
|
+
var newCheckpoint = result.documents.length === 0 ? {
|
|
46
|
+
id,
|
|
47
|
+
lwt
|
|
48
|
+
} : {
|
|
49
|
+
id: ensureNotFalsy(lastOfArray(result.documents))[primaryPath],
|
|
50
|
+
updatedAt: ensureNotFalsy(lastOfArray(result.documents))._meta.lwt
|
|
51
|
+
};
|
|
52
|
+
var responseDocuments = result.documents.map(d => removeServerOnlyFields(d));
|
|
53
|
+
res.setHeader('Content-Type', 'application/json');
|
|
54
|
+
res.json({
|
|
55
|
+
documents: responseDocuments,
|
|
56
|
+
checkpoint: newCheckpoint
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
this.server.expressApp.post('/' + this.urlPath + '/push', async (req, res) => {
|
|
60
|
+
var authData = getFromMapOrThrow(authDataByRequest, req);
|
|
61
|
+
var docDataMatcherWrite = getDocAllowedMatcher(this, ensureNotFalsy(authData));
|
|
62
|
+
var rows = req.body;
|
|
63
|
+
var ids = [];
|
|
64
|
+
rows.forEach(row => ids.push(row.newDocumentState[primaryPath]));
|
|
65
|
+
for (var row of rows) {
|
|
66
|
+
// TODO remove this check
|
|
67
|
+
if (row.assumedMasterState && row.assumedMasterState._meta) {
|
|
68
|
+
throw new Error('body document contains meta!');
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// ensure all writes are allowed
|
|
73
|
+
var nonAllowedRow = rows.find(row => {
|
|
74
|
+
if (!docDataMatcherWrite(row.newDocumentState) || row.assumedMasterState && !docDataMatcherWrite(row.assumedMasterState)) {
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
if (nonAllowedRow) {
|
|
79
|
+
closeConnection(res, 403, 'Forbidden');
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
var hasInvalidChange = false;
|
|
83
|
+
var currentStateDocsArray = await this.collection.storageInstance.findDocumentsById(ids, true);
|
|
84
|
+
var currentStateDocs = new Map();
|
|
85
|
+
currentStateDocsArray.forEach(d => currentStateDocs.set(d[primaryPath], d));
|
|
86
|
+
var useRows = rows.map(row => {
|
|
87
|
+
var id = row.newDocumentState[primaryPath];
|
|
88
|
+
var isChangeValid = this.changeValidator(ensureNotFalsy(authData), {
|
|
89
|
+
newDocumentState: removeServerOnlyFields(row.newDocumentState),
|
|
90
|
+
assumedMasterState: removeServerOnlyFields(row.assumedMasterState)
|
|
91
|
+
});
|
|
92
|
+
if (!isChangeValid) {
|
|
93
|
+
hasInvalidChange = true;
|
|
94
|
+
}
|
|
95
|
+
var serverDoc = currentStateDocs.get(id);
|
|
96
|
+
return {
|
|
97
|
+
newDocumentState: mergeServerDocumentFields(row.newDocumentState, serverDoc),
|
|
98
|
+
assumedMasterState: mergeServerDocumentFields(row.assumedMasterState, serverDoc)
|
|
99
|
+
};
|
|
100
|
+
});
|
|
101
|
+
if (hasInvalidChange) {
|
|
102
|
+
closeConnection(res, 403, 'Forbidden');
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
var conflicts = await replicationHandler.masterWrite(useRows);
|
|
106
|
+
res.setHeader('Content-Type', 'application/json');
|
|
107
|
+
res.json(conflicts);
|
|
108
|
+
});
|
|
109
|
+
this.server.expressApp.get('/' + this.urlPath + '/pullStream', async (req, res) => {
|
|
110
|
+
writeSSEHeaders(res);
|
|
111
|
+
var authData = getFromMapOrThrow(authDataByRequest, req);
|
|
112
|
+
var docDataMatcherStream = getDocAllowedMatcher(this, ensureNotFalsy(authData));
|
|
113
|
+
var subscription = replicationHandler.masterChangeStream$.pipe(mergeMap(async changes => {
|
|
114
|
+
/**
|
|
115
|
+
* The auth-data might be expired
|
|
116
|
+
* so we re-run the auth parsing each time
|
|
117
|
+
* before emitting an event.
|
|
118
|
+
*/
|
|
119
|
+
var authData;
|
|
120
|
+
try {
|
|
121
|
+
authData = await server.authHandler(req.headers);
|
|
122
|
+
} catch (err) {
|
|
123
|
+
closeConnection(res, 401, 'Unauthorized');
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
if (changes === 'RESYNC') {
|
|
127
|
+
return changes;
|
|
128
|
+
} else {
|
|
129
|
+
var useDocs = changes.documents.filter(d => docDataMatcherStream(d));
|
|
130
|
+
return {
|
|
131
|
+
documents: useDocs,
|
|
132
|
+
checkpoint: changes.checkpoint
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
}), filter(f => f !== null && (f === 'RESYNC' || f.documents.length > 0))).subscribe(filteredAndModified => {
|
|
136
|
+
if (filteredAndModified === 'RESYNC') {
|
|
137
|
+
res.write('data: ' + JSON.stringify(filteredAndModified) + '\n\n');
|
|
138
|
+
} else {
|
|
139
|
+
var responseDocuments = ensureNotFalsy(filteredAndModified).documents.map(d => removeServerOnlyFields(d));
|
|
140
|
+
res.write('data: ' + JSON.stringify({
|
|
141
|
+
documents: responseDocuments,
|
|
142
|
+
checkpoint: ensureNotFalsy(filteredAndModified).checkpoint
|
|
143
|
+
}) + '\n\n');
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* @link https://youtu.be/0PcMuYGJPzM?si=AxkczxcMaUwhh8k9&t=363
|
|
149
|
+
*/
|
|
150
|
+
req.on('close', () => {
|
|
151
|
+
subscription.unsubscribe();
|
|
152
|
+
res.end();
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
};
|
|
156
|
+
//# sourceMappingURL=endpoint-replication.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"endpoint-replication.js","names":["prepareQuery","getChangedDocumentsSinceQuery","getReplicationHandlerByCollection","filter","mergeMap","ensureNotFalsy","getFromMapOrThrow","lastOfArray","addAuthMiddleware","blockPreviousVersionPaths","closeConnection","docContainsServerOnlyFields","doesContainRegexQuerySelector","getDocAllowedMatcher","mergeServerDocumentFieldsMonad","removeServerOnlyFieldsMonad","setCors","writeSSEHeaders","RxServerReplicationEndpoint","server","name","collection","queryModifier","changeValidator","serverOnlyFields","cors","type","join","schema","version","urlPath","primaryPath","replicationHandler","database","authDataByRequest","authData","query","selector","Error","change","assumedMasterState","newDocumentState","removeServerOnlyFields","mergeServerDocumentFields","expressApp","get","req","res","id","lwt","parseInt","limit","plainQuery","storageInstance","useQueryChanges","prepared","jsonSchema","result","newCheckpoint","documents","length","updatedAt","_meta","responseDocuments","map","d","setHeader","json","checkpoint","post","docDataMatcherWrite","rows","body","ids","forEach","row","push","nonAllowedRow","find","hasInvalidChange","currentStateDocsArray","findDocumentsById","currentStateDocs","Map","set","useRows","isChangeValid","serverDoc","conflicts","masterWrite","docDataMatcherStream","subscription","masterChangeStream$","pipe","changes","authHandler","headers","err","useDocs","f","subscribe","filteredAndModified","write","JSON","stringify","on","unsubscribe","end"],"sources":["../../../../src/plugins/server/endpoint-replication.ts"],"sourcesContent":["import {\n FilledMangoQuery,\n RxCollection,\n RxReplicationHandler,\n RxReplicationWriteToMasterRow,\n RxStorageDefaultCheckpoint,\n StringKeys,\n prepareQuery,\n getChangedDocumentsSinceQuery,\n RxDocumentData\n} from 'rxdb/plugins/core';\nimport { getReplicationHandlerByCollection } from 'rxdb/plugins/replication-websocket';\nimport type { RxServer } from './rx-server.ts';\nimport type {\n RxServerAuthData,\n RxServerChangeValidator,\n RxServerEndpoint,\n RxServerQueryModifier\n} from './types.ts';\nimport { filter, mergeMap } from 'rxjs';\nimport {\n ensureNotFalsy,\n getFromMapOrThrow,\n lastOfArray\n} from 'rxdb/plugins/utils';\n\nimport {\n addAuthMiddleware,\n blockPreviousVersionPaths,\n closeConnection,\n docContainsServerOnlyFields,\n doesContainRegexQuerySelector,\n getDocAllowedMatcher,\n mergeServerDocumentFieldsMonad,\n removeServerOnlyFieldsMonad,\n setCors,\n writeSSEHeaders\n} from './helper.ts';\n\nexport type RxReplicationEndpointMessageType = {\n id: string;\n method: StringKeys<RxReplicationHandler<any, any>> | 'auth';\n params: any[];\n};\n\nexport class RxServerReplicationEndpoint<AuthType, RxDocType> implements RxServerEndpoint<AuthType, RxDocType> {\n readonly type = 'replication';\n readonly urlPath: string;\n readonly changeValidator: RxServerChangeValidator<AuthType, RxDocType>;\n readonly queryModifier: RxServerQueryModifier<AuthType, RxDocType>;\n constructor(\n public readonly server: RxServer<AuthType>,\n public readonly name: string,\n public readonly collection: RxCollection<RxDocType>,\n queryModifier: RxServerQueryModifier<AuthType, RxDocType>,\n changeValidator: RxServerChangeValidator<AuthType, RxDocType>,\n public readonly serverOnlyFields: string[],\n public readonly cors?: string,\n ) {\n setCors(this.server, [this.name].join('/'), cors);\n blockPreviousVersionPaths(this.server, [this.name].join('/'), collection.schema.version);\n\n this.urlPath = [this.name, collection.schema.version].join('/');\n\n const primaryPath = this.collection.schema.primaryPath;\n const replicationHandler = getReplicationHandlerByCollection(this.server.database, collection.name);\n const authDataByRequest = addAuthMiddleware(\n this.server,\n this.urlPath\n );\n\n this.queryModifier = (authData, query) => {\n if (doesContainRegexQuerySelector(query.selector)) {\n throw new Error('$regex queries not allowed because of DOS-attacks');\n }\n return queryModifier(authData, query);\n }\n this.changeValidator = (authData, change) => {\n if (\n (change.assumedMasterState && docContainsServerOnlyFields(serverOnlyFields, change.assumedMasterState)) ||\n docContainsServerOnlyFields(serverOnlyFields, change.newDocumentState)\n ) {\n return false;\n }\n return changeValidator(authData, change);\n }\n const removeServerOnlyFields = removeServerOnlyFieldsMonad<RxDocType>(this.serverOnlyFields);\n const mergeServerDocumentFields = mergeServerDocumentFieldsMonad<RxDocType>(this.serverOnlyFields);\n\n this.server.expressApp.get('/' + this.urlPath + '/pull', async (req, res) => {\n const authData = getFromMapOrThrow(authDataByRequest, req);\n const id = req.query.id ? req.query.id as string : '';\n const lwt = req.query.lwt ? parseInt(req.query.lwt as any, 10) : 0;\n const limit = req.query.limit ? parseInt(req.query.limit as any, 10) : 1;\n const plainQuery = getChangedDocumentsSinceQuery<RxDocType, RxStorageDefaultCheckpoint>(\n this.collection.storageInstance,\n limit,\n { id, lwt }\n );\n const useQueryChanges: FilledMangoQuery<RxDocType> = this.queryModifier(\n ensureNotFalsy(authData),\n plainQuery\n );\n const prepared = prepareQuery<RxDocType>(\n this.collection.schema.jsonSchema,\n useQueryChanges\n );\n const result = await this.collection.storageInstance.query(prepared);\n\n const newCheckpoint = result.documents.length === 0 ? { id, lwt } : {\n id: ensureNotFalsy(lastOfArray(result.documents))[primaryPath],\n updatedAt: ensureNotFalsy(lastOfArray(result.documents))._meta.lwt\n };\n const responseDocuments = result.documents.map(d => removeServerOnlyFields(d));\n res.setHeader('Content-Type', 'application/json');\n res.json({\n documents: responseDocuments,\n checkpoint: newCheckpoint\n });\n });\n this.server.expressApp.post('/' + this.urlPath + '/push', async (req, res) => {\n const authData = getFromMapOrThrow(authDataByRequest, req);\n const docDataMatcherWrite = getDocAllowedMatcher(this, ensureNotFalsy(authData));\n const rows: RxReplicationWriteToMasterRow<RxDocType>[] = req.body;\n const ids: string[] = [];\n rows.forEach(row => ids.push((row.newDocumentState as any)[primaryPath]));\n\n for (const row of rows) {\n // TODO remove this check\n if (row.assumedMasterState && (row.assumedMasterState as any)._meta) {\n throw new Error('body document contains meta!');\n }\n }\n\n // ensure all writes are allowed\n const nonAllowedRow = rows.find(row => {\n if (\n !docDataMatcherWrite(row.newDocumentState as any) ||\n (row.assumedMasterState && !docDataMatcherWrite(row.assumedMasterState as any))\n ) {\n return true;\n }\n });\n if (nonAllowedRow) {\n closeConnection(res, 403, 'Forbidden');\n return;\n }\n let hasInvalidChange = false;\n\n const currentStateDocsArray = await this.collection.storageInstance.findDocumentsById(ids, true);\n const currentStateDocs = new Map<string, RxDocumentData<RxDocType>>();\n currentStateDocsArray.forEach(d => currentStateDocs.set((d as any)[primaryPath], d));\n\n const useRows: typeof rows = rows.map((row) => {\n const id = (row.newDocumentState as any)[primaryPath];\n const isChangeValid = this.changeValidator(ensureNotFalsy(authData), {\n newDocumentState: removeServerOnlyFields(row.newDocumentState),\n assumedMasterState: removeServerOnlyFields(row.assumedMasterState)\n });\n if (!isChangeValid) {\n hasInvalidChange = true;\n }\n\n const serverDoc = currentStateDocs.get(id);\n return {\n newDocumentState: mergeServerDocumentFields(row.newDocumentState, serverDoc),\n assumedMasterState: mergeServerDocumentFields(row.assumedMasterState as any, serverDoc)\n } as typeof row;\n });\n if (hasInvalidChange) {\n closeConnection(res, 403, 'Forbidden');\n return;\n }\n\n const conflicts = await replicationHandler.masterWrite(useRows);\n\n res.setHeader('Content-Type', 'application/json');\n res.json(conflicts);\n });\n this.server.expressApp.get('/' + this.urlPath + '/pullStream', async (req, res) => {\n writeSSEHeaders(res);\n\n const authData = getFromMapOrThrow(authDataByRequest, req);\n const docDataMatcherStream = getDocAllowedMatcher(this, ensureNotFalsy(authData));\n const subscription = replicationHandler.masterChangeStream$.pipe(\n mergeMap(async (changes) => {\n /**\n * The auth-data might be expired\n * so we re-run the auth parsing each time\n * before emitting an event.\n */\n let authData: RxServerAuthData<AuthType>;\n try {\n authData = await server.authHandler(req.headers);\n } catch (err) {\n closeConnection(res, 401, 'Unauthorized');\n return null;\n }\n\n if (changes === 'RESYNC') {\n return changes;\n } else {\n const useDocs = changes.documents.filter(d => docDataMatcherStream(d as any));\n return {\n documents: useDocs,\n checkpoint: changes.checkpoint\n };\n }\n }),\n filter(f => f !== null && (f === 'RESYNC' || f.documents.length > 0))\n ).subscribe(filteredAndModified => {\n if (filteredAndModified === 'RESYNC') {\n res.write('data: ' + JSON.stringify(filteredAndModified) + '\\n\\n');\n } else {\n const responseDocuments = ensureNotFalsy(filteredAndModified).documents.map(d => removeServerOnlyFields(d as any));\n res.write('data: ' + JSON.stringify({ documents: responseDocuments, checkpoint: ensureNotFalsy(filteredAndModified).checkpoint }) + '\\n\\n');\n }\n\n });\n\n /**\n * @link https://youtu.be/0PcMuYGJPzM?si=AxkczxcMaUwhh8k9&t=363\n */\n req.on('close', () => {\n subscription.unsubscribe();\n res.end();\n });\n });\n }\n}\n"],"mappings":"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"}
|