From 0cc6c770802dcbd4f766d27f4adb9388f2a250e1 Mon Sep 17 00:00:00 2001 From: wangqifan Date: Thu, 10 Jul 2025 10:02:51 +0800 Subject: [PATCH] 0709 --- backend/app.py | 5 +- backend/database.py | 3 + backend/init_db.py | 3 +- backend/models.py | 5 +- backend/requirements.txt | 3 +- backend/reset_db.py | 37 +++ backend/routes/auth.py | 2 +- backend/routes/execute.py | 2 +- backend/routes/scripts.py | 2 +- backend/routes/servers.py | 102 +++++--- backend/routes/users.py | 2 +- frontend/src/layout/index.vue | 93 ++++--- frontend/src/main.js | 20 +- frontend/src/router/index.js | 9 +- frontend/src/store/auth.js | 29 ++- frontend/src/views/execute/index.vue | 336 +++++++++++++------------ frontend/src/views/login/index.vue | 144 ++++++----- frontend/src/views/scripts/index.vue | 232 ++++++++--------- frontend/src/views/servers/index.vue | 199 +++++++++------ frontend/src/views/users/index.vue | 363 +++++++++++++++++---------- 20 files changed, 929 insertions(+), 662 deletions(-) create mode 100644 backend/database.py create mode 100644 backend/reset_db.py diff --git a/backend/app.py b/backend/app.py index 435262a..5eea937 100644 --- a/backend/app.py +++ b/backend/app.py @@ -1,12 +1,11 @@ from flask import Flask from flask_cors import CORS -from flask_sqlalchemy import SQLAlchemy from flask_jwt_extended import JWTManager from datetime import timedelta import os +from database import db # 初始化扩展 -db = SQLAlchemy() jwt = JWTManager() def create_app(): @@ -16,7 +15,7 @@ def create_app(): app.config['SECRET_KEY'] = os.getenv('SECRET_KEY', 'dev-secret-key') app.config['SQLALCHEMY_DATABASE_URI'] = os.getenv( 'DATABASE_URL', - 'mysql+pymysql://root:password@localhost:3306/server_monitor' + 'mysql+pymysql://wqf_server_monitor:iwaA7WAirswiwbRb@47.96.181.224:3306/wqf_server_monitor' ) app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False app.config['JWT_SECRET_KEY'] = os.getenv('JWT_SECRET_KEY', 'jwt-secret-key') diff --git a/backend/database.py b/backend/database.py new file mode 100644 index 0000000..97a5faf --- /dev/null +++ b/backend/database.py @@ -0,0 +1,3 @@ +from flask_sqlalchemy import SQLAlchemy + +db = SQLAlchemy() \ No newline at end of file diff --git a/backend/init_db.py b/backend/init_db.py index b6fed8d..68a074f 100644 --- a/backend/init_db.py +++ b/backend/init_db.py @@ -1,4 +1,5 @@ -from app import create_app, db +from app import create_app +from database import db from models import User def init_database(): diff --git a/backend/models.py b/backend/models.py index b8af195..a94bf5a 100644 --- a/backend/models.py +++ b/backend/models.py @@ -1,4 +1,4 @@ -from app import db +from database import db from datetime import datetime from werkzeug.security import generate_password_hash, check_password_hash import json @@ -8,7 +8,7 @@ class User(db.Model): id = db.Column(db.Integer, primary_key=True) username = db.Column(db.String(80), unique=True, nullable=False) - password_hash = db.Column(db.String(128), nullable=False) + password_hash = db.Column(db.String(255), nullable=False) real_name = db.Column(db.String(120), nullable=False) email = db.Column(db.String(120)) role = db.Column(db.String(20), nullable=False, default='viewer') @@ -36,6 +36,7 @@ class Server(db.Model): ip = db.Column(db.String(15), nullable=False) port = db.Column(db.Integer, default=22) username = db.Column(db.String(50), default='root') + password = db.Column(db.String(255)) # SSH密码 status = db.Column(db.String(20), default='offline') description = db.Column(db.Text) created_at = db.Column(db.DateTime, default=datetime.utcnow) diff --git a/backend/requirements.txt b/backend/requirements.txt index 194da95..642127e 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -1,5 +1,6 @@ Flask==2.3.0 -Flask-SQLAlchemy==3.0.0 +Flask-SQLAlchemy==3.0.5 +SQLAlchemy==2.0.23 Flask-CORS==4.0.0 Flask-JWT-Extended==4.5.1 PyMySQL==1.1.0 diff --git a/backend/reset_db.py b/backend/reset_db.py new file mode 100644 index 0000000..aeb7246 --- /dev/null +++ b/backend/reset_db.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 +"""重置数据库脚本""" + +from app import create_app +from database import db +from models import User, Server, Script, ExecuteHistory, ExecuteResult + +def reset_database(): + """重置数据库""" + app = create_app() + + with app.app_context(): + # 删除所有表 + db.drop_all() + print("已删除所有表") + + # 创建所有表 + db.create_all() + print("已创建所有表") + + # 创建默认管理员用户 + admin_user = User( + username='admin', + real_name='系统管理员', + email='admin@example.com', + role='admin' + ) + admin_user.set_password('admin123') + admin_user.set_permissions(['all']) + + db.session.add(admin_user) + db.session.commit() + print("已创建默认管理员用户 (admin/admin123)") + +if __name__ == '__main__': + reset_database() + print("数据库重置完成!") \ No newline at end of file diff --git a/backend/routes/auth.py b/backend/routes/auth.py index e688e6e..1928fb6 100644 --- a/backend/routes/auth.py +++ b/backend/routes/auth.py @@ -1,7 +1,7 @@ from flask import Blueprint, request, jsonify from flask_jwt_extended import create_access_token, jwt_required, get_jwt_identity from models import User -from app import db +from database import db auth_bp = Blueprint('auth', __name__) diff --git a/backend/routes/execute.py b/backend/routes/execute.py index d187d46..413d233 100644 --- a/backend/routes/execute.py +++ b/backend/routes/execute.py @@ -1,7 +1,7 @@ from flask import Blueprint, request, jsonify from flask_jwt_extended import jwt_required, get_jwt_identity from models import Script, Server, ExecuteHistory, ExecuteResult -from app import db +from database import db import paramiko import time import threading diff --git a/backend/routes/scripts.py b/backend/routes/scripts.py index 242e62d..9cd4402 100644 --- a/backend/routes/scripts.py +++ b/backend/routes/scripts.py @@ -1,7 +1,7 @@ from flask import Blueprint, request, jsonify from flask_jwt_extended import jwt_required, get_jwt_identity from models import Script -from app import db +from database import db scripts_bp = Blueprint('scripts', __name__) diff --git a/backend/routes/servers.py b/backend/routes/servers.py index 7a7ffc0..1b43834 100644 --- a/backend/routes/servers.py +++ b/backend/routes/servers.py @@ -1,18 +1,18 @@ from flask import Blueprint, request, jsonify from flask_jwt_extended import jwt_required, get_jwt_identity from models import Server -from app import db +from database import db import paramiko import socket servers_bp = Blueprint('servers', __name__) -def check_server_status(ip, port, username): +def check_server_status(ip, port, username, password=None): """检查服务器连接状态""" try: ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - ssh.connect(ip, port=port, username=username, timeout=5) + ssh.connect(ip, port=port, username=username, password=password, timeout=5) ssh.close() return 'online' except: @@ -26,7 +26,7 @@ def get_servers(): for server in servers: # 实时检查服务器状态 - server.status = check_server_status(server.ip, server.port, server.username) + server.status = check_server_status(server.ip, server.port, server.username, server.password) db.session.commit() result.append({ @@ -37,50 +37,60 @@ def get_servers(): 'username': server.username, 'status': server.status, 'description': server.description, - 'createdAt': server.created_at.strftime('%Y-%m-%d %H:%M:%S') + 'created_at': server.created_at.strftime('%Y-%m-%d %H:%M:%S') }) - return jsonify(result) + return jsonify({'data': result}) @servers_bp.route('', methods=['POST']) @jwt_required() def create_server(): - data = request.get_json() - - server = Server( - name=data['name'], - ip=data['ip'], - port=data.get('port', 22), - username=data.get('username', 'root'), - description=data.get('description', '') - ) - - # 检查服务器连接状态 - server.status = check_server_status(server.ip, server.port, server.username) - - db.session.add(server) - db.session.commit() - - return jsonify({'message': '服务器添加成功', 'id': server.id}), 201 + try: + data = request.get_json() + + server = Server( + name=data['name'], + ip=data['ip'], + port=data.get('port', 22), + username=data.get('username', 'root'), + password=data.get('password'), + description=data.get('description', '') + ) + + # 检查服务器连接状态 + server.status = check_server_status(server.ip, server.port, server.username, server.password) + + db.session.add(server) + db.session.commit() + + return jsonify({'message': '服务器添加成功', 'id': server.id}), 201 + except Exception as e: + db.session.rollback() + return jsonify({'error': f'添加服务器失败: {str(e)}'}), 400 @servers_bp.route('/', methods=['PUT']) @jwt_required() def update_server(server_id): - server = Server.query.get_or_404(server_id) - data = request.get_json() - - server.name = data.get('name', server.name) - server.ip = data.get('ip', server.ip) - server.port = data.get('port', server.port) - server.username = data.get('username', server.username) - server.description = data.get('description', server.description) - - # 重新检查服务器状态 - server.status = check_server_status(server.ip, server.port, server.username) - - db.session.commit() - - return jsonify({'message': '服务器更新成功'}) + try: + server = Server.query.get_or_404(server_id) + data = request.get_json() + + server.name = data.get('name', server.name) + server.ip = data.get('ip', server.ip) + server.port = data.get('port', server.port) + server.username = data.get('username', server.username) + server.password = data.get('password', server.password) + server.description = data.get('description', server.description) + + # 重新检查服务器状态 + server.status = check_server_status(server.ip, server.port, server.username, server.password) + + db.session.commit() + + return jsonify({'message': '服务器更新成功'}) + except Exception as e: + db.session.rollback() + return jsonify({'error': f'更新服务器失败: {str(e)}'}), 400 @servers_bp.route('/', methods=['DELETE']) @jwt_required() @@ -94,12 +104,12 @@ def delete_server(server_id): @servers_bp.route('//test', methods=['POST']) @jwt_required() def test_server_connection(server_id): - server = Server.query.get_or_404(server_id) - try: + server = Server.query.get_or_404(server_id) + ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - ssh.connect(server.ip, port=server.port, username=server.username, timeout=10) + ssh.connect(server.ip, port=server.port, username=server.username, password=server.password, timeout=10) # 执行简单命令测试 stdin, stdout, stderr = ssh.exec_command('echo "connection test"') @@ -107,12 +117,22 @@ def test_server_connection(server_id): ssh.close() + # 更新服务器状态 + server.status = 'online' + db.session.commit() + return jsonify({ 'status': 'success', 'message': '连接测试成功', 'output': output }) except Exception as e: + # 更新服务器状态为离线 + server = Server.query.get(server_id) + if server: + server.status = 'offline' + db.session.commit() + return jsonify({ 'status': 'error', 'message': f'连接测试失败: {str(e)}' diff --git a/backend/routes/users.py b/backend/routes/users.py index 71201bb..135d61f 100644 --- a/backend/routes/users.py +++ b/backend/routes/users.py @@ -1,7 +1,7 @@ from flask import Blueprint, request, jsonify from flask_jwt_extended import jwt_required, get_jwt_identity from models import User -from app import db +from database import db users_bp = Blueprint('users', __name__) diff --git a/frontend/src/layout/index.vue b/frontend/src/layout/index.vue index 8e13b91..51ef104 100644 --- a/frontend/src/layout/index.vue +++ b/frontend/src/layout/index.vue @@ -11,23 +11,23 @@ @click="handleMenuClick" > - + 📊 控制台 - + 🖥️ 服务器管理 - + 📝 脚本管理 - + 批量执行 - + 👥 用户管理 @@ -41,28 +41,17 @@ @click="collapsed = !collapsed" class="mr-4" > - - + {{ collapsed ? '▶' : '◀' }} - - {{ currentTitle }} - +
{{ currentTitle }}
- - - - {{ user.username }} +
+ 👤 {{ user?.username || '管理员' }} + + 退出登录 - - +
@@ -76,16 +65,6 @@ import { ref, computed } from 'vue' import { useRouter, useRoute } from 'vue-router' import { useAuthStore } from '@/store/auth' -import { - DashboardOutlined, - ServerOutlined, - FileTextOutlined, - PlayCircleOutlined, - UserOutlined, - MenuUnfoldOutlined, - MenuFoldOutlined, - LogoutOutlined -} from '@ant-design/icons-vue' const router = useRouter() const route = useRoute() @@ -100,10 +79,46 @@ const handleMenuClick = ({ key }) => { router.push(`/${key}`) } -const handleUserMenu = ({ key }) => { - if (key === 'logout') { - authStore.logout() - router.push('/login') - } +const handleLogout = () => { + authStore.logout() + router.push('/login') } - \ No newline at end of file + + + \ No newline at end of file diff --git a/frontend/src/main.js b/frontend/src/main.js index ebf8745..e6e1547 100644 --- a/frontend/src/main.js +++ b/frontend/src/main.js @@ -6,8 +6,26 @@ import './style.css' import App from './App.vue' import router from './router' +// 基本的全局错误处理 +window.addEventListener('error', (event) => { + console.error('JavaScript错误:', event.error) +}) + +window.addEventListener('unhandledrejection', (event) => { + console.error('Promise拒绝:', event.reason) + event.preventDefault() +}) + const app = createApp(App) -app.use(createPinia()) + +// Vue应用错误处理 +app.config.errorHandler = (err, instance, info) => { + console.error('Vue错误:', err, info) +} + +const pinia = createPinia() +app.use(pinia) app.use(router) app.use(Antd) + app.mount('#app') \ No newline at end of file diff --git a/frontend/src/router/index.js b/frontend/src/router/index.js index 4020ce3..484fe26 100644 --- a/frontend/src/router/index.js +++ b/frontend/src/router/index.js @@ -5,7 +5,8 @@ const routes = [ { path: '/login', name: 'Login', - component: () => import('@/views/login/index.vue') + component: () => import('@/views/login/index.vue'), + meta: { title: '用户登录' } }, { path: '/', @@ -46,7 +47,9 @@ const routes = [ } ] -export default createRouter({ +const router = createRouter({ history: createWebHistory(), routes -}) \ No newline at end of file +}) + +export default router \ No newline at end of file diff --git a/frontend/src/store/auth.js b/frontend/src/store/auth.js index 6a30113..5da4677 100644 --- a/frontend/src/store/auth.js +++ b/frontend/src/store/auth.js @@ -1,9 +1,24 @@ import { defineStore } from 'pinia' -import { ref } from 'vue' +import { ref, computed } from 'vue' export const useAuthStore = defineStore('auth', () => { const token = ref(localStorage.getItem('token') || '') - const user = ref(JSON.parse(localStorage.getItem('user') || '{}')) + const user = ref(null) + + // 初始化用户信息 + try { + const storedUser = localStorage.getItem('user') + if (storedUser) { + user.value = JSON.parse(storedUser) + } + } catch (error) { + console.error('解析用户信息失败:', error) + user.value = null + } + + const isLoggedIn = computed(() => { + return !!token.value && !!user.value + }) const login = (userData, userToken) => { user.value = userData @@ -13,21 +28,17 @@ export const useAuthStore = defineStore('auth', () => { } const logout = () => { - user.value = {} + user.value = null token.value = '' localStorage.removeItem('user') localStorage.removeItem('token') } - const isLoggedIn = () => { - return !!token.value - } - return { user, token, + isLoggedIn, login, - logout, - isLoggedIn + logout } }) \ No newline at end of file diff --git a/frontend/src/views/execute/index.vue b/frontend/src/views/execute/index.vue index ea66d43..f7d5075 100644 --- a/frontend/src/views/execute/index.vue +++ b/frontend/src/views/execute/index.vue @@ -1,124 +1,122 @@ \ No newline at end of file diff --git a/frontend/src/views/login/index.vue b/frontend/src/views/login/index.vue index 28228dd..d11270d 100644 --- a/frontend/src/views/login/index.vue +++ b/frontend/src/views/login/index.vue @@ -1,53 +1,59 @@ @@ -55,35 +61,43 @@