@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.
Files changed (205) hide show
  1. package/codegen/orm/index.d.ts +7 -603
  2. package/codegen/orm/index.js +4 -127
  3. package/codegen/orm/input-types.d.ts +14682 -7790
  4. package/codegen/orm/input-types.js +0 -5
  5. package/codegen/orm/models/api.d.ts +6 -6
  6. package/codegen/orm/models/api.js +3 -4
  7. package/codegen/orm/models/domain.d.ts +6 -6
  8. package/codegen/orm/models/domain.js +3 -4
  9. package/codegen/orm/models/index.d.ts +1 -58
  10. package/codegen/orm/models/index.js +3 -118
  11. package/codegen/orm/mutation/index.d.ts +1 -525
  12. package/codegen/orm/mutation/index.js +2 -591
  13. package/codegen/orm/query/index.d.ts +3 -257
  14. package/codegen/orm/query/index.js +8 -274
  15. package/codegen/orm/query-builder.d.ts +2 -1
  16. package/codegen/orm/query-builder.js +376 -129
  17. package/codegen/orm/select-types.d.ts +33 -0
  18. package/esm/codegen/orm/index.js +4 -127
  19. package/esm/codegen/orm/input-types.js +0 -5
  20. package/esm/codegen/orm/models/api.js +3 -4
  21. package/esm/codegen/orm/models/domain.js +3 -4
  22. package/esm/codegen/orm/models/index.js +1 -58
  23. package/esm/codegen/orm/mutation/index.js +2 -591
  24. package/esm/codegen/orm/query/index.js +8 -274
  25. package/esm/codegen/orm/query-builder.js +343 -129
  26. package/esm/middleware/api.js +145 -173
  27. package/esm/middleware/gql.js +72 -71
  28. package/middleware/api.d.ts +16 -3
  29. package/middleware/api.js +148 -173
  30. package/middleware/gql.d.ts +174 -24
  31. package/middleware/gql.js +76 -76
  32. package/middleware/types.d.ts +3 -19
  33. package/package.json +7 -4
  34. package/types.d.ts +17 -44
  35. package/codegen/orm/models/apiExtension.d.ts +0 -42
  36. package/codegen/orm/models/apiExtension.js +0 -77
  37. package/codegen/orm/models/apiModule.d.ts +0 -42
  38. package/codegen/orm/models/apiModule.js +0 -77
  39. package/codegen/orm/models/apiSchema.d.ts +0 -42
  40. package/codegen/orm/models/apiSchema.js +0 -77
  41. package/codegen/orm/models/app.d.ts +0 -42
  42. package/codegen/orm/models/app.js +0 -77
  43. package/codegen/orm/models/checkConstraint.d.ts +0 -42
  44. package/codegen/orm/models/checkConstraint.js +0 -77
  45. package/codegen/orm/models/connectedAccountsModule.d.ts +0 -42
  46. package/codegen/orm/models/connectedAccountsModule.js +0 -77
  47. package/codegen/orm/models/cryptoAddressesModule.d.ts +0 -42
  48. package/codegen/orm/models/cryptoAddressesModule.js +0 -77
  49. package/codegen/orm/models/cryptoAuthModule.d.ts +0 -42
  50. package/codegen/orm/models/cryptoAuthModule.js +0 -77
  51. package/codegen/orm/models/database.d.ts +0 -42
  52. package/codegen/orm/models/database.js +0 -77
  53. package/codegen/orm/models/databaseExtension.d.ts +0 -42
  54. package/codegen/orm/models/databaseExtension.js +0 -77
  55. package/codegen/orm/models/databaseProvision.d.ts +0 -42
  56. package/codegen/orm/models/databaseProvision.js +0 -77
  57. package/codegen/orm/models/defaultIdsModule.d.ts +0 -42
  58. package/codegen/orm/models/defaultIdsModule.js +0 -77
  59. package/codegen/orm/models/denormalizedTableField.d.ts +0 -42
  60. package/codegen/orm/models/denormalizedTableField.js +0 -77
  61. package/codegen/orm/models/emailsModule.d.ts +0 -42
  62. package/codegen/orm/models/emailsModule.js +0 -77
  63. package/codegen/orm/models/encryptedSecretsModule.d.ts +0 -42
  64. package/codegen/orm/models/encryptedSecretsModule.js +0 -77
  65. package/codegen/orm/models/extension.d.ts +0 -42
  66. package/codegen/orm/models/extension.js +0 -77
  67. package/codegen/orm/models/field.d.ts +0 -42
  68. package/codegen/orm/models/field.js +0 -77
  69. package/codegen/orm/models/fieldModule.d.ts +0 -42
  70. package/codegen/orm/models/fieldModule.js +0 -77
  71. package/codegen/orm/models/foreignKeyConstraint.d.ts +0 -42
  72. package/codegen/orm/models/foreignKeyConstraint.js +0 -77
  73. package/codegen/orm/models/fullTextSearch.d.ts +0 -42
  74. package/codegen/orm/models/fullTextSearch.js +0 -77
  75. package/codegen/orm/models/hierarchyModule.d.ts +0 -42
  76. package/codegen/orm/models/hierarchyModule.js +0 -77
  77. package/codegen/orm/models/indexModel.d.ts +0 -42
  78. package/codegen/orm/models/indexModel.js +0 -77
  79. package/codegen/orm/models/invitesModule.d.ts +0 -42
  80. package/codegen/orm/models/invitesModule.js +0 -77
  81. package/codegen/orm/models/levelsModule.d.ts +0 -42
  82. package/codegen/orm/models/levelsModule.js +0 -77
  83. package/codegen/orm/models/limitFunction.d.ts +0 -42
  84. package/codegen/orm/models/limitFunction.js +0 -77
  85. package/codegen/orm/models/limitsModule.d.ts +0 -42
  86. package/codegen/orm/models/limitsModule.js +0 -77
  87. package/codegen/orm/models/membershipTypesModule.d.ts +0 -42
  88. package/codegen/orm/models/membershipTypesModule.js +0 -77
  89. package/codegen/orm/models/membershipsModule.d.ts +0 -42
  90. package/codegen/orm/models/membershipsModule.js +0 -77
  91. package/codegen/orm/models/module.d.ts +0 -42
  92. package/codegen/orm/models/module.js +0 -77
  93. package/codegen/orm/models/moduleDefinition.d.ts +0 -42
  94. package/codegen/orm/models/moduleDefinition.js +0 -77
  95. package/codegen/orm/models/moduleField.d.ts +0 -42
  96. package/codegen/orm/models/moduleField.js +0 -77
  97. package/codegen/orm/models/moduleInputRecord.d.ts +0 -42
  98. package/codegen/orm/models/moduleInputRecord.js +0 -77
  99. package/codegen/orm/models/moduleOutput.d.ts +0 -42
  100. package/codegen/orm/models/moduleOutput.js +0 -77
  101. package/codegen/orm/models/permissionsModule.d.ts +0 -42
  102. package/codegen/orm/models/permissionsModule.js +0 -77
  103. package/codegen/orm/models/phoneNumbersModule.d.ts +0 -42
  104. package/codegen/orm/models/phoneNumbersModule.js +0 -77
  105. package/codegen/orm/models/policy.d.ts +0 -42
  106. package/codegen/orm/models/policy.js +0 -77
  107. package/codegen/orm/models/primaryKeyConstraint.d.ts +0 -42
  108. package/codegen/orm/models/primaryKeyConstraint.js +0 -77
  109. package/codegen/orm/models/procedure.d.ts +0 -42
  110. package/codegen/orm/models/procedure.js +0 -77
  111. package/codegen/orm/models/profilesModule.d.ts +0 -42
  112. package/codegen/orm/models/profilesModule.js +0 -77
  113. package/codegen/orm/models/rlsFunction.d.ts +0 -42
  114. package/codegen/orm/models/rlsFunction.js +0 -77
  115. package/codegen/orm/models/rlsModule.d.ts +0 -42
  116. package/codegen/orm/models/rlsModule.js +0 -77
  117. package/codegen/orm/models/schema.d.ts +0 -42
  118. package/codegen/orm/models/schema.js +0 -77
  119. package/codegen/orm/models/schemaGrant.d.ts +0 -42
  120. package/codegen/orm/models/schemaGrant.js +0 -77
  121. package/codegen/orm/models/secretsModule.d.ts +0 -42
  122. package/codegen/orm/models/secretsModule.js +0 -77
  123. package/codegen/orm/models/site.d.ts +0 -42
  124. package/codegen/orm/models/site.js +0 -77
  125. package/codegen/orm/models/siteMetadatum.d.ts +0 -42
  126. package/codegen/orm/models/siteMetadatum.js +0 -77
  127. package/codegen/orm/models/siteModule.d.ts +0 -42
  128. package/codegen/orm/models/siteModule.js +0 -77
  129. package/codegen/orm/models/siteTheme.d.ts +0 -42
  130. package/codegen/orm/models/siteTheme.js +0 -77
  131. package/codegen/orm/models/table.d.ts +0 -42
  132. package/codegen/orm/models/table.js +0 -77
  133. package/codegen/orm/models/tableGrant.d.ts +0 -42
  134. package/codegen/orm/models/tableGrant.js +0 -77
  135. package/codegen/orm/models/tokensModule.d.ts +0 -42
  136. package/codegen/orm/models/tokensModule.js +0 -77
  137. package/codegen/orm/models/trigger.d.ts +0 -42
  138. package/codegen/orm/models/trigger.js +0 -77
  139. package/codegen/orm/models/triggerFunction.d.ts +0 -42
  140. package/codegen/orm/models/triggerFunction.js +0 -77
  141. package/codegen/orm/models/uniqueConstraint.d.ts +0 -42
  142. package/codegen/orm/models/uniqueConstraint.js +0 -77
  143. package/codegen/orm/models/userAuthModule.d.ts +0 -42
  144. package/codegen/orm/models/userAuthModule.js +0 -77
  145. package/codegen/orm/models/usersModule.d.ts +0 -42
  146. package/codegen/orm/models/usersModule.js +0 -77
  147. package/codegen/orm/models/uuidModule.d.ts +0 -42
  148. package/codegen/orm/models/uuidModule.js +0 -77
  149. package/esm/codegen/orm/models/apiExtension.js +0 -73
  150. package/esm/codegen/orm/models/apiModule.js +0 -73
  151. package/esm/codegen/orm/models/apiSchema.js +0 -73
  152. package/esm/codegen/orm/models/app.js +0 -73
  153. package/esm/codegen/orm/models/checkConstraint.js +0 -73
  154. package/esm/codegen/orm/models/connectedAccountsModule.js +0 -73
  155. package/esm/codegen/orm/models/cryptoAddressesModule.js +0 -73
  156. package/esm/codegen/orm/models/cryptoAuthModule.js +0 -73
  157. package/esm/codegen/orm/models/database.js +0 -73
  158. package/esm/codegen/orm/models/databaseExtension.js +0 -73
  159. package/esm/codegen/orm/models/databaseProvision.js +0 -73
  160. package/esm/codegen/orm/models/defaultIdsModule.js +0 -73
  161. package/esm/codegen/orm/models/denormalizedTableField.js +0 -73
  162. package/esm/codegen/orm/models/emailsModule.js +0 -73
  163. package/esm/codegen/orm/models/encryptedSecretsModule.js +0 -73
  164. package/esm/codegen/orm/models/extension.js +0 -73
  165. package/esm/codegen/orm/models/field.js +0 -73
  166. package/esm/codegen/orm/models/fieldModule.js +0 -73
  167. package/esm/codegen/orm/models/foreignKeyConstraint.js +0 -73
  168. package/esm/codegen/orm/models/fullTextSearch.js +0 -73
  169. package/esm/codegen/orm/models/hierarchyModule.js +0 -73
  170. package/esm/codegen/orm/models/indexModel.js +0 -73
  171. package/esm/codegen/orm/models/invitesModule.js +0 -73
  172. package/esm/codegen/orm/models/levelsModule.js +0 -73
  173. package/esm/codegen/orm/models/limitFunction.js +0 -73
  174. package/esm/codegen/orm/models/limitsModule.js +0 -73
  175. package/esm/codegen/orm/models/membershipTypesModule.js +0 -73
  176. package/esm/codegen/orm/models/membershipsModule.js +0 -73
  177. package/esm/codegen/orm/models/module.js +0 -73
  178. package/esm/codegen/orm/models/moduleDefinition.js +0 -73
  179. package/esm/codegen/orm/models/moduleField.js +0 -73
  180. package/esm/codegen/orm/models/moduleInputRecord.js +0 -73
  181. package/esm/codegen/orm/models/moduleOutput.js +0 -73
  182. package/esm/codegen/orm/models/permissionsModule.js +0 -73
  183. package/esm/codegen/orm/models/phoneNumbersModule.js +0 -73
  184. package/esm/codegen/orm/models/policy.js +0 -73
  185. package/esm/codegen/orm/models/primaryKeyConstraint.js +0 -73
  186. package/esm/codegen/orm/models/procedure.js +0 -73
  187. package/esm/codegen/orm/models/profilesModule.js +0 -73
  188. package/esm/codegen/orm/models/rlsFunction.js +0 -73
  189. package/esm/codegen/orm/models/rlsModule.js +0 -73
  190. package/esm/codegen/orm/models/schema.js +0 -73
  191. package/esm/codegen/orm/models/schemaGrant.js +0 -73
  192. package/esm/codegen/orm/models/secretsModule.js +0 -73
  193. package/esm/codegen/orm/models/site.js +0 -73
  194. package/esm/codegen/orm/models/siteMetadatum.js +0 -73
  195. package/esm/codegen/orm/models/siteModule.js +0 -73
  196. package/esm/codegen/orm/models/siteTheme.js +0 -73
  197. package/esm/codegen/orm/models/table.js +0 -73
  198. package/esm/codegen/orm/models/tableGrant.js +0 -73
  199. package/esm/codegen/orm/models/tokensModule.js +0 -73
  200. package/esm/codegen/orm/models/trigger.js +0 -73
  201. package/esm/codegen/orm/models/triggerFunction.js +0 -73
  202. package/esm/codegen/orm/models/uniqueConstraint.js +0 -73
  203. package/esm/codegen/orm/models/userAuthModule.js +0 -73
  204. package/esm/codegen/orm/models/usersModule.js +0 -73
  205. 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 transformServiceToApi = (svc) => {
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 svc = await (0, exports.getApiConfig)(opts, req);
90
- if (svc?.errorHtml) {
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', svc.errorHtml));
77
+ .send((0, _404_message_1.default)('API not found', apiConfig.errorHtml));
94
78
  return;
95
79
  }
96
- else if (!svc) {
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
- const api = transformServiceToApi(svc);
103
- req.api = api;
104
- req.databaseId = api.databaseId;
86
+ req.api = apiConfig;
87
+ req.databaseId = apiConfig.databaseId;
105
88
  if (isDev())
106
- log.debug(`Resolved API: db=${api.dbname}, schemas=[${api.schema?.join(', ')}]`);
89
+ log.debug(`Resolved API: db=${apiConfig.dbname}, schemas=[${apiConfig.schema?.join(', ')}]`);
107
90
  next();
108
91
  }
109
- catch (e) {
110
- if (e.code === 'NO_VALID_SCHEMAS') {
111
- res.status(404).send((0, _404_message_1.default)(e.message));
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 (e.message.match(/does not exist/)) {
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:', e);
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 getHardCodedSchemata = ({ opts, schemata, databaseId, key, }) => {
127
- const svc = {
128
- data: {
129
- api: {
130
- databaseId,
131
- isPublic: false,
132
- dbname: opts.pg.database,
133
- anonRole: 'administrator',
134
- roleName: 'administrator',
135
- schemaNamesFromExt: {
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, svc);
147
- return svc;
121
+ server_utils_1.svcCache.set(key, api);
122
+ return api;
148
123
  };
149
- const getMetaSchema = ({ opts, key, databaseId, }) => {
150
- const apiOpts = opts.api || {};
151
- const schemata = apiOpts.metaSchemas || [];
152
- const svc = {
153
- data: {
154
- api: {
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
- server_utils_1.svcCache.set(key, svc);
169
- return svc;
170
- };
171
- const queryServiceByDomainAndSubdomain = async ({ opts, key, client, domain, subdomain, }) => {
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 nodes = result?.data?.domains?.nodes;
183
- if (nodes?.length) {
184
- const data = nodes[0];
185
- const apiPublic = opts.api?.isPublic;
186
- if (!data.api || data.api.isPublic !== apiPublic)
187
- return null;
188
- const svc = { data };
189
- server_utils_1.svcCache.set(key, svc);
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, client, databaseId, name, }) => {
195
- const doc = (0, gql_1.buildApiByDatabaseIdAndName)({ databaseId, name });
196
- const result = await client.query({
197
- role: 'administrator',
198
- query: doc.document,
199
- variables: doc.variables,
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 data = result?.data;
157
+ const api = result.data.apiByDatabaseIdAndName;
206
158
  const apiPublic = opts.api?.isPublic;
207
- if (data?.api && data.api.isPublic === apiPublic) {
208
- const svc = { data };
209
- server_utils_1.svcCache.set(key, svc);
210
- return svc;
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.urlDomains.domain;
216
- const key = req.urlDomains.subdomains
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
- // @ts-ignore
241
- const subdomain = (0, exports.getSubdomain)(req.urlDomains.subdomains);
242
- const domain = req.urlDomains.domain;
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 svc;
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
- svc = server_utils_1.svcCache.get(key);
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 allSchemata = apiOpts.metaSchemas || [];
256
- const validatedSchemata = await validateSchemata(rootPgPool, allSchemata);
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 apiOpts2 = opts.api || {};
259
- const message = `No valid schemas found. Configured metaSchemas: [${(apiOpts2.metaSchemas || []).join(', ')}]`;
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
- // @ts-ignore
272
- const schema = await (0, graphile_query_1.getSchema)(rootPgPool, settings);
273
- // @ts-ignore
274
- const client = new graphile_query_1.GraphileQuery({ schema, pool: rootPgPool, settings });
275
- const apiPublic = opts.api?.isPublic;
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 (req.get('X-Schemata')) {
278
- svc = getHardCodedSchemata({
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
- schemata: req.get('X-Schemata'),
282
- databaseId: req.get('X-Database-Id'),
261
+ databaseId: databaseIdHeader,
283
262
  });
284
263
  }
285
- else if (req.get('X-Api-Name')) {
286
- svc = await queryServiceByApiName({
264
+ else if (apiNameHeader) {
265
+ apiConfig = await (0, exports.queryServiceByApiName)({
287
266
  opts,
288
267
  key,
289
- client,
290
- name: req.get('X-Api-Name'),
291
- databaseId: req.get('X-Database-Id'),
268
+ queryOps: orm.query,
269
+ name: apiNameHeader,
270
+ databaseId: databaseIdHeader,
292
271
  });
293
272
  }
294
- else if (req.get('X-Meta-Schema')) {
295
- svc = getMetaSchema({
273
+ else if (metaSchemaHeader) {
274
+ apiConfig = createAdminApiStructure({
296
275
  opts,
276
+ schemata: validatedSchemata,
297
277
  key,
298
- databaseId: req.get('X-Database-Id'),
278
+ databaseId: databaseIdHeader,
299
279
  });
300
280
  }
301
281
  else {
302
- svc = await queryServiceByDomainAndSubdomain({
282
+ apiConfig = await queryServiceByDomainAndSubdomain({
303
283
  opts,
304
284
  key,
305
- client,
285
+ domainModel: orm.domain,
306
286
  domain,
307
287
  subdomain,
308
288
  });
309
289
  }
310
290
  }
311
291
  else {
312
- svc = await queryServiceByDomainAndSubdomain({
292
+ apiConfig = await queryServiceByDomainAndSubdomain({
313
293
  opts,
314
294
  key,
315
- client,
295
+ domainModel: orm.domain,
316
296
  domain,
317
297
  subdomain,
318
298
  });
319
- if (!svc) {
299
+ if (!apiConfig) {
300
+ // IMPORTANT NOTE: ONLY DO THIS IN DEV MODE
320
301
  if ((0, graphql_env_1.getNodeEnv)() === 'development') {
321
- // TODO ONLY DO THIS IN DEV MODE
322
- const fallbackResult = await client.query({
323
- role: 'administrator',
324
- // @ts-ignore
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((d) => `<li><a href="${d.href}" class="text-brand hover:underline">${d.href}</a></li>`)
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 svc;
333
+ return apiConfig;
359
334
  };
360
335
  exports.getApiConfig = getApiConfig;
@@ -1,26 +1,176 @@
1
- type BuiltDocument = {
2
- document: string;
3
- variables: Record<string, unknown>;
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
- * Build query for domain lookup with optional subdomain
7
- * This uses domains connection instead of domainBySubdomainAndDomain
8
- * because we need to handle null subdomain with condition filter
9
- */
10
- export declare const buildDomainLookup: (vars: {
11
- domain: string;
12
- subdomain?: string | null;
13
- }) => BuiltDocument;
14
- /**
15
- * Build query for API lookup by database ID and name
16
- * Uses the generated apiByDatabaseIdAndName custom query
17
- */
18
- export declare const buildApiByDatabaseIdAndName: (vars: {
19
- databaseId: string;
20
- name: string;
21
- }) => BuiltDocument;
22
- /**
23
- * Build query to list all APIs
24
- */
25
- export declare const buildListApis: () => BuiltDocument;
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 {};