lnlink-server 1.0.2 → 1.0.4

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.
@@ -1,15 +1,18 @@
1
+ /* eslint-disable unused-imports/no-unused-vars */
2
+ /* eslint-disable no-unused-vars */
1
3
  /* global bootstrap */
2
4
 
3
5
  // Global toggle state
4
- let isAdvancedConfigVisible = false;
6
+ let isAdvancedConfigVisible = false
5
7
 
6
8
  // Function to open external URL in default browser
7
9
  function openExternalUrl(url) {
8
10
  if (window.electronAPI && window.electronAPI.openExternal) {
9
- window.electronAPI.openExternal(url);
10
- } else {
11
+ window.electronAPI.openExternal(url)
12
+ }
13
+ else {
11
14
  // Fallback for non-Electron environments
12
- window.open(url, "_blank");
15
+ window.open(url, "_blank")
13
16
  }
14
17
  }
15
18
 
@@ -24,10 +27,10 @@ const defaultNetworkConfigs = {
24
27
  mainnet: {
25
28
  network: "mainnet",
26
29
  },
27
- };
30
+ }
28
31
 
29
32
  // Initialize networkConfigs with default values
30
- let networkConfigs = JSON.parse(JSON.stringify(defaultNetworkConfigs));
33
+ let networkConfigs = JSON.parse(JSON.stringify(defaultNetworkConfigs))
31
34
 
32
35
  // Fetch network configurations from API
33
36
  async function fetchNetworkConfigs() {
@@ -41,16 +44,17 @@ async function fetchNetworkConfigs() {
41
44
  },
42
45
  body: JSON.stringify({}),
43
46
  },
44
- );
47
+ )
45
48
 
46
- const result = await response.json();
49
+ const result = await response.json()
47
50
 
48
51
  if (result.code === "0" && result.data && Array.isArray(result.data)) {
49
- const apiConfigs = {};
52
+ const apiConfigs = {}
50
53
 
51
54
  result.data.forEach((item) => {
52
- if (!item.network) return;
53
- const networkKey = item.network.toLowerCase();
55
+ if (!item.network)
56
+ return
57
+ const networkKey = item.network.toLowerCase()
54
58
 
55
59
  apiConfigs[networkKey] = {
56
60
  bitcoindIndex: item.rgbIndex,
@@ -70,24 +74,26 @@ async function fetchNetworkConfigs() {
70
74
  officialUniverseServer: item.lndUniverseServer,
71
75
  priceOracle: item.lndPriceOracle,
72
76
  rgbProxy: item.rgbProxy,
73
- };
74
- });
77
+ }
78
+ })
75
79
 
76
80
  // Update global configs, strictly using API response if valid
77
81
  if (Object.keys(apiConfigs).length > 0) {
78
- networkConfigs = apiConfigs;
79
- console.log("Network configs updated from API");
82
+ networkConfigs = apiConfigs
83
+ console.log("Network configs updated from API")
80
84
  }
81
- } else {
85
+ }
86
+ else {
82
87
  console.warn(
83
88
  "API returned success but no data or invalid format, using default configs.",
84
- );
89
+ )
85
90
  }
86
- } catch (error) {
91
+ }
92
+ catch (error) {
87
93
  console.error(
88
94
  "Error fetching network configs, using default configs:",
89
95
  error,
90
- );
96
+ )
91
97
  }
92
98
  }
93
99
 
@@ -115,149 +121,157 @@ const fieldLabels = {
115
121
  nostrRelays: "Relays",
116
122
  network: "Network Type",
117
123
  owner: "Owner Address",
118
- };
124
+ }
119
125
 
120
126
  // Show error modal
121
127
  function showErrorModal(message) {
122
- const errorModalBody = document.getElementById("errorModalBody");
123
- errorModalBody.textContent = message;
128
+ const errorModalBody = document.getElementById("errorModalBody")
129
+ errorModalBody.textContent = message
124
130
  if (typeof bootstrap !== "undefined") {
125
131
  const errorModal = new bootstrap.Modal(
126
132
  document.getElementById("errorModal"),
127
- );
128
- errorModal.show();
129
- } else {
130
- console.error("Error:", message);
133
+ )
134
+ errorModal.show()
135
+ }
136
+ else {
137
+ console.error("Error:", message)
131
138
  }
