mirror of
https://github.com/Retropex/custom-ocean.xyz-dashboard.git
synced 2025-05-12 19:20:45 +02:00

Added fixed height and flexbox properties to center the Bitcoin logo. Adjusted positioning of the DeepSea ASCII art for perfect centering within the logo area.
992 lines
44 KiB
HTML
992 lines
44 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Ocean.xyz Pool Miner - Initializing...</title>
|
|
<link href="https://fonts.googleapis.com/css2?family=VT323&display=swap" rel="stylesheet">
|
|
<link rel="stylesheet" href="/static/css/boot.css">
|
|
<link rel="stylesheet" href="/static/css/theme-toggle.css">
|
|
<!-- Add Theme JS -->
|
|
<script src="/static/js/theme.js"></script>
|
|
<style>
|
|
/* Config form styling - fixed width and hidden by default */
|
|
#config-form {
|
|
display: none;
|
|
width: 500px;
|
|
max-width: 90%;
|
|
margin: 30px auto;
|
|
padding: 20px;
|
|
background-color: #0d0d0d;
|
|
border: 1px solid #f7931a;
|
|
box-shadow: 0 0 10px rgba(247, 147, 26, 0.5);
|
|
border-radius: 4px;
|
|
}
|
|
|
|
/* Boot text color - updated with theme toggling */
|
|
body:not(.deepsea-theme) #terminal,
|
|
body:not(.deepsea-theme) #output,
|
|
body:not(.deepsea-theme) #prompt-container,
|
|
body:not(.deepsea-theme) #prompt-text,
|
|
body:not(.deepsea-theme) #user-input,
|
|
body:not(.deepsea-theme) #loading-message {
|
|
color: #f7931a;
|
|
text-shadow: 0 0 5px rgba(247, 147, 26, 0.4);
|
|
}
|
|
|
|
/* DeepSea theme text color */
|
|
body.deepsea-theme #terminal,
|
|
body.deepsea-theme #output,
|
|
body.deepsea-theme #prompt-container,
|
|
body.deepsea-theme #prompt-text,
|
|
body.deepsea-theme #user-input,
|
|
body.deepsea-theme #loading-message {
|
|
color: #0088cc;
|
|
text-shadow: 0 0 5px rgba(0, 136, 204, 0.4);
|
|
}
|
|
|
|
/* DeepSea cursor color */
|
|
body.deepsea-theme .cursor,
|
|
body.deepsea-theme .prompt-cursor {
|
|
background-color: #0088cc;
|
|
box-shadow: 0 0 5px rgba(0, 136, 204, 0.8);
|
|
}
|
|
|
|
/* Boot-specific DeepSea theme adjustments */
|
|
body.deepsea-theme #bitcoin-logo {
|
|
color: #0088cc;
|
|
border-color: #0088cc;
|
|
text-shadow: 0 0 10px rgba(0, 136, 204, 0.5);
|
|
box-shadow: 0 0 15px rgba(0, 136, 204, 0.5);
|
|
}
|
|
|
|
body.deepsea-theme #config-form {
|
|
border: 1px solid #0088cc;
|
|
box-shadow: 0 0 10px rgba(0, 136, 204, 0.5);
|
|
}
|
|
|
|
body.deepsea-theme .config-title {
|
|
color: #0088cc;
|
|
text-shadow: 0 0 8px rgba(0, 136, 204, 0.8);
|
|
}
|
|
|
|
body.deepsea-theme .form-group label {
|
|
color: #0088cc;
|
|
}
|
|
|
|
body.deepsea-theme .form-group input,
|
|
body.deepsea-theme .form-group select {
|
|
border: 1px solid #0088cc;
|
|
}
|
|
|
|
body.deepsea-theme .form-group input:focus,
|
|
body.deepsea-theme .form-group select:focus {
|
|
box-shadow: 0 0 5px #0088cc;
|
|
}
|
|
|
|
body.deepsea-theme .btn {
|
|
background-color: #0088cc;
|
|
}
|
|
|
|
body.deepsea-theme .btn:hover {
|
|
background-color: #00b3ff;
|
|
}
|
|
|
|
body.deepsea-theme .btn-secondary {
|
|
background-color: #333;
|
|
color: #0088cc;
|
|
}
|
|
|
|
body.deepsea-theme .tooltip .tooltip-text {
|
|
border: 1px solid #0088cc;
|
|
}
|
|
|
|
body.deepsea-theme .form-group select {
|
|
background-image: linear-gradient(45deg, transparent 50%, #0088cc 50%), linear-gradient(135deg, #0088cc 50%, transparent 50%);
|
|
}
|
|
|
|
/* DeepSea skip button */
|
|
body.deepsea-theme #skip-button {
|
|
background-color: #0088cc;
|
|
box-shadow: 0 0 8px rgba(0, 136, 204, 0.5);
|
|
}
|
|
|
|
body.deepsea-theme #skip-button:hover {
|
|
background-color: #00b3ff;
|
|
box-shadow: 0 0 12px rgba(0, 136, 204, 0.7);
|
|
}
|
|
|
|
/* Original Bitcoin styling preserved by default */
|
|
.config-title {
|
|
font-size: 24px;
|
|
text-align: center;
|
|
margin-bottom: 20px;
|
|
color: #f7931a;
|
|
text-shadow: 0 0 8px rgba(247, 147, 26, 0.8);
|
|
}
|
|
|
|
.form-group {
|
|
margin-bottom: 15px;
|
|
}
|
|
|
|
.form-group label {
|
|
display: block;
|
|
margin-bottom: 5px;
|
|
color: #f7931a;
|
|
}
|
|
|
|
.form-group input,
|
|
.form-group select {
|
|
width: 100%;
|
|
padding: 8px;
|
|
background-color: #0d0d0d;
|
|
border: 1px solid #f7931a;
|
|
color: #fff;
|
|
font-family: 'VT323', monospace;
|
|
font-size: 18px;
|
|
}
|
|
|
|
.form-group input:focus,
|
|
.form-group select:focus {
|
|
outline: none;
|
|
box-shadow: 0 0 5px #f7931a;
|
|
}
|
|
|
|
.form-actions {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
margin-top: 20px;
|
|
}
|
|
|
|
.btn {
|
|
padding: 8px 16px;
|
|
background-color: #f7931a;
|
|
color: #000;
|
|
border: none;
|
|
cursor: pointer;
|
|
font-family: 'VT323', monospace;
|
|
font-size: 18px;
|
|
}
|
|
|
|
.btn:hover {
|
|
background-color: #ffa32e;
|
|
}
|
|
|
|
.btn-secondary {
|
|
background-color: #333;
|
|
color: #f7931a;
|
|
}
|
|
|
|
#form-message {
|
|
margin-top: 15px;
|
|
padding: 10px;
|
|
border-radius: 3px;
|
|
display: none;
|
|
}
|
|
|
|
.message-success {
|
|
background-color: rgba(50, 205, 50, 0.2);
|
|
border: 1px solid #32CD32;
|
|
color: #32CD32;
|
|
}
|
|
|
|
.message-error {
|
|
background-color: rgba(255, 0, 0, 0.2);
|
|
border: 1px solid #ff0000;
|
|
color: #ff0000;
|
|
}
|
|
|
|
.tooltip {
|
|
position: relative;
|
|
display: inline-block;
|
|
margin-left: 5px;
|
|
width: 14px;
|
|
height: 14px;
|
|
background-color: #333;
|
|
color: #fff;
|
|
border-radius: 50%;
|
|
text-align: center;
|
|
line-height: 14px;
|
|
font-size: 10px;
|
|
cursor: help;
|
|
}
|
|
|
|
.tooltip .tooltip-text {
|
|
visibility: hidden;
|
|
width: 200px;
|
|
background-color: #000;
|
|
color: #fff;
|
|
text-align: center;
|
|
border-radius: 3px;
|
|
padding: 5px;
|
|
position: absolute;
|
|
z-index: 1;
|
|
bottom: 125%;
|
|
left: 50%;
|
|
margin-left: -100px;
|
|
opacity: 0;
|
|
transition: opacity 0.3s;
|
|
font-size: 14px;
|
|
border: 1px solid #f7931a;
|
|
}
|
|
|
|
.tooltip:hover .tooltip-text {
|
|
visibility: visible;
|
|
opacity: 1;
|
|
}
|
|
|
|
/* Style the select dropdown with custom arrow */
|
|
.form-group select {
|
|
appearance: none;
|
|
-webkit-appearance: none;
|
|
-moz-appearance: none;
|
|
background-image: linear-gradient(45deg, transparent 50%, #f7931a 50%), linear-gradient(135deg, #f7931a 50%, transparent 50%);
|
|
background-position: calc(100% - 15px) calc(1em + 0px), calc(100% - 10px) calc(1em + 0px);
|
|
background-size: 5px 5px, 5px 5px;
|
|
background-repeat: no-repeat;
|
|
padding-right: 30px;
|
|
}
|
|
|
|
/* Base styling for the Bitcoin logo */
|
|
#bitcoin-logo {
|
|
position: relative;
|
|
white-space: pre;
|
|
font-family: monospace;
|
|
height: 130px; /* Set fixed height to match original logo */
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
flex-direction: column;
|
|
}
|
|
|
|
/* Update the DeepSea theme logo styling */
|
|
body.deepsea-theme #bitcoin-logo {
|
|
color: transparent; /* Hide original logo */
|
|
position: relative;
|
|
text-shadow: none;
|
|
min-height: 120px; /* Ensure enough height for the new logo */
|
|
}
|
|
|
|
/* Add the new DeepSea ASCII art */
|
|
body.deepsea-theme #bitcoin-logo::after {
|
|
content: " ____ ____ \A| _ \\ ___ ___ _ __/ ___| ___ __ _ \A| | | |/ _ \\/ _ \\ '_ \\___ \\ / _ \\/ _` |\A| |_| | __/ __/ |_) |__) | __/ (_| |\A|____/ \\___|\\___|_.__/____/ \\___|\\__,_|\A|_| ";
|
|
position: absolute;
|
|
top: 50%;
|
|
left: 50%;
|
|
transform: translate(-50%, -50%); /* Center perfectly */
|
|
font-size: 100%; /* Full size */
|
|
font-weight: bold;
|
|
line-height: 1.2;
|
|
color: #0088cc;
|
|
white-space: pre;
|
|
display: block;
|
|
text-shadow: 0 0 10px rgba(0, 136, 204, 0.5);
|
|
font-family: monospace;
|
|
z-index: 1;
|
|
padding: 10px 0;
|
|
}
|
|
|
|
/* Add "DeepSea" version info */
|
|
body.deepsea-theme #bitcoin-logo::before {
|
|
content: "v.21";
|
|
position: absolute;
|
|
bottom: 0;
|
|
right: 10px;
|
|
color: #0088cc;
|
|
font-size: 16px;
|
|
text-shadow: 0 0 5px rgba(0, 136, 204, 0.5);
|
|
font-family: 'VT323', monospace;
|
|
z-index: 2; /* Ensure version displays on top */
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<!-- Theme toggle button (new) -->
|
|
<button id="themeToggle" class="theme-toggle-btn">
|
|
<span>Toggle Theme</span>
|
|
</button>
|
|
<button id="skip-button">SKIP</button>
|
|
<div id="debug-info"></div>
|
|
<div id="loading-message">Loading mining data...</div>
|
|
<div id="bitcoin-logo">
|
|
██████╗ ████████╗ ██████╗ ██████╗ ███████╗
|
|
██╔══██╗╚══██╔══╝██╔════╝ ██╔═══██╗██╔════╝
|
|
██████╔╝ ██║ ██║ ██║ ██║███████╗
|
|
██╔══██╗ ██║ ██║ ██║ ██║╚════██║
|
|
██████╔╝ ██║ ╚██████╗ ╚██████╔╝███████║
|
|
╚═════╝ ╚═╝ ╚═════╝ ╚═════╝ ╚══════╝
|
|
v.21
|
|
</div>
|
|
<div id="terminal">
|
|
<div id="terminal-content">
|
|
<span id="output"></span><span class="cursor"></span>
|
|
<span id="prompt-container">
|
|
<span id="prompt-text">
|
|
Initialize mining dashboard? [Y/N]:
|
|
<span class="prompt-cursor"></span>
|
|
<input type="text" id="user-input" maxlength="1" autocomplete="off" spellcheck="false" autofocus style="font-size: 16px; font-weight: bold;">
|
|
</span>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Configuration Form -->
|
|
<div id="config-form">
|
|
<div class="config-title">MINING CONFIGURATION</div>
|
|
<div class="form-group">
|
|
<label for="wallet-address">
|
|
Bitcoin Wallet Address
|
|
<span class="tooltip">
|
|
?
|
|
<span class="tooltip-text">Your Ocean.xyz pool mining address</span>
|
|
</span>
|
|
</label>
|
|
<input type="text" id="wallet-address" placeholder="bc1..." value="">
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="power-cost">
|
|
Power Cost ($/kWh)
|
|
<span class="tooltip">
|
|
?
|
|
<span class="tooltip-text">Your electricity cost per kilowatt-hour</span>
|
|
</span>
|
|
</label>
|
|
<input type="number" id="power-cost" step="0.01" min="0" placeholder="0.12" value="">
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="power-usage">
|
|
Power Usage (Watts)
|
|
<span class="tooltip">
|
|
?
|
|
<span class="tooltip-text">Total power consumption of your mining equipment</span>
|
|
</span>
|
|
</label>
|
|
<input type="number" id="power-usage" step="50" min="0" placeholder="13450" value="">
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="network-fee">
|
|
Network Fee (%)
|
|
<span class="tooltip">
|
|
?
|
|
<span class="tooltip-text">Additional fees beyond pool fee, like Firmware fees</span>
|
|
</span>
|
|
</label>
|
|
<input type="number" id="network-fee" step="0.1" min="0" max="10" placeholder="0.0" value="">
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="timezone">
|
|
Timezone
|
|
<span class="tooltip">
|
|
?
|
|
<span class="tooltip-text">Your local timezone for displaying time information</span>
|
|
</span>
|
|
</label>
|
|
<select id="timezone" class="form-control">
|
|
<optgroup label="Common Timezones">
|
|
<option value="America/Los_Angeles">Los Angeles (Pacific Time)</option>
|
|
<option value="America/Denver">Denver (Mountain Time)</option>
|
|
<option value="America/Chicago">Chicago (Central Time)</option>
|
|
<option value="America/New_York">New York (Eastern Time)</option>
|
|
<option value="Europe/London">London (GMT/BST)</option>
|
|
<option value="Europe/Paris">Paris (Central European Time)</option>
|
|
<option value="Asia/Tokyo">Tokyo (Japan Standard Time)</option>
|
|
<option value="Australia/Sydney">Sydney (Australian Eastern Time)</option>
|
|
</optgroup>
|
|
<optgroup label="Other Timezones" id="other-timezones">
|
|
<!-- Will be populated by JavaScript -->
|
|
</optgroup>
|
|
</select>
|
|
</div>
|
|
<div id="form-message"></div>
|
|
<div class="form-actions">
|
|
<button class="btn btn-secondary" id="use-defaults">Use Defaults</button>
|
|
<button class="btn" id="save-config">Save & Continue</button>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
// Theme toggle initialization
|
|
document.addEventListener('DOMContentLoaded', function () {
|
|
// Initialize theme toggle button based on current theme
|
|
const themeToggle = document.getElementById('themeToggle');
|
|
if (themeToggle) {
|
|
// Check current theme
|
|
const isDeepSea = localStorage.getItem('useDeepSeaTheme') === 'true';
|
|
|
|
// Update button style based on theme
|
|
if (isDeepSea) {
|
|
document.body.classList.add('deepsea-theme');
|
|
themeToggle.style.borderColor = '#0088cc';
|
|
themeToggle.style.color = '#0088cc';
|
|
} else {
|
|
document.body.classList.remove('deepsea-theme');
|
|
themeToggle.style.borderColor = '#f2a900';
|
|
themeToggle.style.color = '#f2a900';
|
|
}
|
|
|
|
// Add click event listener
|
|
themeToggle.addEventListener('click', function () {
|
|
toggleTheme(); // This will now trigger a page refresh
|
|
});
|
|
}
|
|
|
|
// Update terminal colors based on theme (boot.html specific)
|
|
function updateTerminalColors() {
|
|
const isDeepSeaTheme = localStorage.getItem('useDeepSeaTheme') === 'true';
|
|
if (isDeepSeaTheme) {
|
|
document.body.classList.add('deepsea-theme');
|
|
} else {
|
|
document.body.classList.remove('deepsea-theme');
|
|
}
|
|
}
|
|
|
|
// Initialize terminal colors
|
|
updateTerminalColors();
|
|
});
|
|
|
|
// Add a function to populate all available timezones
|
|
function populateTimezones() {
|
|
const otherTimezones = document.getElementById('other-timezones');
|
|
|
|
// Common timezone areas to include
|
|
const commonAreas = [
|
|
'Africa', 'America', 'Antarctica', 'Asia', 'Atlantic',
|
|
'Australia', 'Europe', 'Indian', 'Pacific'
|
|
];
|
|
|
|
// Fetch the list of available timezones
|
|
fetch('/api/available_timezones')
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (!data.timezones || !Array.isArray(data.timezones)) {
|
|
console.error('Invalid timezone data received');
|
|
return;
|
|
}
|
|
|
|
// Sort timezones and filter to include only common areas
|
|
const sortedTimezones = data.timezones
|
|
.filter(tz => commonAreas.some(area => tz.startsWith(area + '/')))
|
|
.sort();
|
|
|
|
// Add options for each timezone (excluding those already in common list)
|
|
const commonOptions = Array.from(document.querySelectorAll('#timezone optgroup:first-child option'))
|
|
.map(opt => opt.value);
|
|
|
|
sortedTimezones.forEach(tz => {
|
|
if (!commonOptions.includes(tz)) {
|
|
const option = document.createElement('option');
|
|
option.value = tz;
|
|
option.textContent = tz.replace('_', ' ');
|
|
otherTimezones.appendChild(option);
|
|
}
|
|
});
|
|
})
|
|
.catch(error => console.error('Error fetching timezones:', error));
|
|
}
|
|
|
|
// Call this when the page loads
|
|
document.addEventListener('DOMContentLoaded', populateTimezones);
|
|
|
|
// Load the current timezone from configuration
|
|
function loadTimezoneFromConfig() {
|
|
if (currentConfig && currentConfig.timezone) {
|
|
const timezoneSelect = document.getElementById('timezone');
|
|
|
|
// First, check if the option exists
|
|
let optionExists = false;
|
|
for (let i = 0; i < timezoneSelect.options.length; i++) {
|
|
if (timezoneSelect.options[i].value === currentConfig.timezone) {
|
|
timezoneSelect.selectedIndex = i;
|
|
optionExists = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If the option doesn't exist yet (might be in the 'other' group being loaded)
|
|
// set a data attribute to select it when options are loaded
|
|
if (!optionExists) {
|
|
timezoneSelect.setAttribute('data-select-value', currentConfig.timezone);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Call this after loading config
|
|
loadConfig().then(() => {
|
|
loadTimezoneFromConfig();
|
|
});
|
|
|
|
// Update saveConfig to include network fee
|
|
function saveConfig() {
|
|
const wallet = document.getElementById('wallet-address').value.trim();
|
|
const powerCost = parseFloat(document.getElementById('power-cost').value) || 0;
|
|
const powerUsage = parseFloat(document.getElementById('power-usage').value) || 0;
|
|
const timezone = document.getElementById('timezone').value;
|
|
const networkFee = parseFloat(document.getElementById('network-fee').value) || 0;
|
|
|
|
const updatedConfig = {
|
|
wallet: wallet || (currentConfig ? currentConfig.wallet : ""),
|
|
power_cost: powerCost,
|
|
power_usage: powerUsage,
|
|
timezone: timezone,
|
|
network_fee: networkFee
|
|
};
|
|
|
|
return fetch('/api/config', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify(updatedConfig)
|
|
})
|
|
.then(response => {
|
|
if (!response.ok) {
|
|
throw new Error('Failed to save configuration');
|
|
}
|
|
return response.json();
|
|
});
|
|
}
|
|
|
|
// Debug logging
|
|
function updateDebug(message) {
|
|
document.getElementById('debug-info').textContent = message;
|
|
console.log(message);
|
|
}
|
|
|
|
// Format numbers with commas
|
|
function numberWithCommas(x) {
|
|
if (x == null) return "N/A";
|
|
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
|
}
|
|
|
|
// Global variables
|
|
let bootMessages = [];
|
|
let dashboardData = null;
|
|
let outputElement = document.getElementById('output');
|
|
const bitcoinLogo = document.getElementById('bitcoin-logo');
|
|
const skipButton = document.getElementById('skip-button');
|
|
const loadingMessage = document.getElementById('loading-message');
|
|
const promptContainer = document.getElementById('prompt-container');
|
|
const userInput = document.getElementById('user-input');
|
|
const configForm = document.getElementById('config-form');
|
|
let messageIndex = 0;
|
|
let timeoutId = null;
|
|
let waitingForUserInput = false;
|
|
let bootComplete = false;
|
|
let configLoaded = false;
|
|
let currentConfig = {
|
|
wallet: "yourwallethere",
|
|
power_cost: 0.0,
|
|
power_usage: 0.0
|
|
};
|
|
|
|
// Update loadConfig function to include network fee
|
|
function loadConfig() {
|
|
return new Promise((resolve, reject) => {
|
|
fetch('/api/config?nocache=' + new Date().getTime())
|
|
.then(response => {
|
|
if (!response.ok) {
|
|
throw new Error('Failed to load configuration: ' + response.statusText);
|
|
}
|
|
return response.json();
|
|
})
|
|
.then(data => {
|
|
console.log("Loaded configuration:", data);
|
|
currentConfig = data;
|
|
|
|
// Update form fields with latest values
|
|
document.getElementById('wallet-address').value = currentConfig.wallet || "";
|
|
document.getElementById('power-cost').value = currentConfig.power_cost || "";
|
|
document.getElementById('power-usage').value = currentConfig.power_usage || "";
|
|
document.getElementById('network-fee').value = currentConfig.network_fee || "";
|
|
configLoaded = true;
|
|
resolve(currentConfig);
|
|
})
|
|
.catch(err => {
|
|
console.error("Error loading config:", err);
|
|
// Use default values if loading fails
|
|
currentConfig = {
|
|
wallet: "yourwallethere",
|
|
power_cost: 0.0,
|
|
power_usage: 0.0,
|
|
network_fee: 0.0
|
|
};
|
|
|
|
document.getElementById('wallet-address').value = currentConfig.wallet || "";
|
|
document.getElementById('power-cost').value = currentConfig.power_cost || "";
|
|
document.getElementById('power-usage').value = currentConfig.power_usage || "";
|
|
document.getElementById('network-fee').value = currentConfig.network_fee || "";
|
|
resolve(currentConfig);
|
|
});
|
|
});
|
|
}
|
|
|
|
// Also update the save button event handler to reload the config after saving
|
|
document.getElementById('save-config').addEventListener('click', function () {
|
|
const messageElement = document.getElementById('form-message');
|
|
messageElement.style.display = 'block';
|
|
|
|
saveConfig()
|
|
.then(data => {
|
|
console.log("Configuration saved:", data);
|
|
messageElement.textContent = "Configuration saved successfully!";
|
|
messageElement.className = "message-success";
|
|
|
|
// Update currentConfig with the saved values
|
|
currentConfig = data.config || data;
|
|
|
|
setTimeout(redirectToDashboard, 1000);
|
|
})
|
|
.catch(error => {
|
|
console.error("Error saving configuration:", error);
|
|
messageElement.textContent = "Error saving configuration. Please try again.";
|
|
messageElement.className = "message-error";
|
|
});
|
|
});
|
|
|
|
// Safety timeout: redirect after 120 seconds if boot not complete
|
|
window.addEventListener('load', function () {
|
|
setTimeout(function () {
|
|
if (!bootComplete && !waitingForUserInput) {
|
|
console.warn("Safety timeout reached - redirecting to dashboard");
|
|
redirectToDashboard();
|
|
}
|
|
}, 120000);
|
|
});
|
|
|
|
// Configuration form event listeners
|
|
document.getElementById('save-config').addEventListener('click', function () {
|
|
const messageElement = document.getElementById('form-message');
|
|
messageElement.style.display = 'block';
|
|
|
|
saveConfig()
|
|
.then(data => {
|
|
console.log("Configuration saved:", data);
|
|
messageElement.textContent = "Configuration saved successfully!";
|
|
messageElement.className = "message-success";
|
|
setTimeout(redirectToDashboard, 1000);
|
|
})
|
|
.catch(error => {
|
|
console.error("Error saving configuration:", error);
|
|
messageElement.textContent = "Error saving configuration. Please try again.";
|
|
messageElement.className = "message-error";
|
|
});
|
|
});
|
|
|
|
// Update Use Defaults button handler
|
|
document.getElementById('use-defaults').addEventListener('click', function () {
|
|
// Set default values including network fee
|
|
document.getElementById('wallet-address').value = "bc1py5zmrtssheq3shd8cptpl5l5m3txxr5afynyg2gyvam6w78s4dlqqnt4v9";
|
|
document.getElementById('power-cost').value = 0.0;
|
|
document.getElementById('power-usage').value = 0.0;
|
|
document.getElementById('network-fee').value = 0.0;
|
|
|
|
// Visual feedback
|
|
const btn = document.getElementById('use-defaults');
|
|
const originalText = btn.textContent;
|
|
btn.textContent = "Defaults Applied";
|
|
btn.style.backgroundColor = "#32CD32";
|
|
setTimeout(function () {
|
|
btn.textContent = originalText;
|
|
btn.style.backgroundColor = "";
|
|
}, 1500);
|
|
});
|
|
|
|
// Redirect to dashboard
|
|
function redirectToDashboard() {
|
|
updateDebug("Boot sequence complete, redirecting...");
|
|
const baseUrl = window.location.origin;
|
|
window.location.href = baseUrl + "/dashboard";
|
|
}
|
|
|
|
// Fade in Bitcoin logo
|
|
document.addEventListener('DOMContentLoaded', function () {
|
|
setTimeout(function () {
|
|
bitcoinLogo.style.visibility = 'visible';
|
|
setTimeout(function () {
|
|
bitcoinLogo.style.opacity = '1';
|
|
}, 100);
|
|
}, 500);
|
|
|
|
// Load configuration
|
|
loadConfig();
|
|
});
|
|
|
|
// Post-confirmation messages with retro typing effect
|
|
function showPostConfirmationMessages(response) {
|
|
try {
|
|
outputElement = document.getElementById('output');
|
|
if (!outputElement) {
|
|
setTimeout(redirectToDashboard, 1000);
|
|
return;
|
|
}
|
|
|
|
// Configuration form will be shown after boot sequence
|
|
if (response.toUpperCase() === 'Y') {
|
|
const yesMessages = [
|
|
{ text: "INITIALIZING DASHBOARD...\n", html: true, delay: 400 },
|
|
{ text: "Connecting to real-time data feeds...", speed: 20, delay: 300 },
|
|
{ text: "<span class='green'>CONNECTED</span>\n", html: true, delay: 400 },
|
|
{ text: "Loading blockchain validators...", speed: 15, delay: 300 },
|
|
{ text: "<span class='green'>COMPLETE</span>\n", html: true, delay: 400 },
|
|
{ text: "Starting TX fee calculation module...", speed: 15, delay: 400 },
|
|
{ text: "<span class='green'>ACTIVE</span>\n", html: true, delay: 400 },
|
|
{ text: "Verifying BTC-USD exchange rates...", speed: 15, delay: 200 },
|
|
{ text: "<span class='green'>CURRENT RATE CONFIRMED</span>\n", html: true, delay: 300 },
|
|
{ text: "Calibrating hashrate telemetry...", speed: 15, delay: 200 },
|
|
{ text: "<span class='green'>CALIBRATED</span>\n", html: true, delay: 200 },
|
|
{ text: "Loading mining configuration...", speed: 15, delay: 200 },
|
|
{ text: "<span class='green'>LOADED</span>\n", html: true, delay: 300 },
|
|
{ text: "Preparing configuration interface...", speed: 15, delay: 800 },
|
|
{ text: "<span class='green'>READY</span>\n", html: true, delay: 500 },
|
|
{ text: "\nPlease configure your mining setup or use the default values:\n", html: true, delay: 800, showConfigForm: true }
|
|
];
|
|
|
|
let msgIndex = 0;
|
|
function processNextMessage() {
|
|
if (msgIndex >= yesMessages.length) {
|
|
return;
|
|
}
|
|
const currentMessage = yesMessages[msgIndex];
|
|
|
|
if (currentMessage.showConfigForm) {
|
|
msgIndex++;
|
|
// Show configuration form
|
|
document.getElementById('config-form').style.display = 'block';
|
|
return;
|
|
}
|
|
|
|
if (currentMessage.html) {
|
|
outputElement.innerHTML += currentMessage.text;
|
|
msgIndex++;
|
|
setTimeout(processNextMessage, currentMessage.delay || 300);
|
|
} else {
|
|
let charIndex = 0;
|
|
function typeCharacter() {
|
|
if (charIndex < currentMessage.text.length) {
|
|
outputElement.innerHTML += currentMessage.text.charAt(charIndex);
|
|
charIndex++;
|
|
setTimeout(typeCharacter, currentMessage.speed || 20);
|
|
} else {
|
|
msgIndex++;
|
|
setTimeout(processNextMessage, currentMessage.delay || 300);
|
|
}
|
|
}
|
|
typeCharacter();
|
|
}
|
|
}
|
|
setTimeout(processNextMessage, 500);
|
|
} else {
|
|
// If user selects 'N', just redirect to dashboard
|
|
outputElement.innerHTML += "N\n\nDASHBOARD INITIALIZATION ABORTED.\n";
|
|
outputElement.innerHTML += "\nUsing default configuration values.\n";
|
|
setTimeout(redirectToDashboard, 2000);
|
|
}
|
|
} catch (err) {
|
|
setTimeout(redirectToDashboard, 1000);
|
|
}
|
|
}
|
|
|
|
// Handle Y/N prompt input
|
|
userInput.addEventListener('keydown', function (e) {
|
|
if (waitingForUserInput && e.key === 'Enter') {
|
|
e.preventDefault();
|
|
const response = userInput.value.toUpperCase();
|
|
|
|
if (response === 'Y' || response === 'N') {
|
|
promptContainer.style.display = 'none';
|
|
waitingForUserInput = false;
|
|
outputElement.innerHTML += response + "\n";
|
|
userInput.value = '';
|
|
showPostConfirmationMessages(response);
|
|
}
|
|
}
|
|
});
|
|
|
|
// Show the prompt
|
|
function showUserPrompt() {
|
|
promptContainer.style.display = 'inline';
|
|
waitingForUserInput = true;
|
|
document.querySelector('.cursor').style.display = 'none';
|
|
userInput.focus();
|
|
}
|
|
|
|
// Disable truncation so all text is visible
|
|
function manageTerminalContent() { }
|
|
|
|
// Retro typing effect for boot messages
|
|
function typeBootMessages() {
|
|
try {
|
|
if (!outputElement) {
|
|
outputElement = document.getElementById('output');
|
|
if (!outputElement) {
|
|
skipButton.click();
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (messageIndex >= bootMessages.length) { return; }
|
|
const currentMessage = bootMessages[messageIndex];
|
|
|
|
if (currentMessage.showPrompt) {
|
|
messageIndex++;
|
|
showUserPrompt();
|
|
return;
|
|
}
|
|
|
|
if (currentMessage.html) {
|
|
outputElement.innerHTML += currentMessage.text;
|
|
messageIndex++;
|
|
timeoutId = setTimeout(typeBootMessages, currentMessage.delay || 300);
|
|
return;
|
|
}
|
|
|
|
if (!currentMessage.typingIndex) { currentMessage.typingIndex = 0; }
|
|
if (currentMessage.typingIndex < currentMessage.text.length) {
|
|
outputElement.innerHTML += currentMessage.text.charAt(currentMessage.typingIndex);
|
|
currentMessage.typingIndex++;
|
|
timeoutId = setTimeout(typeBootMessages, currentMessage.speed || 15);
|
|
} else {
|
|
messageIndex++;
|
|
timeoutId = setTimeout(typeBootMessages, currentMessage.delay || 300);
|
|
}
|
|
} catch (err) {
|
|
messageIndex++;
|
|
timeoutId = setTimeout(typeBootMessages, 500);
|
|
}
|
|
}
|
|
|
|
// Skip button: reveal configuration form only
|
|
skipButton.addEventListener('click', function () {
|
|
clearTimeout(timeoutId);
|
|
// Optionally, clear boot messages or hide elements related to boot sequence
|
|
outputElement.innerHTML = "";
|
|
// Hide any loading or prompt messages
|
|
loadingMessage.style.display = 'none';
|
|
promptContainer.style.display = 'none';
|
|
// Show the configuration form
|
|
configForm.style.display = 'block';
|
|
});
|
|
|
|
// Start the typing animation (hides loading message)
|
|
function startTyping() {
|
|
loadingMessage.style.display = 'none';
|
|
setTimeout(typeBootMessages, 150);
|
|
}
|
|
|
|
// Fallback messages (used immediately)
|
|
function setupFallbackMessages() {
|
|
bootMessages = [
|
|
{ text: "BITCOIN OS - MINING SYSTEM - v21.000.000\n", speed: 25, delay: 300 },
|
|
{ text: "Copyright (c) 2009-2025 Satoshi Nakamoto\n", speed: 20, delay: 250 },
|
|
{ text: "All rights reserved.\n\n", speed: 25, delay: 300 },
|
|
{ text: "INITIALIZING SYSTEM...\n", speed: 25, delay: 300 },
|
|
{ text: "HARDWARE: ", speed: 25, delay: 100 },
|
|
{ text: "<span class='green'>OK</span>\n", html: true, delay: 300 },
|
|
{ text: "NETWORK: ", speed: 25, delay: 100 },
|
|
{ text: "<span class='green'>OK</span>\n", html: true, delay: 300 },
|
|
{ text: "BLOCKCHAIN: ", speed: 25, delay: 100 },
|
|
{ text: "<span class='green'>SYNCHRONIZED</span>\n", html: true, delay: 300 },
|
|
{ text: "MINING RIG: ", speed: 25, delay: 100 },
|
|
{ text: "<span class='green'>ONLINE</span>\n", html: true, delay: 300 },
|
|
{ text: "\nSystem ready. ", speed: 25, delay: 400 },
|
|
{ showPrompt: true, delay: 0 }
|
|
];
|
|
startTyping();
|
|
}
|
|
|
|
// Initialize with fallback, then try live data
|
|
setupFallbackMessages();
|
|
updateDebug("Fetching dashboard data...");
|
|
fetch('/api/metrics')
|
|
.then(response => {
|
|
if (!response.ok) { throw new Error(`HTTP error! Status: ${response.status}`); }
|
|
return response.json();
|
|
})
|
|
.then(data => {
|
|
dashboardData = data;
|
|
clearTimeout(timeoutId);
|
|
messageIndex = 0;
|
|
outputElement = document.getElementById('output');
|
|
outputElement.innerHTML = "";
|
|
bootMessages = [
|
|
{ text: "BITCOIN OS - MINING CONTROL SYSTEM - v21.000.000\n", speed: 25, delay: 300 },
|
|
{ text: "Copyright (c) 2009-2025 Satoshi Nakamoto & The Bitcoin Core Developers\n", speed: 20, delay: 250 },
|
|
{ text: "All rights reserved.\n\n", speed: 25, delay: 300 },
|
|
{ text: "INITIALIZING SHA-256 MINING SUBSYSTEMS...\n", speed: 25, delay: 400 },
|
|
{ text: "ASIC CLUSTER STATUS: ", speed: 15, delay: 100 },
|
|
{ text: "<span class='green'>ONLINE</span>\n", html: true, delay: 300 },
|
|
{ text: "CHIP TEMPERATURE: ", speed: 15, delay: 100 },
|
|
{ text: "<span class='green'>62°C - WITHIN OPTIMAL RANGE</span>\n", html: true, delay: 300 },
|
|
{ text: "COOLING SYSTEMS: ", speed: 15, delay: 100 },
|
|
{ text: "<span class='green'>OPERATIONAL</span>\n", html: true, delay: 300 },
|
|
{ text: "POWER SUPPLY HEALTH: ", speed: 15, delay: 100 },
|
|
{ text: "<span class='green'>98.7% - NOMINAL</span>\n", html: true, delay: 300 },
|
|
{ text: "\nCONNECTING TO BITCOIN NETWORK...\n", speed: 20, delay: 400 },
|
|
{ text: "BLOCKCHAIN SYNC STATUS: ", speed: 15, delay: 100 },
|
|
{ text: "<span class='green'>SYNCHRONIZED</span>\n", html: true, delay: 300 },
|
|
{ text: "DIFFICULTY ADJUSTMENT: ", speed: 15, delay: 100 },
|
|
{ text: "<span class='yellow'>CALCULATED</span>\n", html: true, delay: 300 },
|
|
{ text: "MEMPOOL MONITORING: ", speed: 15, delay: 100 },
|
|
{ text: "<span class='green'>ACTIVE</span>\n", html: true, delay: 300 },
|
|
{ text: "\nESTABLISHING POOL CONNECTION...\n", speed: 20, delay: 300 },
|
|
{ text: "CONNECTING TO OCEAN.XYZ...\n", speed: 20, delay: 300 },
|
|
{ text: "STRATUM PROTOCOL v2: ", speed: 15, delay: 100 },
|
|
{ text: "<span class='green'>INITIALIZED</span>\n", html: true, delay: 300 },
|
|
{ text: "POOL HASHRATE: ", speed: 15, delay: 100 },
|
|
{ text: "<span class='green'>VERIFIED</span>\n", html: true, delay: 300 },
|
|
{ text: "WORKER AUTHENTICATION: ", speed: 15, delay: 100 },
|
|
{ text: "<span class='green'>SUCCESSFUL</span>\n", html: true, delay: 300 },
|
|
{ text: "\nINITIALIZING METRICS COLLECTORS...\n", speed: 20, delay: 300 },
|
|
{ text: "HASHRATE MONITOR: ", speed: 15, delay: 100 },
|
|
{ text: "<span class='green'>ACTIVE</span>\n", html: true, delay: 300 },
|
|
{ text: "EARNINGS CALCULATOR: ", speed: 15, delay: 100 },
|
|
{ text: "<span class='green'>CALIBRATED</span>\n", html: true, delay: 300 },
|
|
{ text: "POWER USAGE TRACKING: ", speed: 15, delay: 100 },
|
|
{ text: "<span class='green'>ENABLED</span>\n", html: true, delay: 300 },
|
|
{ text: "PAYOUT THRESHOLD MONITOR: ", speed: 15, delay: 100 },
|
|
{ text: "<span class='green'>ACTIVE</span>\n", html: true, delay: 300 },
|
|
{ text: "\nCURRENT NETWORK METRICS DETECTED\n", speed: 20, delay: 300 },
|
|
{ text: "BTC PRICE: ", speed: 20, delay: 100 },
|
|
{ text: "<span class='yellow'>$" + numberWithCommas((data.btc_price || 0).toFixed(2)) + "</span>\n", html: true, delay: 300 },
|
|
{ text: "NETWORK DIFFICULTY: ", speed: 20, delay: 100 },
|
|
{ text: "<span class='white'>" + numberWithCommas(Math.round(data.difficulty || 0)) + "</span>\n", html: true, delay: 300 },
|
|
{ text: "NETWORK HASHRATE: ", speed: 20, delay: 100 },
|
|
{ text: "<span class='white'>" + (data.network_hashrate ? numberWithCommas(Math.round(data.network_hashrate)) : "N/A") + " EH/s</span>\n", html: true, delay: 300 },
|
|
{ text: "BLOCK HEIGHT: ", speed: 20, delay: 100 },
|
|
{ text: "<span class='white'>" + numberWithCommas(data.block_number || "N/A") + "</span>\n", html: true, delay: 300 },
|
|
{ text: "\nMINER PERFORMANCE DATA\n", speed: 20, delay: 300 },
|
|
{ text: "CURRENT HASHRATE: ", speed: 20, delay: 100 },
|
|
{ text: "<span class='yellow'>" + (data.hashrate_60sec || "N/A") + " " + (data.hashrate_60sec_unit || "TH/s") + "</span>\n", html: true, delay: 300 },
|
|
{ text: "24HR AVG HASHRATE: ", speed: 20, delay: 100 },
|
|
{ text: "<span class='yellow'>" + (data.hashrate_24hr || "N/A") + " " + (data.hashrate_24hr_unit || "TH/s") + "</span>\n", html: true, delay: 300 },
|
|
{ text: "ACTIVE WORKERS: ", speed: 20, delay: 100 },
|
|
{ text: "<span class='yellow'>" + (data.workers_hashing || "0") + "</span>\n", html: true, delay: 300 },
|
|
{ text: "\nFINANCIAL CALCULATIONS\n", speed: 20, delay: 300 },
|
|
{ text: "DAILY MINING REVENUE: ", speed: 20, delay: 100 },
|
|
{ text: "<span class='green'>$" + numberWithCommas((data.daily_revenue || 0).toFixed(2)) + "</span>\n", html: true, delay: 300 },
|
|
{ text: "DAILY POWER COST: ", speed: 20, delay: 100 },
|
|
{ text: "<span class='red'>$" + numberWithCommas((data.daily_power_cost || 0).toFixed(2)) + "</span>\n", html: true, delay: 300 },
|
|
{ text: "DAILY PROFIT: ", speed: 20, delay: 100 },
|
|
{ text: "<span class='green'>$" + numberWithCommas((data.daily_profit_usd || 0).toFixed(2)) + "</span>\n", html: true, delay: 300 },
|
|
{ text: "PROJECTED MONTHLY PROFIT: ", speed: 20, delay: 100 },
|
|
{ text: "<span class='green'>$" + numberWithCommas((data.monthly_profit_usd || 0).toFixed(2)) + "</span>\n", html: true, delay: 300 },
|
|
{ text: "DAILY SATOSHI YIELD: ", speed: 20, delay: 100 },
|
|
{ text: "<span class='yellow'>" + numberWithCommas(data.daily_mined_sats || 0) + " sats</span>\n", html: true, delay: 300 },
|
|
{ text: "UNPAID EARNINGS: ", speed: 20, delay: 100 },
|
|
{ text: "<span class='green'>" + (data.unpaid_earnings || "0") + " BTC</span>\n", html: true, delay: 300 },
|
|
{ text: "ESTIMATED TIME TO PAYOUT: ", speed: 20, delay: 100 },
|
|
{ text: "<span class='yellow'>" + (data.est_time_to_payout || "Unknown") + "</span>\n", html: true, delay: 300 },
|
|
{ text: "\n", speed: 25, delay: 100 },
|
|
{ text: "<span class='green'>ALL MINING PROCESSES OPERATIONAL</span>\n", html: true, delay: 400 },
|
|
{ text: "\nInitialize mining dashboard? ", speed: 25, delay: 400 },
|
|
{ showPrompt: true, delay: 0 }
|
|
];
|
|
startTyping();
|
|
})
|
|
.catch(error => {
|
|
updateDebug(`Error fetching dashboard data: ${error.message}`);
|
|
});
|
|
</script>
|
|
</body>
|
|
</html>
|