lnlink-server 1.0.1 → 1.0.2

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.
Files changed (91) hide show
  1. package/README.md +461 -0
  2. package/{app.js → dist/app.js} +329 -321
  3. package/dist/binaries.json +25 -0
  4. package/{build-info.json → dist/build-info.json} +2 -1
  5. package/{index.js → dist/index.js} +690 -121
  6. package/dist/index.js.map +7 -0
  7. package/dist/node_modules/debug/.coveralls.yml +1 -0
  8. package/dist/node_modules/debug/.eslintrc +11 -0
  9. package/dist/node_modules/debug/.travis.yml +14 -0
  10. package/dist/node_modules/debug/CHANGELOG.md +362 -0
  11. package/dist/node_modules/debug/LICENSE +19 -0
  12. package/dist/node_modules/debug/Makefile +50 -0
  13. package/dist/node_modules/debug/README.md +312 -0
  14. package/dist/node_modules/debug/component.json +19 -0
  15. package/dist/node_modules/debug/karma.conf.js +70 -0
  16. package/dist/node_modules/debug/node.js +1 -0
  17. package/dist/node_modules/debug/package.json +49 -0
  18. package/dist/node_modules/debug/src/browser.js +185 -0
  19. package/dist/node_modules/debug/src/debug.js +202 -0
  20. package/dist/node_modules/debug/src/index.js +10 -0
  21. package/dist/node_modules/debug/src/inspector-log.js +15 -0
  22. package/dist/node_modules/debug/src/node.js +248 -0
  23. package/dist/node_modules/depd/History.md +96 -0
  24. package/dist/node_modules/depd/LICENSE +22 -0
  25. package/dist/node_modules/depd/Readme.md +280 -0
  26. package/dist/node_modules/depd/index.js +522 -0
  27. package/dist/node_modules/depd/lib/browser/index.js +77 -0
  28. package/dist/node_modules/depd/lib/compat/callsite-tostring.js +103 -0
  29. package/dist/node_modules/depd/lib/compat/event-listener-count.js +22 -0
  30. package/dist/node_modules/depd/lib/compat/index.js +79 -0
  31. package/dist/node_modules/depd/package.json +41 -0
  32. package/dist/node_modules/http-errors/HISTORY.md +132 -0
  33. package/dist/node_modules/http-errors/LICENSE +23 -0
  34. package/dist/node_modules/http-errors/README.md +135 -0
  35. package/dist/node_modules/http-errors/index.js +260 -0
  36. package/dist/node_modules/http-errors/package.json +48 -0
  37. package/dist/node_modules/inherits/LICENSE +16 -0
  38. package/dist/node_modules/inherits/README.md +42 -0
  39. package/dist/node_modules/inherits/inherits.js +7 -0
  40. package/dist/node_modules/inherits/inherits_browser.js +23 -0
  41. package/dist/node_modules/inherits/package.json +29 -0
  42. package/dist/node_modules/ms/index.js +152 -0
  43. package/dist/node_modules/ms/license.md +21 -0
  44. package/dist/node_modules/ms/package.json +37 -0
  45. package/dist/node_modules/ms/readme.md +51 -0
  46. package/dist/node_modules/setprototypeof/LICENSE +13 -0
  47. package/dist/node_modules/setprototypeof/README.md +26 -0
  48. package/dist/node_modules/setprototypeof/index.d.ts +2 -0
  49. package/dist/node_modules/setprototypeof/index.js +15 -0
  50. package/dist/node_modules/setprototypeof/package.json +25 -0
  51. package/dist/node_modules/statuses/HISTORY.md +65 -0
  52. package/dist/node_modules/statuses/LICENSE +23 -0
  53. package/dist/node_modules/statuses/README.md +127 -0
  54. package/dist/node_modules/statuses/codes.json +66 -0
  55. package/dist/node_modules/statuses/index.js +113 -0
  56. package/dist/node_modules/statuses/package.json +48 -0
  57. package/dist/package.json +62 -0
  58. package/dist/public/css/initOwner.css +705 -0
  59. package/dist/public/img/logo.svg +1 -0
  60. package/{public → dist/public}/init.html +5 -5
  61. package/dist/public/js/init.js +840 -0
  62. package/{setting.regtest.json → dist/setting.regtest.json} +2 -1
  63. package/package.json +51 -20
  64. package/binaries.json +0 -20
  65. package/index.js.map +0 -7
  66. package/public/css/initOwner.css +0 -553
  67. package/public/js/init.js +0 -454
  68. /package/{config.default.js → dist/config.default.js} +0 -0
  69. /package/{prisma → dist/prisma}/migrations/20250918020814_/migration.sql +0 -0
  70. /package/{prisma → dist/prisma}/migrations/20251114105314_auto_update/migration.sql +0 -0
  71. /package/{prisma → dist/prisma}/migrations/migration_lock.toml +0 -0
  72. /package/{prisma → dist/prisma}/schema.prisma +0 -0
  73. /package/{proto → dist/proto}/chainkit.proto +0 -0
  74. /package/{proto → dist/proto}/lightning.proto +0 -0
  75. /package/{proto → dist/proto}/lit-status.proto +0 -0
  76. /package/{proto → dist/proto}/looprpc/client.proto +0 -0
  77. /package/{proto → dist/proto}/price_oracle.proto +0 -0
  78. /package/{proto → dist/proto}/rfqrpc/rfq.proto +0 -0
  79. /package/{proto → dist/proto}/routerrpc/router.proto +0 -0
  80. /package/{proto → dist/proto}/signrpc/signer.proto +0 -0
  81. /package/{proto → dist/proto}/stateservice.proto +0 -0
  82. /package/{proto → dist/proto}/swapserverrpc/common.proto +0 -0
  83. /package/{proto → dist/proto}/tapchannel.proto +0 -0
  84. /package/{proto → dist/proto}/tapcommon.proto +0 -0
  85. /package/{proto → dist/proto}/taprootassets.proto +0 -0
  86. /package/{proto → dist/proto}/universe.proto +0 -0
  87. /package/{proto → dist/proto}/walletkit.proto +0 -0
  88. /package/{proto → dist/proto}/walletunlocker.proto +0 -0
  89. /package/{public → dist/public}/favicon.ico +0 -0
  90. /package/{setting.mainnet.json → dist/setting.mainnet.json} +0 -0
  91. /package/{setting.testnet.json → dist/setting.testnet.json} +0 -0