132
139
  }
133
140
 
134
141
  // Show success modal
135
142
  function showSuccessModal(message) {
136
- const successModalBody = document.getElementById("successModalBody");
137
- successModalBody.textContent = message;
143
+ const successModalBody = document.getElementById("successModalBody")
144
+ successModalBody.textContent = message
138
145
  if (typeof bootstrap !== "undefined") {
139
146
  const successModal = new bootstrap.Modal(
140
147
  document.getElementById("successModal"),
141
- );
142
- successModal.show();
143
- } else {
144
- console.log("Success:", message);
148
+ )
149
+ successModal.show()
150
+ }
151
+ else {
152
+ console.log("Success:", message)
145
153
  }
146
154
  }
147
155
 
148
156
  // Status determination function
149
157
  function getStatusClass(value) {
150
- const strValue = String(value).toLowerCase();
158
+ const strValue = String(value).toLowerCase()
151
159
  if (
152
- strValue === "true" ||
153
- strValue === "running" ||
154
- strValue === "active" ||
155
- strValue === "online"
160
+ strValue === "true"
161
+ || strValue === "running"
162
+ || strValue === "active"
163
+ || strValue === "online"
156
164
  ) {
157
- return { class: "status-success", icon: "fas fa-check-circle" };
158
- } else if (
159
- strValue === "false" ||
160
- strValue === "stopped" ||
161
- strValue === "inactive" ||
162
- strValue === "offline"
165
+ return { class: "status-success", icon: "fas fa-check-circle" }
166
+ }
167
+ else if (
168
+ strValue === "false"
169
+ || strValue === "stopped"
170
+ || strValue === "inactive"
171
+ || strValue === "offline"
163
172
  ) {
164
- return { class: "status-error", icon: "fas fa-times-circle" };
165
- } else if (strValue.includes("pending") || strValue.includes("waiting")) {
166
- return { class: "status-warning", icon: "fas fa-clock" };
173
+ return { class: "status-error", icon: "fas fa-times-circle" }
174
+ }
175
+ else if (strValue.includes("pending") || strValue.includes("waiting")) {
176
+ return { class: "status-warning", icon: "fas fa-clock" }
167
177
  }
168
- return { class: "", icon: "fas fa-info-circle" };
178
+ return { class: "", icon: "fas fa-info-circle" }
169
179
  }
170
180
 
171
181
  // Get system information
172
182
  async function getInfo(showLoading = true) {
173
- const mainContent = document.getElementById("main-content");
183
+ const mainContent = document.getElementById("main-content")
174
184
 
175
185
  // Show loading state
176
186
  if (showLoading) {
177
- mainContent.innerHTML =
178
- '<div class="loading-container"><div class="loading"></div><div>Loading information...</div></div>';
187
+ mainContent.innerHTML
188
+ = "<div class=\"loading-container\"><div class=\"loading\"></div><div>Loading information...</div></div>"
179
189
  }
180
190
 
181
191
  try {
182
- const res = await fetch("/api/lnd/info").then((res) => res.json());
192
+ const res = await fetch("/api/lnd/info").then(res => res.json())
183
193
 
184
194
  if (res.code === 200) {
185
195
  if (res.data) {
186
- const { owner, settings } = res.data;
196
+ const { owner, settings } = res.data
187
197
 
188
198
  // If both owner and settings exist, show configured view
189
199
  if (owner && settings) {
190
- renderConfiguredView(res.data, settings);
200
+ renderConfiguredView(res.data, settings)
191
201
  }
192
202
  // If missing owner or settings, show configuration form
193
203
  else {
194
204
  // Fetch network configs only when needed for configuration
195
- await fetchNetworkConfigs();
205
+ await fetchNetworkConfigs()
196
206
 
197
207
  fetch("https://api.example.com/login", {
198
208
  method: "POST",
199
209
  headers: {
200
210
  "Content-Type": "application/json",
201
- Authorization: "Bearer your-token-here", // 可选
211
+ "Authorization": "Bearer your-token-here", // 可选
202
212
  },
203
213
  body: JSON.stringify({
204
214
  username: "test",
205
215
  password: "123456",
206
216
  }),
207
- });
208
- renderConfigurationForm(res.data);
217
+ })
218
+ renderConfigurationForm(res.data)
209
219
  }
210
220
  }
211
- } else if (
212
- res.code === 500 &&
213
- res.message === "Please init the wallet or init owner first"
221
+ }
222
+ else if (
223
+ res.code === 500
224
+ && res.message === "Please init the wallet or init owner first"
214
225
  ) {
215
226
  // Fetch network configs only when needed for configuration
216
- await fetchNetworkConfigs();
227
+ await fetchNetworkConfigs()
217
228
 
218
229
  // If wallet/owner not initialized, show configuration form
219
- renderConfigurationForm({});
220
- } else {
221
- mainContent.innerHTML =
222
- '<div class="alert alert-danger"><i class="fas fa-exclamation-triangle"></i> Failed to load information</div>';
230
+ renderConfigurationForm({})
231
+ }
232
+ else {
233
+ mainContent.innerHTML
234
+ = "<div class=\"alert alert-danger\"><i class=\"fas fa-exclamation-triangle\"></i> Failed to load information</div>"
223
235
  }
