Compare commits

...

4 Commits

8 changed files with 552 additions and 509 deletions

View File

@@ -1,6 +1,8 @@
# Alpine-based MUSL static binary builder for C-Relay # Alpine-based MUSL static binary builder for C-Relay
# Produces truly portable binaries with zero runtime dependencies # Produces truly portable binaries with zero runtime dependencies
ARG DEBUG_BUILD=false
FROM alpine:3.19 AS builder FROM alpine:3.19 AS builder
# Install build dependencies # Install build dependencies
@@ -98,9 +100,19 @@ RUN cd nostr_core_lib && \
COPY src/ /build/src/ COPY src/ /build/src/
COPY Makefile /build/Makefile COPY Makefile /build/Makefile
# Build c-relay with full static linking and debug symbols (only rebuilds when src/ changes) # Build c-relay with full static linking (only rebuilds when src/ changes)
# Disable fortification to avoid __*_chk symbols that don't exist in MUSL # Disable fortification to avoid __*_chk symbols that don't exist in MUSL
RUN gcc -static -g -O0 -DDEBUG -Wall -Wextra -std=c99 \ # Use conditional compilation flags based on DEBUG_BUILD argument
RUN if [ "$DEBUG_BUILD" = "true" ]; then \
CFLAGS="-g -O0 -DDEBUG"; \
STRIP_CMD=""; \
echo "Building with DEBUG symbols enabled"; \
else \
CFLAGS="-O2"; \
STRIP_CMD="strip /build/c_relay_static"; \
echo "Building optimized production binary"; \
fi && \
gcc -static $CFLAGS -Wall -Wextra -std=c99 \
-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0 \ -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0 \
-I. -Ic_utils_lib/src -Inostr_core_lib -Inostr_core_lib/nostr_core \ -I. -Ic_utils_lib/src -Inostr_core_lib -Inostr_core_lib/nostr_core \
-Inostr_core_lib/cjson -Inostr_core_lib/nostr_websocket \ -Inostr_core_lib/cjson -Inostr_core_lib/nostr_websocket \
@@ -111,10 +123,8 @@ RUN gcc -static -g -O0 -DDEBUG -Wall -Wextra -std=c99 \
c_utils_lib/libc_utils.a \ c_utils_lib/libc_utils.a \
nostr_core_lib/libnostr_core_x64.a \ nostr_core_lib/libnostr_core_x64.a \
-lwebsockets -lssl -lcrypto -lsqlite3 -lsecp256k1 \ -lwebsockets -lssl -lcrypto -lsqlite3 -lsecp256k1 \
-lcurl -lz -lpthread -lm -ldl -lcurl -lz -lpthread -lm -ldl && \
eval "$STRIP_CMD"
# DO NOT strip - we need debug symbols for debugging
# RUN strip /build/c_relay_static
# Verify it's truly static # Verify it's truly static
RUN echo "=== Binary Information ===" && \ RUN echo "=== Binary Information ===" && \

View File

