exec / public /js /main.js
aigems's picture
ok
618974d
const commandInput = document.getElementById('command');
const output = document.getElementById('output');
const history = document.getElementById('history');
const executeButton = document.getElementById('executeButton');
const loadingIndicator = document.getElementById('loadingIndicator');
commandInput.addEventListener('keypress', function (event) {
if (event.key === 'Enter') {
executeCommand();
}
});
async function executeCommand() {
const command = commandInput.value;
if (!command.trim()) return;
showLoading(true);
clearOutput();
appendToOutput('$ ' + command, 'text-blue-400');
appendToOutput('正在执行命令...', 'text-yellow-400');
try {
let response = await fetch('/api/execute', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${localStorage.getItem('token')}`
},
body: JSON.stringify({ command })
});
if (response.status === 403) {
const refreshed = await refreshToken();
if (refreshed) {
response = await fetch('/api/execute', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${localStorage.getItem('token')}`
},
body: JSON.stringify({ command })
});
} else {
throw new Error('Token 刷新失败');
}
}
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
clearOutput();
appendToOutput('$ ' + command, 'text-blue-400');
appendToOutput(data.output || data.error || '命令执行成功,但没有输出。', 'text-green-400');
commandInput.value = '';
loadCommandHistory();
} catch (error) {
console.error('执行命令时出错:', error);
clearOutput();
appendToOutput('$ ' + command, 'text-blue-400');
appendToOutput('错误: ' + (error.message || '未知错误'), 'text-red-500');
if (error.message.includes('Token 刷新失败')) {
showNotification('访问被拒绝。请重新登录。', 'error');
localStorage.removeItem('token');
localStorage.removeItem('username');
localStorage.removeItem('password');
checkLoginStatus();
}
} finally {
showLoading(false);
}
}
function clearOutput() {
output.innerHTML = '';
}
function appendToOutput(text, className = 'text-green-400') {
const lines = text.split('\n');
lines.forEach((line, index) => {
const p = document.createElement('p');
p.className = className;
p.textContent = line;
output.appendChild(p);
});
output.scrollTop = output.scrollHeight;
}
async function loadCommandHistory() {
try {
const response = await fetch('/api/command-history', {
headers: {
'Authorization': `Bearer ${localStorage.getItem('token')}`
}
});
const data = await response.json();
history.innerHTML = '';
data.reverse().forEach(item => {
const li = document.createElement('li');
li.innerHTML = `
<div class="flex justify-between items-center p-2 hover:bg-gray-700 rounded transition-colors duration-200">
<span class="font-semibold text-blue-400">${filterXSS(item.command)}</span>
<span class="text-sm text-gray-500">${new Date(item.timestamp).toLocaleString()}</span>
</div>
`;
li.className = 'cursor-pointer';
li.onclick = () => {
commandInput.value = item.command;
commandInput.focus();
};
history.appendChild(li);
});
} catch (error) {
console.error('加载命令历史失败:', error);
showNotification('加载命令历史失败', 'error');
}
}
function showLoading(isLoading) {
loadingIndicator.style.display = isLoading ? 'block' : 'none';
executeButton.disabled = isLoading;
executeButton.classList.toggle('opacity-50', isLoading);
}
async function login() {
const username = document.getElementById('username').value;
const password = document.getElementById('password').value;
try {
const response = await fetch('/api/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username, password })
});
const data = await response.json();
if (response.ok) {
localStorage.setItem('token', data.token);
localStorage.setItem('username', username);
localStorage.setItem('password', password);
document.getElementById('loginForm').style.display = 'none';
document.getElementById('commandInterface').style.display = 'block';
loadCommandHistory();
showNotification('登录成功', 'success');
} else {
showNotification('登录失败: ' + data.error, 'error');
}
} catch (error) {
showNotification('登录错误: ' + error.message, 'error');
}
}
async function refreshToken() {
const username = localStorage.getItem('username');
const password = localStorage.getItem('password');
if (!username || !password) {
document.getElementById('loginForm').style.display = 'block';
document.getElementById('commandInterface').style.display = 'none';
return;
}
try {
const response = await fetch('/api/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username, password })
});
const data = await response.json();
if (response.ok) {
localStorage.setItem('token', data.token);
return true;
} else {
throw new Error(data.error);
}
} catch (error) {
console.error('刷新 token 失败:', error);
return false;
}
}
function showNotification(message, type = 'info') {
const notification = document.createElement('div');
notification.textContent = message;
notification.className = `fixed top-4 right-4 p-4 rounded-lg text-white ${type === 'error' ? 'bg-red-500' : 'bg-green-500'} shadow-lg transition-opacity duration-300`;
document.body.appendChild(notification);
setTimeout(() => {
notification.style.opacity = '0';
setTimeout(() => notification.remove(), 300);
}, 3000);
}
document.getElementById('loginButton').addEventListener('click', login);
function checkLoginStatus() {
const token = localStorage.getItem('token');
if (token) {
document.getElementById('loginForm').style.display = 'none';
document.getElementById('commandInterface').style.display = 'block';
loadCommandHistory();
}
}
window.addEventListener('load', checkLoginStatus);
document.getElementById('executeButton').addEventListener('click', executeCommand);
// 添加打字机效果
function typeWriter(element, text, speed = 50) {
let i = 0;
function type() {
if (i < text.length) {
element.textContent += text.charAt(i);
i++;
setTimeout(type, speed);
}
}
type();
}
// 在页面加载时添加一些酷炫的效果
window.addEventListener('load', () => {
const title = document.querySelector('h1');
title.textContent = '';
typeWriter(title, 'Web 命令执行器', 100);
});