@bitmacro/relay-agent 0.1.4 → 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 +85 -56
- package/dist/bin/relay-agent.mjs +482 -203
- package/dist/bin/relay-agent.mjs.map +1 -1
- package/dist/index.mjs +470 -194
- 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,68 +3039,92 @@ 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
|
-
|
|
2990
|
-
|
|
3098
|
+
var PUBKEY_HEX_REGEX = /^[0-9a-f]{64}$/;
|
|
3099
|
+
function isValidPubkey(s) {
|
|
3100
|
+
return PUBKEY_HEX_REGEX.test(s.toLowerCase());
|
|
3101
|
+
}
|
|
3102
|
+
async function getPolicyEntries(cfg = null) {
|
|
3103
|
+
const resolved = resolveConfig(cfg);
|
|
3104
|
+
const lines = await readWhitelist(resolved.whitelistPath);
|
|
3105
|
+
const entries = [];
|
|
3106
|
+
for (const line of lines) {
|
|
3107
|
+
if (line.startsWith("#") || !line) continue;
|
|
3108
|
+
if (line.startsWith("!")) {
|
|
3109
|
+
const pubkey2 = line.slice(1).toLowerCase();
|
|
3110
|
+
if (isValidPubkey(pubkey2)) entries.push({ pubkey: pubkey2, status: "blocked" });
|
|
3111
|
+
continue;
|
|
3112
|
+
}
|
|
3113
|
+
const pubkey = line.toLowerCase();
|
|
3114
|
+
if (isValidPubkey(pubkey)) entries.push({ pubkey, status: "allowed" });
|
|
3115
|
+
}
|
|
3116
|
+
return entries;
|
|
3117
|
+
}
|
|
3118
|
+
async function writeWhitelist(whitelistPath, lines) {
|
|
3119
|
+
const dir = dirname(whitelistPath);
|
|
2991
3120
|
if (!existsSync(dir)) {
|
|
2992
3121
|
await mkdir(dir, { recursive: true });
|
|
2993
3122
|
}
|
|
2994
|
-
await writeFile(
|
|
3123
|
+
await writeFile(whitelistPath, lines.join("\n") + "\n", "utf-8");
|
|
2995
3124
|
}
|
|
2996
|
-
async function blockPubkey(pubkey) {
|
|
2997
|
-
const
|
|
3125
|
+
async function blockPubkey(pubkey, cfg = null) {
|
|
3126
|
+
const resolved = resolveConfig(cfg);
|
|
3127
|
+
const lines = await readWhitelist(resolved.whitelistPath);
|
|
2998
3128
|
const blockLine = `!${pubkey}`;
|
|
2999
3129
|
const withoutPubkey = lines.filter(
|
|
3000
3130
|
(l) => l !== pubkey && l !== blockLine
|
|
@@ -3002,80 +3132,143 @@ async function blockPubkey(pubkey) {
|
|
|
3002
3132
|
if (!withoutPubkey.includes(blockLine)) {
|
|
3003
3133
|
withoutPubkey.push(blockLine);
|
|
3004
3134
|
}
|
|
3005
|
-
await writeWhitelist(withoutPubkey);
|
|
3006
|
-
await deleteByPubkey(pubkey);
|
|
3135
|
+
await writeWhitelist(resolved.whitelistPath, withoutPubkey);
|
|
3136
|
+
await deleteByPubkey(pubkey, cfg);
|
|
3007
3137
|
}
|
|
3008
|
-
async function allowPubkey(pubkey) {
|
|
3009
|
-
const
|
|
3138
|
+
async function allowPubkey(pubkey, cfg = null) {
|
|
3139
|
+
const resolved = resolveConfig(cfg);
|
|
3140
|
+
const lines = await readWhitelist(resolved.whitelistPath);
|
|
3010
3141
|
const blockLine = `!${pubkey}`;
|
|
3011
3142
|
const filtered = lines.filter((l) => l !== blockLine);
|
|
3012
3143
|
if (!filtered.includes(pubkey)) {
|
|
3013
3144
|
filtered.push(pubkey);
|
|
3014
3145
|
}
|
|
3015
|
-
await writeWhitelist(filtered);
|
|
3146
|
+
await writeWhitelist(resolved.whitelistPath, filtered);
|
|
3016
3147
|
}
|
|
3017
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
|
+
|
|
3018
3189
|
// src/routes/events.ts
|
|
3019
|
-
|
|
3020
|
-
|
|
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);
|
|
3021
3224
|
try {
|
|
3022
|
-
const
|
|
3023
|
-
const authors = c.req.query("authors");
|
|
3024
|
-
const since = c.req.query("since");
|
|
3025
|
-
const until = c.req.query("until");
|
|
3026
|
-
const limit = c.req.query("limit");
|
|
3027
|
-
const filter = {};
|
|
3028
|
-
if (kinds) {
|
|
3029
|
-
const parsed = kinds.split(",").map((k) => parseInt(k, 10));
|
|
3030
|
-
if (parsed.some((n) => Number.isNaN(n))) {
|
|
3031
|
-
return c.json({ error: "invalid kinds" }, 400);
|
|
3032
|
-
}
|
|
3033
|
-
filter.kinds = parsed;
|
|
3034
|
-
}
|
|
3035
|
-
if (authors) filter.authors = authors.split(",").map((a) => a.trim());
|
|
3036
|
-
if (since) {
|
|
3037
|
-
const n = parseInt(since, 10);
|
|
3038
|
-
if (Number.isNaN(n)) return c.json({ error: "invalid since" }, 400);
|
|
3039
|
-
filter.since = n;
|
|
3040
|
-
}
|
|
3041
|
-
if (until) {
|
|
3042
|
-
const n = parseInt(until, 10);
|
|
3043
|
-
if (Number.isNaN(n)) return c.json({ error: "invalid until" }, 400);
|
|
3044
|
-
filter.until = n;
|
|
3045
|
-
}
|
|
3046
|
-
if (limit) {
|
|
3047
|
-
const n = parseInt(limit, 10);
|
|
3048
|
-
if (Number.isNaN(n)) return c.json({ error: "invalid limit" }, 400);
|
|
3049
|
-
filter.limit = n;
|
|
3050
|
-
}
|
|
3051
|
-
const events = await scanEvents(filter);
|
|
3225
|
+
const events = await scanEvents(filter, null);
|
|
3052
3226
|
return c.json(events);
|
|
3053
3227
|
} catch {
|
|
3054
3228
|
return c.json({ error: "relay unavailable" }, 503);
|
|
3055
3229
|
}
|
|
3056
3230
|
});
|
|
3057
|
-
|
|
3231
|
+
eventsLegacyRoutes.delete("/events/:id", async (c) => {
|
|
3058
3232
|
try {
|
|
3059
3233
|
const id = c.req.param("id");
|
|
3060
|
-
await deleteEvent(id);
|
|
3234
|
+
await deleteEvent(id, null);
|
|
3061
3235
|
return c.json({ deleted: id });
|
|
3062
3236
|
} catch {
|
|
3063
3237
|
return c.json({ error: "relay unavailable" }, 503);
|
|
3064
3238
|
}
|
|
3065
3239
|
});
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
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);
|
|
3070
3247
|
try {
|
|
3071
|
-
const
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
uptime: raw2.uptime_seconds,
|
|
3076
|
-
version: raw2.strfry_version
|
|
3248
|
+
const cfg = {
|
|
3249
|
+
strfryConfig: instance.strfryConfig,
|
|
3250
|
+
strfryDb: instance.strfryDb,
|
|
3251
|
+
whitelistPath: instance.whitelistPath
|
|
3077
3252
|
};
|
|
3078
|
-
|
|
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 });
|
|
3079
3272
|
} catch {
|
|
3080
3273
|
return c.json({ error: "relay unavailable" }, 503);
|
|
3081
3274
|
}
|
|
@@ -3083,34 +3276,83 @@ statsRoutes.get("/stats", async (c) => {
|
|
|
3083
3276
|
|
|
3084
3277
|
// src/routes/policy.ts
|
|
3085
3278
|
var PUBKEY_REGEX = /^[0-9a-f]{64}$/;
|
|
3086
|
-
|
|
3087
|
-
|
|
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) => {
|
|
3288
|
+
try {
|
|
3289
|
+
const entries = await getPolicyEntries(null);
|
|
3290
|
+
return c.json({ entries });
|
|
3291
|
+
} catch {
|
|
3292
|
+
return c.json({ error: "relay unavailable" }, 503);
|
|
3293
|
+
}
|
|
3294
|
+
});
|
|
3295
|
+
policyLegacyRoutes.post("/policy/block", async (c) => {
|
|
3088
3296
|
try {
|
|
3089
3297
|
const body = await c.req.json();
|
|
3090
3298
|
const { pubkey } = body;
|
|
3091
|
-
if (!pubkey || typeof pubkey !== "string") {
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
if (!PUBKEY_REGEX.test(pubkey.toLowerCase())) {
|
|
3095
|
-
return c.json({ error: "invalid pubkey format" }, 400);
|
|
3096
|
-
}
|
|
3097
|
-
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);
|
|
3098
3302
|
return c.json({ blocked: pubkey });
|
|
3099
3303
|
} catch {
|
|
3100
3304
|
return c.json({ error: "relay unavailable" }, 503);
|
|
3101
3305
|
}
|
|
3102
3306
|
});
|
|
3103
|
-
|
|
3307
|
+
policyLegacyRoutes.post("/policy/allow", async (c) => {
|
|
3104
3308
|
try {
|
|
3105
3309
|
const body = await c.req.json();
|
|
3106
3310
|
const { pubkey } = body;
|
|
3107
|
-
if (!pubkey || typeof pubkey !== "string") {
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
}
|
|
3113
|
-
|
|
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));
|
|
3114
3356
|
return c.json({ allowed: pubkey });
|
|
3115
3357
|
} catch {
|
|
3116
3358
|
return c.json({ error: "relay unavailable" }, 503);
|
|
@@ -3118,17 +3360,41 @@ policyRoutes.post("/policy/allow", async (c) => {
|
|
|
3118
3360
|
});
|
|
3119
3361
|
|
|
3120
3362
|
// src/routes/users.ts
|
|
3121
|
-
var
|
|
3122
|
-
|
|
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
|
+
}
|
|
3372
|
+
try {
|
|
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
|
+
}
|
|
3123
3391
|
try {
|
|
3124
|
-
const
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
}
|
|
3131
|
-
const pubkeys = await listUsers(limit);
|
|
3392
|
+
const cfg = {
|
|
3393
|
+
strfryConfig: instance.strfryConfig,
|
|
3394
|
+
strfryDb: instance.strfryDb,
|
|
3395
|
+
whitelistPath: instance.whitelistPath
|
|
3396
|
+
};
|
|
3397
|
+
const pubkeys = await listUsers(limit ?? 1e3, cfg);
|
|
3132
3398
|
return c.json({ users: pubkeys });
|
|
3133
3399
|
} catch {
|
|
3134
3400
|
return c.json({ error: "relay unavailable" }, 503);
|
|
@@ -3137,22 +3403,32 @@ usersRoutes.get("/users", async (c) => {
|
|
|
3137
3403
|
|
|
3138
3404
|
// src/index.ts
|
|
3139
3405
|
var DEFAULT_ORIGINS = [
|
|
3140
|
-
"https://
|
|
3406
|
+
"https://relay-panel.bitmacro.io",
|
|
3141
3407
|
"http://localhost:3000"
|
|
3142
3408
|
];
|
|
3143
3409
|
var EXTRA_ORIGINS = (process.env.ALLOWED_ORIGINS ?? "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
3144
3410
|
var ALLOWED_ORIGINS = [...DEFAULT_ORIGINS, ...EXTRA_ORIGINS];
|
|
3145
3411
|
var app = new Hono2();
|
|
3146
|
-
app.use("*", cors({ origin: ALLOWED_ORIGINS }));
|
|
3147
3412
|
app.use("*", async (c, next) => {
|
|
3148
|
-
|
|
3149
|
-
|
|
3413
|
+
const start = Date.now();
|
|
3414
|
+
await next();
|
|
3415
|
+
console.log(`[relay-agent] ${c.req.method} ${c.req.path} ${c.res.status} ${Date.now() - start}ms`);
|
|
3150
3416
|
});
|
|
3417
|
+
app.use("*", cors({ origin: ALLOWED_ORIGINS }));
|
|
3418
|
+
app.use("*", authMiddleware);
|
|
3151
3419
|
app.route("/", healthRoutes);
|
|
3152
|
-
|
|
3153
|
-
app.route("/",
|
|
3154
|
-
app.route("/",
|
|
3155
|
-
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
|
+
}
|
|
3156
3432
|
function createServer(port2) {
|
|
3157
3433
|
return serve({
|
|
3158
3434
|
fetch: app.fetch,
|
|
@@ -3206,12 +3482,15 @@ if (values.help) {
|
|
|
3206
3482
|
console.log(HELP);
|
|
3207
3483
|
process.exit(0);
|
|
3208
3484
|
}
|
|
3209
|
-
var
|
|
3210
|
-
if (!
|
|
3211
|
-
|
|
3212
|
-
|
|
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;
|
|
3213
3493
|
}
|
|
3214
|
-
process.env.RELAY_AGENT_TOKEN = token;
|
|
3215
3494
|
var port = parseInt(values.port ?? process.env.PORT ?? "7800", 10);
|
|
3216
3495
|
createServer(port);
|
|
3217
3496
|
console.log(`relay-agent listening on http://localhost:${port}`);
|