@certik/skynet 0.25.3 → 0.25.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/.vscode/settings.json +5 -0
- package/CHANGELOG.md +10 -1
- package/dist/api.js +38 -38
- package/dist/app.js +287 -293
- package/dist/cli.d.ts +1 -2
- package/dist/cli.js +0 -4
- package/dist/deploy.js +19 -22
- package/dist/dynamodb.js +22 -22
- package/dist/indexer.js +131 -134
- package/examples/api.ts +0 -0
- package/examples/indexer.ts +0 -0
- package/examples/mode-indexer.ts +0 -0
- package/package.json +7 -1
- package/src/dynamodb.ts +7 -0
- package/src/email.ts +40 -0
- package/src/indexer.ts +9 -2
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
|
-
}
|
|
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
|
-
}
|
|
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)]);
|
|
251
390
|
}
|
|
252
|
-
|
|
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));
|
|
397
|
+
}
|
|
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,11 +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
|
}
|
|
640
|
-
|
|
641
|
-
|
|
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]]);
|
|
642
783
|
}
|
|
643
784
|
// src/indexer.ts
|
|
644
|
-
import
|
|
785
|
+
import meow2 from "meow";
|
|
645
786
|
var STATE_TABLE_NAME = "skynet-" + getEnvironment() + "-indexer-state";
|
|
646
787
|
async function getIndexerLatestId(name, selectorFlags) {
|
|
647
788
|
const record = await getRecordByKey(STATE_TABLE_NAME, {
|
|
@@ -984,7 +1125,7 @@ function createModeIndexerApp({
|
|
|
984
1125
|
if (!binaryName) {
|
|
985
1126
|
binaryName = getBinaryName();
|
|
986
1127
|
}
|
|
987
|
-
const cli =
|
|
1128
|
+
const cli = meow2(`
|
|
988
1129
|
Usage
|
|
989
1130
|
|
|
990
1131
|
$ ${binaryName} <options>
|
|
@@ -1042,7 +1183,7 @@ function createIndexerApp({
|
|
|
1042
1183
|
if (!binaryName) {
|
|
1043
1184
|
binaryName = getBinaryName();
|
|
1044
1185
|
}
|
|
1045
|
-
const cli =
|
|
1186
|
+
const cli = meow2(`
|
|
1046
1187
|
Usage
|
|
1047
1188
|
$ ${binaryName} <options>
|
|
1048
1189
|
|
|
@@ -1099,7 +1240,7 @@ ${selector ? getSelectorDesc(selector) : ""}
|
|
|
1099
1240
|
import fs2 from "fs/promises";
|
|
1100
1241
|
import fso from "fs";
|
|
1101
1242
|
import { execa } from "execa";
|
|
1102
|
-
import
|
|
1243
|
+
import meow3 from "meow";
|
|
1103
1244
|
import chalk from "chalk";
|
|
1104
1245
|
import which from "which";
|
|
1105
1246
|
var INTERVAL_ALIASES = {
|
|
@@ -1406,7 +1547,7 @@ function createModeDeploy({
|
|
|
1406
1547
|
if (!binaryName) {
|
|
1407
1548
|
binaryName = getBinaryName();
|
|
1408
1549
|
}
|
|
1409
|
-
const cli =
|
|
1550
|
+
const cli = meow3(`
|
|
1410
1551
|
Usage
|
|
1411
1552
|
|
|
1412
1553
|
$ ${binaryName} <options>
|
|
@@ -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>
|
|
@@ -1584,150 +1725,6 @@ ${getSelectorDesc(selector)}
|
|
|
1584
1725
|
}
|
|
1585
1726
|
return { deploy };
|
|
1586
1727
|
}
|
|
1587
|
-
// src/api.ts
|
|
1588
|
-
import osModule from "os";
|
|
1589
|
-
import express from "express";
|
|
1590
|
-
import meow3 from "meow";
|
|
1591
|
-
async function logStartMiddleware(_, res, next) {
|
|
1592
|
-
const start = new Date;
|
|
1593
|
-
res.set("x-requested-at", start.toISOString());
|
|
1594
|
-
next();
|
|
1595
|
-
}
|
|
1596
|
-
async function contextMiddleware(_, res, next) {
|
|
1597
|
-
res.set("x-instance-id", osModule.hostname());
|
|
1598
|
-
next();
|
|
1599
|
-
}
|
|
1600
|
-
async function logEndMiddleware(req, res, next) {
|
|
1601
|
-
const requestedAt = res.get("x-requested-at");
|
|
1602
|
-
if (!requestedAt) {
|
|
1603
|
-
inline.log("missing x-requested-at header");
|
|
1604
|
-
next();
|
|
1605
|
-
return;
|
|
1606
|
-
}
|
|
1607
|
-
const start = new Date(requestedAt).getTime();
|
|
1608
|
-
const end = new Date().getTime();
|
|
1609
|
-
const logInfo = {
|
|
1610
|
-
start,
|
|
1611
|
-
end,
|
|
1612
|
-
elapsed: `${end - start}ms`,
|
|
1613
|
-
endpoint: req.path,
|
|
1614
|
-
host: req.hostname,
|
|
1615
|
-
status: res.statusCode
|
|
1616
|
-
};
|
|
1617
|
-
if (res.statusMessage) {
|
|
1618
|
-
logInfo.errorMessage = res.statusMessage;
|
|
1619
|
-
}
|
|
1620
|
-
inline.log(JSON.stringify(logInfo));
|
|
1621
|
-
next();
|
|
1622
|
-
}
|
|
1623
|
-
var apiKeyMiddleware = (key) => {
|
|
1624
|
-
async function requireAPIKey(req, res, next) {
|
|
1625
|
-
try {
|
|
1626
|
-
const apiKey = req.get("x-api-key") || req.query["api-key"];
|
|
1627
|
-
if (!apiKey) {
|
|
1628
|
-
inline.log("request without api key");
|
|
1629
|
-
res.status(400).send("require x-api-key header");
|
|
1630
|
-
return;
|
|
1631
|
-
}
|
|
1632
|
-
if (typeof key === "string") {
|
|
1633
|
-
if (apiKey !== key) {
|
|
1634
|
-
inline.log("request has an invalid api key");
|
|
1635
|
-
res.status(400).send("invalid api key");
|
|
1636
|
-
return;
|
|
1637
|
-
}
|
|
1638
|
-
inline.log(`requested by valid key ${key.slice(0, 6)}`);
|
|
1639
|
-
} else {
|
|
1640
|
-
const name = key[apiKey];
|
|
1641
|
-
if (!name) {
|
|
1642
|
-
inline.log("request has an invalid api key");
|
|
1643
|
-
res.status(400).send("invalid api key");
|
|
1644
|
-
return;
|
|
1645
|
-
}
|
|
1646
|
-
inline.log(`requested by authorized user ${name}`);
|
|
1647
|
-
}
|
|
1648
|
-
next();
|
|
1649
|
-
} catch (err) {
|
|
1650
|
-
inline.log("check api key error", err);
|
|
1651
|
-
res.status(500).send("internal error");
|
|
1652
|
-
}
|
|
1653
|
-
}
|
|
1654
|
-
return requireAPIKey;
|
|
1655
|
-
};
|
|
1656
|
-
async function startApiApp({
|
|
1657
|
-
binaryName,
|
|
1658
|
-
name,
|
|
1659
|
-
selector = {},
|
|
1660
|
-
routes,
|
|
1661
|
-
serve,
|
|
1662
|
-
beforeListen
|
|
1663
|
-
}) {
|
|
1664
|
-
const app = express();
|
|
1665
|
-
app.use(express.json({ limit: "20mb" }));
|
|
1666
|
-
const cli = meow3(`
|
|
1667
|
-
Usage
|
|
1668
|
-
$ ${binaryName} <options>
|
|
1669
|
-
|
|
1670
|
-
Options
|
|
1671
|
-
${getSelectorDesc(selector)}
|
|
1672
|
-
--verbose Output debug messages
|
|
1673
|
-
`, {
|
|
1674
|
-
importMeta: import.meta,
|
|
1675
|
-
description: false,
|
|
1676
|
-
flags: {
|
|
1677
|
-
...getSelectorFlags(selector),
|
|
1678
|
-
verbose: {
|
|
1679
|
-
type: "boolean",
|
|
1680
|
-
default: false
|
|
1681
|
-
}
|
|
1682
|
-
}
|
|
1683
|
-
});
|
|
1684
|
-
const { verbose, ...selectorFlags } = cli.flags;
|
|
1685
|
-
for (const route of routes) {
|
|
1686
|
-
const method = route.method ? route.method.toLowerCase() : "get";
|
|
1687
|
-
const middlewares = route.middlewares || [];
|
|
1688
|
-
if (route.protected) {
|
|
1689
|
-
if (!serve.apiKey) {
|
|
1690
|
-
throw new Error("serve.apiKey is required for protected route");
|
|
1691
|
-
}
|
|
1692
|
-
middlewares.unshift(apiKeyMiddleware(serve.apiKey));
|
|
1693
|
-
}
|
|
1694
|
-
if (app[method]) {
|
|
1695
|
-
if (verbose) {
|
|
1696
|
-
inline.log(`registering ${method} ${route.path}`);
|
|
1697
|
-
}
|
|
1698
|
-
app[method](route.path, contextMiddleware, logStartMiddleware, ...middlewares, async (req, res, next) => {
|
|
1699
|
-
try {
|
|
1700
|
-
await route.handler({ req, res, ...selectorFlags });
|
|
1701
|
-
} catch (routeErr) {
|
|
1702
|
-
if (routeErr instanceof Error) {
|
|
1703
|
-
inline.log("caught route err", routeErr, routeErr.stack);
|
|
1704
|
-
res.status(500).send(`internal server error: ${routeErr.message}`);
|
|
1705
|
-
} else {
|
|
1706
|
-
inline.log("caught route err", routeErr);
|
|
1707
|
-
res.status(500).send("internal server error");
|
|
1708
|
-
}
|
|
1709
|
-
}
|
|
1710
|
-
next();
|
|
1711
|
-
}, logEndMiddleware);
|
|
1712
|
-
}
|
|
1713
|
-
}
|
|
1714
|
-
if (!routes.some((r) => r.path === "/" && r.method?.toUpperCase() === "GET")) {
|
|
1715
|
-
app.get("/", (_, res) => {
|
|
1716
|
-
res.send("ok");
|
|
1717
|
-
});
|
|
1718
|
-
}
|
|
1719
|
-
if (beforeListen) {
|
|
1720
|
-
await beforeListen({ app });
|
|
1721
|
-
}
|
|
1722
|
-
app.listen(serve.port, () => {
|
|
1723
|
-
if (isProduction()) {
|
|
1724
|
-
inline.log(`${name} listening at https://api.wf.corp.certik.com${serve.prefix}`);
|
|
1725
|
-
} else {
|
|
1726
|
-
inline.log(`${name} listening at http://localhost:${serve.port}`);
|
|
1727
|
-
}
|
|
1728
|
-
});
|
|
1729
|
-
}
|
|
1730
|
-
|
|
1731
1728
|
// src/app.ts
|
|
1732
1729
|
import { EOL } from "os";
|
|
1733
1730
|
function printAppHelp() {
|
|
@@ -1837,13 +1834,12 @@ function indexer({
|
|
|
1837
1834
|
},
|
|
1838
1835
|
onDeploy: () => {
|
|
1839
1836
|
const bin = detectBin();
|
|
1840
|
-
const runBin = getDeployBin(bin);
|
|
1841
1837
|
const needDoppler = Object.values(env).some((v) => v === SENSITIVE_VALUE);
|
|
1842
1838
|
const { deploy } = createDeploy({
|
|
1843
1839
|
binaryName: `${getBinaryName()} deploy`,
|
|
1844
1840
|
name,
|
|
1845
1841
|
workingDirectory: detectWorkingDirectory(),
|
|
1846
|
-
bin: needDoppler ? `doppler run -- ${
|
|
1842
|
+
bin: needDoppler ? `doppler run -- ${bin} run` : `${bin} run`,
|
|
1847
1843
|
selector,
|
|
1848
1844
|
region,
|
|
1849
1845
|
env,
|
|
@@ -1921,13 +1917,12 @@ function modeIndexer({
|
|
|
1921
1917
|
},
|
|
1922
1918
|
onDeploy: () => {
|
|
1923
1919
|
const bin = detectBin();
|
|
1924
|
-
const runBin = getDeployBin(bin);
|
|
1925
1920
|
const needDoppler = Object.values(env).some((v) => v === SENSITIVE_VALUE);
|
|
1926
1921
|
const { deploy } = createModeDeploy({
|
|
1927
1922
|
binaryName: `${getBinaryName()} deploy`,
|
|
1928
1923
|
name,
|
|
1929
1924
|
workingDirectory: detectWorkingDirectory(),
|
|
1930
|
-
bin: needDoppler ? `doppler run -- ${
|
|
1925
|
+
bin: needDoppler ? `doppler run -- ${bin} run` : `${bin} run`,
|
|
1931
1926
|
selector,
|
|
1932
1927
|
region,
|
|
1933
1928
|
env,
|
|
@@ -2017,13 +2012,12 @@ function api({
|
|
|
2017
2012
|
},
|
|
2018
2013
|
onDeploy: () => {
|
|
2019
2014
|
const bin = detectBin();
|
|
2020
|
-
const runBin = getDeployBin(bin);
|
|
2021
2015
|
const needDoppler = Object.values(env).some((v) => v === SENSITIVE_VALUE);
|
|
2022
2016
|
const { deploy } = createDeploy({
|
|
2023
2017
|
binaryName: `${getBinaryName()} deploy`,
|
|
2024
2018
|
name,
|
|
2025
2019
|
workingDirectory: detectWorkingDirectory(),
|
|
2026
|
-
bin: needDoppler ? `doppler run -- ${
|
|
2020
|
+
bin: needDoppler ? `doppler run -- ${bin} run` : `${bin} run`,
|
|
2027
2021
|
selector,
|
|
2028
2022
|
region,
|
|
2029
2023
|
env,
|
package/dist/cli.d.ts
CHANGED
|
@@ -2,5 +2,4 @@ declare function getBinaryName(): string;
|
|
|
2
2
|
declare function detectSkynetDirectory(): string;
|
|
3
3
|
declare function detectWorkingDirectory(): string;
|
|
4
4
|
declare function detectBin(): string;
|
|
5
|
-
|
|
6
|
-
export { getBinaryName, detectSkynetDirectory, detectWorkingDirectory, detectBin, getDeployBin };
|
|
5
|
+
export { getBinaryName, detectSkynetDirectory, detectWorkingDirectory, detectBin };
|