@constructive-io/graphql-server 2.14.8 → 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.
- package/codegen/orm/client.d.ts +55 -0
- package/codegen/orm/client.js +75 -0
- package/codegen/orm/index.d.ts +632 -0
- package/codegen/orm/index.js +182 -0
- package/codegen/orm/input-types.d.ts +13248 -0
- package/codegen/orm/input-types.js +7 -0
- package/codegen/orm/models/api.d.ts +42 -0
- package/codegen/orm/models/api.js +77 -0
- package/codegen/orm/models/apiExtension.d.ts +42 -0
- package/codegen/orm/models/apiExtension.js +77 -0
- package/codegen/orm/models/apiModule.d.ts +42 -0
- package/codegen/orm/models/apiModule.js +77 -0
- package/codegen/orm/models/apiSchema.d.ts +42 -0
- package/codegen/orm/models/apiSchema.js +77 -0
- package/codegen/orm/models/app.d.ts +42 -0
- package/codegen/orm/models/app.js +77 -0
- package/codegen/orm/models/checkConstraint.d.ts +42 -0
- package/codegen/orm/models/checkConstraint.js +77 -0
- package/codegen/orm/models/connectedAccountsModule.d.ts +42 -0
- package/codegen/orm/models/connectedAccountsModule.js +77 -0
- package/codegen/orm/models/cryptoAddressesModule.d.ts +42 -0
- package/codegen/orm/models/cryptoAddressesModule.js +77 -0
- package/codegen/orm/models/cryptoAuthModule.d.ts +42 -0
- package/codegen/orm/models/cryptoAuthModule.js +77 -0
- package/codegen/orm/models/database.d.ts +42 -0
- package/codegen/orm/models/database.js +77 -0
- package/codegen/orm/models/databaseExtension.d.ts +42 -0
- package/codegen/orm/models/databaseExtension.js +77 -0
- package/codegen/orm/models/databaseProvision.d.ts +42 -0
- package/codegen/orm/models/databaseProvision.js +77 -0
- package/codegen/orm/models/defaultIdsModule.d.ts +42 -0
- package/codegen/orm/models/defaultIdsModule.js +77 -0
- package/codegen/orm/models/denormalizedTableField.d.ts +42 -0
- package/codegen/orm/models/denormalizedTableField.js +77 -0
- package/codegen/orm/models/domain.d.ts +42 -0
- package/codegen/orm/models/domain.js +77 -0
- package/codegen/orm/models/emailsModule.d.ts +42 -0
- package/codegen/orm/models/emailsModule.js +77 -0
- package/codegen/orm/models/encryptedSecretsModule.d.ts +42 -0
- package/codegen/orm/models/encryptedSecretsModule.js +77 -0
- package/codegen/orm/models/extension.d.ts +42 -0
- package/codegen/orm/models/extension.js +77 -0
- package/codegen/orm/models/field.d.ts +42 -0
- package/codegen/orm/models/field.js +77 -0
- package/codegen/orm/models/fieldModule.d.ts +42 -0
- package/codegen/orm/models/fieldModule.js +77 -0
- package/codegen/orm/models/foreignKeyConstraint.d.ts +42 -0
- package/codegen/orm/models/foreignKeyConstraint.js +77 -0
- package/codegen/orm/models/fullTextSearch.d.ts +42 -0
- package/codegen/orm/models/fullTextSearch.js +77 -0
- package/codegen/orm/models/hierarchyModule.d.ts +42 -0
- package/codegen/orm/models/hierarchyModule.js +77 -0
- package/codegen/orm/models/index.d.ts +64 -0
- package/codegen/orm/models/index.js +127 -0
- package/codegen/orm/models/indexModel.d.ts +42 -0
- package/codegen/orm/models/indexModel.js +77 -0
- package/codegen/orm/models/invitesModule.d.ts +42 -0
- package/codegen/orm/models/invitesModule.js +77 -0
- package/codegen/orm/models/levelsModule.d.ts +42 -0
- package/codegen/orm/models/levelsModule.js +77 -0
- package/codegen/orm/models/limitFunction.d.ts +42 -0
- package/codegen/orm/models/limitFunction.js +77 -0
- package/codegen/orm/models/limitsModule.d.ts +42 -0
- package/codegen/orm/models/limitsModule.js +77 -0
- package/codegen/orm/models/membershipTypesModule.d.ts +42 -0
- package/codegen/orm/models/membershipTypesModule.js +77 -0
- package/codegen/orm/models/membershipsModule.d.ts +42 -0
- package/codegen/orm/models/membershipsModule.js +77 -0
- package/codegen/orm/models/module.d.ts +42 -0
- package/codegen/orm/models/module.js +77 -0
- package/codegen/orm/models/moduleDefinition.d.ts +42 -0
- package/codegen/orm/models/moduleDefinition.js +77 -0
- package/codegen/orm/models/moduleField.d.ts +42 -0
- package/codegen/orm/models/moduleField.js +77 -0
- package/codegen/orm/models/moduleInputRecord.d.ts +42 -0
- package/codegen/orm/models/moduleInputRecord.js +77 -0
- package/codegen/orm/models/moduleOutput.d.ts +42 -0
- package/codegen/orm/models/moduleOutput.js +77 -0
- package/codegen/orm/models/permissionsModule.d.ts +42 -0
- package/codegen/orm/models/permissionsModule.js +77 -0
- package/codegen/orm/models/phoneNumbersModule.d.ts +42 -0
- package/codegen/orm/models/phoneNumbersModule.js +77 -0
- package/codegen/orm/models/policy.d.ts +42 -0
- package/codegen/orm/models/policy.js +77 -0
- package/codegen/orm/models/primaryKeyConstraint.d.ts +42 -0
- package/codegen/orm/models/primaryKeyConstraint.js +77 -0
- package/codegen/orm/models/procedure.d.ts +42 -0
- package/codegen/orm/models/procedure.js +77 -0
- package/codegen/orm/models/profilesModule.d.ts +42 -0
- package/codegen/orm/models/profilesModule.js +77 -0
- package/codegen/orm/models/rlsFunction.d.ts +42 -0
- package/codegen/orm/models/rlsFunction.js +77 -0
- package/codegen/orm/models/rlsModule.d.ts +42 -0
- package/codegen/orm/models/rlsModule.js +77 -0
- package/codegen/orm/models/schema.d.ts +42 -0
- package/codegen/orm/models/schema.js +77 -0
- package/codegen/orm/models/schemaGrant.d.ts +42 -0
- package/codegen/orm/models/schemaGrant.js +77 -0
- package/codegen/orm/models/secretsModule.d.ts +42 -0
- package/codegen/orm/models/secretsModule.js +77 -0
- package/codegen/orm/models/site.d.ts +42 -0
- package/codegen/orm/models/site.js +77 -0
- package/codegen/orm/models/siteMetadatum.d.ts +42 -0
- package/codegen/orm/models/siteMetadatum.js +77 -0
- package/codegen/orm/models/siteModule.d.ts +42 -0
- package/codegen/orm/models/siteModule.js +77 -0
- package/codegen/orm/models/siteTheme.d.ts +42 -0
- package/codegen/orm/models/siteTheme.js +77 -0
- package/codegen/orm/models/table.d.ts +42 -0
- package/codegen/orm/models/table.js +77 -0
- package/codegen/orm/models/tableGrant.d.ts +42 -0
- package/codegen/orm/models/tableGrant.js +77 -0
- package/codegen/orm/models/tokensModule.d.ts +42 -0
- package/codegen/orm/models/tokensModule.js +77 -0
- package/codegen/orm/models/trigger.d.ts +42 -0
- package/codegen/orm/models/trigger.js +77 -0
- package/codegen/orm/models/triggerFunction.d.ts +42 -0
- package/codegen/orm/models/triggerFunction.js +77 -0
- package/codegen/orm/models/uniqueConstraint.d.ts +42 -0
- package/codegen/orm/models/uniqueConstraint.js +77 -0
- package/codegen/orm/models/userAuthModule.d.ts +42 -0
- package/codegen/orm/models/userAuthModule.js +77 -0
- package/codegen/orm/models/usersModule.d.ts +42 -0
- package/codegen/orm/models/usersModule.js +77 -0
- package/codegen/orm/models/uuidModule.d.ts +42 -0
- package/codegen/orm/models/uuidModule.js +77 -0
- package/codegen/orm/mutation/index.d.ts +531 -0
- package/codegen/orm/mutation/index.js +596 -0
- package/codegen/orm/query/index.d.ts +274 -0
- package/codegen/orm/query/index.js +290 -0
- package/codegen/orm/query-builder.d.ts +80 -0
- package/codegen/orm/query-builder.js +249 -0
- package/codegen/orm/select-types.d.ts +50 -0
- package/codegen/orm/select-types.js +7 -0
- package/codegen/orm/types.d.ts +6 -0
- package/codegen/orm/types.js +23 -0
- package/esm/codegen/orm/client.js +70 -0
- package/esm/codegen/orm/index.js +162 -0
- package/esm/codegen/orm/input-types.js +6 -0
- package/esm/codegen/orm/models/api.js +73 -0
- package/esm/codegen/orm/models/apiExtension.js +73 -0
- package/esm/codegen/orm/models/apiModule.js +73 -0
- package/esm/codegen/orm/models/apiSchema.js +73 -0
- package/esm/codegen/orm/models/app.js +73 -0
- package/esm/codegen/orm/models/checkConstraint.js +73 -0
- package/esm/codegen/orm/models/connectedAccountsModule.js +73 -0
- package/esm/codegen/orm/models/cryptoAddressesModule.js +73 -0
- package/esm/codegen/orm/models/cryptoAuthModule.js +73 -0
- package/esm/codegen/orm/models/database.js +73 -0
- package/esm/codegen/orm/models/databaseExtension.js +73 -0
- package/esm/codegen/orm/models/databaseProvision.js +73 -0
- package/esm/codegen/orm/models/defaultIdsModule.js +73 -0
- package/esm/codegen/orm/models/denormalizedTableField.js +73 -0
- package/esm/codegen/orm/models/domain.js +73 -0
- package/esm/codegen/orm/models/emailsModule.js +73 -0
- package/esm/codegen/orm/models/encryptedSecretsModule.js +73 -0
- package/esm/codegen/orm/models/extension.js +73 -0
- package/esm/codegen/orm/models/field.js +73 -0
- package/esm/codegen/orm/models/fieldModule.js +73 -0
- package/esm/codegen/orm/models/foreignKeyConstraint.js +73 -0
- package/esm/codegen/orm/models/fullTextSearch.js +73 -0
- package/esm/codegen/orm/models/hierarchyModule.js +73 -0
- package/esm/codegen/orm/models/index.js +64 -0
- package/esm/codegen/orm/models/indexModel.js +73 -0
- package/esm/codegen/orm/models/invitesModule.js +73 -0
- package/esm/codegen/orm/models/levelsModule.js +73 -0
- package/esm/codegen/orm/models/limitFunction.js +73 -0
- package/esm/codegen/orm/models/limitsModule.js +73 -0
- package/esm/codegen/orm/models/membershipTypesModule.js +73 -0
- package/esm/codegen/orm/models/membershipsModule.js +73 -0
- package/esm/codegen/orm/models/module.js +73 -0
- package/esm/codegen/orm/models/moduleDefinition.js +73 -0
- package/esm/codegen/orm/models/moduleField.js +73 -0
- package/esm/codegen/orm/models/moduleInputRecord.js +73 -0
- package/esm/codegen/orm/models/moduleOutput.js +73 -0
- package/esm/codegen/orm/models/permissionsModule.js +73 -0
- package/esm/codegen/orm/models/phoneNumbersModule.js +73 -0
- package/esm/codegen/orm/models/policy.js +73 -0
- package/esm/codegen/orm/models/primaryKeyConstraint.js +73 -0
- package/esm/codegen/orm/models/procedure.js +73 -0
- package/esm/codegen/orm/models/profilesModule.js +73 -0
- package/esm/codegen/orm/models/rlsFunction.js +73 -0
- package/esm/codegen/orm/models/rlsModule.js +73 -0
- package/esm/codegen/orm/models/schema.js +73 -0
- package/esm/codegen/orm/models/schemaGrant.js +73 -0
- package/esm/codegen/orm/models/secretsModule.js +73 -0
- package/esm/codegen/orm/models/site.js +73 -0
- package/esm/codegen/orm/models/siteMetadatum.js +73 -0
- package/esm/codegen/orm/models/siteModule.js +73 -0
- package/esm/codegen/orm/models/siteTheme.js +73 -0
- package/esm/codegen/orm/models/table.js +73 -0
- package/esm/codegen/orm/models/tableGrant.js +73 -0
- package/esm/codegen/orm/models/tokensModule.js +73 -0
- package/esm/codegen/orm/models/trigger.js +73 -0
- package/esm/codegen/orm/models/triggerFunction.js +73 -0
- package/esm/codegen/orm/models/uniqueConstraint.js +73 -0
- package/esm/codegen/orm/models/userAuthModule.js +73 -0
- package/esm/codegen/orm/models/usersModule.js +73 -0
- package/esm/codegen/orm/models/uuidModule.js +73 -0
- package/esm/codegen/orm/mutation/index.js +593 -0
- package/esm/codegen/orm/query/index.js +287 -0
- package/esm/codegen/orm/query-builder.js +238 -0
- package/esm/codegen/orm/select-types.js +6 -0
- package/esm/codegen/orm/types.js +7 -0
- package/esm/middleware/api.js +10 -9
- package/esm/middleware/gql.js +118 -123
- package/esm/middleware/graphile.js +19 -3
- package/esm/scripts/codegen-schema.js +71 -0
- package/esm/server.js +21 -0
- package/middleware/api.js +9 -8
- package/middleware/gql.d.ts +25 -5
- package/middleware/gql.js +122 -127
- package/middleware/graphile.js +19 -3
- package/middleware/types.d.ts +1 -0
- package/package.json +15 -11
- package/scripts/codegen-schema.d.ts +1 -0
- package/scripts/codegen-schema.js +76 -0
- package/server.js +21 -0
- package/types.d.ts +2 -2
package/esm/middleware/gql.js
CHANGED
|
@@ -1,125 +1,120 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
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
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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.
|
|
22
|
-
const additionalSchemas = api.
|
|
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:
|
|
175
|
-
variables:
|
|
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:
|
|
197
|
-
variables:
|
|
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.
|
|
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) {
|
package/middleware/gql.d.ts
CHANGED
|
@@ -1,6 +1,26 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
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 {};
|