/**
* Application JavaScript pour le Dashboard Bitcoin Signet
*/
const API_BASE_URL = window.location.origin;
// Utiliser le même origin pour le faucet (sera configuré via le proxy)
// Si on est sur dashboard.certificator.4nkweb.com, utiliser faucet.certificator.4nkweb.com
let FAUCET_API_URL;
if (window.location.hostname.includes('dashboard.certificator.4nkweb.com')) {
FAUCET_API_URL = window.location.origin.replace('dashboard.certificator.4nkweb.com', 'faucet.certificator.4nkweb.com');
} else if (window.location.hostname.includes('localhost') || window.location.hostname === '127.0.0.1') {
FAUCET_API_URL = 'http://localhost:3021';
} else {
FAUCET_API_URL = window.location.origin;
}
let selectedFile = null;
// Initialisation
document.addEventListener('DOMContentLoaded', () => {
loadData();
setInterval(loadData, 30000); // Rafraîchir toutes les 30 secondes
});
/**
* Charge toutes les données de supervision
*/
async function loadData() {
try {
await Promise.all([
loadBlockchainInfo(),
loadLatestBlock(),
loadWalletBalance(),
loadAnchorCount(),
loadNetworkPeers(),
]);
updateLastUpdateTime();
} catch (error) {
console.error('Error loading data:', error);
}
}
/**
* Charge les informations de la blockchain
*/
async function loadBlockchainInfo() {
try {
const response = await fetch(`${API_BASE_URL}/api/blockchain/info`);
const data = await response.json();
document.getElementById('block-height').textContent = data.blocks || 0;
} catch (error) {
console.error('Error loading blockchain info:', error);
document.getElementById('block-height').textContent = 'Erreur';
}
}
/**
* Charge les informations du dernier bloc
*/
async function loadLatestBlock() {
try {
const response = await fetch(`${API_BASE_URL}/api/blockchain/latest-block`);
const data = await response.json();
if (data.time) {
const date = new Date(data.time * 1000);
document.getElementById('last-block-time').textContent = date.toLocaleString('fr-FR');
} else {
document.getElementById('last-block-time').textContent = 'Aucun bloc';
}
document.getElementById('last-block-tx-count').textContent = data.tx_count || 0;
} catch (error) {
console.error('Error loading latest block:', error);
document.getElementById('last-block-time').textContent = 'Erreur';
document.getElementById('last-block-tx-count').textContent = 'Erreur';
}
}
/**
* Charge le solde du wallet
*/
async function loadWalletBalance() {
try {
const response = await fetch(`${API_BASE_URL}/api/wallet/balance`);
const data = await response.json();
document.getElementById('balance-mature').textContent = formatBTC(data.mature || 0);
document.getElementById('balance-immature').textContent = formatBTC(data.immature || 0);
} catch (error) {
console.error('Error loading wallet balance:', error);
document.getElementById('balance-mature').textContent = 'Erreur';
document.getElementById('balance-immature').textContent = 'Erreur';
}
}
/**
* Charge le nombre d'ancrages
*/
async function loadAnchorCount() {
try {
const response = await fetch(`${API_BASE_URL}/api/anchor/count`);
const data = await response.json();
document.getElementById('anchor-count').textContent = data.count || 0;
} catch (error) {
console.error('Error loading anchor count:', error);
document.getElementById('anchor-count').textContent = 'Erreur';
}
}
/**
* Charge les informations sur les pairs
*/
async function loadNetworkPeers() {
try {
const response = await fetch(`${API_BASE_URL}/api/network/peers`);
const data = await response.json();
document.getElementById('peer-count').textContent = data.connections || 0;
} catch (error) {
console.error('Error loading network peers:', error);
document.getElementById('peer-count').textContent = 'Erreur';
}
}
/**
* Formate un montant en BTC
*/
function formatBTC(btc) {
if (btc === 0) return '0 BTC';
if (btc < 0.000001) return `${(btc * 100000000).toFixed(0)} sats`;
return `${btc.toFixed(8)} BTC`;
}
/**
* Met à jour l'heure de dernière mise à jour
*/
function updateLastUpdateTime() {
const now = new Date();
document.getElementById('last-update').textContent = now.toLocaleString('fr-FR');
}
/**
* Change d'onglet
*/
function switchTab(tab, buttonElement) {
// Désactiver tous les onglets
document.querySelectorAll('.tab-content').forEach(content => {
content.classList.remove('active');
});
document.querySelectorAll('.tab-button').forEach(button => {
button.classList.remove('active');
});
// Activer l'onglet sélectionné
document.getElementById(`${tab}-tab`).classList.add('active');
// Activer le bouton correspondant
if (buttonElement) {
buttonElement.classList.add('active');
}
}
/**
* Gère la sélection de fichier
*/
function handleFileSelect(event) {
const file = event.target.files[0];
if (file) {
selectedFile = file;
const fileInfo = document.getElementById('file-info');
fileInfo.innerHTML = `
Fichier sélectionné : ${file.name}
Taille : ${formatFileSize(file.size)}
Type : ${file.type || 'Non spécifié'}
`;
}
}
/**
* Formate la taille d'un fichier
*/
function formatFileSize(bytes) {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i];
}
/**
* Génère le hash depuis le texte
*/
async function generateHashFromText() {
const text = document.getElementById('anchor-text').value;
if (!text.trim()) {
showResult('anchor-result', 'error', 'Veuillez entrer un texte à ancrer.');
return;
}
try {
const response = await fetch(`${API_BASE_URL}/api/hash/generate`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ text }),
});
const data = await response.json();
if (data.hash) {
document.getElementById('anchor-hash').value = data.hash;
showResult('anchor-result', 'success', `Hash généré avec succès : ${data.hash}`);
} else {
showResult('anchor-result', 'error', 'Erreur lors de la génération du hash.');
}
} catch (error) {
showResult('anchor-result', 'error', `Erreur : ${error.message}`);
}
}
/**
* Génère le hash depuis le fichier
*/
async function generateHashFromFile() {
if (!selectedFile) {
showResult('anchor-result', 'error', 'Veuillez sélectionner un fichier.');
return;
}
try {
const reader = new FileReader();
reader.onload = async (e) => {
const fileContent = e.target.result;
try {
const response = await fetch(`${API_BASE_URL}/api/hash/generate`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ fileContent }),
});
const data = await response.json();
if (data.hash) {
document.getElementById('anchor-hash').value = data.hash;
showResult('anchor-result', 'success', `Hash généré avec succès : ${data.hash}`);
} else {
showResult('anchor-result', 'error', 'Erreur lors de la génération du hash.');
}
} catch (error) {
showResult('anchor-result', 'error', `Erreur : ${error.message}`);
}
};
reader.readAsText(selectedFile);
} catch (error) {
showResult('anchor-result', 'error', `Erreur lors de la lecture du fichier : ${error.message}`);
}
}
/**
* Ancre le document
*/
async function anchorDocument() {
const hash = document.getElementById('anchor-hash').value;
if (!hash || !/^[0-9a-fA-F]{64}$/.test(hash)) {
showResult('anchor-result', 'error', 'Veuillez générer un hash valide (64 caractères hexadécimaux).');
return;
}
try {
showResult('anchor-result', 'info', 'Ancrage en cours...');
const response = await fetch(`${API_BASE_URL}/api/anchor/test`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ hash }),
});
const data = await response.json();
if (response.ok && data.txid) {
showResult('anchor-result', 'success',
`Document ancré avec succès !
TXID : ${data.txid}
Statut : ${data.status}
Confirmations : ${data.confirmations || 0}`);
// Recharger le nombre d'ancrages après un court délai
setTimeout(loadAnchorCount, 2000);
} else {
showResult('anchor-result', 'error', data.message || data.error || 'Erreur lors de l\'ancrage.');
}
} catch (error) {
showResult('anchor-result', 'error', `Erreur : ${error.message}`);
}
}
/**
* Demande des sats via le faucet
*/
async function requestFaucet() {
const address = document.getElementById('faucet-address').value.trim();
if (!address) {
showResult('faucet-result', 'error', 'Veuillez entrer une adresse Bitcoin.');
return;
}
// Validation basique de l'adresse
const addressPattern = /^(tb1|bcrt1|2|3)[a-zA-HJ-NP-Z0-9]{25,62}$/;
if (!addressPattern.test(address)) {
showResult('faucet-result', 'error', 'Adresse Bitcoin invalide.');
return;
}
try {
showResult('faucet-result', 'info', 'Demande en cours...');
const response = await fetch(`${FAUCET_API_URL}/api/faucet/request`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ address }),
});
const data = await response.json();
if (response.ok && data.txid) {
showResult('faucet-result', 'success',
`50 000 sats envoyés avec succès !
TXID : ${data.txid}
Montant : ${data.amount || '50000'} sats
Statut : ${data.status || 'En attente de confirmation'}`);
} else {
showResult('faucet-result', 'error', data.message || data.error || 'Erreur lors de la demande.');
}
} catch (error) {
showResult('faucet-result', 'error', `Erreur : ${error.message}`);
}
}
/**
* Affiche un résultat
*/
function showResult(elementId, type, message) {
const element = document.getElementById(elementId);
element.className = `result ${type}`;
element.innerHTML = message;
}