224
- } catch (error) {
225
- console.error("Failed to get info:", error);
226
- mainContent.innerHTML =
227
- '<div class="alert alert-danger"><i class="fas fa-exclamation-triangle"></i> Network error occurred</div>';
236
+ }
237
+ catch (error) {
238
+ console.error("Failed to get info:", error)
239
+ mainContent.innerHTML
240
+ = "<div class=\"alert alert-danger\"><i class=\"fas fa-exclamation-triangle\"></i> Network error occurred</div>"
228
241
  }
229
242
  }
230
243
 
231
244
  // Render configured view
232
245
  function renderConfiguredView(basicData, settings) {
233
- const mainContent = document.getElementById("main-content");
246
+ const mainContent = document.getElementById("main-content")
234
247
 
235
248
  // Extract Node ID (Nostr PubKey)
236
- const nodeId =
237
- settings.officialNostrPubKey || basicData.officialNostrPubKey || "Unknown";
249
+ const nodeId
250
+ = settings.officialNostrPubKey || basicData.officialNostrPubKey || "Unknown"
238
251
 
239
252
  // Determine Service Status
240
253
  // We check basicData for status indicators or default to design mocks if not present
241
254
  // Assuming keys might exist, otherwise using placeholders for the design requirement
242
- const rgbStatus = basicData.rgb || "Stopped"; // Mock/Default based on design
243
- const litdStatus = basicData.litd || "Stopped"; // Mock/Default based on design
255
+ const rgbStatus = basicData.rgb || "Stopped" // Mock/Default based on design
256
+ const litdStatus = basicData.litd || "Stopped" // Mock/Default based on design
244
257
 
245
- const isRgbRunning =
246
- rgbStatus.toLowerCase() === "running" || rgbStatus === "true";
247
- const isLitdRunning =
248
- litdStatus.toLowerCase() === "running" || litdStatus === "true";
258
+ const isRgbRunning
259
+ = rgbStatus.toLowerCase() === "running" || rgbStatus === "true"
260
+ const isLitdRunning
261
+ = litdStatus.toLowerCase() === "running" || litdStatus === "true"
249
262
 
250
- const rgbClass = isRgbRunning ? "running" : "stopped";
251
- const litdClass = isLitdRunning ? "running" : "stopped";
263
+ const rgbClass = isRgbRunning ? "running" : "stopped"
264
+ const litdClass = isLitdRunning ? "running" : "stopped"
252
265
 
253
- const rgbText = isRgbRunning ? "Running" : "Stopped";
254
- const litdText = isLitdRunning ? "Running" : "Stopped";
266
+ const rgbText = isRgbRunning ? "Running" : "Stopped"
267
+ const litdText = isLitdRunning ? "Running" : "Stopped"
255
268
 
256
269
  // Helper to shorten address in the middle
257
270
  const shortenAddress = (address, chars = 10) => {
258
- if (!address || address.length <= chars * 2) return address || "";
259
- return `${address.substring(0, chars)}...${address.substring(address.length - chars)}`;
260
- };
271
+ if (!address || address.length <= chars * 2)
272
+ return address || ""
273
+ return `${address.substring(0, chars)}...${address.substring(address.length - chars)}`
274
+ }
261
275
 
