Browse Source

停止所有服务

master
zhanglei 3 months ago
parent
commit
8da67a4112
  1. 45
      web/app.py
  2. 23
      web/static/css/style.css
  3. 517
      web/static/css/style.css.bak
  4. 59
      web/static/js/script.js
  5. 521
      web/static/js/script.js.bak
  6. 2
      web/templates/index.html
  7. 192
      web/templates/index.html.bak

45
web/app.py

@ -47,6 +47,50 @@ def get_safe_path(requested_path):
if is_path_allowed(requested_path):
return requested_path
return ALLOWED_BASE_DIR
def kill_all_services():
"""
杀死所有frpc_linux和python ./p2pfile.py相关的进程
"""
killed_processes = []
try:
# 查找并杀死所有frpc_linux进程
for proc in psutil.process_iter(['pid', 'name', 'cmdline']):
try:
if proc.info['cmdline'] and any('frpc_linux' in part for part in proc.info['cmdline']):
proc.terminate()
killed_processes.append(f"frpc_linux (PID: {proc.info['pid']})")
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
pass
# 查找并杀死所有python p2pfile.py进程
for proc in psutil.process_iter(['pid', 'name', 'cmdline']):
try:
if (proc.info['cmdline'] and
'python' in proc.info['cmdline'] and
any('p2pfile.py' in part for part in proc.info['cmdline'])):
proc.terminate()
killed_processes.append(f"p2pfile.py (PID: {proc.info['pid']})")
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
pass
# 等待一段时间让进程结束
time.sleep(1)
# 强制杀死任何仍然运行的进程
for proc in psutil.process_iter(['pid', 'name', 'cmdline']):
try:
if (proc.info['cmdline'] and
(any('frpc_linux' in part for part in proc.info['cmdline']) or
('python' in proc.info['cmdline'] and any('p2pfile.py' in part for part in proc.info['cmdline'])))):
proc.kill()
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
pass
return killed_processes
except Exception as e:
return [f"错误: {str(e)}"]
@app.route('/')
def index():
@ -665,6 +709,7 @@ def check_file():
except Exception as e:
return jsonify({'status': 'error', 'message': f'检查文件失败: {str(e)}'})
if __name__ == '__main__':
print("启动蜗牛文件传输Web服务...")
print(f"允许访问的目录: {ALLOWED_BASE_DIR}")

23
web/static/css/style.css

@ -514,4 +514,27 @@ body {
.button-group .btn {
width: 100%;
}
/* 添加停止所有服务按钮样式 */
.btn-warning {
background-color: #ffc107;
color: #212529;
}
.btn-warning:hover {
background-color: #e0a800;
color: #212529;
}
/* 按钮组样式调整 */
.button-group {
display: flex;
gap: 10px;
margin: 20px 0;
flex-wrap: wrap;
}
.button-group .btn {
margin: 5px 0;
}
}

517
web/static/css/style.css.bak