@@ -6,7 +6,7 @@
--muted-color: #dddddd; --muted-color: #dddddd;
--border-color: var(--muted-color); --border-color: var(--muted-color);
--font-family: "Courier New", Courier, monospace; --font-family: "Courier New", Courier, monospace;
--border-radius: 15px; --border-radius: 5px;
--border-width: 1px; --border-width: 1px;
/* Floating Tab Variables (8) */ /* Floating Tab Variables (8) */
@@ -22,6 +22,23 @@
--tab-border-opacity-logged-in: 0.1; --tab-border-opacity-logged-in: 0.1;
} }
/* Dark Mode Overrides */
body.dark-mode {
--primary-color: #ffffff;
--secondary-color: #000000;
--accent-color: #ff0000;
--muted-color: #222222;
--border-color: var(--muted-color);
--tab-bg-logged-out: #000000;
--tab-color-logged-out: #ffffff;
--tab-border-logged-out: #ffffff;
--tab-bg-logged-in: #000000;
--tab-color-logged-in: #ffffff;
--tab-border-logged-in: #00ffff;
}
* { * {
margin: 0; margin: 0;
padding: 0; padding: 0;
@@ -41,10 +58,8 @@ body {
/* Header Styles */ /* Header Styles */
.main-header { .main-header {
background-color: var(--secondary-color); background-color: var(--secondary-color);
border-bottom: var(--border-width) solid var(--border-color);
padding: 15px 20px; padding: 15px 20px;
position: sticky;
top: 0;
z-index: 100; z-index: 100;
max-width: 1200px; max-width: 1200px;
margin: 0 auto; margin: 0 auto;
@@ -67,6 +82,86 @@ body {
text-align: left; text-align: left;
} }
.relay-info {
text-align: center;
flex: 1;
}
.relay-name {
font-size: 14px;
font-weight: bold;
color: var(--primary-color);
margin-bottom: 2px;
}
.relay-pubkey-container {
border: 1px solid transparent;
border-radius: var(--border-radius);
padding: 4px;
margin-top: 4px;
cursor: pointer;
transition: border-color 0.2s ease;
background-color: var(--secondary-color);
}
.relay-pubkey-container:hover {
border-color: var(--border-color);
}
.relay-pubkey-container.copied {
border-color: var(--accent-color);
animation: flash-accent 0.5s ease-in-out;
}
.relay-pubkey {
font-size: 8px;
color: var(--primary-color);
font-family: "Courier New", Courier, monospace;
line-height: 1.2;
white-space: pre-line;
text-align: center;
}
@keyframes flash-accent {
0% { border-color: var(--accent-color); }
50% { border-color: var(--accent-color); }
100% { border-color: transparent; }
}
.relay-description {
font-size: 10px;
color: var(--primary-color);
margin-bottom: 0;
}
.header-title {
margin: 0;
font-size: 24px;
font-weight: bolder;
color: var(--primary-color);
border: none;
padding: 0;
text-align: left;
display: flex;
gap: 2px;
}
.relay-letter {
position: relative;
display: inline-block;
transition: all 0.05s ease;
}
.relay-letter.underlined::after {
content: '';
position: absolute;
bottom: -2px;
left: 0;
right: 0;
height: 2px;
background-color: var(--accent-color);
}
.header-user-name { .header-user-name {
display: block; display: block;
font-weight: 500; font-weight: 500;
@@ -78,6 +173,7 @@ body {
.profile-area { .profile-area {
display: flex; display: flex;
flex-direction: column;
align-items: center; align-items: center;
position: relative; position: relative;
cursor: pointer; cursor: pointer;
@@ -87,6 +183,14 @@ body {
margin-left: auto; margin-left: auto;
} }
.admin-label {
font-size: 10px;
color: var(--primary-color);
font-weight: normal;
margin-bottom: 4px;
text-align: center;
}
.profile-container { .profile-container {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@@ -129,13 +233,13 @@ body {
.logout-btn { .logout-btn {
width: 100%; width: 100%;
padding: 10px 15px; padding: 5px 10px;
background: none; background: none;
border: none; border: none;
color: var(--primary-color); color: var(--primary-color);
text-align: left; text-align: left;
cursor: pointer; cursor: pointer;
font-size: 14px; font-size: 10px;
font-family: var(--font-family); font-family: var(--font-family);
border-radius: var(--border-radius); border-radius: var(--border-radius);
transition: background-color 0.2s ease; transition: background-color 0.2s ease;
@@ -255,10 +359,10 @@ button:active {
} }
button:disabled { button:disabled {
background-color: #ccc; background-color: var(--muted-color);
color: var(--muted-color); color: var(--primary-color);
cursor: not-allowed; cursor: not-allowed;
border-color: #ccc; border-color: var(--muted-color);
} }
/* Flash animation for refresh button */ /* Flash animation for refresh button */
@@ -269,7 +373,7 @@ button:disabled {
} }
.flash-red { .flash-red {
animation: flash-red 0.5s ease-in-out; animation: flash-red 1s ease-in-out;
} }
/* Flash animation for updated statistics values */ /* Flash animation for updated statistics values */
@@ -280,7 +384,7 @@ button:disabled {
} }
.flash-value { .flash-value {
animation: flash-value 0.5s ease-in-out; animation: flash-value 1s ease-in-out;
} }
/* Npub links styling */ /* Npub links styling */
@@ -326,23 +430,6 @@ button:disabled {
border-color: var(--accent-color); border-color: var(--accent-color);
} }
/* Authentication warning message */
.auth-warning-message {
margin-bottom: 15px;
padding: 12px;
background-color: #fff3cd;
border: 1px solid #ffeaa7;
border-radius: var(--border-radius);
color: #856404;
}
.warning-content {
line-height: 1.4;
}
.warning-content strong {
color: #d68910;
}
.config-table { .config-table {
border: 1px solid var(--border-color); border: 1px solid var(--border-color);
@@ -363,6 +450,10 @@ button:disabled {
font-size: 10px; font-size: 10px;
} }
.config-table tbody tr:hover {
background-color: rgba(0, 0, 0, 0.05);
}
.config-table-container { .config-table-container {
overflow-x: auto; overflow-x: auto;
max-width: 100%; max-width: 100%;
@@ -370,12 +461,13 @@ button:disabled {
.config-table th { .config-table th {
font-weight: bold; font-weight: bold;
height: 40px; /* Double the default height */ height: 24px; /* Base height for tbody rows */
line-height: 40px; /* Center text vertically */ line-height: 24px; /* Center text vertically */
} }
.config-table tr:hover { .config-table td {
background-color: var(--muted-color); height: 16px; /* 50% taller than tbody rows would be */
line-height: 16px; /* Center text vertically */
} }
/* Inline config value inputs - remove borders and padding to fit seamlessly in table cells */ /* Inline config value inputs - remove borders and padding to fit seamlessly in table cells */
@@ -713,21 +805,6 @@ button:disabled {
transition: all 0.2s ease; transition: all 0.2s ease;
} }
/* Main Sections Wrapper */
.main-sections-wrapper {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
display: flex;
flex-wrap: wrap;
gap: var(--border-width);
}
.flex-section {
flex: 1;
min-width: 300px;
}
@media (max-width: 700px) { @media (max-width: 700px) {
body { body {
padding: 10px; padding: 10px;

View File

@@ -10,21 +10,38 @@
<body> <body>
<!-- Header with title and profile display --> <!-- Header with title and profile display -->
<header class="main-header"> <div class="section">
<div class="header-content"> <div class="header-content">
<div class="header-title">RELAY</div> <div class="header-title">
<span class="relay-letter" data-letter="R">R</span>
<span class="relay-letter" data-letter="E">E</span>
<span class="relay-letter" data-letter="L">L</span>
<span class="relay-letter" data-letter="A">A</span>
<span class="relay-letter" data-letter="Y">Y</span>
</div>
<div class="relay-info">
<div id="relay-name" class="relay-name">C-Relay</div>
<div id="relay-description" class="relay-description">Loading...</div>
<div id="relay-pubkey-container" class="relay-pubkey-container">
<div id="relay-pubkey" class="relay-pubkey">Loading...</div>
</div>
</div>
<div class="profile-area" id="profile-area" style="display: none;"> <div class="profile-area" id="profile-area" style="display: none;">
<div class="admin-label">admin</div>
<div class="profile-container"> <div class="profile-container">
<img id="header-user-image" class="header-user-image" alt="Profile" style="display: none;"> <img id="header-user-image" class="header-user-image" alt="Profile" style="display: none;">
<span id="header-user-name" class="header-user-name">Loading...</span> <span id="header-user-name" class="header-user-name">Loading...</span>
</div> </div>
<!-- Logout dropdown --> <!-- Logout dropdown -->
<div class="logout-dropdown" id="logout-dropdown" style="display: none;"> <div class="logout-dropdown" id="logout-dropdown" style="display: none;">
<button type="button" id="dark-mode-btn" class="logout-btn">🌙 DARK MODE</button>
<button type="button" id="logout-btn" class="logout-btn">LOGOUT</button> <button type="button" id="logout-btn" class="logout-btn">LOGOUT</button>
</div> </div>
</div> </div>
</div> </div>
</header>
</div>
<!-- Login Modal Overlay --> <!-- Login Modal Overlay -->
<div id="login-modal" class="login-modal-overlay" style="display: none;"> <div id="login-modal" class="login-modal-overlay" style="display: none;">
@@ -33,58 +50,6 @@
</div> </div>
</div> </div>
<!-- Main Sections Wrapper -->
<div class="main-sections-wrapper">
<!-- Relay Connection Section -->
<div id="relay-connection-section" class="flex-section">
<div class="section">
<h2>RELAY CONNECTION</h2>
<div class="input-group">
<label for="relay-connection-url">Relay URL:</label>
<input type="text" id="relay-connection-url" value=""
placeholder="ws://localhost:8888 or wss://relay.example.com">
</div>
<div class="input-group">
<label for="relay-pubkey-manual">Relay Pubkey (if not available via NIP-11):</label>
<input type="text" id="relay-pubkey-manual" placeholder="64-character hex pubkey"
pattern="[0-9a-fA-F]{64}" title="64-character hexadecimal public key">
</div>
<div class="inline-buttons">
<button type="button" id="connect-relay-btn">CONNECT TO RELAY</button>
<button type="button" id="disconnect-relay-btn" disabled>DISCONNECT</button>
<button type="button" id="restart-relay-btn" disabled>RESTART RELAY</button>
</div>
<div class="status disconnected" id="relay-connection-status">NOT CONNECTED</div>
<!-- Relay Information Display -->
<div id="relay-info-display" class="hidden">
<h3>Relay Information (NIP-11)</h3>
<table class="config-table" id="relay-info-table">
<thead>
<tr>
<th>Property</th>
<th>Value</th>
</tr>
</thead>
<tbody id="relay-info-table-body">
</tbody>
</table>
</div>
</div>
</div>
</div> <!-- End Main Sections Wrapper -->
<!-- DATABASE STATISTICS Section --> <!-- DATABASE STATISTICS Section -->
<div class="section flex-section" id="databaseStatisticsSection" style="display: none;"> <div class="section flex-section" id="databaseStatisticsSection" style="display: none;">
<div class="section-header"> <div class="section-header">
@@ -95,36 +60,30 @@
<!-- Database Overview Table --> <!-- Database Overview Table -->
<div class="input-group"> <div class="input-group">
<label>Database Overview:</label>
<div class="config-table-container"> <div class="config-table-container">
<table class="config-table" id="stats-overview-table"> <table class="config-table" id="stats-overview-table">
<thead> <thead>
<tr> <tr>
<th>Metric</th> <th>Metric</th>
<th>Value</th> <th>Value</th>
<th>Description</th>
</tr> </tr>
</thead> </thead>
<tbody id="stats-overview-table-body"> <tbody id="stats-overview-table-body">
<tr> <tr>
<td>Database Size</td> <td>Database Size</td>
<td id="db-size">-</td> <td id="db-size">-</td>
<td>Current database file size</td>
</tr> </tr>
<tr> <tr>
<td>Total Events</td> <td>Total Events</td>
<td id="total-events">-</td> <td id="total-events">-</td>
<td>Total number of events stored</td>
</tr> </tr>
<tr> <tr>
<td>Oldest Event</td> <td>Oldest Event</td>
<td id="oldest-event">-</td> <td id="oldest-event">-</td>
<td>Timestamp of oldest event</td>
</tr> </tr>
<tr> <tr>
<td>Newest Event</td> <td>Newest Event</td>
<td id="newest-event">-</td> <td id="newest-event">-</td>
<td>Timestamp of newest event</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
@@ -161,24 +120,20 @@
<tr> <tr>
<th>Period</th> <th>Period</th>
<th>Events</th> <th>Events</th>
<th>Description</th>
</tr> </tr>
</thead> </thead>
<tbody id="stats-time-table-body"> <tbody id="stats-time-table-body">
<tr> <tr>
<td>Last 24 Hours</td> <td>Last 24 Hours</td>
<td id="events-24h">-</td> <td id="events-24h">-</td>
<td>Events in the last day</td>
</tr> </tr>
<tr> <tr>
<td>Last 7 Days</td> <td>Last 7 Days</td>
<td id="events-7d">-</td> <td id="events-7d">-</td>
<td>Events in the last week</td>
</tr> </tr>
<tr> <tr>
<td>Last 30 Days</td> <td>Last 30 Days</td>
<td id="events-30d">-</td> <td id="events-30d">-</td>
<td>Events in the last month</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>

View File

@@ -22,10 +22,13 @@ let currentConfig = null;
// Global subscription state // Global subscription state
let relayPool = null; let relayPool = null;
let subscriptionId = null; let subscriptionId = null;
let isSubscribed = false; // Flag to prevent multiple simultaneous subscriptions
// Relay connection state // Relay connection state
let relayInfo = null; let relayInfo = null;
let isRelayConnected = false; let isRelayConnected = false;
let relayPubkey = null; let relayPubkey = null;
// Simple relay URL object (replaces DOM element)
let relayConnectionUrl = { value: '' };
// Database statistics auto-refresh // Database statistics auto-refresh
let statsAutoRefreshInterval = null; let statsAutoRefreshInterval = null;
let countdownInterval = null; let countdownInterval = null;
@@ -46,13 +49,6 @@ const persistentUserPubkey = document.getElementById('persistent-user-pubkey');
const persistentUserAbout = document.getElementById('persistent-user-about'); const persistentUserAbout = document.getElementById('persistent-user-about');
const persistentUserDetails = document.getElementById('persistent-user-details'); const persistentUserDetails = document.getElementById('persistent-user-details');
const fetchConfigBtn = document.getElementById('fetch-config-btn'); const fetchConfigBtn = document.getElementById('fetch-config-btn');
// Relay connection elements
const relayConnectionUrl = document.getElementById('relay-connection-url');
const relayPubkeyManual = document.getElementById('relay-pubkey-manual');
const relayConnectionStatus = document.getElementById('relay-connection-status');
const connectRelayBtn = document.getElementById('connect-relay-btn');
const disconnectRelayBtn = document.getElementById('disconnect-relay-btn');
const restartRelayBtn = document.getElementById('restart-relay-btn');
const configDisplay = document.getElementById('config-display'); const configDisplay = document.getElementById('config-display');
const configTableBody = document.getElementById('config-table-body'); const configTableBody = document.getElementById('config-table-body');
@@ -83,40 +79,6 @@ function log(message, type = 'INFO') {
// UI logging removed - using console only // UI logging removed - using console only
} }
// Show authentication warning message
function showAuthenticationWarning(message) {
// Remove any existing warning
hideAuthenticationWarning();
// Create warning element
const warningDiv = document.createElement('div');
warningDiv.id = 'auth-warning-message';
warningDiv.className = 'auth-warning-message';
warningDiv.innerHTML = `
<div class="warning-content">
<strong>⚠️ Authentication Issue:</strong> ${message}
<br><br>
<small>This usually means your pubkey is not authorized as an admin for this relay.
Please check that you are using the correct admin pubkey that was shown during relay startup.</small>
</div>
`;
// Insert warning at the top of the relay connection section
const relaySection = document.getElementById('relay-connection-section');
if (relaySection) {
relaySection.insertBefore(warningDiv, relaySection.firstChild);
}
log(`Authentication warning displayed: ${message}`, 'WARNING');
}
// Hide authentication warning message
function hideAuthenticationWarning() {
const warningDiv = document.getElementById('auth-warning-message');
if (warningDiv) {
warningDiv.remove();
}
}
// NIP-59 helper: randomize created_at to thwart time-analysis (past 2 days) // NIP-59 helper: randomize created_at to thwart time-analysis (past 2 days)
function randomNow() { function randomNow() {
@@ -237,239 +199,9 @@ async function testWebSocketConnection(wsUrl) {
}); });
} }
// Connect to relay (NIP-11 + WebSocket test)
async function connectToRelay() {
try {
const url = relayConnectionUrl.value.trim();
if (!url) {
throw new Error('Please enter a relay URL');
}
// Update UI to show connecting state
updateRelayConnectionStatus('connecting');
connectRelayBtn.disabled = true;
log(`Connecting to relay: ${url}`, 'INFO');
// Clear any previous authentication warnings
hideAuthenticationWarning();
let fetchedRelayInfo;
try {
// Step 1: Try to fetch NIP-11 relay information
fetchedRelayInfo = await fetchRelayInfo(url);
// Check if NIP-11 response includes a pubkey
if (fetchedRelayInfo.pubkey) {
// NIP-11 provided pubkey - populate the manual input field
log(`NIP-11 provided relay pubkey: ${fetchedRelayInfo.pubkey.substring(0, 16)}...`, 'INFO');
relayPubkeyManual.value = fetchedRelayInfo.pubkey;
} else {
// NIP-11 response missing pubkey, check for manual input
log('NIP-11 response missing pubkey, checking for manual input...', 'INFO');
const manualPubkey = relayPubkeyManual.value.trim();
if (!manualPubkey) {
throw new Error('Relay NIP-11 response does not include a pubkey. Please enter the relay pubkey manually (shown during relay startup).');
}
if (!/^[0-9a-fA-F]{64}$/.test(manualPubkey)) {
throw new Error('Manual relay pubkey must be exactly 64 hexadecimal characters');
}
log(`Using manual relay pubkey: ${manualPubkey.substring(0, 16)}...`, 'INFO');
// Add manual pubkey to the fetched relay info
fetchedRelayInfo.pubkey = manualPubkey;
// If relay info was completely empty, create minimal info
if (Object.keys(fetchedRelayInfo).length === 1) {
fetchedRelayInfo = {
name: 'C-Relay (Manual Config)',
description: 'C-Relay instance - pubkey provided manually',
pubkey: manualPubkey,
contact: 'admin@manual.config.relay',
supported_nips: [1, 9, 11, 13, 15, 20, 33, 40, 42],
software: 'https://github.com/0xtrr/c-relay',
version: '1.0.0'
};
}
}
} catch (nip11Error) {
// If NIP-11 completely fails (network error, etc.), require manual pubkey
const manualPubkey = relayPubkeyManual.value.trim();
if (!manualPubkey) {
throw new Error(`NIP-11 fetch failed: ${nip11Error.message}. Please enter the relay pubkey manually if the relay hasn't been configured yet.`);
}
if (!/^[0-9a-fA-F]{64}$/.test(manualPubkey)) {
throw new Error('Manual relay pubkey must be exactly 64 hexadecimal characters');
}
log(`NIP-11 failed, using manual relay pubkey: ${manualPubkey.substring(0, 16)}...`, 'INFO');
// Create minimal relay info with manual pubkey
fetchedRelayInfo = {
name: 'C-Relay (Manual Config)',
description: 'C-Relay instance - pubkey provided manually',
pubkey: manualPubkey,
contact: 'admin@manual.config.relay',
supported_nips: [1, 9, 11, 13, 15, 20, 33, 40, 42],
software: 'https://github.com/0xtrr/c-relay',
version: '1.0.0'
};
}
// Step 2: Test WebSocket connection
await testWebSocketConnection(url);
// Step 3: Update global state
relayInfo = fetchedRelayInfo;
relayPubkey = fetchedRelayInfo.pubkey;
isRelayConnected = true;
// Step 4: Update UI
updateRelayConnectionStatus('connected');
updateAdminSectionsVisibility();
// Step 5: Relay URL updated
// Step 6: Automatically load configuration and auth rules
log('Relay connected successfully. Auto-loading configuration and auth rules...', 'INFO');
// Auto-fetch configuration
setTimeout(() => {
fetchConfiguration().catch(error => {
log('Auto-fetch configuration failed: ' + error.message, 'ERROR');
});
}, 500);
// Auto-fetch auth rules
setTimeout(() => {
loadAuthRules().catch(error => {
log('Auto-fetch auth rules failed: ' + error.message, 'ERROR');
});
}, 1000);
// Auto-fetch database statistics
setTimeout(() => {
sendStatsQuery().catch(error => {
log('Auto-fetch statistics failed: ' + error.message, 'ERROR');
});
}, 1500);
log(`Successfully connected to relay: ${relayInfo.name || 'Unknown'}`, 'INFO');
} catch (error) {
log(`Failed to connect to relay: ${error.message}`, 'ERROR');
// Check if this is an authentication-related error
if (error.message.includes('authentication') ||
error.message.includes('auth') ||
error.message.includes('permission') ||
error.message.includes('unauthorized') ||
error.message.includes('forbidden')) {
updateRelayConnectionStatus('auth_error');
showAuthenticationWarning(error.message);
} else {
updateRelayConnectionStatus('error');
}
// Reset state on failure
relayInfo = null;
relayPubkey = null;
isRelayConnected = false;
} finally {
connectRelayBtn.disabled = false;
}
}
// Disconnect from relay
function disconnectFromRelay() {
try {
log('Disconnecting from relay...', 'INFO');
// Clean up relay pool if exists
if (relayPool) {
const url = relayConnectionUrl.value.trim();
if (url) {
relayPool.close([url]);
}
relayPool = null;
subscriptionId = null;
}
// Reset state
relayInfo = null;
relayPubkey = null;
isRelayConnected = false;
// Update UI
updateRelayConnectionStatus('disconnected');
hideRelayInfo();
updateAdminSectionsVisibility();
// Hide any authentication warnings
hideAuthenticationWarning();
log('Disconnected from relay', 'INFO');
} catch (error) {
log(`Error during relay disconnection: ${error.message}`, 'ERROR');
}
}
// Update relay connection status UI
function updateRelayConnectionStatus(status) {
if (!relayConnectionStatus) return;
switch (status) {
case 'connecting':
relayConnectionStatus.textContent = 'CONNECTING...';
relayConnectionStatus.className = 'status connected';
connectRelayBtn.disabled = true;
disconnectRelayBtn.disabled = true;
restartRelayBtn.disabled = true;
break;
case 'connected':
relayConnectionStatus.textContent = 'CONNECTED';
relayConnectionStatus.className = 'status connected';
connectRelayBtn.disabled = true;
disconnectRelayBtn.disabled = false;
restartRelayBtn.disabled = false;
break;
case 'disconnected':
relayConnectionStatus.textContent = 'NOT CONNECTED';
relayConnectionStatus.className = 'status disconnected';
connectRelayBtn.disabled = false;
disconnectRelayBtn.disabled = true;
restartRelayBtn.disabled = true;
break;
case 'error':
relayConnectionStatus.textContent = 'CONNECTION FAILED';
relayConnectionStatus.className = 'status error';
connectRelayBtn.disabled = false;
disconnectRelayBtn.disabled = true;
restartRelayBtn.disabled = true;
break;
case 'auth_error':
relayConnectionStatus.textContent = 'AUTHENTICATION FAILED';
relayConnectionStatus.className = 'status error';
connectRelayBtn.disabled = false;
disconnectRelayBtn.disabled = true;
restartRelayBtn.disabled = true;
break;
}
}
// Hide relay information display (placeholder for removed functionality)
function hideRelayInfo() {
// Relay info display functionality has been removed
console.log('Relay info display functionality has been removed');
}
// Check for existing authentication state with multiple API methods and retry logic // Check for existing authentication state with multiple API methods and retry logic
async function checkExistingAuthWithRetries() { async function checkExistingAuthWithRetries() {
@@ -560,25 +292,88 @@ async function restoreAuthenticationState(pubkey) {
showProfileInHeader(); showProfileInHeader();
loadUserProfile(); loadUserProfile();
// Automatically attempt to connect to relay after restoring authentication state // Automatically set up relay connection (but don't show admin sections yet)
console.log('✅ Authentication state restored - automatically attempting to connect to relay...'); await setupAutomaticRelayConnection();
setTimeout(() => {
connectToRelay().catch(error => {
console.log(`Automatic relay connection failed: ${error.message}`);
// Check if this is an authentication-related error
if (error.message.includes('authentication') ||
error.message.includes('auth') ||
error.message.includes('permission') ||
error.message.includes('unauthorized') ||
error.message.includes('forbidden')) {
showAuthenticationWarning(error.message);
}
});
}, 500); // Small delay to allow profile loading to complete
console.log('✅ Authentication state restored successfully'); console.log('✅ Authentication state restored successfully');
} }
// Automatically set up relay connection based on current page URL
async function setupAutomaticRelayConnection(showSections = false) {
try {
// Get the current page URL and convert to WebSocket URL
const currentUrl = window.location.href;
let relayUrl = '';
if (currentUrl.startsWith('https://')) {
relayUrl = currentUrl.replace('https://', 'wss://');
} else if (currentUrl.startsWith('http://')) {
relayUrl = currentUrl.replace('http://', 'ws://');
} else {
// Fallback for development
relayUrl = 'ws://localhost:8888';
}
// Remove any path components to get just the base URL
const url = new URL(relayUrl);
relayUrl = `${url.protocol}//${url.host}`;
// Set the relay URL
relayConnectionUrl.value = relayUrl;
console.log('🔗 Auto-setting relay URL to:', relayUrl);
// Fetch relay info to get pubkey
try {
const httpUrl = relayUrl.replace('ws', 'http').replace('wss', 'https');
const relayInfo = await fetchRelayInfo(httpUrl);
if (relayInfo && relayInfo.pubkey) {
relayPubkey = relayInfo.pubkey;
console.log('🔑 Auto-fetched relay pubkey:', relayPubkey.substring(0, 16) + '...');
} else {
// Use fallback pubkey
relayPubkey = '4f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa';
console.log('⚠️ Using fallback relay pubkey');
}
} catch (error) {
console.log('⚠️ Could not fetch relay info, using fallback pubkey:', error.message);
relayPubkey = '4f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa';
}
// Initialize relay pool for admin API communication
if (!relayPool) {
relayPool = new window.NostrTools.SimplePool();
console.log('🔌 Initialized SimplePool for admin API communication');
}
// Set up subscription to receive admin API responses
await subscribeToConfiguration();
console.log('📡 Subscription established for admin API responses');
// Mark as connected
isRelayConnected = true;
// Update relay info in header
updateRelayInfoInHeader();
// Only show admin sections if explicitly requested
if (showSections) {
updateAdminSectionsVisibility();
}
console.log('✅ Automatic relay connection setup complete');
} catch (error) {
console.error('❌ Failed to setup automatic relay connection:', error);
// Still mark as connected to allow basic functionality
isRelayConnected = true;
if (showSections) {
updateAdminSectionsVisibility();
}
}
}
// Legacy function for backward compatibility // Legacy function for backward compatibility
async function checkExistingAuth() { async function checkExistingAuth() {
return await checkExistingAuthWithRetries(); return await checkExistingAuthWithRetries();
@@ -611,6 +406,8 @@ async function initializeApp() {
if (wasAlreadyLoggedIn) { if (wasAlreadyLoggedIn) {
console.log('User was already logged in, showing profile in header'); console.log('User was already logged in, showing profile in header');
showProfileInHeader(); showProfileInHeader();
// Show admin sections since user is already authenticated and relay is connected
updateAdminSectionsVisibility();
} else { } else {
console.log('No existing authentication found, showing login modal'); console.log('No existing authentication found, showing login modal');
showLoginModal(); showLoginModal();
@@ -640,20 +437,8 @@ function handleAuthEvent(event) {
showProfileInHeader(); showProfileInHeader();
loadUserProfile(); loadUserProfile();
// Automatically attempt to connect to relay after successful login // Automatically set up relay connection and show admin sections
console.log('Login successful. Automatically attempting to connect to relay...'); setupAutomaticRelayConnection(true);
setTimeout(() => {
connectToRelay().catch(error => {
console.log(`Automatic relay connection failed: ${error.message}`);
// Check if this is an authentication-related error
if (error.message.includes('authentication') ||
error.message.includes('auth') ||
error.message.includes('permission') ||
error.message.includes('unauthorized')) {
showAuthenticationWarning(error.message);
}
});
}, 500); // Small delay to allow profile loading to complete
} else if (error) { } else if (error) {
console.log(`Authentication error: ${error}`); console.log(`Authentication error: ${error}`);
@@ -668,11 +453,9 @@ function handleLogoutEvent() {
isLoggedIn = false; isLoggedIn = false;
currentConfig = null; currentConfig = null;
// Clean up relay connection // Reset relay connection state
disconnectFromRelay(); isRelayConnected = false;
relayPubkey = null;
// Hide any authentication warnings
hideAuthenticationWarning();
// Reset UI - hide profile and show login modal // Reset UI - hide profile and show login modal
hideProfileFromHeader(); hideProfileFromHeader();
@@ -700,7 +483,18 @@ function updateAdminSectionsVisibility() {
// Start/stop auto-refresh based on visibility // Start/stop auto-refresh based on visibility
if (shouldShow && databaseStatisticsSection && databaseStatisticsSection.style.display === 'block') { if (shouldShow && databaseStatisticsSection && databaseStatisticsSection.style.display === 'block') {
// Load statistics immediately, then start auto-refresh
sendStatsQuery().catch(error => {
console.log('Auto-fetch statistics failed: ' + error.message);
});
startStatsAutoRefresh(); startStatsAutoRefresh();
// Also load configuration and auth rules automatically when sections become visible
fetchConfiguration().catch(error => {
console.log('Auto-fetch configuration failed: ' + error.message);
});
loadAuthRules().catch(error => {
console.log('Auto-load auth rules failed: ' + error.message);
});
} else { } else {
stopStatsAutoRefresh(); stopStatsAutoRefresh();
} }
@@ -947,8 +741,6 @@ async function logout() {
// Stop auto-refresh before disconnecting // Stop auto-refresh before disconnecting
stopStatsAutoRefresh(); stopStatsAutoRefresh();
// Clean up relay connection
disconnectFromRelay();
// Clean up configuration pool // Clean up configuration pool
if (relayPool) { if (relayPool) {
@@ -959,6 +751,8 @@ async function logout() {
} }
relayPool = null; relayPool = null;
subscriptionId = null; subscriptionId = null;
// Reset subscription flag
isSubscribed = false;
} }
await nlLite.logout(); await nlLite.logout();
@@ -967,8 +761,11 @@ async function logout() {
isLoggedIn = false; isLoggedIn = false;
currentConfig = null; currentConfig = null;
// Hide any authentication warnings // Reset relay connection state
hideAuthenticationWarning(); isRelayConnected = false;
relayPubkey = null;
// Reset subscription flag
isSubscribed = false;
// Reset UI - hide profile and show login modal // Reset UI - hide profile and show login modal
hideProfileFromHeader(); hideProfileFromHeader();
@@ -978,7 +775,6 @@ async function logout() {
updateAdminSectionsVisibility(); updateAdminSectionsVisibility();
log('Logged out successfully', 'INFO'); log('Logged out successfully', 'INFO');
} catch (error) { } catch (error) {
log('Logout failed: ' + error.message, 'ERROR'); log('Logout failed: ' + error.message, 'ERROR');
} }
@@ -1010,6 +806,12 @@ async function subscribeToConfiguration() {
try { try {
console.log('=== STARTING SIMPLEPOOL CONFIGURATION SUBSCRIPTION ==='); console.log('=== STARTING SIMPLEPOOL CONFIGURATION SUBSCRIPTION ===');
// Prevent multiple simultaneous subscription attempts
if (isSubscribed) {
console.log('Subscription already established, skipping duplicate subscription attempt');
return true;
}
if (!isLoggedIn) { if (!isLoggedIn) {
console.log('WARNING: Not logged in, but proceeding with subscription test'); console.log('WARNING: Not logged in, but proceeding with subscription test');
} }
@@ -1022,16 +824,14 @@ async function subscribeToConfiguration() {
console.log(`Connecting to relay via SimplePool: ${url}`); console.log(`Connecting to relay via SimplePool: ${url}`);
// Clean up existing pool // Reuse existing pool if available, otherwise create new one
if (relayPool) { if (!relayPool) {
console.log('Closing existing pool connection'); console.log('Creating new SimplePool instance');
relayPool.close([url]); relayPool = new window.NostrTools.SimplePool();
relayPool = null; } else {
subscriptionId = null; console.log('Reusing existing SimplePool instance');
} }
// Create new SimplePool instance
relayPool = new window.NostrTools.SimplePool();
subscriptionId = generateSubId(); subscriptionId = generateSubId();
console.log(`Generated subscription ID: ${subscriptionId}`); console.log(`Generated subscription ID: ${subscriptionId}`);
@@ -1050,6 +850,7 @@ async function subscribeToConfiguration() {
"#p": [userPubkey], // Only DMs directed to this user "#p": [userPubkey], // Only DMs directed to this user
limit: 50 limit: 50
}, { }, {
since: Math.floor(Date.now() / 1000), // Start from current time
kinds: [1059], // NIP-17 GiftWrap events kinds: [1059], // NIP-17 GiftWrap events
"#p": [userPubkey], // Only GiftWrap events addressed to this user "#p": [userPubkey], // Only GiftWrap events addressed to this user
limit: 50 limit: 50
@@ -1152,6 +953,9 @@ async function subscribeToConfiguration() {
// Store subscription for cleanup // Store subscription for cleanup
relayPool.currentSubscription = subscription; relayPool.currentSubscription = subscription;
// Mark as subscribed to prevent duplicate attempts
isSubscribed = true;
console.log('SimplePool subscription established'); console.log('SimplePool subscription established');
return true; return true;
@@ -1313,6 +1117,9 @@ function handleConfigQueryResponse(responseData) {
// Display the configuration using the original display function // Display the configuration using the original display function
displayConfiguration(syntheticEvent); displayConfiguration(syntheticEvent);
// Update relay info in header with config data
updateStoredRelayInfo(responseData);
log(`Configuration loaded: ${responseData.total_results} parameters`, 'INFO'); log(`Configuration loaded: ${responseData.total_results} parameters`, 'INFO');
} else { } else {
console.log('No configuration data received'); console.log('No configuration data received');
@@ -1547,14 +1354,16 @@ async function fetchConfiguration() {
throw new Error('Must be connected to relay to fetch configuration. Please use the Relay Connection section first.'); throw new Error('Must be connected to relay to fetch configuration. Please use the Relay Connection section first.');
} }
// First establish subscription to receive responses // First establish subscription to receive responses (only if not already subscribed)
const subscriptionResult = await subscribeToConfiguration(); const subscriptionResult = await subscribeToConfiguration();
if (!subscriptionResult) { if (!subscriptionResult) {
throw new Error('Failed to establish admin response subscription'); throw new Error('Failed to establish admin response subscription');
} }
// Wait a moment for subscription to be established // Wait a moment for subscription to be established (only if we just created it)
if (!isSubscribed) {
await new Promise(resolve => setTimeout(resolve, 500)); await new Promise(resolve => setTimeout(resolve, 500));
}
// Send config query command if logged in // Send config query command if logged in
if (isLoggedIn && userPubkey && relayPool) { if (isLoggedIn && userPubkey && relayPool) {
@@ -1910,6 +1719,43 @@ if (logoutBtn) {
}); });
} }
// Initialize dark mode button handler
const darkModeBtn = document.getElementById('dark-mode-btn');
if (darkModeBtn) {
darkModeBtn.addEventListener('click', function(e) {
e.stopPropagation(); // Prevent profile area click
toggleDarkMode();
});
}
// Initialize relay pubkey container click handler for clipboard copy
const relayPubkeyContainer = document.getElementById('relay-pubkey-container');
if (relayPubkeyContainer) {
relayPubkeyContainer.addEventListener('click', async function() {
const relayPubkeyElement = document.getElementById('relay-pubkey');
if (relayPubkeyElement && relayPubkeyElement.textContent !== 'Loading...') {
try {
// Get the full npub (remove line breaks for clipboard)
const fullNpub = relayPubkeyElement.textContent.replace(/\n/g, '');
await navigator.clipboard.writeText(fullNpub);
// Add copied class for visual feedback
relayPubkeyContainer.classList.add('copied');
// Remove the class after animation completes
setTimeout(() => {
relayPubkeyContainer.classList.remove('copied');
}, 500);
log('Relay npub copied to clipboard', 'INFO');
} catch (error) {
log('Failed to copy relay npub to clipboard', 'ERROR');
}
}
});
}
// Event handlers // Event handlers
fetchConfigBtn.addEventListener('click', function (e) { fetchConfigBtn.addEventListener('click', function (e) {
e.preventDefault(); e.preventDefault();
@@ -1922,28 +1768,6 @@ fetchConfigBtn.addEventListener('click', function (e) {
// Relay connection event handlers
connectRelayBtn.addEventListener('click', function (e) {
e.preventDefault();
e.stopPropagation();
connectToRelay().catch(error => {
console.log('Relay connection failed: ' + error.message);
});
});
disconnectRelayBtn.addEventListener('click', function (e) {
e.preventDefault();
e.stopPropagation();
disconnectFromRelay();
});
restartRelayBtn.addEventListener('click', function (e) {
e.preventDefault();
e.stopPropagation();
sendRestartCommand().catch(error => {
log(`Restart command failed: ${error.message}`, 'ERROR');
});
});
// ================================ // ================================
// AUTH RULES MANAGEMENT FUNCTIONS // AUTH RULES MANAGEMENT FUNCTIONS
@@ -3415,16 +3239,90 @@ function addMessageToInbox(direction, message, timestamp, pubkey = null) {
} }
} }
// Update relay info in header
function updateRelayInfoInHeader() {
const relayNameElement = document.getElementById('relay-name');
const relayPubkeyElement = document.getElementById('relay-pubkey');
const relayDescriptionElement = document.getElementById('relay-description');
if (!relayNameElement || !relayPubkeyElement || !relayDescriptionElement) {
return;
}
// Get relay info from NIP-11 data or use defaults
const relayInfo = getRelayInfo();
const relayName = relayInfo.name || 'C-Relay';
const relayDescription = relayInfo.description || 'Nostr Relay';
// Convert relay pubkey to npub
let relayNpub = 'Loading...';
if (relayPubkey) {
try {
relayNpub = window.NostrTools.nip19.npubEncode(relayPubkey);
} catch (error) {
console.log('Failed to encode relay pubkey to npub:', error.message);
relayNpub = relayPubkey.substring(0, 16) + '...';
}
}
// Format npub into 3 lines of 21 characters each
let formattedNpub = relayNpub;
if (relayNpub.length === 63) {
formattedNpub = relayNpub.substring(0, 21) + '\n' +
relayNpub.substring(21, 42) + '\n' +
relayNpub.substring(42, 63);
}
relayNameElement.textContent = relayName;
relayPubkeyElement.textContent = formattedNpub;
relayDescriptionElement.textContent = relayDescription;
}
// Global variable to store relay info from NIP-11 or config
let relayInfoData = null;
// Helper function to get relay info from stored data
function getRelayInfo() {
// Return stored relay info if available, otherwise defaults
if (relayInfoData) {
return relayInfoData;
}
// Default values
return {
name: 'C-Relay',
description: 'Nostr Relay',
pubkey: relayPubkey
};
}
// Update stored relay info when config is loaded
function updateStoredRelayInfo(configData) {
if (configData && configData.data) {
// Extract relay info from config data
const relayName = configData.data.find(item => item.key === 'relay_name')?.value || 'C-Relay';
const relayDescription = configData.data.find(item => item.key === 'relay_description')?.value || 'Nostr Relay';
relayInfoData = {
name: relayName,
description: relayDescription,
pubkey: relayPubkey
};
// Update header immediately
updateRelayInfoInHeader();
}
}
// Helper function to get relay pubkey // Helper function to get relay pubkey
function getRelayPubkey() { function getRelayPubkey() {
// Use the dynamically fetched relay pubkey if available // Use the dynamically fetched relay pubkey if available
if (relayPubkey && isRelayConnected) { if (relayPubkey) {
return relayPubkey; return relayPubkey;
} }
// Fallback to hardcoded value for testing/development // No fallback - throw error if relay pubkey not available
log('Warning: Using hardcoded relay pubkey. Please connect to relay first.', 'WARNING'); throw new Error('Relay pubkey not available. Please connect to relay first.');
return '4f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa';
} }
// Enhanced SimplePool message handler to capture test responses // Enhanced SimplePool message handler to capture test responses
@@ -3928,37 +3826,54 @@ document.addEventListener('DOMContentLoaded', () => {
} }
}); });
// Set default relay URL based on where the page is being served from
function setDefaultRelayUrl() {
const relayUrlInput = document.getElementById('relay-connection-url');
if (!relayUrlInput) return;
// Get the current page's protocol and hostname // Dark mode functionality
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; function toggleDarkMode() {
const hostname = window.location.hostname; const body = document.body;
const port = window.location.port; const isDarkMode = body.classList.contains('dark-mode');
// Construct the relay URL if (isDarkMode) {
let relayUrl; body.classList.remove('dark-mode');
if (hostname === 'localhost' || hostname === '127.0.0.1') { localStorage.setItem('darkMode', 'false');
// For localhost, default to ws://localhost:8888 updateDarkModeButton(false);
relayUrl = 'ws://localhost:8888'; log('Switched to light mode', 'INFO');
} else { } else {
// For production, use the same hostname with WebSocket protocol body.classList.add('dark-mode');
// Remove port from URL since relay typically runs on standard ports (80/443) localStorage.setItem('darkMode', 'true');
relayUrl = `${protocol}//${hostname}`; updateDarkModeButton(true);
log('Switched to dark mode', 'INFO');
}
} }
relayUrlInput.value = relayUrl; function updateDarkModeButton(isDarkMode) {
log(`Default relay URL set to: ${relayUrl}`, 'INFO'); const darkModeBtn = document.getElementById('dark-mode-btn');
if (darkModeBtn) {
darkModeBtn.textContent = isDarkMode ? 'LIGHT MODE' : 'DARK MODE';
}
}
function initializeDarkMode() {
const savedDarkMode = localStorage.getItem('darkMode');
const prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
const shouldBeDark = savedDarkMode === 'true' || (savedDarkMode === null && prefersDark);
if (shouldBeDark) {
document.body.classList.add('dark-mode');
updateDarkModeButton(true);
} else {
updateDarkModeButton(false);
}
} }
// Initialize the app // Initialize the app
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
console.log('C-Relay Admin API interface loaded'); console.log('C-Relay Admin API interface loaded');
// Set default relay URL based on current page location // Initialize dark mode
setDefaultRelayUrl(); initializeDarkMode();
// Start RELAY letter animation
startRelayAnimation();
// Ensure admin sections are hidden by default on page load // Ensure admin sections are hidden by default on page load
updateAdminSectionsVisibility(); updateAdminSectionsVisibility();
@@ -3969,3 +3884,38 @@ document.addEventListener('DOMContentLoaded', () => {
setTimeout(enhancePoolForTesting, 2000); setTimeout(enhancePoolForTesting, 2000);
}, 100); }, 100);
}); });
// RELAY letter animation function
function startRelayAnimation() {
const letters = document.querySelectorAll('.relay-letter');
let currentIndex = 0;
function animateLetter() {
// Remove underline from all letters first
letters.forEach(letter => letter.classList.remove('underlined'));
// Add underline to current letter
if (letters[currentIndex]) {
letters[currentIndex].classList.add('underlined');
}
// Move to next letter
currentIndex++;
// If we've gone through all letters, remove all underlines and wait 4000ms then restart
if (currentIndex > letters.length) {
// Remove all underlines before the pause
letters.forEach(letter => letter.classList.remove('underlined'));
setTimeout(() => {
currentIndex = 0;
animateLetter();
}, 4000);
} else {
// Otherwise, continue to next letter after 200ms
setTimeout(animateLetter, 100);
}
}
// Start the animation
animateLetter();
}

View File

@@ -9,11 +9,21 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
BUILD_DIR="$SCRIPT_DIR/build" BUILD_DIR="$SCRIPT_DIR/build"
DOCKERFILE="$SCRIPT_DIR/Dockerfile.alpine-musl" DOCKERFILE="$SCRIPT_DIR/Dockerfile.alpine-musl"
# Parse command line arguments
DEBUG_BUILD=false
if [[ "$1" == "--debug" ]]; then
DEBUG_BUILD=true
echo "==========================================" echo "=========================================="
echo "C-Relay MUSL Static Binary Builder" echo "C-Relay MUSL Static Binary Builder (DEBUG MODE)"
echo "==========================================" echo "=========================================="
else
echo "=========================================="
echo "C-Relay MUSL Static Binary Builder (PRODUCTION MODE)"
echo "=========================================="
fi
echo "Project directory: $SCRIPT_DIR" echo "Project directory: $SCRIPT_DIR"
echo "Build directory: $BUILD_DIR" echo "Build directory: $BUILD_DIR"
echo "Debug build: $DEBUG_BUILD"
echo "" echo ""
# Create build directory # Create build directory
@@ -83,6 +93,7 @@ echo ""
$DOCKER_CMD build \ $DOCKER_CMD build \
--platform "$PLATFORM" \ --platform "$PLATFORM" \
--build-arg DEBUG_BUILD=$DEBUG_BUILD \
-f "$DOCKERFILE" \ -f "$DOCKERFILE" \
-t c-relay-musl-builder:latest \ -t c-relay-musl-builder:latest \
--progress=plain \ --progress=plain \
@@ -105,6 +116,7 @@ echo "=========================================="
# Build the builder stage to extract the binary # Build the builder stage to extract the binary
$DOCKER_CMD build \ $DOCKER_CMD build \
--platform "$PLATFORM" \ --platform "$PLATFORM" \
--build-arg DEBUG_BUILD=$DEBUG_BUILD \
--target builder \ --target builder \
-f "$DOCKERFILE" \ -f "$DOCKERFILE" \
-t c-relay-static-builder-stage:latest \ -t c-relay-static-builder-stage:latest \
@@ -179,11 +191,16 @@ echo "=========================================="
echo "Binary: $BUILD_DIR/$OUTPUT_NAME" echo "Binary: $BUILD_DIR/$OUTPUT_NAME"
echo "Size: $(du -h "$BUILD_DIR/$OUTPUT_NAME" | cut -f1)" echo "Size: $(du -h "$BUILD_DIR/$OUTPUT_NAME" | cut -f1)"
echo "Platform: $PLATFORM" echo "Platform: $PLATFORM"
if [ "$DEBUG_BUILD" = true ]; then
echo "Build Type: DEBUG (with symbols, no optimization)"
else
echo "Build Type: PRODUCTION (optimized, stripped)"
fi
if [ "$TRULY_STATIC" = true ]; then if [ "$TRULY_STATIC" = true ]; then
echo "Type: Fully static binary (Alpine MUSL-based)" echo "Linkage: Fully static binary (Alpine MUSL-based)"
echo "Portability: Works on ANY Linux distribution" echo "Portability: Works on ANY Linux distribution"
else else
echo "Type: Static binary (may have minimal dependencies)" echo "Linkage: Static binary (may have minimal dependencies)"
fi fi
echo "" echo ""
echo "✓ Build complete!" echo "✓ Build complete!"

View File

@@ -39,6 +39,40 @@ Even simpler: Use this one-liner
cd /usr/local/bin/c_relay cd /usr/local/bin/c_relay
sudo -u c-relay ./c_relay --debug-level=5 & sleep 2 && sudo gdb -p $(pgrep c_relay) sudo -u c-relay ./c_relay --debug-level=5 & sleep 2 && sudo gdb -p $(pgrep c_relay)
Once gdb attaches, type continue and wait for the crash. This way the relay starts normally and gdb just monitors it.
Which approach would you like to try?
How to View the Logs
Check systemd journal:
# View all c-relay logs
sudo journalctl -u c-relay
# View recent logs (last 50 lines)
sudo journalctl -u c-relay -n 50
# Follow logs in real-time
sudo journalctl -u c-relay -f
# View logs since last boot
sudo journalctl -u c-relay -b
Check if service is running:
To immediately trim the syslog file size:
Safe Syslog Truncation
Stop syslog service first:
sudo systemctl stop rsyslog
Truncate the syslog file:
sudo truncate -s 0 /var/log/syslog
Restart syslog service:
sudo systemctl start rsyslog
sudo systemctl status rsyslog
sudo -u c-relay ./c_relay --debug-level=5 -r 85d0b37e2ae822966dcadd06b2dc9368cde73865f90ea4d44f8b57d47ef0820a -a 1ec454734dcbf6fe54901ce25c0c7c6bca5edd89443416761fadc321d38df139
./c_relay_static_x86_64 -p 7889 --debug-level=5 -r 85d0b37e2ae822966dcadd06b2dc9368cde73865f90ea4d44f8b57d47ef0820a -a 1ec454734dcbf6fe54901ce25c0c7c6bca5edd89443416761fadc321d38df139

View File

@@ -1 +1 @@
3952751 343475

File diff suppressed because one or more lines are too long