262
276
  const html = `
263
277
  <div class="header-logo text-center mb-5">
@@ -319,34 +333,59 @@ function renderConfiguredView(basicData, settings) {
319
333
  </div>
320
334
  </div>
321
335
 
322
- <button class="manage-btn" onclick="window.location.href='${basicData.manageUrl || "#"}'">
336
+ <button class="manage-btn" onclick="navigateToManage('${basicData.manageUrl || "#"}', this)">
323
337
  Manage My Node <i class="fas fa-arrow-right"></i>
324
338
  </button>
325
339
  </div>
326
340
 
327
341
  <!-- Settings Modal Placeholder -->
328
342
  <div id="settings-modal-container"></div>
329
- `;
343
+ `
330
344
 
331
- mainContent.innerHTML = html;
345
+ mainContent.innerHTML = html
332
346
 
333
347
  // Store data globally for modal use
334
- window.currentSettings = settings;
335
- window.currentBasicData = basicData;
348
+ window.currentSettings = settings
349
+ window.currentBasicData = basicData
350
+ }
351
+
352
+ // Navigate to manage URL with loading state
353
+ window.navigateToManage = function navigateToManage(url, button) {
354
+ if (!url || url === "#" || !button) {
355
+ return
356
+ }
357
+
358
+ // Prevent double-click
359
+ if (button.dataset.loading === "true") {
360
+ return
361
+ }
362
+
363
+ // Set loading state
364
+ button.dataset.loading = "true"
365
+ button.disabled = true
366
+ button.classList.add("loading-state")
367
+ button.innerHTML = `<span class="loading"></span> Loading...`
368
+
369
+ // Allow UI to render before navigation
370
+ requestAnimationFrame(() => {
371
+ setTimeout(() => {
372
+ window.location.href = url
373
+ }, 50)
374
+ })
336
375
  }
337
376
 
338
377
  // Open Settings Modal
339
378
  function openSettingsModal() {
340
- const container = document.getElementById("settings-modal-container");
341
- const settings = window.currentSettings || {};
342
- const basicData = window.currentBasicData || {};
379
+ const container = document.getElementById("settings-modal-container")
380
+ const settings = window.currentSettings || {}
381
+ const basicData = window.currentBasicData || {}
343
382
 
344
383
  // Helper to create read-only field
345
384
  const createField = (label, value, isPassword = false) => {
346
- const type = isPassword ? "password" : "text";
385
+ const type = isPassword ? "password" : "text"
347
386
  const toggleHtml = isPassword
348
387
  ? `<i class="far fa-eye password-toggle-icon" onclick="togglePasswordVisibility(this)"></i>`
349
- : "";
388
+ : ""
350
389
 
351
390
  return `
352
391
  <div class="settings-field">
@@ -356,8 +395,8 @@ function openSettingsModal() {
356
395
  ${toggleHtml}
357
396
  </div>
358
397
  </div>
359
- `;
360
- };
398
+ `
399
+ }
361
400
 
