Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6c38aaebf3 | ||
|
|
18b0ac44bf | ||
|
|
b6749eff2f | ||
|
|
c73a103280 | ||
|
|
a5d194f730 |
@@ -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 ===" && \
|
||||||
|
|||||||
171
api/index.css
171
api/index.css
@@ -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;
|
||||||
|
|||||||
103
api/index.html
103
api/index.html
@@ -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-title">RELAY</div>
|
<div class="header-content">
|
||||||
<div class="profile-area" id="profile-area" style="display: none;">
|
<div class="header-title">
|
||||||
<div class="profile-container">
|
<span class="relay-letter" data-letter="R">R</span>
|
||||||
<img id="header-user-image" class="header-user-image" alt="Profile" style="display: none;">
|
<span class="relay-letter" data-letter="E">E</span>
|
||||||
<span id="header-user-name" class="header-user-name">Loading...</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>
|
||||||
<!-- Logout dropdown -->
|
<div class="relay-info">
|
||||||
<div class="logout-dropdown" id="logout-dropdown" style="display: none;">
|
<div id="relay-name" class="relay-name">C-Relay</div>
|
||||||
<button type="button" id="logout-btn" class="logout-btn">LOGOUT</button>
|
<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="admin-label">admin</div>
|
||||||
|
<div class="profile-container">
|
||||||
|
<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>
|
||||||
|
</div>
|
||||||
|
<!-- Logout dropdown -->
|
||||||
|
<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>
|
||||||
|
</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>
|
||||||
|
|||||||
674
api/index.js
674
api/index.js
@@ -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,13 +292,88 @@ async function restoreAuthenticationState(pubkey) {
|
|||||||
showProfileInHeader();
|
showProfileInHeader();
|
||||||
loadUserProfile();
|
loadUserProfile();
|
||||||
|
|
||||||
// Note: Configuration fetching now requires explicit relay connection
|
// Automatically set up relay connection (but don't show admin sections yet)
|
||||||
// User must connect to relay manually after login
|
await setupAutomaticRelayConnection();
|
||||||
console.log('✅ Authentication state restored - connect to relay to fetch configuration');
|
|
||||||
|
|
||||||
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();
|
||||||
@@ -599,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();
|
||||||
@@ -628,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}`);
|
||||||
@@ -656,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();
|
||||||
@@ -688,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();
|
||||||
}
|
}
|
||||||
@@ -935,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) {
|
||||||
@@ -947,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();
|
||||||
@@ -955,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();
|
||||||
@@ -966,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');
|
||||||
}
|
}
|
||||||
@@ -998,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');
|
||||||
}
|
}
|
||||||
@@ -1010,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}`);
|
||||||
@@ -1038,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
|
||||||
@@ -1140,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;
|
||||||
|
|
||||||
@@ -1301,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');
|
||||||
@@ -1535,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)
|
||||||
await new Promise(resolve => setTimeout(resolve, 500));
|
if (!isSubscribed) {
|
||||||
|
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) {
|
||||||
@@ -1898,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();
|
||||||
@@ -1910,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
|
||||||
@@ -3403,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
|
||||||
@@ -3916,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();
|
||||||
@@ -3956,4 +3883,39 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
// Enhance SimplePool for testing after initialization
|
// Enhance SimplePool for testing after initialization
|
||||||
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();
|
||||||
|
}
|
||||||
@@ -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"
|
||||||
|
|
||||||
echo "=========================================="
|
# Parse command line arguments
|
||||||
echo "C-Relay MUSL Static Binary Builder"
|
DEBUG_BUILD=false
|
||||||
echo "=========================================="
|
if [[ "$1" == "--debug" ]]; then
|
||||||
|
DEBUG_BUILD=true
|
||||||
|
echo "=========================================="
|
||||||
|
echo "C-Relay MUSL Static Binary Builder (DEBUG MODE)"
|
||||||
|
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!"
|
||||||
|
|||||||
38
notes.txt
38
notes.txt
@@ -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
|
||||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user