web>app
This commit is contained in:
@@ -375,6 +375,18 @@
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
/* Keypair display */
|
||||
.keypair-section { margin-bottom: 16px; }
|
||||
.keypair-section:last-child { margin-bottom: 0; }
|
||||
.keypair-section-label {
|
||||
font-size: 0.72rem;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
color: var(--text-muted);
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
/* Icon SVG */
|
||||
svg { display: inline-block; vertical-align: middle; }
|
||||
</style>
|
||||
@@ -389,7 +401,7 @@
|
||||
</svg>
|
||||
Key Generator
|
||||
</h1>
|
||||
<p>JWT Secret · API Key · Operation Key · UUID · Random Hex</p>
|
||||
<p>JWT Secret · RS256 · ES256 · API Key · Operation Key · UUID · Random Hex</p>
|
||||
</header>
|
||||
|
||||
<div class="card">
|
||||
@@ -463,6 +475,35 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Keypair result -->
|
||||
<div id="resultKeypair" style="display:none">
|
||||
<div class="keypair-section">
|
||||
<div class="keypair-section-label">Private Key</div>
|
||||
<div class="key-display" id="privateKeyDisplay" style="white-space:pre;font-size:0.75rem;"></div>
|
||||
<div class="copy-row">
|
||||
<button class="copy-btn" id="copyPrivateBtn" onclick="copyPrivateKey()">
|
||||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/>
|
||||
</svg>
|
||||
Copy Private Key
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="keypair-section">
|
||||
<div class="keypair-section-label">Public Key</div>
|
||||
<div class="key-display" id="publicKeyDisplay" style="white-space:pre;font-size:0.75rem;"></div>
|
||||
<div class="copy-row">
|
||||
<button class="copy-btn" id="copyPublicBtn" onclick="copyPublicKey()">
|
||||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/>
|
||||
</svg>
|
||||
Copy Public Key
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="key-meta" id="keyMetaKeypair"></div>
|
||||
</div>
|
||||
|
||||
<!-- Bulk result -->
|
||||
<div id="resultBulk" style="display:none">
|
||||
<div class="bulk-list" id="bulkList"></div>
|
||||
@@ -483,6 +524,8 @@
|
||||
let selectedType = 'jwt_hs256';
|
||||
let bulkMode = false;
|
||||
let lastBulkKeys = [];
|
||||
let lastBulkData = [];
|
||||
let lastKeypair = null;
|
||||
|
||||
function selectType(type, el) {
|
||||
selectedType = type;
|
||||
@@ -541,69 +584,111 @@
|
||||
}
|
||||
}
|
||||
|
||||
const COPY_ICON = `<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>`;
|
||||
const CHECK_ICON = `<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>`;
|
||||
|
||||
function renderSingle(data) {
|
||||
document.getElementById('resultArea').style.display = 'block';
|
||||
document.getElementById('resultSingle').style.display = 'block';
|
||||
document.getElementById('resultBulk').style.display = 'none';
|
||||
|
||||
document.getElementById('keyDisplay').textContent = data.key;
|
||||
if (data.keypair) {
|
||||
lastKeypair = data;
|
||||
document.getElementById('resultSingle').style.display = 'none';
|
||||
document.getElementById('resultKeypair').style.display = 'block';
|
||||
|
||||
const meta = document.getElementById('keyMeta');
|
||||
meta.innerHTML = `
|
||||
<span class="meta-tag highlight">${data.label}</span>
|
||||
<span class="meta-tag">${data.bits} bit</span>
|
||||
<span class="meta-tag">${data.algorithm}</span>
|
||||
<span class="meta-tag">${data.length} chars</span>
|
||||
`;
|
||||
document.getElementById('privateKeyDisplay').textContent = data.key;
|
||||
document.getElementById('publicKeyDisplay').textContent = data.public_key;
|
||||
|
||||
const copyBtn = document.getElementById('copyBtn');
|
||||
copyBtn.classList.remove('copied');
|
||||
copyBtn.innerHTML = `
|
||||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/>
|
||||
</svg> Copy`;
|
||||
const meta = document.getElementById('keyMetaKeypair');
|
||||
meta.innerHTML = `
|
||||
<span class="meta-tag highlight">${data.label}</span>
|
||||
<span class="meta-tag">${data.bits} bit</span>
|
||||
<span class="meta-tag">${data.algorithm}</span>
|
||||
`;
|
||||
document.getElementById('copyPrivateBtn').classList.remove('copied');
|
||||
document.getElementById('copyPublicBtn').classList.remove('copied');
|
||||
document.getElementById('copyPrivateBtn').innerHTML = `${COPY_ICON} Copy Private Key`;
|
||||
document.getElementById('copyPublicBtn').innerHTML = `${COPY_ICON} Copy Public Key`;
|
||||
} else {
|
||||
lastKeypair = null;
|
||||
document.getElementById('resultSingle').style.display = 'block';
|
||||
document.getElementById('resultKeypair').style.display = 'none';
|
||||
|
||||
document.getElementById('keyDisplay').textContent = data.key;
|
||||
|
||||
const meta = document.getElementById('keyMeta');
|
||||
meta.innerHTML = `
|
||||
<span class="meta-tag highlight">${data.label}</span>
|
||||
<span class="meta-tag">${data.bits} bit</span>
|
||||
<span class="meta-tag">${data.algorithm}</span>
|
||||
<span class="meta-tag">${data.length} chars</span>
|
||||
`;
|
||||
|
||||
const copyBtn = document.getElementById('copyBtn');
|
||||
copyBtn.classList.remove('copied');
|
||||
copyBtn.innerHTML = `${COPY_ICON} Copy`;
|
||||
}
|
||||
}
|
||||
|
||||
function renderBulk(items) {
|
||||
lastBulkData = items;
|
||||
lastBulkKeys = items.map(i => i.key);
|
||||
document.getElementById('resultArea').style.display = 'block';
|
||||
document.getElementById('resultSingle').style.display = 'none';
|
||||
document.getElementById('resultKeypair').style.display = 'none';
|
||||
document.getElementById('resultBulk').style.display = 'block';
|
||||
|
||||
const list = document.getElementById('bulkList');
|
||||
list.innerHTML = items.map((item, idx) => `
|
||||
<div class="bulk-item">
|
||||
<span class="bulk-index">#${idx + 1}</span>
|
||||
<span class="bulk-key">${item.key}</span>
|
||||
<button class="bulk-copy" onclick="copyBulkItem(this, '${escHtml(item.key)}')">Copy</button>
|
||||
</div>
|
||||
`).join('');
|
||||
list.innerHTML = items.map((item, idx) => {
|
||||
const displayKey = item.keypair
|
||||
? item.key.split('\n').slice(0, 2).join('\n') + '\n...'
|
||||
: item.key;
|
||||
return `
|
||||
<div class="bulk-item">
|
||||
<span class="bulk-index">#${idx + 1}</span>
|
||||
<span class="bulk-key" style="${item.keypair ? 'white-space:pre;font-size:0.72rem;' : ''}">${escHtmlDisplay(displayKey)}</span>
|
||||
<button class="bulk-copy" data-index="${idx}" onclick="copyBulkItem(this)">Copy</button>
|
||||
</div>`;
|
||||
}).join('');
|
||||
}
|
||||
|
||||
function escHtml(s) {
|
||||
return s.replace(/'/g, "\\'");
|
||||
function escHtmlDisplay(s) {
|
||||
return s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
||||
}
|
||||
|
||||
function flashCopied(btn, resetLabel) {
|
||||
btn.classList.add('copied');
|
||||
btn.innerHTML = `${CHECK_ICON} Copied!`;
|
||||
setTimeout(() => {
|
||||
btn.classList.remove('copied');
|
||||
btn.innerHTML = `${COPY_ICON} ${resetLabel}`;
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
function copyKey() {
|
||||
const key = document.getElementById('keyDisplay').textContent;
|
||||
navigator.clipboard.writeText(key).then(() => {
|
||||
const btn = document.getElementById('copyBtn');
|
||||
btn.classList.add('copied');
|
||||
btn.innerHTML = `
|
||||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="20 6 9 17 4 12"/>
|
||||
</svg> Copied!`;
|
||||
setTimeout(() => {
|
||||
btn.classList.remove('copied');
|
||||
btn.innerHTML = `
|
||||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/>
|
||||
</svg> Copy`;
|
||||
}, 2000);
|
||||
flashCopied(document.getElementById('copyBtn'), 'Copy');
|
||||
});
|
||||
}
|
||||
|
||||
function copyBulkItem(btn, key) {
|
||||
function copyPrivateKey() {
|
||||
if (!lastKeypair) return;
|
||||
navigator.clipboard.writeText(lastKeypair.key).then(() => {
|
||||
flashCopied(document.getElementById('copyPrivateBtn'), 'Copy Private Key');
|
||||
});
|
||||
}
|
||||
|
||||
function copyPublicKey() {
|
||||
if (!lastKeypair) return;
|
||||
navigator.clipboard.writeText(lastKeypair.public_key).then(() => {
|
||||
flashCopied(document.getElementById('copyPublicBtn'), 'Copy Public Key');
|
||||
});
|
||||
}
|
||||
|
||||
function copyBulkItem(btn) {
|
||||
const idx = parseInt(btn.dataset.index);
|
||||
const key = lastBulkData[idx].key;
|
||||
navigator.clipboard.writeText(key).then(() => {
|
||||
btn.classList.add('copied');
|
||||
btn.textContent = 'Copied!';
|
||||
|
||||
Reference in New Issue
Block a user