362
401
  const modalHtml = `
363
402
  <div class="settings-modal-overlay" onclick="closeSettingsModal(event)">
@@ -413,28 +452,29 @@ function openSettingsModal() {
413
452
  </div>
414
453
  </div>
415
454
  </div>
416
- `;
455
+ `
417
456
 
418
- container.innerHTML = modalHtml;
457
+ container.innerHTML = modalHtml
419
458
  }
420
459
 
421
460
  // Close Settings Modal
422
461
  function closeSettingsModal(event) {
423
- const container = document.getElementById("settings-modal-container");
424
- container.innerHTML = "";
462
+ const container = document.getElementById("settings-modal-container")
463
+ container.innerHTML = ""
425
464
  }
426
465
 
427
466
  // Toggle Password Visibility
428
467
  function togglePasswordVisibility(icon) {
429
- const input = icon.previousElementSibling;
468
+ const input = icon.previousElementSibling
430
469
  if (input.type === "password") {
431
- input.type = "text";
432
- icon.classList.remove("fa-eye");
433
- icon.classList.add("fa-eye-slash");
434
- } else {
435
- input.type = "password";
436
- icon.classList.remove("fa-eye-slash");
437
- icon.classList.add("fa-eye");
470
+ input.type = "text"
471
+ icon.classList.remove("fa-eye")
472
+ icon.classList.add("fa-eye-slash")
473
+ }
474
+ else {
475
+ input.type = "password"
476
+ icon.classList.remove("fa-eye-slash")
477
+ icon.classList.add("fa-eye")
438
478
  }
439
479
  }
440
480
 
@@ -442,48 +482,50 @@ function togglePasswordVisibility(icon) {
442
482
  function copyToClipboard(text, triggerElement) {
443
483
  const showFeedback = () => {
444
484
  if (triggerElement) {
445
- const icon = triggerElement.querySelector("i");
485
+ const icon = triggerElement.querySelector("i")
446
486
  if (icon) {
447
- const originalClass = icon.className;
448
- icon.className = "fas fa-check";
487
+ const originalClass = icon.className
488
+ icon.className = "fas fa-check"
449
489
 
450
490
  setTimeout(() => {
451
- icon.className = originalClass;
452
- }, 2000);
491
+ icon.className = originalClass
492
+ }, 2000)
453
493
  }
454
494
  }
455
- };
495
+ }
456
496
 
457
497
  if (navigator.clipboard && window.isSecureContext) {
458
498
  navigator.clipboard.writeText(text).then(
459
499
  () => {
460
- showFeedback();
500
+ showFeedback()
461
501
  },
462
502
  (err) => {
463
- console.error("Could not copy text: ", err);
503
+ console.error("Could not copy text: ", err)
464
504
  },
465
- );
466
- } else {
467
- const textArea = document.createElement("textarea");
468
- textArea.value = text;
469
- textArea.style.position = "fixed";
470
- textArea.style.left = "-9999px";
471
- document.body.appendChild(textArea);
472
- textArea.focus();
473
- textArea.select();
505
+ )
506
+ }
507
+ else {
508
+ const textArea = document.createElement("textarea")
509
+ textArea.value = text
510
+ textArea.style.position = "fixed"
511
+ textArea.style.left = "-9999px"
512
+ document.body.appendChild(textArea)
513
+ textArea.focus()
514
+ textArea.select()
474
515
  try {
475
- document.execCommand("copy");
476
- showFeedback();
477
- } catch (err) {
478
- console.error("Fallback: Oops, unable to copy", err);
516
+ document.execCommand("copy")
517
+ showFeedback()
518
+ }
519
+ catch (err) {
520
+ console.error("Fallback: Oops, unable to copy", err)
479
521
  }
480
- document.body.removeChild(textArea);
522
+ document.body.removeChild(textArea)
481
523
  }
482
524
  }
483
525
 
484
526
  // Render configuration form (when owner or settings missing)
485
527
  function renderConfigurationForm(basicData) {
486
- const mainContent = document.getElementById("main-content");
528
+ const mainContent = document.getElementById("main-content")
487
529
 
488
530
  const html = `
489
531
  <div class="header-logo text-center mb-4">
@@ -539,89 +581,93 @@ function renderConfigurationForm(basicData) {
539
581
  </div>
540
582
  </form>
541
583
  </div>
542
- `;
584
+ `
543
585
 
544
- mainContent.innerHTML = html;
586
+ mainContent.innerHTML = html
545
587
 
546
588
  // Initialize configuration fields
547
- updateConfigFields();
589
+ updateConfigFields()
548
590
  }
549
591
 
550
592
  // Toggle Advanced Configuration
551
593
  window.toggleAdvanced = function () {
552
- isAdvancedConfigVisible = !isAdvancedConfigVisible;
553
- const container = document.getElementById("config-fields-container");
554
- const icon = document.getElementById("advanced-icon");
594
+ isAdvancedConfigVisible = !isAdvancedConfigVisible
595
+ const container = document.getElementById("config-fields-container")
596
+ const icon = document.getElementById("advanced-icon")
555
597
 
556
598
  if (isAdvancedConfigVisible) {
557
- container.style.display = "block";
558
- icon.classList.remove("fa-chevron-down");
559
- icon.classList.add("fa-chevron-up");
560
- } else {
561
- container.style.display = "none";
562
- icon.classList.remove("fa-chevron-up");
563
- icon.classList.add("fa-chevron-down");
599
+ container.style.display = "block"
600
+ icon.classList.remove("fa-chevron-down")
601
+ icon.classList.add("fa-chevron-up")
564
602
  }
565
- };
603
+ else {
604
+ container.style.display = "none"
605
+ icon.classList.remove("fa-chevron-up")
606
+ icon.classList.add("fa-chevron-down")
607
+ }
608
+ }
566
609
 
