@certik/skynet 0.9.6 → 0.10.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 +12 -0
- package/api.js +105 -0
- package/app.js +150 -9
- package/const.js +1 -1
- package/deploy.js +35 -0
- package/examples/api +73 -0
- package/examples/consumer +1 -1
- package/examples/indexer +1 -1
- package/examples/mode-indexer +1 -1
- package/examples/producer +1 -1
- package/package.json +2 -1
- package/examples/legacy-deploy-consumer +0 -24
- package/examples/legacy-deploy-indexer +0 -19
- package/examples/legacy-deploy-mode-indexer +0 -22
- package/examples/legacy-deploy-producer +0 -25
- package/examples/legacy-indexer +0 -25
- package/examples/legacy-kafka-consumer +0 -33
- package/examples/legacy-kafka-producer +0 -64
- package/examples/legacy-mode-indexer +0 -64
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.10.1
|
|
4
|
+
|
|
5
|
+
- Fixed a bug for the new api app type
|
|
6
|
+
|
|
7
|
+
## 0.10.0
|
|
8
|
+
|
|
9
|
+
- Added a new application type `api` to the `app` library
|
|
10
|
+
|
|
11
|
+
## 0.9.7
|
|
12
|
+
|
|
13
|
+
- Added Polygon multicall support
|
|
14
|
+
|
|
3
15
|
## 0.9.6
|
|
4
16
|
|
|
5
17
|
- Updated `inline` method in `log` library to print a timestamp in front of each log line
|
package/api.js
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
const express = require("express");
|
|
2
|
+
const meow = require("meow");
|
|
3
|
+
const { getSelectorFlags, getSelectorDesc } = require("./selector");
|
|
4
|
+
const { isProduction } = require("./env");
|
|
5
|
+
|
|
6
|
+
const apiKeyMiddleware = (key) => {
|
|
7
|
+
async function requireAPIKey(req, res, next) {
|
|
8
|
+
try {
|
|
9
|
+
const apiKey = req.get("x-api-key");
|
|
10
|
+
|
|
11
|
+
if (!apiKey) {
|
|
12
|
+
console.log("request without api key");
|
|
13
|
+
|
|
14
|
+
res.status(400).send("require x-api-key header");
|
|
15
|
+
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (apiKey !== key) {
|
|
20
|
+
console.log("request has an invalid api key");
|
|
21
|
+
|
|
22
|
+
res.status(400).send("invalid api key");
|
|
23
|
+
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
next();
|
|
28
|
+
} catch (err) {
|
|
29
|
+
console.log("check api key error", err);
|
|
30
|
+
|
|
31
|
+
res.status(500).send("internal error");
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return requireAPIKey;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
function startApiApp({ binaryName, name, selector = {}, routes, serve }) {
|
|
39
|
+
const app = express();
|
|
40
|
+
app.use(express.json());
|
|
41
|
+
|
|
42
|
+
const cli = meow(
|
|
43
|
+
`
|
|
44
|
+
Usage
|
|
45
|
+
$ ${binaryName} <options>
|
|
46
|
+
|
|
47
|
+
Options
|
|
48
|
+
${getSelectorDesc(selector)}
|
|
49
|
+
--verbose Output debug messages
|
|
50
|
+
`,
|
|
51
|
+
{
|
|
52
|
+
description: false,
|
|
53
|
+
version: false,
|
|
54
|
+
flags: {
|
|
55
|
+
...getSelectorFlags(selector),
|
|
56
|
+
verbose: {
|
|
57
|
+
type: "boolean",
|
|
58
|
+
default: false,
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
}
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
const { verbose, ...selectorFlags } = cli.flags;
|
|
65
|
+
|
|
66
|
+
// for health check
|
|
67
|
+
app.get("/", (req, res) => {
|
|
68
|
+
res.send("ok");
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
for (const route of routes) {
|
|
72
|
+
const method = route.method ? route.method.toLowerCase() : "get";
|
|
73
|
+
const middlewares = route.middlewares || [];
|
|
74
|
+
|
|
75
|
+
if (route.protected) {
|
|
76
|
+
middlewares.push(apiKeyMiddleware(serve.apiKey));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (app[method]) {
|
|
80
|
+
if (verbose) {
|
|
81
|
+
console.log(`registering ${method} ${route.path}`);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
app[method](route.path, ...middlewares, async (req, res) => {
|
|
85
|
+
try {
|
|
86
|
+
await route.handler({ req, res, ...selectorFlags });
|
|
87
|
+
} catch (routeErr) {
|
|
88
|
+
console.log("caught route err", routeErr);
|
|
89
|
+
|
|
90
|
+
res.status(500).send(`internal server error: ${routeErr.message}`);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
app.listen(serve.port, () => {
|
|
97
|
+
if (isProduction()) {
|
|
98
|
+
console.log(`${name} listening at https://api.certik-skynet.com/${serve.prefix}`);
|
|
99
|
+
} else {
|
|
100
|
+
console.log(`${name} listening at http://localhost:${serve.port}`);
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
module.exports = { startApiApp };
|
package/app.js
CHANGED
|
@@ -4,6 +4,7 @@ const { SKYNET_API_PREFIX } = require("./const");
|
|
|
4
4
|
const { createIndexerApp, createModeIndexerApp } = require("./indexer");
|
|
5
5
|
const { createDeploy, createModeDeploy } = require("./deploy");
|
|
6
6
|
const { createProducerApp, createConsumerApp } = require("./kafka");
|
|
7
|
+
const { startApiApp } = require("./api");
|
|
7
8
|
const { createMonitor, ERROR_LEVEL } = require("./monitor");
|
|
8
9
|
const { getBinaryName, detectBin, detectWorkingDirectory } = require("./cli");
|
|
9
10
|
|
|
@@ -202,7 +203,7 @@ function indexer({ name, selector, build, check, env = {}, region = "us-east-1"
|
|
|
202
203
|
bin: `${bin} check`,
|
|
203
204
|
schedule: check.schedule,
|
|
204
205
|
cpu: check.cpu || 100,
|
|
205
|
-
mem: check.mem || 100
|
|
206
|
+
mem: check.mem || 100,
|
|
206
207
|
},
|
|
207
208
|
});
|
|
208
209
|
|
|
@@ -215,7 +216,7 @@ function indexer({ name, selector, build, check, env = {}, region = "us-east-1"
|
|
|
215
216
|
type: "stateless",
|
|
216
217
|
selector,
|
|
217
218
|
check: check.func,
|
|
218
|
-
maxRetry: check.maxRetry
|
|
219
|
+
maxRetry: check.maxRetry,
|
|
219
220
|
});
|
|
220
221
|
|
|
221
222
|
monitor();
|
|
@@ -315,7 +316,7 @@ function modeIndexer({ name, selector, state, build, validate, check, env = {},
|
|
|
315
316
|
bin: `${bin} check`,
|
|
316
317
|
schedule: check.schedule,
|
|
317
318
|
cpu: check.cpu || 100,
|
|
318
|
-
mem: check.mem || 100
|
|
319
|
+
mem: check.mem || 100,
|
|
319
320
|
},
|
|
320
321
|
});
|
|
321
322
|
|
|
@@ -329,7 +330,7 @@ function modeIndexer({ name, selector, state, build, validate, check, env = {},
|
|
|
329
330
|
selector,
|
|
330
331
|
mode: true,
|
|
331
332
|
check: check.func,
|
|
332
|
-
maxRetry: check.maxRetry
|
|
333
|
+
maxRetry: check.maxRetry,
|
|
333
334
|
});
|
|
334
335
|
|
|
335
336
|
monitor();
|
|
@@ -417,7 +418,7 @@ function producer({ name, selector, produce, check, state, env = {}, region = "u
|
|
|
417
418
|
bin: `${bin} check`,
|
|
418
419
|
schedule: check.schedule,
|
|
419
420
|
cpu: check.cpu || 100,
|
|
420
|
-
mem: check.mem || 100
|
|
421
|
+
mem: check.mem || 100,
|
|
421
422
|
},
|
|
422
423
|
});
|
|
423
424
|
|
|
@@ -430,7 +431,7 @@ function producer({ name, selector, produce, check, state, env = {}, region = "u
|
|
|
430
431
|
type: "stateful",
|
|
431
432
|
selector,
|
|
432
433
|
check: check.func,
|
|
433
|
-
maxRetry: check.maxRetry
|
|
434
|
+
maxRetry: check.maxRetry,
|
|
434
435
|
});
|
|
435
436
|
|
|
436
437
|
monitor();
|
|
@@ -509,7 +510,7 @@ function consumer({ name, selector, consume, check, env = {}, region = "us-east-
|
|
|
509
510
|
bin: `${bin} check`,
|
|
510
511
|
schedule: check.schedule,
|
|
511
512
|
cpu: check.cpu || 100,
|
|
512
|
-
mem: check.mem || 100
|
|
513
|
+
mem: check.mem || 100,
|
|
513
514
|
},
|
|
514
515
|
});
|
|
515
516
|
|
|
@@ -522,7 +523,147 @@ function consumer({ name, selector, consume, check, env = {}, region = "us-east-
|
|
|
522
523
|
type: "stateless",
|
|
523
524
|
selector,
|
|
524
525
|
check: check.func,
|
|
525
|
-
maxRetry: check.maxRetry
|
|
526
|
+
maxRetry: check.maxRetry,
|
|
527
|
+
});
|
|
528
|
+
|
|
529
|
+
monitor();
|
|
530
|
+
},
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
function checkApiServeParameter(serve, routes) {
|
|
535
|
+
const errors = [];
|
|
536
|
+
|
|
537
|
+
if (!serve?.prefix) {
|
|
538
|
+
errors.push("must define serve.prefix");
|
|
539
|
+
} else if (!serve.prefix.startsWith("/")) {
|
|
540
|
+
errors.push("server.prefix must start with /, e.g. /my-api");
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
if (!serve?.port) {
|
|
544
|
+
errors.push("must define serve.port");
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
if (!serve?.cpu) {
|
|
548
|
+
errors.push("must define serve.cpu");
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
if (!serve?.mem) {
|
|
552
|
+
errors.push("must define serve.mem");
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
if (routes.some((r) => r.protected) && !serve.apiKey) {
|
|
556
|
+
errors.push("must define serve.apiKey since some routes are protected");
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
return errors;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
function checkApiRoutesParameter(routes) {
|
|
563
|
+
const errors = [];
|
|
564
|
+
|
|
565
|
+
if (!Array.isArray(routes)) {
|
|
566
|
+
errors.push("routes must be an array");
|
|
567
|
+
} else {
|
|
568
|
+
for (let i = 0; i < routes.length; i++) {
|
|
569
|
+
const route = routes[i];
|
|
570
|
+
|
|
571
|
+
if (!route.path) {
|
|
572
|
+
errors.push(`routes[${i}] must define path`);
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
if (!route.handler) {
|
|
576
|
+
errors.push(`routes[${i}] must define handler`);
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
if (route.middlewares && !Array.isArray(route.middlewares)) {
|
|
580
|
+
errors.push(`routes[${i}].middlewares must be an array`);
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
return errors;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
function api({ name, routes, serve, env = {}, region = "us-east-1" }) {
|
|
589
|
+
// do not support selector for now
|
|
590
|
+
const selector = {};
|
|
591
|
+
|
|
592
|
+
// hard code a simple health check for now
|
|
593
|
+
const check = {
|
|
594
|
+
func: async () => {
|
|
595
|
+
const errors = [];
|
|
596
|
+
|
|
597
|
+
const url = `http://localhost:9999${serve.prefix}/`;
|
|
598
|
+
const res = await fetch(url);
|
|
599
|
+
|
|
600
|
+
if (!res.ok) {
|
|
601
|
+
errors.push({
|
|
602
|
+
type: ERROR_LEVEL.ERROR,
|
|
603
|
+
message: `service ${name} is not healthy`,
|
|
604
|
+
});
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
return errors;
|
|
608
|
+
},
|
|
609
|
+
schedule: every(5).minutes,
|
|
610
|
+
maxRetry: 0,
|
|
611
|
+
};
|
|
612
|
+
|
|
613
|
+
return createApp({
|
|
614
|
+
parameterErrors: [
|
|
615
|
+
...checkApiRoutesParameter(routes),
|
|
616
|
+
...checkApiServeParameter(serve, routes),
|
|
617
|
+
...checkCheckParameter(check),
|
|
618
|
+
...checkEnvParameter(env),
|
|
619
|
+
],
|
|
620
|
+
env,
|
|
621
|
+
check,
|
|
622
|
+
onRun: () => {
|
|
623
|
+
startApiApp({
|
|
624
|
+
binaryName: `${getBinaryName()} run`,
|
|
625
|
+
name,
|
|
626
|
+
selector,
|
|
627
|
+
routes,
|
|
628
|
+
serve,
|
|
629
|
+
});
|
|
630
|
+
},
|
|
631
|
+
onDeploy: () => {
|
|
632
|
+
const bin = detectBin();
|
|
633
|
+
|
|
634
|
+
const { deploy } = createDeploy({
|
|
635
|
+
binaryName: `${getBinaryName()} deploy`,
|
|
636
|
+
name,
|
|
637
|
+
workingDirectory: detectWorkingDirectory(),
|
|
638
|
+
bin: `${bin} run`,
|
|
639
|
+
selector,
|
|
640
|
+
region,
|
|
641
|
+
env,
|
|
642
|
+
schedule: "@minutely",
|
|
643
|
+
cpu: serve.cpu,
|
|
644
|
+
mem: serve.mem,
|
|
645
|
+
service: {
|
|
646
|
+
prefix: serve.prefix,
|
|
647
|
+
port: serve.port,
|
|
648
|
+
},
|
|
649
|
+
check: check && {
|
|
650
|
+
bin: `${bin} check`,
|
|
651
|
+
schedule: check.schedule,
|
|
652
|
+
cpu: check.cpu || 100,
|
|
653
|
+
mem: check.mem || 100,
|
|
654
|
+
},
|
|
655
|
+
});
|
|
656
|
+
|
|
657
|
+
deploy();
|
|
658
|
+
},
|
|
659
|
+
onCheck: () => {
|
|
660
|
+
const { monitor } = createMonitor({
|
|
661
|
+
binaryName: `${getBinaryName()} check`,
|
|
662
|
+
name,
|
|
663
|
+
type: "stateless",
|
|
664
|
+
selector,
|
|
665
|
+
check: check.func,
|
|
666
|
+
maxRetry: check.maxRetry,
|
|
526
667
|
});
|
|
527
668
|
|
|
528
669
|
monitor();
|
|
@@ -551,4 +692,4 @@ const every = (n = 1) => {
|
|
|
551
692
|
};
|
|
552
693
|
};
|
|
553
694
|
|
|
554
|
-
module.exports = { indexer, modeIndexer, producer, consumer, every, SENSITIVE_VALUE, ERROR_LEVEL };
|
|
695
|
+
module.exports = { indexer, modeIndexer, producer, consumer, api, every, SENSITIVE_VALUE, ERROR_LEVEL };
|
package/const.js
CHANGED
package/deploy.js
CHANGED
|
@@ -49,6 +49,7 @@ const genConfig = ({
|
|
|
49
49
|
restart,
|
|
50
50
|
cpu,
|
|
51
51
|
mem,
|
|
52
|
+
service,
|
|
52
53
|
additionalEnv = [],
|
|
53
54
|
region = "us-east-1",
|
|
54
55
|
isProduction,
|
|
@@ -83,6 +84,16 @@ const genConfig = ({
|
|
|
83
84
|
unlimited = false
|
|
84
85
|
}
|
|
85
86
|
|
|
87
|
+
${
|
|
88
|
+
service
|
|
89
|
+
? `# Setup Service Network
|
|
90
|
+
network {
|
|
91
|
+
port "http" {
|
|
92
|
+
static = ${service.port}
|
|
93
|
+
}
|
|
94
|
+
}` : ""
|
|
95
|
+
}
|
|
96
|
+
|
|
86
97
|
task "log-shipper" {
|
|
87
98
|
driver = "raw_exec"
|
|
88
99
|
|
|
@@ -123,6 +134,28 @@ const genConfig = ({
|
|
|
123
134
|
]
|
|
124
135
|
}
|
|
125
136
|
|
|
137
|
+
${
|
|
138
|
+
service
|
|
139
|
+
? `# Setup API Routes
|
|
140
|
+
service {
|
|
141
|
+
name = "${jobName}"
|
|
142
|
+
port = "http"
|
|
143
|
+
|
|
144
|
+
tags = [
|
|
145
|
+
"urlprefix-${service.prefix} strip=${service.prefix}",
|
|
146
|
+
]
|
|
147
|
+
|
|
148
|
+
check {
|
|
149
|
+
type = "http"
|
|
150
|
+
path = "/"
|
|
151
|
+
interval = "10s"
|
|
152
|
+
timeout = "2s"
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
`
|
|
156
|
+
: ""
|
|
157
|
+
}
|
|
158
|
+
|
|
126
159
|
template {
|
|
127
160
|
change_mode = "restart"
|
|
128
161
|
destination = "secrets/context.env"
|
|
@@ -507,6 +540,7 @@ function createDeploy({
|
|
|
507
540
|
restart,
|
|
508
541
|
cpu,
|
|
509
542
|
mem,
|
|
543
|
+
service,
|
|
510
544
|
}) {
|
|
511
545
|
async function deployModeless({ production, stop, dryRun, verbose, schedule: cmdSchedule, ...selectorFlags }) {
|
|
512
546
|
const jobName = getJobName(name, selectorFlags, null);
|
|
@@ -538,6 +572,7 @@ function createDeploy({
|
|
|
538
572
|
cmd: `${bin} ${args}`,
|
|
539
573
|
cpu,
|
|
540
574
|
mem,
|
|
575
|
+
service,
|
|
541
576
|
isProduction: production,
|
|
542
577
|
});
|
|
543
578
|
|
package/examples/api
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// this file is executable and is for kafka producer testing
|
|
4
|
+
// a few test commands to try on
|
|
5
|
+
// $ examples/api run --verbose
|
|
6
|
+
// $ examples/api run
|
|
7
|
+
// $ examples/api check
|
|
8
|
+
// $ examples/api deploy
|
|
9
|
+
// $ examples/api --help
|
|
10
|
+
|
|
11
|
+
const { api, every, SENSITIVE_VALUE, ERROR_LEVEL } = require("../app");
|
|
12
|
+
|
|
13
|
+
// an example middleware
|
|
14
|
+
async function logRequest(req, res, next) {
|
|
15
|
+
const start = new Date();
|
|
16
|
+
next();
|
|
17
|
+
const end = new Date();
|
|
18
|
+
const logInfo = { start, end, elapsed: `${end - start}ms` };
|
|
19
|
+
console.log(logInfo);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async function getProjectsHandler({ req, res, verbose }) {
|
|
23
|
+
console.log("receieved call", req.query, verbose);
|
|
24
|
+
|
|
25
|
+
const type = req.query.type;
|
|
26
|
+
|
|
27
|
+
if (!type) {
|
|
28
|
+
res.status(400).end("must provide type parameter");
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
res.json([{ id: "proj-1", name: "Test Project 1" }]);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async function createProjectHandler({ req, res, verbose }) {
|
|
36
|
+
console.log("receieved call", req.body, verbose);
|
|
37
|
+
|
|
38
|
+
res.json({ success: true });
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const app = api({
|
|
42
|
+
name: "example-api",
|
|
43
|
+
|
|
44
|
+
env: {
|
|
45
|
+
EXAMPLE_API_KEY: SENSITIVE_VALUE,
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
routes: [
|
|
49
|
+
{
|
|
50
|
+
method: "GET",
|
|
51
|
+
path: "/projects",
|
|
52
|
+
handler: getProjectsHandler,
|
|
53
|
+
middlewares: [logRequest],
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
method: "POST",
|
|
57
|
+
path: "/projects",
|
|
58
|
+
handler: createProjectHandler,
|
|
59
|
+
protected: true,
|
|
60
|
+
middlewares: [logRequest],
|
|
61
|
+
},
|
|
62
|
+
],
|
|
63
|
+
|
|
64
|
+
serve: {
|
|
65
|
+
prefix: "/example-api", // deployed to a sub path
|
|
66
|
+
port: 12345,
|
|
67
|
+
apiKey: process.env.EXAMPLE_API_KEY,
|
|
68
|
+
cpu: 600,
|
|
69
|
+
mem: 200,
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
app();
|
package/examples/consumer
CHANGED
|
@@ -25,7 +25,7 @@ async function check({ protocol, state, verbose }) {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
const app = consumer({
|
|
28
|
-
name: "
|
|
28
|
+
name: "example-consumer",
|
|
29
29
|
selector: { protocol: { type: "string", description: "from which chain to consume data" } },
|
|
30
30
|
|
|
31
31
|
consume: {
|
package/examples/indexer
CHANGED
package/examples/mode-indexer
CHANGED
|
@@ -32,7 +32,7 @@ async function check({ mode, protocol, state, verbose }) {
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
const app = modeIndexer({
|
|
35
|
-
name: "
|
|
35
|
+
name: "example-mode-indexer",
|
|
36
36
|
|
|
37
37
|
selector: {
|
|
38
38
|
// for more flags check meow documentation at https://github.com/sindresorhus/meow
|
package/examples/producer
CHANGED
|
@@ -32,7 +32,7 @@ async function check({ protocol, state, verbose }) {
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
const app = producer({
|
|
35
|
-
name: "
|
|
35
|
+
name: "example-producer",
|
|
36
36
|
selector: { protocol: { type: "string", description: "for which chain to produce data" } },
|
|
37
37
|
|
|
38
38
|
env: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@certik/skynet",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.1",
|
|
4
4
|
"description": "Skynet Shared JS library",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"author": "CertiK Engineering",
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
"aws-sdk": "^2.932.0",
|
|
17
17
|
"chalk": "^4.1.1",
|
|
18
18
|
"execa": "^5.0.0",
|
|
19
|
+
"express": "^4.18.1",
|
|
19
20
|
"kafkajs": "^1.15.0",
|
|
20
21
|
"meow": "^7.0.1",
|
|
21
22
|
"node-fetch": "^2.6.1",
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
// this file is executable and is for deployer testing
|
|
4
|
-
// try this test command
|
|
5
|
-
// $ examples/deploy-consumer --protocol bsc
|
|
6
|
-
|
|
7
|
-
const { createDeploy } = require("../deploy");
|
|
8
|
-
|
|
9
|
-
const { deploy } = createDeploy({
|
|
10
|
-
name: "lib-skynet-test-consumer",
|
|
11
|
-
workingDirectory: "lib-skynet",
|
|
12
|
-
bin: "examples/kafka-consumer",
|
|
13
|
-
selector: { protocol: { type: "string" } },
|
|
14
|
-
env: {
|
|
15
|
-
SKYNET_KAFKA_SERVER: process.env.SKYNET_KAFKA_SERVER,
|
|
16
|
-
SKYNET_KAFKA_USERNAME: process.env.SKYNET_KAFKA_USERNAME,
|
|
17
|
-
SKYNET_KAFKA_PASSWORD: process.env.SKYNET_KAFKA_PASSWORD
|
|
18
|
-
},
|
|
19
|
-
restart: { attempts: 3 },
|
|
20
|
-
cpu: 600,
|
|
21
|
-
mem: 200
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
deploy();
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
// this file is executable and is for indexer testing
|
|
4
|
-
// try this test command
|
|
5
|
-
// $ examples/deploy-indexer --protocol bsc
|
|
6
|
-
|
|
7
|
-
const { createDeploy } = require("../deploy");
|
|
8
|
-
|
|
9
|
-
const { deploy } = createDeploy({
|
|
10
|
-
name: "lib-skynet-test-indexer",
|
|
11
|
-
workingDirectory: "lib-skynet",
|
|
12
|
-
bin: "examples/indexer",
|
|
13
|
-
selector: { protocol: { type: "string" } },
|
|
14
|
-
schedule: "@minutely",
|
|
15
|
-
cpu: 600,
|
|
16
|
-
mem: 200,
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
deploy();
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
// this file is executable and is for indexer with mode testing
|
|
4
|
-
// try this test command
|
|
5
|
-
// $ examples/deploy-mode-indexer --protocol bsc
|
|
6
|
-
|
|
7
|
-
const { createModeDeploy } = require("../deploy");
|
|
8
|
-
|
|
9
|
-
const { deploy } = createModeDeploy({
|
|
10
|
-
name: "lib-skynet-test-mode-indexer",
|
|
11
|
-
workingDirectory: "lib-skynet",
|
|
12
|
-
bin: "examples/mode-indexer",
|
|
13
|
-
selector: { protocol: { type: "string" } },
|
|
14
|
-
deltaSchedule: "@minutely",
|
|
15
|
-
validateSchedule: "@hourly",
|
|
16
|
-
deltaCpu: 600,
|
|
17
|
-
deltaMem: 200,
|
|
18
|
-
rebuildCpu: 800,
|
|
19
|
-
rebuildMem: 400
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
deploy();
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
// this file is executable and is for deployer testing
|
|
4
|
-
// try this test command
|
|
5
|
-
// $ examples/deploy-producer --protocol bsc --from 1
|
|
6
|
-
|
|
7
|
-
const path = require("path");
|
|
8
|
-
|
|
9
|
-
const { createDeploy } = require("../deploy");
|
|
10
|
-
|
|
11
|
-
const { deploy } = createDeploy({
|
|
12
|
-
name: "lib-skynet-test-producer",
|
|
13
|
-
workingDirectory: "lib-skynet", // where package.json was located
|
|
14
|
-
bin: "examples/kafka-producer",
|
|
15
|
-
selector: { protocol: { type: "string" } },
|
|
16
|
-
env: {
|
|
17
|
-
SKYNET_KAFKA_SERVER: process.env.SKYNET_KAFKA_SERVER,
|
|
18
|
-
SKYNET_KAFKA_USERNAME: process.env.SKYNET_KAFKA_USERNAME,
|
|
19
|
-
SKYNET_KAFKA_PASSWORD: process.env.SKYNET_KAFKA_PASSWORD
|
|
20
|
-
},
|
|
21
|
-
cpu: 600,
|
|
22
|
-
mem: 200
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
deploy();
|
package/examples/legacy-indexer
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
// this file is executable and is for kafka producer testing
|
|
4
|
-
// a few test commands to try on
|
|
5
|
-
// $ examples/indexer --protocol bsc --verbose
|
|
6
|
-
// $ examples/indexer --protocol eth
|
|
7
|
-
// $ examples/indexer --help
|
|
8
|
-
|
|
9
|
-
const { createIndexerApp } = require("../indexer");
|
|
10
|
-
|
|
11
|
-
async function build({ protocol, verbose }) {
|
|
12
|
-
console.log("build called with", protocol, verbose);
|
|
13
|
-
|
|
14
|
-
if (protocol === "eth") {
|
|
15
|
-
throw new Error("when problem is not recoverable and cannot be resolved by retry, throw error here");
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const { run } = createIndexerApp({
|
|
20
|
-
name: "lib-skynet-test-indexer",
|
|
21
|
-
selector: { protocol: { type: "string" } },
|
|
22
|
-
build
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
run();
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
// this file is executable and is for kafka consumer testing
|
|
4
|
-
// a few test commands to try on
|
|
5
|
-
// $ examples/kafka-consumer --protocol bsc --verbose
|
|
6
|
-
// $ examples/kafka-consumer --help
|
|
7
|
-
|
|
8
|
-
const { createConsumerApp } = require("../kafka");
|
|
9
|
-
|
|
10
|
-
async function consume({ protocol, messages, verbose }) {
|
|
11
|
-
console.log("consume called with", protocol, messages, verbose);
|
|
12
|
-
|
|
13
|
-
messages.forEach(message => {
|
|
14
|
-
// if (message.id === 4) {
|
|
15
|
-
// throw new Error(
|
|
16
|
-
// "when critical error that cannot be fixed happened, we can throw and stop processing here"
|
|
17
|
-
// );
|
|
18
|
-
// }
|
|
19
|
-
});
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const { run } = createConsumerApp({
|
|
23
|
-
name: "ShareJSTestConsumer",
|
|
24
|
-
selector: { protocol: { type: "string" } },
|
|
25
|
-
|
|
26
|
-
consumer: {
|
|
27
|
-
topic: ({ protocol }) => `lib-skynet-test-kafka-${protocol}-dev`,
|
|
28
|
-
consume,
|
|
29
|
-
maxRetry: 1 // default to 2
|
|
30
|
-
}
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
run();
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
// this file is executable and is for kafka producer testing
|
|
4
|
-
// a few test commands to try on
|
|
5
|
-
// $ examples/kafka-producer --protocol bsc --verbose
|
|
6
|
-
// $ examples/kafka-producer --protocol bsc --from 4 --verbose
|
|
7
|
-
// $ examples/kafka-producer --protocol bsc --from 4 --to 5 --verbose
|
|
8
|
-
// $ examples/kafka-producer --protocol bsc --status
|
|
9
|
-
// $ examples/kafka-producer --help
|
|
10
|
-
|
|
11
|
-
const { createProducerApp } = require("../kafka");
|
|
12
|
-
|
|
13
|
-
async function produce({ protocol, from, to, verbose, send }) {
|
|
14
|
-
console.log("produce called with", protocol, from, to, verbose);
|
|
15
|
-
|
|
16
|
-
if (from <= 50) {
|
|
17
|
-
// send records to topic
|
|
18
|
-
const items = [];
|
|
19
|
-
for (let i = from; i <= to; i++) {
|
|
20
|
-
items.push({ id: i, name: `User ${i}` });
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
await send(items);
|
|
24
|
-
|
|
25
|
-
return []; // no errors
|
|
26
|
-
} else if (from > 80) {
|
|
27
|
-
throw new Error("critical error, cannot continue, better to throw");
|
|
28
|
-
} else {
|
|
29
|
-
const failed = [];
|
|
30
|
-
|
|
31
|
-
for (let i = from; i <= to; i++) {
|
|
32
|
-
failed.push(i);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// return failed item ids, which will be send to dead letter topic
|
|
36
|
-
return failed;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const { run } = createProducerApp({
|
|
41
|
-
name: "ShareJSTestProducer",
|
|
42
|
-
selector: { protocol: { type: "string" } },
|
|
43
|
-
|
|
44
|
-
producer: {
|
|
45
|
-
topic: ({ protocol }) => `lib-skynet-test-kafka-${protocol}-dev`,
|
|
46
|
-
deadLetterTopic: ({ protocol }) => `lib-skynet-test-kafka-${protocol}-dead-letter-dev`, // problematic ids will be sent to dead letter topic for later retry
|
|
47
|
-
produce,
|
|
48
|
-
batchSize: 10, // default to 50
|
|
49
|
-
maxRetry: 1 // default to 2
|
|
50
|
-
},
|
|
51
|
-
|
|
52
|
-
state: {
|
|
53
|
-
type: "block", // can be omitted, default is block
|
|
54
|
-
updateInterval: ({ protocol }) => (protocol === "bsc" ? 3000 : 20000), // how often max id is increasing, will determine poll interval, default to 5000ms
|
|
55
|
-
getMinId: async () => 4, // default returns 1
|
|
56
|
-
getMaxId: async ({ protocol }) => {
|
|
57
|
-
console.log("getMaxId called", protocol);
|
|
58
|
-
|
|
59
|
-
return 100;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
run();
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
// this file is executable and is for kafka producer testing
|
|
4
|
-
// a few test commands to try on
|
|
5
|
-
// $ examples/indexer --protocol bsc --verbose
|
|
6
|
-
// $ examples/indexer --help
|
|
7
|
-
|
|
8
|
-
const { createModeIndexerApp } = require("../indexer");
|
|
9
|
-
const { readFile, writeFile } = require("fs/promises");
|
|
10
|
-
|
|
11
|
-
async function build({ protocol, from, to, verbose }) {
|
|
12
|
-
console.log("build called with", protocol, from, to, verbose);
|
|
13
|
-
|
|
14
|
-
for (let i = from; i <= to; i++) {
|
|
15
|
-
if (i !== 5) {
|
|
16
|
-
// simulate a gap, id = 5
|
|
17
|
-
// we can use validate mode to fix
|
|
18
|
-
const filename = `/tmp/ShareJSTestModeIndexer-${protocol}-${i}`;
|
|
19
|
-
|
|
20
|
-
await writeFile(filename, "helloworld");
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
console.log("build done", protocol, from, to);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
async function validate({ protocol, from, to, verbose }) {
|
|
28
|
-
console.log("validate called with", protocol, from, to, verbose);
|
|
29
|
-
|
|
30
|
-
for (let i = from; i <= to; i++) {
|
|
31
|
-
const filename = `/tmp/ShareJSTestModeIndexer-${protocol}-${i}`;
|
|
32
|
-
|
|
33
|
-
try {
|
|
34
|
-
await readFile(filename);
|
|
35
|
-
} catch (notFound) {
|
|
36
|
-
// fix missing data
|
|
37
|
-
await writeFile(filename, "helloworld");
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
console.log("validate done", protocol, from, to);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const { run } = createModeIndexerApp({
|
|
45
|
-
name: "ShareJSTestModeIndexer",
|
|
46
|
-
selector: { protocol: { type: "string" } },
|
|
47
|
-
|
|
48
|
-
build,
|
|
49
|
-
buildBatchSize: 5,
|
|
50
|
-
buildConcurrency: 2, // default is 1, no concurrency
|
|
51
|
-
validate,
|
|
52
|
-
validateBatchSize: 2,
|
|
53
|
-
validateConcurrency: 2, // default is 1, no concurrency
|
|
54
|
-
|
|
55
|
-
state: {
|
|
56
|
-
type: "block", // can be omitted, default is block
|
|
57
|
-
getMinId: async () => 2, // default returns 1
|
|
58
|
-
getMaxId: async ({ protocol }) => {
|
|
59
|
-
return 10;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
run();
|