Compare commits
8 Commits
Author | SHA1 | Date |
---|---|---|
Kenneth | 3a71eb6df3 | 16 hours ago |
Kenneth | 125bf5022b | 6 days ago |
Kenneth | 748a43da4d | 6 days ago |
Kenneth | 048a9d6636 | 6 days ago |
Kenneth | 16f89ac361 | 4 weeks ago |
Kenneth | cd209ff913 | 4 weeks ago |
Kenneth | c496465c4a | 4 weeks ago |
Kenneth | 86ab82ec9e | 4 weeks ago |
@ -1,2 +1,6 @@
|
|||||||
# battlecar
|
# battlecar
|
||||||
|
|
||||||
|
python app.py
|
||||||
|
|
||||||
|
登陆 127.0.0.1:8000/login
|
||||||
|
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
app.py
|
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,599 @@
|
|||||||
|
import os
|
||||||
|
import uuid
|
||||||
|
import logging
|
||||||
|
from datetime import datetime
|
||||||
|
from flask import Flask, current_app, session, redirect, url_for, request, flash, render_template, jsonify, send_from_directory
|
||||||
|
from flask_sqlalchemy import SQLAlchemy
|
||||||
|
from flask_mysqldb import MySQL
|
||||||
|
import MySQLdb.cursors
|
||||||
|
from werkzeug.utils import secure_filename
|
||||||
|
from flask_login import LoginManager, UserMixin, login_user, login_required, current_user
|
||||||
|
from wtforms import Form, StringField
|
||||||
|
from wtforms.validators import Length, EqualTo, ValidationError
|
||||||
|
import pymysql
|
||||||
|
|
||||||
|
from cryptography.fernet import Fernet,InvalidToken
|
||||||
|
|
||||||
|
# 生成密钥并保存到文件(仅在第一次运行时生成)
|
||||||
|
def generate_key():
|
||||||
|
key = Fernet.generate_key()
|
||||||
|
with open("secret.key", "wb") as key_file:
|
||||||
|
key_file.write(key)
|
||||||
|
|
||||||
|
# 加载密钥
|
||||||
|
def load_key():
|
||||||
|
return open("secret.key", "rb").read()
|
||||||
|
|
||||||
|
# 加密函数
|
||||||
|
def encrypt_message(message):
|
||||||
|
key = load_key()
|
||||||
|
f = Fernet(key)
|
||||||
|
encrypted_message = f.encrypt(message.encode())
|
||||||
|
return encrypted_message
|
||||||
|
|
||||||
|
# 解密函数
|
||||||
|
def decrypt_message(encrypted_message):
|
||||||
|
key = load_key()
|
||||||
|
f = Fernet(key)
|
||||||
|
try:
|
||||||
|
print(f"Encrypted message: {encrypted_message}")
|
||||||
|
decrypted_message = f.decrypt(encrypted_message).decode()
|
||||||
|
return decrypted_message
|
||||||
|
except (InvalidToken, ValueError) as e:
|
||||||
|
logger.error(f"Failed to decrypt message: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
conn = pymysql.connect(host="localhost", port=3306,
|
||||||
|
user="root", password="lin556688",
|
||||||
|
database="mydatabase", charset="utf8mb4")
|
||||||
|
|
||||||
|
# 初始化 Flask 应用
|
||||||
|
app = Flask(__name__)
|
||||||
|
app.config['SECRET_KEY'] = 'your_secret_key'
|
||||||
|
app.secret_key = os.urandom(24) # 生成更安全的会话密钥
|
||||||
|
app.config['UPLOAD_FOLDER'] = 'uploads/' # 设置上传文件存储目录
|
||||||
|
os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True) # 确保目录存在
|
||||||
|
|
||||||
|
# 配置日志
|
||||||
|
logging.basicConfig(filename='app.log', level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
||||||
|
|
||||||
|
# MySQL 配置
|
||||||
|
app.config['MYSQL_HOST'] = 'localhost'
|
||||||
|
app.config['MYSQL_USER'] = 'root' # 替换为你的 MySQL 用户名
|
||||||
|
app.config['MYSQL_PASSWORD'] = 'lin556688' # 替换为你的 MySQL 密码
|
||||||
|
app.config['MYSQL_DB'] = 'mydatabase'
|
||||||
|
app.config['MYSQL_CURSORCLASS'] = 'DictCursor'
|
||||||
|
|
||||||
|
mysql = MySQL(app)
|
||||||
|
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif'}
|
||||||
|
|
||||||
|
# 配置日志记录器
|
||||||
|
logging.basicConfig(filename='app.log', level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
# 自定义日志处理器类
|
||||||
|
class DatabaseLogHandler(logging.Handler):
|
||||||
|
def emit(self, record):
|
||||||
|
log_entry = self.format(record)
|
||||||
|
# 去掉时间戳中的毫秒部分
|
||||||
|
timestamp = datetime.strptime(record.asctime, '%Y-%m-%d %H:%M:%S,%f').strftime('%Y-%m-%d %H:%M:%S')
|
||||||
|
cursor = mysql.connection.cursor()
|
||||||
|
cursor.execute(
|
||||||
|
"INSERT INTO logs (timestamp, level, message) VALUES (%s, %s, %s)",
|
||||||
|
(timestamp, record.levelname, log_entry)
|
||||||
|
)
|
||||||
|
mysql.connection.commit()
|
||||||
|
cursor.close()
|
||||||
|
|
||||||
|
# 添加自定义日志处理器
|
||||||
|
db_handler = DatabaseLogHandler()
|
||||||
|
db_handler.setLevel(logging.INFO)
|
||||||
|
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
|
||||||
|
db_handler.setFormatter(formatter)
|
||||||
|
logger.addHandler(db_handler)
|
||||||
|
|
||||||
|
def allowed_file(filename):
|
||||||
|
return '.' in filename and \
|
||||||
|
filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
|
||||||
|
|
||||||
|
# 初始化 Flask-Login
|
||||||
|
login_manager = LoginManager()
|
||||||
|
login_manager.init_app(app)
|
||||||
|
login_manager.login_view = 'login'
|
||||||
|
|
||||||
|
class User(UserMixin):
|
||||||
|
def __init__(self, id, username, role):
|
||||||
|
self.id = id
|
||||||
|
self.username = username
|
||||||
|
self.role = role
|
||||||
|
|
||||||
|
@login_manager.user_loader
|
||||||
|
def load_user(user_id):
|
||||||
|
cursor = mysql.connection.cursor()
|
||||||
|
cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,))
|
||||||
|
user_data = cursor.fetchone()
|
||||||
|
cursor.close()
|
||||||
|
|
||||||
|
if user_data:
|
||||||
|
return User(user_data['id'], user_data['username'], user_data['role'])
|
||||||
|
return None
|
||||||
|
|
||||||
|
@app.route('/')
|
||||||
|
def home():
|
||||||
|
return render_template('login.html')
|
||||||
|
|
||||||
|
@app.route('/view_logs')
|
||||||
|
@login_required
|
||||||
|
def view_logs():
|
||||||
|
with open('app.log', 'r') as log_file:
|
||||||
|
logs = log_file.readlines()
|
||||||
|
return render_template('logs.html', logs=logs)
|
||||||
|
|
||||||
|
# 处理文件上传
|
||||||
|
@app.route('/upload', methods=['POST'])
|
||||||
|
def upload_file():
|
||||||
|
logger.info("Handling file upload request")
|
||||||
|
if 'file' not in request.files:
|
||||||
|
logger.warning("No file part in request")
|
||||||
|
return jsonify({'message': 'No file part'}), 400
|
||||||
|
file = request.files['file']
|
||||||
|
if file.filename == '':
|
||||||
|
logger.warning("No selected file")
|
||||||
|
return jsonify({'message': 'No selected file'}), 400
|
||||||
|
if file and allowed_file(file.filename):
|
||||||
|
filename = secure_filename(f"{uuid.uuid4().hex}_{file.filename}")
|
||||||
|
filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
|
||||||
|
file.save(filepath)
|
||||||
|
logger.info(f"File uploaded successfully: {filename}")
|
||||||
|
return jsonify({'message': 'File uploaded successfully', 'filepath': filepath}), 201
|
||||||
|
else:
|
||||||
|
logger.warning("File type not allowed")
|
||||||
|
return jsonify({'message': 'File type not allowed'}), 400
|
||||||
|
|
||||||
|
# 提供上传文件的访问
|
||||||
|
@app.route('/uploads/<filename>')
|
||||||
|
def uploaded_file(filename):
|
||||||
|
logger.info(f"Accessing uploaded file: {filename}")
|
||||||
|
return send_from_directory(app.config['UPLOAD_FOLDER'], filename)
|
||||||
|
|
||||||
|
# 登录页面
|
||||||
|
@app.route('/login', methods=['GET', 'POST'])
|
||||||
|
def login():
|
||||||
|
logger.info("Handling login request")
|
||||||
|
if request.method == 'POST':
|
||||||
|
username = request.form['username']
|
||||||
|
password = request.form['password']
|
||||||
|
|
||||||
|
cursor = mysql.connection.cursor()
|
||||||
|
# 使用参数化查询检查用户是否存在
|
||||||
|
cursor.execute("SELECT * FROM users WHERE username = %s", (username,))
|
||||||
|
user_data = cursor.fetchone()
|
||||||
|
cursor.close()
|
||||||
|
print(user_data['password'].encode('utf-8'))
|
||||||
|
if user_data:
|
||||||
|
# 解密密码
|
||||||
|
decrypted_password = decrypt_message(user_data['password'].encode('utf-8'))
|
||||||
|
print("解密:",decrypted_password)
|
||||||
|
user = User(user_data['id'], user_data['username'], user_data['role'])
|
||||||
|
login_user(user)
|
||||||
|
logger.info(f"User {username} logged in with role {user_data['role']}")
|
||||||
|
|
||||||
|
if user_data['role'] == '侦查者':
|
||||||
|
return redirect(url_for('scout'))
|
||||||
|
elif user_data['role'] == '指挥者':
|
||||||
|
return redirect(url_for('commander'))
|
||||||
|
elif user_data['role'] == '攻击者':
|
||||||
|
return redirect(url_for('attacker'))
|
||||||
|
else:
|
||||||
|
logger.warning("Invalid credentials")
|
||||||
|
return "Invalid credentials. Please try again."
|
||||||
|
|
||||||
|
return render_template('login.html')
|
||||||
|
|
||||||
|
# 处理数据库语句
|
||||||
|
def con_my_sql(sql_code, params=None):
|
||||||
|
try:
|
||||||
|
# 尝试连接数据库
|
||||||
|
conn.ping(reconnect=True)
|
||||||
|
# 创建游标对象,结果以字典形式返回
|
||||||
|
cursor = conn.cursor(pymysql.cursors.DictCursor)
|
||||||
|
# 如果传入了参数,使用参数化查询
|
||||||
|
if params:
|
||||||
|
cursor.execute(sql_code, params)
|
||||||
|
else:
|
||||||
|
cursor.execute(sql_code)
|
||||||
|
# 提交事务
|
||||||
|
conn.commit()
|
||||||
|
# 返回游标对象
|
||||||
|
return cursor
|
||||||
|
except pymysql.MySQLError as err_massage:
|
||||||
|
# 捕获异常,回滚事务
|
||||||
|
conn.rollback()
|
||||||
|
# 关闭连接
|
||||||
|
conn.close()
|
||||||
|
# 返回异常类型和异常信息
|
||||||
|
return type(err_massage), err_massage
|
||||||
|
|
||||||
|
|
||||||
|
class RegisterForm(Form):
|
||||||
|
captcha = StringField(validators=[Length(min=4,max=4,message='校验码格式错误')])
|
||||||
|
username = StringField(validators=[Length(min=3,max=10,message='用户名长度必须在3到10个字符之间')])
|
||||||
|
password = StringField(validators=[Length(min=6,max=20,message='密码长度必须在6到20个字符之间')])
|
||||||
|
password_confirm = StringField(validators=[EqualTo('password',message='两次输入的密码不一致')])
|
||||||
|
|
||||||
|
def validate_captcha(self, filed):
|
||||||
|
if filed.data not in ['1111', '2222', '3333']:
|
||||||
|
raise ValidationError('校验码错误')
|
||||||
|
|
||||||
|
# 注册页面
|
||||||
|
@app.route('/register', methods=['GET', 'POST'])
|
||||||
|
def register():
|
||||||
|
if request.method == 'GET':
|
||||||
|
return render_template('register.html')
|
||||||
|
else:
|
||||||
|
form = RegisterForm(request.form)
|
||||||
|
if form.validate():
|
||||||
|
username = form.username.data
|
||||||
|
password = form.password.data
|
||||||
|
captcha = form.captcha.data
|
||||||
|
# 静态注册码进行角色注册
|
||||||
|
if captcha == "1111":
|
||||||
|
role = "侦查者"
|
||||||
|
elif captcha == "2222":
|
||||||
|
role = "指挥者"
|
||||||
|
elif captcha == "3333":
|
||||||
|
role = "攻击者"
|
||||||
|
else:
|
||||||
|
return '无效的校验码 <a href="/">返回登录</a>'
|
||||||
|
|
||||||
|
# 使用参数化查询检查用户是否存在
|
||||||
|
code = "SELECT * FROM users WHERE username = %s"
|
||||||
|
cursor_ans = con_my_sql(code, (username,))
|
||||||
|
cursor_select = cursor_ans.fetchall()
|
||||||
|
|
||||||
|
if len(cursor_select) > 0:
|
||||||
|
return '用户已经存在 <a href="/">返回登录</a>'
|
||||||
|
else:
|
||||||
|
# 加密密码
|
||||||
|
encrypted_password = encrypt_message(password)
|
||||||
|
# 使用参数化查询插入新用户
|
||||||
|
code = "INSERT INTO users(username, password, role) VALUES(%s, %s, %s)"
|
||||||
|
con_my_sql(code, (username, encrypted_password, role))
|
||||||
|
return '注册成功 <a href="/">返回登录</a>'
|
||||||
|
else:
|
||||||
|
print(form.errors)
|
||||||
|
return redirect(url_for('register'))
|
||||||
|
|
||||||
|
# 侦查者页面
|
||||||
|
@app.route('/scout')
|
||||||
|
@login_required
|
||||||
|
def scout():
|
||||||
|
logger.info("Accessing scout page")
|
||||||
|
if current_user.role != '侦查者':
|
||||||
|
logger.warning("Unauthorized access to scout page")
|
||||||
|
return '用户无此页面访问权限 <a href="/">返回登录<a/>'
|
||||||
|
|
||||||
|
# 获取侦察者的通知
|
||||||
|
cursor = mysql.connection.cursor()
|
||||||
|
cursor.execute("SELECT * FROM notifications WHERE user_id = %s ORDER BY created_at DESC", (current_user.id,))
|
||||||
|
notifications = cursor.fetchall()
|
||||||
|
cursor.close()
|
||||||
|
logger.info(f"Notifications fetched: {notifications}")
|
||||||
|
|
||||||
|
# 解密通知
|
||||||
|
for notification in notifications:
|
||||||
|
notification['message'] = decrypt_message(notification['message'])
|
||||||
|
|
||||||
|
return render_template('scout.html', notifications=notifications)
|
||||||
|
|
||||||
|
# 处理消息
|
||||||
|
@app.route('/handle_message/<int:message_id>/<action>', methods=['POST'])
|
||||||
|
@login_required
|
||||||
|
def handle_message(message_id, action):
|
||||||
|
logger.info(f"Handling message action: {action} for ID: {message_id}")
|
||||||
|
if current_user.role != '指挥者':
|
||||||
|
logger.warning("Unauthorized access to handle message")
|
||||||
|
return redirect(url_for('login'))
|
||||||
|
|
||||||
|
cursor = mysql.connection.cursor()
|
||||||
|
if action == 'accept':
|
||||||
|
cursor.execute("UPDATE messages SET status = 'accepted' WHERE id = %s", (message_id,))
|
||||||
|
flash('消息已接受')
|
||||||
|
elif action == 'reject':
|
||||||
|
cursor.execute("UPDATE messages SET status = 'rejected' WHERE id = %s", (message_id,))
|
||||||
|
flash('消息已打回')
|
||||||
|
mysql.connection.commit()
|
||||||
|
cursor.close()
|
||||||
|
logger.info(f"Message status updated: {action}")
|
||||||
|
|
||||||
|
# 获取消息内容
|
||||||
|
cursor = mysql.connection.cursor()
|
||||||
|
cursor.execute("SELECT message FROM messages WHERE id = %s", (message_id,))
|
||||||
|
message_data = cursor.fetchone()
|
||||||
|
cursor.close()
|
||||||
|
logger.info(f"Message data fetched: {message_data}")
|
||||||
|
|
||||||
|
# 解密消息
|
||||||
|
decrypted_message = decrypt_message(message_data['message'])
|
||||||
|
|
||||||
|
# 将结果反馈给侦察者
|
||||||
|
cursor = mysql.connection.cursor()
|
||||||
|
cursor.execute("SELECT id FROM users WHERE role = '侦查者'")
|
||||||
|
scouts = cursor.fetchall()
|
||||||
|
cursor.close()
|
||||||
|
logger.info(f"Scouts found: {scouts}")
|
||||||
|
|
||||||
|
for scout in scouts:
|
||||||
|
cursor = mysql.connection.cursor()
|
||||||
|
cursor.execute("INSERT INTO notifications (user_id, message_id, action, message) VALUES (%s, %s, %s, %s)",
|
||||||
|
(scout['id'], message_id, action, message_data['message']))
|
||||||
|
mysql.connection.commit()
|
||||||
|
cursor.close()
|
||||||
|
logger.info(f"Notification inserted for scout: {scout['id']}")
|
||||||
|
|
||||||
|
return redirect(url_for('commander'))
|
||||||
|
|
||||||
|
# 清除通知
|
||||||
|
@app.route('/clear_notifications', methods=['POST'])
|
||||||
|
@login_required
|
||||||
|
def clear_notifications():
|
||||||
|
user_id = request.json.get('user_id')
|
||||||
|
if not user_id:
|
||||||
|
return jsonify({'success': False, 'message': 'User ID is required'}), 400
|
||||||
|
|
||||||
|
cursor = mysql.connection.cursor()
|
||||||
|
cursor.execute("DELETE FROM notifications WHERE user_id = %s", (user_id,))
|
||||||
|
mysql.connection.commit()
|
||||||
|
cursor.close()
|
||||||
|
|
||||||
|
return jsonify({'success': True, 'message': 'Notifications cleared successfully'})
|
||||||
|
|
||||||
|
# 指挥者页面
|
||||||
|
@app.route('/commander')
|
||||||
|
@login_required
|
||||||
|
def commander():
|
||||||
|
logger.info("Accessing commander page")
|
||||||
|
if current_user.role != '指挥者':
|
||||||
|
logger.warning("Unauthorized access to commander page")
|
||||||
|
return '用户无此页面访问权限 <a href="/">返回登录<a/>'
|
||||||
|
|
||||||
|
# 获取特定目录下的所有文件和攻击坐标状态
|
||||||
|
directory = 'E:/_Ufo/0000jiegou/TheBattleCar/uploads'
|
||||||
|
media_items = []
|
||||||
|
|
||||||
|
try:
|
||||||
|
media_items = [f for f in os.listdir(directory)
|
||||||
|
if os.path.isfile(os.path.join(directory, f)) and allowed_file(f)]
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error accessing directory: {str(e)}")
|
||||||
|
flash(f"Error accessing directory: {str(e)}")
|
||||||
|
|
||||||
|
# 获取攻击状态
|
||||||
|
cursor = mysql.connection.cursor()
|
||||||
|
cursor.execute("SELECT id, coordinate, attacked, created_at FROM attacks ORDER BY created_at DESC")
|
||||||
|
attacks = cursor.fetchall()
|
||||||
|
cursor.close()
|
||||||
|
|
||||||
|
# 解密坐标
|
||||||
|
for attack in attacks:
|
||||||
|
attack['coordinate'] = decrypt_message(attack['coordinate'])
|
||||||
|
|
||||||
|
# 获取侦察者发送的消息
|
||||||
|
cursor = mysql.connection.cursor()
|
||||||
|
cursor.execute("SELECT id, message, photo_url, created_at, status FROM messages ORDER BY created_at DESC")
|
||||||
|
messages = cursor.fetchall()
|
||||||
|
cursor.close()
|
||||||
|
|
||||||
|
# 解密消息
|
||||||
|
for message in messages:
|
||||||
|
message['message'] = decrypt_message(message['message'])
|
||||||
|
|
||||||
|
return render_template('commander.html', media_items=media_items, attacks=attacks, messages=messages)
|
||||||
|
|
||||||
|
# 指挥者发送攻击坐标
|
||||||
|
@app.route('/send_attack', methods=['POST'])
|
||||||
|
@login_required
|
||||||
|
def send_attack():
|
||||||
|
logger.info("Handling send attack request")
|
||||||
|
if current_user.role != '指挥者':
|
||||||
|
logger.warning("Unauthorized access to send attack")
|
||||||
|
return redirect(url_for('login'))
|
||||||
|
|
||||||
|
coordinate = request.form.get('coordinate')
|
||||||
|
if not coordinate:
|
||||||
|
logger.warning("No coordinate provided")
|
||||||
|
return "No coordinate provided", 400
|
||||||
|
|
||||||
|
# 加密坐标
|
||||||
|
encrypted_coordinate = encrypt_message(coordinate)
|
||||||
|
coordinate = encrypted_coordinate
|
||||||
|
|
||||||
|
# 插入攻击坐标到数据库
|
||||||
|
cursor = mysql.connection.cursor()
|
||||||
|
cursor.execute("INSERT INTO attacks (coordinate) VALUES (%s)", (coordinate,))
|
||||||
|
mysql.connection.commit()
|
||||||
|
cursor.close()
|
||||||
|
logger.info(f"Attack coordinate sent: {coordinate}")
|
||||||
|
|
||||||
|
flash('攻击坐标已发送')
|
||||||
|
return redirect(url_for('commander'))
|
||||||
|
|
||||||
|
# 清除照片
|
||||||
|
@app.route('/clear_photos', methods=['POST'])
|
||||||
|
@login_required
|
||||||
|
def clear_photos():
|
||||||
|
logger.info("Handling clear photos request")
|
||||||
|
if current_user.role != '指挥者':
|
||||||
|
logger.warning("Unauthorized access to clear photos")
|
||||||
|
return redirect(url_for('login'))
|
||||||
|
|
||||||
|
directory = app.config['UPLOAD_FOLDER']
|
||||||
|
for filename in os.listdir(directory):
|
||||||
|
file_path = os.path.join(directory, filename)
|
||||||
|
try:
|
||||||
|
if os.path.isfile(file_path):
|
||||||
|
os.unlink(file_path)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error deleting file: {str(e)}")
|
||||||
|
flash(f"Error deleting file: {str(e)}")
|
||||||
|
|
||||||
|
logger.info("Photos cleared successfully")
|
||||||
|
flash('照片已清空')
|
||||||
|
return redirect(url_for('commander'))
|
||||||
|
|
||||||
|
# 清除侦察者发送的消息
|
||||||
|
@app.route('/clear_messages', methods=['POST'])
|
||||||
|
@login_required
|
||||||
|
def clear_messages():
|
||||||
|
logger.info("Handling clear messages request")
|
||||||
|
if current_user.role != '指挥者':
|
||||||
|
logger.warning("Unauthorized access to clear messages")
|
||||||
|
return redirect(url_for('login'))
|
||||||
|
|
||||||
|
cursor = mysql.connection.cursor()
|
||||||
|
cursor.execute("DELETE FROM messages")
|
||||||
|
mysql.connection.commit()
|
||||||
|
cursor.close()
|
||||||
|
|
||||||
|
logger.info("Messages cleared successfully")
|
||||||
|
flash('消息已清空')
|
||||||
|
return redirect(url_for('commander'))
|
||||||
|
|
||||||
|
# 攻击者页面
|
||||||
|
@app.route('/attacker')
|
||||||
|
@login_required
|
||||||
|
def attacker():
|
||||||
|
logger.info("Accessing attacker page")
|
||||||
|
if current_user.role != '攻击者':
|
||||||
|
logger.warning("Unauthorized access to attacker page")
|
||||||
|
return '用户无此页面访问权限 <a href="/">返回登录<a/>'
|
||||||
|
|
||||||
|
# 获取攻击坐标列表
|
||||||
|
cursor = mysql.connection.cursor()
|
||||||
|
cursor.execute("SELECT id, coordinate, created_at FROM attacks ORDER BY created_at DESC")
|
||||||
|
attacks = cursor.fetchall()
|
||||||
|
cursor.close()
|
||||||
|
|
||||||
|
# 解密坐标
|
||||||
|
for attack in attacks:
|
||||||
|
attack['coordinate'] = decrypt_message(attack['coordinate'])
|
||||||
|
|
||||||
|
return render_template('attacker.html', attacks=attacks)
|
||||||
|
|
||||||
|
# 攻击者执行攻击
|
||||||
|
@app.route('/execute_attack/<int:attack_id>', methods=['POST'])
|
||||||
|
@login_required
|
||||||
|
def execute_attack(attack_id):
|
||||||
|
logger.info(f"Handling execute attack request for ID: {attack_id}")
|
||||||
|
if current_user.role != '攻击者':
|
||||||
|
logger.warning("Unauthorized access to execute attack")
|
||||||
|
return redirect(url_for('login'))
|
||||||
|
|
||||||
|
cursor = mysql.connection.cursor()
|
||||||
|
cursor.execute("UPDATE attacks SET attacked = TRUE WHERE id = %s", (attack_id,))
|
||||||
|
mysql.connection.commit()
|
||||||
|
cursor.close()
|
||||||
|
logger.info(f"Attack executed for ID: {attack_id}")
|
||||||
|
|
||||||
|
flash(f'已对坐标ID为 {attack_id} 的目标完成打击')
|
||||||
|
return redirect(url_for('attacker'))
|
||||||
|
|
||||||
|
# 退出登录(清除会话)
|
||||||
|
@app.route('/logout')
|
||||||
|
@login_required
|
||||||
|
def logout():
|
||||||
|
logger.info("Handling logout request")
|
||||||
|
session.pop('username', None)
|
||||||
|
session.pop('role', None)
|
||||||
|
return redirect(url_for('login'))
|
||||||
|
|
||||||
|
# 发送消息
|
||||||
|
@app.route('/send_message', methods=['GET', 'POST'])
|
||||||
|
@login_required
|
||||||
|
def send_message():
|
||||||
|
logger.info("Handling send message request")
|
||||||
|
if request.method == 'POST':
|
||||||
|
message = request.form.get('message')
|
||||||
|
if not message:
|
||||||
|
logger.warning("No message provided")
|
||||||
|
return "No message provided", 400
|
||||||
|
|
||||||
|
photo_url = None
|
||||||
|
if 'photo' in request.files:
|
||||||
|
file = request.files['photo']
|
||||||
|
if file.filename != '':
|
||||||
|
if file and allowed_file(file.filename):
|
||||||
|
filename = secure_filename(f"{uuid.uuid4().hex}_{file.filename}")
|
||||||
|
filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
|
||||||
|
file.save(filepath)
|
||||||
|
photo_url = url_for('uploaded_file', filename=filename, _external=True)
|
||||||
|
else:
|
||||||
|
logger.warning("Allowed file types are png, jpg, jpeg, gif")
|
||||||
|
return "Allowed file types are png, jpg, jpeg, gif", 400
|
||||||
|
|
||||||
|
# 加密数据
|
||||||
|
encrypted_message = encrypt_message(message)
|
||||||
|
message = encrypted_message
|
||||||
|
|
||||||
|
# 插入数据到 MySQL
|
||||||
|
cursor = mysql.connection.cursor()
|
||||||
|
cursor.execute("INSERT INTO messages (message, photo_url, status) VALUES (%s, %s, %s)", (message, photo_url, 'pending'))
|
||||||
|
mysql.connection.commit()
|
||||||
|
cursor.close()
|
||||||
|
logger.info(f"Message sent: {message}, Photo URL: {photo_url}")
|
||||||
|
|
||||||
|
return f"Message and photo (if uploaded) have been received. Message: {message}\nPhoto URL: {photo_url if photo_url else 'N/A'}"
|
||||||
|
|
||||||
|
return render_template('send_message.html')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/messages', methods=['GET'])
|
||||||
|
@login_required
|
||||||
|
def messages():
|
||||||
|
logger.info("Handling messages request")
|
||||||
|
|
||||||
|
# 角色检查
|
||||||
|
if current_user.role != '指挥者':
|
||||||
|
logger.warning("Unauthorized access to messages")
|
||||||
|
if request.accept_mimetypes.best_match(['application/json', 'text/html']) == 'application/json':
|
||||||
|
return jsonify({"error": "Unauthorized access"}), 403
|
||||||
|
else:
|
||||||
|
return redirect(url_for('login'))
|
||||||
|
|
||||||
|
# 获取消息
|
||||||
|
cursor = mysql.connection.cursor()
|
||||||
|
cursor.execute("SELECT id, message, photo_url, created_at, status FROM messages ORDER BY created_at DESC")
|
||||||
|
result = cursor.fetchall()
|
||||||
|
cursor.close()
|
||||||
|
|
||||||
|
# 解密消息
|
||||||
|
messages = []
|
||||||
|
for row in result:
|
||||||
|
decrypted_message = decrypt_message(row['message'])
|
||||||
|
messages.append({
|
||||||
|
'id': row['id'],
|
||||||
|
'message': decrypted_message,
|
||||||
|
'photo_url': row['photo_url'],
|
||||||
|
'created_at': row['created_at'],
|
||||||
|
'status': row['status']
|
||||||
|
})
|
||||||
|
|
||||||
|
# 根据请求类型返回不同的响应
|
||||||
|
if request.accept_mimetypes.best_match(['application/json', 'text/html']) == 'application/json':
|
||||||
|
return jsonify(messages)
|
||||||
|
else:
|
||||||
|
return render_template('view_messages.html', messages=messages)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
# generate_key() # 仅在第一次运行时生成密钥
|
||||||
|
app.run(debug=True, host='0.0.0.0', port=8000)
|
||||||
|
|
||||||
|
# # 测试加密和解密
|
||||||
|
# test_message = "This is a test message"
|
||||||
|
# encrypted_message = encrypt_message(test_message)
|
||||||
|
# decrypted_message = decrypt_message(encrypted_message)
|
||||||
|
|
||||||
|
# print(f"Original message: {test_message}")
|
||||||
|
# print(f"Encrypted message: {encrypted_message}")
|
||||||
|
# print(f"Decrypted message: {decrypted_message}")
|
@ -0,0 +1,20 @@
|
|||||||
|
from cryptography.fernet import Fernet
|
||||||
|
import os
|
||||||
|
|
||||||
|
# 生成一个新的Fernet密钥
|
||||||
|
key = Fernet.generate_key()
|
||||||
|
|
||||||
|
# 指定密钥文件的路径
|
||||||
|
key_path = 'secret.key'
|
||||||
|
|
||||||
|
# 将密钥保存到文件中
|
||||||
|
with open(key_path, 'wb') as key_file:
|
||||||
|
key_file.write(key)
|
||||||
|
|
||||||
|
# 打印密钥以便查看
|
||||||
|
# print(f"Generated key: {key.decode()}")
|
||||||
|
|
||||||
|
# 确保密钥文件的权限设置为只读
|
||||||
|
os.chmod(key_path, 0o400)
|
||||||
|
|
||||||
|
print(f"Key has been saved to {key_path}")
|
@ -0,0 +1 @@
|
|||||||
|
Ex5GV0VAWNvGixTKY9rKEf_kTrAFPeEO7L3zL3rHzVY=
|
@ -0,0 +1,29 @@
|
|||||||
|
// server.js
|
||||||
|
const express = require('express');
|
||||||
|
const http = require('http');
|
||||||
|
const socketIo = require('socket.io');
|
||||||
|
|
||||||
|
const app = express();
|
||||||
|
const server = http.createServer(app);
|
||||||
|
const io = socketIo(server);
|
||||||
|
|
||||||
|
let clients = {};
|
||||||
|
|
||||||
|
io.on('connection', (socket) => {
|
||||||
|
console.log('A client connected:', socket.id);
|
||||||
|
clients[socket.id] = { status: 'connected' };
|
||||||
|
|
||||||
|
socket.on('disconnect', () => {
|
||||||
|
console.log('A client disconnected:', socket.id);
|
||||||
|
clients[socket.id] = { status: 'disconnected' };
|
||||||
|
});
|
||||||
|
|
||||||
|
// 定时发送客户端状态
|
||||||
|
setInterval(() => {
|
||||||
|
io.emit('clients_status', clients);
|
||||||
|
}, 5000);
|
||||||
|
});
|
||||||
|
|
||||||
|
server.listen(3000, () => {
|
||||||
|
console.log('Server listening on port 3000');
|
||||||
|
});
|
@ -0,0 +1,39 @@
|
|||||||
|
<!-- templates/network.html -->
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Network Connection Status</title>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.min.js"></script>
|
||||||
|
<style>
|
||||||
|
body { font-family: Arial, sans-serif; }
|
||||||
|
#network { padding: 20px; }
|
||||||
|
.client {
|
||||||
|
color: white;
|
||||||
|
padding: 10px;
|
||||||
|
margin: 5px;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
.connected { background-color: green; }
|
||||||
|
.disconnected { background-color: red; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Client Connection Status</h1>
|
||||||
|
<div id="network"></div>
|
||||||
|
<script>
|
||||||
|
const socket = io('http://localhost:3000');
|
||||||
|
const networkElement = document.getElementById('network');
|
||||||
|
|
||||||
|
socket.on('clients_status', (clients) => {
|
||||||
|
networkElement.innerHTML = '';
|
||||||
|
for (const [id, info] of Object.entries(clients)) {
|
||||||
|
const clientElement = document.createElement('div');
|
||||||
|
clientElement.className = 'client ' + info.status;
|
||||||
|
clientElement.textContent = `Client ID: ${id} - Status: ${info.status}`;
|
||||||
|
networkElement.appendChild(clientElement);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
Before Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 49 KiB |
Loading…
Reference in new issue