@@ -908,6 +908,7 @@ var require_constants = __commonJS({
908
908
  "backup_node",
909
909
  "restore_node",
910
910
  "update_node_name",
911
+ "toggle_tor",
911
912
  ...READ_ONLY_PERMISSIONS
912
913
  ];
913
914
  var SOCILA_TYPE = {
@@ -1098,7 +1099,7 @@ var require_nostrEventRepository = __commonJS({
1098
1099
  }
1099
1100
  /**
1100
1101
  * Update event status and related fields
1101
- * @param {number} id - Event ID
1102
+ * @param {number|string} id - Event ID (int) or event_id (string)
1102
1103
  * @param {object} updateData - Update data
1103
1104
  * @returns {Promise<object>} Updated event
1104
1105
  */
@@ -1118,6 +1119,13 @@ var require_nostrEventRepository = __commonJS({
1118
1119
  if (reply_err) {
1119
1120
  data.reply_err = reply_err;
1120
1121
  }
1122
+ const isStringId = typeof id === "string" && id.length > 10;
1123
+ if (isStringId) {
1124
+ return this.prisma.lnlinkNostrEvent.updateMany({
1125
+ where: { event_id: id },
1126
+ data
1127
+ });
1128
+ }
1121
1129
  return this.prisma.lnlinkNostrEvent.update({
1122
1130
  where: { id },
1123
1131
  data
@@ -1218,8 +1226,11 @@ var require_events = __commonJS({
1218
1226
  }, "insertEvent"),
1219
1227
  updateEvent: /* @__PURE__ */ __name(async (id, updateData) => {
1220
1228
  try {
1221
- await getNostrEventRepo().updateEvent(id, updateData);
1222
- return 1;
1229
+ const result = await getNostrEventRepo().updateEvent(id, updateData);
1230
+ if (result && typeof result.count === "number") {
1231
+ return result.count;
1232
+ }
1233
+ return result ? 1 : 0;
1223
1234
  } catch (error) {
1224
1235
  console.error("Error updating event:", error);
1225
1236
  return 0;
@@ -2709,7 +2720,8 @@ var require_getConfig = __commonJS({
2709
2720
  "LINK_DATABASE_URL",
2710
2721
  "LINK_REPORT_BASE_URL",
2711
2722
  "LINK_REPORT_ADDRESS",
2712
- "LINK_RGB_HOST"
2723
+ "LINK_RGB_HOST",
2724
+ "LINK_EXTERNAL_NODES"
2713
2725
  ];
2714
2726
  const processConfig = {};
2715
2727
  ENV_KEYS.forEach((key) => {
@@ -5326,6 +5338,7 @@ var require_reportService = __commonJS({
5326
5338
  // business/service/nodeManage/config.js
5327
5339
  var require_config2 = __commonJS({
5328
5340
  "business/service/nodeManage/config.js"(exports2, module2) {
5341
+ var path2 = require("node:path");
5329
5342
  var { getConfig: getConfig2 } = require_getConfig();
5330
5343
  function getBinaryName(baseName) {
5331
5344
  const isWindows = process.platform === "win32";
@@ -5484,18 +5497,18 @@ var require_config2 = __commonJS({
5484
5497
  const { LINK_BINARY_PATH } = config;
5485
5498
  return {
5486
5499
  litd: {
5487
- command: `${LINK_BINARY_PATH}/${getBinaryName("litd")}`,
5500
+ command: path2.join(LINK_BINARY_PATH || "", getBinaryName("litd")),
5488
5501
  args: null,
5489
5502
  // Generated dynamically
5490
5503
  process: null
5491
5504
  },
5492
5505
  tor: {
5493
- command: `${LINK_BINARY_PATH}/${getBinaryName("tor")}`,
5506
+ command: path2.join(LINK_BINARY_PATH || "", getBinaryName("tor")),
5494
5507
  args: buildTorArgs(),
5495
5508
  process: null
5496
5509
  },
5497
5510
  rgb: {
5498
- command: `${LINK_BINARY_PATH}/${getBinaryName("rgb-lightning-node")}`,
5511
+ command: path2.join(LINK_BINARY_PATH || "", getBinaryName("rgb-lightning-node")),
5499
5512
  args: null,
5500
5513
  // Generated dynamically
5501
5514
  process: null
@@ -5518,10 +5531,16 @@ var require_processManager = __commonJS({
5518
5531
  "business/service/nodeManage/processManager.js"(exports2, module2) {
5519
5532
  var { spawn, exec } = require("node:child_process");
5520
5533
  var { getLightningService } = require_common();
5534
+ var { getConfig: getConfig2 } = require_getConfig();
5521
5535
  var Logger = require_linkLogger();
5522
5536
  var { sleep } = require_timeUtils();
5523
5537
  var { getServiceConfig } = require_config2();
5524
5538
  var isWindows = process.platform === "win32";
5539
+ function isExternalNodesMode() {
5540
+ const config = getConfig2();
5541
+ return config.LINK_EXTERNAL_NODES === "true" || config.LINK_EXTERNAL_NODES === true;
5542
+ }
5543
+ __name(isExternalNodesMode, "isExternalNodesMode");
5525
5544
  function findProcesses(processName) {
5526
5545
  return new Promise((resolve) => {
5527
5546
  let command;
@@ -5601,6 +5620,10 @@ var require_processManager = __commonJS({
5601
5620
  __name(setServiceProcess, "setServiceProcess");
5602
5621
  function startLitdService(args) {
5603
5622
  const logger2 = new Logger("processManager");
5623
+ if (isExternalNodesMode()) {
5624
+ logger2.info("External nodes mode: skipping litd local spawn (managed externally)");
5625
+ return Promise.resolve(true);
5626
+ }
5604
5627
  return new Promise((resolve, reject) => {
5605
5628
  const service = getService("litd");
5606
5629
  if (!service) {
@@ -5652,6 +5675,10 @@ var require_processManager = __commonJS({
5652
5675
  __name(startLitdService, "startLitdService");
5653
5676
  function startTorService(args) {
5654
5677
  const logger2 = new Logger("processManager");
5678
+ if (isExternalNodesMode()) {
5679
+ logger2.info("External nodes mode: skipping Tor local spawn (managed externally)");
5680
+ return Promise.resolve(true);
5681
+ }
5655
5682
  return new Promise((resolve, reject) => {
5656
5683
  const service = getService("tor");
5657
5684
  if (!service) {
@@ -5711,6 +5738,10 @@ var require_processManager = __commonJS({
5711
5738
  __name(startTorService, "startTorService");
5712
5739
  function startRgbService(args) {
5713
5740
  const logger2 = new Logger("processManager");
5741
+ if (isExternalNodesMode()) {
5742
+ logger2.info("External nodes mode: skipping RGB local spawn (managed externally)");
5743
+ return Promise.resolve(true);
5744
+ }
5714
5745
  return new Promise((resolve, reject) => {
5715
5746
  const service = getService("rgb");
5716
5747
  if (!service) {
@@ -5832,8 +5863,12 @@ var require_processManager = __commonJS({
5832
5863
  }
5833
5864
  __name(findAndKillProcess, "findAndKillProcess");
5834
5865
  async function stopLitdService() {
5835
- const initialService = getService("litd");
5836
5866
  const logger2 = new Logger("processManager");
5867
+ if (isExternalNodesMode()) {
5868
+ logger2.info("External nodes mode: skipping litd stop (managed externally)");
5869
+ return true;
5870
+ }
5871
+ const initialService = getService("litd");
5837
5872
  if (!initialService) {
5838
5873
  throw new Error("litd service not found");
5839
5874
  }
@@ -5865,6 +5900,11 @@ var require_processManager = __commonJS({
5865
5900
  }
5866
5901
  __name(stopLitdService, "stopLitdService");
5867
5902
  async function stopTorService() {
5903
+ const logger2 = new Logger("processManager");
5904
+ if (isExternalNodesMode()) {
5905
+ logger2.info("External nodes mode: skipping Tor stop (managed externally)");
5906
+ return true;
5907
+ }
5868
5908
  const service = getService("tor");
5869
5909
  if (!service) {
5870
5910
  throw new Error("tor service not found");
@@ -5878,6 +5918,10 @@ var require_processManager = __commonJS({
5878
5918
  __name(stopTorService, "stopTorService");
5879
5919
  async function stopRgbService() {
5880
5920
  const logger2 = new Logger("processManager");
5921
+ if (isExternalNodesMode()) {
5922
+ logger2.info("External nodes mode: skipping RGB stop (managed externally)");
5923
+ return true;
5924
+ }
5881
5925
  const service = getService("rgb");
5882
5926
  if (!service) {
5883
5927
  throw new Error("rgb service not found");
@@ -5885,15 +5929,27 @@ var require_processManager = __commonJS({
5885
5929
  try {
5886
5930
  logger2.info("Attempting graceful shutdown of RGB via SIGTERM...");
5887
5931
  const gracefulShutdown = new Promise((resolve) => {
5888
- exec(`pkill -SIGTERM -f 'rgb-lightning-node'`, (err, stdout) => {
5889
- if (!err) {
5890
- logger2.info(`RGB stopped gracefully: ${stdout}`);
5891
- resolve(true);
5892
- } else {
5893
- logger2.warn(`Graceful shutdown of RGB failed: ${err?.message}`);
5894
- resolve(false);
5895
- }
5896
- });
5932
+ if (isWindows) {
5933
+ exec(`taskkill /F /IM rgb-lightning-node.exe`, (err, stdout) => {
5934
+ if (!err) {
5935
+ logger2.info(`RGB stopped gracefully: ${stdout}`);
5936
+ resolve(true);
5937
+ } else {
5938
+ logger2.warn(`Graceful shutdown of RGB failed: ${err?.message}`);
5939
+ resolve(false);
5940
+ }
5941
+ });
5942
+ } else {
5943
+ exec(`pkill -SIGTERM -f 'rgb-lightning-node'`, (err, stdout) => {
5944
+ if (!err) {
5945
+ logger2.info(`RGB stopped gracefully: ${stdout}`);
5946
+ resolve(true);
5947
+ } else {
5948
+ logger2.warn(`Graceful shutdown of RGB failed: ${err?.message}`);
5949
+ resolve(false);
5950
+ }
5951
+ });
5952
+ }
5897
5953
  });
5898
5954
  const gracefulResult = await gracefulShutdown;
5899
5955
  if (gracefulResult) {
@@ -6148,11 +6204,17 @@ var require_statusChecker = __commonJS({
6148
6204
  "business/service/nodeManage/statusChecker.js"(exports2, module2) {
6149
6205
  var findProcess = require("find-process").default;
6150
6206
  var { getWalletState } = require_common();
6207
+ var { getConfig: getConfig2 } = require_getConfig();
6151
6208
  var { WALLET_STATE_CODE } = require_constants();
6152
6209
  var Logger = require_linkLogger();
6153
6210
  var { getNodeState } = require_node();
6154
6211
  var { getServiceConfig } = require_config2();
6155
6212
  var { getServicesState } = require_processManager();
6213
+ function isExternalNodesMode() {
6214
+ const config = getConfig2();
6215
+ return config.LINK_EXTERNAL_NODES === "true" || config.LINK_EXTERNAL_NODES === true;
6216
+ }
6217
+ __name(isExternalNodesMode, "isExternalNodesMode");
6156
6218
  var ServiceStatus = {
6157
6219
  RUNNING: "Running",
6158
6220
  STOPPED: "Stopped"
@@ -6172,6 +6234,19 @@ var require_statusChecker = __commonJS({
6172
6234
  async function checkLitdProcess() {
6173
6235
  const logger2 = new Logger("statusChecker");
6174
6236
  try {
6237
+ if (isExternalNodesMode()) {
6238
+ logger2.info("External nodes mode: checking litd via RPC...");
6239
+ try {
6240
+ const walletState = await getWalletState();
6241
+ if (walletState !== void 0 && walletState !== null) {
6242
+ logger2.info(`Litd is running (wallet state: ${walletState})`);
6243
+ return ServiceStatus.RUNNING;
6244
+ }
6245
+ } catch (rpcError) {
6246
+ logger2.warn(`RPC check failed: ${rpcError.message}`);
6247
+ }
6248
+ return ServiceStatus.STOPPED;
6249
+ }
6175
6250
  const processList = await findProcess("name", "litd");
6176
6251
  if (processList.length > 0) {
6177
6252
  logger2.info(`Found litd process(es): ${processList.map((p) => p.pid).join(", ")}`);
@@ -6187,6 +6262,19 @@ var require_statusChecker = __commonJS({
6187
6262
  async function checkRgbProcess() {
6188
6263
  const logger2 = new Logger("statusChecker");
6189
6264
  try {
6265
+ if (isExternalNodesMode()) {
6266
+ logger2.info("External nodes mode: checking RGB via SDK...");
6267
+ try {
6268
+ const nodeState = await getNodeState();
6269
+ if (nodeState !== void 0 && nodeState !== null) {
6270
+ logger2.info(`RGB is running (node state received)`);
6271
+ return ServiceStatus.RUNNING;
6272
+ }
6273
+ } catch (sdkError) {
6274
+ logger2.warn(`RGB SDK check failed: ${sdkError.message}`);
6275
+ }
6276
+ return ServiceStatus.STOPPED;
6277
+ }
6190
6278
  const processList = await findProcess("name", "rgb-lightning-node");
6191
6279
  if (processList.length > 0) {
6192
6280
  logger2.info(`Found rgb-lightning-node process(es): ${processList.map((p) => p.pid).join(", ")}`);
@@ -7157,11 +7245,11 @@ var require_accountService = __commonJS({
7157
7245
  const lnLinkConfig = await getMainLnlinkConfig();
7158
7246
  const {
7159
7247
  node_npub,
7160
- settings,
7161
- npub: owner_npub
7248
+ settings
7162
7249
  } = lnLinkConfig;
7163
7250
  const relay = settings?.nostrRelays ? settings?.nostrRelays?.[0] : null;
7164
- const sk = stringToFixedBytes(`${owner_npub}`).toString(
7251
+ const READONLY_SALT = "lnlink_readonly_v1";
7252
+ const sk = stringToFixedBytes(`${node_npub}:${READONLY_SALT}`).toString(
7165
7253
  "hex"
7166
7254
  );
7167
7255
  const pk = getPublicKey(sk);
@@ -7288,7 +7376,7 @@ var require_package = __commonJS({
7288
7376
  "package.json"(exports2, module2) {
7289
7377
  module2.exports = {
7290
7378
  name: "lnlink-server",
7291
- version: "1.0.1",
7379
+ version: "1.0.2",
7292
7380
  private: false,
7293
7381
  main: "dist/index.js",
7294
7382
  files: [
@@ -7297,13 +7385,13 @@ var require_package = __commonJS({
7297
7385
  ],
7298
7386
  scripts: {
7299
7387
  build: "node build.js && node build.js --mode development --external all --entry electron",
7300
- "start:bin": 'dotenv -e .env.bin -- sh -c "prisma generate && (prisma migrate dev --name auto_update || prisma db push) && node ./scripts/binSetup.js && node ./app.js"',
7388
+ "start:bin": "dotenv -e .env.bin -- node scripts/start-bin.js",
7301
7389
  "start:docker:dev": "dotenv -e .env.dev -- docker compose -f ./docker-compose.dev.yml up --build",
7302
7390
  "start:dev": 'dotenv -e .env.dev -- sh -c "prisma generate && (prisma migrate dev --name auto_update || prisma db push) && clinic heapprof -- node ./app.js"',
7303
7391
  "start:regtest": "docker compose --env-file ./.env.regtest -f ./docker-compose-lnlink.yml up --build",
7304
7392
  "start:testnet": "docker compose --env-file ./.env.testnet -f ./docker-compose-lnlink.yml up --build",
7305
7393
  "start:mainnet": "docker compose --env-file ./.env.mainnet -f ./docker-compose-lnlink.yml up --build",
7306
- "start:bin:dist": 'dotenv -e .env.bin -- sh -c "prisma generate && prisma migrate deploy && node ./scripts/binSetup.js && node ./dist/index.js"',
7394
+ "start:bin:dist": "dotenv -e .env.bin -- node scripts/start-bin.js --dist",
7307
7395
  lint: "eslint .",
7308
7396
  "lint:fix": "eslint . --fix",
7309
7397
  "lint:staged": "lint-staged",
@@ -7325,6 +7413,7 @@ var require_package = __commonJS({
7325
7413
  axios: "^1.7.2",
7326
7414
  "bitcoin-core": "^4.2.0",
7327
7415
  "body-parser": "^1.20.2",
7416
+ bolt11: "^1.4.1",
7328
7417
  "cookie-parser": "~1.4.4",
7329
7418
  "crypto-js": "^4.2.0",
7330
7419
  dayjs: "^1.11.10",
@@ -8381,8 +8470,49 @@ var require_lndService = __commonJS({
8381
8470
  return ret;
8382
8471
  }
8383
8472
  __name(startLink, "startLink");
8473
+ async function toggleTor(args) {
8474
+ const logger2 = new Logger("lnd");
8475
+ const { enable, lnlinkUser } = args;
8476
+ if (enable === void 0 || enable === null) {
8477
+ throw new Error("enable parameter is required (true or false)");
8478
+ }
8479
+ const enableTor = enable === true || enable === "true";
8480
+ logger2.info(`Toggling Tor mode to: ${enableTor}, requested by: ${lnlinkUser?.npub || "unknown"}`);
8481
+ try {
8482
+ const { updateMainLnlinkConfig: updateConfig } = require_dbService();
8483
+ const updateRet = await updateConfig({ enable_tor: enableTor });
8484
+ if (updateRet !== 1) {
8485
+ logger2.warn(`Database update returned: ${updateRet}, proceeding anyway`);
8486
+ }
8487
+ const { reloadConfig } = require_getConfig();
8488
+ await reloadConfig();
8489
+ const { restartTerminal, stopService: stopTor, isServiceRunning: isTorRunning } = require_nodeManage();
8490
+ if (!enableTor) {
8491
+ const torRunning = await isTorRunning("tor");
8492
+ if (torRunning) {
8493
+ logger2.info("Stopping Tor service...");
8494
+ await stopTor("tor");
8495
+ }
8496
+ }
8497
+ logger2.info("Restarting litd with new Tor configuration...");
8498
+ const terminalStatus = await restartTerminal({ lnlinkUser });
8499
+ const nodeInfo = await getNodeInfo();
8500
+ logger2.info(`Tor mode toggled successfully. Tor enabled: ${enableTor}`);
8501
+ return {
8502
+ enable_tor: enableTor,
8503
+ status: "ok",
8504
+ terminal_status: terminalStatus,
8505
+ node_info: nodeInfo
8506
+ };
8507
+ } catch (error) {
8508
+ logger2.error(`Failed to toggle Tor: ${error.message}`);
8509
+ throw error;
8510
+ }
8511
+ }
8512
+ __name(toggleTor, "toggleTor");
8384
8513
  module2.exports = {
8385
8514
  startLink,
8515
+ toggleTor,
8386
8516
  getWalletState,
8387
8517
  sendPaymentSync,
8388
8518
  createLnInvoice,
@@ -8457,6 +8587,7 @@ var require_tapdService = __commonJS({
8457
8587
  } = require_dbService();
8458
8588
  var { reportTaskCompletion } = require_reportService();
8459
8589
  var TaprootAssetsInstance = require_instance2();
8590
+ var { decode } = require("bolt11");
8460
8591
  var syncUniverse = withChecks(async (args) => {
8461
8592
  const { LINK_UNIVERSE_HOST } = getConfig2();
8462
8593
  const {
@@ -8941,6 +9072,14 @@ var require_tapdService = __commonJS({
8941
9072
  if (outgoing_chan_id) {
8942
9073
  paymentRequest.outgoing_chan_id = `${outgoing_chan_id}`;
8943
9074
  }
9075
+ const decodedInvoice = decode(payment_request);
9076
+ const routingInfo = decodedInvoice.tags.find((tag) => tag.tagName === "routing_info");
9077
+ if (routingInfo && routingInfo.data && routingInfo.data.length > 0) {
9078
+ const lastHop = routingInfo.data[routingInfo.data.length - 1];
9079
+ if (lastHop.pubkey) {
9080
+ paymentRequest.last_hop_pubkey = Buffer2.from(lastHop.pubkey, "hex");
9081
+ }
9082
+ }
8944
9083
  const sendParams = {
8945
9084
  asset_amount,
8946
9085
  asset_id: Buffer2.from(asset_id, "hex"),
@@ -9227,30 +9366,40 @@ var require_constants2 = __commonJS({
9227
9366
  "nostr/config/constants.js"(exports2, module2) {
9228
9367
  var EVENT_PROCESSING_CONSTANTS = {
9229
9368
  // Error handling
9230
- MAX_CONSECUTIVE_ERRORS: 5,
9231
- ERROR_BACKOFF_TIME: 5e3,
9369
+ MAX_CONSECUTIVE_ERRORS: 10,
9370
+ ERROR_BACKOFF_TIME: 2e3,
9232
9371
  MAX_FATAL_ERRORS: 10,
9233
- MAX_BACKOFF_TIME: 6e4,
9234
- // 60 seconds max backoff
9235
- // Timeout settings
9372
+ MAX_BACKOFF_TIME: 3e4,
9373
+ // 30 seconds max backoff (reduced from 60s)
9374
+ // Timeout settings - increased for litd/rgb unlock operations (30-50s)
9236
9375
  EVENT_PROCESSING_TIMEOUT: 60 * 1e3,
9237
- // 60 seconds
9238
- // Concurrent processing settings
9239
- MAX_CONCURRENT_EVENTS: 3,
9240
- // Maximum concurrent event processing
9241
- CONCURRENT_BATCH_SIZE: 5,
9242
- // How many events to fetch at once
9376
+ // 60 seconds (to support litd/rgb unlock)
9377
+ // Concurrent processing settings - INCREASED for better throughput
9378
+ MAX_CONCURRENT_EVENTS: 10,
9379
+ // Increased from 3 to 10
9380
+ CONCURRENT_BATCH_SIZE: 15,
9381
+ // Increased from 5 to 15
9243
9382
  // Batch processing settings
9244
- BATCH_SIZE: 2,
9245
- // Sleep intervals
9246
- NORMAL_SLEEP: 300,
9247
- // Normal processing interval
9248
- IDLE_SLEEP: 500,
9249
- // Idle sleep time
9250
- ERROR_SLEEP: 1e3,
9251
- // Sleep time after error
9252
- FATAL_ERROR_SLEEP: 3e3
9253
- // Sleep time after fatal error
9383
+ BATCH_SIZE: 10,
9384
+ // Increased from 2 to 10
9385
+ // Sleep intervals - REDUCED for lower latency
9386
+ NORMAL_SLEEP: 50,
9387
+ // Reduced from 300ms to 50ms
9388
+ IDLE_SLEEP: 200,
9389
+ // Reduced from 500ms to 200ms
9390
+ ERROR_SLEEP: 500,
9391
+ // Reduced from 1000ms
9392
+ FATAL_ERROR_SLEEP: 2e3,
9393
+ // Reduced from 3000ms
9394
+ // Rate limiting - INCREASED throughput
9395
+ RATE_LIMIT_REQUESTS: 10,
9396
+ // Increased from 2 to 10 requests
9397
+ RATE_LIMIT_WINDOW: 1e3,
9398
+ // Per second
9399
+ // Memory queue settings
9400
+ MEMORY_QUEUE_MAX_SIZE: 1e3,
9401
+ DB_RECOVERY_INTERVAL: 5e3
9402
+ // Check DB for missed events every 5s
9254
9403
  };
9255
9404
  var CONNECTION_CONSTANTS = {
9256
9405
  MAX_RECONNECT_ATTEMPTS: 100,
@@ -11318,42 +11467,42 @@ var require_info = __commonJS({
11318
11467
  let stateValue = null;
11319
11468
  if (serviceRunning) {
11320
11469
  const rgbClient = getRgbClient();
11321
- const [
11322
- nodeInfoResult,
11323
- stateResult,
11324
- balanceResult,
11325
- peersResult
11326
- ] = await Promise.allSettled([
11327
- rgbClient.node.getNodeInfo(),
11328
- getNodeState(),
11329
- rgbClient.onchain.getBtcBalance({
11330
- skip_sync: false
11331
- }),
11332
- rgbClient.lightning.listPeers({})
11333
- ]);
11334
- if (stateResult.status === "fulfilled") {
11335
- stateValue = stateResult.value?.state ?? null;
11336
- } else {
11337
- logger2.warn(`getCombinedNodeInfo - getNodeState failed: ${stateResult.reason?.message || stateResult.reason}`);
11338
- }
11339
- if (nodeInfoResult.status === "fulfilled") {
11340
- nodeInfo = nodeInfoResult.value;
11341
- } else {
11342
- logger2.warn(`getCombinedNodeInfo - getNodeInfo failed: ${nodeInfoResult.reason?.message || nodeInfoResult.reason}`);
11343
- }
11344
- if (balanceResult.status === "fulfilled") {
11345
- balance = balanceResult.value;
11346
- } else {
11347
- logger2.warn(`getCombinedNodeInfo - getBtcBalance failed: ${balanceResult.reason?.message || balanceResult.reason}`);
11348
- }
11349
- if (peersResult.status === "fulfilled") {
11350
- peers = peersResult.value?.peers ?? [];
11351
- } else {
11352
- logger2.warn(`getCombinedNodeInfo - listPeers failed: ${peersResult.reason?.message || peersResult.reason}`);
11470
+ const stateResult = await getNodeState().catch((error) => {
11471
+ logger2.warn(`getCombinedNodeInfo - getNodeState failed: ${error.message}`);
11472
+ return { state: 0 };
11473
+ });
11474
+ stateValue = stateResult?.state ?? null;
11475
+ if (stateValue === 4) {
11476
+ const [
11477
+ nodeInfoResult,
11478
+ balanceResult,
11479
+ peersResult
11480
+ ] = await Promise.allSettled([
11481
+ rgbClient.node.getNodeInfo(),
11482
+ rgbClient.onchain.getBtcBalance({
11483
+ skip_sync: false
11484
+ }),
11485
+ rgbClient.lightning.listPeers({})
11486
+ ]);
11487
+ if (nodeInfoResult.status === "fulfilled") {
11488
+ nodeInfo = nodeInfoResult.value;
11489
+ } else {
11490
+ logger2.warn(`getCombinedNodeInfo - getNodeInfo failed: ${nodeInfoResult.reason?.message || nodeInfoResult.reason}`);
11491
+ }
11492
+ if (balanceResult.status === "fulfilled") {
11493
+ balance = balanceResult.value;
11494
+ } else {
11495
+ logger2.warn(`getCombinedNodeInfo - getBtcBalance failed: ${balanceResult.reason?.message || balanceResult.reason}`);
11496
+ }
11497
+ if (peersResult.status === "fulfilled") {
11498
+ peers = peersResult.value?.peers ?? [];
11499
+ } else {
11500
+ logger2.warn(`getCombinedNodeInfo - listPeers failed: ${peersResult.reason?.message || peersResult.reason}`);
11501
+ }
11502
+ rgbClient.rgb.refreshTransfers({ skip_sync: false }).catch((error) => {
11503
+ logger2.warn(`getCombinedNodeInfo - refreshTransfers failed: ${error.message}`);
11504
+ });
11353
11505
  }
11354
- rgbClient.rgb.refreshTransfers({ skip_sync: false }).catch((error) => {
11355
- logger2.warn(`getCombinedNodeInfo - refreshTransfers failed: ${error.message}`);
11356
- });
11357
11506
  }
11358
11507
  const readOnlyAccount = await getLnlinkUser({
11359
11508
  account_type: ACCOUNT_TYPE.READ_ONLY
@@ -13013,7 +13162,7 @@ var require_eventProcessor = __commonJS({
13013
13162
  __name(parseAndExecute, "parseAndExecute");
13014
13163
  async function parseEvent(event, sk) {
13015
13164
  const content = event.content;
13016
- const subFrom = event.from;
13165
+ const subFrom = event.from || event.pubkey;
13017
13166
  const decryptContent = await nip04.decrypt(sk, subFrom, content);
13018
13167
  if (event.kind === NWC_CONSTANTS.REQUEST_KIND) {
13019
13168
  await handleNWCRequest(event, sk, subFrom, decryptContent);
@@ -13062,21 +13211,25 @@ var require_eventProcessor = __commonJS({
13062
13211
  replyEventId: event.event_id,
13063
13212
  kind: NWC_CONSTANTS.RESPONSE_KIND
13064
13213
  });
13065
- const updateSuccess = await safeUpdateEvent(event.id, {
13066
- status: EVENT_HANDLE_STATUS.SUCCESS,
13067
- reply_event_id: replyEvent.id
13068
- });
13069
- if (!updateSuccess) {
13070
- throw new Error("Failed to update event status to success");
13214
+ if (typeof event.id === "number") {
13215
+ const updateId = event.event_id || event.id;
13216
+ const updateSuccess = await safeUpdateEvent(updateId, {
13217
+ status: EVENT_HANDLE_STATUS.SUCCESS,
13218
+ reply_event_id: replyEvent.id
13219
+ });
13220
+ if (!updateSuccess) {
13221
+ const logger2 = new Logger("nostr-events");
13222
+ logger2.warn(`Failed to update event ${updateId} status, possibly not in DB yet`);
13223
+ }
13071
13224
  }
13072
13225
  }
13073
13226
  __name(handleNWCRequest, "handleNWCRequest");
13074
13227
  async function handleDirectMessage(event, sk, subFrom, decryptContent) {
13075
- const tags = JSON.parse(event.tags);
13228
+ const tags = typeof event.tags === "string" ? JSON.parse(event.tags) : event.tags;
13076
13229
  const replyProxyAddr = tags?.find((item) => item?.[0] === "a")?.[1];
13077
13230
  const replyTo = replyProxyAddr || subFrom;
13078
13231
  const content = decryptContent;
13079
- const retMsg = await parseAndExecute(content, subFrom, event.event_id);
13232
+ const retMsg = await parseAndExecute(content, subFrom, event.event_id || event.id);
13080
13233
  if (retMsg) {
13081
13234
  const replyEvent = await execSendMessage({
13082
13235
  message: retMsg,
@@ -13084,12 +13237,16 @@ var require_eventProcessor = __commonJS({
13084
13237
  robotPrivatekey: sk,
13085
13238
  replyEventId: event.event_id
13086
13239
  });
13087
- const updateSuccess = await safeUpdateEvent(event.id, {
13088
- status: EVENT_HANDLE_STATUS.SUCCESS,
13089
- reply_event_id: replyEvent.id
13090
- });
13091
- if (!updateSuccess) {
13092
- throw new Error("Failed to update event status to success");
13240
+ if (typeof event.id === "number") {
13241
+ const updateId = event.event_id || event.id;
13242
+ const updateSuccess = await safeUpdateEvent(updateId, {
13243
+ status: EVENT_HANDLE_STATUS.SUCCESS,
13244
+ reply_event_id: replyEvent.id
13245
+ });
13246
+ if (!updateSuccess) {
13247
+ const logger2 = new Logger("nostr-events");
13248
+ logger2.warn(`Failed to update event ${updateId} status, possibly not in DB yet`);
13249
+ }
13093
13250
  }
13094
13251
  }
13095
13252
  }
@@ -13129,28 +13286,33 @@ var require_eventProcessor = __commonJS({
13129
13286
  }
13130
13287
  __name(safeUpdateEvent, "safeUpdateEvent");
13131
13288
  async function processSingleEvent(event, sk) {
13132
- const eventId = event.id;
13289
+ const eventId = event.event_id || event.id;
13133
13290
  const logger2 = new Logger("nostr-events");
13291
+ const isFromMemoryQueue = !event.isRecovery && typeof event.id === "number";
13134
13292
  try {
13135
- const claimed = await safeUpdateEvent(eventId, {
13136
- status: EVENT_HANDLE_STATUS.REPLYING
13137
- });
13138
- if (!claimed) {
13139
- logger2.debug(`Event ${eventId} could not be claimed, possibly already processing`);
13140
- return false;
13293
+ if (!isFromMemoryQueue && typeof event.id === "number") {
13294
+ const claimed = await safeUpdateEvent(eventId, {
13295
+ status: EVENT_HANDLE_STATUS.REPLYING
13296
+ });
13297
+ if (!claimed) {
13298
+ logger2.debug(`Event ${eventId} could not be claimed, possibly already processing`);
13299
+ return false;
13300
+ }
13301
+ logger2.debug(`Successfully claimed event ${eventId} for processing`);
13141
13302
  }
13142
- logger2.debug(`Successfully claimed event ${eventId} for processing`);
13143
13303
  await parseEvent(event, sk);
13144
13304
  return true;
13145
13305
  } catch (e) {
13146
13306
  logger2.error(`Error processing event ${eventId}: ${e.message}`);
13147
- try {
13148
- await safeUpdateEvent(eventId, {
13149
- status: EVENT_HANDLE_STATUS.ERROR,
13150
- reply_err: e.message
13151
- });
13152
- } catch (updateError) {
13153
- logger2.error(`Failed to update event ${eventId} status to error: ${updateError.message}`);
13307
+ if (!isFromMemoryQueue && typeof event.id === "number") {
13308
+ try {
13309
+ await safeUpdateEvent(eventId, {
13310
+ status: EVENT_HANDLE_STATUS.ERROR,
13311
+ reply_err: e.message
13312
+ });
13313
+ } catch (updateError) {
13314
+ logger2.error(`Failed to update event ${eventId} status to error: ${updateError.message}`);
13315
+ }
13154
13316
  }
13155
13317
  return false;
13156
13318
  }
@@ -13244,7 +13406,7 @@ var require_eventLoop = __commonJS({
13244
13406
  this.recordError();
13245
13407
  if (error.message.includes("timeout")) {
13246
13408
  try {
13247
- await updateEvent(event.id, {
13409
+ await updateEvent(event.event_id || event.id, {
13248
13410
  status: EVENT_HANDLE_STATUS.ERROR,
13249
13411
  reply_err: error.message
13250
13412
  });
@@ -13347,7 +13509,10 @@ var require_eventLoop = __commonJS({
13347
13509
  }
13348
13510
  };
13349
13511
  async function intervalParseEvent(stopSignal = { stop: false }) {
13350
- const rateLimiter = new RateLimiter(2, 1e3);
13512
+ const rateLimiter = new RateLimiter(
13513
+ EVENT_PROCESSING_CONSTANTS.RATE_LIMIT_REQUESTS || 10,
13514
+ EVENT_PROCESSING_CONSTANTS.RATE_LIMIT_WINDOW || 1e3
13515
+ );
13351
13516
  const processor = new ConcurrentEventProcessor(
13352
13517
  EVENT_PROCESSING_CONSTANTS.MAX_CONCURRENT_EVENTS,
13353
13518
  rateLimiter
@@ -13436,6 +13601,396 @@ var require_eventLoop = __commonJS({
13436
13601
  }
13437
13602
  });
13438
13603
 
13604
+ // nostr/events/eventQueue.js
13605
+ var require_eventQueue = __commonJS({
13606
+ "nostr/events/eventQueue.js"(exports2, module2) {
13607
+ var { EventEmitter: EventEmitter2 } = require("node:events");
13608
+ var Logger = require_linkLogger();
13609
+ var PRIORITY = {
13610
+ HIGH: 1,
13611
+ // NWC requests (23194) - user payments
13612
+ NORMAL: 2,
13613
+ // Direct messages (4) - user commands
13614
+ LOW: 3
13615
+ // Other events
13616
+ };
13617
+ function getPriorityForKind(kind) {
13618
+ switch (kind) {
13619
+ case 23194:
13620
+ return PRIORITY.HIGH;
13621
+ case 4:
13622
+ return PRIORITY.NORMAL;
13623
+ default:
13624
+ return PRIORITY.LOW;
13625
+ }
13626
+ }
13627
+ __name(getPriorityForKind, "getPriorityForKind");
13628
+ var EventQueue = class extends EventEmitter2 {
13629
+ static {
13630
+ __name(this, "EventQueue");
13631
+ }
13632
+ constructor(options = {}) {
13633
+ super();
13634
+ this.maxSize = options.maxSize || 1e3;
13635
+ this.highPriorityQueue = [];
13636
+ this.normalPriorityQueue = [];
13637
+ this.lowPriorityQueue = [];
13638
+ this.processingSet = /* @__PURE__ */ new Set();
13639
+ this.logger = new Logger("event-queue");
13640
+ this.stats = {
13641
+ enqueued: 0,
13642
+ processed: 0,
13643
+ dropped: 0,
13644
+ duplicates: 0
13645
+ };
13646
+ }
13647
+ /**
13648
+ * Get total queue size
13649
+ */
13650
+ get size() {
13651
+ return this.highPriorityQueue.length + this.normalPriorityQueue.length + this.lowPriorityQueue.length;
13652
+ }
13653
+ /**
13654
+ * Check if event is already in queue or being processed
13655
+ * @param {string} eventId - Event ID
13656
+ */
13657
+ has(eventId) {
13658
+ if (this.processingSet.has(eventId)) {
13659
+ return true;
13660
+ }
13661
+ return this.highPriorityQueue.some((e) => e.id === eventId) || this.normalPriorityQueue.some((e) => e.id === eventId) || this.lowPriorityQueue.some((e) => e.id === eventId);
13662
+ }
13663
+ /**
13664
+ * Add event to queue
13665
+ * @param {object} event - Event object with id, kind, content, etc.
13666
+ * @param {object} options - Additional options
13667
+ * @returns {boolean} Whether event was added
13668
+ */
13669
+ enqueue(event, options = {}) {
13670
+ const eventId = event.id || event.event_id;
13671
+ if (this.has(eventId)) {
13672
+ this.stats.duplicates++;
13673
+ return false;
13674
+ }
13675
+ if (this.size >= this.maxSize) {
13676
+ if (this.lowPriorityQueue.length > 0) {
13677
+ const dropped = this.lowPriorityQueue.shift();
13678
+ this.logger.warn(`Queue full, dropping low priority event: ${dropped.id}`);
13679
+ this.stats.dropped++;
13680
+ } else if (this.normalPriorityQueue.length > 0) {
13681
+ const dropped = this.normalPriorityQueue.shift();
13682
+ this.logger.warn(`Queue full, dropping normal priority event: ${dropped.id}`);
13683
+ this.stats.dropped++;
13684
+ } else {
13685
+ this.logger.error(`Queue full with only high priority events, dropping new event: ${eventId}`);
13686
+ this.stats.dropped++;
13687
+ return false;
13688
+ }
13689
+ }
13690
+ const priority = options.priority || getPriorityForKind(event.kind);
13691
+ const queueItem = {
13692
+ ...event,
13693
+ id: eventId,
13694
+ enqueuedAt: Date.now(),
13695
+ priority,
13696
+ ...options
13697
+ };
13698
+ switch (priority) {
13699
+ case PRIORITY.HIGH:
13700
+ this.highPriorityQueue.push(queueItem);
13701
+ break;
13702
+ case PRIORITY.NORMAL:
13703
+ this.normalPriorityQueue.push(queueItem);
13704
+ break;
13705
+ default:
13706
+ this.lowPriorityQueue.push(queueItem);
13707
+ }
13708
+ this.stats.enqueued++;
13709
+ this.emit("enqueue", queueItem);
13710
+ return true;
13711
+ }
13712
+ /**
13713
+ * Get next event to process (respects priority)
13714
+ * @returns {object|null} Next event or null if empty
13715
+ */
13716
+ dequeue() {
13717
+ let event = null;
13718
+ if (this.highPriorityQueue.length > 0) {
13719
+ event = this.highPriorityQueue.shift();
13720
+ } else if (this.normalPriorityQueue.length > 0) {
13721
+ event = this.normalPriorityQueue.shift();
13722
+ } else if (this.lowPriorityQueue.length > 0) {
13723
+ event = this.lowPriorityQueue.shift();
13724
+ }
13725
+ if (event) {
13726
+ this.processingSet.add(event.id);
13727
+ }
13728
+ return event;
13729
+ }
13730
+ /**
13731
+ * Mark event as done processing
13732
+ * @param {string} eventId - Event ID
13733
+ */
13734
+ markDone(eventId) {
13735
+ this.processingSet.delete(eventId);
13736
+ this.stats.processed++;
13737
+ }
13738
+ /**
13739
+ * Get multiple events at once
13740
+ * @param {number} count - Number of events to get
13741
+ * @returns {Array} Array of events
13742
+ */
13743
+ dequeueBatch(count) {
13744
+ const events = [];
13745
+ for (let i = 0; i < count; i++) {
13746
+ const event = this.dequeue();
13747
+ if (!event)
13748
+ break;
13749
+ events.push(event);
13750
+ }
13751
+ return events;
13752
+ }
13753
+ /**
13754
+ * Get queue statistics
13755
+ * @returns {object} Queue statistics object
13756
+ */
13757
+ getStats() {
13758
+ return {
13759
+ ...this.stats,
13760
+ currentSize: this.size,
13761
+ highPriority: this.highPriorityQueue.length,
13762
+ normalPriority: this.normalPriorityQueue.length,
13763
+ lowPriority: this.lowPriorityQueue.length,
13764
+ processing: this.processingSet.size
13765
+ };
13766
+ }
13767
+ /**
13768
+ * Clear all queues
13769
+ */
13770
+ clear() {
13771
+ this.highPriorityQueue = [];
13772
+ this.normalPriorityQueue = [];
13773
+ this.lowPriorityQueue = [];
13774
+ this.processingSet.clear();
13775
+ }
13776
+ };
13777
+ var queueInstance = null;
13778
+ function getEventQueue() {
13779
+ if (!queueInstance) {
13780
+ queueInstance = new EventQueue({ maxSize: 1e3 });
13781
+ }
13782
+ return queueInstance;
13783
+ }
13784
+ __name(getEventQueue, "getEventQueue");
13785
+ module2.exports = {
13786
+ EventQueue,
13787
+ getEventQueue,
13788
+ PRIORITY,
13789
+ getPriorityForKind
13790
+ };
13791
+ }
13792
+ });
13793
+
13794
+ // nostr/events/immediateProcessor.js
13795
+ var require_immediateProcessor = __commonJS({
13796
+ "nostr/events/immediateProcessor.js"(exports2, module2) {
13797
+ var { EventEmitter: EventEmitter2 } = require("node:events");
13798
+ var {
13799
+ updateEvent,
13800
+ getUnReplyEvents
13801
+ } = require_dbService();
13802
+ var { sleep } = require_utils2();
13803
+ var Logger = require_linkLogger();
13804
+ var { EVENT_HANDLE_STATUS } = require_constants();
13805
+ var { EVENT_PROCESSING_CONSTANTS } = require_constants2();
13806
+ var { processSingleEvent } = require_eventProcessor();
13807
+ var { getEventQueue, PRIORITY } = require_eventQueue();
13808
+ var ImmediateProcessor = class extends EventEmitter2 {
13809
+ static {
13810
+ __name(this, "ImmediateProcessor");
13811
+ }
13812
+ constructor(options = {}) {
13813
+ super();
13814
+ this.maxWorkers = options.maxWorkers || EVENT_PROCESSING_CONSTANTS.MAX_CONCURRENT_EVENTS;
13815
+ this.activeWorkers = 0;
13816
+ this.isRunning = false;
13817
+ this.queue = getEventQueue();
13818
+ this.logger = new Logger("immediate-processor");
13819
+ this.sk = null;
13820
+ this.stats = {
13821
+ processed: 0,
13822
+ succeeded: 0,
13823
+ failed: 0,
13824
+ timeouts: 0
13825
+ };
13826
+ this.processingMap = /* @__PURE__ */ new Map();
13827
+ }
13828
+ /**
13829
+ * Start the processor
13830
+ * @param {string} sk - Private key for signing responses
13831
+ */
13832
+ start(sk) {
13833
+ if (this.isRunning) {
13834
+ this.logger.warn("Processor already running");
13835
+ return;
13836
+ }
13837
+ this.sk = sk;
13838
+ this.isRunning = true;
13839
+ this.logger.info(`ImmediateProcessor started with ${this.maxWorkers} workers`);
13840
+ this.queue.on("enqueue", () => this.tryProcessNext());
13841
+ this.startDBRecoveryLoop();
13842
+ }
13843
+ /**
13844
+ * Stop the processor
13845
+ */
13846
+ async stop() {
13847
+ this.isRunning = false;
13848
+ this.queue.removeAllListeners("enqueue");
13849
+ const timeout = 3e4;
13850
+ const startTime = Date.now();
13851
+ while (this.activeWorkers > 0 && Date.now() - startTime < timeout) {
13852
+ this.logger.info(`Waiting for ${this.activeWorkers} workers to complete...`);
13853
+ await sleep(1e3);
13854
+ }
13855
+ if (this.activeWorkers > 0) {
13856
+ this.logger.warn(`Timeout waiting for ${this.activeWorkers} workers`);
13857
+ }
13858
+ this.logger.info("ImmediateProcessor stopped");
13859
+ }
13860
+ /**
13861
+ * Try to start processing next event if workers available
13862
+ */
13863
+ tryProcessNext() {
13864
+ if (!this.isRunning) {
13865
+ return;
13866
+ }
13867
+ while (this.activeWorkers < this.maxWorkers && this.queue.size > 0) {
13868
+ const event = this.queue.dequeue();
13869
+ if (event) {
13870
+ this.processEvent(event);
13871
+ } else {
13872
+ break;
13873
+ }
13874
+ }
13875
+ }
13876
+ /**
13877
+ * Process a single event
13878
+ * @param {object} event - Event to process
13879
+ */
13880
+ async processEvent(event) {
13881
+ this.activeWorkers++;
13882
+ const eventId = event.id || event.event_id;
13883
+ const startTime = Date.now();
13884
+ this.processingMap.set(eventId, startTime);
13885
+ this.logger.debug(`Processing event ${eventId} (workers: ${this.activeWorkers}/${this.maxWorkers})`);
13886
+ try {
13887
+ let timeoutId;
13888
+ const timeoutPromise = new Promise((_, reject) => {
13889
+ timeoutId = setTimeout(
13890
+ () => reject(new Error("Event processing timeout")),
13891
+ EVENT_PROCESSING_CONSTANTS.EVENT_PROCESSING_TIMEOUT
13892
+ );
13893
+ });
13894
+ try {
13895
+ await Promise.race([
13896
+ processSingleEvent(event, this.sk),
13897
+ timeoutPromise
13898
+ ]);
13899
+ this.stats.succeeded++;
13900
+ this.logger.debug(`Event ${eventId} processed in ${Date.now() - startTime}ms`);
13901
+ } finally {
13902
+ clearTimeout(timeoutId);
13903
+ }
13904
+ } catch (error) {
13905
+ this.stats.failed++;
13906
+ this.logger.error(`Failed to process event ${eventId}: ${error.message}`);
13907
+ if (error.message.includes("timeout")) {
13908
+ this.stats.timeouts++;
13909
+ try {
13910
+ await updateEvent(event.id, {
13911
+ status: EVENT_HANDLE_STATUS.ERROR,
13912
+ reply_err: error.message
13913
+ });
13914
+ } catch (updateError) {
13915
+ this.logger.error(`Failed to update event status: ${updateError.message}`);
13916
+ }
13917
+ }
13918
+ } finally {
13919
+ this.stats.processed++;
13920
+ this.processingMap.delete(eventId);
13921
+ this.queue.markDone(eventId);
13922
+ this.activeWorkers--;
13923
+ this.tryProcessNext();
13924
+ }
13925
+ }
13926
+ /**
13927
+ * Add event directly (called from connection manager)
13928
+ * @param {object} event - Raw nostr event
13929
+ * @param {object} options - Additional options
13930
+ * @returns {boolean} Whether event was added
13931
+ */
13932
+ addEvent(event, options = {}) {
13933
+ return this.queue.enqueue(event, options);
13934
+ }
13935
+ /**
13936
+ * Start DB recovery loop to catch missed events
13937
+ */
13938
+ startDBRecoveryLoop() {
13939
+ const recoveryLoop = /* @__PURE__ */ __name(async () => {
13940
+ while (this.isRunning) {
13941
+ try {
13942
+ if (this.queue.size < EVENT_PROCESSING_CONSTANTS.MEMORY_QUEUE_MAX_SIZE / 2) {
13943
+ const missedEvents = await getUnReplyEvents(
13944
+ EVENT_PROCESSING_CONSTANTS.BATCH_SIZE
13945
+ );
13946
+ for (const event of missedEvents) {
13947
+ this.queue.enqueue(event, { priority: PRIORITY.LOW, isRecovery: true });
13948
+ }
13949
+ if (missedEvents.length > 0) {
13950
+ this.logger.debug(`Recovered ${missedEvents.length} events from DB`);
13951
+ this.tryProcessNext();
13952
+ }
13953
+ }
13954
+ } catch (error) {
13955
+ this.logger.error(`DB recovery error: ${error.message}`);
13956
+ }
13957
+ await sleep(EVENT_PROCESSING_CONSTANTS.DB_RECOVERY_INTERVAL);
13958
+ }
13959
+ }, "recoveryLoop");
13960
+ recoveryLoop();
13961
+ }
13962
+ /**
13963
+ * Get processor statistics
13964
+ * @returns {object} Statistics object
13965
+ */
13966
+ getStats() {
13967
+ return {
13968
+ ...this.stats,
13969
+ activeWorkers: this.activeWorkers,
13970
+ maxWorkers: this.maxWorkers,
13971
+ queueStats: this.queue.getStats(),
13972
+ processing: Array.from(this.processingMap.entries()).map(([id, time]) => ({
13973
+ id,
13974
+ duration: Date.now() - time
13975
+ }))
13976
+ };
13977
+ }
13978
+ };
13979
+ var processorInstance = null;
13980
+ function getImmediateProcessor() {
13981
+ if (!processorInstance) {
13982
+ processorInstance = new ImmediateProcessor();
13983
+ }
13984
+ return processorInstance;
13985
+ }
13986
+ __name(getImmediateProcessor, "getImmediateProcessor");
13987
+ module2.exports = {
13988
+ ImmediateProcessor,
13989
+ getImmediateProcessor
13990
+ };
13991
+ }
13992
+ });
13993
+
13439
13994
  // nostr/connection/connectionManager.js
13440
13995
  var require_connectionManager = __commonJS({
13441
13996
  "nostr/connection/connectionManager.js"(exports2, module2) {
@@ -13457,6 +14012,8 @@ var require_connectionManager = __commonJS({
13457
14012
  CONNECTION_CONSTANTS
13458
14013
  } = require_constants2();
13459
14014
  var { intervalParseEvent } = require_eventLoop();
14015
+ var { getEventQueue, PRIORITY } = require_eventQueue();
14016
+ var { getImmediateProcessor } = require_immediateProcessor();
13460
14017
  var {
13461
14018
  getRelays,
13462
14019
  execSendMessage,
@@ -13729,6 +14286,7 @@ var require_connectionManager = __commonJS({
13729
14286
  };
13730
14287
  function createEventHandler(lndConfig, manager) {
13731
14288
  const logger2 = new Logger("nostr");
14289
+ const eventQueue = getEventQueue();
13732
14290
  return async (event) => {
13733
14291
  try {
13734
14292
  manager.lastEventReceived = Date.now();
@@ -13743,18 +14301,25 @@ var require_connectionManager = __commonJS({
13743
14301
  pubkey: event.pubkey
13744
14302
  });
13745
14303
  if (user) {
13746
- const ret = await insertEvent(event).catch((e) => {
13747
- logger2.error(`Nostr eventHandler ${event.id} error: ${e.message}`);
13748
- return 0;
14304
+ const priority = event.kind === 23194 ? PRIORITY.HIGH : PRIORITY.NORMAL;
14305
+ const enqueued = eventQueue.enqueue(event, { priority });
14306
+ insertEvent(event).then((ret) => {
14307
+ if (ret > 0) {
14308
+ logger2.debug(`Event ${event.id} persisted to DB`);
14309
+ }
14310
+ }).catch((e) => {
14311
+ logger2.error(`Nostr eventHandler ${event.id} DB error: ${e.message}`);
13749
14312
  });
13750
- logger2.info(
13751
- `Nostr event:${event.id} stored ${ret > 0 ? "success" : "failed"}`
13752
- );
14313
+ if (enqueued) {
14314
+ logger2.info(`Event ${event.id} queued for immediate processing (priority: ${priority})`);
14315
+ } else {
14316
+ logger2.warn(`Event ${event.id} duplicate or queue full`);
14317
+ }
13753
14318
  } else {
13754
14319
  const tags = event.tags;
13755
14320
  const replyProxyAddr = tags?.find((item) => item?.[0] === "a")?.[1];
13756
14321
  const replyTo = replyProxyAddr || event.pubkey;
13757
- await execSendMessage({
14322
+ execSendMessage({
13758
14323
  message: JSON.stringify({
13759
14324
  code: ERROR_CODES.FORBIDDEN,
13760
14325
  data: null,
@@ -13865,11 +14430,14 @@ var require_connectionManager = __commonJS({
13865
14430
  }
13866
14431
  }, 45e3);
13867
14432
  const stopSignal = { stop: false };
14433
+ const immediateProcessor = getImmediateProcessor();
14434
+ immediateProcessor.start(lndConfig.node_sk);
13868
14435
  intervalParseEvent(stopSignal);
13869
14436
  manager.addService({
13870
- stop: /* @__PURE__ */ __name(() => {
14437
+ stop: /* @__PURE__ */ __name(async () => {
13871
14438
  stopSignal.stop = true;
13872
14439
  clearInterval(monitorInterval);
14440
+ await immediateProcessor.stop();
13873
14441
  }, "stop")
13874
14442
  });
13875
14443
  return {
@@ -18579,7 +19147,8 @@ var require_setting_regtest = __commonJS({
18579
19147
  bitcoindZmqRawTx: "tcp://regtest.lnfi.network:28335",
18580
19148
  network: "regtest",
18581
19149
  nostrRelays: [
18582
- "wss://relay01.lnfi.network"
19150
+ "wss://relay01.lnfi.network",
19151
+ "wss://relay02.lnfi.network"
18583
19152
  ],
18584
19153
  officialLndPeer: "03b24a4bf911ffd26ac1d5e5f2440a3c2f6974e4cc85d2ef54e17ee6d3717433d3",
18585
19154
  officialLndPeerHost: "34.84.66.29:7739",
@@ -18761,7 +19330,7 @@ var LnLink = class extends EventEmitter {
18761
19330
  */
18762
19331
  _ensureEnvironmentAndPrisma() {
18763
19332
  if (!process.env.LINK_DATABASE_URL) {
18764
- const dbPath = path.join(this.options.dataPath, "link", "lnlink.db");
19333
+ const dbPath = path.join(this.options.dataPath, ".link", "lnlink.db");
18765
19334
  process.env.LINK_DATABASE_URL = `file:${dbPath}`;
18766
19335
  console.log(`\u{1F5C4}\uFE0F Setting database URL: ${dbPath}`);
18767
19336
  }