Skip to content

Commit 2382ca1

Browse files
authored
Fix monitoring page CSP compliance by extracting inline script (#130)
1 parent 6edd0ad commit 2382ca1

4 files changed

Lines changed: 120 additions & 112 deletions

File tree

nest-cli.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"collection": "@nestjs/schematics",
33
"sourceRoot": "src",
44
"compilerOptions": {
5-
"assets": ["assets/home.html", "assets/monitoring.html"],
5+
"assets": ["assets/home.html", "assets/monitoring.html", "assets/monitoring.js"],
66
"watchAssets": true
77
}
88
}

src/assets/monitoring.html

Lines changed: 1 addition & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -72,116 +72,6 @@ <h1>lightning.space monitoring</h1>
7272
<div class="loading">Loading monitoring data...</div>
7373
</div>
7474

75-
<script>
76-
function fmt(n, decimals) {
77-
if (n == null) return '-';
78-
return Number(n).toLocaleString('en-US', {
79-
minimumFractionDigits: decimals != null ? decimals : 0,
80-
maximumFractionDigits: decimals != null ? decimals : 8
81-
});
82-
}
83-
84-
function colorClass(n) {
85-
if (n > 0) return 'positive';
86-
if (n < 0) return 'negative';
87-
return '';
88-
}
89-
90-
function truncate(s, len) {
91-
if (!s) return '-';
92-
if (s.length <= len) return s;
93-
return s.substring(0, len) + '...';
94-
}
95-
96-
async function loadData() {
97-
var content = document.getElementById('content');
98-
try {
99-
var res = await fetch('/monitoring/data');
100-
if (!res.ok) throw new Error('HTTP ' + res.status);
101-
var data = await res.json();
102-
render(data);
103-
} catch (e) {
104-
content.innerHTML = '<div class="error">Failed to load data: ' + e.message + '</div>';
105-
}
106-
}
107-
108-
function render(data) {
109-
var content = document.getElementById('content');
110-
document.getElementById('timestamp').textContent = 'Last updated: ' + new Date(data.timestamp).toLocaleString();
111-
112-
var html = '';
113-
114-
// Lightning Channels
115-
html += '<div class="section">';
116-
html += '<h2>Lightning Channels</h2>';
117-
if (data.channels.length === 0) {
118-
html += '<div class="loading">No channel data available</div>';
119-
} else {
120-
html += '<table>';
121-
html += '<tr><th>Peer</th><th class="number">Capacity</th><th class="number">Local Balance</th><th class="number">Remote Balance</th></tr>';
122-
for (var i = 0; i < data.channels.length; i++) {
123-
var ch = data.channels[i];
124-
html += '<tr>';
125-
html += '<td title="' + ch.name + '">' + truncate(ch.name, 24) + '</td>';
126-
html += '<td class="number">' + fmt(ch.capacity) + '</td>';
127-
html += '<td class="number">' + fmt(ch.localBalance) + '</td>';
128-
html += '<td class="number">' + fmt(ch.remoteBalance) + '</td>';
129-
html += '</tr>';
130-
}
131-
html += '</table>';
132-
}
133-
html += '</div>';
134-
135-
// Balances
136-
html += '<div class="section">';
137-
html += '<h2>Balances</h2>';
138-
if (data.balances.length === 0) {
139-
html += '<div class="loading">No balance data available</div>';
140-
} else {
141-
html += '<table>';
142-
html += '<tr><th>Asset</th><th class="number">Onchain</th><th class="number">LND Onchain</th><th class="number">Lightning</th><th class="number">Citrea</th><th class="number">Customer</th><th class="number">LDS Balance</th><th class="number">LDS in CHF</th></tr>';
143-
for (var i = 0; i < data.balances.length; i++) {
144-
var b = data.balances[i];
145-
html += '<tr>';
146-
html += '<td>' + b.assetName + (b.assetSymbol ? ' (' + b.assetSymbol + ')' : '') + '</td>';
147-
html += '<td class="number">' + fmt(b.onchainBalance) + '</td>';
148-
html += '<td class="number">' + fmt(b.lndOnchainBalance) + '</td>';
149-
html += '<td class="number">' + fmt(b.lightningBalance) + '</td>';
150-
html += '<td class="number">' + fmt(b.citreaBalance) + '</td>';
151-
html += '<td class="number">' + fmt(b.customerBalance) + '</td>';
152-
html += '<td class="number ' + colorClass(b.ldsBalance) + '">' + fmt(b.ldsBalance) + '</td>';
153-
html += '<td class="number ' + colorClass(b.ldsBalanceInCHF) + '">' + fmt(b.ldsBalanceInCHF, 2) + '</td>';
154-
html += '</tr>';
155-
}
156-
html += '</table>';
157-
}
158-
html += '</div>';
159-
160-
// EVM Balances
161-
html += '<div class="section">';
162-
html += '<h2>EVM Balances</h2>';
163-
if (data.evmBalances.length === 0) {
164-
html += '<div class="loading">No EVM balance data available</div>';
165-
} else {
166-
for (var i = 0; i < data.evmBalances.length; i++) {
167-
var evm = data.evmBalances[i];
168-
html += '<h3 style="font-size:13px;color:#aaa;margin:12px 0 6px;text-transform:capitalize;">' + evm.blockchain + '</h3>';
169-
html += '<table>';
170-
html += '<tr><th>Token</th><th class="number">Balance</th></tr>';
171-
html += '<tr><td>' + evm.nativeSymbol + ' (native)</td><td class="number">' + fmt(evm.nativeBalance) + '</td></tr>';
172-
for (var j = 0; j < evm.tokens.length; j++) {
173-
var t = evm.tokens[j];
174-
html += '<tr><td>' + t.symbol + '</td><td class="number">' + fmt(t.balance) + '</td></tr>';
175-
}
176-
html += '</table>';
177-
}
178-
}
179-
html += '</div>';
180-
181-
content.innerHTML = html;
182-
}
183-
184-
loadData();
185-
</script>
75+
<script src="/monitoring/monitoring.js"></script>
18676
</body>
18777
</html>