567
610
  // Select Network
568
611
  window.selectNetwork = function (network) {
569
612
  if (networkConfigs && !networkConfigs[network]) {
570
- showErrorModal("This network is not supported");
571
- return;
613
+ showErrorModal("This network is not supported")
614
+ return
572
615
  }
573
- document.getElementById("network").value = network;
616
+ document.getElementById("network").value = network
574
617
 
575
618
  // Update active button state
576
619
  document.querySelectorAll(".btn-network").forEach((btn) => {
577
620
  if (btn.dataset.value === network) {
578
- btn.classList.add("active");
579
- } else {
580
- btn.classList.remove("active");
621
+ btn.classList.add("active")
622
+ }
623
+ else {
624
+ btn.classList.remove("active")
581
625
  }
582
- });
626
+ })
583
627
 
584
628
  // Toggle mainnet badge
585
- const badge = document.getElementById("mainnet-badge");
629
+ const badge = document.getElementById("mainnet-badge")
586
630
  if (badge) {
587
- badge.style.display = network === "mainnet" ? "inline-block" : "none";
631
+ badge.style.display = network === "mainnet" ? "inline-block" : "none"
588
632
  }
589
633
 
590
- updateConfigFields();
591
- };
634
+ updateConfigFields()
635
+ }
592
636
 
593
- window.copyToClipboard = copyToClipboard;
637
+ window.copyToClipboard = copyToClipboard
594
638
 
595
639
  window.togglePassword = function (id) {
596
- const input = document.getElementById(id);
640
+ const input = document.getElementById(id)
597
641
  if (input.type === "password") {
598
- input.type = "text";
599
- } else {
600
- input.type = "password";
642
+ input.type = "text"
643
+ }
644
+ else {
645
+ input.type = "password"
601
646
  }
602
- };
647
+ }
603
648
 
604
649
  // Render basic information list
605
650
  function renderBasicInfoList(data) {
606
- let html = "";
607
- const excludeKeys = ["settings"];
651
+ let html = ""
652
+ const excludeKeys = ["settings"]
608
653
 
609
654
  for (const key in data) {
610
- if (excludeKeys.includes(key)) continue;
655
+ if (excludeKeys.includes(key))
656
+ continue
611
657
 
612
- const value = data[key];
613
- const status = getStatusClass(value);
658
+ const value = data[key]
659
+ const status = getStatusClass(value)
614
660
 
615
661
  let valueHtml = `<span class="info-value ${status.class}">
616
662
  <i class="${status.icon} status-icon"></i>${value}
617
- </span>`;
663
+ </span>`
618
664
 
619
665
  if (key === "manageUrl") {
620
666
  valueHtml = `<span class="info-value">
621
667
  <a href="#" class="info-link" onclick="this.innerHTML='<i class=\\'fas fa-spinner fa-spin status-icon\\'></i>Loading...';setTimeout(()=>{window.location.href='${value}';},100);return false;">
622
668
  <i class="fas fa-external-link-alt status-icon"></i>Click to manage
623
669
  </a>
624
- </span>`;
670
+ </span>`
625
671
  }
626
672
 
627
673
  html += `
@@ -629,19 +675,19 @@ function renderBasicInfoList(data) {
629
675
  <div class="info-key">${key}:</div>
630
676
  <div class="info-value-container">${valueHtml}</div>
631
677
  </li>
632
- `;
678
+ `
633
679
  }
634
- return html;
680
+ return html
635
681
  }
636
682
 
637
683
  // Render settings list
638
684
  function renderSettingsList(settings) {
639
- let html = "";
685
+ let html = ""
640
686
  for (const key in settings) {
641
687
  const value = Array.isArray(settings[key])
642
688
  ? settings[key].join(", ")
643
- : settings[key];
644
- const label = fieldLabels[key] || key;
689
+ : settings[key]
690
+ const label = fieldLabels[key] || key
645
691
 
646
692
  html += `
647
693
  <li class="list-group-item">
@@ -650,24 +696,26 @@ function renderSettingsList(settings) {
650
696
  <span class="info-value">${value}</span>
651
697
  </div>
652
698
  </li>
653
- `;
699
+ `
654
700
  }
655
- return html;
701
+ return html
656
702
  }
