@powerhousedao/reactor-api 6.0.0-dev.107 → 6.0.0-dev.109
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -0
- package/dist/src/graphql/base-subgraph.d.ts +2 -3
- package/dist/src/graphql/base-subgraph.d.ts.map +1 -1
- package/dist/src/graphql/base-subgraph.js.map +1 -1
- package/dist/src/graphql/gateway/adapter-gateway-apollo.d.ts +23 -0
- package/dist/src/graphql/gateway/adapter-gateway-apollo.d.ts.map +1 -0
- package/dist/src/graphql/gateway/adapter-gateway-apollo.js +156 -0
- package/dist/src/graphql/gateway/adapter-gateway-apollo.js.map +1 -0
- package/dist/src/graphql/gateway/adapter-http-express.d.ts +23 -0
- package/dist/src/graphql/gateway/adapter-http-express.d.ts.map +1 -0
- package/dist/src/graphql/gateway/adapter-http-express.js +155 -0
- package/dist/src/graphql/gateway/adapter-http-express.js.map +1 -0
- package/dist/src/graphql/gateway/auth-middleware.d.ts +7 -0
- package/dist/src/graphql/gateway/auth-middleware.d.ts.map +1 -0
- package/dist/src/graphql/gateway/auth-middleware.js +15 -0
- package/dist/src/graphql/gateway/auth-middleware.js.map +1 -0
- package/dist/src/graphql/gateway/factory.d.ts +11 -0
- package/dist/src/graphql/gateway/factory.d.ts.map +1 -0
- package/dist/src/graphql/gateway/factory.js +15 -0
- package/dist/src/graphql/gateway/factory.js.map +1 -0
- package/dist/src/graphql/gateway/types.d.ts +84 -0
- package/dist/src/graphql/gateway/types.d.ts.map +1 -0
- package/dist/src/graphql/gateway/types.js +2 -0
- package/dist/src/graphql/gateway/types.js.map +1 -0
- package/dist/src/graphql/graphql-manager.d.ts +10 -14
- package/dist/src/graphql/graphql-manager.d.ts.map +1 -1
- package/dist/src/graphql/graphql-manager.js +117 -221
- package/dist/src/graphql/graphql-manager.js.map +1 -1
- package/dist/src/graphql/index.d.ts +3 -0
- package/dist/src/graphql/index.d.ts.map +1 -1
- package/dist/src/graphql/index.js +3 -0
- package/dist/src/graphql/index.js.map +1 -1
- package/dist/src/graphql/sse.d.ts.map +1 -1
- package/dist/src/graphql/sse.js.map +1 -1
- package/dist/src/graphql/types.d.ts +3 -4
- package/dist/src/graphql/types.d.ts.map +1 -1
- package/dist/src/packages/import-loader.d.ts +2 -3
- package/dist/src/packages/import-loader.d.ts.map +1 -1
- package/dist/src/packages/import-loader.js.map +1 -1
- package/dist/src/packages/package-manager.d.ts +2 -3
- package/dist/src/packages/package-manager.d.ts.map +1 -1
- package/dist/src/packages/package-manager.js.map +1 -1
- package/dist/src/packages/types.d.ts +4 -5
- package/dist/src/packages/types.d.ts.map +1 -1
- package/dist/src/packages/util.d.ts +3 -4
- package/dist/src/packages/util.d.ts.map +1 -1
- package/dist/src/packages/util.js.map +1 -1
- package/dist/src/packages/vite-loader.d.ts +3 -4
- package/dist/src/packages/vite-loader.d.ts.map +1 -1
- package/dist/src/packages/vite-loader.js +2 -0
- package/dist/src/packages/vite-loader.js.map +1 -1
- package/dist/src/server.d.ts +5 -4
- package/dist/src/server.d.ts.map +1 -1
- package/dist/src/server.js +54 -84
- package/dist/src/server.js.map +1 -1
- package/dist/src/services/auth.service.d.ts +11 -3
- package/dist/src/services/auth.service.d.ts.map +1 -1
- package/dist/src/services/auth.service.js +48 -0
- package/dist/src/services/auth.service.js.map +1 -1
- package/dist/src/types.d.ts +14 -5
- package/dist/src/types.d.ts.map +1 -1
- package/dist/test/auth.service.test.d.ts +10 -0
- package/dist/test/auth.service.test.d.ts.map +1 -0
- package/dist/test/auth.service.test.js +141 -0
- package/dist/test/auth.service.test.js.map +1 -0
- package/dist/test/document-drive-subgraph.test.js +8 -13
- package/dist/test/document-drive-subgraph.test.js.map +1 -1
- package/dist/test/gateway/adapter-gateway-apollo.test.d.ts +2 -0
- package/dist/test/gateway/adapter-gateway-apollo.test.d.ts.map +1 -0
- package/dist/test/gateway/adapter-gateway-apollo.test.js +206 -0
- package/dist/test/gateway/adapter-gateway-apollo.test.js.map +1 -0
- package/dist/test/gateway/adapter-http-express.test.d.ts +2 -0
- package/dist/test/gateway/adapter-http-express.test.d.ts.map +1 -0
- package/dist/test/gateway/adapter-http-express.test.js +17 -0
- package/dist/test/gateway/adapter-http-express.test.js.map +1 -0
- package/dist/test/gateway/auth-middleware.test.d.ts +8 -0
- package/dist/test/gateway/auth-middleware.test.d.ts.map +1 -0
- package/dist/test/gateway/auth-middleware.test.js +73 -0
- package/dist/test/gateway/auth-middleware.test.js.map +1 -0
- package/dist/test/gateway/gateway-adapter-contract.d.ts +30 -0
- package/dist/test/gateway/gateway-adapter-contract.d.ts.map +1 -0
- package/dist/test/gateway/gateway-adapter-contract.js +347 -0
- package/dist/test/gateway/gateway-adapter-contract.js.map +1 -0
- package/dist/test/gateway/http-adapter-contract.d.ts +30 -0
- package/dist/test/gateway/http-adapter-contract.d.ts.map +1 -0
- package/dist/test/gateway/http-adapter-contract.js +226 -0
- package/dist/test/gateway/http-adapter-contract.js.map +1 -0
- package/dist/test/graphql-manager.test.d.ts +12 -0
- package/dist/test/graphql-manager.test.d.ts.map +1 -0
- package/dist/test/graphql-manager.test.js +488 -0
- package/dist/test/graphql-manager.test.js.map +1 -0
- package/dist/test/utils.d.ts +0 -4
- package/dist/test/utils.d.ts.map +1 -1
- package/dist/test/utils.js +0 -11
- package/dist/test/utils.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +11 -11
- package/dist/src/utils/auth.d.ts +0 -3
- package/dist/src/utils/auth.d.ts.map +0 -1
- package/dist/src/utils/auth.js +0 -12
- package/dist/src/utils/auth.js.map +0 -1
|
@@ -1,21 +1,20 @@
|
|
|
1
1
|
import type { IAnalyticsStore } from "@powerhousedao/analytics-engine-core";
|
|
2
|
-
import type { IReactorClient, ISyncManager } from "@powerhousedao/reactor";
|
|
3
|
-
import type { ISubgraph, SubgraphClass } from "@powerhousedao/reactor-api";
|
|
4
|
-
import type {
|
|
5
|
-
import { type ILogger } from "document-model";
|
|
6
|
-
import type express from "express";
|
|
2
|
+
import type { IReactorClient, IRelationalDb, ISyncManager } from "@powerhousedao/reactor";
|
|
3
|
+
import type { Context, ISubgraph, SubgraphClass } from "@powerhousedao/reactor-api";
|
|
4
|
+
import type { ILogger } from "document-model";
|
|
7
5
|
import type http from "node:http";
|
|
8
6
|
import type { WebSocketServer } from "ws";
|
|
9
7
|
import type { AuthConfig } from "../services/auth.service.js";
|
|
8
|
+
import { type AuthFetchMiddleware } from "./gateway/auth-middleware.js";
|
|
10
9
|
import type { AuthorizationService } from "../services/authorization.service.js";
|
|
11
10
|
import type { DocumentPermissionService } from "../services/document-permission.service.js";
|
|
11
|
+
import type { IGatewayAdapter, IHttpAdapter } from "./gateway/types.js";
|
|
12
12
|
export type GraphqlManagerFeatureFlags = {
|
|
13
13
|
enableDocumentModelSubgraphs?: boolean;
|
|
14
14
|
};
|
|
15
15
|
export declare class GraphQLManager {
|
|
16
16
|
#private;
|
|
17
17
|
private readonly path;
|
|
18
|
-
private readonly app;
|
|
19
18
|
private readonly httpServer;
|
|
20
19
|
private readonly wsServer;
|
|
21
20
|
private readonly reactorClient;
|
|
@@ -23,27 +22,24 @@ export declare class GraphQLManager {
|
|
|
23
22
|
private readonly analyticsStore;
|
|
24
23
|
private readonly syncManager;
|
|
25
24
|
private readonly logger;
|
|
25
|
+
private readonly httpAdapter;
|
|
26
|
+
private readonly gatewayAdapter;
|
|
26
27
|
private readonly authConfig?;
|
|
27
28
|
private readonly documentPermissionService?;
|
|
28
29
|
private readonly featureFlags;
|
|
29
30
|
private readonly port;
|
|
30
31
|
private readonly authorizationService?;
|
|
31
32
|
private initialized;
|
|
32
|
-
private router;
|
|
33
33
|
private coreSubgraphsMap;
|
|
34
34
|
private contextFields;
|
|
35
35
|
private readonly subgraphs;
|
|
36
36
|
private authService;
|
|
37
|
-
private coreApolloServer;
|
|
38
|
-
private readonly subgraphServers;
|
|
39
|
-
private readonly subgraphHandlers;
|
|
40
37
|
private readonly subgraphWsDisposers;
|
|
41
|
-
private gatewayOptions;
|
|
42
38
|
/** Cached document models for schema generation - updated on init and regenerate */
|
|
43
39
|
private cachedDocumentModels;
|
|
44
|
-
private readonly
|
|
45
|
-
constructor(path: string,
|
|
46
|
-
init(coreSubgraphs: SubgraphClass[]): Promise<void>;
|
|
40
|
+
private readonly subgraphHandlerCache;
|
|
41
|
+
constructor(path: string, httpServer: http.Server, wsServer: WebSocketServer, reactorClient: IReactorClient, relationalDb: IRelationalDb, analyticsStore: IAnalyticsStore, syncManager: ISyncManager, logger: ILogger, httpAdapter: IHttpAdapter, gatewayAdapter: IGatewayAdapter<Context>, authConfig?: AuthConfig | undefined, documentPermissionService?: DocumentPermissionService | undefined, featureFlags?: GraphqlManagerFeatureFlags, port?: number, authorizationService?: AuthorizationService | undefined);
|
|
42
|
+
init(coreSubgraphs: SubgraphClass[], authMiddleware?: AuthFetchMiddleware): Promise<void>;
|
|
47
43
|
/**
|
|
48
44
|
* Regenerate document model subgraphs when models are dynamically loaded.
|
|
49
45
|
* Fetches current modules from reactor client (source of truth).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"graphql-manager.d.ts","sourceRoot":"","sources":["../../../src/graphql/graphql-manager.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"graphql-manager.d.ts","sourceRoot":"","sources":["../../../src/graphql/graphql-manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,32 +1,12 @@
|
|
|
1
|
-
import { ApolloGateway, LocalCompose, RemoteGraphQLDataSource, } from "@apollo/gateway";
|
|
2
|
-
import { ApolloServer } from "@apollo/server";
|
|
3
|
-
import { ApolloServerPluginInlineTraceDisabled } from "@apollo/server/plugin/disabled";
|
|
4
|
-
import { ApolloServerPluginDrainHttpServer } from "@apollo/server/plugin/drainHttpServer";
|
|
5
|
-
import { ApolloServerPluginLandingPageLocalDefault } from "@apollo/server/plugin/landingPage/default";
|
|
6
|
-
import { expressMiddleware } from "@as-integrations/express4";
|
|
7
|
-
import {} from "@powerhousedao/shared/document-model";
|
|
8
|
-
import bodyParser from "body-parser";
|
|
9
|
-
import cors from "cors";
|
|
10
|
-
import { debounce, responseForDrive } from "document-drive";
|
|
11
|
-
import { childLogger } from "document-model";
|
|
12
|
-
import { Router } from "express";
|
|
13
1
|
import path from "node:path";
|
|
14
|
-
import { setTimeout } from "node:timers/promises";
|
|
15
2
|
import { match } from "path-to-regexp";
|
|
3
|
+
import { debounce } from "../packages/util.js";
|
|
16
4
|
import { AuthService } from "../services/auth.service.js";
|
|
5
|
+
import { getAuthContext, } from "./gateway/auth-middleware.js";
|
|
17
6
|
import { buildSubgraphSchemaModule, createMergedSchema, createSchema, } from "../utils/create-schema.js";
|
|
18
7
|
import { DocumentModelSubgraph } from "./document-model-subgraph.js";
|
|
19
8
|
import { createGraphQLSSEHandler } from "./sse.js";
|
|
20
9
|
import { useServer } from "./websocket.js";
|
|
21
|
-
class AuthenticatedDataSource extends RemoteGraphQLDataSource {
|
|
22
|
-
willSendRequest(options) {
|
|
23
|
-
const { authorization } = options.context.headers;
|
|
24
|
-
// console.log("context", options.context.headers.authorization);
|
|
25
|
-
if (authorization && options?.request.http) {
|
|
26
|
-
options.request.http.headers.set("authorization", authorization);
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
10
|
const DOCUMENT_MODELS_TO_EXCLUDE = [];
|
|
31
11
|
/**
|
|
32
12
|
* Check if a document model has any operations with valid schemas.
|
|
@@ -36,13 +16,11 @@ function hasOperationSchemas(documentModel) {
|
|
|
36
16
|
const specification = documentModel.documentModel.global.specifications.at(-1);
|
|
37
17
|
if (!specification)
|
|
38
18
|
return false;
|
|
39
|
-
// Check if any operation has a schema with actual GraphQL type definitions
|
|
40
19
|
const hasValidSchema = (schema) => schema && /\b(input|type|enum|union|interface)\s+\w+/.test(schema);
|
|
41
20
|
return specification.modules.some((module) => module.operations.some((op) => hasValidSchema(op.schema)));
|
|
42
21
|
}
|
|
43
22
|
/**
|
|
44
23
|
* Filter document models to keep only the latest version of each unique document model.
|
|
45
|
-
* When multiple versions exist with the same name, the one with the most recent specification is kept.
|
|
46
24
|
*/
|
|
47
25
|
function filterLatestDocumentModelVersions(documentModels) {
|
|
48
26
|
const latestByName = new Map();
|
|
@@ -53,7 +31,6 @@ function filterLatestDocumentModelVersions(documentModels) {
|
|
|
53
31
|
latestByName.set(name, documentModel);
|
|
54
32
|
continue;
|
|
55
33
|
}
|
|
56
|
-
// Compare version numbers from the latest specification
|
|
57
34
|
const currentVersion = documentModel.documentModel.global.specifications.at(-1)?.version ?? 0;
|
|
58
35
|
const existingVersion = existing.documentModel.global.specifications.at(-1)?.version ?? 0;
|
|
59
36
|
if (currentVersion > existingVersion) {
|
|
@@ -67,7 +44,6 @@ const DefaultFeatureFlags = {
|
|
|
67
44
|
};
|
|
68
45
|
export class GraphQLManager {
|
|
69
46
|
path;
|
|
70
|
-
app;
|
|
71
47
|
httpServer;
|
|
72
48
|
wsServer;
|
|
73
49
|
reactorClient;
|
|
@@ -75,32 +51,25 @@ export class GraphQLManager {
|
|
|
75
51
|
analyticsStore;
|
|
76
52
|
syncManager;
|
|
77
53
|
logger;
|
|
54
|
+
httpAdapter;
|
|
55
|
+
gatewayAdapter;
|
|
78
56
|
authConfig;
|
|
79
57
|
documentPermissionService;
|
|
80
58
|
featureFlags;
|
|
81
59
|
port;
|
|
82
60
|
authorizationService;
|
|
83
61
|
initialized = false;
|
|
84
|
-
router = Router();
|
|
85
62
|
coreSubgraphsMap = new Map();
|
|
86
63
|
contextFields = {};
|
|
87
64
|
subgraphs = new Map();
|
|
88
65
|
authService = null;
|
|
89
|
-
coreApolloServer = null;
|
|
90
|
-
subgraphServers = new Map();
|
|
91
|
-
subgraphHandlers = new Map();
|
|
92
66
|
subgraphWsDisposers = new Map();
|
|
93
|
-
|
|
67
|
+
#authMiddleware;
|
|
94
68
|
/** Cached document models for schema generation - updated on init and regenerate */
|
|
95
69
|
cachedDocumentModels = [];
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
"graphql-manager",
|
|
99
|
-
"apollo",
|
|
100
|
-
]);
|
|
101
|
-
constructor(path, app, httpServer, wsServer, reactorClient, relationalDb, analyticsStore, syncManager, logger, authConfig, documentPermissionService, featureFlags = DefaultFeatureFlags, port = 4001, authorizationService) {
|
|
70
|
+
subgraphHandlerCache = new Map();
|
|
71
|
+
constructor(path, httpServer, wsServer, reactorClient, relationalDb, analyticsStore, syncManager, logger, httpAdapter, gatewayAdapter, authConfig, documentPermissionService, featureFlags = DefaultFeatureFlags, port = 4001, authorizationService) {
|
|
102
72
|
this.path = path;
|
|
103
|
-
this.app = app;
|
|
104
73
|
this.httpServer = httpServer;
|
|
105
74
|
this.wsServer = wsServer;
|
|
106
75
|
this.reactorClient = reactorClient;
|
|
@@ -108,6 +77,8 @@ export class GraphQLManager {
|
|
|
108
77
|
this.analyticsStore = analyticsStore;
|
|
109
78
|
this.syncManager = syncManager;
|
|
110
79
|
this.logger = logger;
|
|
80
|
+
this.httpAdapter = httpAdapter;
|
|
81
|
+
this.gatewayAdapter = gatewayAdapter;
|
|
111
82
|
this.authConfig = authConfig;
|
|
112
83
|
this.documentPermissionService = documentPermissionService;
|
|
113
84
|
this.featureFlags = featureFlags;
|
|
@@ -115,10 +86,10 @@ export class GraphQLManager {
|
|
|
115
86
|
this.authorizationService = authorizationService;
|
|
116
87
|
if (this.authConfig) {
|
|
117
88
|
this.authService = new AuthService(this.authConfig);
|
|
118
|
-
this.setAdditionalContextFields(this.authService.getAdditionalContextFields());
|
|
119
89
|
}
|
|
120
90
|
}
|
|
121
|
-
async init(coreSubgraphs) {
|
|
91
|
+
async init(coreSubgraphs, authMiddleware) {
|
|
92
|
+
this.#authMiddleware = authMiddleware;
|
|
122
93
|
this.logger.debug(`Initializing Subgraph Manager...`);
|
|
123
94
|
// check if Document Drive model is available
|
|
124
95
|
const modulesResult = await this.reactorClient.getDocumentModelModules();
|
|
@@ -129,34 +100,45 @@ export class GraphQLManager {
|
|
|
129
100
|
if (!driveModel) {
|
|
130
101
|
throw new Error("DocumentDrive model required");
|
|
131
102
|
}
|
|
132
|
-
this.
|
|
133
|
-
this.
|
|
134
|
-
|
|
135
|
-
this.
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
103
|
+
await this.gatewayAdapter.start(this.httpServer);
|
|
104
|
+
this.httpAdapter.setupMiddleware({ bodyLimit: "50mb" });
|
|
105
|
+
// Register REST endpoint for drive info as a framework-agnostic FetchHandler.
|
|
106
|
+
const driveRoutePath = path.join(this.path, "d/:drive");
|
|
107
|
+
const driveMatcher = match(driveRoutePath);
|
|
108
|
+
this.httpAdapter.mount(driveRoutePath, async (request) => {
|
|
109
|
+
const url = new URL(request.url);
|
|
110
|
+
const matched = driveMatcher(url.pathname);
|
|
111
|
+
const driveIdOrSlug = matched ? matched.params.drive : undefined;
|
|
112
|
+
if (!driveIdOrSlug) {
|
|
113
|
+
return Response.json({ error: "Drive ID or slug is required" }, { status: 400 });
|
|
114
|
+
}
|
|
115
|
+
try {
|
|
116
|
+
const driveDoc = await this.reactorClient.get(driveIdOrSlug);
|
|
117
|
+
const forwardedProto = request.headers.get("x-forwarded-proto");
|
|
118
|
+
const protocol = (forwardedProto ?? url.protocol.replace(":", "")) + ":";
|
|
119
|
+
const host = request.headers.get("host") ?? "";
|
|
120
|
+
const basePath = this.path === "/" ? "" : this.path;
|
|
121
|
+
const graphqlEndpoint = `${protocol}//${host}${basePath}/graphql/r`;
|
|
122
|
+
return Response.json({
|
|
123
|
+
id: driveDoc.header.id,
|
|
124
|
+
slug: driveDoc.header.slug,
|
|
125
|
+
meta: driveDoc.header.meta,
|
|
126
|
+
name: driveDoc.state.global.name,
|
|
127
|
+
icon: driveDoc.state.global.icon ?? undefined,
|
|
128
|
+
...(graphqlEndpoint && { graphqlEndpoint }),
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
catch (error) {
|
|
132
|
+
this.logger.debug(`Drive not found: ${driveIdOrSlug}`, error);
|
|
133
|
+
return Response.json({ error: "Drive not found" }, { status: 404 });
|
|
141
134
|
}
|
|
142
|
-
return result.handler(req, res, next);
|
|
143
|
-
});
|
|
144
|
-
this.app.use("/", (req, res, next) => {
|
|
145
|
-
this.setAdditionalContextFields({
|
|
146
|
-
user: req.user,
|
|
147
|
-
isAdmin: (address) => !req.auth_enabled
|
|
148
|
-
? true
|
|
149
|
-
: (req.admins
|
|
150
|
-
?.map((a) => a.toLowerCase())
|
|
151
|
-
.includes(address.toLowerCase() ?? "") ?? false),
|
|
152
|
-
});
|
|
153
|
-
this.router(req, res, next);
|
|
154
135
|
});
|
|
136
|
+
this.logger.info(`Registered REST endpoint: GET ${driveRoutePath}`);
|
|
155
137
|
await this.#setupCoreSubgraphs("graphql", coreSubgraphs);
|
|
156
138
|
if (this.featureFlags.enableDocumentModelSubgraphs) {
|
|
157
139
|
await this.#setupDocumentModelSubgraphs("graphql", models);
|
|
158
140
|
}
|
|
159
|
-
await this.#
|
|
141
|
+
await this.#createSupergraphGateway();
|
|
160
142
|
return this.updateRouter();
|
|
161
143
|
}
|
|
162
144
|
/**
|
|
@@ -190,19 +172,16 @@ export class GraphQLManager {
|
|
|
190
172
|
this.logger.error(`Failed to setup core subgraph ${subgraph.name}`, error);
|
|
191
173
|
}
|
|
192
174
|
}
|
|
193
|
-
// REST endpoint for drive info at /d/:drive
|
|
194
|
-
this.#setupDriveInfoRestEndpoint(this.router);
|
|
195
175
|
return this.#setupSubgraphs(this.coreSubgraphsMap);
|
|
196
176
|
}
|
|
197
177
|
async #setupDocumentModelSubgraphs(supergraph, documentModels) {
|
|
198
|
-
// Filter to keep only the latest version of each document model
|
|
199
178
|
const latestDocumentModels = filterLatestDocumentModelVersions(documentModels);
|
|
200
179
|
for (const documentModel of latestDocumentModels) {
|
|
201
180
|
if (DOCUMENT_MODELS_TO_EXCLUDE.includes(documentModel.documentModel.global.id)) {
|
|
202
|
-
continue;
|
|
181
|
+
continue;
|
|
203
182
|
}
|
|
204
183
|
if (!hasOperationSchemas(documentModel)) {
|
|
205
|
-
continue;
|
|
184
|
+
continue;
|
|
206
185
|
}
|
|
207
186
|
try {
|
|
208
187
|
const subgraphInstance = new DocumentModelSubgraph(documentModel, {
|
|
@@ -222,7 +201,12 @@ export class GraphQLManager {
|
|
|
222
201
|
this.logger.debug("@error", error);
|
|
223
202
|
}
|
|
224
203
|
}
|
|
225
|
-
|
|
204
|
+
// Document model subgraph instances are added to this.subgraphs above.
|
|
205
|
+
// Their handlers are wired in _updateRouter() → #setupSubgraphs(this.subgraphs),
|
|
206
|
+
// which is called at the end of init() via updateRouter(). We intentionally do
|
|
207
|
+
// NOT call #setupSubgraphs(this.coreSubgraphsMap) here - doing so would
|
|
208
|
+
// create duplicate Apollo servers for the same core-subgraph schemas, which
|
|
209
|
+
// in the new IGatewayAdapter architecture hangs #waitForServer and blocks init().
|
|
226
210
|
}
|
|
227
211
|
async #addSubgraphInstance(subgraphInstance, supergraph = "", core = false) {
|
|
228
212
|
const subgraphsMap = core ? this.coreSubgraphsMap : this.subgraphs;
|
|
@@ -235,7 +219,6 @@ export class GraphQLManager {
|
|
|
235
219
|
await subgraphInstance.onSetup?.();
|
|
236
220
|
subgraphs.push(subgraphInstance);
|
|
237
221
|
subgraphsMap.set(supergraph, subgraphs);
|
|
238
|
-
// also add to global graphql supergraph
|
|
239
222
|
if (supergraph !== "" && supergraph !== "graphql") {
|
|
240
223
|
subgraphsMap.get("graphql")?.push(subgraphInstance);
|
|
241
224
|
}
|
|
@@ -271,21 +254,13 @@ export class GraphQLManager {
|
|
|
271
254
|
updateRouter = debounce(this._updateRouter.bind(this), 1000);
|
|
272
255
|
async _updateRouter() {
|
|
273
256
|
this.logger.debug("Updating router");
|
|
274
|
-
// @todo:
|
|
275
|
-
// if auth enabled, subgraphs are only available to guests, users and admins
|
|
276
|
-
// if auth enabled, set req user to the graphql context
|
|
277
|
-
// if auth disabled, subgraphs are available to all
|
|
278
257
|
await this.#setupSubgraphs(this.subgraphs);
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
}
|
|
286
|
-
catch (error) {
|
|
287
|
-
this.logger.error("Failed to update Apollo Gateway supergraph", error);
|
|
288
|
-
}
|
|
258
|
+
try {
|
|
259
|
+
await this.gatewayAdapter.updateSupergraph();
|
|
260
|
+
this.logger.debug("Updated Apollo Gateway supergraph");
|
|
261
|
+
}
|
|
262
|
+
catch (error) {
|
|
263
|
+
this.logger.error("Failed to update Apollo Gateway supergraph", error);
|
|
289
264
|
}
|
|
290
265
|
// Refresh the supergraph-level SSE handler so it picks up
|
|
291
266
|
// any newly registered subscription-enabled subgraphs.
|
|
@@ -315,6 +290,29 @@ export class GraphQLManager {
|
|
|
315
290
|
}
|
|
316
291
|
return context;
|
|
317
292
|
}
|
|
293
|
+
#makeContextFactory() {
|
|
294
|
+
return (request) => {
|
|
295
|
+
const authCtx = getAuthContext(request);
|
|
296
|
+
const headers = {};
|
|
297
|
+
request.headers.forEach((v, k) => {
|
|
298
|
+
headers[k] = v;
|
|
299
|
+
});
|
|
300
|
+
return Promise.resolve({
|
|
301
|
+
headers,
|
|
302
|
+
db: this.relationalDb,
|
|
303
|
+
...this.getAdditionalContextFields(),
|
|
304
|
+
user: authCtx?.user,
|
|
305
|
+
isAdmin: authCtx
|
|
306
|
+
? (addr) => !authCtx.auth_enabled
|
|
307
|
+
? true
|
|
308
|
+
: authCtx.admins.includes(addr.toLowerCase())
|
|
309
|
+
: () => true,
|
|
310
|
+
});
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
#makeWsContextFactory() {
|
|
314
|
+
return (connectionParams) => this.#createWebSocketContext(connectionParams);
|
|
315
|
+
}
|
|
318
316
|
setSupergraph(supergraph, subgraphs) {
|
|
319
317
|
this.subgraphs.set(supergraph, subgraphs);
|
|
320
318
|
const globalSubgraphs = this.subgraphs.get("graphql");
|
|
@@ -328,6 +326,13 @@ export class GraphQLManager {
|
|
|
328
326
|
}
|
|
329
327
|
async shutdown() {
|
|
330
328
|
this.logger.info("Shutting down GraphQL Manager");
|
|
329
|
+
// Dispose per-subgraph WebSocket handlers before closing the WS server.
|
|
330
|
+
for (const disposer of this.subgraphWsDisposers.values()) {
|
|
331
|
+
await disposer.dispose();
|
|
332
|
+
}
|
|
333
|
+
this.subgraphWsDisposers.clear();
|
|
334
|
+
// Stop all Apollo servers (per-subgraph + federation gateway) via the adapter.
|
|
335
|
+
await this.gatewayAdapter.stop();
|
|
331
336
|
return new Promise((resolve) => {
|
|
332
337
|
this.wsServer.close(() => {
|
|
333
338
|
this.logger.info("WebSocket server closed");
|
|
@@ -335,30 +340,6 @@ export class GraphQLManager {
|
|
|
335
340
|
});
|
|
336
341
|
});
|
|
337
342
|
}
|
|
338
|
-
async #createApolloServer(schema) {
|
|
339
|
-
const server = new ApolloServer({
|
|
340
|
-
schema,
|
|
341
|
-
logger: this.apolloLogger,
|
|
342
|
-
introspection: true,
|
|
343
|
-
plugins: [
|
|
344
|
-
ApolloServerPluginInlineTraceDisabled(),
|
|
345
|
-
ApolloServerPluginLandingPageLocalDefault(),
|
|
346
|
-
],
|
|
347
|
-
});
|
|
348
|
-
server.startInBackgroundHandlingStartupErrorsByLoggingAndFailingAllRequests();
|
|
349
|
-
await this.#waitForServer(server);
|
|
350
|
-
return server;
|
|
351
|
-
}
|
|
352
|
-
async #waitForServer(server) {
|
|
353
|
-
try {
|
|
354
|
-
server.assertStarted("waitForServer");
|
|
355
|
-
return true;
|
|
356
|
-
}
|
|
357
|
-
catch {
|
|
358
|
-
await setTimeout(100);
|
|
359
|
-
return this.#waitForServer(server);
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
343
|
#getSubgraphPath(subgraph, supergraph) {
|
|
363
344
|
return path.join(subgraph.path ?? "", supergraph, subgraph.name);
|
|
364
345
|
}
|
|
@@ -368,36 +349,24 @@ export class GraphQLManager {
|
|
|
368
349
|
this.logger.debug(`Setting up subgraph ${subgraph.name}`);
|
|
369
350
|
const subgraphPath = this.#getSubgraphPath(subgraph, supergraph);
|
|
370
351
|
try {
|
|
371
|
-
//
|
|
372
|
-
//
|
|
373
|
-
//
|
|
374
|
-
//
|
|
375
|
-
//
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
for (const client of this.wsServer.clients) {
|
|
379
|
-
client.close(1001, "Going away");
|
|
380
|
-
}
|
|
381
|
-
this.wsServer.removeAllListeners("connection");
|
|
382
|
-
this.wsServer.removeAllListeners("error");
|
|
383
|
-
this.subgraphWsDisposers.delete(subgraphPath);
|
|
352
|
+
// Skip if handler already cached - subgraphs are deduplicated by name
|
|
353
|
+
// in #addSubgraphInstance, so a cached path means the schema is unchanged.
|
|
354
|
+
// This prevents unbounded schema/server creation across repeated
|
|
355
|
+
// _updateRouter() calls. The handler was already mounted on first setup,
|
|
356
|
+
// so no re-mount is needed.
|
|
357
|
+
if (this.subgraphHandlerCache.has(subgraphPath)) {
|
|
358
|
+
continue;
|
|
384
359
|
}
|
|
385
|
-
// create subgraph schema
|
|
386
360
|
const schema = createSchema(this.cachedDocumentModels, subgraph.resolvers, subgraph.typeDefs);
|
|
387
|
-
|
|
388
|
-
const
|
|
389
|
-
|
|
390
|
-
|
|
361
|
+
const rawHandler = await this.gatewayAdapter.createHandler(schema, this.#makeContextFactory());
|
|
362
|
+
const fetchHandler = this.#authMiddleware
|
|
363
|
+
? this.#authMiddleware(rawHandler)
|
|
364
|
+
: rawHandler;
|
|
365
|
+
this.subgraphHandlerCache.set(subgraphPath, fetchHandler);
|
|
366
|
+
this.httpAdapter.mount(subgraphPath, fetchHandler);
|
|
391
367
|
if (subgraph.hasSubscriptions) {
|
|
392
368
|
try {
|
|
393
|
-
const wsDisposer =
|
|
394
|
-
schema,
|
|
395
|
-
context: async (ctx) => {
|
|
396
|
-
const connectionParams = (ctx.connectionParams ??
|
|
397
|
-
{});
|
|
398
|
-
return this.#createWebSocketContext(connectionParams);
|
|
399
|
-
},
|
|
400
|
-
}, this.wsServer);
|
|
369
|
+
const wsDisposer = this.gatewayAdapter.attachWebSocket(this.wsServer, schema, this.#makeWsContextFactory());
|
|
401
370
|
this.subgraphWsDisposers.set(subgraphPath, wsDisposer);
|
|
402
371
|
this.logger.debug(`WebSocket subscriptions enabled for ${subgraph.name}`);
|
|
403
372
|
}
|
|
@@ -415,7 +384,6 @@ export class GraphQLManager {
|
|
|
415
384
|
this.logger.error("Failed to setup SSE for subgraph @name at path @path: @error", subgraph.name, subgraphPath, error);
|
|
416
385
|
}
|
|
417
386
|
}
|
|
418
|
-
this.#setupApolloExpressMiddleware(server, subgraphPath);
|
|
419
387
|
}
|
|
420
388
|
catch (error) {
|
|
421
389
|
this.logger.error("Failed to setup subgraph @name at path @path: @error", subgraph.name, subgraphPath, error);
|
|
@@ -433,91 +401,33 @@ export class GraphQLManager {
|
|
|
433
401
|
continue;
|
|
434
402
|
}
|
|
435
403
|
for (const subgraph of subgraphs) {
|
|
436
|
-
const
|
|
437
|
-
subgraphsMap.set(
|
|
404
|
+
const subgraphPath = this.#getSubgraphPath(subgraph, supergraph);
|
|
405
|
+
subgraphsMap.set(subgraphPath, subgraph);
|
|
438
406
|
}
|
|
439
407
|
}
|
|
440
408
|
return subgraphsMap;
|
|
441
409
|
}
|
|
442
|
-
/**
|
|
443
|
-
* Setup REST GET endpoint for drive info at /d/:drive
|
|
444
|
-
* Accepts both drive slug (e.g., "powerhouse") and UUID
|
|
445
|
-
* Returns DriveInfo JSON: { id, name, slug, icon, meta, graphqlEndpoint }
|
|
446
|
-
*/
|
|
447
|
-
#setupDriveInfoRestEndpoint(router) {
|
|
448
|
-
const routePath = path.join(this.path, "d/:drive");
|
|
449
|
-
router.get(routePath, (req, res) => {
|
|
450
|
-
const driveIdOrSlug = req.params.drive;
|
|
451
|
-
if (!driveIdOrSlug) {
|
|
452
|
-
res.status(400).json({ error: "Drive ID or slug is required" });
|
|
453
|
-
return;
|
|
454
|
-
}
|
|
455
|
-
(async () => {
|
|
456
|
-
const driveDoc = await this.reactorClient.get(driveIdOrSlug);
|
|
457
|
-
// Construct the graphqlEndpoint from the request
|
|
458
|
-
// Use X-Forwarded-Proto header when behind a reverse proxy (Heroku, Traefik, etc.)
|
|
459
|
-
const forwardedProto = req.get("x-forwarded-proto");
|
|
460
|
-
const protocol = (forwardedProto ?? req.protocol) + ":";
|
|
461
|
-
const host = req.get("host") ?? "";
|
|
462
|
-
const basePath = this.path === "/" ? "" : this.path;
|
|
463
|
-
const graphqlEndpoint = `${protocol}//${host}${basePath}/graphql/r`;
|
|
464
|
-
const driveInfo = responseForDrive(driveDoc, graphqlEndpoint);
|
|
465
|
-
res.json(driveInfo);
|
|
466
|
-
})().catch((error) => {
|
|
467
|
-
this.logger.debug(`Drive not found: ${driveIdOrSlug}`, error);
|
|
468
|
-
res.status(404).json({ error: "Drive not found" });
|
|
469
|
-
});
|
|
470
|
-
});
|
|
471
|
-
this.logger.info(`Registered REST endpoint: GET ${routePath}`);
|
|
472
|
-
}
|
|
473
410
|
#buildSubgraphSchemaModule(subgraph) {
|
|
474
411
|
return buildSubgraphSchemaModule(this.cachedDocumentModels, subgraph.resolvers, subgraph.typeDefs);
|
|
475
412
|
}
|
|
476
|
-
|
|
477
|
-
if (!this.gatewayOptions) {
|
|
478
|
-
throw new Error("Gateway is not ready");
|
|
479
|
-
}
|
|
413
|
+
#getSubgraphDefinitions() {
|
|
480
414
|
const subgraphs = this.#getAllSubgraphs();
|
|
481
415
|
const herokuOrLocal = process.env.HEROKU_APP_DEFAULT_DOMAIN_NAME
|
|
482
416
|
? `https://${process.env.HEROKU_APP_DEFAULT_DOMAIN_NAME}`
|
|
483
417
|
: `http://localhost:${this.port}`;
|
|
484
|
-
|
|
485
|
-
name:
|
|
418
|
+
return Array.from(subgraphs.entries()).map(([subgraphPath, subgraph]) => ({
|
|
419
|
+
name: subgraphPath.replace("/", ":"),
|
|
486
420
|
typeDefs: this.#buildSubgraphSchemaModule(subgraph).typeDefs,
|
|
487
|
-
url: `${herokuOrLocal}${
|
|
421
|
+
url: `${herokuOrLocal}${subgraphPath}`,
|
|
488
422
|
}));
|
|
489
|
-
const localCompose = new LocalCompose({
|
|
490
|
-
localServiceList: serviceList,
|
|
491
|
-
});
|
|
492
|
-
return await localCompose.initialize(this.gatewayOptions);
|
|
493
423
|
}
|
|
494
|
-
async #
|
|
495
|
-
const gateway = new ApolloGateway({
|
|
496
|
-
supergraphSdl: async (options) => {
|
|
497
|
-
this.gatewayOptions = options;
|
|
498
|
-
return await this.#buildSupergrahSdl();
|
|
499
|
-
},
|
|
500
|
-
buildService: (serviceConfig) => {
|
|
501
|
-
return new AuthenticatedDataSource(serviceConfig);
|
|
502
|
-
},
|
|
503
|
-
});
|
|
504
|
-
if (this.coreApolloServer) {
|
|
505
|
-
throw new Error("Supergrah server is already running");
|
|
506
|
-
}
|
|
507
|
-
this.coreApolloServer = new ApolloServer({
|
|
508
|
-
gateway,
|
|
509
|
-
logger: this.apolloLogger,
|
|
510
|
-
introspection: true,
|
|
511
|
-
plugins: [
|
|
512
|
-
ApolloServerPluginDrainHttpServer({ httpServer: this.httpServer }),
|
|
513
|
-
ApolloServerPluginInlineTraceDisabled(),
|
|
514
|
-
ApolloServerPluginLandingPageLocalDefault(),
|
|
515
|
-
],
|
|
516
|
-
});
|
|
517
|
-
await this.coreApolloServer.start();
|
|
518
|
-
await this.#waitForServer(this.coreApolloServer);
|
|
424
|
+
async #createSupergraphGateway() {
|
|
519
425
|
const superGraphPath = path.join(this.path, "graphql");
|
|
520
|
-
this.#
|
|
426
|
+
const rawHandler = await this.gatewayAdapter.createSupergraphHandler(() => this.#getSubgraphDefinitions(), this.httpServer, this.#makeContextFactory());
|
|
427
|
+
const fetchHandler = this.#authMiddleware
|
|
428
|
+
? this.#authMiddleware(rawHandler)
|
|
429
|
+
: rawHandler;
|
|
430
|
+
this.httpAdapter.mount(superGraphPath, fetchHandler);
|
|
521
431
|
// Set up SSE subscriptions at the supergraph level (/graphql/stream).
|
|
522
432
|
// Build a subscription schema from all subgraphs that define subscriptions.
|
|
523
433
|
this.#setupSupergraphSSE(superGraphPath);
|
|
@@ -550,19 +460,6 @@ export class GraphQLManager {
|
|
|
550
460
|
this.logger.error("Failed to setup supergraph SSE: @error", error);
|
|
551
461
|
}
|
|
552
462
|
}
|
|
553
|
-
#setupApolloExpressMiddleware(server, path) {
|
|
554
|
-
this.subgraphHandlers.set(path, {
|
|
555
|
-
handler: expressMiddleware(server, {
|
|
556
|
-
context: ({ req }) => Promise.resolve({
|
|
557
|
-
headers: req.headers,
|
|
558
|
-
driveId: req.params.drive ?? undefined,
|
|
559
|
-
db: this.relationalDb,
|
|
560
|
-
...this.getAdditionalContextFields(),
|
|
561
|
-
}),
|
|
562
|
-
}),
|
|
563
|
-
matcher: match(path),
|
|
564
|
-
});
|
|
565
|
-
}
|
|
566
463
|
/**
|
|
567
464
|
* Set up a GraphQL-over-SSE handler at `<basePath>/stream`.
|
|
568
465
|
*
|
|
@@ -582,10 +479,9 @@ export class GraphQLManager {
|
|
|
582
479
|
...this.getAdditionalContextFields(),
|
|
583
480
|
}),
|
|
584
481
|
});
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
});
|
|
482
|
+
// SSE handler is Express-specific; mount directly on the underlying app.
|
|
483
|
+
// TODO: replace sse.ts with a Fetch API handler so this can use httpAdapter.mount()
|
|
484
|
+
this.httpAdapter.handle.use(ssePath, sseHandler);
|
|
589
485
|
}
|
|
590
486
|
}
|
|
591
487
|
//# sourceMappingURL=graphql-manager.js.map
|