@constructive-io/graphql-server 2.14.7 → 2.15.0

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 (219) hide show
  1. package/codegen/orm/client.d.ts +55 -0
  2. package/codegen/orm/client.js +75 -0
  3. package/codegen/orm/index.d.ts +632 -0
  4. package/codegen/orm/index.js +182 -0
  5. package/codegen/orm/input-types.d.ts +13248 -0
  6. package/codegen/orm/input-types.js +7 -0
  7. package/codegen/orm/models/api.d.ts +42 -0
  8. package/codegen/orm/models/api.js +77 -0
  9. package/codegen/orm/models/apiExtension.d.ts +42 -0
  10. package/codegen/orm/models/apiExtension.js +77 -0
  11. package/codegen/orm/models/apiModule.d.ts +42 -0
  12. package/codegen/orm/models/apiModule.js +77 -0
  13. package/codegen/orm/models/apiSchema.d.ts +42 -0
  14. package/codegen/orm/models/apiSchema.js +77 -0
  15. package/codegen/orm/models/app.d.ts +42 -0
  16. package/codegen/orm/models/app.js +77 -0
  17. package/codegen/orm/models/checkConstraint.d.ts +42 -0
  18. package/codegen/orm/models/checkConstraint.js +77 -0
  19. package/codegen/orm/models/connectedAccountsModule.d.ts +42 -0
  20. package/codegen/orm/models/connectedAccountsModule.js +77 -0
  21. package/codegen/orm/models/cryptoAddressesModule.d.ts +42 -0
  22. package/codegen/orm/models/cryptoAddressesModule.js +77 -0
  23. package/codegen/orm/models/cryptoAuthModule.d.ts +42 -0
  24. package/codegen/orm/models/cryptoAuthModule.js +77 -0
  25. package/codegen/orm/models/database.d.ts +42 -0
  26. package/codegen/orm/models/database.js +77 -0
  27. package/codegen/orm/models/databaseExtension.d.ts +42 -0
  28. package/codegen/orm/models/databaseExtension.js +77 -0
  29. package/codegen/orm/models/databaseProvision.d.ts +42 -0
  30. package/codegen/orm/models/databaseProvision.js +77 -0
  31. package/codegen/orm/models/defaultIdsModule.d.ts +42 -0
  32. package/codegen/orm/models/defaultIdsModule.js +77 -0
  33. package/codegen/orm/models/denormalizedTableField.d.ts +42 -0
  34. package/codegen/orm/models/denormalizedTableField.js +77 -0
  35. package/codegen/orm/models/domain.d.ts +42 -0
  36. package/codegen/orm/models/domain.js +77 -0
  37. package/codegen/orm/models/emailsModule.d.ts +42 -0
  38. package/codegen/orm/models/emailsModule.js +77 -0
  39. package/codegen/orm/models/encryptedSecretsModule.d.ts +42 -0
  40. package/codegen/orm/models/encryptedSecretsModule.js +77 -0
  41. package/codegen/orm/models/extension.d.ts +42 -0
  42. package/codegen/orm/models/extension.js +77 -0
  43. package/codegen/orm/models/field.d.ts +42 -0
  44. package/codegen/orm/models/field.js +77 -0
  45. package/codegen/orm/models/fieldModule.d.ts +42 -0
  46. package/codegen/orm/models/fieldModule.js +77 -0
  47. package/codegen/orm/models/foreignKeyConstraint.d.ts +42 -0
  48. package/codegen/orm/models/foreignKeyConstraint.js +77 -0
  49. package/codegen/orm/models/fullTextSearch.d.ts +42 -0
  50. package/codegen/orm/models/fullTextSearch.js +77 -0
  51. package/codegen/orm/models/hierarchyModule.d.ts +42 -0
  52. package/codegen/orm/models/hierarchyModule.js +77 -0
  53. package/codegen/orm/models/index.d.ts +64 -0
  54. package/codegen/orm/models/index.js +127 -0
  55. package/codegen/orm/models/indexModel.d.ts +42 -0
  56. package/codegen/orm/models/indexModel.js +77 -0
  57. package/codegen/orm/models/invitesModule.d.ts +42 -0
  58. package/codegen/orm/models/invitesModule.js +77 -0
  59. package/codegen/orm/models/levelsModule.d.ts +42 -0
  60. package/codegen/orm/models/levelsModule.js +77 -0
  61. package/codegen/orm/models/limitFunction.d.ts +42 -0
  62. package/codegen/orm/models/limitFunction.js +77 -0
  63. package/codegen/orm/models/limitsModule.d.ts +42 -0
  64. package/codegen/orm/models/limitsModule.js +77 -0
  65. package/codegen/orm/models/membershipTypesModule.d.ts +42 -0
  66. package/codegen/orm/models/membershipTypesModule.js +77 -0
  67. package/codegen/orm/models/membershipsModule.d.ts +42 -0
  68. package/codegen/orm/models/membershipsModule.js +77 -0
  69. package/codegen/orm/models/module.d.ts +42 -0
  70. package/codegen/orm/models/module.js +77 -0
  71. package/codegen/orm/models/moduleDefinition.d.ts +42 -0
  72. package/codegen/orm/models/moduleDefinition.js +77 -0
  73. package/codegen/orm/models/moduleField.d.ts +42 -0
  74. package/codegen/orm/models/moduleField.js +77 -0
  75. package/codegen/orm/models/moduleInputRecord.d.ts +42 -0
  76. package/codegen/orm/models/moduleInputRecord.js +77 -0
  77. package/codegen/orm/models/moduleOutput.d.ts +42 -0
  78. package/codegen/orm/models/moduleOutput.js +77 -0
  79. package/codegen/orm/models/permissionsModule.d.ts +42 -0
  80. package/codegen/orm/models/permissionsModule.js +77 -0
  81. package/codegen/orm/models/phoneNumbersModule.d.ts +42 -0
  82. package/codegen/orm/models/phoneNumbersModule.js +77 -0
  83. package/codegen/orm/models/policy.d.ts +42 -0
  84. package/codegen/orm/models/policy.js +77 -0
  85. package/codegen/orm/models/primaryKeyConstraint.d.ts +42 -0
  86. package/codegen/orm/models/primaryKeyConstraint.js +77 -0
  87. package/codegen/orm/models/procedure.d.ts +42 -0
  88. package/codegen/orm/models/procedure.js +77 -0
  89. package/codegen/orm/models/profilesModule.d.ts +42 -0
  90. package/codegen/orm/models/profilesModule.js +77 -0
  91. package/codegen/orm/models/rlsFunction.d.ts +42 -0
  92. package/codegen/orm/models/rlsFunction.js +77 -0
  93. package/codegen/orm/models/rlsModule.d.ts +42 -0
  94. package/codegen/orm/models/rlsModule.js +77 -0
  95. package/codegen/orm/models/schema.d.ts +42 -0
  96. package/codegen/orm/models/schema.js +77 -0
  97. package/codegen/orm/models/schemaGrant.d.ts +42 -0
  98. package/codegen/orm/models/schemaGrant.js +77 -0
  99. package/codegen/orm/models/secretsModule.d.ts +42 -0
  100. package/codegen/orm/models/secretsModule.js +77 -0
  101. package/codegen/orm/models/site.d.ts +42 -0
  102. package/codegen/orm/models/site.js +77 -0
  103. package/codegen/orm/models/siteMetadatum.d.ts +42 -0
  104. package/codegen/orm/models/siteMetadatum.js +77 -0
  105. package/codegen/orm/models/siteModule.d.ts +42 -0
  106. package/codegen/orm/models/siteModule.js +77 -0
  107. package/codegen/orm/models/siteTheme.d.ts +42 -0
  108. package/codegen/orm/models/siteTheme.js +77 -0
  109. package/codegen/orm/models/table.d.ts +42 -0
  110. package/codegen/orm/models/table.js +77 -0
  111. package/codegen/orm/models/tableGrant.d.ts +42 -0
  112. package/codegen/orm/models/tableGrant.js +77 -0
  113. package/codegen/orm/models/tokensModule.d.ts +42 -0
  114. package/codegen/orm/models/tokensModule.js +77 -0
  115. package/codegen/orm/models/trigger.d.ts +42 -0
  116. package/codegen/orm/models/trigger.js +77 -0
  117. package/codegen/orm/models/triggerFunction.d.ts +42 -0
  118. package/codegen/orm/models/triggerFunction.js +77 -0
  119. package/codegen/orm/models/uniqueConstraint.d.ts +42 -0
  120. package/codegen/orm/models/uniqueConstraint.js +77 -0
  121. package/codegen/orm/models/userAuthModule.d.ts +42 -0
  122. package/codegen/orm/models/userAuthModule.js +77 -0
  123. package/codegen/orm/models/usersModule.d.ts +42 -0
  124. package/codegen/orm/models/usersModule.js +77 -0
  125. package/codegen/orm/models/uuidModule.d.ts +42 -0
  126. package/codegen/orm/models/uuidModule.js +77 -0
  127. package/codegen/orm/mutation/index.d.ts +531 -0
  128. package/codegen/orm/mutation/index.js +596 -0
  129. package/codegen/orm/query/index.d.ts +274 -0
  130. package/codegen/orm/query/index.js +290 -0
  131. package/codegen/orm/query-builder.d.ts +80 -0
  132. package/codegen/orm/query-builder.js +249 -0
  133. package/codegen/orm/select-types.d.ts +50 -0
  134. package/codegen/orm/select-types.js +7 -0
  135. package/codegen/orm/types.d.ts +6 -0
  136. package/codegen/orm/types.js +23 -0
  137. package/esm/codegen/orm/client.js +70 -0
  138. package/esm/codegen/orm/index.js +162 -0
  139. package/esm/codegen/orm/input-types.js +6 -0
  140. package/esm/codegen/orm/models/api.js +73 -0
  141. package/esm/codegen/orm/models/apiExtension.js +73 -0
  142. package/esm/codegen/orm/models/apiModule.js +73 -0
  143. package/esm/codegen/orm/models/apiSchema.js +73 -0
  144. package/esm/codegen/orm/models/app.js +73 -0
  145. package/esm/codegen/orm/models/checkConstraint.js +73 -0
  146. package/esm/codegen/orm/models/connectedAccountsModule.js +73 -0
  147. package/esm/codegen/orm/models/cryptoAddressesModule.js +73 -0
  148. package/esm/codegen/orm/models/cryptoAuthModule.js +73 -0
  149. package/esm/codegen/orm/models/database.js +73 -0
  150. package/esm/codegen/orm/models/databaseExtension.js +73 -0
  151. package/esm/codegen/orm/models/databaseProvision.js +73 -0
  152. package/esm/codegen/orm/models/defaultIdsModule.js +73 -0
  153. package/esm/codegen/orm/models/denormalizedTableField.js +73 -0
  154. package/esm/codegen/orm/models/domain.js +73 -0
  155. package/esm/codegen/orm/models/emailsModule.js +73 -0
  156. package/esm/codegen/orm/models/encryptedSecretsModule.js +73 -0
  157. package/esm/codegen/orm/models/extension.js +73 -0
  158. package/esm/codegen/orm/models/field.js +73 -0
  159. package/esm/codegen/orm/models/fieldModule.js +73 -0
  160. package/esm/codegen/orm/models/foreignKeyConstraint.js +73 -0
  161. package/esm/codegen/orm/models/fullTextSearch.js +73 -0
  162. package/esm/codegen/orm/models/hierarchyModule.js +73 -0
  163. package/esm/codegen/orm/models/index.js +64 -0
  164. package/esm/codegen/orm/models/indexModel.js +73 -0
  165. package/esm/codegen/orm/models/invitesModule.js +73 -0
  166. package/esm/codegen/orm/models/levelsModule.js +73 -0
  167. package/esm/codegen/orm/models/limitFunction.js +73 -0
  168. package/esm/codegen/orm/models/limitsModule.js +73 -0
  169. package/esm/codegen/orm/models/membershipTypesModule.js +73 -0
  170. package/esm/codegen/orm/models/membershipsModule.js +73 -0
  171. package/esm/codegen/orm/models/module.js +73 -0
  172. package/esm/codegen/orm/models/moduleDefinition.js +73 -0
  173. package/esm/codegen/orm/models/moduleField.js +73 -0
  174. package/esm/codegen/orm/models/moduleInputRecord.js +73 -0
  175. package/esm/codegen/orm/models/moduleOutput.js +73 -0
  176. package/esm/codegen/orm/models/permissionsModule.js +73 -0
  177. package/esm/codegen/orm/models/phoneNumbersModule.js +73 -0
  178. package/esm/codegen/orm/models/policy.js +73 -0
  179. package/esm/codegen/orm/models/primaryKeyConstraint.js +73 -0
  180. package/esm/codegen/orm/models/procedure.js +73 -0
  181. package/esm/codegen/orm/models/profilesModule.js +73 -0
  182. package/esm/codegen/orm/models/rlsFunction.js +73 -0
  183. package/esm/codegen/orm/models/rlsModule.js +73 -0
  184. package/esm/codegen/orm/models/schema.js +73 -0
  185. package/esm/codegen/orm/models/schemaGrant.js +73 -0
  186. package/esm/codegen/orm/models/secretsModule.js +73 -0
  187. package/esm/codegen/orm/models/site.js +73 -0
  188. package/esm/codegen/orm/models/siteMetadatum.js +73 -0
  189. package/esm/codegen/orm/models/siteModule.js +73 -0
  190. package/esm/codegen/orm/models/siteTheme.js +73 -0
  191. package/esm/codegen/orm/models/table.js +73 -0
  192. package/esm/codegen/orm/models/tableGrant.js +73 -0
  193. package/esm/codegen/orm/models/tokensModule.js +73 -0
  194. package/esm/codegen/orm/models/trigger.js +73 -0
  195. package/esm/codegen/orm/models/triggerFunction.js +73 -0
  196. package/esm/codegen/orm/models/uniqueConstraint.js +73 -0
  197. package/esm/codegen/orm/models/userAuthModule.js +73 -0
  198. package/esm/codegen/orm/models/usersModule.js +73 -0
  199. package/esm/codegen/orm/models/uuidModule.js +73 -0
  200. package/esm/codegen/orm/mutation/index.js +593 -0
  201. package/esm/codegen/orm/query/index.js +287 -0
  202. package/esm/codegen/orm/query-builder.js +238 -0
  203. package/esm/codegen/orm/select-types.js +6 -0
  204. package/esm/codegen/orm/types.js +7 -0
  205. package/esm/middleware/api.js +10 -9
  206. package/esm/middleware/gql.js +118 -123
  207. package/esm/middleware/graphile.js +19 -3
  208. package/esm/scripts/codegen-schema.js +71 -0
  209. package/esm/server.js +21 -0
  210. package/middleware/api.js +9 -8
  211. package/middleware/gql.d.ts +25 -5
  212. package/middleware/gql.js +122 -127
  213. package/middleware/graphile.js +19 -3
  214. package/middleware/types.d.ts +1 -0
  215. package/package.json +15 -11
  216. package/scripts/codegen-schema.d.ts +1 -0
  217. package/scripts/codegen-schema.js +76 -0
  218. package/server.js +21 -0
  219. package/types.d.ts +2 -2
