lthn.io/app/Mod/Names/Views/register.blade.php
Claude 8a21996add
security: add CSP nonce attributes to inline scripts and styles
Added @cspnonce to all inline <script> and <style> tags in layout,
explorer, and register views. Enabled nonce generation in headers
config. unsafe-inline kept as fallback. Nonces will activate after
container restart when the Headers Boot registers the Blade directive.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 10:22:38 +01:00

139 lines
6.8 KiB
PHP

@extends('lethean::layout')
@section('title', 'Register a .lthn name')
@section('meta_description', 'Check availability and register your .lthn blockchain domain name. Free registration, DNS included, pay with BTC.')
@section('content')
<div class="section" style="max-width: 600px; margin: 0 auto;">
<div style="text-align: center; margin-bottom: 2rem;">
<h2>Register a .lthn Name</h2>
<p style="color: var(--muted);">Claim your identity on the Lethean blockchain. Free registration.</p>
</div>
<div class="card">
<div style="display: flex; gap: 0.5rem;">
<input type="text" id="name-input" placeholder="Enter a name..." autofocus
style="flex: 1; padding: 0.75rem 1rem; background: var(--bg); border: 1px solid var(--border); border-radius: 0.5rem; color: var(--text); font-size: 1rem;">
<span style="padding: 0.75rem 0; color: var(--muted); font-size: 1rem;">.lthn</span>
<button id="check-btn" class="api-link" style="margin-top: 0; padding: 0.75rem 1.5rem;">Check</button>
</div>
<div id="result" style="margin-top: 1rem; min-height: 60px;"></div>
</div>
<div class="card-grid" style="margin-top: 2rem;">
<div class="card">
<h3>What you get</h3>
<ul style="color: var(--muted); line-height: 2; list-style: none; padding: 0; font-size: 0.85rem;">
<li>Blockchain-native identity</li>
<li>DNS management portal</li>
<li>VPN/proxy routing</li>
<li>Free .lthn SSL cert</li>
</ul>
</div>
<div class="card">
<h3>Requirements</h3>
<ul style="color: var(--muted); line-height: 2; list-style: none; padding: 0; font-size: 0.85rem;">
<li>6+ characters (a-z, 0-9, dash, dot)</li>
<li>Not already registered</li>
<li>Not reserved during sunrise</li>
<li>BTC for payment (free for now)</li>
</ul>
</div>
</div>
</div>
<script @cspnonce>
(function() {
var input = document.getElementById('name-input');
var btn = document.getElementById('check-btn');
var result = document.getElementById('result');
function el(tag, style, text) {
var e = document.createElement(tag);
if (style) Object.assign(e.style, style);
if (text) e.textContent = text;
return e;
}
function check() {
var name = input.value.trim().toLowerCase().replace(/\.lthn$/, '').replace(/[^a-z0-9.\-]/g, '');
if (!name || name.length < 6) {
result.textContent = '';
result.appendChild(el('p', {color: '#fbbf24', fontSize: '0.9rem'}, 'Name must be at least 6 characters.'));
return;
}
input.value = name;
result.textContent = '';
result.appendChild(el('p', {color: '#9ca3af', fontSize: '0.9rem'}, 'Checking...'));
btn.disabled = true;
fetch((window.LTHN_API || '') + '/v1/names/available/' + encodeURIComponent(name), {headers: {'Accept': 'application/json'}})
.then(function(r) { return r.json(); })
.then(function(data) {
btn.disabled = false;
result.textContent = '';
if (data.available && !data.reserved) {
var wrap = el('div', {});
wrap.appendChild(el('p', {color: '#34d399', fontWeight: '600', fontSize: '1.1rem', marginBottom: '0.75rem'}, name + '.lthn is available!'));
var emailInput = el('input', {
padding: '0.5rem 1rem', background: '#0a0e17', border: '1px solid #1f2937',
borderRadius: '0.5rem', color: '#e5e7eb', fontSize: '0.9rem', width: '100%', marginBottom: '0.5rem'
});
emailInput.type = 'email';
emailInput.placeholder = 'Your email address';
emailInput.id = 'claim-email';
wrap.appendChild(emailInput);
var claimBtn = el('button', {
padding: '0.75rem 2rem', background: '#34d399', color: '#fff',
borderRadius: '0.5rem', border: 'none', fontWeight: '600', fontSize: '1rem', cursor: 'pointer', width: '100%'
}, 'Claim ' + name + '.lthn');
claimBtn.addEventListener('click', function() {
var email = document.getElementById('claim-email').value;
if (!email) { return; }
claimBtn.disabled = true;
claimBtn.textContent = 'Submitting...';
fetch((window.LTHN_API || '') + '/v1/names/claim', {
method: 'POST',
headers: {'Content-Type': 'application/json', 'Accept': 'application/json'},
body: JSON.stringify({name: name, email: email})
}).then(function(r) { return r.json(); }).then(function(d) {
result.textContent = '';
if (d.claim_id) {
result.appendChild(el('p', {color: '#34d399', fontWeight: '600', fontSize: '1rem'}, 'Claim submitted!'));
result.appendChild(el('p', {color: '#9ca3af', fontSize: '0.85rem'}, 'Claim ID: ' + d.claim_id + '. We will email ' + email + ' when approved.'));
} else {
result.appendChild(el('p', {color: '#fbbf24', fontSize: '0.9rem'}, d.error || 'Something went wrong.'));
}
});
});
wrap.appendChild(claimBtn);
result.appendChild(wrap);
} else if (data.reserved) {
var p = el('p', {color: '#fbbf24', fontSize: '0.9rem'}, name + '.lthn is reserved during the sunrise period. ');
var a = el('a', {color: '#818cf8'}, 'Learn more');
a.href = '/sunrise';
p.appendChild(a);
result.appendChild(p);
} else {
var p2 = el('p', {color: '#fbbf24', fontSize: '0.9rem'}, name + '.lthn is already taken. ');
var a2 = el('a', {color: '#818cf8'}, 'View details');
a2.href = '/names/' + name;
p2.appendChild(a2);
result.appendChild(p2);
}
})
.catch(function() {
btn.disabled = false;
result.textContent = '';
result.appendChild(el('p', {color: '#fbbf24', fontSize: '0.9rem'}, 'Could not check. Try again.'));
});
}
btn.addEventListener('click', check);
input.addEventListener('keypress', function(e) { if (e.key === 'Enter') check(); });
})();
</script>
@endsection