@foxtware/mineral 0.1.3 → 0.1.5
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/.hosting.yml.sample +6 -3
- package/_deploy_scripts/deployFromHostingYml.js +23 -24
- package/_deploy_scripts/generateHosted.js +19 -5
- package/bin/mineral.js +11 -0
- package/handlerPaths.js +23 -0
- package/hosting.utils.js +77 -49
- package/package.json +1 -1
- package/server.js +3 -7
- package/server.utils.js +0 -19
- package/wrappers.js +37 -0
package/.hosting.yml.sample
CHANGED
|
@@ -4,16 +4,19 @@ google_cloud_info:
|
|
|
4
4
|
|
|
5
5
|
# TODO: consider credsPayload in google_cloud_info instead of workspace .creds.yml
|
|
6
6
|
|
|
7
|
+
env:
|
|
8
|
+
- HOSTED_API_KEY
|
|
9
|
+
|
|
7
10
|
functions:
|
|
8
11
|
exampleFunction:
|
|
9
12
|
max_instances: 1
|
|
10
13
|
timeout: 300s
|
|
11
14
|
wrappers:
|
|
12
|
-
- requireHostedApiKey
|
|
13
15
|
# entry_point: otherExportName
|
|
16
|
+
- requireHostedApiKey
|
|
17
|
+
- allowCrossOriginCallsAndHandleOptions
|
|
14
18
|
|
|
15
|
-
|
|
16
|
-
source: '@foxtware/mineral/api/pokemon/pokemonPokeballThrow.js'
|
|
19
|
+
pokemonPokeballThrow:
|
|
17
20
|
max_instances: 1
|
|
18
21
|
wrappers:
|
|
19
22
|
- checkTrainer
|
|
@@ -2,7 +2,13 @@ const readline = require('readline');
|
|
|
2
2
|
const { toAbsolutePath, setWorkspace } = require('../api/workspace');
|
|
3
3
|
const { getApiDirs, readCliFlag } = require('../cli');
|
|
4
4
|
const { loadHandlers } = require('../server');
|
|
5
|
-
const {
|
|
5
|
+
const {
|
|
6
|
+
readHostingYml,
|
|
7
|
+
getCredsJsonForDeploy,
|
|
8
|
+
validateEnvVarsForDeploy,
|
|
9
|
+
resolveHostedHandlersForDeploy,
|
|
10
|
+
functionUsesWrapper,
|
|
11
|
+
} = require('../hosting.utils');
|
|
6
12
|
const { writeHostedJs } = require('./generateHosted');
|
|
7
13
|
const { execCommand } = require('./execCommand');
|
|
8
14
|
const { formatSetEnvVarsForGcloud, shellQuoteSingle } = require('./setEnvVarsGcloud');
|
|
@@ -44,10 +50,6 @@ const chooseOption = async (prompt, options) => new Promise((resolve) => {
|
|
|
44
50
|
});
|
|
45
51
|
});
|
|
46
52
|
|
|
47
|
-
const anyHostedEntryUsesWrapper = (hostedEntries, wrapperName) => (
|
|
48
|
-
hostedEntries.some((hostedEntry) => hostedEntry.wrappers?.includes(wrapperName))
|
|
49
|
-
);
|
|
50
|
-
|
|
51
53
|
const getDeployArgs = () => {
|
|
52
54
|
const args = process.argv.slice(2);
|
|
53
55
|
const deployArgs = [];
|
|
@@ -55,6 +57,10 @@ const getDeployArgs = () => {
|
|
|
55
57
|
for (let index = 0; index < args.length; index++) {
|
|
56
58
|
const arg = args[index];
|
|
57
59
|
|
|
60
|
+
if (arg === 'host') {
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
|
|
58
64
|
if (arg === '--workspace' || arg === '--api_dirs') {
|
|
59
65
|
index++;
|
|
60
66
|
continue;
|
|
@@ -86,7 +92,7 @@ const deployFunction = async ({
|
|
|
86
92
|
googleCloudInfo,
|
|
87
93
|
workspace,
|
|
88
94
|
credsJson,
|
|
89
|
-
|
|
95
|
+
deployEnvVars = {},
|
|
90
96
|
}) => {
|
|
91
97
|
const config = {
|
|
92
98
|
...googleCloudInfo,
|
|
@@ -113,12 +119,9 @@ const deployFunction = async ({
|
|
|
113
119
|
const envParts = [
|
|
114
120
|
'HOSTED=true',
|
|
115
121
|
`CREDS=${ credsJson }`,
|
|
122
|
+
...Object.entries(deployEnvVars).map(([envName, envValue]) => `${ envName }=${ envValue }`),
|
|
116
123
|
];
|
|
117
124
|
|
|
118
|
-
if (hostedApiKey) {
|
|
119
|
-
envParts.push(`HOSTED_API_KEY=${ hostedApiKey }`);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
125
|
if (extraSetEnvVars) {
|
|
123
126
|
envParts.push(extraSetEnvVars);
|
|
124
127
|
}
|
|
@@ -154,6 +157,7 @@ const deployFunction = async ({
|
|
|
154
157
|
}
|
|
155
158
|
|
|
156
159
|
const requiresHostedApiKey = functionUsesWrapper(functionConfig, 'requireHostedApiKey');
|
|
160
|
+
const hostedApiKey = deployEnvVars.HOSTED_API_KEY;
|
|
157
161
|
|
|
158
162
|
for (const schedule of schedules) {
|
|
159
163
|
const {
|
|
@@ -203,6 +207,7 @@ const deployFromHostingYml = async (options = {}) => {
|
|
|
203
207
|
const hostingConfig = readHostingYml(config.workspace);
|
|
204
208
|
const {
|
|
205
209
|
google_cloud_info: googleCloudInfo,
|
|
210
|
+
env: envNames = [],
|
|
206
211
|
functions = {},
|
|
207
212
|
groups = {},
|
|
208
213
|
} = hostingConfig;
|
|
@@ -222,12 +227,7 @@ const deployFromHostingYml = async (options = {}) => {
|
|
|
222
227
|
handlersByName,
|
|
223
228
|
});
|
|
224
229
|
|
|
225
|
-
|
|
226
|
-
const hostedApiKey = getHostedApiKeyForDeploy(config.workspace);
|
|
227
|
-
if (!hostedApiKey) {
|
|
228
|
-
throw new Error('HOSTED_API_KEY is required in workspace .env when using requireHostedApiKey wrapper');
|
|
229
|
-
}
|
|
230
|
-
}
|
|
230
|
+
const deployEnvVars = validateEnvVarsForDeploy(config.workspace, envNames);
|
|
231
231
|
|
|
232
232
|
writeHostedJs({
|
|
233
233
|
workspace: config.workspace,
|
|
@@ -235,7 +235,6 @@ const deployFromHostingYml = async (options = {}) => {
|
|
|
235
235
|
});
|
|
236
236
|
|
|
237
237
|
const credsJson = getCredsJsonForDeploy(config.workspace);
|
|
238
|
-
const hostedApiKey = getHostedApiKeyForDeploy(config.workspace);
|
|
239
238
|
const deployArgs = getDeployArgs();
|
|
240
239
|
|
|
241
240
|
const deployOne = async (functionName) => {
|
|
@@ -250,7 +249,7 @@ const deployFromHostingYml = async (options = {}) => {
|
|
|
250
249
|
googleCloudInfo,
|
|
251
250
|
workspace: config.workspace,
|
|
252
251
|
credsJson,
|
|
253
|
-
|
|
252
|
+
deployEnvVars,
|
|
254
253
|
});
|
|
255
254
|
};
|
|
256
255
|
|
|
@@ -289,9 +288,9 @@ const deployFromHostingYml = async (options = {}) => {
|
|
|
289
288
|
|
|
290
289
|
console.log(`
|
|
291
290
|
Usage (from workspace repo):
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
291
|
+
mineral host --workspace . --api_dirs api all
|
|
292
|
+
mineral host --workspace . --api_dirs api function
|
|
293
|
+
mineral host --workspace . --api_dirs api group
|
|
295
294
|
`);
|
|
296
295
|
};
|
|
297
296
|
|
|
@@ -309,11 +308,11 @@ module.exports = {
|
|
|
309
308
|
|
|
310
309
|
/*
|
|
311
310
|
Deploy everything:
|
|
312
|
-
|
|
311
|
+
mineral host all
|
|
313
312
|
|
|
314
313
|
Deploy a single function:
|
|
315
|
-
|
|
314
|
+
mineral host function
|
|
316
315
|
|
|
317
316
|
Deploy a group of functions:
|
|
318
|
-
|
|
317
|
+
mineral host group
|
|
319
318
|
*/
|
|
@@ -1,23 +1,37 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const { wrapHostedFunction } = require('../hosting.utils');
|
|
3
3
|
|
|
4
|
+
const formatResolvedWrapperForHostedJs = ({ modulePath, wrapperName }) => (
|
|
5
|
+
`require('${ modulePath }').${ wrapperName }`
|
|
6
|
+
);
|
|
7
|
+
|
|
8
|
+
const formatWrappersArg = (resolvedWrappers = []) => {
|
|
9
|
+
if (!resolvedWrappers.length) {
|
|
10
|
+
return '';
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const wrapperLines = resolvedWrappers.map((resolvedWrapper) => (
|
|
14
|
+
` ${ formatResolvedWrapperForHostedJs(resolvedWrapper) },`
|
|
15
|
+
));
|
|
16
|
+
return `, [\n${ wrapperLines.join('\n') }\n ]`;
|
|
17
|
+
};
|
|
18
|
+
|
|
4
19
|
const generateHostedJs = ({
|
|
5
20
|
hostedHandlers,
|
|
6
21
|
}) => {
|
|
7
22
|
const exportLines = [];
|
|
8
|
-
const hostingUtilsRequire = '@foxtware/mineral/hosting.utils';
|
|
9
23
|
|
|
10
24
|
for (const hostedHandler of hostedHandlers) {
|
|
11
|
-
const {
|
|
12
|
-
const wrappersArg =
|
|
25
|
+
const { hostedName, handlerName, resolvedWrappers = [], requirePath } = hostedHandler;
|
|
26
|
+
const wrappersArg = formatWrappersArg(resolvedWrappers);
|
|
13
27
|
|
|
14
28
|
exportLines.push(
|
|
15
|
-
` ${
|
|
29
|
+
` ${ hostedName }: wrapHostedFunction(() => require('${ requirePath }'), '${ handlerName }'${ wrappersArg }),`,
|
|
16
30
|
);
|
|
17
31
|
}
|
|
18
32
|
|
|
19
33
|
return `// Generated by mineral — do not edit
|
|
20
|
-
const { wrapHostedFunction } = require('
|
|
34
|
+
const { wrapHostedFunction } = require('@foxtware/mineral/hosting.utils');
|
|
21
35
|
|
|
22
36
|
module.exports = {
|
|
23
37
|
${ exportLines.join('\n') }
|
package/bin/mineral.js
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
const command = process.argv[2];
|
|
4
|
+
|
|
5
|
+
if (command === 'host') {
|
|
6
|
+
const { deployFromHostingYml } = require('../_deploy_scripts/deployFromHostingYml');
|
|
7
|
+
deployFromHostingYml().catch((error) => {
|
|
8
|
+
console.error(error);
|
|
9
|
+
process.exit(1);
|
|
10
|
+
});
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
|
|
3
14
|
const { startServer } = require('../server');
|
|
4
15
|
|
|
5
16
|
startServer();
|
package/handlerPaths.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
const MINERAL_ROOT = __dirname;
|
|
2
|
+
const MINERAL_API_DIR = `${ MINERAL_ROOT }/api`;
|
|
3
|
+
|
|
4
|
+
const getRequirePathForHandler = (handler, workspace) => {
|
|
5
|
+
const normalizedWorkspace = workspace.replace(/\/$/, '');
|
|
6
|
+
const { filePath } = handler;
|
|
7
|
+
|
|
8
|
+
if (filePath.startsWith(`${ normalizedWorkspace }/`)) {
|
|
9
|
+
return `./${ filePath.slice(normalizedWorkspace.length + 1) }`;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
if (filePath.startsWith(`${ MINERAL_API_DIR }/`)) {
|
|
13
|
+
const relativePath = filePath.slice(MINERAL_API_DIR.length + 1);
|
|
14
|
+
return `@foxtware/mineral/api/${ relativePath }`;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
throw new Error(`Handler "${ handler.routeName }" is not in workspace or mineral api dirs`);
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
module.exports = {
|
|
21
|
+
MINERAL_API_DIR,
|
|
22
|
+
getRequirePathForHandler,
|
|
23
|
+
};
|
package/hosting.utils.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const { createRequire } = require('module');
|
|
3
3
|
const yaml = require('yaml');
|
|
4
|
+
const { getRequirePathForHandler } = require('./handlerPaths');
|
|
4
5
|
const {
|
|
5
6
|
respondJson,
|
|
6
7
|
errorToReadable,
|
|
@@ -8,49 +9,64 @@ const {
|
|
|
8
9
|
argsFromBody,
|
|
9
10
|
funcApi,
|
|
10
11
|
wrapFunction,
|
|
11
|
-
requireHostedApiKey,
|
|
12
12
|
statusCodeFromResult,
|
|
13
13
|
} = require('./server.utils');
|
|
14
14
|
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
};
|
|
15
|
+
const MINERAL_WRAPPERS_MODULE = '@foxtware/mineral/wrappers.js';
|
|
16
|
+
const WORKSPACE_WRAPPERS_MODULE = './wrappers.js';
|
|
18
17
|
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
18
|
+
const getMineralWrappers = (workspaceRequire) => {
|
|
19
|
+
try {
|
|
20
|
+
return workspaceRequire(MINERAL_WRAPPERS_MODULE);
|
|
21
|
+
} catch (error) {
|
|
22
|
+
if (error.code !== 'MODULE_NOT_FOUND') {
|
|
23
|
+
throw error;
|
|
24
24
|
}
|
|
25
|
-
return wrapper;
|
|
26
|
-
})
|
|
27
|
-
);
|
|
28
25
|
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
return require('./wrappers.js');
|
|
27
|
+
}
|
|
28
|
+
};
|
|
31
29
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
30
|
+
const resolveWrapperName = (wrapperName, workspaceRequire) => {
|
|
31
|
+
if (typeof wrapperName !== 'string' || !wrapperName.trim()) {
|
|
32
|
+
throw new Error(`Invalid wrapper name: ${ wrapperName }`);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const mineralWrappers = getMineralWrappers(workspaceRequire);
|
|
36
|
+
if (typeof mineralWrappers[wrapperName] === 'function') {
|
|
37
|
+
return {
|
|
38
|
+
wrapperName,
|
|
39
|
+
modulePath: MINERAL_WRAPPERS_MODULE,
|
|
37
40
|
};
|
|
41
|
+
}
|
|
38
42
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
43
|
+
try {
|
|
44
|
+
const workspaceWrappers = workspaceRequire(WORKSPACE_WRAPPERS_MODULE);
|
|
45
|
+
if (typeof workspaceWrappers[wrapperName] === 'function') {
|
|
46
|
+
return {
|
|
47
|
+
wrapperName,
|
|
48
|
+
modulePath: WORKSPACE_WRAPPERS_MODULE,
|
|
49
|
+
};
|
|
42
50
|
}
|
|
43
|
-
|
|
44
|
-
if (
|
|
45
|
-
|
|
51
|
+
} catch (error) {
|
|
52
|
+
if (error.code !== 'MODULE_NOT_FOUND') {
|
|
53
|
+
throw error;
|
|
46
54
|
}
|
|
47
|
-
|
|
48
|
-
byEntryPoint.set(entryPoint, existing);
|
|
49
55
|
}
|
|
50
56
|
|
|
51
|
-
|
|
57
|
+
throw new Error(
|
|
58
|
+
`Wrapper "${ wrapperName }" not found in ${ MINERAL_WRAPPERS_MODULE } or ${ WORKSPACE_WRAPPERS_MODULE }`,
|
|
59
|
+
);
|
|
52
60
|
};
|
|
53
61
|
|
|
62
|
+
const getHostedEntries = (functions = {}) => (
|
|
63
|
+
Object.entries(functions).map(([hostedName, functionConfig = {}]) => ({
|
|
64
|
+
hostedName,
|
|
65
|
+
handlerName: functionConfig.entry_point || functionConfig.entryPoint || hostedName,
|
|
66
|
+
wrappers: Array.isArray(functionConfig.wrappers) ? functionConfig.wrappers : [],
|
|
67
|
+
}))
|
|
68
|
+
);
|
|
69
|
+
|
|
54
70
|
const functionUsesWrapper = (functionConfig = {}, wrapperName) => (
|
|
55
71
|
Array.isArray(functionConfig.wrappers) && functionConfig.wrappers.includes(wrapperName)
|
|
56
72
|
);
|
|
@@ -76,10 +92,9 @@ const getFuncApiConfig = ({
|
|
|
76
92
|
}
|
|
77
93
|
};
|
|
78
94
|
|
|
79
|
-
const wrapHostedFunction = (loader, exportName,
|
|
95
|
+
const wrapHostedFunction = (loader, exportName, wrappers = []) => {
|
|
80
96
|
let handler = null;
|
|
81
97
|
let usesFuncApi = false;
|
|
82
|
-
const wrappers = resolveWrappers(wrapperNames);
|
|
83
98
|
|
|
84
99
|
const coreHandler = async (req, res) => {
|
|
85
100
|
if (!handler) {
|
|
@@ -168,7 +183,7 @@ const getCredsJsonForDeploy = (workspace) => {
|
|
|
168
183
|
return JSON.stringify(yaml.parse(credsText));
|
|
169
184
|
};
|
|
170
185
|
|
|
171
|
-
const
|
|
186
|
+
const getEnvValueForDeploy = (workspace, envName) => {
|
|
172
187
|
const envPath = `${ workspace }/.env`;
|
|
173
188
|
|
|
174
189
|
if (!fs.existsSync(envPath)) {
|
|
@@ -176,10 +191,27 @@ const getHostedApiKeyForDeploy = (workspace) => {
|
|
|
176
191
|
}
|
|
177
192
|
|
|
178
193
|
const envText = fs.readFileSync(envPath, 'utf8');
|
|
179
|
-
const match = envText.match(
|
|
194
|
+
const match = envText.match(new RegExp(`^${ envName }=(.*)$`, 'm'));
|
|
180
195
|
return match ? match[1].trim() : '';
|
|
181
196
|
};
|
|
182
197
|
|
|
198
|
+
const getEnvVarsForDeploy = (workspace, envNames = []) => (
|
|
199
|
+
Object.fromEntries(
|
|
200
|
+
envNames.map((envName) => [envName, getEnvValueForDeploy(workspace, envName)]),
|
|
201
|
+
)
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
const validateEnvVarsForDeploy = (workspace, envNames = []) => {
|
|
205
|
+
const envVars = getEnvVarsForDeploy(workspace, envNames);
|
|
206
|
+
const missing = envNames.filter((envName) => !envVars[envName]);
|
|
207
|
+
|
|
208
|
+
if (missing.length) {
|
|
209
|
+
throw new Error(`Missing required .env values: ${ missing.join(', ') }`);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return envVars;
|
|
213
|
+
};
|
|
214
|
+
|
|
183
215
|
const resolveHostedHandlersForDeploy = ({
|
|
184
216
|
functions = {},
|
|
185
217
|
workspace,
|
|
@@ -189,31 +221,23 @@ const resolveHostedHandlersForDeploy = ({
|
|
|
189
221
|
const hostedEntries = getHostedEntries(functions);
|
|
190
222
|
|
|
191
223
|
return hostedEntries.map((hostedEntry) => {
|
|
192
|
-
const {
|
|
193
|
-
|
|
194
|
-
if (source) {
|
|
195
|
-
workspaceRequire.resolve(source);
|
|
196
|
-
const moduleExports = workspaceRequire(source);
|
|
197
|
-
if (typeof moduleExports[entryPoint] !== 'function') {
|
|
198
|
-
throw new Error(`Hosted export not found: ${ entryPoint } in ${ source }`);
|
|
199
|
-
}
|
|
224
|
+
const { handlerName, wrappers = [] } = hostedEntry;
|
|
200
225
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
};
|
|
205
|
-
}
|
|
226
|
+
const resolvedWrappers = wrappers.map((wrapperName) => (
|
|
227
|
+
resolveWrapperName(wrapperName, workspaceRequire)
|
|
228
|
+
));
|
|
206
229
|
|
|
207
|
-
const handler = handlersByName.get(
|
|
230
|
+
const handler = handlersByName.get(handlerName);
|
|
208
231
|
if (!handler) {
|
|
209
232
|
throw new Error(
|
|
210
|
-
`
|
|
233
|
+
`Function "${ handlerName }" not found — add a handler in workspace api dirs or mineral api`,
|
|
211
234
|
);
|
|
212
235
|
}
|
|
213
236
|
|
|
214
237
|
return {
|
|
215
238
|
...hostedEntry,
|
|
216
|
-
|
|
239
|
+
resolvedWrappers,
|
|
240
|
+
requirePath: getRequirePathForHandler(handler, workspace),
|
|
217
241
|
};
|
|
218
242
|
});
|
|
219
243
|
};
|
|
@@ -221,12 +245,16 @@ const resolveHostedHandlersForDeploy = ({
|
|
|
221
245
|
// TODO: support credsPayload in google_cloud_info instead of full workspace .creds.yml
|
|
222
246
|
|
|
223
247
|
module.exports = {
|
|
248
|
+
resolveWrapperName,
|
|
224
249
|
getFuncApiConfig,
|
|
225
250
|
wrapHostedFunction,
|
|
226
251
|
readHostingYml,
|
|
227
252
|
getCredsJsonForDeploy,
|
|
228
|
-
|
|
253
|
+
getEnvVarsForDeploy,
|
|
254
|
+
validateEnvVarsForDeploy,
|
|
229
255
|
getHostedEntries,
|
|
230
256
|
resolveHostedHandlersForDeploy,
|
|
231
257
|
functionUsesWrapper,
|
|
258
|
+
MINERAL_WRAPPERS_MODULE,
|
|
259
|
+
WORKSPACE_WRAPPERS_MODULE,
|
|
232
260
|
};
|
package/package.json
CHANGED
package/server.js
CHANGED
|
@@ -4,8 +4,7 @@ const { respondJson, errorToReadable, getRequestBody, argsFromBody, funcApi, sta
|
|
|
4
4
|
const { getWorkspace, setWorkspace, loadWorkspaceEnv, toAbsolutePath } = require('./api/workspace');
|
|
5
5
|
const { getApiDirs, readCliFlag } = require('./cli');
|
|
6
6
|
const { getFuncApiConfig } = require('./hosting.utils');
|
|
7
|
-
|
|
8
|
-
const MINERAL_API_DIR = `${ __dirname }/api`;
|
|
7
|
+
const { MINERAL_API_DIR, getRequirePathForHandler } = require('./handlerPaths');
|
|
9
8
|
|
|
10
9
|
const getConfig = (options = {}) => ({
|
|
11
10
|
port: Number(options.port ?? process.env.PORT ?? 8000),
|
|
@@ -69,7 +68,6 @@ const listJsFiles = (directory) => {
|
|
|
69
68
|
const directoriesToScan = ({
|
|
70
69
|
workspace,
|
|
71
70
|
api_dirs,
|
|
72
|
-
host_mode = false,
|
|
73
71
|
}) => {
|
|
74
72
|
const extraDirs = api_dirs.map((dir) => (
|
|
75
73
|
dir.startsWith('/')
|
|
@@ -77,10 +75,6 @@ const directoriesToScan = ({
|
|
|
77
75
|
: `${ workspace }/${ dir }`
|
|
78
76
|
));
|
|
79
77
|
|
|
80
|
-
if (host_mode && api_dirs.length) {
|
|
81
|
-
return extraDirs;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
78
|
return [MINERAL_API_DIR, ...extraDirs];
|
|
85
79
|
};
|
|
86
80
|
|
|
@@ -225,6 +219,8 @@ const startServer = (options = {}) => {
|
|
|
225
219
|
module.exports = {
|
|
226
220
|
startServer,
|
|
227
221
|
loadHandlers,
|
|
222
|
+
getRequirePathForHandler,
|
|
223
|
+
MINERAL_API_DIR,
|
|
228
224
|
get server() {
|
|
229
225
|
if (!server) {
|
|
230
226
|
startServer();
|
package/server.utils.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
const { logDeep } = require('./api/utils');
|
|
2
2
|
const { StringDecoder } = require('string_decoder');
|
|
3
|
-
const { HOSTED } = require('./api/constants');
|
|
4
3
|
|
|
5
4
|
const respondJson = (res, statusCode, payload) => {
|
|
6
5
|
logDeep(payload);
|
|
@@ -132,23 +131,6 @@ const wrapFunction = (func, wrappers = []) => async (req, res, ...rest) => {
|
|
|
132
131
|
return func(req, res, ...rest);
|
|
133
132
|
};
|
|
134
133
|
|
|
135
|
-
const requireHostedApiKey = async (req) => {
|
|
136
|
-
if (!HOSTED) {
|
|
137
|
-
return;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
if (req.headers['x-api-key'] !== process.env.HOSTED_API_KEY) {
|
|
141
|
-
return {
|
|
142
|
-
ok: false,
|
|
143
|
-
error: {
|
|
144
|
-
code: 'UNAUTHORIZED',
|
|
145
|
-
message: 'Unauthorized',
|
|
146
|
-
statusCode: 401,
|
|
147
|
-
},
|
|
148
|
-
};
|
|
149
|
-
}
|
|
150
|
-
};
|
|
151
|
-
|
|
152
134
|
const statusCodeFromResult = (result) => {
|
|
153
135
|
if (result?.ok === false) {
|
|
154
136
|
return result?.error?.statusCode ?? 400;
|
|
@@ -266,7 +248,6 @@ module.exports = {
|
|
|
266
248
|
getRequestBody,
|
|
267
249
|
argsFromBody,
|
|
268
250
|
wrapFunction,
|
|
269
|
-
requireHostedApiKey,
|
|
270
251
|
statusCodeFromResult,
|
|
271
252
|
funcApi,
|
|
272
253
|
};
|
package/wrappers.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
const { HOSTED } = require('./api/constants');
|
|
2
|
+
|
|
3
|
+
const requireHostedApiKey = async (req) => {
|
|
4
|
+
if (!HOSTED) {
|
|
5
|
+
return;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
if (req.headers['x-api-key'] !== process.env.HOSTED_API_KEY) {
|
|
9
|
+
return {
|
|
10
|
+
ok: false,
|
|
11
|
+
error: {
|
|
12
|
+
code: 'UNAUTHORIZED',
|
|
13
|
+
message: 'Unauthorized',
|
|
14
|
+
statusCode: 401,
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const allowCrossOriginCallsAndHandleOptions = async (req, res) => {
|
|
21
|
+
const { origin } = req.headers;
|
|
22
|
+
|
|
23
|
+
res.setHeader('Access-Control-Allow-Origin', origin || '*');
|
|
24
|
+
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
|
|
25
|
+
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, x-api-key, x-wf-token, x-wf-value');
|
|
26
|
+
|
|
27
|
+
if (req.method === 'OPTIONS') {
|
|
28
|
+
res.writeHead(204);
|
|
29
|
+
res.end();
|
|
30
|
+
return { handled: true };
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
module.exports = {
|
|
35
|
+
requireHostedApiKey,
|
|
36
|
+
allowCrossOriginCallsAndHandleOptions,
|
|
37
|
+
};
|