657
703
 
658
704
  // Update configuration fields
659
705
  function updateConfigFields() {
660
- const networkInput = document.getElementById("network");
661
- const container = document.getElementById("config-fields-container");
706
+ const networkInput = document.getElementById("network")
707
+ const container = document.getElementById("config-fields-container")
662
708
 
663
- if (!networkInput || !container) return;
709
+ if (!networkInput || !container)
710
+ return
664
711
 
665
- const selectedNetwork = networkInput.value;
666
- const config = networkConfigs[selectedNetwork];
712
+ const selectedNetwork = networkInput.value
713
+ const config = networkConfigs[selectedNetwork]
667
714
 
668
- if (!config) return;
715
+ if (!config)
716
+ return
669
717
 
670
- let fieldsHtml = "";
718
+ let fieldsHtml = ""
671
719
 
672
720
  // Group fields by category
673
721
  const groups = {
@@ -692,13 +740,14 @@ function updateConfigFields() {
692
740
  "bitcoindIndex",
693
741
  ],
694
742
  "Nostr Config": ["officialNostrPubKey", "nostrRelays"],
695
- };
743
+ }
696
744
 
697
745
  // Define editable fields per network
698
- let editableFields = [];
746
+ let editableFields = []
699
747
  if (selectedNetwork === "regtest") {
700
- editableFields = ["bitcoindPass"];
701
- } else if (selectedNetwork === "mainnet") {
748
+ editableFields = ["bitcoindPass"]
749
+ }
750
+ else if (selectedNetwork === "mainnet") {
702
751
  editableFields = [
703
752
  "bitcoindRpcHost",
704
753
  "bitcoindRpcPort",
@@ -708,26 +757,27 @@ function updateConfigFields() {
708
757
  "bitcoindZmqRawTx",
709
758
  "rgbProxy",
710
759
  "bitcoindIndex",
711
- ];
712
- } else if (selectedNetwork === "testnet") {
713
- editableFields = ["bitcoindPass"];
760
+ ]
761
+ }
762
+ else if (selectedNetwork === "testnet") {
763
+ editableFields = ["bitcoindPass"]
714
764
  }
715
765
 
