@bitmacro/relay-agent 0.1.5 → 0.2.0-beta.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/README.md +72 -52
- package/dist/bin/relay-agent.mjs +455 -209
- package/dist/bin/relay-agent.mjs.map +1 -1
- package/dist/index.mjs +443 -200
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/bin/relay-agent.mjs
CHANGED
|
@@ -1488,8 +1488,8 @@ var Node = class _Node {
|
|
|
1488
1488
|
this.#index = index;
|
|
1489
1489
|
return;
|
|
1490
1490
|
}
|
|
1491
|
-
const [
|
|
1492
|
-
const pattern =
|
|
1491
|
+
const [token, ...restTokens] = tokens;
|
|
1492
|
+
const pattern = token === "*" ? restTokens.length === 0 ? ["", "", ONLY_WILDCARD_REG_EXP_STR] : ["", "", LABEL_REG_EXP_STR] : token === "/*" ? ["", "", TAIL_WILDCARD_REG_EXP_STR] : token.match(/^\:([^\{\}]+)(?:\{(.+)\})?$/);
|
|
1493
1493
|
let node;
|
|
1494
1494
|
if (pattern) {
|
|
1495
1495
|
const name = pattern[1];
|
|
@@ -1522,7 +1522,7 @@ var Node = class _Node {
|
|
|
1522
1522
|
paramMap.push([name, node.#varIndex]);
|
|
1523
1523
|
}
|
|
1524
1524
|
} else {
|
|
1525
|
-
node = this.#children[
|
|
1525
|
+
node = this.#children[token];
|
|
1526
1526
|
if (!node) {
|
|
1527
1527
|
if (Object.keys(this.#children).some(
|
|
1528
1528
|
(k) => k.length > 1 && k !== ONLY_WILDCARD_REG_EXP_STR && k !== TAIL_WILDCARD_REG_EXP_STR
|
|
@@ -1532,7 +1532,7 @@ var Node = class _Node {
|
|
|
1532
1532
|
if (pathErrorCheckOnly) {
|
|
1533
1533
|
return;
|
|
1534
1534
|
}
|
|
1535
|
-
node = this.#children[
|
|
1535
|
+
node = this.#children[token] = new _Node();
|
|
1536
1536
|
}
|
|
1537
1537
|
}
|
|
1538
1538
|
node.insert(restTokens, index, paramMap, context, pathErrorCheckOnly);
|
|
@@ -2714,20 +2714,81 @@ var serve = (options, listeningListener) => {
|
|
|
2714
2714
|
return server;
|
|
2715
2715
|
};
|
|
2716
2716
|
|
|
2717
|
+
// src/config/relay-instances.ts
|
|
2718
|
+
var cachedInstances = null;
|
|
2719
|
+
function parseRelayInstances() {
|
|
2720
|
+
const raw2 = process.env.RELAY_INSTANCES;
|
|
2721
|
+
if (!raw2 || typeof raw2 !== "string") return null;
|
|
2722
|
+
try {
|
|
2723
|
+
const arr = JSON.parse(raw2);
|
|
2724
|
+
if (!Array.isArray(arr)) return null;
|
|
2725
|
+
const instances = [];
|
|
2726
|
+
const defaultWhitelist = process.env.WHITELIST_PATH ?? "/etc/strfry/whitelist.txt";
|
|
2727
|
+
for (const item of arr) {
|
|
2728
|
+
if (!item || typeof item !== "object") continue;
|
|
2729
|
+
const obj = item;
|
|
2730
|
+
const id = typeof obj.id === "string" ? obj.id.trim() : "";
|
|
2731
|
+
const token = typeof obj.token === "string" ? obj.token.trim() : "";
|
|
2732
|
+
const strfryConfig = typeof obj.strfryConfig === "string" ? obj.strfryConfig.trim() : "";
|
|
2733
|
+
const strfryDb = typeof obj.strfryDb === "string" ? obj.strfryDb.trim() : "";
|
|
2734
|
+
if (!id || !token || !strfryConfig || !strfryDb) continue;
|
|
2735
|
+
if (instances.some((i) => i.id === id)) continue;
|
|
2736
|
+
instances.push({
|
|
2737
|
+
id,
|
|
2738
|
+
token,
|
|
2739
|
+
strfryConfig,
|
|
2740
|
+
strfryDb,
|
|
2741
|
+
whitelistPath: typeof obj.whitelistPath === "string" ? obj.whitelistPath.trim() || void 0 : void 0
|
|
2742
|
+
});
|
|
2743
|
+
}
|
|
2744
|
+
return instances.length > 0 ? instances : null;
|
|
2745
|
+
} catch {
|
|
2746
|
+
return null;
|
|
2747
|
+
}
|
|
2748
|
+
}
|
|
2749
|
+
function getRelayInstances() {
|
|
2750
|
+
if (cachedInstances === null) {
|
|
2751
|
+
cachedInstances = parseRelayInstances();
|
|
2752
|
+
}
|
|
2753
|
+
return cachedInstances;
|
|
2754
|
+
}
|
|
2755
|
+
function getRelayInstance(relayId) {
|
|
2756
|
+
const instances = getRelayInstances();
|
|
2757
|
+
if (!instances) return null;
|
|
2758
|
+
return instances.find((i) => i.id === relayId) ?? null;
|
|
2759
|
+
}
|
|
2760
|
+
function isMultiRelayMode() {
|
|
2761
|
+
return getRelayInstances() !== null;
|
|
2762
|
+
}
|
|
2763
|
+
|
|
2717
2764
|
// src/middleware/auth.ts
|
|
2718
2765
|
var UNAUTHORIZED_JSON = { error: "unauthorized" };
|
|
2719
|
-
|
|
2766
|
+
function getBearerToken(c) {
|
|
2720
2767
|
const authHeader = c.req.header("Authorization");
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2768
|
+
if (!authHeader?.startsWith("Bearer ")) return null;
|
|
2769
|
+
return authHeader.slice(7);
|
|
2770
|
+
}
|
|
2771
|
+
async function authMiddleware(c, next) {
|
|
2772
|
+
const path = c.req.path;
|
|
2773
|
+
if (path === "/health") return next();
|
|
2774
|
+
if (isMultiRelayMode()) {
|
|
2775
|
+
const segments = path.split("/").filter(Boolean);
|
|
2776
|
+
if (segments.length >= 2 && segments[1] === "health") return next();
|
|
2777
|
+
}
|
|
2778
|
+
const token = getBearerToken(c);
|
|
2779
|
+
if (!token) return c.json(UNAUTHORIZED_JSON, 401);
|
|
2780
|
+
if (isMultiRelayMode()) {
|
|
2781
|
+
const segments = path.split("/").filter(Boolean);
|
|
2782
|
+
const relayId = segments[0];
|
|
2783
|
+
if (!relayId) return c.json(UNAUTHORIZED_JSON, 401);
|
|
2784
|
+
const instance = getRelayInstance(relayId);
|
|
2785
|
+
if (!instance) return c.json(UNAUTHORIZED_JSON, 401);
|
|
2786
|
+
if (token !== instance.token) return c.json(UNAUTHORIZED_JSON, 401);
|
|
2787
|
+
c.set("relayInstance", instance);
|
|
2788
|
+
} else {
|
|
2789
|
+
const expectedToken = process.env.RELAY_AGENT_TOKEN;
|
|
2790
|
+
if (!expectedToken) return c.json(UNAUTHORIZED_JSON, 401);
|
|
2791
|
+
if (token !== expectedToken) return c.json(UNAUTHORIZED_JSON, 401);
|
|
2731
2792
|
}
|
|
2732
2793
|
await next();
|
|
2733
2794
|
}
|
|
@@ -2735,8 +2796,25 @@ async function authMiddleware(c, next) {
|
|
|
2735
2796
|
// src/routes/health.ts
|
|
2736
2797
|
var healthRoutes = new Hono2();
|
|
2737
2798
|
healthRoutes.get("/health", (c) => {
|
|
2799
|
+
if (isMultiRelayMode()) {
|
|
2800
|
+
const instances = getRelayInstances() ?? [];
|
|
2801
|
+
return c.json({
|
|
2802
|
+
status: "ok",
|
|
2803
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2804
|
+
relayIds: instances.map((i) => i.id)
|
|
2805
|
+
});
|
|
2806
|
+
}
|
|
2738
2807
|
return c.json({ status: "ok", timestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
2739
2808
|
});
|
|
2809
|
+
var healthMultiRoutes = new Hono2();
|
|
2810
|
+
healthMultiRoutes.get("/:relayId/health", (c) => {
|
|
2811
|
+
const relayId = c.req.param("relayId");
|
|
2812
|
+
const instances = getRelayInstances();
|
|
2813
|
+
if (!instances?.some((i) => i.id === relayId)) {
|
|
2814
|
+
return c.json({ error: "relay not found", relayId }, 404);
|
|
2815
|
+
}
|
|
2816
|
+
return c.json({ status: "ok", relayId, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
2817
|
+
});
|
|
2740
2818
|
|
|
2741
2819
|
// src/adapters/strfry.ts
|
|
2742
2820
|
import { spawn } from "child_process";
|
|
@@ -2822,8 +2900,22 @@ ${stderr}`), {
|
|
|
2822
2900
|
});
|
|
2823
2901
|
}
|
|
2824
2902
|
var STRFRY_BIN = process.env.STRFRY_BIN ?? "strfry";
|
|
2825
|
-
var
|
|
2826
|
-
var
|
|
2903
|
+
var DEFAULT_STRFRY_CONFIG = process.env.STRFRY_CONFIG;
|
|
2904
|
+
var DEFAULT_WHITELIST_PATH = process.env.WHITELIST_PATH ?? "/etc/strfry/whitelist.txt";
|
|
2905
|
+
function resolveConfig(cfg) {
|
|
2906
|
+
if (cfg) {
|
|
2907
|
+
return {
|
|
2908
|
+
strfryConfig: cfg.strfryConfig || void 0,
|
|
2909
|
+
strfryDb: cfg.strfryDb,
|
|
2910
|
+
whitelistPath: cfg.whitelistPath ?? DEFAULT_WHITELIST_PATH
|
|
2911
|
+
};
|
|
2912
|
+
}
|
|
2913
|
+
return {
|
|
2914
|
+
strfryConfig: DEFAULT_STRFRY_CONFIG,
|
|
2915
|
+
strfryDb: process.env.STRFRY_DB_PATH ?? "./strfry-db",
|
|
2916
|
+
whitelistPath: DEFAULT_WHITELIST_PATH
|
|
2917
|
+
};
|
|
2918
|
+
}
|
|
2827
2919
|
function logStrfryError(operation, err) {
|
|
2828
2920
|
const msg = err instanceof Error ? err.message : String(err);
|
|
2829
2921
|
const extra = err && typeof err === "object" ? err : {};
|
|
@@ -2836,15 +2928,11 @@ function logStrfryError(operation, err) {
|
|
|
2836
2928
|
if (stderr) parts.push(`stderr: ${stderr}`);
|
|
2837
2929
|
console.error(parts.join("\n"));
|
|
2838
2930
|
}
|
|
2839
|
-
function
|
|
2840
|
-
|
|
2841
|
-
}
|
|
2842
|
-
function strfryArgs(subcommand, ...args) {
|
|
2843
|
-
const base = STRFRY_CONFIG ? ["--config", STRFRY_CONFIG, subcommand] : [subcommand];
|
|
2931
|
+
function strfryArgs(cfg, subcommand, ...args) {
|
|
2932
|
+
const base = cfg.strfryConfig ? ["--config", cfg.strfryConfig, subcommand] : [subcommand];
|
|
2844
2933
|
return [...base, ...args];
|
|
2845
2934
|
}
|
|
2846
|
-
function getStrfryCwd() {
|
|
2847
|
-
const dbPath = getStrfryDbPath();
|
|
2935
|
+
function getStrfryCwd(dbPath) {
|
|
2848
2936
|
if (!dbPath) return void 0;
|
|
2849
2937
|
const parent = dirname(dbPath);
|
|
2850
2938
|
return parent !== "." ? parent : void 0;
|
|
@@ -2859,57 +2947,75 @@ function buildFilterJson(filter) {
|
|
|
2859
2947
|
if (filter.limit != null) obj.limit = filter.limit;
|
|
2860
2948
|
return JSON.stringify(obj);
|
|
2861
2949
|
}
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
}
|
|
2950
|
+
var strfryLocks = /* @__PURE__ */ new Map();
|
|
2951
|
+
async function withStrfryMutex(strfryDb, fn) {
|
|
2952
|
+
const prev = strfryLocks.get(strfryDb) ?? Promise.resolve();
|
|
2953
|
+
const work = prev.then(() => fn());
|
|
2954
|
+
strfryLocks.set(strfryDb, work.finally(() => {
|
|
2955
|
+
}));
|
|
2956
|
+
return work;
|
|
2957
|
+
}
|
|
2958
|
+
async function scanEvents(filter, cfg = null) {
|
|
2959
|
+
const resolved = resolveConfig(cfg);
|
|
2960
|
+
return withStrfryMutex(resolved.strfryDb, async () => {
|
|
2961
|
+
try {
|
|
2962
|
+
const filterJson = buildFilterJson(filter);
|
|
2963
|
+
const cwd = getStrfryCwd(resolved.strfryDb);
|
|
2964
|
+
const { stdout } = await spawnAsync(STRFRY_BIN, strfryArgs(resolved, "scan", filterJson), {
|
|
2965
|
+
maxBuffer: 50 * 1024 * 1024,
|
|
2966
|
+
cwd: cwd || void 0
|
|
2967
|
+
});
|
|
2968
|
+
const events = [];
|
|
2969
|
+
for (const line of stdout.trim().split("\n")) {
|
|
2970
|
+
if (!line) continue;
|
|
2971
|
+
try {
|
|
2972
|
+
const event = JSON.parse(line);
|
|
2973
|
+
events.push(event);
|
|
2974
|
+
} catch {
|
|
2975
|
+
}
|
|
2877
2976
|
}
|
|
2977
|
+
return events;
|
|
2978
|
+
} catch (err) {
|
|
2979
|
+
logStrfryError("scanEvents", err);
|
|
2980
|
+
throw err;
|
|
2878
2981
|
}
|
|
2879
|
-
|
|
2880
|
-
} catch (err) {
|
|
2881
|
-
logStrfryError("scanEvents", err);
|
|
2882
|
-
throw err;
|
|
2883
|
-
}
|
|
2982
|
+
});
|
|
2884
2983
|
}
|
|
2885
|
-
async function deleteEvent(id) {
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2984
|
+
async function deleteEvent(id, cfg = null) {
|
|
2985
|
+
const resolved = resolveConfig(cfg);
|
|
2986
|
+
return withStrfryMutex(resolved.strfryDb, async () => {
|
|
2987
|
+
try {
|
|
2988
|
+
const filterJson = JSON.stringify({ ids: [id] });
|
|
2989
|
+
const cwd = getStrfryCwd(resolved.strfryDb);
|
|
2990
|
+
await spawnAsync(STRFRY_BIN, strfryArgs(resolved, "delete", "--filter", filterJson), {
|
|
2991
|
+
cwd: cwd || void 0
|
|
2992
|
+
});
|
|
2993
|
+
} catch (err) {
|
|
2994
|
+
logStrfryError("deleteEvent", err);
|
|
2995
|
+
throw err;
|
|
2996
|
+
}
|
|
2997
|
+
});
|
|
2896
2998
|
}
|
|
2897
|
-
async function deleteByPubkey(pubkey) {
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2999
|
+
async function deleteByPubkey(pubkey, cfg = null) {
|
|
3000
|
+
const resolved = resolveConfig(cfg);
|
|
3001
|
+
return withStrfryMutex(resolved.strfryDb, async () => {
|
|
3002
|
+
try {
|
|
3003
|
+
const filterJson = JSON.stringify({ authors: [pubkey] });
|
|
3004
|
+
const cwd = getStrfryCwd(resolved.strfryDb);
|
|
3005
|
+
await spawnAsync(STRFRY_BIN, strfryArgs(resolved, "delete", "--filter", filterJson), {
|
|
3006
|
+
cwd: cwd || void 0
|
|
3007
|
+
});
|
|
3008
|
+
} catch (err) {
|
|
3009
|
+
logStrfryError("deleteByPubkey", err);
|
|
3010
|
+
throw err;
|
|
3011
|
+
}
|
|
3012
|
+
});
|
|
2908
3013
|
}
|
|
2909
3014
|
var SCAN_COUNT_TIMEOUT_MS = 6e4;
|
|
2910
|
-
function spawnScanCount(
|
|
3015
|
+
function spawnScanCount(resolved) {
|
|
3016
|
+
const cwd = getStrfryCwd(resolved.strfryDb);
|
|
2911
3017
|
return new Promise((resolve) => {
|
|
2912
|
-
const child = spawn(STRFRY_BIN, strfryArgs("scan", "{}"), {
|
|
3018
|
+
const child = spawn(STRFRY_BIN, strfryArgs(resolved, "scan", "{}"), {
|
|
2913
3019
|
stdio: ["ignore", "pipe", "pipe"],
|
|
2914
3020
|
cwd: cwd || void 0
|
|
2915
3021
|
});
|
|
@@ -2933,65 +3039,69 @@ function spawnScanCount(cwd) {
|
|
|
2933
3039
|
});
|
|
2934
3040
|
});
|
|
2935
3041
|
}
|
|
2936
|
-
async function getStats() {
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
try {
|
|
2951
|
-
const { stdout } = await spawnAsync("du", ["-sh", getStrfryDbPath()]);
|
|
2952
|
-
db_size = stdout.trim().split(/\s+/)[0] ?? "0";
|
|
2953
|
-
} catch {
|
|
2954
|
-
db_size = "unknown";
|
|
2955
|
-
}
|
|
2956
|
-
let uptime_seconds = 0;
|
|
2957
|
-
try {
|
|
2958
|
-
const { stdout: pidOut } = await spawnAsync("pgrep", ["-x", "strfry"]);
|
|
2959
|
-
const pid = pidOut.trim().split("\n")[0];
|
|
2960
|
-
if (pid) {
|
|
2961
|
-
const { stdout } = await spawnAsync("ps", ["-o", "etimes=", "-p", pid]);
|
|
2962
|
-
uptime_seconds = parseInt(stdout.trim(), 10) || 0;
|
|
3042
|
+
async function getStats(cfg = null) {
|
|
3043
|
+
const resolved = resolveConfig(cfg);
|
|
3044
|
+
return withStrfryMutex(resolved.strfryDb, async () => {
|
|
3045
|
+
let total_events = 0;
|
|
3046
|
+
let strfry_version = "unknown";
|
|
3047
|
+
const cwd = getStrfryCwd(resolved.strfryDb);
|
|
3048
|
+
total_events = await spawnScanCount(resolved);
|
|
3049
|
+
try {
|
|
3050
|
+
const { stdout } = await spawnAsync(STRFRY_BIN, ["--version"], {
|
|
3051
|
+
cwd: cwd || void 0
|
|
3052
|
+
});
|
|
3053
|
+
const match2 = stdout.match(/strfry\s+([\d.]+)/i);
|
|
3054
|
+
strfry_version = match2?.[1] ?? "unknown";
|
|
3055
|
+
} catch {
|
|
2963
3056
|
}
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
3057
|
+
let db_size = "0";
|
|
3058
|
+
try {
|
|
3059
|
+
const { stdout } = await spawnAsync("du", ["-sh", resolved.strfryDb]);
|
|
3060
|
+
db_size = stdout.trim().split(/\s+/)[0] ?? "0";
|
|
3061
|
+
} catch {
|
|
3062
|
+
db_size = "unknown";
|
|
3063
|
+
}
|
|
3064
|
+
let uptime_seconds = 0;
|
|
3065
|
+
try {
|
|
3066
|
+
const { stdout: pidOut } = await spawnAsync("pgrep", ["-x", "strfry"]);
|
|
3067
|
+
const pid = pidOut.trim().split("\n")[0];
|
|
3068
|
+
if (pid) {
|
|
3069
|
+
const { stdout } = await spawnAsync("ps", ["-o", "etimes=", "-p", pid]);
|
|
3070
|
+
uptime_seconds = parseInt(stdout.trim(), 10) || 0;
|
|
3071
|
+
}
|
|
3072
|
+
} catch {
|
|
3073
|
+
}
|
|
3074
|
+
return {
|
|
3075
|
+
total_events,
|
|
3076
|
+
db_size,
|
|
3077
|
+
uptime_seconds,
|
|
3078
|
+
strfry_version
|
|
3079
|
+
};
|
|
3080
|
+
});
|
|
2972
3081
|
}
|
|
2973
|
-
async function listUsers(limit = 1e3) {
|
|
3082
|
+
async function listUsers(limit = 1e3, cfg = null) {
|
|
2974
3083
|
const filter = { kinds: [0, 1, 3], limit };
|
|
2975
|
-
const events = await scanEvents(filter);
|
|
3084
|
+
const events = await scanEvents(filter, cfg);
|
|
2976
3085
|
const pubkeys = /* @__PURE__ */ new Set();
|
|
2977
3086
|
for (const e of events) {
|
|
2978
3087
|
pubkeys.add(e.pubkey);
|
|
2979
3088
|
}
|
|
2980
3089
|
return Array.from(pubkeys);
|
|
2981
3090
|
}
|
|
2982
|
-
async function readWhitelist() {
|
|
2983
|
-
if (!existsSync(
|
|
3091
|
+
async function readWhitelist(whitelistPath) {
|
|
3092
|
+
if (!existsSync(whitelistPath)) {
|
|
2984
3093
|
return [];
|
|
2985
3094
|
}
|
|
2986
|
-
const content = await readFile(
|
|
3095
|
+
const content = await readFile(whitelistPath, "utf-8");
|
|
2987
3096
|
return content.split("\n").map((l) => l.trim()).filter(Boolean);
|
|
2988
3097
|
}
|
|
2989
3098
|
var PUBKEY_HEX_REGEX = /^[0-9a-f]{64}$/;
|
|
2990
3099
|
function isValidPubkey(s) {
|
|
2991
3100
|
return PUBKEY_HEX_REGEX.test(s.toLowerCase());
|
|
2992
3101
|
}
|
|
2993
|
-
async function getPolicyEntries() {
|
|
2994
|
-
const
|
|
3102
|
+
async function getPolicyEntries(cfg = null) {
|
|
3103
|
+
const resolved = resolveConfig(cfg);
|
|
3104
|
+
const lines = await readWhitelist(resolved.whitelistPath);
|
|
2995
3105
|
const entries = [];
|
|
2996
3106
|
for (const line of lines) {
|
|
2997
3107
|
if (line.startsWith("#") || !line) continue;
|
|
@@ -3005,15 +3115,16 @@ async function getPolicyEntries() {
|
|
|
3005
3115
|
}
|
|
3006
3116
|
return entries;
|
|
3007
3117
|
}
|
|
3008
|
-
async function writeWhitelist(lines) {
|
|
3009
|
-
const dir = dirname(
|
|
3118
|
+
async function writeWhitelist(whitelistPath, lines) {
|
|
3119
|
+
const dir = dirname(whitelistPath);
|
|
3010
3120
|
if (!existsSync(dir)) {
|
|
3011
3121
|
await mkdir(dir, { recursive: true });
|
|
3012
3122
|
}
|
|
3013
|
-
await writeFile(
|
|
3123
|
+
await writeFile(whitelistPath, lines.join("\n") + "\n", "utf-8");
|
|
3014
3124
|
}
|
|
3015
|
-
async function blockPubkey(pubkey) {
|
|
3016
|
-
const
|
|
3125
|
+
async function blockPubkey(pubkey, cfg = null) {
|
|
3126
|
+
const resolved = resolveConfig(cfg);
|
|
3127
|
+
const lines = await readWhitelist(resolved.whitelistPath);
|
|
3017
3128
|
const blockLine = `!${pubkey}`;
|
|
3018
3129
|
const withoutPubkey = lines.filter(
|
|
3019
3130
|
(l) => l !== pubkey && l !== blockLine
|
|
@@ -3021,80 +3132,143 @@ async function blockPubkey(pubkey) {
|
|
|
3021
3132
|
if (!withoutPubkey.includes(blockLine)) {
|
|
3022
3133
|
withoutPubkey.push(blockLine);
|
|
3023
3134
|
}
|
|
3024
|
-
await writeWhitelist(withoutPubkey);
|
|
3025
|
-
await deleteByPubkey(pubkey);
|
|
3135
|
+
await writeWhitelist(resolved.whitelistPath, withoutPubkey);
|
|
3136
|
+
await deleteByPubkey(pubkey, cfg);
|
|
3026
3137
|
}
|
|
3027
|
-
async function allowPubkey(pubkey) {
|
|
3028
|
-
const
|
|
3138
|
+
async function allowPubkey(pubkey, cfg = null) {
|
|
3139
|
+
const resolved = resolveConfig(cfg);
|
|
3140
|
+
const lines = await readWhitelist(resolved.whitelistPath);
|
|
3029
3141
|
const blockLine = `!${pubkey}`;
|
|
3030
3142
|
const filtered = lines.filter((l) => l !== blockLine);
|
|
3031
3143
|
if (!filtered.includes(pubkey)) {
|
|
3032
3144
|
filtered.push(pubkey);
|
|
3033
3145
|
}
|
|
3034
|
-
await writeWhitelist(filtered);
|
|
3146
|
+
await writeWhitelist(resolved.whitelistPath, filtered);
|
|
3035
3147
|
}
|
|
3036
3148
|
|
|
3149
|
+
// src/routes/stats.ts
|
|
3150
|
+
var statsLegacyRoutes = new Hono2();
|
|
3151
|
+
statsLegacyRoutes.get("/stats", async (c) => {
|
|
3152
|
+
try {
|
|
3153
|
+
const raw2 = await getStats(null);
|
|
3154
|
+
const stats = {
|
|
3155
|
+
total_events: raw2.total_events,
|
|
3156
|
+
db_size: raw2.db_size,
|
|
3157
|
+
uptime: raw2.uptime_seconds,
|
|
3158
|
+
version: raw2.strfry_version
|
|
3159
|
+
};
|
|
3160
|
+
return c.json(stats);
|
|
3161
|
+
} catch {
|
|
3162
|
+
return c.json({ error: "relay unavailable" }, 503);
|
|
3163
|
+
}
|
|
3164
|
+
});
|
|
3165
|
+
var statsMultiRoutes = new Hono2();
|
|
3166
|
+
statsMultiRoutes.get("/:relayId/stats", async (c) => {
|
|
3167
|
+
const relayId = c.req.param("relayId");
|
|
3168
|
+
const instance = getRelayInstance(relayId);
|
|
3169
|
+
if (!instance) return c.json({ error: "relay not found", relayId }, 404);
|
|
3170
|
+
try {
|
|
3171
|
+
const cfg = {
|
|
3172
|
+
strfryConfig: instance.strfryConfig,
|
|
3173
|
+
strfryDb: instance.strfryDb,
|
|
3174
|
+
whitelistPath: instance.whitelistPath
|
|
3175
|
+
};
|
|
3176
|
+
const raw2 = await getStats(cfg);
|
|
3177
|
+
const stats = {
|
|
3178
|
+
total_events: raw2.total_events,
|
|
3179
|
+
db_size: raw2.db_size,
|
|
3180
|
+
uptime: raw2.uptime_seconds,
|
|
3181
|
+
version: raw2.strfry_version
|
|
3182
|
+
};
|
|
3183
|
+
return c.json(stats);
|
|
3184
|
+
} catch {
|
|
3185
|
+
return c.json({ error: "relay unavailable" }, 503);
|
|
3186
|
+
}
|
|
3187
|
+
});
|
|
3188
|
+
|
|
3037
3189
|
// src/routes/events.ts
|
|
3038
|
-
|
|
3039
|
-
|
|
3190
|
+
function parseFilter(c) {
|
|
3191
|
+
const kinds = c.req.query("kinds");
|
|
3192
|
+
const authors = c.req.query("authors");
|
|
3193
|
+
const since = c.req.query("since");
|
|
3194
|
+
const until = c.req.query("until");
|
|
3195
|
+
const limit = c.req.query("limit");
|
|
3196
|
+
const filter = {};
|
|
3197
|
+
if (kinds) {
|
|
3198
|
+
const parsed = kinds.split(",").map((k) => parseInt(k, 10));
|
|
3199
|
+
if (parsed.some((n) => Number.isNaN(n))) return null;
|
|
3200
|
+
filter.kinds = parsed;
|
|
3201
|
+
}
|
|
3202
|
+
if (authors) filter.authors = authors.split(",").map((a) => a.trim());
|
|
3203
|
+
if (since) {
|
|
3204
|
+
const n = parseInt(since, 10);
|
|
3205
|
+
if (Number.isNaN(n)) return null;
|
|
3206
|
+
filter.since = n;
|
|
3207
|
+
}
|
|
3208
|
+
if (until) {
|
|
3209
|
+
const n = parseInt(until, 10);
|
|
3210
|
+
if (Number.isNaN(n)) return null;
|
|
3211
|
+
filter.until = n;
|
|
3212
|
+
}
|
|
3213
|
+
if (limit) {
|
|
3214
|
+
const n = parseInt(limit, 10);
|
|
3215
|
+
if (Number.isNaN(n)) return null;
|
|
3216
|
+
filter.limit = n;
|
|
3217
|
+
}
|
|
3218
|
+
return filter;
|
|
3219
|
+
}
|
|
3220
|
+
var eventsLegacyRoutes = new Hono2();
|
|
3221
|
+
eventsLegacyRoutes.get("/events", async (c) => {
|
|
3222
|
+
const filter = parseFilter(c);
|
|
3223
|
+
if (!filter) return c.json({ error: "invalid query params" }, 400);
|
|
3040
3224
|
try {
|
|
3041
|
-
const
|
|
3042
|
-
const authors = c.req.query("authors");
|
|
3043
|
-
const since = c.req.query("since");
|
|
3044
|
-
const until = c.req.query("until");
|
|
3045
|
-
const limit = c.req.query("limit");
|
|
3046
|
-
const filter = {};
|
|
3047
|
-
if (kinds) {
|
|
3048
|
-
const parsed = kinds.split(",").map((k) => parseInt(k, 10));
|
|
3049
|
-
if (parsed.some((n) => Number.isNaN(n))) {
|
|
3050
|
-
return c.json({ error: "invalid kinds" }, 400);
|
|
3051
|
-
}
|
|
3052
|
-
filter.kinds = parsed;
|
|
3053
|
-
}
|
|
3054
|
-
if (authors) filter.authors = authors.split(",").map((a) => a.trim());
|
|
3055
|
-
if (since) {
|
|
3056
|
-
const n = parseInt(since, 10);
|
|
3057
|
-
if (Number.isNaN(n)) return c.json({ error: "invalid since" }, 400);
|
|
3058
|
-
filter.since = n;
|
|
3059
|
-
}
|
|
3060
|
-
if (until) {
|
|
3061
|
-
const n = parseInt(until, 10);
|
|
3062
|
-
if (Number.isNaN(n)) return c.json({ error: "invalid until" }, 400);
|
|
3063
|
-
filter.until = n;
|
|
3064
|
-
}
|
|
3065
|
-
if (limit) {
|
|
3066
|
-
const n = parseInt(limit, 10);
|
|
3067
|
-
if (Number.isNaN(n)) return c.json({ error: "invalid limit" }, 400);
|
|
3068
|
-
filter.limit = n;
|
|
3069
|
-
}
|
|
3070
|
-
const events = await scanEvents(filter);
|
|
3225
|
+
const events = await scanEvents(filter, null);
|
|
3071
3226
|
return c.json(events);
|
|
3072
3227
|
} catch {
|
|
3073
3228
|
return c.json({ error: "relay unavailable" }, 503);
|
|
3074
3229
|
}
|
|
3075
3230
|
});
|
|
3076
|
-
|
|
3231
|
+
eventsLegacyRoutes.delete("/events/:id", async (c) => {
|
|
3077
3232
|
try {
|
|
3078
3233
|
const id = c.req.param("id");
|
|
3079
|
-
await deleteEvent(id);
|
|
3234
|
+
await deleteEvent(id, null);
|
|
3080
3235
|
return c.json({ deleted: id });
|
|
3081
3236
|
} catch {
|
|
3082
3237
|
return c.json({ error: "relay unavailable" }, 503);
|
|
3083
3238
|
}
|
|
3084
3239
|
});
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3240
|
+
var eventsMultiRoutes = new Hono2();
|
|
3241
|
+
eventsMultiRoutes.get("/:relayId/events", async (c) => {
|
|
3242
|
+
const relayId = c.req.param("relayId");
|
|
3243
|
+
const instance = getRelayInstance(relayId);
|
|
3244
|
+
if (!instance) return c.json({ error: "relay not found", relayId }, 404);
|
|
3245
|
+
const filter = parseFilter(c);
|
|
3246
|
+
if (!filter) return c.json({ error: "invalid query params" }, 400);
|
|
3089
3247
|
try {
|
|
3090
|
-
const
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
uptime: raw2.uptime_seconds,
|
|
3095
|
-
version: raw2.strfry_version
|
|
3248
|
+
const cfg = {
|
|
3249
|
+
strfryConfig: instance.strfryConfig,
|
|
3250
|
+
strfryDb: instance.strfryDb,
|
|
3251
|
+
whitelistPath: instance.whitelistPath
|
|
3096
3252
|
};
|
|
3097
|
-
|
|
3253
|
+
const events = await scanEvents(filter, cfg);
|
|
3254
|
+
return c.json(events);
|
|
3255
|
+
} catch {
|
|
3256
|
+
return c.json({ error: "relay unavailable" }, 503);
|
|
3257
|
+
}
|
|
3258
|
+
});
|
|
3259
|
+
eventsMultiRoutes.delete("/:relayId/events/:id", async (c) => {
|
|
3260
|
+
const relayId = c.req.param("relayId");
|
|
3261
|
+
const id = c.req.param("id");
|
|
3262
|
+
const instance = getRelayInstance(relayId);
|
|
3263
|
+
if (!instance) return c.json({ error: "relay not found", relayId }, 404);
|
|
3264
|
+
try {
|
|
3265
|
+
const cfg = {
|
|
3266
|
+
strfryConfig: instance.strfryConfig,
|
|
3267
|
+
strfryDb: instance.strfryDb,
|
|
3268
|
+
whitelistPath: instance.whitelistPath
|
|
3269
|
+
};
|
|
3270
|
+
await deleteEvent(id, cfg);
|
|
3271
|
+
return c.json({ deleted: id });
|
|
3098
3272
|
} catch {
|
|
3099
3273
|
return c.json({ error: "relay unavailable" }, 503);
|
|
3100
3274
|
}
|
|
@@ -3102,42 +3276,83 @@ statsRoutes.get("/stats", async (c) => {
|
|
|
3102
3276
|
|
|
3103
3277
|
// src/routes/policy.ts
|
|
3104
3278
|
var PUBKEY_REGEX = /^[0-9a-f]{64}$/;
|
|
3105
|
-
|
|
3106
|
-
|
|
3279
|
+
function cfgFromInstance(instance) {
|
|
3280
|
+
return {
|
|
3281
|
+
strfryConfig: instance.strfryConfig,
|
|
3282
|
+
strfryDb: instance.strfryDb,
|
|
3283
|
+
whitelistPath: instance.whitelistPath
|
|
3284
|
+
};
|
|
3285
|
+
}
|
|
3286
|
+
var policyLegacyRoutes = new Hono2();
|
|
3287
|
+
policyLegacyRoutes.get("/policy", async (c) => {
|
|
3107
3288
|
try {
|
|
3108
|
-
const entries = await getPolicyEntries();
|
|
3289
|
+
const entries = await getPolicyEntries(null);
|
|
3109
3290
|
return c.json({ entries });
|
|
3110
3291
|
} catch {
|
|
3111
3292
|
return c.json({ error: "relay unavailable" }, 503);
|
|
3112
3293
|
}
|
|
3113
3294
|
});
|
|
3114
|
-
|
|
3295
|
+
policyLegacyRoutes.post("/policy/block", async (c) => {
|
|
3115
3296
|
try {
|
|
3116
3297
|
const body = await c.req.json();
|
|
3117
3298
|
const { pubkey } = body;
|
|
3118
|
-
if (!pubkey || typeof pubkey !== "string") {
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
if (!PUBKEY_REGEX.test(pubkey.toLowerCase())) {
|
|
3122
|
-
return c.json({ error: "invalid pubkey format" }, 400);
|
|
3123
|
-
}
|
|
3124
|
-
await blockPubkey(pubkey);
|
|
3299
|
+
if (!pubkey || typeof pubkey !== "string") return c.json({ error: "pubkey is required" }, 400);
|
|
3300
|
+
if (!PUBKEY_REGEX.test(pubkey.toLowerCase())) return c.json({ error: "invalid pubkey format" }, 400);
|
|
3301
|
+
await blockPubkey(pubkey, null);
|
|
3125
3302
|
return c.json({ blocked: pubkey });
|
|
3126
3303
|
} catch {
|
|
3127
3304
|
return c.json({ error: "relay unavailable" }, 503);
|
|
3128
3305
|
}
|
|
3129
3306
|
});
|
|
3130
|
-
|
|
3307
|
+
policyLegacyRoutes.post("/policy/allow", async (c) => {
|
|
3131
3308
|
try {
|
|
3132
3309
|
const body = await c.req.json();
|
|
3133
3310
|
const { pubkey } = body;
|
|
3134
|
-
if (!pubkey || typeof pubkey !== "string") {
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
}
|
|
3140
|
-
|
|
3311
|
+
if (!pubkey || typeof pubkey !== "string") return c.json({ error: "pubkey is required" }, 400);
|
|
3312
|
+
if (!PUBKEY_REGEX.test(pubkey.toLowerCase())) return c.json({ error: "invalid pubkey format" }, 400);
|
|
3313
|
+
await allowPubkey(pubkey, null);
|
|
3314
|
+
return c.json({ allowed: pubkey });
|
|
3315
|
+
} catch {
|
|
3316
|
+
return c.json({ error: "relay unavailable" }, 503);
|
|
3317
|
+
}
|
|
3318
|
+
});
|
|
3319
|
+
var policyMultiRoutes = new Hono2();
|
|
3320
|
+
policyMultiRoutes.get("/:relayId/policy", async (c) => {
|
|
3321
|
+
const relayId = c.req.param("relayId");
|
|
3322
|
+
const instance = getRelayInstance(relayId);
|
|
3323
|
+
if (!instance) return c.json({ error: "relay not found", relayId }, 404);
|
|
3324
|
+
try {
|
|
3325
|
+
const entries = await getPolicyEntries(cfgFromInstance(instance));
|
|
3326
|
+
return c.json({ entries });
|
|
3327
|
+
} catch {
|
|
3328
|
+
return c.json({ error: "relay unavailable" }, 503);
|
|
3329
|
+
}
|
|
3330
|
+
});
|
|
3331
|
+
policyMultiRoutes.post("/:relayId/policy/block", async (c) => {
|
|
3332
|
+
const relayId = c.req.param("relayId");
|
|
3333
|
+
const instance = getRelayInstance(relayId);
|
|
3334
|
+
if (!instance) return c.json({ error: "relay not found", relayId }, 404);
|
|
3335
|
+
try {
|
|
3336
|
+
const body = await c.req.json();
|
|
3337
|
+
const { pubkey } = body;
|
|
3338
|
+
if (!pubkey || typeof pubkey !== "string") return c.json({ error: "pubkey is required" }, 400);
|
|
3339
|
+
if (!PUBKEY_REGEX.test(pubkey.toLowerCase())) return c.json({ error: "invalid pubkey format" }, 400);
|
|
3340
|
+
await blockPubkey(pubkey, cfgFromInstance(instance));
|
|
3341
|
+
return c.json({ blocked: pubkey });
|
|
3342
|
+
} catch {
|
|
3343
|
+
return c.json({ error: "relay unavailable" }, 503);
|
|
3344
|
+
}
|
|
3345
|
+
});
|
|
3346
|
+
policyMultiRoutes.post("/:relayId/policy/allow", async (c) => {
|
|
3347
|
+
const relayId = c.req.param("relayId");
|
|
3348
|
+
const instance = getRelayInstance(relayId);
|
|
3349
|
+
if (!instance) return c.json({ error: "relay not found", relayId }, 404);
|
|
3350
|
+
try {
|
|
3351
|
+
const body = await c.req.json();
|
|
3352
|
+
const { pubkey } = body;
|
|
3353
|
+
if (!pubkey || typeof pubkey !== "string") return c.json({ error: "pubkey is required" }, 400);
|
|
3354
|
+
if (!PUBKEY_REGEX.test(pubkey.toLowerCase())) return c.json({ error: "invalid pubkey format" }, 400);
|
|
3355
|
+
await allowPubkey(pubkey, cfgFromInstance(instance));
|
|
3141
3356
|
return c.json({ allowed: pubkey });
|
|
3142
3357
|
} catch {
|
|
3143
3358
|
return c.json({ error: "relay unavailable" }, 503);
|
|
@@ -3145,17 +3360,41 @@ policyRoutes.post("/policy/allow", async (c) => {
|
|
|
3145
3360
|
});
|
|
3146
3361
|
|
|
3147
3362
|
// src/routes/users.ts
|
|
3148
|
-
var
|
|
3149
|
-
|
|
3363
|
+
var usersLegacyRoutes = new Hono2();
|
|
3364
|
+
usersLegacyRoutes.get("/users", async (c) => {
|
|
3365
|
+
const limitParam = c.req.query("limit");
|
|
3366
|
+
let limit;
|
|
3367
|
+
if (limitParam) {
|
|
3368
|
+
const n = parseInt(limitParam, 10);
|
|
3369
|
+
if (Number.isNaN(n)) return c.json({ error: "invalid limit" }, 400);
|
|
3370
|
+
limit = n;
|
|
3371
|
+
}
|
|
3150
3372
|
try {
|
|
3151
|
-
const
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
|
|
3373
|
+
const pubkeys = await listUsers(limit ?? 1e3, null);
|
|
3374
|
+
return c.json({ users: pubkeys });
|
|
3375
|
+
} catch {
|
|
3376
|
+
return c.json({ error: "relay unavailable" }, 503);
|
|
3377
|
+
}
|
|
3378
|
+
});
|
|
3379
|
+
var usersMultiRoutes = new Hono2();
|
|
3380
|
+
usersMultiRoutes.get("/:relayId/users", async (c) => {
|
|
3381
|
+
const relayId = c.req.param("relayId");
|
|
3382
|
+
const instance = getRelayInstance(relayId);
|
|
3383
|
+
if (!instance) return c.json({ error: "relay not found", relayId }, 404);
|
|
3384
|
+
const limitParam = c.req.query("limit");
|
|
3385
|
+
let limit;
|
|
3386
|
+
if (limitParam) {
|
|
3387
|
+
const n = parseInt(limitParam, 10);
|
|
3388
|
+
if (Number.isNaN(n)) return c.json({ error: "invalid limit" }, 400);
|
|
3389
|
+
limit = n;
|
|
3390
|
+
}
|
|
3391
|
+
try {
|
|
3392
|
+
const cfg = {
|
|
3393
|
+
strfryConfig: instance.strfryConfig,
|
|
3394
|
+
strfryDb: instance.strfryDb,
|
|
3395
|
+
whitelistPath: instance.whitelistPath
|
|
3396
|
+
};
|
|
3397
|
+
const pubkeys = await listUsers(limit ?? 1e3, cfg);
|
|
3159
3398
|
return c.json({ users: pubkeys });
|
|
3160
3399
|
} catch {
|
|
3161
3400
|
return c.json({ error: "relay unavailable" }, 503);
|
|
@@ -3164,8 +3403,7 @@ usersRoutes.get("/users", async (c) => {
|
|
|
3164
3403
|
|
|
3165
3404
|
// src/index.ts
|
|
3166
3405
|
var DEFAULT_ORIGINS = [
|
|
3167
|
-
"https://relay-panel.bitmacro.
|
|
3168
|
-
"https://relay-panel.bitmacro.pro",
|
|
3406
|
+
"https://relay-panel.bitmacro.io",
|
|
3169
3407
|
"http://localhost:3000"
|
|
3170
3408
|
];
|
|
3171
3409
|
var EXTRA_ORIGINS = (process.env.ALLOWED_ORIGINS ?? "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
@@ -3177,15 +3415,20 @@ app.use("*", async (c, next) => {
|
|
|
3177
3415
|
console.log(`[relay-agent] ${c.req.method} ${c.req.path} ${c.res.status} ${Date.now() - start}ms`);
|
|
3178
3416
|
});
|
|
3179
3417
|
app.use("*", cors({ origin: ALLOWED_ORIGINS }));
|
|
3180
|
-
app.use("*",
|
|
3181
|
-
if (c.req.path === "/health") return next();
|
|
3182
|
-
return authMiddleware(c, next);
|
|
3183
|
-
});
|
|
3418
|
+
app.use("*", authMiddleware);
|
|
3184
3419
|
app.route("/", healthRoutes);
|
|
3185
|
-
|
|
3186
|
-
app.route("/",
|
|
3187
|
-
app.route("/",
|
|
3188
|
-
app.route("/",
|
|
3420
|
+
if (isMultiRelayMode()) {
|
|
3421
|
+
app.route("/", healthMultiRoutes);
|
|
3422
|
+
app.route("/", statsMultiRoutes);
|
|
3423
|
+
app.route("/", eventsMultiRoutes);
|
|
3424
|
+
app.route("/", policyMultiRoutes);
|
|
3425
|
+
app.route("/", usersMultiRoutes);
|
|
3426
|
+
} else {
|
|
3427
|
+
app.route("/", statsLegacyRoutes);
|
|
3428
|
+
app.route("/", eventsLegacyRoutes);
|
|
3429
|
+
app.route("/", policyLegacyRoutes);
|
|
3430
|
+
app.route("/", usersLegacyRoutes);
|
|
3431
|
+
}
|
|
3189
3432
|
function createServer(port2) {
|
|
3190
3433
|
return serve({
|
|
3191
3434
|
fetch: app.fetch,
|
|
@@ -3239,12 +3482,15 @@ if (values.help) {
|
|
|
3239
3482
|
console.log(HELP);
|
|
3240
3483
|
process.exit(0);
|
|
3241
3484
|
}
|
|
3242
|
-
var
|
|
3243
|
-
if (!
|
|
3244
|
-
|
|
3245
|
-
|
|
3485
|
+
var relayInstances = process.env.RELAY_INSTANCES;
|
|
3486
|
+
if (!relayInstances) {
|
|
3487
|
+
const token = values.token ?? process.env.RELAY_AGENT_TOKEN ?? process.env.TOKEN;
|
|
3488
|
+
if (!token) {
|
|
3489
|
+
console.error("Error: --token is required (or set RELAY_AGENT_TOKEN env var)");
|
|
3490
|
+
process.exit(1);
|
|
3491
|
+
}
|
|
3492
|
+
process.env.RELAY_AGENT_TOKEN = token;
|
|
3246
3493
|
}
|
|
3247
|
-
process.env.RELAY_AGENT_TOKEN = token;
|
|
3248
3494
|
var port = parseInt(values.port ?? process.env.PORT ?? "7800", 10);
|
|
3249
3495
|
createServer(port);
|
|
3250
3496
|
console.log(`relay-agent listening on http://localhost:${port}`);
|