|
342 | 342 | <label for="sshPrivateKey"> |
343 | 343 | <%= t('nodes.sshPrivateKey') %> |
344 | 344 | <% if (node?.ssh?.privateKey) { %> |
345 | | - <span class="badge badge-success"><%= t('nodes.sshPrivateKeySet') %></span> |
| 345 | + <span class="badge badge-success" id="sshPrivateKeyBadge"><%= t('nodes.sshPrivateKeySet') %></span> |
| 346 | + <% } else { %> |
| 347 | + <span class="badge badge-success" id="sshPrivateKeyBadge" style="display:none"><%= t('nodes.sshPrivateKeySet') %></span> |
346 | 348 | <% } %> |
347 | 349 | </label> |
348 | 350 | <textarea id="sshPrivateKey" name="ssh.privateKey" rows="5" |
349 | 351 | placeholder="<%= t('nodes.sshPrivateKeyHint') %>" |
350 | 352 | style="font-family: monospace; font-size: 12px; resize: vertical;"></textarea> |
351 | | - <small class="hint"><%= t('nodes.sshPrivateKeyEncrypted') %></small> |
352 | | - <% if (node?.ssh?.privateKey) { %> |
353 | | - <div style="margin-top: 6px;"> |
354 | | - <label style="display: inline-flex; align-items: center; gap: 6px; font-size: 13px; cursor: pointer;"> |
355 | | - <input type="checkbox" id="clearPrivateKey" name="ssh.clearPrivateKey" value="1"> |
356 | | - <%= t('nodes.sshPrivateKeyClear') %> |
357 | | - </label> |
358 | | - </div> |
359 | | - <% } %> |
| 353 | + <input type="hidden" name="ssh.clearPrivateKey" id="clearPrivateKeyInput" value="0"> |
| 354 | + <div style="display: flex; align-items: center; gap: 8px; margin-top: 6px; flex-wrap: wrap;"> |
| 355 | + <label class="btn btn-sm btn-secondary" for="sshPrivateKeyFile" style="cursor:pointer; margin:0;"> |
| 356 | + <%= t('nodes.sshPrivateKeyUpload') %> |
| 357 | + <input type="file" id="sshPrivateKeyFile" accept=".pem,.key,*" style="display:none"> |
| 358 | + </label> |
| 359 | + <% if (node?.ssh?.privateKey) { %> |
| 360 | + <button type="button" id="clearPrivateKeyBtn" class="btn btn-sm btn-danger"> |
| 361 | + <%= t('nodes.sshPrivateKeyClear') %> |
| 362 | + </button> |
| 363 | + <% } %> |
| 364 | + <small class="hint" style="margin:0"><%= t('nodes.sshPrivateKeyEncrypted') %></small> |
| 365 | + </div> |
360 | 366 | </div> |
361 | 367 | <% if (node?._id) { %> |
362 | 368 | <div class="form-group" id="generateSshKeyGroup"> |
@@ -1039,6 +1045,32 @@ async function generateXrayKeys() { |
1039 | 1045 | } |
1040 | 1046 | })(); |
1041 | 1047 |
|
| 1048 | +// SSH private key file upload |
| 1049 | +document.getElementById('sshPrivateKeyFile')?.addEventListener('change', function() { |
| 1050 | + const file = this.files[0]; |
| 1051 | + if (!file) return; |
| 1052 | + const reader = new FileReader(); |
| 1053 | + reader.onload = (e) => { |
| 1054 | + document.getElementById('sshPrivateKey').value = e.target.result; |
| 1055 | + document.getElementById('sshPrivateKeyBadge').style.display = ''; |
| 1056 | + }; |
| 1057 | + reader.readAsText(file); |
| 1058 | + this.value = ''; |
| 1059 | +}); |
| 1060 | +
|
| 1061 | +// Clear private key button |
| 1062 | +document.getElementById('clearPrivateKeyBtn')?.addEventListener('click', function() { |
| 1063 | + document.getElementById('sshPrivateKey').value = ''; |
| 1064 | + document.getElementById('clearPrivateKeyInput').value = '1'; |
| 1065 | + document.getElementById('sshPrivateKeyBadge').style.display = 'none'; |
| 1066 | + this.style.display = 'none'; |
| 1067 | +}); |
| 1068 | +
|
| 1069 | +// Show badge when user types in textarea |
| 1070 | +document.getElementById('sshPrivateKey')?.addEventListener('input', function() { |
| 1071 | + document.getElementById('sshPrivateKeyBadge').style.display = this.value.trim() ? '' : 'none'; |
| 1072 | +}); |
| 1073 | +
|
1042 | 1074 | <% if (node && node._id) { %> |
1043 | 1075 | // Generate & Install SSH Key |
1044 | 1076 | document.getElementById('generateSshKeyBtn')?.addEventListener('click', async function() { |
|
0 commit comments