Files
web_security/app.js

617 lines
19 KiB
JavaScript
Raw Permalink Normal View History

// API基础URL
const API_BASE_URL = 'http://localhost:8001';
// 全局状态
let currentUser = null;
let accessToken = localStorage.getItem('accessToken');
let lastApiCalls = {}; // 记录上次API调用时间防止重复请求
// DOM元素
const navMenu = document.getElementById('navMenu');
const userInfo = document.getElementById('userInfo');
const loginBtn = document.getElementById('loginBtn');
const registerBtn = document.getElementById('registerBtn');
const logoutBtn = document.getElementById('logoutBtn');
const loginSection = document.getElementById('loginSection');
const registerSection = document.getElementById('registerSection');
const mainSection = document.getElementById('mainSection');
const loginForm = document.getElementById('loginForm');
const registerForm = document.getElementById('registerForm');
const editProfileForm = document.getElementById('editProfileForm');
const createDocForm = document.getElementById('createDocForm');
const editDocForm = document.getElementById('editDocForm');
const currentUserName = document.getElementById('currentUserName');
const editProfileBtn = document.getElementById('editProfileBtn');
const viewLogsBtn = document.getElementById('viewLogsBtn');
const createDocBtn = document.getElementById('createDocBtn');
const documentsList = document.getElementById('documentsList');
const logsList = document.getElementById('logsList');
const message = document.getElementById('message');
// 模态框相关元素
const editProfileModal = document.getElementById('editProfileModal');
const createDocModal = document.getElementById('createDocModal');
const editDocModal = document.getElementById('editDocModal');
const logsModal = document.getElementById('logsModal');
// 用户管理相关元素
const userManagementSection = document.getElementById('userManagementSection');
const usersList = document.getElementById('usersList');
const refreshUsersBtn = document.getElementById('refreshUsersBtn');
// API请求函数
async function apiRequest(url, options = {}) {
const config = {
headers: {
'Content-Type': 'application/json',
...options.headers
},
...options
};
if (accessToken) {
config.headers['Authorization'] = `Bearer ${accessToken}`;
}
try {
const response = await fetch(`${API_BASE_URL}${url}`, config);
if (response.status === 401) {
// Token过期清除本地存储并重新登录
accessToken = null;
localStorage.removeItem('accessToken');
currentUser = null;
showLoginSection();
showMessage('登录已过期,请重新登录', 'error');
return null;
}
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new Error(errorData.detail || `HTTP error! status: ${response.status}`);
}
const result = await response.json();
return result;
} catch (error) {
console.error('API请求错误:', error);
showMessage(`请求失败: ${error.message}`, 'error');
return null;
}
}
// 显示消息提示
function showMessage(text, type = 'info') {
message.textContent = text;
message.className = `message ${type}`;
message.style.display = 'block';
setTimeout(() => {
message.style.display = 'none';
}, 3000);
}
// 用户认证相关函数
async function login(username, password) {
const data = await apiRequest('/auth/login', {
method: 'POST',
body: JSON.stringify({ username, password })
});
if (data && data.access_token) {
accessToken = data.access_token;
localStorage.setItem('accessToken', accessToken);
await loadCurrentUser();
showMainSection();
showMessage('登录成功', 'success');
return true;
}
return false;
}
async function register(userData) {
// 将isAdmin添加到userData中
const registrationData = {
...userData,
is_admin: document.getElementById('registerIsAdmin') ? document.getElementById('registerIsAdmin').checked : false
};
const data = await apiRequest('/auth/register', {
method: 'POST',
body: JSON.stringify(registrationData)
});
if (data) {
showMessage('注册成功,请登录', 'success');
// 重置勾选框
if(document.getElementById('registerIsAdmin')) {
document.getElementById('registerIsAdmin').checked = false;
}
showLoginSection();
return true;
}
return false;
}
async function logout() {
await apiRequest('/auth/logout', { method: 'POST' });
accessToken = null;
localStorage.removeItem('accessToken');
currentUser = null;
showLoginSection();
showMessage('已退出登录', 'info');
}
async function loadCurrentUser() {
const data = await apiRequest('/users/me');
if (data) {
currentUser = data;
userInfo.textContent = `欢迎, ${data.username}`;
currentUserName.textContent = data.username;
return true;
}
return false;
}
async function updateUserProfile(updateData) {
const data = await apiRequest('/users/me', {
method: 'PUT',
body: JSON.stringify(updateData)
});
if (data) {
await loadCurrentUser();
showMessage('个人信息更新成功', 'success');
return true;
}
return false;
}
// 文档管理相关函数
async function loadDocuments() {
const data = await apiRequest('/documents');
if (data) {
displayDocuments(data);
return true;
}
return false;
}
async function createDocument(docData) {
const data = await apiRequest('/documents', {
method: 'POST',
body: JSON.stringify(docData)
});
if (data) {
await loadDocuments();
showMessage('文档创建成功', 'success');
return true;
}
return false;
}
async function updateDocument(docId, docData) {
const data = await apiRequest(`/documents/${docId}`, {
method: 'PUT',
body: JSON.stringify(docData)
});
if (data) {
await loadDocuments();
showMessage('文档更新成功', 'success');
return true;
}
return false;
}
async function deleteDocument(docId) {
const data = await apiRequest(`/documents/${docId}`, {
method: 'DELETE'
});
if (data) {
await loadDocuments();
showMessage('文档删除成功', 'success');
return true;
}
return false;
}
// 操作日志相关函数
async function loadLogs() {
const data = await apiRequest('/logs/my');
if (data) {
displayLogs(data);
return true;
}
return false;
}
// 用户管理相关函数
async function loadUsers() {
const data = await apiRequest('/users');
if (data) {
displayUsers(data);
return true;
}
return false;
}
async function deleteUser(userId) {
if (confirm('确定要删除这个用户吗?此操作不可恢复。')) {
const data = await apiRequest(`/users/${userId}`, {
method: 'DELETE'
});
if (data) {
await loadUsers();
showMessage('用户删除成功', 'success');
return true;
}
}
return false;
}
// 界面显示控制函数
function showLoginSection() {
loginSection.style.display = 'block';
registerSection.style.display = 'none';
mainSection.style.display = 'none';
loginBtn.style.display = 'inline-block';
registerBtn.style.display = 'inline-block';
logoutBtn.style.display = 'none';
userInfo.style.display = 'none';
}
function showRegisterSection() {
loginSection.style.display = 'none';
registerSection.style.display = 'block';
mainSection.style.display = 'none';
}
function showMainSection() {
loginSection.style.display = 'none';
registerSection.style.display = 'none';
mainSection.style.display = 'block';
loginBtn.style.display = 'none';
registerBtn.style.display = 'none';
logoutBtn.style.display = 'inline-block';
userInfo.style.display = 'inline-block';
// 检查是否为管理员,如果是则显示用户管理界面
if (currentUser && currentUser.is_admin) {
userManagementSection.style.display = 'block';
loadUsers();
} else {
userManagementSection.style.display = 'none';
}
loadDocuments();
}
// 显示/隐藏模态框函数
function showModal(modal) {
modal.style.display = 'flex';
}
function hideModal(modal) {
modal.style.display = 'none';
}
// 文档显示函数
function displayDocuments(documents) {
if (documents.length === 0) {
documentsList.innerHTML = `
<div class="empty-state">
<h3>暂无文档</h3>
<p>点击"创建新文档"按钮开始创建您的第一个文档</p>
</div>
`;
return;
}
documentsList.innerHTML = documents.map(doc => `
<div class="document-item" onclick="openEditDocumentModal(${doc.id})">
<div class="document-header">
<div>
<div class="document-title">${escapeHtml(doc.title)}</div>
<div class="document-meta">
<span>类型: ${doc.file_type}</span>
<span>大小: ${formatFileSize(doc.file_size)}</span>
<span>${doc.is_public ? '公开' : '私有'}</span>
</div>
</div>
<div class="document-actions">
<button class="btn-secondary" onclick="event.stopPropagation(); openEditDocumentModal(${doc.id})">编辑</button>
</div>
</div>
${doc.description ? `<div class="document-description">${escapeHtml(doc.description)}</div>` : ''}
<div class="document-content-preview">${escapeHtml(doc.content.substring(0, 100))}${doc.content.length > 100 ? '...' : ''}</div>
<div class="document-meta">创建时间: ${formatDate(doc.created_at)}</div>
</div>
`).join('');
}
// 用户显示函数
function displayUsers(users) {
if (users.length === 0) {
usersList.innerHTML = '<div class="empty-state"><h3>暂无用户</h3></div>';
return;
}
usersList.innerHTML = users.map(user => `
<div class="user-item">
<div class="user-header">
<div class="user-info">
<div class="user-username">${escapeHtml(user.username)}</div>
<div class="user-email">${escapeHtml(user.email)}</div>
${user.full_name ? `<div class="user-fullname">${escapeHtml(user.full_name)}</div>` : ''}
<div class="user-meta">
<span>ID: ${user.id}</span>
<span>注册时间: ${formatDate(user.created_at)}</span>
${user.is_admin ? '<span class="user-admin-badge">管理员</span>' : ''}
</div>
</div>
<div class="user-actions">
${user.id !== currentUser.id ? `<button class="btn-danger" onclick="deleteUser(${user.id})">删除</button>` : ''}
</div>
</div>
</div>
`).join('');
}
// 日志显示函数
function displayLogs(logs) {
if (logs.length === 0) {
logsList.innerHTML = '<div class="empty-state"><h3>暂无操作日志</h3></div>';
return;
}
logsList.innerHTML = logs.map(log => `
<div class="log-item">
<div class="log-header">
<span class="log-action">${escapeHtml(log.operation)}</span>
<span class="log-time">${formatDate(log.created_at)}</span>
</div>
<div class="log-details">
详情: ${escapeHtml(log.details || '无')}
</div>
</div>
`).join('');
}
// 工具函数
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
function formatDate(dateString) {
return new Date(dateString).toLocaleString('zh-CN');
}
function formatFileSize(bytes) {
if (bytes === 0) return '0 B';
const k = 1024;
const sizes = ['B', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
}
// 模态框操作函数
function openEditProfileModal() {
if (!currentUser) return;
document.getElementById('editEmail').value = currentUser.email || '';
document.getElementById('editFullName').value = currentUser.full_name || '';
document.getElementById('editPassword').value = '';
showModal(editProfileModal);
}
function openCreateDocumentModal() {
document.getElementById('docTitle').value = '';
document.getElementById('docDescription').value = '';
document.getElementById('docContent').value = '';
document.getElementById('docType').value = 'txt';
document.getElementById('docPublic').checked = false;
showModal(createDocModal);
}
async function openEditDocumentModal(docId) {
const data = await apiRequest(`/documents/${docId}`);
if (!data) return;
document.getElementById('editDocId').value = docId;
document.getElementById('editDocTitle').value = data.title;
document.getElementById('editDocDescription').value = data.description || '';
document.getElementById('editDocContent').value = data.content;
document.getElementById('editDocType').value = data.file_type;
document.getElementById('editDocPublic').checked = data.is_public;
showModal(editDocModal);
}
async function openLogsModal() {
await loadLogs();
showModal(logsModal);
}
// 导航按钮事件
loginBtn.addEventListener('click', showLoginSection);
registerBtn.addEventListener('click', showRegisterSection);
logoutBtn.addEventListener('click', logout);
// 表单切换链接
document.getElementById('showRegister').addEventListener('click', (e) => {
e.preventDefault();
showRegisterSection();
});
document.getElementById('showLogin').addEventListener('click', (e) => {
e.preventDefault();
showLoginSection();
});
// 删除文档按钮
document.getElementById('deleteDocBtn').addEventListener('click', async (e) => {
e.preventDefault();
const docId = document.getElementById('editDocId').value;
if (confirm('确定要删除这个文档吗?此操作不可恢复。')) {
const success = await deleteDocument(docId);
if (success) {
hideModal(editDocModal);
}
}
});
// 主界面按钮事件
editProfileBtn.addEventListener('click', openEditProfileModal);
viewLogsBtn.addEventListener('click', openLogsModal);
createDocBtn.addEventListener('click', openCreateDocumentModal);
// 用户管理按钮事件
refreshUsersBtn.addEventListener('click', loadUsers);
// 模态框关闭事件
document.getElementById('closeEditModal').addEventListener('click', () => hideModal(editProfileModal));
document.getElementById('closeCreateDocModal').addEventListener('click', () => hideModal(createDocModal));
document.getElementById('closeEditDocModal').addEventListener('click', () => hideModal(editDocModal));
document.getElementById('closeLogsModal').addEventListener('click', () => hideModal(logsModal));
// 点击模态框外部关闭
[editProfileModal, createDocModal, editDocModal, logsModal].forEach(modal => {
modal.addEventListener('click', (e) => {
if (e.target === modal) {
hideModal(modal);
}
});
});
// 键盘事件ESC键关闭模态框
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
[editProfileModal, createDocModal, editDocModal, logsModal].forEach(modal => {
if (modal.style.display === 'flex') {
hideModal(modal);
}
});
}
});
// 表单提交事件
loginForm.addEventListener('submit', async (e) => {
e.preventDefault();
const username = document.getElementById('loginUsername').value;
const password = document.getElementById('loginPassword').value;
await login(username, password);
});
registerForm.addEventListener('submit', async (e) => {
e.preventDefault();
const userData = {
username: document.getElementById('registerUsername').value,
email: document.getElementById('registerEmail').value,
full_name: document.getElementById('registerFullName').value,
password: document.getElementById('registerPassword').value,
is_admin: document.getElementById('registerIsAdmin').checked
};
await register(userData);
});
editProfileForm.addEventListener('submit', async (e) => {
e.preventDefault();
const updateData = {
email: document.getElementById('editEmail').value,
full_name: document.getElementById('editFullName').value
};
const newPassword = document.getElementById('editPassword').value;
if (newPassword) {
updateData.password = newPassword;
}
const success = await updateUserProfile(updateData);
if (success) {
hideModal(editProfileModal);
}
});
createDocForm.addEventListener('submit', async (e) => {
e.preventDefault();
const docData = {
title: document.getElementById('docTitle').value,
description: document.getElementById('docDescription').value,
content: document.getElementById('docContent').value,
file_type: document.getElementById('docType').value,
is_public: document.getElementById('docPublic').checked
};
const success = await createDocument(docData);
if (success) {
hideModal(createDocModal);
}
});
editDocForm.addEventListener('submit', async (e) => {
e.preventDefault();
const docId = document.getElementById('editDocId').value;
const docData = {
title: document.getElementById('editDocTitle').value,
description: document.getElementById('editDocDescription').value,
content: document.getElementById('editDocContent').value,
file_type: document.getElementById('editDocType').value,
is_public: document.getElementById('editDocPublic').checked
};
const success = await updateDocument(docId, docData);
if (success) {
hideModal(editDocModal);
}
});
// 事件监听器
let isInitialized = false; // 防止重复初始化
document.addEventListener('DOMContentLoaded', async function() {
// 防止重复初始化
if (isInitialized) {
console.log('页面已初始化,跳过重复初始化');
return;
}
isInitialized = true;
// 检查是否已登录
if (accessToken) {
try {
const success = await loadCurrentUser();
if (success) {
showMainSection();
} else {
// 如果loadCurrentUser返回false说明token无效清除token并显示登录界面
accessToken = null;
localStorage.removeItem('accessToken');
showLoginSection();
}
} catch (error) {
// 捕获可能的错误清除token并显示登录界面
console.error('初始化错误:', error);
accessToken = null;
localStorage.removeItem('accessToken');
showLoginSection();
}
} else {
showLoginSection();
}
});