@@ -1,125 +1,120 @@
1
- import gql from 'graphql-tag';
2
- // DO NOT CHANGE TO domainBySubdomainAndDomain(domain: $domain, subdomain: $subdomain)
3
- // condition is the way to handle since it will pass in null properly
4
- // e.g. subdomain.domain or domain both work
5
- export const ApiQuery = gql `
6
- query ApiRoot($domain: String!, $subdomain: String) {
7
- domains(condition: { domain: $domain, subdomain: $subdomain }) {
8
- nodes {
9
- api {
10
- databaseId
11
- dbname
12
- roleName
13
- anonRole
14
- isPublic
15
- schemaNamesFromExt: apiExtensions {
16
- nodes {
17
- schemaName
18
- }
19
- }
20
- schemaNames: schemataByApiSchemaApiIdAndSchemaId {
21
- nodes {
22
- schemaName
23
- }
24
- }
25
- rlsModule {
26
- privateSchema {
27
- schemaName
28
- }
29
- authenticateStrict
30
- authenticate
31
- currentRole
32
- currentRoleId
33
- }
34
- database {
35
- sites {
36
- nodes {
37
- domains {
38
- nodes {
39
- subdomain
40
- domain
41
- }
42
- }
43
- }
44
- }
45
- } # for now keep this for patches
46
- apiModules {
47
- nodes {
48
- name
49
- data
50
- }
51
- }
52
- }
53
- }
1
+ import { buildFindFirstDocument } from '../codegen/orm/query-builder';
2
+ const apiSelect = {
3
+ databaseId: true,
4
+ dbname: true,
5
+ roleName: true,
6
+ anonRole: true,
7
+ isPublic: true,
8
+ domains: {
9
+ select: {
10
+ subdomain: true,
11
+ domain: true,
12
+ },
13
+ connection: true,
14
+ },
15
+ apiExtensions: {
16
+ select: { schemaName: true },
17
+ connection: true,
18
+ },
19
+ schemasByApiSchemaApiIdAndSchemaId: {
20
+ select: { schemaName: true },
21
+ connection: true,
22
+ },
23
+ rlsModule: {
24
+ select: {
25
+ privateSchema: { select: { schemaName: true } },
26
+ authenticateStrict: true,
27
+ authenticate: true,
28
+ currentRole: true,
29
+ currentRoleId: true,
30
+ },
31
+ },
32
+ database: {
33
+ select: {
34
+ sites: {
35
+ select: {
36
+ domains: {
37
+ select: { subdomain: true, domain: true },
38
+ connection: true,
39
+ },
40
+ },
41
+ connection: true,
42
+ },
43
+ },
44
+ },
45
+ apiModules: {
46
+ select: { name: true, data: true },
47
+ connection: true,
48
+ },
49
+ };
50
+ const domainSelect = {
51
+ domain: true,
52
+ subdomain: true,
53
+ api: { select: apiSelect },
54
+ };
55
+ const apisSelect = {
56
+ id: true,
57
+ databaseId: true,
58
+ name: true,
59
+ dbname: true,
60
+ roleName: true,
61
+ anonRole: true,
62
+ isPublic: true,
63
+ domains: {
64
+ select: { domain: true, subdomain: true },
65
+ connection: true,
66
+ },
67
+ database: {
68
+ select: {
69
+ sites: {
70
+ select: {
71
+ domains: {
72
+ select: { domain: true, subdomain: true },
73
+ connection: true,
74
+ },
75
+ },
76
+ connection: true,
77
+ },
78
+ },
79
+ },
80
+ };
81
+ /**
82
+ * Build query for domain lookup with optional subdomain
83
+ * This uses domains connection instead of domainBySubdomainAndDomain
84
+ * because we need to handle null subdomain with condition filter
85
+ */
86
+ export const buildDomainLookup = (vars) => {
87
+ const where = {
88
+ domain: { equalTo: vars.domain },
89
+ };
90
+ if (vars.subdomain === null || vars.subdomain === undefined) {
91
+ where.subdomain = { isNull: true };
54
92
  }
55
- }
56
- `;
57
- export const ApiByNameQuery = gql `
58
- query ApiByName($name: String!, $databaseId: UUID!) {
59
- api: apiByDatabaseIdAndName(name: $name, databaseId: $databaseId) {
60
- databaseId
61
- dbname
62
- roleName
63
- anonRole
64
- isPublic
65
- schemaNamesFromExt: apiExtensions {
66
- nodes {
67
- schemaName
68
- }
69
- }
70
- schemaNames: schemataByApiSchemaApiIdAndSchemaId {
71
- nodes {
72
- schemaName
73
- }
74
- }
75
- rlsModule {
76
- privateSchema {
77
- schemaName
78
- }
79
- authenticate
80
- authenticateStrict
81
- currentRole
82
- currentRoleId
83
- }
84
- database {
85
- sites {
86
- nodes {
87
- domains {
88
- nodes {
89
- subdomain
90
- domain
91
- }
92
- }
93
- }
94
- }
95
- } # for now keep this for patches
96
- apiModules {
97
- nodes {
98
- name
99
- data
100
- }
101
- }
93
+ else {
94
+ where.subdomain = { equalTo: vars.subdomain };
102
95
  }
103
- }
104
- `;
105
- export const ListOfAllDomainsOfDb = gql `
106
- query ListApisByDatabaseId {
107
- apis {
108
- nodes {
109
- id
110
- databaseId
111
- name
112
- dbname
113
- roleName
114
- anonRole
115
- isPublic
116
- domains {
117
- nodes {
118
- domain
119
- subdomain
120
- }
121
- }
122
- }
123
- }
124
- }
125
- `;
96
+ return buildFindFirstDocument('DomainLookup', 'domains', domainSelect, { where }, 'DomainFilter');
97
+ };
98
+ /**
99
+ * Build query for API lookup by database ID and name
100
+ * Uses the generated apiByDatabaseIdAndName custom query
101
+ */
102
+ export const buildApiByDatabaseIdAndName = (vars) => {
103
+ // Import buildCustomDocument locally to avoid circular dependency
104
+ const { buildCustomDocument } = require('../codegen/orm/query-builder');
105
+ return buildCustomDocument('query', 'ApiByDatabaseIdAndName', 'apiByDatabaseIdAndName', apiSelect, vars, [
106
+ { name: 'databaseId', type: 'UUID!' },
107
+ { name: 'name', type: 'String!' },
108
+ ]);
109
+ };
110
+ /**
111
+ * Build query to list all APIs
112
+ */
113
+ export const buildListApis = () => {
114
+ // Import buildCustomDocument locally to avoid circular dependency
115
+ const { buildCustomDocument } = require('../codegen/orm/query-builder');
116
+ return buildCustomDocument('query', 'ListApisByDatabaseId', 'apis', {
117
+ select: apisSelect,
118
+ connection: true,
119
+ }, undefined, []);
120
+ };
@@ -1,25 +1,34 @@
1
+ import { Logger } from '@pgpmjs/logger';
1
2
  import { graphileCache } from 'graphile-cache';
