-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* add utilities for route generation * cleanup imports
- Loading branch information
1 parent
bfc0dc5
commit 3f994ae
Showing
11 changed files
with
189 additions
and
113 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
'@powersync/service-core': minor | ||
--- | ||
|
||
Added utility functions for registering routes |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
import type fastify from 'fastify'; | ||
import { registerFastifyRoutes } from './route-register.js'; | ||
|
||
import * as system from '../system/system-index.js'; | ||
|
||
import { ADMIN_ROUTES } from './endpoints/admin.js'; | ||
import { CHECKPOINT_ROUTES } from './endpoints/checkpointing.js'; | ||
import { DEV_ROUTES } from './endpoints/dev.js'; | ||
import { SYNC_RULES_ROUTES } from './endpoints/sync-rules.js'; | ||
import { SYNC_STREAM_ROUTES } from './endpoints/sync-stream.js'; | ||
import { createRequestQueueHook, CreateRequestQueueParams } from './hooks.js'; | ||
import { RouteDefinition } from './router.js'; | ||
|
||
/** | ||
* A list of route definitions to be registered as endpoints. | ||
* Supplied concurrency limits will be applied to the grouped routes. | ||
*/ | ||
export type RouteRegistrationOptions = { | ||
routes: RouteDefinition[]; | ||
queueOptions: CreateRequestQueueParams; | ||
}; | ||
|
||
/** | ||
* HTTP routes separated by API and Sync stream categories. | ||
* This allows for separate concurrency limits. | ||
*/ | ||
export type RouteDefinitions = { | ||
api?: Partial<RouteRegistrationOptions>; | ||
syncStream?: Partial<RouteRegistrationOptions>; | ||
}; | ||
|
||
export type FastifyServerConfig = { | ||
system: system.CorePowerSyncSystem; | ||
routes?: RouteDefinitions; | ||
}; | ||
|
||
export const DEFAULT_ROUTE_OPTIONS = { | ||
api: { | ||
routes: [...ADMIN_ROUTES, ...CHECKPOINT_ROUTES, ...DEV_ROUTES, ...SYNC_RULES_ROUTES], | ||
queueOptions: { | ||
concurrency: 10, | ||
max_queue_depth: 20 | ||
} | ||
}, | ||
syncStream: { | ||
routes: [...SYNC_STREAM_ROUTES], | ||
queueOptions: { | ||
concurrency: 200, | ||
max_queue_depth: 0 | ||
} | ||
} | ||
}; | ||
|
||
/** | ||
* Registers default routes on a Fastify server. Consumers can optionally configure | ||
* concurrency queue limits or override routes. | ||
*/ | ||
export function configureFastifyServer(server: fastify.FastifyInstance, options: FastifyServerConfig) { | ||
const { system, routes = DEFAULT_ROUTE_OPTIONS } = options; | ||
/** | ||
* Fastify creates an encapsulated context for each `.register` call. | ||
* Creating a separate context here to separate the concurrency limits for Admin APIs | ||
* and Sync Streaming routes. | ||
* https://github.com/fastify/fastify/blob/main/docs/Reference/Encapsulation.md | ||
*/ | ||
server.register(async function (childContext) { | ||
registerFastifyRoutes( | ||
childContext, | ||
async () => { | ||
return { | ||
user_id: undefined, | ||
system: system | ||
}; | ||
}, | ||
routes.api?.routes ?? DEFAULT_ROUTE_OPTIONS.api.routes | ||
); | ||
// Limit the active concurrent requests | ||
childContext.addHook( | ||
'onRequest', | ||
createRequestQueueHook(routes.api?.queueOptions ?? DEFAULT_ROUTE_OPTIONS.api.queueOptions) | ||
); | ||
}); | ||
|
||
// Create a separate context for concurrency queueing | ||
server.register(async function (childContext) { | ||
registerFastifyRoutes( | ||
childContext, | ||
async () => { | ||
return { | ||
user_id: undefined, | ||
system: system | ||
}; | ||
}, | ||
routes.syncStream?.routes ?? DEFAULT_ROUTE_OPTIONS.syncStream.routes | ||
); | ||
// Limit the active concurrent requests | ||
childContext.addHook( | ||
'onRequest', | ||
createRequestQueueHook(routes.syncStream?.queueOptions ?? DEFAULT_ROUTE_OPTIONS.syncStream.queueOptions) | ||
); | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
import { deserialize } from 'bson'; | ||
import * as http from 'http'; | ||
|
||
import { errors, logger } from '@powersync/lib-services-framework'; | ||
import { ReactiveSocketRouter, RSocketRequestMeta } from '@powersync/service-rsocket-router'; | ||
|
||
import { CorePowerSyncSystem } from '../system/CorePowerSyncSystem.js'; | ||
import { generateContext, getTokenFromHeader } from './auth.js'; | ||
import { syncStreamReactive } from './endpoints/socket-route.js'; | ||
import { RSocketContextMeta, SocketRouteGenerator } from './router-socket.js'; | ||
import { Context } from './router.js'; | ||
|
||
export type RSockerRouterConfig = { | ||
system: CorePowerSyncSystem; | ||
server: http.Server; | ||
routeGenerators?: SocketRouteGenerator[]; | ||
}; | ||
|
||
export const DEFAULT_SOCKET_ROUTES = [syncStreamReactive]; | ||
|
||
export function configureRSocket(router: ReactiveSocketRouter<Context>, options: RSockerRouterConfig) { | ||
const { routeGenerators = DEFAULT_SOCKET_ROUTES, server, system } = options; | ||
|
||
router.applyWebSocketEndpoints(server, { | ||
contextProvider: async (data: Buffer) => { | ||
const { token } = RSocketContextMeta.decode(deserialize(data) as any); | ||
|
||
if (!token) { | ||
throw new errors.AuthorizationError('No token provided'); | ||
} | ||
|
||
try { | ||
const extracted_token = getTokenFromHeader(token); | ||
if (extracted_token != null) { | ||
const { context, errors: token_errors } = await generateContext(system, extracted_token); | ||
if (context?.token_payload == null) { | ||
throw new errors.AuthorizationError(token_errors ?? 'Authentication required'); | ||
} | ||
return { | ||
token, | ||
...context, | ||
token_errors: token_errors, | ||
system | ||
}; | ||
} else { | ||
throw new errors.AuthorizationError('No token provided'); | ||
} | ||
} catch (ex) { | ||
logger.error(ex); | ||
throw ex; | ||
} | ||
}, | ||
endpoints: routeGenerators.map((generator) => generator(router)), | ||
metaDecoder: async (meta: Buffer) => { | ||
return RSocketRequestMeta.decode(deserialize(meta) as any); | ||
}, | ||
payloadDecoder: async (rawData?: Buffer) => rawData && deserialize(rawData) | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,13 @@ | ||
import { IReactiveStream, ReactiveSocketRouter } from '@powersync/service-rsocket-router'; | ||
import * as t from 'ts-codec'; | ||
import { ReactiveSocketRouter, IReactiveStream } from '@powersync/service-rsocket-router'; | ||
|
||
import { Context } from './router.js'; | ||
|
||
export const RSocketContextMeta = t.object({ | ||
token: t.string | ||
}); | ||
|
||
/** | ||
* Creates a socket route handler given a router instance | ||
*/ | ||
export type SocketRouteGenerator = (router: ReactiveSocketRouter<Context>) => IReactiveStream; | ||
|
||
export const RSocketContextMeta = t.object({ | ||
token: t.string | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
export * from './BroadcastIterable.js'; | ||
export * from './LastValueSink.js'; | ||
export * from './merge.js'; | ||
export * from './RequestTracker.js'; | ||
export * from './safeRace.js'; | ||
export * from './sync.js'; | ||
export * from './util.js'; |
Oops, something went wrong.