@certik/skynet 0.22.3 → 0.24.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/.vscode/settings.json +5 -0
- package/CHANGELOG.md +13 -1
- package/dist/api.js +38 -39
- package/dist/app.js +284 -289
- package/dist/deploy.js +19 -21
- package/dist/dynamodb.js +22 -22
- package/dist/goalert.d.ts +19 -0
- package/dist/goalert.js +43 -0
- package/dist/indexer.js +132 -134
- package/dist/slack.d.ts +7 -1
- package/dist/slack.js +2 -5
- package/examples/api.ts +0 -0
- package/examples/indexer.ts +0 -0
- package/examples/mode-indexer.ts +0 -0
- package/package.json +33 -34
- package/patches/@databricks%2Fsql@1.12.0.patch +31 -0
- package/src/api.ts +0 -1
- package/src/deploy.ts +0 -2
- package/src/goalert.ts +70 -0
- package/src/indexer.ts +0 -2
- package/src/slack.ts +18 -12
- package/dist/opsgenie.d.ts +0 -99
- package/dist/opsgenie.js +0 -43
- package/src/opsgenie.ts +0 -176
package/dist/app.js
CHANGED
|
@@ -1,3 +1,41 @@
|
|
|
1
|
+
// src/selector.ts
|
|
2
|
+
function getSelectorDesc(selector) {
|
|
3
|
+
return Object.keys(selector).map((name) => {
|
|
4
|
+
return ` --${name.padEnd(14)}${selector[name].desc || selector[name].description || ""}`;
|
|
5
|
+
}).join(`
|
|
6
|
+
`);
|
|
7
|
+
}
|
|
8
|
+
function getSelectorFlags(selector) {
|
|
9
|
+
return Object.keys(selector).reduce((acc, name) => {
|
|
10
|
+
const flag = {
|
|
11
|
+
type: selector[name].type || "string",
|
|
12
|
+
...selector[name]
|
|
13
|
+
};
|
|
14
|
+
if (!selector[name].optional && selector[name].isRequired !== false) {
|
|
15
|
+
flag.isRequired = true;
|
|
16
|
+
}
|
|
17
|
+
return { ...acc, [name]: flag };
|
|
18
|
+
}, {});
|
|
19
|
+
}
|
|
20
|
+
function toSelectorString(selectorFlags, delim = ",") {
|
|
21
|
+
return Object.keys(selectorFlags).sort().map((flag) => {
|
|
22
|
+
return `${flag}=${selectorFlags[flag]}`;
|
|
23
|
+
}).join(delim);
|
|
24
|
+
}
|
|
25
|
+
function normalizeSelectorValue(v) {
|
|
26
|
+
return v.replace(/[^A-Za-z0-9]+/g, "-");
|
|
27
|
+
}
|
|
28
|
+
function getJobName(name, selectorFlags, mode) {
|
|
29
|
+
const selectorNamePart = Object.keys(selectorFlags).sort().map((name2) => selectorFlags[name2]).join("-");
|
|
30
|
+
let jobName = name;
|
|
31
|
+
if (mode) {
|
|
32
|
+
jobName += `-${mode}`;
|
|
33
|
+
}
|
|
34
|
+
if (selectorNamePart.length > 0) {
|
|
35
|
+
jobName += `-${normalizeSelectorValue(selectorNamePart)}`;
|
|
36
|
+
}
|
|
37
|
+
return jobName;
|
|
38
|
+
}
|
|
1
39
|
// src/env.ts
|
|
2
40
|
function ensureAndGet(envName, defaultValue) {
|
|
3
41
|
return process.env[envName] || defaultValue;
|
|
@@ -17,56 +55,207 @@ function isProduction() {
|
|
|
17
55
|
function isDev() {
|
|
18
56
|
return getEnvironment() === "dev";
|
|
19
57
|
}
|
|
20
|
-
// src/
|
|
21
|
-
function
|
|
22
|
-
|
|
23
|
-
for (let i = startAt;i <= endAt; i += step) {
|
|
24
|
-
arr.push([i, Math.min(endAt, i + step - 1)]);
|
|
25
|
-
}
|
|
26
|
-
return arr;
|
|
58
|
+
// src/log.ts
|
|
59
|
+
function isObject(a) {
|
|
60
|
+
return !!a && a.constructor === Object;
|
|
27
61
|
}
|
|
28
|
-
function
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
groups.push(array.slice(i, i + groupSize));
|
|
62
|
+
function print(o) {
|
|
63
|
+
if (Array.isArray(o)) {
|
|
64
|
+
return `[${o.map(print).join(", ")}]`;
|
|
32
65
|
}
|
|
33
|
-
|
|
66
|
+
if (isObject(o)) {
|
|
67
|
+
return `{${Object.keys(o).map((k) => `${k}: ${o[k]}`).join(", ")}}`;
|
|
68
|
+
}
|
|
69
|
+
return `${o}`;
|
|
34
70
|
}
|
|
35
|
-
function
|
|
36
|
-
|
|
37
|
-
for (let i =
|
|
38
|
-
|
|
71
|
+
function getLine(params) {
|
|
72
|
+
let line = "";
|
|
73
|
+
for (let i = 0, l = params.length;i < l; i++) {
|
|
74
|
+
line += `${print(params[i])} `.replace(/\n/gm, "\t");
|
|
39
75
|
}
|
|
40
|
-
return
|
|
76
|
+
return line.trim();
|
|
41
77
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
function getDateOnly(date) {
|
|
45
|
-
return new Date(date).toISOString().split("T")[0];
|
|
78
|
+
function timestamp() {
|
|
79
|
+
return new Date().toISOString();
|
|
46
80
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
81
|
+
var inline = {
|
|
82
|
+
debug: function(...args) {
|
|
83
|
+
if (true) {
|
|
84
|
+
console.log(`${timestamp()} ${getLine(args)}`);
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
log: function(...args) {
|
|
88
|
+
if (true) {
|
|
89
|
+
console.log(`${timestamp()} ${getLine(args)}`);
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
error: function(...args) {
|
|
93
|
+
if (true) {
|
|
94
|
+
console.error(`${timestamp()} ${getLine(args)}`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
var logger = {
|
|
99
|
+
debug: function(...args) {
|
|
100
|
+
if (true) {
|
|
101
|
+
console.log(`[${timestamp()}]`, ...args);
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
log: function(...args) {
|
|
105
|
+
if (true) {
|
|
106
|
+
console.log(`[${timestamp()}]`, ...args);
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
error: function(...args) {
|
|
110
|
+
if (true) {
|
|
111
|
+
console.error(`[${timestamp()}]`, ...args);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
// src/api.ts
|
|
116
|
+
import osModule from "os";
|
|
117
|
+
import express from "express";
|
|
118
|
+
import meow from "meow";
|
|
119
|
+
async function logStartMiddleware(_, res, next) {
|
|
120
|
+
const start = new Date;
|
|
121
|
+
res.set("x-requested-at", start.toISOString());
|
|
122
|
+
next();
|
|
51
123
|
}
|
|
52
|
-
function
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
124
|
+
async function contextMiddleware(_, res, next) {
|
|
125
|
+
res.set("x-instance-id", osModule.hostname());
|
|
126
|
+
next();
|
|
127
|
+
}
|
|
128
|
+
async function logEndMiddleware(req, res, next) {
|
|
129
|
+
const requestedAt = res.get("x-requested-at");
|
|
130
|
+
if (!requestedAt) {
|
|
131
|
+
inline.log("missing x-requested-at header");
|
|
132
|
+
next();
|
|
133
|
+
return;
|
|
57
134
|
}
|
|
58
|
-
const
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
135
|
+
const start = new Date(requestedAt).getTime();
|
|
136
|
+
const end = new Date().getTime();
|
|
137
|
+
const logInfo = {
|
|
138
|
+
start,
|
|
139
|
+
end,
|
|
140
|
+
elapsed: `${end - start}ms`,
|
|
141
|
+
endpoint: req.path,
|
|
142
|
+
host: req.hostname,
|
|
143
|
+
status: res.statusCode
|
|
144
|
+
};
|
|
145
|
+
if (res.statusMessage) {
|
|
146
|
+
logInfo.errorMessage = res.statusMessage;
|
|
62
147
|
}
|
|
63
|
-
|
|
148
|
+
inline.log(JSON.stringify(logInfo));
|
|
149
|
+
next();
|
|
64
150
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
151
|
+
var apiKeyMiddleware = (key) => {
|
|
152
|
+
async function requireAPIKey(req, res, next) {
|
|
153
|
+
try {
|
|
154
|
+
const apiKey = req.get("x-api-key") || req.query["api-key"];
|
|
155
|
+
if (!apiKey) {
|
|
156
|
+
inline.log("request without api key");
|
|
157
|
+
res.status(400).send("require x-api-key header");
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
if (typeof key === "string") {
|
|
161
|
+
if (apiKey !== key) {
|
|
162
|
+
inline.log("request has an invalid api key");
|
|
163
|
+
res.status(400).send("invalid api key");
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
inline.log(`requested by valid key ${key.slice(0, 6)}`);
|
|
167
|
+
} else {
|
|
168
|
+
const name = key[apiKey];
|
|
169
|
+
if (!name) {
|
|
170
|
+
inline.log("request has an invalid api key");
|
|
171
|
+
res.status(400).send("invalid api key");
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
inline.log(`requested by authorized user ${name}`);
|
|
175
|
+
}
|
|
176
|
+
next();
|
|
177
|
+
} catch (err) {
|
|
178
|
+
inline.log("check api key error", err);
|
|
179
|
+
res.status(500).send("internal error");
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return requireAPIKey;
|
|
183
|
+
};
|
|
184
|
+
async function startApiApp({
|
|
185
|
+
binaryName,
|
|
186
|
+
name,
|
|
187
|
+
selector = {},
|
|
188
|
+
routes,
|
|
189
|
+
serve,
|
|
190
|
+
beforeListen
|
|
191
|
+
}) {
|
|
192
|
+
const app = express();
|
|
193
|
+
app.use(express.json({ limit: "20mb" }));
|
|
194
|
+
const cli = meow(`
|
|
195
|
+
Usage
|
|
196
|
+
$ ${binaryName} <options>
|
|
197
|
+
|
|
198
|
+
Options
|
|
199
|
+
${getSelectorDesc(selector)}
|
|
200
|
+
--verbose Output debug messages
|
|
201
|
+
`, {
|
|
202
|
+
importMeta: import.meta,
|
|
203
|
+
description: false,
|
|
204
|
+
flags: {
|
|
205
|
+
...getSelectorFlags(selector),
|
|
206
|
+
verbose: {
|
|
207
|
+
type: "boolean",
|
|
208
|
+
default: false
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
const { verbose, ...selectorFlags } = cli.flags;
|
|
213
|
+
for (const route of routes) {
|
|
214
|
+
const method = route.method ? route.method.toLowerCase() : "get";
|
|
215
|
+
const middlewares = route.middlewares || [];
|
|
216
|
+
if (route.protected) {
|
|
217
|
+
if (!serve.apiKey) {
|
|
218
|
+
throw new Error("serve.apiKey is required for protected route");
|
|
219
|
+
}
|
|
220
|
+
middlewares.unshift(apiKeyMiddleware(serve.apiKey));
|
|
221
|
+
}
|
|
222
|
+
if (app[method]) {
|
|
223
|
+
if (verbose) {
|
|
224
|
+
inline.log(`registering ${method} ${route.path}`);
|
|
225
|
+
}
|
|
226
|
+
app[method](route.path, contextMiddleware, logStartMiddleware, ...middlewares, async (req, res, next) => {
|
|
227
|
+
try {
|
|
228
|
+
await route.handler({ req, res, ...selectorFlags });
|
|
229
|
+
} catch (routeErr) {
|
|
230
|
+
if (routeErr instanceof Error) {
|
|
231
|
+
inline.log("caught route err", routeErr, routeErr.stack);
|
|
232
|
+
res.status(500).send(`internal server error: ${routeErr.message}`);
|
|
233
|
+
} else {
|
|
234
|
+
inline.log("caught route err", routeErr);
|
|
235
|
+
res.status(500).send("internal server error");
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
next();
|
|
239
|
+
}, logEndMiddleware);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
if (!routes.some((r) => r.path === "/" && r.method?.toUpperCase() === "GET")) {
|
|
243
|
+
app.get("/", (_, res) => {
|
|
244
|
+
res.send("ok");
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
if (beforeListen) {
|
|
248
|
+
await beforeListen({ app });
|
|
249
|
+
}
|
|
250
|
+
app.listen(serve.port, () => {
|
|
251
|
+
if (isProduction()) {
|
|
252
|
+
inline.log(`${name} listening at https://api.wf.corp.certik.com${serve.prefix}`);
|
|
253
|
+
} else {
|
|
254
|
+
inline.log(`${name} listening at http://localhost:${serve.port}`);
|
|
255
|
+
}
|
|
256
|
+
});
|
|
69
257
|
}
|
|
258
|
+
|
|
70
259
|
// src/object-hash.ts
|
|
71
260
|
import xh from "@node-rs/xxhash";
|
|
72
261
|
function getHash(obj) {
|
|
@@ -191,65 +380,30 @@ function memoize(func, options) {
|
|
|
191
380
|
if (!options.cacheKey) {
|
|
192
381
|
options.cacheKey = (args) => getHash(args);
|
|
193
382
|
}
|
|
194
|
-
return pMemoize(func, options);
|
|
195
|
-
}
|
|
196
|
-
// src/
|
|
197
|
-
function
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
if (Array.isArray(o)) {
|
|
202
|
-
return `[${o.map(print).join(", ")}]`;
|
|
203
|
-
}
|
|
204
|
-
if (isObject(o)) {
|
|
205
|
-
return `{${Object.keys(o).map((k) => `${k}: ${o[k]}`).join(", ")}}`;
|
|
206
|
-
}
|
|
207
|
-
return `${o}`;
|
|
208
|
-
}
|
|
209
|
-
function getLine(params) {
|
|
210
|
-
let line = "";
|
|
211
|
-
for (let i = 0, l = params.length;i < l; i++) {
|
|
212
|
-
line += `${print(params[i])} `.replace(/\n/gm, "\t");
|
|
213
|
-
}
|
|
214
|
-
return line.trim();
|
|
215
|
-
}
|
|
216
|
-
function timestamp() {
|
|
217
|
-
return new Date().toISOString();
|
|
218
|
-
}
|
|
219
|
-
var inline = {
|
|
220
|
-
debug: function(...args) {
|
|
221
|
-
if (true) {
|
|
222
|
-
console.log(`${timestamp()} ${getLine(args)}`);
|
|
223
|
-
}
|
|
224
|
-
},
|
|
225
|
-
log: function(...args) {
|
|
226
|
-
if (true) {
|
|
227
|
-
console.log(`${timestamp()} ${getLine(args)}`);
|
|
228
|
-
}
|
|
229
|
-
},
|
|
230
|
-
error: function(...args) {
|
|
231
|
-
if (true) {
|
|
232
|
-
console.error(`${timestamp()} ${getLine(args)}`);
|
|
233
|
-
}
|
|
383
|
+
return pMemoize(func, options);
|
|
384
|
+
}
|
|
385
|
+
// src/util.ts
|
|
386
|
+
function range(startAt, endAt, step) {
|
|
387
|
+
const arr = [];
|
|
388
|
+
for (let i = startAt;i <= endAt; i += step) {
|
|
389
|
+
arr.push([i, Math.min(endAt, i + step - 1)]);
|
|
234
390
|
}
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
},
|
|
242
|
-
log: function(...args) {
|
|
243
|
-
if (true) {
|
|
244
|
-
console.log(`[${timestamp()}]`, ...args);
|
|
245
|
-
}
|
|
246
|
-
},
|
|
247
|
-
error: function(...args) {
|
|
248
|
-
if (true) {
|
|
249
|
-
console.error(`[${timestamp()}]`, ...args);
|
|
250
|
-
}
|
|
391
|
+
return arr;
|
|
392
|
+
}
|
|
393
|
+
function arrayGroup(array, groupSize) {
|
|
394
|
+
const groups = [];
|
|
395
|
+
for (let i = 0;i < array.length; i += groupSize) {
|
|
396
|
+
groups.push(array.slice(i, i + groupSize));
|
|
251
397
|
}
|
|
252
|
-
|
|
398
|
+
return groups;
|
|
399
|
+
}
|
|
400
|
+
function fillRange(start, end) {
|
|
401
|
+
const result = [];
|
|
402
|
+
for (let i = start;i <= end; i++) {
|
|
403
|
+
result.push(i);
|
|
404
|
+
}
|
|
405
|
+
return result;
|
|
406
|
+
}
|
|
253
407
|
// src/dynamodb.ts
|
|
254
408
|
import {
|
|
255
409
|
DynamoDBDocumentClient,
|
|
@@ -564,44 +718,6 @@ async function deleteRecordsByHashKey(tableName, indexName, hashKeyValue, verbos
|
|
|
564
718
|
}
|
|
565
719
|
return totalDeleted;
|
|
566
720
|
}
|
|
567
|
-
// src/selector.ts
|
|
568
|
-
function getSelectorDesc(selector) {
|
|
569
|
-
return Object.keys(selector).map((name) => {
|
|
570
|
-
return ` --${name.padEnd(14)}${selector[name].desc || selector[name].description || ""}`;
|
|
571
|
-
}).join(`
|
|
572
|
-
`);
|
|
573
|
-
}
|
|
574
|
-
function getSelectorFlags(selector) {
|
|
575
|
-
return Object.keys(selector).reduce((acc, name) => {
|
|
576
|
-
const flag = {
|
|
577
|
-
type: selector[name].type || "string",
|
|
578
|
-
...selector[name]
|
|
579
|
-
};
|
|
580
|
-
if (!selector[name].optional && selector[name].isRequired !== false) {
|
|
581
|
-
flag.isRequired = true;
|
|
582
|
-
}
|
|
583
|
-
return { ...acc, [name]: flag };
|
|
584
|
-
}, {});
|
|
585
|
-
}
|
|
586
|
-
function toSelectorString(selectorFlags, delim = ",") {
|
|
587
|
-
return Object.keys(selectorFlags).sort().map((flag) => {
|
|
588
|
-
return `${flag}=${selectorFlags[flag]}`;
|
|
589
|
-
}).join(delim);
|
|
590
|
-
}
|
|
591
|
-
function normalizeSelectorValue(v) {
|
|
592
|
-
return v.replace(/[^A-Za-z0-9]+/g, "-");
|
|
593
|
-
}
|
|
594
|
-
function getJobName(name, selectorFlags, mode) {
|
|
595
|
-
const selectorNamePart = Object.keys(selectorFlags).sort().map((name2) => selectorFlags[name2]).join("-");
|
|
596
|
-
let jobName = name;
|
|
597
|
-
if (mode) {
|
|
598
|
-
jobName += `-${mode}`;
|
|
599
|
-
}
|
|
600
|
-
if (selectorNamePart.length > 0) {
|
|
601
|
-
jobName += `-${normalizeSelectorValue(selectorNamePart)}`;
|
|
602
|
-
}
|
|
603
|
-
return jobName;
|
|
604
|
-
}
|
|
605
721
|
// src/cli.ts
|
|
606
722
|
import path from "path";
|
|
607
723
|
import fs from "fs";
|
|
@@ -637,8 +753,36 @@ function detectBin() {
|
|
|
637
753
|
const wd = detectDirectory(process.argv[1], "package.json");
|
|
638
754
|
return process.argv[1].slice(wd.length + path.sep.length).replace(path.sep, "/");
|
|
639
755
|
}
|
|
756
|
+
// src/date.ts
|
|
757
|
+
var MS_IN_A_DAY = 3600 * 24 * 1000;
|
|
758
|
+
function getDateOnly(date) {
|
|
759
|
+
return new Date(date).toISOString().split("T")[0];
|
|
760
|
+
}
|
|
761
|
+
function findDateAfter(date, n) {
|
|
762
|
+
const d = new Date(date);
|
|
763
|
+
const after = new Date(d.getTime() + MS_IN_A_DAY * n);
|
|
764
|
+
return getDateOnly(after);
|
|
765
|
+
}
|
|
766
|
+
function daysInRange(from, to) {
|
|
767
|
+
const fromTime = new Date(from).getTime();
|
|
768
|
+
const toTime = new Date(to).getTime();
|
|
769
|
+
if (fromTime > toTime) {
|
|
770
|
+
throw new Error(`range to date couldn't be earlier than range from date`);
|
|
771
|
+
}
|
|
772
|
+
const daysBetween = Math.floor((toTime - fromTime) / MS_IN_A_DAY);
|
|
773
|
+
const dates = [getDateOnly(new Date(fromTime))];
|
|
774
|
+
for (let i = 1;i <= daysBetween; i += 1) {
|
|
775
|
+
dates.push(getDateOnly(new Date(fromTime + i * MS_IN_A_DAY)));
|
|
776
|
+
}
|
|
777
|
+
return dates;
|
|
778
|
+
}
|
|
779
|
+
function dateRange(from, to, step) {
|
|
780
|
+
const days = daysInRange(from, to);
|
|
781
|
+
const windows = arrayGroup(days, step);
|
|
782
|
+
return windows.map((w) => [w[0], w[w.length - 1]]);
|
|
783
|
+
}
|
|
640
784
|
// src/indexer.ts
|
|
641
|
-
import
|
|
785
|
+
import meow2 from "meow";
|
|
642
786
|
var STATE_TABLE_NAME = "skynet-" + getEnvironment() + "-indexer-state";
|
|
643
787
|
async function getIndexerLatestId(name, selectorFlags) {
|
|
644
788
|
const record = await getRecordByKey(STATE_TABLE_NAME, {
|
|
@@ -981,7 +1125,7 @@ function createModeIndexerApp({
|
|
|
981
1125
|
if (!binaryName) {
|
|
982
1126
|
binaryName = getBinaryName();
|
|
983
1127
|
}
|
|
984
|
-
const cli =
|
|
1128
|
+
const cli = meow2(`
|
|
985
1129
|
Usage
|
|
986
1130
|
|
|
987
1131
|
$ ${binaryName} <options>
|
|
@@ -996,7 +1140,6 @@ ${selector ? getSelectorDesc(selector) : ""}
|
|
|
996
1140
|
`, {
|
|
997
1141
|
importMeta: import.meta,
|
|
998
1142
|
description: false,
|
|
999
|
-
version: false,
|
|
1000
1143
|
flags: {
|
|
1001
1144
|
...getSelectorFlags(selector),
|
|
1002
1145
|
mode: {
|
|
@@ -1040,7 +1183,7 @@ function createIndexerApp({
|
|
|
1040
1183
|
if (!binaryName) {
|
|
1041
1184
|
binaryName = getBinaryName();
|
|
1042
1185
|
}
|
|
1043
|
-
const cli =
|
|
1186
|
+
const cli = meow2(`
|
|
1044
1187
|
Usage
|
|
1045
1188
|
$ ${binaryName} <options>
|
|
1046
1189
|
|
|
@@ -1050,7 +1193,6 @@ ${selector ? getSelectorDesc(selector) : ""}
|
|
|
1050
1193
|
`, {
|
|
1051
1194
|
importMeta: import.meta,
|
|
1052
1195
|
description: false,
|
|
1053
|
-
version: false,
|
|
1054
1196
|
flags: {
|
|
1055
1197
|
...getSelectorFlags(selector),
|
|
1056
1198
|
verbose: {
|
|
@@ -1098,7 +1240,7 @@ ${selector ? getSelectorDesc(selector) : ""}
|
|
|
1098
1240
|
import fs2 from "fs/promises";
|
|
1099
1241
|
import fso from "fs";
|
|
1100
1242
|
import { execa } from "execa";
|
|
1101
|
-
import
|
|
1243
|
+
import meow3 from "meow";
|
|
1102
1244
|
import chalk from "chalk";
|
|
1103
1245
|
import which from "which";
|
|
1104
1246
|
var INTERVAL_ALIASES = {
|
|
@@ -1405,7 +1547,7 @@ function createModeDeploy({
|
|
|
1405
1547
|
if (!binaryName) {
|
|
1406
1548
|
binaryName = getBinaryName();
|
|
1407
1549
|
}
|
|
1408
|
-
const cli =
|
|
1550
|
+
const cli = meow3(`
|
|
1409
1551
|
Usage
|
|
1410
1552
|
|
|
1411
1553
|
$ ${binaryName} <options>
|
|
@@ -1428,7 +1570,6 @@ ${getSelectorDesc(selector)}
|
|
|
1428
1570
|
`, {
|
|
1429
1571
|
importMeta: import.meta,
|
|
1430
1572
|
description: false,
|
|
1431
|
-
version: false,
|
|
1432
1573
|
flags: {
|
|
1433
1574
|
...getSelectorFlags(selector),
|
|
1434
1575
|
mode: {
|
|
@@ -1536,7 +1677,7 @@ function createDeploy({
|
|
|
1536
1677
|
if (!binaryName) {
|
|
1537
1678
|
binaryName = getBinaryName();
|
|
1538
1679
|
}
|
|
1539
|
-
const cli =
|
|
1680
|
+
const cli = meow3(`
|
|
1540
1681
|
Usage
|
|
1541
1682
|
|
|
1542
1683
|
$ ${binaryName} <options>
|
|
@@ -1551,7 +1692,6 @@ ${getSelectorDesc(selector)}
|
|
|
1551
1692
|
`, {
|
|
1552
1693
|
importMeta: import.meta,
|
|
1553
1694
|
description: false,
|
|
1554
|
-
version: false,
|
|
1555
1695
|
flags: {
|
|
1556
1696
|
...getSelectorFlags(selector),
|
|
1557
1697
|
schedule: {
|
|
@@ -1585,151 +1725,6 @@ ${getSelectorDesc(selector)}
|
|
|
1585
1725
|
}
|
|
1586
1726
|
return { deploy };
|
|
1587
1727
|
}
|
|
1588
|
-
// src/api.ts
|
|
1589
|
-
import osModule from "os";
|
|
1590
|
-
import express from "express";
|
|
1591
|
-
import meow3 from "meow";
|
|
1592
|
-
async function logStartMiddleware(_, res, next) {
|
|
1593
|
-
const start = new Date;
|
|
1594
|
-
res.set("x-requested-at", start.toISOString());
|
|
1595
|
-
next();
|
|
1596
|
-
}
|
|
1597
|
-
async function contextMiddleware(_, res, next) {
|
|
1598
|
-
res.set("x-instance-id", osModule.hostname());
|
|
1599
|
-
next();
|
|
1600
|
-
}
|
|
1601
|
-
async function logEndMiddleware(req, res, next) {
|
|
1602
|
-
const requestedAt = res.get("x-requested-at");
|
|
1603
|
-
if (!requestedAt) {
|
|
1604
|
-
inline.log("missing x-requested-at header");
|
|
1605
|
-
next();
|
|
1606
|
-
return;
|
|
1607
|
-
}
|
|
1608
|
-
const start = new Date(requestedAt).getTime();
|
|
1609
|
-
const end = new Date().getTime();
|
|
1610
|
-
const logInfo = {
|
|
1611
|
-
start,
|
|
1612
|
-
end,
|
|
1613
|
-
elapsed: `${end - start}ms`,
|
|
1614
|
-
endpoint: req.path,
|
|
1615
|
-
host: req.hostname,
|
|
1616
|
-
status: res.statusCode
|
|
1617
|
-
};
|
|
1618
|
-
if (res.statusMessage) {
|
|
1619
|
-
logInfo.errorMessage = res.statusMessage;
|
|
1620
|
-
}
|
|
1621
|
-
inline.log(JSON.stringify(logInfo));
|
|
1622
|
-
next();
|
|
1623
|
-
}
|
|
1624
|
-
var apiKeyMiddleware = (key) => {
|
|
1625
|
-
async function requireAPIKey(req, res, next) {
|
|
1626
|
-
try {
|
|
1627
|
-
const apiKey = req.get("x-api-key") || req.query["api-key"];
|
|
1628
|
-
if (!apiKey) {
|
|
1629
|
-
inline.log("request without api key");
|
|
1630
|
-
res.status(400).send("require x-api-key header");
|
|
1631
|
-
return;
|
|
1632
|
-
}
|
|
1633
|
-
if (typeof key === "string") {
|
|
1634
|
-
if (apiKey !== key) {
|
|
1635
|
-
inline.log("request has an invalid api key");
|
|
1636
|
-
res.status(400).send("invalid api key");
|
|
1637
|
-
return;
|
|
1638
|
-
}
|
|
1639
|
-
inline.log(`requested by valid key ${key.slice(0, 6)}`);
|
|
1640
|
-
} else {
|
|
1641
|
-
const name = key[apiKey];
|
|
1642
|
-
if (!name) {
|
|
1643
|
-
inline.log("request has an invalid api key");
|
|
1644
|
-
res.status(400).send("invalid api key");
|
|
1645
|
-
return;
|
|
1646
|
-
}
|
|
1647
|
-
inline.log(`requested by authorized user ${name}`);
|
|
1648
|
-
}
|
|
1649
|
-
next();
|
|
1650
|
-
} catch (err) {
|
|
1651
|
-
inline.log("check api key error", err);
|
|
1652
|
-
res.status(500).send("internal error");
|
|
1653
|
-
}
|
|
1654
|
-
}
|
|
1655
|
-
return requireAPIKey;
|
|
1656
|
-
};
|
|
1657
|
-
async function startApiApp({
|
|
1658
|
-
binaryName,
|
|
1659
|
-
name,
|
|
1660
|
-
selector = {},
|
|
1661
|
-
routes,
|
|
1662
|
-
serve,
|
|
1663
|
-
beforeListen
|
|
1664
|
-
}) {
|
|
1665
|
-
const app = express();
|
|
1666
|
-
app.use(express.json({ limit: "20mb" }));
|
|
1667
|
-
const cli = meow3(`
|
|
1668
|
-
Usage
|
|
1669
|
-
$ ${binaryName} <options>
|
|
1670
|
-
|
|
1671
|
-
Options
|
|
1672
|
-
${getSelectorDesc(selector)}
|
|
1673
|
-
--verbose Output debug messages
|
|
1674
|
-
`, {
|
|
1675
|
-
importMeta: import.meta,
|
|
1676
|
-
description: false,
|
|
1677
|
-
version: false,
|
|
1678
|
-
flags: {
|
|
1679
|
-
...getSelectorFlags(selector),
|
|
1680
|
-
verbose: {
|
|
1681
|
-
type: "boolean",
|
|
1682
|
-
default: false
|
|
1683
|
-
}
|
|
1684
|
-
}
|
|
1685
|
-
});
|
|
1686
|
-
const { verbose, ...selectorFlags } = cli.flags;
|
|
1687
|
-
for (const route of routes) {
|
|
1688
|
-
const method = route.method ? route.method.toLowerCase() : "get";
|
|
1689
|
-
const middlewares = route.middlewares || [];
|
|
1690
|
-
if (route.protected) {
|
|
1691
|
-
if (!serve.apiKey) {
|
|
1692
|
-
throw new Error("serve.apiKey is required for protected route");
|
|
1693
|
-
}
|
|
1694
|
-
middlewares.unshift(apiKeyMiddleware(serve.apiKey));
|
|
1695
|
-
}
|
|
1696
|
-
if (app[method]) {
|
|
1697
|
-
if (verbose) {
|
|
1698
|
-
inline.log(`registering ${method} ${route.path}`);
|
|
1699
|
-
}
|
|
1700
|
-
app[method](route.path, contextMiddleware, logStartMiddleware, ...middlewares, async (req, res, next) => {
|
|
1701
|
-
try {
|
|
1702
|
-
await route.handler({ req, res, ...selectorFlags });
|
|
1703
|
-
} catch (routeErr) {
|
|
1704
|
-
if (routeErr instanceof Error) {
|
|
1705
|
-
inline.log("caught route err", routeErr, routeErr.stack);
|
|
1706
|
-
res.status(500).send(`internal server error: ${routeErr.message}`);
|
|
1707
|
-
} else {
|
|
1708
|
-
inline.log("caught route err", routeErr);
|
|
1709
|
-
res.status(500).send("internal server error");
|
|
1710
|
-
}
|
|
1711
|
-
}
|
|
1712
|
-
next();
|
|
1713
|
-
}, logEndMiddleware);
|
|
1714
|
-
}
|
|
1715
|
-
}
|
|
1716
|
-
if (!routes.some((r) => r.path === "/" && r.method?.toUpperCase() === "GET")) {
|
|
1717
|
-
app.get("/", (_, res) => {
|
|
1718
|
-
res.send("ok");
|
|
1719
|
-
});
|
|
1720
|
-
}
|
|
1721
|
-
if (beforeListen) {
|
|
1722
|
-
await beforeListen({ app });
|
|
1723
|
-
}
|
|
1724
|
-
app.listen(serve.port, () => {
|
|
1725
|
-
if (isProduction()) {
|
|
1726
|
-
inline.log(`${name} listening at https://api.wf.corp.certik.com${serve.prefix}`);
|
|
1727
|
-
} else {
|
|
1728
|
-
inline.log(`${name} listening at http://localhost:${serve.port}`);
|
|
1729
|
-
}
|
|
1730
|
-
});
|
|
1731
|
-
}
|
|
1732
|
-
|
|
1733
1728
|
// src/app.ts
|
|
1734
1729
|
import { EOL } from "os";
|
|
1735
1730
|
function printAppHelp() {
|