@decentnetwork/lan 0.1.91 → 0.1.92

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.
Binary file
Binary file
Binary file
Binary file
package/dist/ui/server.js CHANGED
@@ -17,6 +17,7 @@ import { existsSync, readFileSync, writeFileSync } from "node:fs";
17
17
  import { fileURLToPath } from "node:url";
18
18
  import { dirname, join } from "node:path";
19
19
  import yaml from "js-yaml";
20
+ import { DEFAULT_EXITS } from "../config/loader.js";
20
21
  // Directory holding the built desktop UI bundle (index.html, app.js, vendor/).
21
22
  // scripts/build-ui.mjs emits it next to this compiled module at dist/ui/desktop/.
22
23
  const DESKTOP_DIR = join(dirname(fileURLToPath(import.meta.url)), "desktop");
@@ -194,27 +195,65 @@ export function startFriendUi(opts) {
194
195
  via: "lan",
195
196
  time: "",
196
197
  }));
197
- // Exit nodes from routes.yaml regions, online-status resolved via ipam.
198
+ // Exit nodes = the AVAILABLE exits this node knows about, grouped by
199
+ // region. The authoritative list is the shipped DEFAULT_EXITS
200
+ // (config/default-exits.yaml) — every install has it, so the panel is
201
+ // populated out-of-the-box even on a node with no routes.yaml. Any
202
+ // extra exit IPs an operator added to routes.yaml are folded in too.
203
+ // online status: is that exit a currently-connected friend (by userid,
204
+ // else by its virtual ip showing up as a live peer).
198
205
  let routes = { regions: [], default: "direct" };
199
206
  if (existsSync(opts.routesPath)) {
200
207
  routes = yaml.load(readFileSync(opts.routesPath, "utf-8")) ?? routes;
201
208
  }
209
+ const onlineUserids = new Set();
202
210
  const onlineIps = new Set();
203
- for (const [uid, ip] of ipByUserid) {
204
- const s = sessByUserid.get(uid);
205
- if (s && s.transport && s.transport !== "none")
206
- onlineIps.add(ip);
211
+ for (const f of diagFriends) {
212
+ const uid = f.carrierId || f.pubkey || "";
213
+ const s = f.session;
214
+ if (uid && s && s.transport && s.transport !== "none") {
215
+ onlineUserids.add(uid);
216
+ const ip = ipByUserid.get(uid);
217
+ if (ip)
218
+ onlineIps.add(ip);
219
+ }
207
220
  }
208
- const exits = (routes.regions ?? []).map((r) => ({
209
- region: r.name,
210
- flag: (r.name || "?").slice(0, 2).toUpperCase(),
211
- label: r.name,
212
- nodes: (r.exits ?? []).map((ip, i) => ({
213
- ip,
214
- online: onlineIps.has(ip),
221
+ const regionMeta = {
222
+ china: { flag: "CN", label: "China" },
223
+ japan: { flag: "JP", label: "Japan" },
224
+ us: { flag: "US", label: "United States" },
225
+ };
226
+ const byRegion = new Map();
227
+ for (const e of DEFAULT_EXITS) {
228
+ if (!e.virtual_ip)
229
+ continue;
230
+ const region = e.region || "other";
231
+ if (!byRegion.has(region))
232
+ byRegion.set(region, []);
233
+ byRegion.get(region).push({
234
+ ip: e.virtual_ip,
235
+ host: e.name,
236
+ online: onlineUserids.has(e.userid) || onlineIps.has(e.virtual_ip),
215
237
  ping: null,
216
- host: `${(r.name || "ex").slice(0, 2)}-${String(i + 1).padStart(2, "0")}`,
217
- })),
238
+ });
239
+ }
240
+ // Fold in any extra exit IPs from routes.yaml not already covered.
241
+ const knownIps = new Set(DEFAULT_EXITS.flatMap((e) => (e.virtual_ip ? [e.virtual_ip] : [])));
242
+ for (const r of routes.regions ?? []) {
243
+ for (const ip of r.exits ?? []) {
244
+ if (knownIps.has(ip))
245
+ continue;
246
+ knownIps.add(ip);
247
+ if (!byRegion.has(r.name))
248
+ byRegion.set(r.name, []);
249
+ byRegion.get(r.name).push({ ip, host: ip, online: onlineIps.has(ip), ping: null });
250
+ }
251
+ }
252
+ const exits = [...byRegion.entries()].map(([region, nodes]) => ({
253
+ region,
254
+ flag: regionMeta[region]?.flag ?? region.slice(0, 2).toUpperCase(),
255
+ label: regionMeta[region]?.label ?? region,
256
+ nodes,
218
257
  }));
219
258
  const activeExit = routes.default && routes.default !== "direct" ? routes.default : null;
220
259
  sendJson(res, 200, { me, peers, requests, exits, activeExit });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@decentnetwork/lan",
3
- "version": "0.1.91",
3
+ "version": "0.1.92",
4
4
  "description": "Private virtual LAN for self-hosted services and AI agents, built on Elastos Carrier. NAT-traversal, name service, ACL, all over a peer-to-peer mesh — no public IP required.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",