|
558 | 558 | </div> |
559 | 559 | </div> |
560 | 560 |
|
| 561 | + <!-- Password Modal --> |
| 562 | + <div class="modal-overlay" id="passwordModal"> |
| 563 | + <div class="password-modal-content"> |
| 564 | + <div class="password-title">Authentication Required</div> |
| 565 | + <form id="passwordForm" onsubmit="return submitPassword(event)"> |
| 566 | + <input type="password" id="passwordInput" class="password-input" placeholder="Enter password" autocomplete="current-password"> |
| 567 | + <div id="passwordError" class="password-error"></div> |
| 568 | + <button type="submit" class="password-submit">Unlock</button> |
| 569 | + </form> |
| 570 | + </div> |
| 571 | + </div> |
| 572 | +
|
| 573 | + <style> |
| 574 | + .password-modal-content { |
| 575 | + background: var(--bg-secondary); |
| 576 | + border: 1px solid var(--border-color); |
| 577 | + border-radius: 8px; |
| 578 | + padding: 1.5rem; |
| 579 | + width: 280px; |
| 580 | + text-align: center; |
| 581 | + } |
| 582 | + .password-title { |
| 583 | + font-size: 0.9rem; |
| 584 | + font-weight: 600; |
| 585 | + margin-bottom: 1rem; |
| 586 | + color: #fff; |
| 587 | + } |
| 588 | + .password-input { |
| 589 | + width: 100%; |
| 590 | + padding: 0.5rem; |
| 591 | + font-size: 0.8rem; |
| 592 | + border: 1px solid var(--border-color); |
| 593 | + border-radius: 4px; |
| 594 | + background: var(--bg-tertiary); |
| 595 | + color: var(--text-primary); |
| 596 | + margin-bottom: 0.5rem; |
| 597 | + } |
| 598 | + .password-input:focus { |
| 599 | + outline: none; |
| 600 | + border-color: var(--accent-blue); |
| 601 | + } |
| 602 | + .password-error { |
| 603 | + color: var(--accent-red); |
| 604 | + font-size: 0.7rem; |
| 605 | + min-height: 1rem; |
| 606 | + margin-bottom: 0.5rem; |
| 607 | + } |
| 608 | + .password-submit { |
| 609 | + width: 100%; |
| 610 | + padding: 0.5rem; |
| 611 | + font-size: 0.8rem; |
| 612 | + border: none; |
| 613 | + border-radius: 4px; |
| 614 | + background: var(--accent-blue); |
| 615 | + color: #fff; |
| 616 | + cursor: pointer; |
| 617 | + } |
| 618 | + .password-submit:hover { |
| 619 | + opacity: 0.9; |
| 620 | + } |
| 621 | + </style> |
| 622 | +
|
561 | 623 | <!-- Loading Modal --> |
562 | 624 | <div class="modal-overlay" id="loadingModal"> |
563 | 625 | <div class="loading-modal-content"> |
@@ -2108,11 +2170,78 @@ function createBalanceChart(serverId, trades, currentBalance, dailyData) { |
2108 | 2170 | } |
2109 | 2171 | } |
2110 | 2172 | |
2111 | | - // Initial load |
2112 | | - loadDashboard(); |
2113 | | - |
2114 | | - // Auto refresh |
2115 | | - refreshInterval = setInterval(loadDashboard, REFRESH_SECONDS * 1000); |
| 2173 | + function getCookie(name) { |
| 2174 | + const value = `; ${document.cookie}`; |
| 2175 | + const parts = value.split(`; ${name}=`); |
| 2176 | + if (parts.length === 2) return parts.pop().split(';').shift(); |
| 2177 | + return null; |
| 2178 | + } |
| 2179 | +
|
| 2180 | + function setCookie(name, value, days) { |
| 2181 | + const expires = new Date(Date.now() + days * 864e5).toUTCString(); |
| 2182 | + document.cookie = `${name}=${value}; expires=${expires}; path=/; SameSite=Strict`; |
| 2183 | + } |
| 2184 | +
|
| 2185 | + async function checkAuth() { |
| 2186 | + const response = await fetch('api.php?action=check_auth'); |
| 2187 | + const result = await response.json(); |
| 2188 | + return result.auth_required; |
| 2189 | + } |
| 2190 | +
|
| 2191 | + async function submitPassword(e) { |
| 2192 | + e.preventDefault(); |
| 2193 | + const password = document.getElementById('passwordInput').value; |
| 2194 | + const errorEl = document.getElementById('passwordError'); |
| 2195 | +
|
| 2196 | + const response = await fetch('api.php?action=verify_password', { |
| 2197 | + method: 'POST', |
| 2198 | + headers: { 'Content-Type': 'application/json' }, |
| 2199 | + body: JSON.stringify({ password }) |
| 2200 | + }); |
| 2201 | + const result = await response.json(); |
| 2202 | +
|
| 2203 | + if (result.success) { |
| 2204 | + setCookie('freqmon_auth', btoa(password), 30); |
| 2205 | + document.getElementById('passwordModal').classList.remove('show'); |
| 2206 | + startDashboard(); |
| 2207 | + } else { |
| 2208 | + errorEl.textContent = 'Invalid password'; |
| 2209 | + document.getElementById('passwordInput').value = ''; |
| 2210 | + document.getElementById('passwordInput').focus(); |
| 2211 | + } |
| 2212 | + return false; |
| 2213 | + } |
| 2214 | +
|
| 2215 | + function startDashboard() { |
| 2216 | + loadDashboard(); |
| 2217 | + refreshInterval = setInterval(loadDashboard, REFRESH_SECONDS * 1000); |
| 2218 | + } |
| 2219 | +
|
| 2220 | + async function initApp() { |
| 2221 | + const authRequired = await checkAuth(); |
| 2222 | +
|
| 2223 | + if (authRequired) { |
| 2224 | + const savedAuth = getCookie('freqmon_auth'); |
| 2225 | + if (savedAuth) { |
| 2226 | + const response = await fetch('api.php?action=verify_password', { |
| 2227 | + method: 'POST', |
| 2228 | + headers: { 'Content-Type': 'application/json' }, |
| 2229 | + body: JSON.stringify({ password: atob(savedAuth) }) |
| 2230 | + }); |
| 2231 | + const result = await response.json(); |
| 2232 | + if (result.success) { |
| 2233 | + startDashboard(); |
| 2234 | + return; |
| 2235 | + } |
| 2236 | + } |
| 2237 | + document.getElementById('passwordModal').classList.add('show'); |
| 2238 | + document.getElementById('passwordInput').focus(); |
| 2239 | + } else { |
| 2240 | + startDashboard(); |
| 2241 | + } |
| 2242 | + } |
| 2243 | +
|
| 2244 | + initApp(); |
2116 | 2245 | </script> |
2117 | 2246 |
|
2118 | 2247 | <footer class="site-footer"> |
|
0 commit comments