v0.3.17 - Embedded login button
This commit is contained in:
357
api/index.html
357
api/index.html
@@ -243,6 +243,53 @@
|
||||
background-color: var(--secondary-color);
|
||||
}
|
||||
|
||||
.user-info-container {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.user-details {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.login-logout-btn {
|
||||
width: auto;
|
||||
min-width: 120px;
|
||||
padding: 12px 16px;
|
||||
background: var(--secondary-color);
|
||||
color: var(--primary-color);
|
||||
border: var(--border-width) solid var(--primary-color);
|
||||
border-radius: var(--border-radius);
|
||||
font-family: var(--font-family);
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
margin: 0;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.login-logout-btn:hover {
|
||||
border-color: var(--accent-color);
|
||||
}
|
||||
|
||||
.login-logout-btn:active {
|
||||
background: var(--accent-color);
|
||||
color: var(--secondary-color);
|
||||
}
|
||||
|
||||
.login-logout-btn.logout-state {
|
||||
background: var(--accent-color);
|
||||
color: var(--secondary-color);
|
||||
border-color: var(--accent-color);
|
||||
}
|
||||
|
||||
.login-logout-btn.logout-state:hover {
|
||||
background: var(--primary-color);
|
||||
border-color: var(--primary-color);
|
||||
}
|
||||
|
||||
.user-pubkey {
|
||||
font-family: var(--font-family);
|
||||
font-size: 12px;
|
||||
@@ -378,7 +425,19 @@
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
/* Two-Column Layout */
|
||||
.two-column-layout {
|
||||
display: flex;
|
||||
gap: var(--border-width);
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.column {
|
||||
flex: 1;
|
||||
min-width: 300px; /* Ensure columns are at least 300px wide */
|
||||
}
|
||||
|
||||
@media (max-width: 700px) {
|
||||
body {
|
||||
padding: 10px;
|
||||
}
|
||||
@@ -394,6 +453,12 @@
|
||||
h2 {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* Stack columns vertically on screens smaller than 700px */
|
||||
.two-column-layout {
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
@@ -401,70 +466,77 @@
|
||||
<body>
|
||||
<h1>C-RELAY ADMIN API</h1>
|
||||
|
||||
<!-- Login Section -->
|
||||
<div id="login-section">
|
||||
<div class="section">
|
||||
<h2>NOSTR AUTHENTICATION</h2>
|
||||
<p>Please login with your Nostr identity to access the admin interface.</p>
|
||||
<!-- nostr-lite login UI will be injected here -->
|
||||
<!-- Persistent Authentication Header - Always Visible -->
|
||||
<div id="persistent-auth-container" class="section">
|
||||
<div class="user-info-container">
|
||||
<button type="button" id="login-logout-btn" class="login-logout-btn">LOGIN</button>
|
||||
<div class="user-details" id="persistent-user-details" style="display: none;">
|
||||
<div><strong>Name:</strong> <span id="persistent-user-name">Loading...</span></div>
|
||||
<div><strong>Public Key:</strong>
|
||||
<div class="user-pubkey" id="persistent-user-pubkey">Loading...</div>
|
||||
</div>
|
||||
<div><strong>About:</strong> <span id="persistent-user-about">Loading...</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Two-Column Layout: Authentication + Relay Connection -->
|
||||
<div class="two-column-layout">
|
||||
<!-- Login Section - Left Column -->
|
||||
<div id="login-section" class="column">
|
||||
<div class="section">
|
||||
<h2>NOSTR AUTHENTICATION</h2>
|
||||
<p id="login-instructions">Please login with your Nostr identity to access the admin interface.</p>
|
||||
<!-- nostr-lite login UI will be injected here -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Relay Connection Section - Right Column -->
|
||||
<div id="relay-connection-section" class="column">
|
||||
<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="ws://localhost:8888" 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">
|
||||
<small>If the relay hasn't been configured yet, enter the relay pubkey shown during startup</small>
|
||||
</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="test-websocket-btn" disabled>TEST WEBSOCKET</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>
|
||||
|
||||
<!-- Main Interface (hidden until logged in) -->
|
||||
<div id="main-interface" class="hidden">
|
||||
|
||||
<!-- User Info Section - At the top of main interface -->
|
||||
<div class="section">
|
||||
<h2>LOGGED IN USER</h2>
|
||||
<div class="user-info">
|
||||
<div><strong>Name:</strong> <span id="user-name">Loading...</span></div>
|
||||
<div><strong>Public Key:</strong>
|
||||
<div class="user-pubkey" id="user-pubkey">Loading...</div>
|
||||
</div>
|
||||
<div><strong>About:</strong> <span id="user-about">Loading...</span></div>
|
||||
</div>
|
||||
<button type="button" id="logout-btn">LOGOUT</button>
|
||||
</div>
|
||||
|
||||
<!-- Relay Connection Section -->
|
||||
<div class="section">
|
||||
|
||||
<div class="input-group">
|
||||
<label for="relay-connection-url">Relay URL:</label>
|
||||
<input type="text" id="relay-connection-url" value="ws://localhost:8888" 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">
|
||||
<small>If the relay hasn't been configured yet, enter the relay pubkey shown during startup</small>
|
||||
</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="test-websocket-btn" disabled>TEST WEBSOCKET</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>
|
||||
|
||||
<!-- Testing Section - Always Visible -->
|
||||
@@ -675,10 +747,10 @@
|
||||
// DOM elements
|
||||
const loginSection = document.getElementById('login-section');
|
||||
const mainInterface = document.getElementById('main-interface');
|
||||
const userName = document.getElementById('user-name');
|
||||
const userPubkeyDisplay = document.getElementById('user-pubkey');
|
||||
const userAbout = document.getElementById('user-about');
|
||||
const logoutBtn = document.getElementById('logout-btn');
|
||||
const persistentUserName = document.getElementById('persistent-user-name');
|
||||
const persistentUserPubkey = document.getElementById('persistent-user-pubkey');
|
||||
const persistentUserAbout = document.getElementById('persistent-user-about');
|
||||
const persistentUserDetails = document.getElementById('persistent-user-details');
|
||||
const relayUrl = document.getElementById('relay-url');
|
||||
const relayStatus = document.getElementById('relay-status');
|
||||
const fetchConfigBtn = document.getElementById('fetch-config-btn');
|
||||
@@ -1106,6 +1178,7 @@
|
||||
// Show main interface
|
||||
showMainInterface();
|
||||
loadUserProfile();
|
||||
updateLoginLogoutButton();
|
||||
|
||||
// Note: Configuration fetching now requires explicit relay connection
|
||||
// User must connect to relay manually after login
|
||||
@@ -1131,25 +1204,25 @@
|
||||
readonly: true,
|
||||
connect: true,
|
||||
remote: true,
|
||||
otp: true
|
||||
otp: false
|
||||
},
|
||||
floatingTab: {
|
||||
enabled: true,
|
||||
hPosition: 1, // 0.0-1.0 or '95%' from left
|
||||
vPosition: 0, // 0.0-1.0 or '50%' from top
|
||||
appearance: {
|
||||
style: 'square', // 'pill', 'square', 'circle', 'minimal'
|
||||
// icon: '[LOGIN]', // Now uses text-based icons like [LOGIN], [KEY], [NET]
|
||||
text: 'Login'
|
||||
},
|
||||
behavior: {
|
||||
hideWhenAuthenticated: false,
|
||||
showUserInfo: true,
|
||||
autoSlide: true
|
||||
},
|
||||
animation: {
|
||||
slideDirection: 'auto' // 'auto', 'left', 'right', 'up', 'down'
|
||||
}
|
||||
enabled: false,
|
||||
// hPosition: 1, // 0.0-1.0 or '95%' from left
|
||||
// vPosition: 0, // 0.0-1.0 or '50%' from top
|
||||
// appearance: {
|
||||
// style: 'square', // 'pill', 'square', 'circle', 'minimal'
|
||||
// // icon: '[LOGIN]', // Now uses text-based icons like [LOGIN], [KEY], [NET]
|
||||
// text: 'Login'
|
||||
// },
|
||||
// behavior: {
|
||||
// hideWhenAuthenticated: false,
|
||||
// showUserInfo: true,
|
||||
// autoSlide: true
|
||||
// },
|
||||
// animation: {
|
||||
// slideDirection: 'auto' // 'auto', 'left', 'right', 'up', 'down'
|
||||
// }
|
||||
|
||||
}
|
||||
});
|
||||
@@ -1167,6 +1240,7 @@
|
||||
|
||||
// Listen for authentication events
|
||||
window.addEventListener('nlMethodSelected', handleAuthEvent);
|
||||
window.addEventListener('nlLogout', handleLogoutEvent);
|
||||
|
||||
} catch (error) {
|
||||
console.log('Failed to initialize Nostr login: ' + error.message);
|
||||
@@ -1185,6 +1259,7 @@
|
||||
|
||||
showMainInterface();
|
||||
loadUserProfile();
|
||||
updateLoginLogoutButton();
|
||||
|
||||
// Note: Configuration fetching now requires explicit relay connection
|
||||
// User must connect to relay manually after login
|
||||
@@ -1195,11 +1270,47 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Handle logout events
|
||||
function handleLogoutEvent() {
|
||||
console.log('Logout event received');
|
||||
|
||||
userPubkey = null;
|
||||
isLoggedIn = false;
|
||||
currentConfig = null;
|
||||
|
||||
// Clean up relay connection
|
||||
disconnectFromRelay();
|
||||
|
||||
// Reset UI
|
||||
mainInterface.classList.add('hidden');
|
||||
loginSection.classList.remove('hidden');
|
||||
updateConfigStatus(false);
|
||||
relayStatus.textContent = 'READY TO FETCH';
|
||||
relayStatus.className = 'status disconnected';
|
||||
updateLoginLogoutButton();
|
||||
hideAuthRulesSection();
|
||||
|
||||
console.log('Logout event handled successfully');
|
||||
}
|
||||
|
||||
// Disconnect from relay and clean up connections
|
||||
function disconnectFromRelay() {
|
||||
if (relayPool) {
|
||||
console.log('Cleaning up relay pool connection...');
|
||||
const url = relayUrl.value.trim();
|
||||
if (url) {
|
||||
relayPool.close([url]);
|
||||
}
|
||||
relayPool = null;
|
||||
subscriptionId = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Show main interface after login
|
||||
function showMainInterface() {
|
||||
loginSection.classList.add('hidden');
|
||||
mainInterface.classList.remove('hidden');
|
||||
userPubkeyDisplay.textContent = userPubkey;
|
||||
updateLoginLogoutButton();
|
||||
}
|
||||
|
||||
// Load user profile using nostr-tools pool
|
||||
@@ -1207,8 +1318,8 @@
|
||||
if (!userPubkey) return;
|
||||
|
||||
console.log('Loading user profile...');
|
||||
userName.textContent = 'Loading...';
|
||||
userAbout.textContent = 'Loading...';
|
||||
persistentUserName.textContent = 'Loading...';
|
||||
persistentUserAbout.textContent = 'Loading...';
|
||||
|
||||
try {
|
||||
// Create a SimplePool instance for profile loading
|
||||
@@ -1223,11 +1334,16 @@
|
||||
});
|
||||
|
||||
if (events.length > 0) {
|
||||
console.log('Profile event found:', events[0]);
|
||||
const profile = JSON.parse(events[0].content);
|
||||
console.log('Parsed profile:', profile);
|
||||
displayProfile(profile);
|
||||
} else {
|
||||
userName.textContent = 'Anonymous User';
|
||||
userAbout.textContent = 'No profile found';
|
||||
console.log('No profile events found for pubkey:', userPubkey);
|
||||
persistentUserName.textContent = 'Anonymous User';
|
||||
persistentUserAbout.textContent = 'No profile found';
|
||||
// Still show the pubkey since we have it
|
||||
persistentUserPubkey.textContent = userPubkey;
|
||||
}
|
||||
|
||||
// Close the profile pool
|
||||
@@ -1235,8 +1351,10 @@
|
||||
|
||||
} catch (error) {
|
||||
console.log('Profile loading failed: ' + error.message);
|
||||
userName.textContent = 'Error loading profile';
|
||||
userAbout.textContent = error.message;
|
||||
persistentUserName.textContent = 'Error loading profile';
|
||||
persistentUserAbout.textContent = error.message;
|
||||
// Still show the pubkey since we have it
|
||||
persistentUserPubkey.textContent = userPubkey;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1245,10 +1363,12 @@
|
||||
const name = profile.name || profile.display_name || profile.displayName || 'Anonymous User';
|
||||
const about = profile.about || 'No description provided';
|
||||
|
||||
userName.textContent = name;
|
||||
userAbout.textContent = about;
|
||||
// Update persistent user details
|
||||
persistentUserName.textContent = name;
|
||||
persistentUserPubkey.textContent = userPubkey;
|
||||
persistentUserAbout.textContent = about;
|
||||
|
||||
console.log(`Profile loaded for: ${name}`);
|
||||
console.log(`Profile loaded for: ${name} with pubkey: ${userPubkey}`);
|
||||
}
|
||||
|
||||
// Logout function
|
||||
@@ -1257,7 +1377,7 @@
|
||||
try {
|
||||
// Clean up relay connection
|
||||
disconnectFromRelay();
|
||||
|
||||
|
||||
// Clean up configuration pool
|
||||
if (relayPool) {
|
||||
console.log('Closing configuration pool...');
|
||||
@@ -1275,12 +1395,13 @@
|
||||
isLoggedIn = false;
|
||||
currentConfig = null;
|
||||
|
||||
// Reset UI
|
||||
// Reset UI - keep persistent auth container visible
|
||||
mainInterface.classList.add('hidden');
|
||||
loginSection.classList.remove('hidden');
|
||||
updateConfigStatus(false);
|
||||
relayStatus.textContent = 'READY TO FETCH';
|
||||
relayStatus.className = 'status disconnected';
|
||||
updateLoginLogoutButton();
|
||||
|
||||
console.log('Logged out successfully');
|
||||
|
||||
@@ -2224,8 +2345,39 @@
|
||||
|
||||
|
||||
|
||||
// Login/Logout button functionality
|
||||
function updateLoginLogoutButton() {
|
||||
const loginLogoutBtn = document.getElementById('login-logout-btn');
|
||||
if (!loginLogoutBtn) return;
|
||||
|
||||
if (isLoggedIn) {
|
||||
loginLogoutBtn.textContent = 'LOGOUT';
|
||||
loginLogoutBtn.className = 'login-logout-btn logout-state';
|
||||
loginLogoutBtn.onclick = logout;
|
||||
// Show user details when logged in
|
||||
if (persistentUserDetails) {
|
||||
persistentUserDetails.style.display = 'block';
|
||||
}
|
||||
} else {
|
||||
loginLogoutBtn.textContent = 'ADMIN NOSTR LOGIN';
|
||||
loginLogoutBtn.className = 'login-logout-btn';
|
||||
loginLogoutBtn.onclick = () => {
|
||||
if (window.NOSTR_LOGIN_LITE && window.NOSTR_LOGIN_LITE.launch) {
|
||||
window.NOSTR_LOGIN_LITE.launch('login');
|
||||
} else {
|
||||
console.log('NOSTR_LOGIN_LITE not available');
|
||||
}
|
||||
};
|
||||
// Hide user details when logged out
|
||||
if (persistentUserDetails) {
|
||||
persistentUserDetails.style.display = 'none';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Event handlers
|
||||
logoutBtn.addEventListener('click', logout);
|
||||
// Initialize login/logout button
|
||||
updateLoginLogoutButton();
|
||||
fetchConfigBtn.addEventListener('click', function (e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
@@ -2336,9 +2488,18 @@
|
||||
function hideAuthRulesSection() {
|
||||
if (authRulesSection) {
|
||||
authRulesSection.style.display = 'none';
|
||||
authRulesTableContainer.style.display = 'none';
|
||||
authRuleFormContainer.style.display = 'none';
|
||||
authRulesStatusDisplay.style.display = 'none';
|
||||
|
||||
// Add null checks for all elements
|
||||
if (authRulesTableContainer) {
|
||||
authRulesTableContainer.style.display = 'none';
|
||||
}
|
||||
if (authRuleFormContainer) {
|
||||
authRuleFormContainer.style.display = 'none';
|
||||
}
|
||||
if (authRulesStatusDisplay) {
|
||||
authRulesStatusDisplay.style.display = 'none';
|
||||
}
|
||||
|
||||
currentAuthRules = [];
|
||||
editingAuthRule = null;
|
||||
log('Auth rules section hidden', 'INFO');
|
||||
@@ -3715,6 +3876,10 @@
|
||||
// Initialize the app
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
console.log('C-Relay Admin API interface loaded');
|
||||
|
||||
// Initialize login/logout button state
|
||||
updateLoginLogoutButton();
|
||||
|
||||
setTimeout(() => {
|
||||
initializeApp();
|
||||
// Enhance SimplePool for testing after initialization
|
||||
|
||||
Reference in New Issue
Block a user