@certik/skynet 0.24.0 → 0.25.1
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/CHANGELOG.md +9 -0
- package/README.md +8 -2
- package/dist/api.js +38 -38
- package/dist/app.js +279 -279
- package/dist/deploy.js +19 -19
- package/dist/dynamodb.js +22 -22
- package/dist/graphql.d.ts +2 -1
- package/dist/graphql.js +11 -4
- package/dist/indexer.js +132 -132
- package/dist/por.d.ts +1 -1
- package/dist/slack.d.ts +1 -1
- package/dist/slack.js +1 -1
- package/examples/api.ts +0 -0
- package/examples/indexer.ts +0 -0
- package/examples/mode-indexer.ts +0 -0
- package/package.json +1 -10
- package/src/graphql.ts +14 -4
- package/src/por.ts +18 -23
- package/.vscode/settings.json +0 -5
- package/dist/databricks.d.ts +0 -3
- package/dist/databricks.js +0 -208
- package/src/databricks.ts +0 -82
package/dist/app.js
CHANGED
|
@@ -1,41 +1,3 @@
|
|
|
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
|
-
}
|
|
39
1
|
// src/env.ts
|
|
40
2
|
function ensureAndGet(envName, defaultValue) {
|
|
41
3
|
return process.env[envName] || defaultValue;
|
|
@@ -55,207 +17,56 @@ function isProduction() {
|
|
|
55
17
|
function isDev() {
|
|
56
18
|
return getEnvironment() === "dev";
|
|
57
19
|
}
|
|
58
|
-
// src/
|
|
59
|
-
function
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
if (Array.isArray(o)) {
|
|
64
|
-
return `[${o.map(print).join(", ")}]`;
|
|
65
|
-
}
|
|
66
|
-
if (isObject(o)) {
|
|
67
|
-
return `{${Object.keys(o).map((k) => `${k}: ${o[k]}`).join(", ")}}`;
|
|
20
|
+
// src/util.ts
|
|
21
|
+
function range(startAt, endAt, step) {
|
|
22
|
+
const arr = [];
|
|
23
|
+
for (let i = startAt;i <= endAt; i += step) {
|
|
24
|
+
arr.push([i, Math.min(endAt, i + step - 1)]);
|
|
68
25
|
}
|
|
69
|
-
return
|
|
26
|
+
return arr;
|
|
70
27
|
}
|
|
71
|
-
function
|
|
72
|
-
|
|
73
|
-
for (let i = 0
|
|
74
|
-
|
|
28
|
+
function arrayGroup(array, groupSize) {
|
|
29
|
+
const groups = [];
|
|
30
|
+
for (let i = 0;i < array.length; i += groupSize) {
|
|
31
|
+
groups.push(array.slice(i, i + groupSize));
|
|
75
32
|
}
|
|
76
|
-
return
|
|
77
|
-
}
|
|
78
|
-
function timestamp() {
|
|
79
|
-
return new Date().toISOString();
|
|
33
|
+
return groups;
|
|
80
34
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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
|
-
}
|
|
35
|
+
function fillRange(start, end) {
|
|
36
|
+
const result = [];
|
|
37
|
+
for (let i = start;i <= end; i++) {
|
|
38
|
+
result.push(i);
|
|
113
39
|
}
|
|
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();
|
|
40
|
+
return result;
|
|
123
41
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
42
|
+
// src/date.ts
|
|
43
|
+
var MS_IN_A_DAY = 3600 * 24 * 1000;
|
|
44
|
+
function getDateOnly(date) {
|
|
45
|
+
return new Date(date).toISOString().split("T")[0];
|
|
127
46
|
}
|
|
128
|
-
|
|
129
|
-
const
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
next();
|
|
133
|
-
return;
|
|
134
|
-
}
|
|
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;
|
|
147
|
-
}
|
|
148
|
-
inline.log(JSON.stringify(logInfo));
|
|
149
|
-
next();
|
|
47
|
+
function findDateAfter(date, n) {
|
|
48
|
+
const d = new Date(date);
|
|
49
|
+
const after = new Date(d.getTime() + MS_IN_A_DAY * n);
|
|
50
|
+
return getDateOnly(after);
|
|
150
51
|
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
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
|
-
});
|
|
52
|
+
function daysInRange(from, to) {
|
|
53
|
+
const fromTime = new Date(from).getTime();
|
|
54
|
+
const toTime = new Date(to).getTime();
|
|
55
|
+
if (fromTime > toTime) {
|
|
56
|
+
throw new Error(`range to date couldn't be earlier than range from date`);
|
|
246
57
|
}
|
|
247
|
-
|
|
248
|
-
|
|
58
|
+
const daysBetween = Math.floor((toTime - fromTime) / MS_IN_A_DAY);
|
|
59
|
+
const dates = [getDateOnly(new Date(fromTime))];
|
|
60
|
+
for (let i = 1;i <= daysBetween; i += 1) {
|
|
61
|
+
dates.push(getDateOnly(new Date(fromTime + i * MS_IN_A_DAY)));
|
|
249
62
|
}
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
});
|
|
63
|
+
return dates;
|
|
64
|
+
}
|
|
65
|
+
function dateRange(from, to, step) {
|
|
66
|
+
const days = daysInRange(from, to);
|
|
67
|
+
const windows = arrayGroup(days, step);
|
|
68
|
+
return windows.map((w) => [w[0], w[w.length - 1]]);
|
|
257
69
|
}
|
|
258
|
-
|
|
259
70
|
// src/object-hash.ts
|
|
260
71
|
import xh from "@node-rs/xxhash";
|
|
261
72
|
function getHash(obj) {
|
|
@@ -382,28 +193,63 @@ function memoize(func, options) {
|
|
|
382
193
|
}
|
|
383
194
|
return pMemoize(func, options);
|
|
384
195
|
}
|
|
385
|
-
// src/
|
|
386
|
-
function
|
|
387
|
-
|
|
388
|
-
for (let i = startAt;i <= endAt; i += step) {
|
|
389
|
-
arr.push([i, Math.min(endAt, i + step - 1)]);
|
|
390
|
-
}
|
|
391
|
-
return arr;
|
|
196
|
+
// src/log.ts
|
|
197
|
+
function isObject(a) {
|
|
198
|
+
return !!a && a.constructor === Object;
|
|
392
199
|
}
|
|
393
|
-
function
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
groups.push(array.slice(i, i + groupSize));
|
|
200
|
+
function print(o) {
|
|
201
|
+
if (Array.isArray(o)) {
|
|
202
|
+
return `[${o.map(print).join(", ")}]`;
|
|
397
203
|
}
|
|
398
|
-
|
|
204
|
+
if (isObject(o)) {
|
|
205
|
+
return `{${Object.keys(o).map((k) => `${k}: ${o[k]}`).join(", ")}}`;
|
|
206
|
+
}
|
|
207
|
+
return `${o}`;
|
|
399
208
|
}
|
|
400
|
-
function
|
|
401
|
-
|
|
402
|
-
for (let i =
|
|
403
|
-
|
|
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");
|
|
404
213
|
}
|
|
405
|
-
return
|
|
214
|
+
return line.trim();
|
|
215
|
+
}
|
|
216
|
+
function timestamp() {
|
|
217
|
+
return new Date().toISOString();
|
|
406
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
|
+
}
|
|
234
|
+
}
|
|
235
|
+
};
|
|
236
|
+
var logger = {
|
|
237
|
+
debug: function(...args) {
|
|
238
|
+
if (true) {
|
|
239
|
+
console.log(`[${timestamp()}]`, ...args);
|
|
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
|
+
}
|
|
251
|
+
}
|
|
252
|
+
};
|
|
407
253
|
// src/dynamodb.ts
|
|
408
254
|
import {
|
|
409
255
|
DynamoDBDocumentClient,
|
|
@@ -718,6 +564,44 @@ async function deleteRecordsByHashKey(tableName, indexName, hashKeyValue, verbos
|
|
|
718
564
|
}
|
|
719
565
|
return totalDeleted;
|
|
720
566
|
}
|
|
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
|
+
}
|
|
721
605
|
// src/cli.ts
|
|
722
606
|
import path from "path";
|
|
723
607
|
import fs from "fs";
|
|
@@ -753,36 +637,8 @@ function detectBin() {
|
|
|
753
637
|
const wd = detectDirectory(process.argv[1], "package.json");
|
|
754
638
|
return process.argv[1].slice(wd.length + path.sep.length).replace(path.sep, "/");
|
|
755
639
|
}
|
|
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
|
-
}
|
|
784
640
|
// src/indexer.ts
|
|
785
|
-
import
|
|
641
|
+
import meow from "meow";
|
|
786
642
|
var STATE_TABLE_NAME = "skynet-" + getEnvironment() + "-indexer-state";
|
|
787
643
|
async function getIndexerLatestId(name, selectorFlags) {
|
|
788
644
|
const record = await getRecordByKey(STATE_TABLE_NAME, {
|
|
@@ -1125,7 +981,7 @@ function createModeIndexerApp({
|
|
|
1125
981
|
if (!binaryName) {
|
|
1126
982
|
binaryName = getBinaryName();
|
|
1127
983
|
}
|
|
1128
|
-
const cli =
|
|
984
|
+
const cli = meow(`
|
|
1129
985
|
Usage
|
|
1130
986
|
|
|
1131
987
|
$ ${binaryName} <options>
|
|
@@ -1183,7 +1039,7 @@ function createIndexerApp({
|
|
|
1183
1039
|
if (!binaryName) {
|
|
1184
1040
|
binaryName = getBinaryName();
|
|
1185
1041
|
}
|
|
1186
|
-
const cli =
|
|
1042
|
+
const cli = meow(`
|
|
1187
1043
|
Usage
|
|
1188
1044
|
$ ${binaryName} <options>
|
|
1189
1045
|
|
|
@@ -1240,7 +1096,7 @@ ${selector ? getSelectorDesc(selector) : ""}
|
|
|
1240
1096
|
import fs2 from "fs/promises";
|
|
1241
1097
|
import fso from "fs";
|
|
1242
1098
|
import { execa } from "execa";
|
|
1243
|
-
import
|
|
1099
|
+
import meow2 from "meow";
|
|
1244
1100
|
import chalk from "chalk";
|
|
1245
1101
|
import which from "which";
|
|
1246
1102
|
var INTERVAL_ALIASES = {
|
|
@@ -1547,7 +1403,7 @@ function createModeDeploy({
|
|
|
1547
1403
|
if (!binaryName) {
|
|
1548
1404
|
binaryName = getBinaryName();
|
|
1549
1405
|
}
|
|
1550
|
-
const cli =
|
|
1406
|
+
const cli = meow2(`
|
|
1551
1407
|
Usage
|
|
1552
1408
|
|
|
1553
1409
|
$ ${binaryName} <options>
|
|
@@ -1677,7 +1533,7 @@ function createDeploy({
|
|
|
1677
1533
|
if (!binaryName) {
|
|
1678
1534
|
binaryName = getBinaryName();
|
|
1679
1535
|
}
|
|
1680
|
-
const cli =
|
|
1536
|
+
const cli = meow2(`
|
|
1681
1537
|
Usage
|
|
1682
1538
|
|
|
1683
1539
|
$ ${binaryName} <options>
|
|
@@ -1725,6 +1581,150 @@ ${getSelectorDesc(selector)}
|
|
|
1725
1581
|
}
|
|
1726
1582
|
return { deploy };
|
|
1727
1583
|
}
|
|
1584
|
+
// src/api.ts
|
|
1585
|
+
import osModule from "os";
|
|
1586
|
+
import express from "express";
|
|
1587
|
+
import meow3 from "meow";
|
|
1588
|
+
async function logStartMiddleware(_, res, next) {
|
|
1589
|
+
const start = new Date;
|
|
1590
|
+
res.set("x-requested-at", start.toISOString());
|
|
1591
|
+
next();
|
|
1592
|
+
}
|
|
1593
|
+
async function contextMiddleware(_, res, next) {
|
|
1594
|
+
res.set("x-instance-id", osModule.hostname());
|
|
1595
|
+
next();
|
|
1596
|
+
}
|
|
1597
|
+
async function logEndMiddleware(req, res, next) {
|
|
1598
|
+
const requestedAt = res.get("x-requested-at");
|
|
1599
|
+
if (!requestedAt) {
|
|
1600
|
+
inline.log("missing x-requested-at header");
|
|
1601
|
+
next();
|
|
1602
|
+
return;
|
|
1603
|
+
}
|
|
1604
|
+
const start = new Date(requestedAt).getTime();
|
|
1605
|
+
const end = new Date().getTime();
|
|
1606
|
+
const logInfo = {
|
|
1607
|
+
start,
|
|
1608
|
+
end,
|
|
1609
|
+
elapsed: `${end - start}ms`,
|
|
1610
|
+
endpoint: req.path,
|
|
1611
|
+
host: req.hostname,
|
|
1612
|
+
status: res.statusCode
|
|
1613
|
+
};
|
|
1614
|
+
if (res.statusMessage) {
|
|
1615
|
+
logInfo.errorMessage = res.statusMessage;
|
|
1616
|
+
}
|
|
1617
|
+
inline.log(JSON.stringify(logInfo));
|
|
1618
|
+
next();
|
|
1619
|
+
}
|
|
1620
|
+
var apiKeyMiddleware = (key) => {
|
|
1621
|
+
async function requireAPIKey(req, res, next) {
|
|
1622
|
+
try {
|
|
1623
|
+
const apiKey = req.get("x-api-key") || req.query["api-key"];
|
|
1624
|
+
if (!apiKey) {
|
|
1625
|
+
inline.log("request without api key");
|
|
1626
|
+
res.status(400).send("require x-api-key header");
|
|
1627
|
+
return;
|
|
1628
|
+
}
|
|
1629
|
+
if (typeof key === "string") {
|
|
1630
|
+
if (apiKey !== key) {
|
|
1631
|
+
inline.log("request has an invalid api key");
|
|
1632
|
+
res.status(400).send("invalid api key");
|
|
1633
|
+
return;
|
|
1634
|
+
}
|
|
1635
|
+
inline.log(`requested by valid key ${key.slice(0, 6)}`);
|
|
1636
|
+
} else {
|
|
1637
|
+
const name = key[apiKey];
|
|
1638
|
+
if (!name) {
|
|
1639
|
+
inline.log("request has an invalid api key");
|
|
1640
|
+
res.status(400).send("invalid api key");
|
|
1641
|
+
return;
|
|
1642
|
+
}
|
|
1643
|
+
inline.log(`requested by authorized user ${name}`);
|
|
1644
|
+
}
|
|
1645
|
+
next();
|
|
1646
|
+
} catch (err) {
|
|
1647
|
+
inline.log("check api key error", err);
|
|
1648
|
+
res.status(500).send("internal error");
|
|
1649
|
+
}
|
|
1650
|
+
}
|
|
1651
|
+
return requireAPIKey;
|
|
1652
|
+
};
|
|
1653
|
+
async function startApiApp({
|
|
1654
|
+
binaryName,
|
|
1655
|
+
name,
|
|
1656
|
+
selector = {},
|
|
1657
|
+
routes,
|
|
1658
|
+
serve,
|
|
1659
|
+
beforeListen
|
|
1660
|
+
}) {
|
|
1661
|
+
const app = express();
|
|
1662
|
+
app.use(express.json({ limit: "20mb" }));
|
|
1663
|
+
const cli = meow3(`
|
|
1664
|
+
Usage
|
|
1665
|
+
$ ${binaryName} <options>
|
|
1666
|
+
|
|
1667
|
+
Options
|
|
1668
|
+
${getSelectorDesc(selector)}
|
|
1669
|
+
--verbose Output debug messages
|
|
1670
|
+
`, {
|
|
1671
|
+
importMeta: import.meta,
|
|
1672
|
+
description: false,
|
|
1673
|
+
flags: {
|
|
1674
|
+
...getSelectorFlags(selector),
|
|
1675
|
+
verbose: {
|
|
1676
|
+
type: "boolean",
|
|
1677
|
+
default: false
|
|
1678
|
+
}
|
|
1679
|
+
}
|
|
1680
|
+
});
|
|
1681
|
+
const { verbose, ...selectorFlags } = cli.flags;
|
|
1682
|
+
for (const route of routes) {
|
|
1683
|
+
const method = route.method ? route.method.toLowerCase() : "get";
|
|
1684
|
+
const middlewares = route.middlewares || [];
|
|
1685
|
+
if (route.protected) {
|
|
1686
|
+
if (!serve.apiKey) {
|
|
1687
|
+
throw new Error("serve.apiKey is required for protected route");
|
|
1688
|
+
}
|
|
1689
|
+
middlewares.unshift(apiKeyMiddleware(serve.apiKey));
|
|
1690
|
+
}
|
|
1691
|
+
if (app[method]) {
|
|
1692
|
+
if (verbose) {
|
|
1693
|
+
inline.log(`registering ${method} ${route.path}`);
|
|
1694
|
+
}
|
|
1695
|
+
app[method](route.path, contextMiddleware, logStartMiddleware, ...middlewares, async (req, res, next) => {
|
|
1696
|
+
try {
|
|
1697
|
+
await route.handler({ req, res, ...selectorFlags });
|
|
1698
|
+
} catch (routeErr) {
|
|
1699
|
+
if (routeErr instanceof Error) {
|
|
1700
|
+
inline.log("caught route err", routeErr, routeErr.stack);
|
|
1701
|
+
res.status(500).send(`internal server error: ${routeErr.message}`);
|
|
1702
|
+
} else {
|
|
1703
|
+
inline.log("caught route err", routeErr);
|
|
1704
|
+
res.status(500).send("internal server error");
|
|
1705
|
+
}
|
|
1706
|
+
}
|
|
1707
|
+
next();
|
|
1708
|
+
}, logEndMiddleware);
|
|
1709
|
+
}
|
|
1710
|
+
}
|
|
1711
|
+
if (!routes.some((r) => r.path === "/" && r.method?.toUpperCase() === "GET")) {
|
|
1712
|
+
app.get("/", (_, res) => {
|
|
1713
|
+
res.send("ok");
|
|
1714
|
+
});
|
|
1715
|
+
}
|
|
1716
|
+
if (beforeListen) {
|
|
1717
|
+
await beforeListen({ app });
|
|
1718
|
+
}
|
|
1719
|
+
app.listen(serve.port, () => {
|
|
1720
|
+
if (isProduction()) {
|
|
1721
|
+
inline.log(`${name} listening at https://api.wf.corp.certik.com${serve.prefix}`);
|
|
1722
|
+
} else {
|
|
1723
|
+
inline.log(`${name} listening at http://localhost:${serve.port}`);
|
|
1724
|
+
}
|
|
1725
|
+
});
|
|
1726
|
+
}
|
|
1727
|
+
|
|
1728
1728
|
// src/app.ts
|
|
1729
1729
|
import { EOL } from "os";
|
|
1730
1730
|
function printAppHelp() {
|
package/dist/deploy.js
CHANGED
|
@@ -1,3 +1,22 @@
|
|
|
1
|
+
// src/env.ts
|
|
2
|
+
function ensureAndGet(envName, defaultValue) {
|
|
3
|
+
return process.env[envName] || defaultValue;
|
|
4
|
+
}
|
|
5
|
+
function getEnvironment() {
|
|
6
|
+
return ensureAndGet("SKYNET_ENVIRONMENT", "dev");
|
|
7
|
+
}
|
|
8
|
+
function getEnvOrThrow(envName) {
|
|
9
|
+
if (!process.env[envName]) {
|
|
10
|
+
throw new Error(`Must set environment variable ${envName}`);
|
|
11
|
+
}
|
|
12
|
+
return process.env[envName];
|
|
13
|
+
}
|
|
14
|
+
function isProduction() {
|
|
15
|
+
return getEnvironment() === "prd";
|
|
16
|
+
}
|
|
17
|
+
function isDev() {
|
|
18
|
+
return getEnvironment() === "dev";
|
|
19
|
+
}
|
|
1
20
|
// src/selector.ts
|
|
2
21
|
function getSelectorDesc(selector) {
|
|
3
22
|
return Object.keys(selector).map((name) => {
|
|
@@ -36,25 +55,6 @@ function getJobName(name, selectorFlags, mode) {
|
|
|
36
55
|
}
|
|
37
56
|
return jobName;
|
|
38
57
|
}
|
|
39
|
-
// src/env.ts
|
|
40
|
-
function ensureAndGet(envName, defaultValue) {
|
|
41
|
-
return process.env[envName] || defaultValue;
|
|
42
|
-
}
|
|
43
|
-
function getEnvironment() {
|
|
44
|
-
return ensureAndGet("SKYNET_ENVIRONMENT", "dev");
|
|
45
|
-
}
|
|
46
|
-
function getEnvOrThrow(envName) {
|
|
47
|
-
if (!process.env[envName]) {
|
|
48
|
-
throw new Error(`Must set environment variable ${envName}`);
|
|
49
|
-
}
|
|
50
|
-
return process.env[envName];
|
|
51
|
-
}
|
|
52
|
-
function isProduction() {
|
|
53
|
-
return getEnvironment() === "prd";
|
|
54
|
-
}
|
|
55
|
-
function isDev() {
|
|
56
|
-
return getEnvironment() === "dev";
|
|
57
|
-
}
|
|
58
58
|
// src/cli.ts
|
|
59
59
|
import path from "path";
|
|
60
60
|
import fs from "fs";
|