@ -1,517 +0,0 @@
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Microsoft YaHei', sans-serif;
background-color: white;
color: #2c3e50;
line-height: 1.6;
}
.container {
max-width: 1000px;
margin: 0 auto;
padding: 20px;
}
.screen {
display: none;
}
.screen.active {
display: block;
}
.logo-area {
text-align: center;
margin: 30px 0;
}
.logo-area h1 {
font-size: 24px;
font-weight: bold;
margin-bottom: 5px;
}
.logo-area p {
font-size: 14px;
color: #7f8c8d;
}
.card-container {
display: flex;
justify-content: center;
gap: 30px;
margin: 40px 0;
}
.card {
background-color: #f8f9fa;
border: 1px solid #ddd;
border-radius: 8px;
padding: 25px;
margin-top: 10px;
}
.card-icon {
font-size: 40px;
margin-bottom: 15px;
}
.card h3 {
font-size: 18px;
margin-bottom: 10px;
}
.card p {
font-size: 12px;
color: #7f8c8d;
margin-bottom: 20px;
}
.footer {
text-align: center;
margin-top: 40px;
color: #bdc3c7;
font-size: 12px;
}
.header {
display: flex;
align-items: center;
margin-bottom: 20px;
}
.header h2 {
margin-left: 15px;
}
.input-group {
display: flex;
align-items: center;
margin-bottom: 15px;
}
.input-group label {
width: 100px;
font-weight: bold;
}
.input-group input {
flex: 1;
padding: 8px 12px;
border: 1px solid #ddd;
border-radius: 4px;
margin: 0 10px;
}
.button-group {
display: flex;
gap: 10px;
margin: 20px 0;
}
.status {
margin: 15px 0;
}
.output-container {
background-color: #ffffff;
border: 1px solid #ddd;
border-radius: 4px;
padding: 15px;
max-height: 500px;
overflow-y: auto;
}
.output-container pre {
white-space: pre-wrap;
font-family: Consolas, monospace;
font-size: 12px;
}
.btn {
padding: 10px 15px;
border: none;
border-radius: 4px;
cursor: pointer;
font-weight: bold;
font-family: 'Microsoft YaHei', sans-serif;
}
.btn-primary {
background-color: #3498db;
color: white;
}
.btn-success {
background-color: #2ecc71;
color: white;
}
.btn-danger {
background-color: #e74c3c;
color: white;
}
.btn-secondary {
background-color: #95a5a6;
color: white;
}
.btn-purple {
background-color: #8e44ad;
color: white;
}
.btn-small {
padding: 5px 10px;
font-size: 12px;
}
.btn:disabled {
opacity: 0.6;
cursor: not-allowed;
}
.modal {
display: none;
position: fixed;
z-index: 1000;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
}
.modal-content {
background-color: white;
margin: 5% auto;
padding: 0;
border-radius: 8px;
width: 80%;
max-width: 600px;
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 15px 20px;
border-bottom: 1px solid #ddd;
}
.modal-header h3 {
margin: 0;
}
.close {
font-size: 24px;
font-weight: bold;
cursor: pointer;
}
.modal-body {
padding: 20px;
max-height: 400px;
overflow-y: auto;
}
.path-navigation {
display: flex;
align-items: center;
margin-bottom: 15px;
}
.path-navigation span {
margin-left: 15px;
font-family: monospace;
}
.file-list {
border: 1px solid #ddd;
border-radius: 4px;
max-height: 300px;
overflow-y: auto;
}
.file-item {
padding: 10px;
border-bottom: 1px solid #eee;
cursor: pointer;
}
.file-item:hover {
background-color: #f5f5f5;
}
.file-item.selected {
background-color: #e3f2fd;
}
.file-item:last-child {
border-bottom: none;
}
.modal-footer {
padding: 15px 20px;
border-top: 1px solid #ddd;
text-align: right;
}
/* 原有样式保持不变 */
.search-controls {
margin-bottom: 20px;
padding: 15px;
background-color: #f8f9fa;
border-radius: 4px;
}
.search-controls .input-group {
margin-bottom: 10px;
}
.search-results {
margin-top: 20px;
}
.search-results h4 {
margin-bottom: 10px;
color: #2c3e50;
}
.file-item {
padding: 10px;
border-bottom: 1px solid #eee;
cursor: pointer;
}
.file-item:hover {
background-color: #f5f5f5;
}
.file-item.selected {
background-color: #e3f2fd;
}
.file-item .file-path {
font-size: 12px;
color: #7f8c8d;
margin-top: 5px;
}
.file-item:last-child {
border-bottom: none;
}
/* 添加文件信息显示样式 */
.file-info {
margin: 15px 0;
padding: 12px;
background-color: #f8f9fa;
border-radius: 4px;
border: 1px solid #e9ecef;
}
.file-info-item {
margin: 5px 0;
font-size: 13px;
color: #6c757d;
display: flex;
justify-content: space-between;
}
.file-info-item span {
font-weight: 500;
color: #495057;
}
.file-status-valid {
color: #28a745 !important;
}
.file-status-invalid {
color: #dc3545 !important;
}
.file-status-unknown {
color: #6c757d !important;
}
/* 输入框组样式调整 */
.input-group {
display: flex;
align-items: center;
margin-bottom: 12px;
}
.input-group label {
width: 100px;
font-weight: bold;
margin-right: 10px;
flex-shrink: 0;
}
.input-group input {
flex: 1;
padding: 8px 12px;
border: 1px solid #ced4da;
border-radius: 4px;
margin: 0 10px;
font-family: 'Microsoft YaHei', sans-serif;
}
.input-group input:focus {
outline: none;
border-color: #80bdff;
box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
}
.input-group .btn-small {
padding: 8px 12px;
font-size: 12px;
white-space: nowrap;
}
/* 按钮样式调整 */
.btn {
padding: 10px 16px;
border: none;
border-radius: 4px;
cursor: pointer;
font-weight: bold;
font-family: 'Microsoft YaHei', sans-serif;
transition: all 0.2s;
}
.btn:hover {
opacity: 0.9;
transform: translateY(-1px);
}
.btn:active {
transform: translateY(0);
}
.btn-small {
padding: 6px 12px;
font-size: 12px;
}
/* 模态框样式调整 */
.modal-content {
max-width: 900px;
max-height: 80vh;
display: flex;
flex-direction: column;
}
.modal-body {
flex: 1;
overflow-y: auto;
max-height: 60vh;
padding: 20px;
}
/* 文件列表样式 */
.file-list {
max-height: 300px;
overflow-y: auto;
border: 1px solid #dee2e6;
border-radius: 4px;
margin-top: 10px;
}
.file-item {
padding: 10px 12px;
border-bottom: 1px solid #e9ecef;
cursor: pointer;
transition: background-color 0.15s;
}
.file-item:hover {
background-color: #f8f9fa;
}
.file-item.selected {
background-color: #e3f2fd;
border-left: 4px solid #2196f3;
}
.file-item:last-child {
border-bottom: none;
}
/* 搜索结果显示 */
.file-name {
font-weight: 600;
color: #2c3e50;
margin-bottom: 4px;
}
.file-details {
display: flex;
justify-content: space-between;
font-size: 12px;
color: #6c757d;
}
.file-path {
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.file-size {
margin-left: 10px;
color: #495057;
font-weight: 500;
}
/* 加载状态 */
.loading {
text-align: center;
padding: 30px;
color: #6c757d;
font-style: italic;
}
.no-results {
text-align: center;
padding: 30px;
color: #6c757d;
font-style: italic;
}
/* 响应式设计 */
@media (max-width: 768px) {
.input-group {
flex-direction: column;
align-items: stretch;
}
.input-group label {
width: auto;
margin-bottom: 5px;
}
.input-group input {
margin: 5px 0;
}
.button-group {
flex-direction: column;
gap: 8px;
}
.button-group .btn {
width: 100%;
}
}

59
web/static/js/script.js

@ -37,7 +37,13 @@ document.addEventListener('DOMContentLoaded', function() {
const filePathInput = document.getElementById('send-file-path');
filePathInput.addEventListener('input', updateFileInfo);
filePathInput.addEventListener('change', updateFileInfo);
// 确保停止所有服务按钮在所有页面都可用
const stopAllButtons = document.querySelectorAll('[onclick="stopAllServices()"]');
stopAllButtons.forEach(btn => {
btn.addEventListener('click', stopAllServices);
});
// 初始更新文件信息
updateFileInfo();
});
@ -518,4 +524,53 @@ function selectItem() {
document.getElementById('search-path').value = path;
closeModal();
}
}
}
// 停止所有服务
function stopAllServices() {
if (confirm('确定要停止所有服务吗?这将终止所有文件传输和连接进程。')) {
fetch('/stop/all', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
}
})
.then(response => response.json())
.then(data => {
if (data.status === 'success') {
alert('所有服务已停止');
// 更新按钮状态
document.getElementById('send-connect-btn').disabled = false;
document.getElementById('send-file-btn').disabled = true;
document.getElementById('send-stop-btn').disabled = true;
document.getElementById('receive-connect-btn').disabled = false;
document.getElementById('receive-stop-btn').disabled = true;
// 更新状态显示
document.getElementById('send-status').textContent = '已停止';
document.getElementById('receive-status').textContent = '已停止';
} else {
alert('停止服务失败: ' + data.message);
}
})
.catch(error => {
console.error('Error:', error);
alert('停止服务失败: ' + error);
});
}
}
// 监听服务停止事件
socket.on('service_stopped', function(data) {
// 更新按钮状态
document.getElementById('send-connect-btn').disabled = false;
document.getElementById('send-file-btn').disabled = true;
document.getElementById('send-stop-btn').disabled = true;
document.getElementById('receive-connect-btn').disabled = false;
document.getElementById('receive-stop-btn').disabled = true;
// 更新状态显示
document.getElementById('send-status').textContent = '已停止';
document.getElementById('receive-status').textContent = '已停止';
});