2
3
  import { getGraphileSettings as getSettings } from 'graphile-settings';
3
4
  import { getPgPool } from 'pg-cache';
4
5
  import { postgraphile } from 'postgraphile';
5
6
  import './types'; // for Request type
6
7
  import PublicKeySignature from '../plugins/PublicKeySignature';
8
+ const log = new Logger('graphile');
9
+ const reqLabel = (req) => req.requestId ? `[${req.requestId}]` : '[req]';
7
10
  export const graphile = (opts) => {
8
11
  return async (req, res, next) => {
12
+ const label = reqLabel(req);
9
13
  try {
10
14
  const api = req.api;
11
15
  if (!api) {
16
+ log.error(`${label} Missing API info`);
12
17
  return res.status(500).send('Missing API info');
13
18
  }
14
19
  const key = req.svc_key;
15
20
  if (!key) {
21
+ log.error(`${label} Missing service cache key`);
16
22
  return res.status(500).send('Missing service cache key');
17
23
  }
18
24
  const { dbname, anonRole, roleName, schema } = api;
19
- if (graphileCache.has(key)) {
20
- const { handler } = graphileCache.get(key);
21
- return handler(req, res, next);
25
+ const schemaLabel = schema?.join(',') || 'unknown';
26
+ const cached = graphileCache.get(key);
27
+ if (cached) {
28
+ log.debug(`${label} PostGraphile cache hit key=${key} db=${dbname} schemas=${schemaLabel}`);
29
+ return cached.handler(req, res, next);
22
30
  }
31
+ log.debug(`${label} PostGraphile cache miss key=${key} db=${dbname} schemas=${schemaLabel}`);
23
32
  const options = getSettings({
24
33
  ...opts,
25
34
  graphile: {
@@ -29,12 +38,14 @@ export const graphile = (opts) => {
29
38
  });
30
39
  const pubkey_challenge = api.apiModules.find((mod) => mod.name === 'pubkey_challenge');
31
40
  if (pubkey_challenge && pubkey_challenge.data) {
41
+ log.info(`${label} Enabling PublicKeySignature plugin for ${dbname}`);
32
42
  options.appendPlugins.push(PublicKeySignature(pubkey_challenge.data));
33
43
  }
34
44
  options.appendPlugins = options.appendPlugins ?? [];
35
45
  options.appendPlugins.push(...opts.graphile.appendPlugins);
36
46
  options.pgSettings = async function pgSettings(request) {
37
47
  const gqlReq = request;
48
+ const settingsLabel = reqLabel(gqlReq);
38
49
  const context = {
39
50
  [`jwt.claims.database_id`]: gqlReq.databaseId,
40
51
  [`jwt.claims.ip_address`]: gqlReq.clientIp,
@@ -46,6 +57,7 @@ export const graphile = (opts) => {
46
57
  context['jwt.claims.user_agent'] = gqlReq.get('User-Agent');
47
58
  }
48
59
  if (gqlReq?.token?.user_id) {
60
+ log.debug(`${settingsLabel} pgSettings role=${roleName} db=${gqlReq.databaseId} ip=${gqlReq.clientIp}`);
49
61
  return {
50
62
  role: roleName,
51
63
  [`jwt.claims.token_id`]: gqlReq.token.id,
@@ -53,6 +65,7 @@ export const graphile = (opts) => {
53
65
  ...context,
54
66
  };
55
67
  }
68
+ log.debug(`${settingsLabel} pgSettings role=${anonRole} db=${gqlReq.databaseId} ip=${gqlReq.clientIp}`);
56
69
  return { role: anonRole, ...context };
57
70
  };
58
71
  options.graphqlRoute = '/graphql';
@@ -65,6 +78,7 @@ export const graphile = (opts) => {
65
78
  ...options,
66
79
  ...opts.graphile.overrideSettings,
67
80
  };
81
+ log.info(`${label} Building PostGraphile handler key=${key} db=${dbname} schemas=${schemaLabel} role=${roleName} anon=${anonRole}`);
68
82
  const pgPool = getPgPool({
69
83
  ...opts.pg,
70
84
  database: dbname,
@@ -75,9 +89,11 @@ export const graphile = (opts) => {
75
89
  pgPoolKey: dbname,
76
90
  handler,
77
91
  });
92
+ log.info(`${label} Cached PostGraphile handler key=${key} db=${dbname}`);
78
93
  return handler(req, res, next);
79
94
  }
80
95
  catch (e) {
96
+ log.error(`${label} PostGraphile middleware error`, e);
81
97
  return res.status(500).send(e.message);
82
98
  }
83
99
  };
@@ -0,0 +1,71 @@
1
+ import { getEnvOptions } from '@constructive-io/graphql-env';
2
+ import { Logger } from '@pgpmjs/logger';
3
+ import { getGraphileSettings } from 'graphile-settings';
4
+ import { getPgPool } from 'pg-cache';
5
+ import { getSchema } from 'graphile-query';
6
+ import { printSchema } from 'graphql';
7
+ import { promises as fs } from 'node:fs';
8
+ import path from 'node:path';
9
+ const log = new Logger('codegen-schema');
10
+ const getSchemaOutputPath = () => process.env.CODEGEN_SCHEMA_OUT ??
11
+ path.resolve(__dirname, '../../codegen/schema.graphql');
12
+ (async () => {
13
+ try {
14
+ const opts = getEnvOptions();
15
+ const apiOpts = opts.api || {};
16
+ const metaSchemas = Array.isArray(apiOpts.metaSchemas)
17
+ ? apiOpts.metaSchemas
18
+ : [];
19
+ let schemas;
20
+ let usingFallback = false;
21
+ const dbName = process.env.CODEGEN_DATABASE || process.env.PGDATABASE || 'constructive';
22
+ const pgConfig = { ...opts.pg, database: dbName };
23
+ log.info(`Target database for codegen: ${dbName}`);
24
+ if (metaSchemas.length) {
25
+ schemas = metaSchemas;
26
+ log.info(`Using meta schemas: ${schemas.join(', ')}`);
27
+ const pool = getPgPool(pgConfig);
28
+ const checkResult = await pool.query(`SELECT schema_name FROM information_schema.schemata
29
+ WHERE schema_name = ANY($1::text[])
30
+ ORDER BY schema_name`, [schemas]);
31
+ const foundSchemas = checkResult.rows.map((r) => r.schema_name);
32
+ const missing = schemas.filter((s) => !foundSchemas.includes(s));
33
+ if (missing.length > 0) {
34
+ log.warn(`Missing schemas: ${missing.join(', ')}. Falling back to 'public'.`);
35
+ schemas = ['public'];
36
+ usingFallback = true;
37
+ }
38
+ }
39
+ else {
40
+ schemas = ['public'];
41
+ log.info('No meta schemas configured, using: public');
42
+ usingFallback = true;
43
+ }
44
+ if (usingFallback) {
45
+ log.warn('Schema will be generated from public schema only. To generate full meta schema, ensure meta tables exist.');
46
+ }
47
+ const settings = getGraphileSettings({
48
+ ...opts,
49
+ pg: pgConfig,
50
+ graphile: {
51
+ ...opts.graphile,
52
+ schema: schemas,
53
+ },
54
+ });
55
+ const pool = getPgPool(pgConfig);
56
+ log.debug(`Connecting to database: ${dbName}`);
57
+ log.debug(`Connecting to host: ${opts.pg?.host || '(default)'}`);
58
+ const dbInfo = await pool.query('SELECT current_database() AS current_database, current_user AS current_user');
59
+ log.info(`Connected to database: ${dbInfo.rows[0]?.current_database} as user: ${dbInfo.rows[0]?.current_user}`);
60
+ const graphqlSchema = await getSchema(pool, settings);
61
+ const sdl = printSchema(graphqlSchema);
62
+ const outputPath = getSchemaOutputPath();
63
+ await fs.mkdir(path.dirname(outputPath), { recursive: true });
64
+ await fs.writeFile(outputPath, sdl, 'utf8');
65
+ log.success(`Schema written to ${outputPath}`);
66
+ }
67
+ catch (error) {
68
+ log.error('Failed to write schema', error?.stack || error);
69
+ process.exitCode = 1;
70
+ }
71
+ })();
package/esm/server.js CHANGED
@@ -2,6 +2,7 @@ import { getEnvOptions, getNodeEnv } from '@constructive-io/graphql-env';
2
2
  import { Logger } from '@pgpmjs/logger';
3
3
  import { healthz, poweredBy, trustProxy } from '@pgpmjs/server-utils';
4
4
  import { middleware as parseDomains } from '@constructive-io/url-domains';
5
+ import { randomUUID } from 'crypto';
5
6
  import express from 'express';
6
7
  // @ts-ignore
7
8
  import graphqlUpload from 'graphql-upload';
@@ -28,6 +29,25 @@ class Server {
28
29
  const app = express();
29
30
  const api = createApiMiddleware(opts);
30
31
  const authenticate = createAuthenticateMiddleware(opts);
32
+ const requestLogger = (req, res, next) => {
33
+ const headerRequestId = req.header('x-request-id');
34
+ const reqId = headerRequestId || randomUUID();
35
+ const start = process.hrtime.bigint();
36
+ req.requestId = reqId;
37
+ const host = req.hostname || req.headers.host || 'unknown';
38
+ const ip = req.clientIp || req.ip;
39
+ log.debug(`[${reqId}] -> ${req.method} ${req.originalUrl} host=${host} ip=${ip}`);
40
+ res.on('finish', () => {
41
+ const durationMs = Number(process.hrtime.bigint() - start) / 1e6;
42
+ const apiInfo = req.api
43
+ ? `db=${req.api.dbname} schemas=${req.api.schema?.join(',') || 'none'}`
44
+ : 'api=unresolved';
45
+ const authInfo = req.token ? 'auth=token' : 'auth=anon';
46
+ const svcInfo = req.svc_key ? `svc=${req.svc_key}` : 'svc=unset';
47
+ log.debug(`[${reqId}] <- ${res.statusCode} ${req.method} ${req.originalUrl} (${durationMs.toFixed(1)} ms) ${apiInfo} ${svcInfo} ${authInfo}`);
48
+ });
49
+ next();
50
+ };
31
51
  // Log startup config in dev mode
32
52
  if (isDev()) {
33
53
  log.debug(`Database: ${opts.pg?.database}@${opts.pg?.host}:${opts.pg?.port}`);
@@ -50,6 +70,7 @@ class Server {
50
70
  app.use(graphqlUpload.graphqlUploadExpress());
51
71
  app.use(parseDomains());
52
72
  app.use(requestIp.mw());
73
+ app.use(requestLogger);
53
74
  app.use(api);
54
75
  app.use(authenticate);
55
76
  app.use(graphile(opts));
package/middleware/api.js CHANGED
@@ -18,8 +18,8 @@ const log = new logger_1.Logger('api');
18
18
  const isDev = () => (0, graphql_env_1.getNodeEnv)() === 'development';
19
19
  const transformServiceToApi = (svc) => {
20
20
  const api = svc.data.api;
21
- const schemaNames = api.schemaNamesFromExt?.nodes?.map((n) => n.schemaName) || [];
22
- const additionalSchemas = api.schemaNames?.nodes?.map((n) => n.schemaName) || [];
21
+ const schemaNames = api.apiExtensions?.nodes?.map((n) => n.schemaName) || [];
22
+ const additionalSchemas = api.schemasByApiSchemaApiIdAndSchemaId?.nodes?.map((n) => n.schemaName) || [];
23
23
  let domains = [];
24
24
  if (api.database?.sites?.nodes) {
25
25
  domains = api.database.sites.nodes.reduce((acc, site) => {
@@ -169,10 +169,11 @@ const getMetaSchema = ({ opts, key, databaseId, }) => {
169
169
  return svc;
170
170
  };
171
171
  const queryServiceByDomainAndSubdomain = async ({ opts, key, client, domain, subdomain, }) => {
172
+ const doc = (0, gql_1.buildDomainLookup)({ domain, subdomain });
172
173
  const result = await client.query({
173
174
  role: 'administrator',
174
- query: gql_1.ApiQuery,
175
- variables: { domain, subdomain },
175
+ query: doc.document,
176
+ variables: doc.variables,
176
177
  });
177
178
  if (result.errors?.length) {
178
179
  log.error('GraphQL query errors:', result.errors);
@@ -191,10 +192,11 @@ const queryServiceByDomainAndSubdomain = async ({ opts, key, client, domain, sub
191
192
  return null;
192
193
  };
193
194
  const queryServiceByApiName = async ({ opts, key, client, databaseId, name, }) => {
195
+ const doc = (0, gql_1.buildApiByDatabaseIdAndName)({ databaseId, name });
194
196
  const result = await client.query({
195
197
  role: 'administrator',
196
- query: gql_1.ApiByNameQuery,
197
- variables: { databaseId, name },
198
+ query: doc.document,
199
+ variables: doc.variables,
198
200
  });
199
201
  if (result.errors?.length) {
200
202
  log.error('GraphQL query errors:', result.errors);
@@ -320,8 +322,7 @@ const getApiConfig = async (opts, req) => {
320
322
  const fallbackResult = await client.query({
321
323
  role: 'administrator',
322
324
  // @ts-ignore
323
- query: gql_1.ListOfAllDomainsOfDb,
324
- // variables: { databaseId }
325
+ query: (0, gql_1.buildListApis)().document,
325
326
  });
326
327
  if (!fallbackResult.errors?.length &&
327
328
  fallbackResult.data?.apis?.nodes?.length) {
@@ -1,6 +1,26 @@
1
- import gql from 'graphql-tag';
2
- type GraphQLDocument = ReturnType<typeof gql>;
3
- export declare const ApiQuery: GraphQLDocument;
4
- export declare const ApiByNameQuery: GraphQLDocument;
5
- export declare const ListOfAllDomainsOfDb: GraphQLDocument;
1
+ type BuiltDocument = {
2
+ document: string;
3
+ variables: Record<string, unknown>;
4
+ };
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;
6
26
  export {};