@constructive-io/graphql-server 2.19.1 → 2.19.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/codegen/orm/index.d.ts +7 -603
- package/codegen/orm/index.js +4 -127
- package/codegen/orm/input-types.d.ts +14682 -7790
- package/codegen/orm/input-types.js +0 -5
- package/codegen/orm/models/api.d.ts +6 -6
- package/codegen/orm/models/api.js +3 -4
- package/codegen/orm/models/domain.d.ts +6 -6
- package/codegen/orm/models/domain.js +3 -4
- package/codegen/orm/models/index.d.ts +1 -58
- package/codegen/orm/models/index.js +3 -118
- package/codegen/orm/mutation/index.d.ts +1 -525
- package/codegen/orm/mutation/index.js +2 -591
- package/codegen/orm/query/index.d.ts +3 -257
- package/codegen/orm/query/index.js +8 -274
- package/codegen/orm/query-builder.d.ts +2 -1
- package/codegen/orm/query-builder.js +376 -129
- package/codegen/orm/select-types.d.ts +33 -0
- package/esm/codegen/orm/index.js +4 -127
- package/esm/codegen/orm/input-types.js +0 -5
- package/esm/codegen/orm/models/api.js +3 -4
- package/esm/codegen/orm/models/domain.js +3 -4
- package/esm/codegen/orm/models/index.js +1 -58
- package/esm/codegen/orm/mutation/index.js +2 -591
- package/esm/codegen/orm/query/index.js +8 -274
- package/esm/codegen/orm/query-builder.js +343 -129
- package/esm/middleware/api.js +145 -173
- package/esm/middleware/gql.js +72 -71
- package/middleware/api.d.ts +16 -3
- package/middleware/api.js +148 -173
- package/middleware/gql.d.ts +174 -24
- package/middleware/gql.js +76 -76
- package/middleware/types.d.ts +3 -19
- package/package.json +7 -4
- package/types.d.ts +17 -44
- package/codegen/orm/models/apiExtension.d.ts +0 -42
- package/codegen/orm/models/apiExtension.js +0 -77
- package/codegen/orm/models/apiModule.d.ts +0 -42
- package/codegen/orm/models/apiModule.js +0 -77
- package/codegen/orm/models/apiSchema.d.ts +0 -42
- package/codegen/orm/models/apiSchema.js +0 -77
- package/codegen/orm/models/app.d.ts +0 -42
- package/codegen/orm/models/app.js +0 -77
- package/codegen/orm/models/checkConstraint.d.ts +0 -42
- package/codegen/orm/models/checkConstraint.js +0 -77
- package/codegen/orm/models/connectedAccountsModule.d.ts +0 -42
- package/codegen/orm/models/connectedAccountsModule.js +0 -77
- package/codegen/orm/models/cryptoAddressesModule.d.ts +0 -42
- package/codegen/orm/models/cryptoAddressesModule.js +0 -77
- package/codegen/orm/models/cryptoAuthModule.d.ts +0 -42
- package/codegen/orm/models/cryptoAuthModule.js +0 -77
- package/codegen/orm/models/database.d.ts +0 -42
- package/codegen/orm/models/database.js +0 -77
- package/codegen/orm/models/databaseExtension.d.ts +0 -42
- package/codegen/orm/models/databaseExtension.js +0 -77
- package/codegen/orm/models/databaseProvision.d.ts +0 -42
- package/codegen/orm/models/databaseProvision.js +0 -77
- package/codegen/orm/models/defaultIdsModule.d.ts +0 -42
- package/codegen/orm/models/defaultIdsModule.js +0 -77
- package/codegen/orm/models/denormalizedTableField.d.ts +0 -42
- package/codegen/orm/models/denormalizedTableField.js +0 -77
- package/codegen/orm/models/emailsModule.d.ts +0 -42
- package/codegen/orm/models/emailsModule.js +0 -77
- package/codegen/orm/models/encryptedSecretsModule.d.ts +0 -42
- package/codegen/orm/models/encryptedSecretsModule.js +0 -77
- package/codegen/orm/models/extension.d.ts +0 -42
- package/codegen/orm/models/extension.js +0 -77
- package/codegen/orm/models/field.d.ts +0 -42
- package/codegen/orm/models/field.js +0 -77
- package/codegen/orm/models/fieldModule.d.ts +0 -42
- package/codegen/orm/models/fieldModule.js +0 -77
- package/codegen/orm/models/foreignKeyConstraint.d.ts +0 -42
- package/codegen/orm/models/foreignKeyConstraint.js +0 -77
- package/codegen/orm/models/fullTextSearch.d.ts +0 -42
- package/codegen/orm/models/fullTextSearch.js +0 -77
- package/codegen/orm/models/hierarchyModule.d.ts +0 -42
- package/codegen/orm/models/hierarchyModule.js +0 -77
- package/codegen/orm/models/indexModel.d.ts +0 -42
- package/codegen/orm/models/indexModel.js +0 -77
- package/codegen/orm/models/invitesModule.d.ts +0 -42
- package/codegen/orm/models/invitesModule.js +0 -77
- package/codegen/orm/models/levelsModule.d.ts +0 -42
- package/codegen/orm/models/levelsModule.js +0 -77
- package/codegen/orm/models/limitFunction.d.ts +0 -42
- package/codegen/orm/models/limitFunction.js +0 -77
- package/codegen/orm/models/limitsModule.d.ts +0 -42
- package/codegen/orm/models/limitsModule.js +0 -77
- package/codegen/orm/models/membershipTypesModule.d.ts +0 -42
- package/codegen/orm/models/membershipTypesModule.js +0 -77
- package/codegen/orm/models/membershipsModule.d.ts +0 -42
- package/codegen/orm/models/membershipsModule.js +0 -77
- package/codegen/orm/models/module.d.ts +0 -42
- package/codegen/orm/models/module.js +0 -77
- package/codegen/orm/models/moduleDefinition.d.ts +0 -42
- package/codegen/orm/models/moduleDefinition.js +0 -77
- package/codegen/orm/models/moduleField.d.ts +0 -42
- package/codegen/orm/models/moduleField.js +0 -77
- package/codegen/orm/models/moduleInputRecord.d.ts +0 -42
- package/codegen/orm/models/moduleInputRecord.js +0 -77
- package/codegen/orm/models/moduleOutput.d.ts +0 -42
- package/codegen/orm/models/moduleOutput.js +0 -77
- package/codegen/orm/models/permissionsModule.d.ts +0 -42
- package/codegen/orm/models/permissionsModule.js +0 -77
- package/codegen/orm/models/phoneNumbersModule.d.ts +0 -42
- package/codegen/orm/models/phoneNumbersModule.js +0 -77
- package/codegen/orm/models/policy.d.ts +0 -42
- package/codegen/orm/models/policy.js +0 -77
- package/codegen/orm/models/primaryKeyConstraint.d.ts +0 -42
- package/codegen/orm/models/primaryKeyConstraint.js +0 -77
- package/codegen/orm/models/procedure.d.ts +0 -42
- package/codegen/orm/models/procedure.js +0 -77
- package/codegen/orm/models/profilesModule.d.ts +0 -42
- package/codegen/orm/models/profilesModule.js +0 -77
- package/codegen/orm/models/rlsFunction.d.ts +0 -42
- package/codegen/orm/models/rlsFunction.js +0 -77
- package/codegen/orm/models/rlsModule.d.ts +0 -42
- package/codegen/orm/models/rlsModule.js +0 -77
- package/codegen/orm/models/schema.d.ts +0 -42
- package/codegen/orm/models/schema.js +0 -77
- package/codegen/orm/models/schemaGrant.d.ts +0 -42
- package/codegen/orm/models/schemaGrant.js +0 -77
- package/codegen/orm/models/secretsModule.d.ts +0 -42
- package/codegen/orm/models/secretsModule.js +0 -77
- package/codegen/orm/models/site.d.ts +0 -42
- package/codegen/orm/models/site.js +0 -77
- package/codegen/orm/models/siteMetadatum.d.ts +0 -42
- package/codegen/orm/models/siteMetadatum.js +0 -77
- package/codegen/orm/models/siteModule.d.ts +0 -42
- package/codegen/orm/models/siteModule.js +0 -77
- package/codegen/orm/models/siteTheme.d.ts +0 -42
- package/codegen/orm/models/siteTheme.js +0 -77
- package/codegen/orm/models/table.d.ts +0 -42
- package/codegen/orm/models/table.js +0 -77
- package/codegen/orm/models/tableGrant.d.ts +0 -42
- package/codegen/orm/models/tableGrant.js +0 -77
- package/codegen/orm/models/tokensModule.d.ts +0 -42
- package/codegen/orm/models/tokensModule.js +0 -77
- package/codegen/orm/models/trigger.d.ts +0 -42
- package/codegen/orm/models/trigger.js +0 -77
- package/codegen/orm/models/triggerFunction.d.ts +0 -42
- package/codegen/orm/models/triggerFunction.js +0 -77
- package/codegen/orm/models/uniqueConstraint.d.ts +0 -42
- package/codegen/orm/models/uniqueConstraint.js +0 -77
- package/codegen/orm/models/userAuthModule.d.ts +0 -42
- package/codegen/orm/models/userAuthModule.js +0 -77
- package/codegen/orm/models/usersModule.d.ts +0 -42
- package/codegen/orm/models/usersModule.js +0 -77
- package/codegen/orm/models/uuidModule.d.ts +0 -42
- package/codegen/orm/models/uuidModule.js +0 -77
- package/esm/codegen/orm/models/apiExtension.js +0 -73
- package/esm/codegen/orm/models/apiModule.js +0 -73
- package/esm/codegen/orm/models/apiSchema.js +0 -73
- package/esm/codegen/orm/models/app.js +0 -73
- package/esm/codegen/orm/models/checkConstraint.js +0 -73
- package/esm/codegen/orm/models/connectedAccountsModule.js +0 -73
- package/esm/codegen/orm/models/cryptoAddressesModule.js +0 -73
- package/esm/codegen/orm/models/cryptoAuthModule.js +0 -73
- package/esm/codegen/orm/models/database.js +0 -73
- package/esm/codegen/orm/models/databaseExtension.js +0 -73
- package/esm/codegen/orm/models/databaseProvision.js +0 -73
- package/esm/codegen/orm/models/defaultIdsModule.js +0 -73
- package/esm/codegen/orm/models/denormalizedTableField.js +0 -73
- package/esm/codegen/orm/models/emailsModule.js +0 -73
- package/esm/codegen/orm/models/encryptedSecretsModule.js +0 -73
- package/esm/codegen/orm/models/extension.js +0 -73
- package/esm/codegen/orm/models/field.js +0 -73
- package/esm/codegen/orm/models/fieldModule.js +0 -73
- package/esm/codegen/orm/models/foreignKeyConstraint.js +0 -73
- package/esm/codegen/orm/models/fullTextSearch.js +0 -73
- package/esm/codegen/orm/models/hierarchyModule.js +0 -73
- package/esm/codegen/orm/models/indexModel.js +0 -73
- package/esm/codegen/orm/models/invitesModule.js +0 -73
- package/esm/codegen/orm/models/levelsModule.js +0 -73
- package/esm/codegen/orm/models/limitFunction.js +0 -73
- package/esm/codegen/orm/models/limitsModule.js +0 -73
- package/esm/codegen/orm/models/membershipTypesModule.js +0 -73
- package/esm/codegen/orm/models/membershipsModule.js +0 -73
- package/esm/codegen/orm/models/module.js +0 -73
- package/esm/codegen/orm/models/moduleDefinition.js +0 -73
- package/esm/codegen/orm/models/moduleField.js +0 -73
- package/esm/codegen/orm/models/moduleInputRecord.js +0 -73
- package/esm/codegen/orm/models/moduleOutput.js +0 -73
- package/esm/codegen/orm/models/permissionsModule.js +0 -73
- package/esm/codegen/orm/models/phoneNumbersModule.js +0 -73
- package/esm/codegen/orm/models/policy.js +0 -73
- package/esm/codegen/orm/models/primaryKeyConstraint.js +0 -73
- package/esm/codegen/orm/models/procedure.js +0 -73
- package/esm/codegen/orm/models/profilesModule.js +0 -73
- package/esm/codegen/orm/models/rlsFunction.js +0 -73
- package/esm/codegen/orm/models/rlsModule.js +0 -73
- package/esm/codegen/orm/models/schema.js +0 -73
- package/esm/codegen/orm/models/schemaGrant.js +0 -73
- package/esm/codegen/orm/models/secretsModule.js +0 -73
- package/esm/codegen/orm/models/site.js +0 -73
- package/esm/codegen/orm/models/siteMetadatum.js +0 -73
- package/esm/codegen/orm/models/siteModule.js +0 -73
- package/esm/codegen/orm/models/siteTheme.js +0 -73
- package/esm/codegen/orm/models/table.js +0 -73
- package/esm/codegen/orm/models/tableGrant.js +0 -73
- package/esm/codegen/orm/models/tokensModule.js +0 -73
- package/esm/codegen/orm/models/trigger.js +0 -73
- package/esm/codegen/orm/models/triggerFunction.js +0 -73
- package/esm/codegen/orm/models/uniqueConstraint.js +0 -73
- package/esm/codegen/orm/models/userAuthModule.js +0 -73
- package/esm/codegen/orm/models/usersModule.js +0 -73
- package/esm/codegen/orm/models/uuidModule.js +0 -73
package/middleware/api.js
CHANGED
|
@@ -3,54 +3,26 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.getApiConfig = exports.createApiMiddleware = exports.getSubdomain = void 0;
|
|
6
|
+
exports.getApiConfig = exports.getSvcKey = exports.queryServiceByApiName = exports.createApiMiddleware = exports.getSubdomain = exports.normalizeApiRecord = void 0;
|
|
7
7
|
const graphql_env_1 = require("@constructive-io/graphql-env");
|
|
8
8
|
const logger_1 = require("@pgpmjs/logger");
|
|
9
9
|
const server_utils_1 = require("@pgpmjs/server-utils");
|
|
10
|
+
const url_domains_1 = require("@constructive-io/url-domains");
|
|
10
11
|
const graphile_query_1 = require("graphile-query");
|
|
11
12
|
const graphile_settings_1 = require("graphile-settings");
|
|
12
13
|
const pg_cache_1 = require("pg-cache");
|
|
13
14
|
const _50x_1 = __importDefault(require("../errors/50x"));
|
|
14
15
|
const _404_message_1 = __importDefault(require("../errors/404-message"));
|
|
16
|
+
/**
|
|
17
|
+
* Normalizes API records into ApiStructure
|
|
18
|
+
*/
|
|
15
19
|
const gql_1 = require("./gql");
|
|
16
20
|
require("./types"); // for Request type
|
|
21
|
+
var gql_2 = require("./gql");
|
|
22
|
+
Object.defineProperty(exports, "normalizeApiRecord", { enumerable: true, get: function () { return gql_2.normalizeApiRecord; } });
|
|
17
23
|
const log = new logger_1.Logger('api');
|
|
18
24
|
const isDev = () => (0, graphql_env_1.getNodeEnv)() === 'development';
|
|
19
|
-
const
|
|
20
|
-
const api = svc.data.api;
|
|
21
|
-
const schemaNames = api.apiExtensions?.nodes?.map((n) => n.schemaName) || [];
|
|
22
|
-
const additionalSchemas = api.schemasByApiSchemaApiIdAndSchemaId?.nodes?.map((n) => n.schemaName) || [];
|
|
23
|
-
let domains = [];
|
|
24
|
-
if (api.database?.sites?.nodes) {
|
|
25
|
-
domains = api.database.sites.nodes.reduce((acc, site) => {
|
|
26
|
-
if (site.domains?.nodes && site.domains.nodes.length) {
|
|
27
|
-
const siteUrls = site.domains.nodes.map((domain) => {
|
|
28
|
-
const hostname = domain.subdomain
|
|
29
|
-
? `${domain.subdomain}.${domain.domain}`
|
|
30
|
-
: domain.domain;
|
|
31
|
-
const protocol = domain.domain === 'localhost' ? 'http://' : 'https://';
|
|
32
|
-
return protocol + hostname;
|
|
33
|
-
});
|
|
34
|
-
return [...acc, ...siteUrls];
|
|
35
|
-
}
|
|
36
|
-
return acc;
|
|
37
|
-
}, []);
|
|
38
|
-
}
|
|
39
|
-
return {
|
|
40
|
-
dbname: api.dbname,
|
|
41
|
-
anonRole: api.anonRole,
|
|
42
|
-
roleName: api.roleName,
|
|
43
|
-
schema: [...schemaNames, ...additionalSchemas],
|
|
44
|
-
apiModules: api.apiModules?.nodes?.map((node) => ({
|
|
45
|
-
name: node.name,
|
|
46
|
-
data: node.data,
|
|
47
|
-
})) || [],
|
|
48
|
-
rlsModule: api.rlsModule,
|
|
49
|
-
domains,
|
|
50
|
-
databaseId: api.databaseId,
|
|
51
|
-
isPublic: api.isPublic,
|
|
52
|
-
};
|
|
53
|
-
};
|
|
25
|
+
const isApiError = (svc) => !!svc && typeof svc.errorHtml === 'string';
|
|
54
26
|
const getPortFromRequest = (req) => {
|
|
55
27
|
const host = req.headers.host;
|
|
56
28
|
if (!host)
|
|
@@ -58,6 +30,18 @@ const getPortFromRequest = (req) => {
|
|
|
58
30
|
const parts = host.split(':');
|
|
59
31
|
return parts.length === 2 ? `:${parts[1]}` : null;
|
|
60
32
|
};
|
|
33
|
+
const parseCommaSeparatedHeader = (value) => value
|
|
34
|
+
.split(',')
|
|
35
|
+
.map((item) => item.trim())
|
|
36
|
+
.filter((item) => item.length > 0);
|
|
37
|
+
const getUrlDomains = (req) => {
|
|
38
|
+
const fullUrl = `${req.protocol}://${req.get('host')}${req.originalUrl}`;
|
|
39
|
+
const parsed = (0, url_domains_1.parseUrl)(fullUrl);
|
|
40
|
+
return {
|
|
41
|
+
domain: parsed.domain ?? '',
|
|
42
|
+
subdomains: parsed.subdomains ?? [],
|
|
43
|
+
};
|
|
44
|
+
};
|
|
61
45
|
const getSubdomain = (reqDomains) => {
|
|
62
46
|
const names = reqDomains.filter((name) => !['www'].includes(name));
|
|
63
47
|
return !names.length ? null : names.join('.');
|
|
@@ -66,9 +50,9 @@ exports.getSubdomain = getSubdomain;
|
|
|
66
50
|
const createApiMiddleware = (opts) => {
|
|
67
51
|
return async (req, res, next) => {
|
|
68
52
|
if (opts.api?.enableServicesApi === false) {
|
|
69
|
-
const schemas = opts.api.exposedSchemas;
|
|
70
|
-
const anonRole = opts.api.anonRole;
|
|
71
|
-
const roleName = opts.api.roleName;
|
|
53
|
+
const schemas = opts.api.exposedSchemas ?? [];
|
|
54
|
+
const anonRole = opts.api.anonRole ?? '';
|
|
55
|
+
const roleName = opts.api.roleName ?? '';
|
|
72
56
|
const databaseId = opts.api.defaultDatabaseId;
|
|
73
57
|
const api = {
|
|
74
58
|
dbname: opts.pg?.database ?? '',
|
|
@@ -86,134 +70,103 @@ const createApiMiddleware = (opts) => {
|
|
|
86
70
|
return next();
|
|
87
71
|
}
|
|
88
72
|
try {
|
|
89
|
-
const
|
|
90
|
-
if (
|
|
73
|
+
const apiConfig = await (0, exports.getApiConfig)(opts, req);
|
|
74
|
+
if (isApiError(apiConfig)) {
|
|
91
75
|
res
|
|
92
76
|
.status(404)
|
|
93
|
-
.send((0, _404_message_1.default)('API not found',
|
|
77
|
+
.send((0, _404_message_1.default)('API not found', apiConfig.errorHtml));
|
|
94
78
|
return;
|
|
95
79
|
}
|
|
96
|
-
else if (!
|
|
80
|
+
else if (!apiConfig) {
|
|
97
81
|
res
|
|
98
82
|
.status(404)
|
|
99
83
|
.send((0, _404_message_1.default)('API service not found for the given domain/subdomain.'));
|
|
100
84
|
return;
|
|
101
85
|
}
|
|
102
|
-
|
|
103
|
-
req.
|
|
104
|
-
req.databaseId = api.databaseId;
|
|
86
|
+
req.api = apiConfig;
|
|
87
|
+
req.databaseId = apiConfig.databaseId;
|
|
105
88
|
if (isDev())
|
|
106
|
-
log.debug(`Resolved API: db=${
|
|
89
|
+
log.debug(`Resolved API: db=${apiConfig.dbname}, schemas=[${apiConfig.schema?.join(', ')}]`);
|
|
107
90
|
next();
|
|
108
91
|
}
|
|
109
|
-
catch (
|
|
110
|
-
|
|
111
|
-
|
|
92
|
+
catch (error) {
|
|
93
|
+
const err = error;
|
|
94
|
+
if (err.code === 'NO_VALID_SCHEMAS') {
|
|
95
|
+
res.status(404).send((0, _404_message_1.default)(err.message));
|
|
112
96
|
}
|
|
113
|
-
else if (
|
|
97
|
+
else if (err.message?.match(/does not exist/)) {
|
|
114
98
|
res
|
|
115
99
|
.status(404)
|
|
116
100
|
.send((0, _404_message_1.default)("The resource you're looking for does not exist."));
|
|
117
101
|
}
|
|
118
102
|
else {
|
|
119
|
-
log.error('API middleware error:',
|
|
103
|
+
log.error('API middleware error:', err);
|
|
120
104
|
res.status(500).send(_50x_1.default);
|
|
121
105
|
}
|
|
122
106
|
}
|
|
123
107
|
};
|
|
124
108
|
};
|
|
125
109
|
exports.createApiMiddleware = createApiMiddleware;
|
|
126
|
-
const
|
|
127
|
-
const
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
nodes: schemata
|
|
137
|
-
.split(',')
|
|
138
|
-
.map((schema) => schema.trim())
|
|
139
|
-
.map((schemaName) => ({ schemaName })),
|
|
140
|
-
},
|
|
141
|
-
schemaNames: { nodes: [] },
|
|
142
|
-
apiModules: [],
|
|
143
|
-
},
|
|
144
|
-
},
|
|
110
|
+
const createAdminApiStructure = ({ opts, schemata, key, databaseId, }) => {
|
|
111
|
+
const api = {
|
|
112
|
+
dbname: opts.pg?.database ?? '',
|
|
113
|
+
anonRole: 'administrator',
|
|
114
|
+
roleName: 'administrator',
|
|
115
|
+
schema: schemata,
|
|
116
|
+
apiModules: [],
|
|
117
|
+
domains: [],
|
|
118
|
+
databaseId,
|
|
119
|
+
isPublic: false,
|
|
145
120
|
};
|
|
146
|
-
server_utils_1.svcCache.set(key,
|
|
147
|
-
return
|
|
121
|
+
server_utils_1.svcCache.set(key, api);
|
|
122
|
+
return api;
|
|
148
123
|
};
|
|
149
|
-
const
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
databaseId,
|
|
156
|
-
isPublic: false,
|
|
157
|
-
dbname: opts.pg.database,
|
|
158
|
-
anonRole: 'administrator',
|
|
159
|
-
roleName: 'administrator',
|
|
160
|
-
schemaNamesFromExt: {
|
|
161
|
-
nodes: schemata.map((schemaName) => ({ schemaName })),
|
|
162
|
-
},
|
|
163
|
-
schemaNames: { nodes: [] },
|
|
164
|
-
apiModules: [],
|
|
165
|
-
},
|
|
166
|
-
},
|
|
124
|
+
const queryServiceByDomainAndSubdomain = async ({ opts, key, domainModel, domain, subdomain, }) => {
|
|
125
|
+
const where = {
|
|
126
|
+
domain: { equalTo: domain },
|
|
127
|
+
subdomain: subdomain === null || subdomain === undefined
|
|
128
|
+
? { isNull: true }
|
|
129
|
+
: { equalTo: subdomain },
|
|
167
130
|
};
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
const doc = (0, gql_1.buildDomainLookup)({ domain, subdomain });
|
|
173
|
-
const result = await client.query({
|
|
174
|
-
role: 'administrator',
|
|
175
|
-
query: doc.document,
|
|
176
|
-
variables: doc.variables,
|
|
177
|
-
});
|
|
178
|
-
if (result.errors?.length) {
|
|
131
|
+
const result = await domainModel
|
|
132
|
+
.findFirst({ select: gql_1.domainSelect, where })
|
|
133
|
+
.execute();
|
|
134
|
+
if (!result.ok) {
|
|
179
135
|
log.error('GraphQL query errors:', result.errors);
|
|
180
136
|
return null;
|
|
181
137
|
}
|
|
182
|
-
const
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
return svc;
|
|
191
|
-
}
|
|
192
|
-
return null;
|
|
138
|
+
const domainRecord = result.data.domains.nodes[0];
|
|
139
|
+
const api = domainRecord?.api;
|
|
140
|
+
const apiPublic = opts.api?.isPublic;
|
|
141
|
+
if (!api || api.isPublic !== apiPublic)
|
|
142
|
+
return null;
|
|
143
|
+
const apiStructure = (0, gql_1.normalizeApiRecord)(api);
|
|
144
|
+
server_utils_1.svcCache.set(key, apiStructure);
|
|
145
|
+
return apiStructure;
|
|
193
146
|
};
|
|
194
|
-
const queryServiceByApiName = async ({ opts, key,
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
if (result.errors?.length) {
|
|
147
|
+
const queryServiceByApiName = async ({ opts, key, queryOps, databaseId, name, }) => {
|
|
148
|
+
if (!databaseId)
|
|
149
|
+
return null;
|
|
150
|
+
const result = await queryOps
|
|
151
|
+
.apiByDatabaseIdAndName({ databaseId, name }, { select: gql_1.apiSelect })
|
|
152
|
+
.execute();
|
|
153
|
+
if (!result.ok) {
|
|
202
154
|
log.error('GraphQL query errors:', result.errors);
|
|
203
155
|
return null;
|
|
204
156
|
}
|
|
205
|
-
const
|
|
157
|
+
const api = result.data.apiByDatabaseIdAndName;
|
|
206
158
|
const apiPublic = opts.api?.isPublic;
|
|
207
|
-
if (
|
|
208
|
-
const
|
|
209
|
-
server_utils_1.svcCache.set(key,
|
|
210
|
-
return
|
|
159
|
+
if (api && api.isPublic === apiPublic) {
|
|
160
|
+
const apiStructure = (0, gql_1.normalizeApiRecord)(api);
|
|
161
|
+
server_utils_1.svcCache.set(key, apiStructure);
|
|
162
|
+
return apiStructure;
|
|
211
163
|
}
|
|
212
164
|
return null;
|
|
213
165
|
};
|
|
166
|
+
exports.queryServiceByApiName = queryServiceByApiName;
|
|
214
167
|
const getSvcKey = (opts, req) => {
|
|
215
|
-
const domain = req
|
|
216
|
-
const key =
|
|
168
|
+
const { domain, subdomains } = getUrlDomains(req);
|
|
169
|
+
const key = subdomains
|
|
217
170
|
.filter((name) => !['www'].includes(name))
|
|
218
171
|
.concat(domain)
|
|
219
172
|
.join('.');
|
|
@@ -231,101 +184,125 @@ const getSvcKey = (opts, req) => {
|
|
|
231
184
|
}
|
|
232
185
|
return key;
|
|
233
186
|
};
|
|
187
|
+
exports.getSvcKey = getSvcKey;
|
|
234
188
|
const validateSchemata = async (pool, schemata) => {
|
|
235
189
|
const result = await pool.query(`SELECT schema_name FROM information_schema.schemata WHERE schema_name = ANY($1::text[])`, [schemata]);
|
|
236
190
|
return result.rows.map((row) => row.schema_name);
|
|
237
191
|
};
|
|
238
192
|
const getApiConfig = async (opts, req) => {
|
|
239
193
|
const rootPgPool = (0, pg_cache_1.getPgPool)(opts.pg);
|
|
240
|
-
|
|
241
|
-
const subdomain = (0, exports.getSubdomain)(
|
|
242
|
-
const
|
|
243
|
-
const key = getSvcKey(opts, req);
|
|
194
|
+
const { domain, subdomains } = getUrlDomains(req);
|
|
195
|
+
const subdomain = (0, exports.getSubdomain)(subdomains);
|
|
196
|
+
const key = (0, exports.getSvcKey)(opts, req);
|
|
244
197
|
req.svc_key = key;
|
|
245
|
-
let
|
|
198
|
+
let apiConfig;
|
|
246
199
|
if (server_utils_1.svcCache.has(key)) {
|
|
247
200
|
if (isDev())
|
|
248
201
|
log.debug(`Cache HIT for key=${key}`);
|
|
249
|
-
|
|
202
|
+
apiConfig = server_utils_1.svcCache.get(key);
|
|
250
203
|
}
|
|
251
204
|
else {
|
|
252
205
|
if (isDev())
|
|
253
206
|
log.debug(`Cache MISS for key=${key}, looking up API`);
|
|
254
207
|
const apiOpts = opts.api || {};
|
|
255
|
-
const
|
|
256
|
-
const
|
|
208
|
+
const apiPublic = apiOpts.isPublic;
|
|
209
|
+
const schemataHeader = req.get('X-Schemata');
|
|
210
|
+
const apiNameHeader = req.get('X-Api-Name');
|
|
211
|
+
const metaSchemaHeader = req.get('X-Meta-Schema');
|
|
212
|
+
const databaseIdHeader = req.get('X-Database-Id');
|
|
213
|
+
const headerSchemata = schemataHeader
|
|
214
|
+
? parseCommaSeparatedHeader(schemataHeader)
|
|
215
|
+
: [];
|
|
216
|
+
const candidateSchemata = apiPublic === false && headerSchemata.length
|
|
217
|
+
? Array.from(new Set([...(apiOpts.metaSchemas || []), ...headerSchemata]))
|
|
218
|
+
: apiOpts.metaSchemas || [];
|
|
219
|
+
const validatedSchemata = await validateSchemata(rootPgPool, candidateSchemata);
|
|
257
220
|
if (validatedSchemata.length === 0) {
|
|
258
|
-
const
|
|
259
|
-
|
|
221
|
+
const schemaSource = headerSchemata.length
|
|
222
|
+
? headerSchemata
|
|
223
|
+
: apiOpts.metaSchemas || [];
|
|
224
|
+
const label = headerSchemata.length ? 'X-Schemata' : 'metaSchemas';
|
|
225
|
+
const message = `No valid schemas found. Configured ${label}: [${schemaSource.join(', ')}]`;
|
|
260
226
|
if (isDev())
|
|
261
227
|
log.debug(message);
|
|
262
228
|
const error = new Error(message);
|
|
263
229
|
error.code = 'NO_VALID_SCHEMAS';
|
|
264
230
|
throw error;
|
|
265
231
|
}
|
|
232
|
+
const validSchemaSet = new Set(validatedSchemata);
|
|
233
|
+
const validatedHeaderSchemata = headerSchemata.filter((schemaName) => validSchemaSet.has(schemaName));
|
|
266
234
|
const settings = (0, graphile_settings_1.getGraphileSettings)({
|
|
267
235
|
graphile: {
|
|
268
236
|
schema: validatedSchemata,
|
|
269
237
|
},
|
|
270
238
|
});
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
const
|
|
239
|
+
const graphileSettings = {
|
|
240
|
+
...settings,
|
|
241
|
+
schema: validatedSchemata,
|
|
242
|
+
};
|
|
243
|
+
const schema = await (0, graphile_query_1.getSchema)(rootPgPool, graphileSettings);
|
|
244
|
+
const graphileClient = new graphile_query_1.GraphileQuery({
|
|
245
|
+
schema,
|
|
246
|
+
pool: rootPgPool,
|
|
247
|
+
settings: graphileSettings,
|
|
248
|
+
});
|
|
249
|
+
const orm = (0, gql_1.createGraphileOrm)(graphileClient);
|
|
276
250
|
if (apiPublic === false) {
|
|
277
|
-
if (
|
|
278
|
-
|
|
251
|
+
if (schemataHeader) {
|
|
252
|
+
if (validatedHeaderSchemata.length === 0) {
|
|
253
|
+
return {
|
|
254
|
+
errorHtml: 'No valid schemas found for the supplied X-Schemata header.',
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
apiConfig = createAdminApiStructure({
|
|
279
258
|
opts,
|
|
259
|
+
schemata: validatedHeaderSchemata,
|
|
280
260
|
key,
|
|
281
|
-
|
|
282
|
-
databaseId: req.get('X-Database-Id'),
|
|
261
|
+
databaseId: databaseIdHeader,
|
|
283
262
|
});
|
|
284
263
|
}
|
|
285
|
-
else if (
|
|
286
|
-
|
|
264
|
+
else if (apiNameHeader) {
|
|
265
|
+
apiConfig = await (0, exports.queryServiceByApiName)({
|
|
287
266
|
opts,
|
|
288
267
|
key,
|
|
289
|
-
|
|
290
|
-
name:
|
|
291
|
-
databaseId:
|
|
268
|
+
queryOps: orm.query,
|
|
269
|
+
name: apiNameHeader,
|
|
270
|
+
databaseId: databaseIdHeader,
|
|
292
271
|
});
|
|
293
272
|
}
|
|
294
|
-
else if (
|
|
295
|
-
|
|
273
|
+
else if (metaSchemaHeader) {
|
|
274
|
+
apiConfig = createAdminApiStructure({
|
|
296
275
|
opts,
|
|
276
|
+
schemata: validatedSchemata,
|
|
297
277
|
key,
|
|
298
|
-
databaseId:
|
|
278
|
+
databaseId: databaseIdHeader,
|
|
299
279
|
});
|
|
300
280
|
}
|
|
301
281
|
else {
|
|
302
|
-
|
|
282
|
+
apiConfig = await queryServiceByDomainAndSubdomain({
|
|
303
283
|
opts,
|
|
304
284
|
key,
|
|
305
|
-
|
|
285
|
+
domainModel: orm.domain,
|
|
306
286
|
domain,
|
|
307
287
|
subdomain,
|
|
308
288
|
});
|
|
309
289
|
}
|
|
310
290
|
}
|
|
311
291
|
else {
|
|
312
|
-
|
|
292
|
+
apiConfig = await queryServiceByDomainAndSubdomain({
|
|
313
293
|
opts,
|
|
314
294
|
key,
|
|
315
|
-
|
|
295
|
+
domainModel: orm.domain,
|
|
316
296
|
domain,
|
|
317
297
|
subdomain,
|
|
318
298
|
});
|
|
319
|
-
if (!
|
|
299
|
+
if (!apiConfig) {
|
|
300
|
+
// IMPORTANT NOTE: ONLY DO THIS IN DEV MODE
|
|
320
301
|
if ((0, graphql_env_1.getNodeEnv)() === 'development') {
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
query: (0, gql_1.buildListApis)().document,
|
|
326
|
-
});
|
|
327
|
-
if (!fallbackResult.errors?.length &&
|
|
328
|
-
fallbackResult.data?.apis?.nodes?.length) {
|
|
302
|
+
const fallbackResult = await orm.api
|
|
303
|
+
.findMany({ select: gql_1.apiListSelect, first: gql_1.connectionFirst })
|
|
304
|
+
.execute();
|
|
305
|
+
if (fallbackResult.ok && fallbackResult.data.apis.nodes.length) {
|
|
329
306
|
const port = getPortFromRequest(req);
|
|
330
307
|
const allDomains = fallbackResult.data.apis.nodes.flatMap((api) => api.domains.nodes.map((d) => ({
|
|
331
308
|
domain: d.domain,
|
|
@@ -337,7 +314,7 @@ const getApiConfig = async (opts, req) => {
|
|
|
337
314
|
const linksHtml = allDomains.length
|
|
338
315
|
? `<ul class="mt-4 pl-5 list-disc space-y-1">` +
|
|
339
316
|
allDomains
|
|
340
|
-
.map((
|
|
317
|
+
.map((domainLink) => `<li><a href="${domainLink.href}" class="text-brand hover:underline">${domainLink.href}</a></li>`)
|
|
341
318
|
.join('') +
|
|
342
319
|
`</ul>`
|
|
343
320
|
: `<p class="text-gray-600">No APIs are currently registered for this database.</p>`;
|
|
@@ -347,14 +324,12 @@ const getApiConfig = async (opts, req) => {
|
|
|
347
324
|
${linksHtml}
|
|
348
325
|
</div>
|
|
349
326
|
`.trim();
|
|
350
|
-
return {
|
|
351
|
-
errorHtml,
|
|
352
|
-
};
|
|
327
|
+
return { errorHtml };
|
|
353
328
|
}
|
|
354
329
|
}
|
|
355
330
|
}
|
|
356
331
|
}
|
|
357
332
|
}
|
|
358
|
-
return
|
|
333
|
+
return apiConfig;
|
|
359
334
|
};
|
|
360
335
|
exports.getApiConfig = getApiConfig;
|
package/middleware/gql.d.ts
CHANGED
|
@@ -1,26 +1,176 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { GraphileQuery } from 'graphile-query';
|
|
2
|
+
import { type ApiSelect, type ApiWithRelations, type DomainFilter, type DomainWithRelations } from '../codegen/orm/input-types';
|
|
3
|
+
import { type QueryResult } from '../codegen/orm/client';
|
|
4
|
+
import { ApiModel, DomainModel } from '../codegen/orm/models';
|
|
5
|
+
import type { InferSelectResult } from '../codegen/orm/select-types';
|
|
6
|
+
import { ApiStructure } from '../types';
|
|
7
|
+
export declare const connectionFirst = 1000;
|
|
8
|
+
export declare const apiSelect: {
|
|
9
|
+
databaseId: true;
|
|
10
|
+
dbname: true;
|
|
11
|
+
roleName: true;
|
|
12
|
+
anonRole: true;
|
|
13
|
+
isPublic: true;
|
|
14
|
+
domains: {
|
|
15
|
+
select: {
|
|
16
|
+
subdomain: true;
|
|
17
|
+
domain: true;
|
|
18
|
+
};
|
|
19
|
+
first: number;
|
|
20
|
+
};
|
|
21
|
+
apiExtensions: {
|
|
22
|
+
select: {
|
|
23
|
+
schemaName: true;
|
|
24
|
+
};
|
|
25
|
+
first: number;
|
|
26
|
+
};
|
|
27
|
+
schemasByApiSchemaApiIdAndSchemaId: {
|
|
28
|
+
select: {
|
|
29
|
+
schemaName: true;
|
|
30
|
+
};
|
|
31
|
+
first: number;
|
|
32
|
+
};
|
|
33
|
+
rlsModule: {
|
|
34
|
+
select: {
|
|
35
|
+
privateSchema: {
|
|
36
|
+
select: {
|
|
37
|
+
schemaName: true;
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
authenticateStrict: true;
|
|
41
|
+
authenticate: true;
|
|
42
|
+
currentRole: true;
|
|
43
|
+
currentRoleId: true;
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
apiModules: {
|
|
47
|
+
select: {
|
|
48
|
+
name: true;
|
|
49
|
+
data: true;
|
|
50
|
+
};
|
|
51
|
+
first: number;
|
|
52
|
+
};
|
|
4
53
|
};
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
54
|
+
export declare const domainSelect: {
|
|
55
|
+
domain: true;
|
|
56
|
+
subdomain: true;
|
|
57
|
+
api: {
|
|
58
|
+
select: {
|
|
59
|
+
databaseId: true;
|
|
60
|
+
dbname: true;
|
|
61
|
+
roleName: true;
|
|
62
|
+
anonRole: true;
|
|
63
|
+
isPublic: true;
|
|
64
|
+
domains: {
|
|
65
|
+
select: {
|
|
66
|
+
subdomain: true;
|
|
67
|
+
domain: true;
|
|
68
|
+
};
|
|
69
|
+
first: number;
|
|
70
|
+
};
|
|
71
|
+
apiExtensions: {
|
|
72
|
+
select: {
|
|
73
|
+
schemaName: true;
|
|
74
|
+
};
|
|
75
|
+
first: number;
|
|
76
|
+
};
|
|
77
|
+
schemasByApiSchemaApiIdAndSchemaId: {
|
|
78
|
+
select: {
|
|
79
|
+
schemaName: true;
|
|
80
|
+
};
|
|
81
|
+
first: number;
|
|
82
|
+
};
|
|
83
|
+
rlsModule: {
|
|
84
|
+
select: {
|
|
85
|
+
privateSchema: {
|
|
86
|
+
select: {
|
|
87
|
+
schemaName: true;
|
|
88
|
+
};
|
|
89
|
+
};
|
|
90
|
+
authenticateStrict: true;
|
|
91
|
+
authenticate: true;
|
|
92
|
+
currentRole: true;
|
|
93
|
+
currentRoleId: true;
|
|
94
|
+
};
|
|
95
|
+
};
|
|
96
|
+
apiModules: {
|
|
97
|
+
select: {
|
|
98
|
+
name: true;
|
|
99
|
+
data: true;
|
|
100
|
+
};
|
|
101
|
+
first: number;
|
|
102
|
+
};
|
|
103
|
+
};
|
|
104
|
+
};
|
|
105
|
+
};
|
|
106
|
+
export declare const apiListSelect: {
|
|
107
|
+
id: true;
|
|
108
|
+
databaseId: true;
|
|
109
|
+
name: true;
|
|
110
|
+
dbname: true;
|
|
111
|
+
roleName: true;
|
|
112
|
+
anonRole: true;
|
|
113
|
+
isPublic: true;
|
|
114
|
+
domains: {
|
|
115
|
+
select: {
|
|
116
|
+
domain: true;
|
|
117
|
+
subdomain: true;
|
|
118
|
+
};
|
|
119
|
+
first: number;
|
|
120
|
+
};
|
|
121
|
+
};
|
|
122
|
+
export type ApiRecord = InferSelectResult<ApiWithRelations, typeof apiSelect>;
|
|
123
|
+
export type DomainRecord = InferSelectResult<DomainWithRelations, typeof domainSelect>;
|
|
124
|
+
export type ApiListRecord = InferSelectResult<ApiWithRelations, typeof apiListSelect>;
|
|
125
|
+
type ApiByNameResult = QueryResult<{
|
|
126
|
+
apiByDatabaseIdAndName: ApiRecord;
|
|
127
|
+
}>;
|
|
128
|
+
export type ApiQueryOps = {
|
|
129
|
+
apiByDatabaseIdAndName: (args: {
|
|
130
|
+
databaseId: string;
|
|
131
|
+
name: string;
|
|
132
|
+
}, options: {
|
|
133
|
+
select: typeof apiSelect;
|
|
134
|
+
}) => {
|
|
135
|
+
execute: () => Promise<ApiByNameResult>;
|
|
136
|
+
};
|
|
137
|
+
};
|
|
138
|
+
type DomainLookupResult = QueryResult<{
|
|
139
|
+
domains: {
|
|
140
|
+
nodes: DomainRecord[];
|
|
141
|
+
};
|
|
142
|
+
}>;
|
|
143
|
+
export type DomainLookupModel = {
|
|
144
|
+
findFirst: (args: {
|
|
145
|
+
select: typeof domainSelect;
|
|
146
|
+
where: DomainFilter;
|
|
147
|
+
}) => {
|
|
148
|
+
execute: () => Promise<DomainLookupResult>;
|
|
149
|
+
};
|
|
150
|
+
};
|
|
151
|
+
type ApiListResult = QueryResult<{
|
|
152
|
+
apis: {
|
|
153
|
+
nodes: ApiListRecord[];
|
|
154
|
+
};
|
|
155
|
+
}>;
|
|
156
|
+
export type ApiListModel = {
|
|
157
|
+
findMany: (args: {
|
|
158
|
+
select: typeof apiListSelect;
|
|
159
|
+
first: number;
|
|
160
|
+
}) => {
|
|
161
|
+
execute: () => Promise<ApiListResult>;
|
|
162
|
+
};
|
|
163
|
+
};
|
|
164
|
+
export declare const createGraphileOrm: (graphile: GraphileQuery) => {
|
|
165
|
+
api: ApiModel;
|
|
166
|
+
domain: DomainModel;
|
|
167
|
+
query: {
|
|
168
|
+
apiByDatabaseIdAndName: <const S extends ApiSelect>(args: import("../codegen/orm/query").ApiByDatabaseIdAndNameVariables, options?: {
|
|
169
|
+
select?: import("../codegen/orm/select-types").DeepExact<S, ApiSelect>;
|
|
170
|
+
}) => import("../codegen/orm").QueryBuilder<{
|
|
171
|
+
apiByDatabaseIdAndName: InferSelectResult<import("../codegen/orm/input-types").Api, S>;
|
|
172
|
+
}>;
|
|
173
|
+
};
|
|
174
|
+
};
|
|
175
|
+
export declare const normalizeApiRecord: (api: ApiRecord) => ApiStructure;
|
|
26
176
|
export {};
|