521
web/static/js/script.js.bak

@ -1,521 +0,0 @@
// 全局变量
let socket = null;
let currentModalType = '';
let selectedItem = null;
let currentPath = '';
let searchResults = [];
// 初始化Socket连接
document.addEventListener('DOMContentLoaded', function() {
socket = io();
// 监听发送输出
socket.on('send_output', function(data) {
const output = document.getElementById('send-output');
output.textContent += data.message;
output.scrollTop = output.scrollHeight;
});
// 监听发送状态
socket.on('send_status', function(data) {
document.getElementById('send-status').textContent = data.status;
});
// 监听接收输出
socket.on('receive_output', function(data) {
const output = document.getElementById('receive-output');
output.textContent += data.message;
output.scrollTop = output.scrollHeight;
});
// 监听接收状态
socket.on('receive_status', function(data) {
document.getElementById('receive-status').textContent = data.status;
});
// 监听文件路径输入变化
const filePathInput = document.getElementById('send-file-path');
filePathInput.addEventListener('input', updateFileInfo);
filePathInput.addEventListener('change', updateFileInfo);
// 初始更新文件信息
updateFileInfo();
});
// 更新文件信息
function updateFileInfo() {
const filePath = document.getElementById('send-file-path').value.trim();
const fileSizeElement = document.getElementById('file-size');
const fileMtimeElement = document.getElementById('file-mtime');
const fileStatusElement = document.getElementById('file-status');
if (!filePath) {
fileSizeElement.textContent = '-';
fileMtimeElement.textContent = '-';
fileStatusElement.textContent = '未选择';
fileStatusElement.className = 'file-status-unknown';
return;
}
// 检查文件是否存在
fetch('/check/file', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: JSON.stringify({ file_path: filePath })
})
.then(response => {
const contentType = response.headers.get('content-type');
if (!contentType || !contentType.includes('application/json')) {
return response.text().then(text => {
throw new Error('服务器返回了非JSON响应');
});
}
return response.json();
})
.then(data => {
if (data.status === 'success') {
fileSizeElement.textContent = formatFileSize(data.size);
fileMtimeElement.textContent = new Date(data.mtime * 1000).toLocaleString();
fileStatusElement.textContent = '文件有效';
fileStatusElement.className = 'file-status-valid';
} else {
fileSizeElement.textContent = '-';
fileMtimeElement.textContent = '-';
fileStatusElement.textContent = '文件不存在或无法访问';
fileStatusElement.className = 'file-status-invalid';
}
})
.catch(error => {
console.error('检查文件错误:', error);
fileSizeElement.textContent = '-';
fileMtimeElement.textContent = '-';
fileStatusElement.textContent = '检查失败';
fileStatusElement.className = 'file-status-invalid';
});
}
// 手动浏览文件
function browseFileManually() {
currentModalType = 'send';
currentPath = document.getElementById('send-file-path').value || '/';
const filePath = document.getElementById('send-file-path').value;
if (filePath) {
try {
const dirPath = require('path').dirname(filePath);
if (require('fs').existsSync(dirPath)) {
currentPath = dirPath;
}
} catch (e) {
// 忽略错误,使用默认路径
}
}
fetchFileList(currentPath, 'file');
document.getElementById('file-browser-modal').style.display = 'block';
}
// 显示指定界面
function showScreen(screenId) {
document.querySelectorAll('.screen').forEach(screen => {
screen.classList.remove('active');
});
document.getElementById(screenId).classList.add('active');
}
// 发送连接
function sendConnect() {
const key = document.getElementById('send-key').value;
fetch('/send/connect', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ key: key })
})
.then(response => response.json())
.then(data => {
if (data.status === 'success') {
document.getElementById('send-connect-btn').disabled = true;
document.getElementById('send-stop-btn').disabled = false;
// 3秒后启用发送文件按钮
setTimeout(() => {
document.getElementById('send-file-btn').disabled = false;
}, 3000);
} else {
alert('错误: ' + data.message);
}
})
.catch(error => {
console.error('Error:', error);
alert('连接失败: ' + error);
});
}
// 发送文件
function sendFile() {
const filePath = document.getElementById('send-file-path').value;
const key = document.getElementById('send-key').value;
const parts = key.split('|');
const psk = parts.length >= 3 ? parts[2].trim() : '';
fetch('/send/file', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
file_path: filePath,
psk: psk
})
})
.then(response => response.json())
.then(data => {
if (data.status !== 'success') {
alert('错误: ' + data.message);
}
})
.catch(error => {
console.error('Error:', error);
alert('发送失败: ' + error);
});
}
// 停止发送
function sendStop() {
fetch('/send/stop', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
}
})
.then(response => response.json())
.then(data => {
if (data.status === 'success') {
document.getElementById('send-connect-btn').disabled = false;
document.getElementById('send-file-btn').disabled = true;
document.getElementById('send-stop-btn').disabled = true;
}
})
.catch(error => {
console.error('Error:', error);
});
}
// 接收连接
function receiveConnect() {
const key = document.getElementById('receive-key').value;
const savePath = document.getElementById('receive-save-path').value;
fetch('/receive/connect', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
key: key,
save_path: savePath
})
})
.then(response => response.json())
.then(data => {
if (data.status === 'success') {
document.getElementById('receive-connect-btn').disabled = true;
document.getElementById('receive-stop-btn').disabled = false;
} else {
alert('错误: ' + data.message);
}
})
.catch(error => {
console.error('Error:', error);
alert('连接失败: ' + error);
});
}
// 停止接收
function receiveStop() {
fetch('/receive/stop', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
}
})
.then(response => response.json())
.then(data => {
if (data.status === 'success') {
document.getElementById('receive-connect-btn').disabled = false;
document.getElementById('receive-stop-btn').disabled = true;
}
})
.catch(error => {
console.error('Error:', error);
});
}
// 打开搜索模态框
function openSearchModal() {
document.getElementById('file-search-modal').style.display = 'block';
document.getElementById('search-path').value = '/';
document.getElementById('search-keyword').value = '*';
document.getElementById('search-results-list').innerHTML = '';
document.getElementById('result-count').textContent = '0';
document.getElementById('search-select-btn').disabled = true;
selectedItem = null;
searchResults = [];
}
// 关闭搜索模态框
function closeSearchModal() {
document.getElementById('file-search-modal').style.display = 'none';
}
// 浏览搜索路径
function browseSearchPath() {
currentModalType = 'search_path';
currentPath = document.getElementById('search-path').value || '/';
fetchFileList(currentPath, 'folder');
document.getElementById('file-browser-modal').style.display = 'block';
}
// 开始搜索
function startSearch() {
const searchPath = document.getElementById('search-path').value;
const keyword = document.getElementById('search-keyword').value;
const maxResults = document.getElementById('max-results').value;
if (!keyword) {
alert('请输入搜索关键词');
return;
}
// 显示加载状态
const resultsList = document.getElementById('search-results-list');
resultsList.innerHTML = '<div class="loading">搜索中...</div>';
fetch('/search/files', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: JSON.stringify({
path: searchPath,
pattern: keyword,
max_results: parseInt(maxResults)
})
})
.then(response => {
const contentType = response.headers.get('content-type');
if (!contentType || !contentType.includes('application/json')) {
return response.text().then(text => {
throw new Error(`服务器返回了非JSON响应: ${text.substring(0, 100)}...`);
});
}
return response.json();
})
.then(data => {
if (data.status === 'success') {
searchResults = data.results;
displaySearchResults(data.results);
document.getElementById('result-count').textContent = data.count;
} else {
alert('搜索失败: ' + data.message);
resultsList.innerHTML = '<div class="no-results">搜索失败</div>';
}
})
.catch(error => {
console.error('搜索错误:', error);
alert('搜索失败: ' + error.message);
resultsList.innerHTML = '<div class="no-results">搜索错误</div>';
});
}
// 显示搜索结果
function displaySearchResults(results) {
const resultsList = document.getElementById('search-results-list');
resultsList.innerHTML = '';
if (results.length === 0) {
resultsList.innerHTML = '<div class="no-results">未找到匹配的文件</div>';
return;
}
results.forEach(result => {
const div = document.createElement('div');
div.className = 'file-item';
const fileSize = formatFileSize(result.size || 0);
div.innerHTML = `
<div class="file-name">📄 ${result.name}</div>
<div class="file-details">
<span class="file-path">${result.relative_path}</span>
<span class="file-size">${fileSize}</span>
</div>
`;
div.dataset.path = result.path;
div.addEventListener('click', function() {
// 取消之前的选择
document.querySelectorAll('.file-item.selected').forEach(el => {
el.classList.remove('selected');
});
// 选择当前项
this.classList.add('selected');
selectedItem = this;
document.getElementById('search-select-btn').disabled = false;
});
resultsList.appendChild(div);
});
}
// 格式化文件大小
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 selectSearchResult() {
if (!selectedItem) {
alert('请先选择一个文件');
return;
}
const filePath = selectedItem.dataset.path;
document.getElementById('send-file-path').value = filePath;
closeSearchModal();
updateFileInfo(); // 更新文件信息显示
}
// 获取文件列表
function fetchFileList(path, type) {
fetch('/browse', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: JSON.stringify({
path: path,
type: type
})
})
.then(response => {
const contentType = response.headers.get('content-type');
if (!contentType || !contentType.includes('application/json')) {
return response.text().then(text => {
throw new Error(`服务器返回了非JSON响应: ${text.substring(0, 100)}...`);
});
}
return response.json();
})
.then(data => {
if (data.status === 'success') {
currentPath = data.current_path;
document.getElementById('current-path').textContent = currentPath;
const fileList = document.getElementById('file-list');
fileList.innerHTML = '';
data.items.forEach(item => {
const div = document.createElement('div');
div.className = 'file-item';
div.innerHTML = item.type === 'folder' ?
`📁 ${item.name}` : `📄 ${item.name}`;
div.dataset.path = item.path;
div.dataset.type = item.type;
div.addEventListener('click', function() {
// 取消之前的选择
document.querySelectorAll('.file-item.selected').forEach(el => {
el.classList.remove('selected');
});
// 选择当前项
this.classList.add('selected');
selectedItem = this;
// 如果是文件夹,双击进入
this.addEventListener('dblclick', function() {
if (item.type === 'folder') {
if (currentModalType === 'search_path') {
document.getElementById('search-path').value = item.path;
closeModal();
} else {
fetchFileList(item.path, type);
}
}
});
});
fileList.appendChild(div);
});
} else {
alert('错误: ' + data.message);
}
})
.catch(error => {
console.error('Error:', error);
alert('获取文件列表失败: ' + error.message);
});
}
// 导航到上级目录
function navigateUp() {
const parentPath = currentPath.split('/').slice(0, -1).join('/');
if (parentPath === '') {
currentPath = '/';
} else {
currentPath = parentPath;
}
const type = currentModalType === 'send' ? 'file' : 'folder';
fetchFileList(currentPath, type);
}
// 关闭模态框
function closeModal() {
document.getElementById('file-browser-modal').style.display = 'none';
selectedItem = null;
}
// 选择文件或文件夹
function selectItem() {
if (!selectedItem) {
alert('请先选择一个项目');
return;
}
const path = selectedItem.dataset.path;
const type = selectedItem.dataset.type;
if (currentModalType === 'send') {
if (type === 'file') {
document.getElementById('send-file-path').value = path;
closeModal();
updateFileInfo(); // 更新文件信息显示
} else {
alert('请选择一个文件');
}
} else if (currentModalType === 'folder') {
document.getElementById('receive-save-path').value = path;
closeModal();
} else if (currentModalType === 'search_path') {
document.getElementById('search-path').value = path;
closeModal();
}
}

