@powersync/service-core 0.15.0 → 0.16.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +27 -0
- package/dist/auth/RemoteJWKSCollector.d.ts +9 -7
- package/dist/auth/RemoteJWKSCollector.js +16 -29
- package/dist/auth/RemoteJWKSCollector.js.map +1 -1
- package/dist/entry/cli-entry.js +2 -0
- package/dist/entry/cli-entry.js.map +1 -1
- package/dist/entry/commands/teardown-action.js +2 -1
- package/dist/entry/commands/teardown-action.js.map +1 -1
- package/dist/entry/commands/test-connection-action.d.ts +2 -0
- package/dist/entry/commands/test-connection-action.js +32 -0
- package/dist/entry/commands/test-connection-action.js.map +1 -0
- package/dist/metrics/Metrics.js +2 -2
- package/dist/metrics/Metrics.js.map +1 -1
- package/dist/replication/AbstractReplicator.d.ts +2 -0
- package/dist/replication/AbstractReplicator.js.map +1 -1
- package/dist/replication/ReplicationEngine.d.ts +2 -0
- package/dist/replication/ReplicationEngine.js +3 -0
- package/dist/replication/ReplicationEngine.js.map +1 -1
- package/dist/replication/ReplicationModule.d.ts +8 -2
- package/dist/replication/ReplicationModule.js +6 -11
- package/dist/replication/ReplicationModule.js.map +1 -1
- package/dist/routes/endpoints/admin.js +3 -3
- package/dist/routes/endpoints/admin.js.map +1 -1
- package/dist/routes/endpoints/socket-route.js +5 -5
- package/dist/routes/endpoints/socket-route.js.map +1 -1
- package/dist/routes/endpoints/sync-rules.js +9 -9
- package/dist/routes/endpoints/sync-rules.js.map +1 -1
- package/dist/routes/endpoints/sync-stream.js +5 -5
- package/dist/routes/endpoints/sync-stream.js.map +1 -1
- package/dist/routes/route-register.js +4 -4
- package/dist/routes/route-register.js.map +1 -1
- package/dist/util/config/compound-config-collector.js +14 -4
- package/dist/util/config/compound-config-collector.js.map +1 -1
- package/dist/util/config/types.d.ts +0 -3
- package/dist/util/utils.js +2 -1
- package/dist/util/utils.js.map +1 -1
- package/package.json +5 -5
- package/src/auth/RemoteJWKSCollector.ts +31 -35
- package/src/entry/cli-entry.ts +2 -0
- package/src/entry/commands/teardown-action.ts +2 -1
- package/src/entry/commands/test-connection-action.ts +41 -0
- package/src/metrics/Metrics.ts +2 -2
- package/src/replication/AbstractReplicator.ts +3 -0
- package/src/replication/ReplicationEngine.ts +5 -0
- package/src/replication/ReplicationModule.ts +15 -13
- package/src/routes/endpoints/admin.ts +3 -3
- package/src/routes/endpoints/socket-route.ts +5 -5
- package/src/routes/endpoints/sync-rules.ts +9 -9
- package/src/routes/endpoints/sync-stream.ts +5 -5
- package/src/routes/route-register.ts +4 -4
- package/src/util/config/compound-config-collector.ts +17 -9
- package/src/util/config/types.ts +0 -3
- package/src/util/utils.ts +2 -1
- package/test/src/auth.test.ts +20 -5
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { errors, router, schema } from '@powersync/lib-services-framework';
|
|
1
|
+
import { ErrorCode, errors, router, schema } from '@powersync/lib-services-framework';
|
|
2
2
|
import { SqlSyncRules, StaticSchema } from '@powersync/service-sync-rules';
|
|
3
3
|
import { internal_routes } from '@powersync/service-types';
|
|
4
4
|
|
|
@@ -120,9 +120,9 @@ export const reprocess = routeDefinition({
|
|
|
120
120
|
|
|
121
121
|
const active = await activeBucketStorage.getActiveSyncRules(apiHandler.getParseSyncRulesOptions());
|
|
122
122
|
if (active == null) {
|
|
123
|
-
throw new errors.
|
|
123
|
+
throw new errors.ServiceError({
|
|
124
124
|
status: 422,
|
|
125
|
-
code:
|
|
125
|
+
code: ErrorCode.PSYNC_S4104,
|
|
126
126
|
description: 'No active sync rules'
|
|
127
127
|
});
|
|
128
128
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { errors, logger, schema } from '@powersync/lib-services-framework';
|
|
1
|
+
import { ErrorCode, errors, logger, schema } from '@powersync/lib-services-framework';
|
|
2
2
|
import { RequestParameters } from '@powersync/service-sync-rules';
|
|
3
3
|
import { serialize } from 'bson';
|
|
4
4
|
|
|
@@ -34,9 +34,9 @@ export const syncStreamReactive: SocketRouteGenerator = (router) =>
|
|
|
34
34
|
|
|
35
35
|
if (routerEngine!.closed) {
|
|
36
36
|
responder.onError(
|
|
37
|
-
new errors.
|
|
37
|
+
new errors.ServiceError({
|
|
38
38
|
status: 503,
|
|
39
|
-
code:
|
|
39
|
+
code: ErrorCode.PSYNC_S2003,
|
|
40
40
|
description: 'Service temporarily unavailable'
|
|
41
41
|
})
|
|
42
42
|
);
|
|
@@ -53,9 +53,9 @@ export const syncStreamReactive: SocketRouteGenerator = (router) =>
|
|
|
53
53
|
const cp = await activeBucketStorage.getActiveCheckpoint();
|
|
54
54
|
if (!cp.hasSyncRules()) {
|
|
55
55
|
responder.onError(
|
|
56
|
-
new errors.
|
|
56
|
+
new errors.ServiceError({
|
|
57
57
|
status: 500,
|
|
58
|
-
code:
|
|
58
|
+
code: ErrorCode.PSYNC_S2302,
|
|
59
59
|
description: 'No sync rules available'
|
|
60
60
|
})
|
|
61
61
|
);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { errors, router, schema } from '@powersync/lib-services-framework';
|
|
1
|
+
import { ErrorCode, errors, router, schema } from '@powersync/lib-services-framework';
|
|
2
2
|
import { SqlSyncRules, SyncRulesErrors } from '@powersync/service-sync-rules';
|
|
3
3
|
import type { FastifyPluginAsync } from 'fastify';
|
|
4
4
|
import * as t from 'ts-codec';
|
|
@@ -43,9 +43,9 @@ export const deploySyncRules = routeDefinition({
|
|
|
43
43
|
|
|
44
44
|
if (service_context.configuration.sync_rules.present) {
|
|
45
45
|
// If sync rules are configured via the config, disable deploy via the API.
|
|
46
|
-
throw new errors.
|
|
46
|
+
throw new errors.ServiceError({
|
|
47
47
|
status: 422,
|
|
48
|
-
code:
|
|
48
|
+
code: ErrorCode.PSYNC_S4105,
|
|
49
49
|
description: 'Sync rules API disabled',
|
|
50
50
|
details: 'Use the management API to deploy sync rules'
|
|
51
51
|
});
|
|
@@ -60,9 +60,9 @@ export const deploySyncRules = routeDefinition({
|
|
|
60
60
|
schema: undefined
|
|
61
61
|
});
|
|
62
62
|
} catch (e) {
|
|
63
|
-
throw new errors.
|
|
63
|
+
throw new errors.ServiceError({
|
|
64
64
|
status: 422,
|
|
65
|
-
code:
|
|
65
|
+
code: ErrorCode.PSYNC_R0001,
|
|
66
66
|
description: 'Sync rules parsing failed',
|
|
67
67
|
details: e.message
|
|
68
68
|
});
|
|
@@ -112,9 +112,9 @@ export const currentSyncRules = routeDefinition({
|
|
|
112
112
|
|
|
113
113
|
const sync_rules = await activeBucketStorage.getActiveSyncRulesContent();
|
|
114
114
|
if (!sync_rules) {
|
|
115
|
-
throw new errors.
|
|
115
|
+
throw new errors.ServiceError({
|
|
116
116
|
status: 422,
|
|
117
|
-
code:
|
|
117
|
+
code: ErrorCode.PSYNC_S4104,
|
|
118
118
|
description: 'No active sync rules'
|
|
119
119
|
});
|
|
120
120
|
}
|
|
@@ -159,9 +159,9 @@ export const reprocessSyncRules = routeDefinition({
|
|
|
159
159
|
const apiHandler = payload.context.service_context.routerEngine!.getAPI();
|
|
160
160
|
const sync_rules = await activeBucketStorage.getActiveSyncRules(apiHandler.getParseSyncRulesOptions());
|
|
161
161
|
if (sync_rules == null) {
|
|
162
|
-
throw new errors.
|
|
162
|
+
throw new errors.ServiceError({
|
|
163
163
|
status: 422,
|
|
164
|
-
code:
|
|
164
|
+
code: ErrorCode.PSYNC_S4104,
|
|
165
165
|
description: 'No active sync rules'
|
|
166
166
|
});
|
|
167
167
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { errors, logger, router, schema } from '@powersync/lib-services-framework';
|
|
1
|
+
import { ErrorCode, errors, logger, router, schema } from '@powersync/lib-services-framework';
|
|
2
2
|
import { RequestParameters } from '@powersync/service-sync-rules';
|
|
3
3
|
import { Readable } from 'stream';
|
|
4
4
|
|
|
@@ -26,9 +26,9 @@ export const syncStreamed = routeDefinition({
|
|
|
26
26
|
const clientId = payload.params.client_id;
|
|
27
27
|
|
|
28
28
|
if (routerEngine!.closed) {
|
|
29
|
-
throw new errors.
|
|
29
|
+
throw new errors.ServiceError({
|
|
30
30
|
status: 503,
|
|
31
|
-
code:
|
|
31
|
+
code: ErrorCode.PSYNC_S2003,
|
|
32
32
|
description: 'Service temporarily unavailable'
|
|
33
33
|
});
|
|
34
34
|
}
|
|
@@ -39,9 +39,9 @@ export const syncStreamed = routeDefinition({
|
|
|
39
39
|
// Sanity check before we start the stream
|
|
40
40
|
const cp = await storageEngine.activeBucketStorage.getActiveCheckpoint();
|
|
41
41
|
if (!cp.hasSyncRules()) {
|
|
42
|
-
throw new errors.
|
|
42
|
+
throw new errors.ServiceError({
|
|
43
43
|
status: 500,
|
|
44
|
-
code:
|
|
44
|
+
code: ErrorCode.PSYNC_S2302,
|
|
45
45
|
description: 'No sync rules available'
|
|
46
46
|
});
|
|
47
47
|
}
|
|
@@ -62,16 +62,16 @@ export function registerFastifyRoutes(
|
|
|
62
62
|
});
|
|
63
63
|
}
|
|
64
64
|
} catch (ex) {
|
|
65
|
-
const
|
|
66
|
-
logger.error(`Request failed`,
|
|
65
|
+
const serviceError = errors.asServiceError(ex);
|
|
66
|
+
logger.error(`Request failed`, serviceError);
|
|
67
67
|
|
|
68
68
|
response = new router.RouterResponse({
|
|
69
|
-
status:
|
|
69
|
+
status: serviceError.errorData.status || 500,
|
|
70
70
|
headers: {
|
|
71
71
|
'Content-Type': 'application/json'
|
|
72
72
|
},
|
|
73
73
|
data: {
|
|
74
|
-
error:
|
|
74
|
+
error: serviceError.errorData
|
|
75
75
|
}
|
|
76
76
|
});
|
|
77
77
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { logger } from '@powersync/lib-services-framework';
|
|
1
|
+
import { logger, LookupOptions } from '@powersync/lib-services-framework';
|
|
2
2
|
import { configFile } from '@powersync/service-types';
|
|
3
3
|
import * as auth from '../../auth/auth-index.js';
|
|
4
4
|
import { ConfigCollector } from './collectors/config-collector.js';
|
|
@@ -91,12 +91,23 @@ export class CompoundConfigCollector {
|
|
|
91
91
|
jwks_uris = [jwks_uris];
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
+
let jwksLookup: LookupOptions = {
|
|
95
|
+
reject_ip_ranges: []
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
if (baseConfig.client_auth?.jwks_reject_ip_ranges != null) {
|
|
99
|
+
jwksLookup = {
|
|
100
|
+
reject_ip_ranges: baseConfig.client_auth?.jwks_reject_ip_ranges
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
if (baseConfig.client_auth?.block_local_jwks) {
|
|
104
|
+
// Deprecated - recommend method is to use jwks_reject_ip_ranges
|
|
105
|
+
jwksLookup.reject_ip_ranges.push('local');
|
|
106
|
+
jwksLookup.reject_ipv6 = true;
|
|
107
|
+
}
|
|
108
|
+
|
|
94
109
|
for (let uri of jwks_uris) {
|
|
95
|
-
collectors.add(
|
|
96
|
-
new auth.CachedKeyCollector(
|
|
97
|
-
new auth.RemoteJWKSCollector(uri, { block_local_ip: !!baseConfig.client_auth?.block_local_jwks })
|
|
98
|
-
)
|
|
99
|
-
);
|
|
110
|
+
collectors.add(new auth.CachedKeyCollector(new auth.RemoteJWKSCollector(uri, { lookupOptions: jwksLookup })));
|
|
100
111
|
}
|
|
101
112
|
|
|
102
113
|
const baseDevKey = (baseConfig.client_auth?.jwks?.keys ?? []).find((key) => key.kid == POWERSYNC_DEV_KID);
|
|
@@ -121,9 +132,6 @@ export class CompoundConfigCollector {
|
|
|
121
132
|
api_tokens: baseConfig.api?.tokens ?? [],
|
|
122
133
|
dev: {
|
|
123
134
|
demo_auth: baseConfig.dev?.demo_auth ?? false,
|
|
124
|
-
demo_client: baseConfig.dev?.demo_client ?? false,
|
|
125
|
-
demo_password: baseConfig.dev?.demo_password,
|
|
126
|
-
crud_api: baseConfig.dev?.crud_api ?? false,
|
|
127
135
|
dev_key: devKey
|
|
128
136
|
},
|
|
129
137
|
port: baseConfig.port ?? 8080,
|
package/src/util/config/types.ts
CHANGED
package/src/util/utils.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { BucketChecksum, OpId, OplogEntry } from './protocol-types.js';
|
|
|
7
7
|
import * as storage from '../storage/storage-index.js';
|
|
8
8
|
|
|
9
9
|
import { PartialChecksum } from '../storage/ChecksumCache.js';
|
|
10
|
+
import { ServiceAssertionError } from '@powersync/lib-services-framework';
|
|
10
11
|
|
|
11
12
|
export type ChecksumMap = Map<string, BucketChecksum>;
|
|
12
13
|
|
|
@@ -34,7 +35,7 @@ export function timestampToOpId(ts: bigint): OpId {
|
|
|
34
35
|
// Dynamic values are passed in in some cases, so we make extra sure that the
|
|
35
36
|
// number is a bigint and not number or Long.
|
|
36
37
|
if (typeof ts != 'bigint') {
|
|
37
|
-
throw new
|
|
38
|
+
throw new ServiceAssertionError(`bigint expected, got: ${ts} (${typeof ts})`);
|
|
38
39
|
}
|
|
39
40
|
return ts.toString(10);
|
|
40
41
|
}
|
package/test/src/auth.test.ts
CHANGED
|
@@ -284,11 +284,23 @@ describe('JWT Auth', () => {
|
|
|
284
284
|
expect(errors).toEqual([]);
|
|
285
285
|
expect(keys.length).toBeGreaterThanOrEqual(1);
|
|
286
286
|
|
|
287
|
-
//
|
|
288
|
-
const invalid = new RemoteJWKSCollector('https://
|
|
289
|
-
|
|
287
|
+
// Domain names are resolved when retrieving keys
|
|
288
|
+
const invalid = new RemoteJWKSCollector('https://localhost/.well-known/jwks.json', {
|
|
289
|
+
lookupOptions: {
|
|
290
|
+
reject_ip_ranges: ['local']
|
|
291
|
+
}
|
|
290
292
|
});
|
|
291
293
|
expect(invalid.getKeys()).rejects.toThrow('IPs in this range are not supported');
|
|
294
|
+
|
|
295
|
+
// IPS throw an error immediately
|
|
296
|
+
expect(
|
|
297
|
+
() =>
|
|
298
|
+
new RemoteJWKSCollector('https://127.0.0.1/.well-known/jwks.json', {
|
|
299
|
+
lookupOptions: {
|
|
300
|
+
reject_ip_ranges: ['local']
|
|
301
|
+
}
|
|
302
|
+
})
|
|
303
|
+
).toThrowError('IPs in this range are not supported');
|
|
292
304
|
});
|
|
293
305
|
|
|
294
306
|
test('http not blocking local IPs', async () => {
|
|
@@ -301,10 +313,13 @@ describe('JWT Auth', () => {
|
|
|
301
313
|
expect(errors).toEqual([]);
|
|
302
314
|
expect(keys.length).toBeGreaterThanOrEqual(1);
|
|
303
315
|
|
|
304
|
-
// The localhost hostname fails to resolve correctly on MacOS https://github.com/nodejs/help/issues/2163
|
|
305
316
|
const invalid = new RemoteJWKSCollector('https://127.0.0.1/.well-known/jwks.json');
|
|
306
317
|
// Should try and fetch
|
|
307
|
-
expect(invalid.getKeys()).rejects.toThrow(
|
|
318
|
+
expect(invalid.getKeys()).rejects.toThrow();
|
|
319
|
+
|
|
320
|
+
const invalid2 = new RemoteJWKSCollector('https://localhost/.well-known/jwks.json');
|
|
321
|
+
// Should try and fetch
|
|
322
|
+
expect(invalid2.getKeys()).rejects.toThrow();
|
|
308
323
|
});
|
|
309
324
|
|
|
310
325
|
test('caching', async () => {
|