716
766
  for (const groupName in groups) {
717
- fieldsHtml += `<div class="config-section mb-4">`;
718
- fieldsHtml += `<h6 class="text-lime mb-3">${groupName}</h6>`;
719
- fieldsHtml += `<div class="row g-3">`;
767
+ fieldsHtml += `<div class="config-section mb-4">`
768
+ fieldsHtml += `<h6 class="text-lime mb-3">${groupName}</h6>`
769
+ fieldsHtml += `<div class="row g-3">`
720
770
 
721
771
  groups[groupName].forEach((key) => {
722
772
  if (config[key] !== undefined) {
723
- const label = fieldLabels[key] || key;
773
+ const label = fieldLabels[key] || key
724
774
  const value = Array.isArray(config[key])
725
775
  ? config[key].join(",")
726
- : config[key];
776
+ : config[key]
727
777
 
728
- const isEditable = editableFields.includes(key);
729
- const isPassword = key.toLowerCase().includes("pass");
730
- const inputType = isPassword ? "password" : "text";
778
+ const isEditable = editableFields.includes(key)
779
+ const isPassword = key.toLowerCase().includes("pass")
780
+ const inputType = isPassword ? "password" : "text"
731
781
 
732
782
  fieldsHtml += `
733
783
  <div class="col-md-4">
@@ -746,95 +796,100 @@ function updateConfigFields() {
746
796
  }
747
797
  </div>
748
798
  </div>
749
- `;
799
+ `
750
800
  }
751
- });
801
+ })
752
802
 
753
- fieldsHtml += `</div></div>`;
803
+ fieldsHtml += `</div></div>`
754
804
  }
755
805
 
756
- container.innerHTML = fieldsHtml;
806
+ container.innerHTML = fieldsHtml
757
807
  }
758
808
 
759
809
  // Handle configuration form submission
760
810
  async function handleConfigFormSubmit(event) {
761
- event.preventDefault();
762
- const form = document.getElementById("configForm");
763
- const submitBtn = document.getElementById("btn_config_submit");
811
+ event.preventDefault()
812
+ const form = document.getElementById("configForm")
813
+ const submitBtn = document.getElementById("btn_config_submit")
764
814
 
765
815
  if (!form.checkValidity()) {
766
- event.stopPropagation();
767
- form.classList.add("was-validated");
768
- return;
816
+ event.stopPropagation()
817
+ form.classList.add("was-validated")
818
+ return
769
819
  }
770
820
 
771
- form.classList.add("was-validated");
772
- submitBtn.setAttribute("disabled", "disabled");
821
+ form.classList.add("was-validated")
822
+ submitBtn.setAttribute("disabled", "disabled")
773
823
  // Use the new loading spinner style
774
- submitBtn.innerHTML = '<div class="loading"></div>Save and Continue';
775
- const formData = new FormData(form);
776
- const myHeaders = new Headers();
777
- myHeaders.append("Content-Type", "application/json");
824
+ submitBtn.innerHTML = "<div class=\"loading\"></div>Save and Continue"
825
+ const formData = new FormData(form)
826
+ const myHeaders = new Headers()
827
+ myHeaders.append("Content-Type", "application/json")
778
828
  // Extract owner
779
- const owner = formData.get("owner");
829
+ const owner = formData.get("owner")
780
830
 
781
831
  // Build settings object (exclude owner)
782
- const settings = {};
832
+ const settings = {}
783
833
  for (const [key, value] of formData.entries()) {
784
- if (key === "owner") continue;
834
+ if (key === "owner")
835
+ continue
785
836
 
786
837
  if (key === "nostrRelays") {
787
838
  // Handle comma-separated array
788
839
  settings[key] = value
789
840
  .split(",")
790
- .map((item) => item.trim())
791
- .filter((item) => item);
792
- } else {
793
- settings[key] = value;
841
+ .map(item => item.trim())
842
+ .filter(item => item)
843
+ }
844
+ else {
845
+ settings[key] = value
794
846
  }
795
847
  }
796
848
 
797
849
  const raw = JSON.stringify({
798
850
  owner,
799
851
  settings,
800
- });
852
+ })
801
853
 
802
854
  const requestOptions = {
803
855
  method: "POST",
804
856
  headers: myHeaders,
805
857
  body: raw,
806
858
  redirect: "follow",
807
- };
859
+ }
808
860
 
809
861
  try {
810
- const response = await fetch("/api/lnd/init", requestOptions);
811
- const result = await response.json();
862
+ const response = await fetch("/api/lnd/init", requestOptions)
863
+ const result = await response.json()
812
864
 
813
865
  if (result.code === 200) {
814
- await getInfo(false);
815
- showSuccessModal("Complete configuration saved successfully!");
816
- } else {
817
- showErrorModal(result.message || "Configuration failed");
866
+ await getInfo(false)
867
+ showSuccessModal("Complete configuration saved successfully!")
868
+ }
869
+ else {
870
+ showErrorModal(result.message || "Configuration failed")
818
871
  }
819
- } catch (error) {
820
- console.error("Network error:", error);
821
- showErrorModal("Network error occurred");
822
- } finally {
823
- submitBtn.removeAttribute("disabled");
824
- submitBtn.innerHTML =
825
- '<i class="fas fa-save"></i> Save Complete Configuration';
872
+ }
873
+ catch (error) {
874
+ console.error("Network error:", error)
875
+ showErrorModal("Network error occurred")
876
+ }
877
+ finally {
878
+ submitBtn.removeAttribute("disabled")
879
+ submitBtn.innerHTML
880
+ = "<i class=\"fas fa-save\"></i> Save Complete Configuration"
826
881
  }
827
882
  }
828
883
 
829
884
  // Page initialization
830
885
  document.addEventListener("DOMContentLoaded", async () => {
831
886
  // Get information
832
- await getInfo();
887
+ await getInfo()
833
888
 
834
889
  // Bind form submission events (using event delegation)
835
890
  document.addEventListener("submit", (event) => {
836
891
  if (event.target.id === "configForm") {
837
- handleConfigFormSubmit(event);
892
+ handleConfigFormSubmit(event)
838
893
  }
839
- });
840
- });
894
+ })
895
+ })