src/assets/monitoring.js

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
function fmt(n, decimals) {
2+
if (n == null) return '-';
3+
return Number(n).toLocaleString('en-US', {
4+
minimumFractionDigits: decimals != null ? decimals : 0,
5+
maximumFractionDigits: decimals != null ? decimals : 8,
6+
});
7+
}
8+
9+
function colorClass(n) {
10+
if (n > 0) return 'positive';
11+
if (n < 0) return 'negative';
12+
return '';
13+
}
14+
15+
function truncate(s, len) {
16+
if (!s) return '-';
17+
if (s.length <= len) return s;
18+
return s.substring(0, len) + '...';
19+
}
20+
21+
async function loadData() {
22+
var content = document.getElementById('content');
23+
try {
24+
var res = await fetch('/monitoring/data');
25+
if (!res.ok) throw new Error('HTTP ' + res.status);
26+
var data = await res.json();
27+
render(data);
28+
} catch (e) {
29+
content.innerHTML = '<div class="error">Failed to load data: ' + e.message + '</div>';
30+
}
31+
}
32+
33+
function render(data) {
34+
var content = document.getElementById('content');
35+
document.getElementById('timestamp').textContent = 'Last updated: ' + new Date(data.timestamp).toLocaleString();
36+
37+
var html = '';
38+
39+
// Lightning Channels
40+
html += '<div class="section">';
41+
html += '<h2>Lightning Channels</h2>';
42+
if (data.channels.length === 0) {
43+
html += '<div class="loading">No channel data available</div>';
44+
} else {
45+
html += '<table>';
46+
html += '<tr><th>Peer</th><th class="number">Capacity</th><th class="number">Local Balance</th><th class="number">Remote Balance</th></tr>';
47+
for (var i = 0; i < data.channels.length; i++) {
48+
var ch = data.channels[i];
49+
html += '<tr>';
50+
html += '<td title="' + ch.name + '">' + truncate(ch.name, 24) + '</td>';
51+
html += '<td class="number">' + fmt(ch.capacity) + '</td>';
52+
html += '<td class="number">' + fmt(ch.localBalance) + '</td>';
53+
html += '<td class="number">' + fmt(ch.remoteBalance) + '</td>';
54+
html += '</tr>';
55+
}
56+
html += '</table>';
57+
}
58+
html += '</div>';
59+
60+
// Balances
61+
html += '<div class="section">';
62+
html += '<h2>Balances</h2>';
63+
if (data.balances.length === 0) {
64+
html += '<div class="loading">No balance data available</div>';
65+
} else {
66+
html += '<table>';
67+
html +=
68+
'<tr><th>Asset</th><th class="number">Onchain</th><th class="number">LND Onchain</th><th class="number">Lightning</th><th class="number">Citrea</th><th class="number">Customer</th><th class="number">LDS Balance</th><th class="number">LDS in CHF</th></tr>';
69+
for (var i = 0; i < data.balances.length; i++) {
70+
var b = data.balances[i];
71+
html += '<tr>';
72+
html += '<td>' + b.assetName + (b.assetSymbol ? ' (' + b.assetSymbol + ')' : '') + '</td>';
73+
html += '<td class="number">' + fmt(b.onchainBalance) + '</td>';
74+
html += '<td class="number">' + fmt(b.lndOnchainBalance) + '</td>';
75+
html += '<td class="number">' + fmt(b.lightningBalance) + '</td>';
76+
html += '<td class="number">' + fmt(b.citreaBalance) + '</td>';
77+
html += '<td class="number">' + fmt(b.customerBalance) + '</td>';
78+
html += '<td class="number ' + colorClass(b.ldsBalance) + '">' + fmt(b.ldsBalance) + '</td>';
79+
html += '<td class="number ' + colorClass(b.ldsBalanceInCHF) + '">' + fmt(b.ldsBalanceInCHF, 2) + '</td>';
80+
html += '</tr>';
81+
}
82+
html += '</table>';
83+
}
84+
html += '</div>';
85+
86+
// EVM Balances
87+
html += '<div class="section">';
88+
html += '<h2>EVM Balances</h2>';
89+
if (data.evmBalances.length === 0) {
90+
html += '<div class="loading">No EVM balance data available</div>';
91+
} else {
92+
for (var i = 0; i < data.evmBalances.length; i++) {
93+
var evm = data.evmBalances[i];
94+
html += '<h3 style="font-size:13px;color:#aaa;margin:12px 0 6px;text-transform:capitalize;">' + evm.blockchain + '</h3>';
95+
html += '<table>';
96+
html += '<tr><th>Token</th><th class="number">Balance</th></tr>';
97+
html += '<tr><td>' + evm.nativeSymbol + ' (native)</td><td class="number">' + fmt(evm.nativeBalance) + '</td></tr>';
98+
for (var j = 0; j < evm.tokens.length; j++) {
99+
var t = evm.tokens[j];
100+
html += '<tr><td>' + t.symbol + '</td><td class="number">' + fmt(t.balance) + '</td></tr>';
101+
}
102+
html += '</table>';
103+
}
104+
}
105+
html += '</div>';
106+
107+
content.innerHTML = html;
108+
}
109+
110+
loadData();

src/subdomains/monitoring/controllers/monitoring.controller.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,14 @@ export class MonitoringController {
2121
res.send(readFileSync(join(__dirname, '..', '..', '..', 'assets', 'monitoring.html')).toString());
2222
}
2323

24+
@Get('monitoring.js')
25+
@ApiExcludeEndpoint()
26+
async monitoringScript(@Res() res: Response): Promise<void> {
27+
res.type('application/javascript').send(
28+
readFileSync(join(__dirname, '..', '..', '..', 'assets', 'monitoring.js')).toString(),
29+
);
30+
}
31+
2432
@Get('data')
2533
@ApiExcludeEndpoint()
2634
async monitoringData(): Promise<{

0 commit comments

Comments
 (0)