2
web/templates/index.html

@ -73,6 +73,7 @@
<button class="btn btn-success" id="send-connect-btn" onclick="sendConnect()">🔄 连接</button>
<button class="btn btn-danger" id="send-file-btn" onclick="sendFile()" disabled>🚀 发送文件</button>
<button class="btn btn-secondary" id="send-stop-btn" onclick="sendStop()" disabled>⏹️ 停止</button>
<button class="btn btn-warning" id="stop-all-btn" onclick="stopAllServices()">🛑 停止所有服务</button>
</div>
<div class="status">
@ -111,6 +112,7 @@
<div class="button-group">
<button class="btn btn-success" id="receive-connect-btn" onclick="receiveConnect()">🔄 连接</button>
<button class="btn btn-danger" id="receive-stop-btn" onclick="receiveStop()" disabled>⏹️ 停止</button>
<button class="btn btn-warning" id="stop-all-btn-receive" onclick="stopAllServices()">🛑 停止所有服务</button>
</div>
<div class="status">

192
web/templates/index.html.bak

@ -1,192 +0,0 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>蜗牛创造的高速文件传输</title>
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
<script src="{{ url_for('static', filename='js/socket.io.min.js') }}"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.7.2/socket.io.min.js"></script>
</head>
<body>
<div class="container">
<!-- 欢迎界面 -->
<div id="welcome-screen" class="screen active">
<div class="logo-area">
<h1>🐌 欢迎来到蜗牛创造的高速世界</h1>
<p>安全、高速的文件传输解决方案</p>
</div>
<div class="card-container">
<div class="card">
<div class="card-icon">📤</div>
<h3>发送文件</h3>
<p>快速安全地发送文件</p>
<button class="btn btn-primary" onclick="showScreen('send-screen')">开始发送</button>
</div>
<div class="card">
<div class="card-icon">📥</div>
<h3>接收文件</h3>
<p>安全可靠地接收文件</p>
<button class="btn btn-success" onclick="showScreen('receive-screen')">开始接收</button>
</div>
</div>
<div class="footer">
<p>© 2024 蜗牛创造 - 让文件传输更简单</p>
</div>
</div>
<!-- 发送界面 -->
<div id="send-screen" class="screen">
<div class="header">
<button class="btn btn-secondary" onclick="showScreen('welcome-screen')">← 返回</button>
<h2>📤 文件发送</h2>
</div>
<div class="card">
<div class="input-group">
<label>🔑 密钥:</label>
<input type="text" id="send-key" placeholder="token|sk|psk|sern">
</div>
<div class="input-group">
<label>🔍 文件搜索:</label>
<input type="text" id="search-pattern" placeholder="输入文件名关键词">
<button class="btn btn-purple" onclick="openSearchModal()">搜索文件</button>
</div>
<div class="input-group">
<label>📄 文件路径:</label>
<input type="text" value="/mnt/wwn-0x5000c500a34676a5-part1/docker/kodbox/data/files/下载/" id="send-file-path" placeholder="手动输入文件路径或从搜索结果选择">
<button class="btn btn-small" onclick="browseFileManually()">选择要发送的文件</button>
</div>
<div class="file-info" id="file-info">
<div class="file-info-item">文件大小: <span id="file-size">-</span></div>
<div class="file-info-item">修改时间: <span id="file-mtime">-</span></div>
<div class="file-info-item">文件状态: <span id="file-status">未选择</span></div>
</div>
<div class="button-group">
<button class="btn btn-success" id="send-connect-btn" onclick="sendConnect()">🔄 连接</button>
<button class="btn btn-danger" id="send-file-btn" onclick="sendFile()" disabled>🚀 发送文件</button>
<button class="btn btn-secondary" id="send-stop-btn" onclick="sendStop()" disabled>⏹️ 停止</button>
</div>
<div class="status">
<p>📊 状态: <span id="send-status">等待连接</span></p>
</div>
</div>
<div class="card">
<h3>📋 发送日志:</h3>
<div class="output-container">
<pre id="send-output">等待输入密钥并选择文件...
密钥格式应为: token|sk|psk|sern</pre>
</div>
</div>
</div>
<!-- 接收界面 -->
<div id="receive-screen" class="screen">
<div class="header">
<button class="btn btn-secondary" onclick="showScreen('welcome-screen')">← 返回</button>
<h2>📥 文件接收</h2>
</div>
<div class="card">
<div class="input-group">
<label>🔑 密钥:</label>
<input type="text" id="receive-key" placeholder="token|sk|psk|sern">
</div>
<div class="input-group">
<label>💾 保存路径:</label>
<input type="text" id="receive-save-path" style="background-color: #eee;color: #333;" readonly value="文件保持在:网络挂载/本地存储/下载/">
<!-- <button class="btn btn-purple" onclick="browseFolder()">浏览文件夹</button> -->
</div>
<div class="button-group">
<button class="btn btn-success" id="receive-connect-btn" onclick="receiveConnect()">🔄 连接</button>
<button class="btn btn-danger" id="receive-stop-btn" onclick="receiveStop()" disabled>⏹️ 停止</button>
</div>
<div class="status">
<p>📊 状态: <span id="receive-status">等待连接</span></p>
</div>
</div>
<div class="card">
<h3>📋 接收日志:</h3>
<div class="output-container">
<pre id="receive-output">等待输入密钥并连接...
密钥格式应为: token|sk|psk|sern
文件将保存到: 网络挂载/本地存储/下载/</pre>
</div>
</div>
</div>
<!-- 文件浏览模态框 -->
<div id="file-browser-modal" class="modal" style="z-index:2000">
<div class="modal-content">
<div class="modal-header">
<h3 id="modal-title">选择文件</h3>
<span class="close" onclick="closeModal()">&times;</span>
</div>
<div class="modal-body">
<div class="path-navigation">
<button class="btn btn-small" onclick="navigateUp()">↑ 上级目录</button>
<span id="current-path"></span>
</div>
<div id="file-list" class="file-list"></div>
</div>
<div class="modal-footer">
<button class="btn btn-secondary" onclick="closeModal()">取消</button>
<button class="btn btn-primary" id="modal-select-btn" onclick="selectItem()">选择</button>
</div>
</div>
</div>
<!-- 文件搜索模态框 -->
<div id="file-search-modal" class="modal">
<div class="modal-content">
<div class="modal-header">
<h3 id="search-modal-title">搜索文件</h3>
<span class="close" onclick="closeSearchModal()">&times;</span>
</div>
<div class="modal-body">
<div class="search-controls">
<div class="input-group">
<label>搜索路径:</label>
<input type="text" id="search-path">
<button class="btn btn-small" onclick="browseSearchPath()">选择搜索目录</button>
</div>
<div class="input-group">
<label>文件名包含:</label>
<input type="text" id="search-keyword" placeholder="默认*则展示该目录下所有文件/输入文件名关键词">
</div>
<div class="input-group">
<label>最大结果数:</label>
<input type="number" id="max-results" value="50" min="1" max="500">
</div>
<button class="btn btn-primary" onclick="startSearch()">开始搜索</button>
</div>
<div class="search-results">
<h4>搜索结果 (<span id="result-count">0</span>):</h4>
<div id="search-results-list" class="file-list"></div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-secondary" onclick="closeSearchModal()">取消</button>
<button class="btn btn-primary" id="search-select-btn" onclick="selectSearchResult()" disabled>选择文件</button>
</div>
</div>
</div>
</div>
<script src="{{ url_for('static', filename='js/socket.io.min.js') }}"></script>
<script src="{{ url_for('static', filename='js/script.js') }}"></script>
</body>
</html>
Loading…
Cancel
Save