', 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'))
- return render_template('scout.html')
+
+ 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}")
+
+ # 将结果反馈给侦察者
+ 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')
@@ -166,7 +294,7 @@ def commander():
logger.info("Accessing commander page")
if current_user.role != '指挥者':
logger.warning("Unauthorized access to commander page")
- return redirect(url_for('login'))
+ return '用户无此页面访问权限 返回登录'
# 获取特定目录下的所有文件和攻击坐标状态
directory = 'E:/_Ufo/0000jiegou/TheBattleCar/uploads'
@@ -185,7 +313,13 @@ def commander():
attacks = cursor.fetchall()
cursor.close()
- return render_template('commander.html', media_items=media_items, attacks=attacks)
+ # 获取侦察者发送的消息
+ 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()
+
+ return render_template('commander.html', media_items=media_items, attacks=attacks, messages=messages)
# 指挥者发送攻击坐标
@app.route('/send_attack', methods=['POST'])
@@ -210,6 +344,47 @@ def send_attack():
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')
@@ -218,7 +393,7 @@ def attacker():
logger.info("Accessing attacker page")
if current_user.role != '攻击者':
logger.warning("Unauthorized access to attacker page")
- return redirect(url_for('login'))
+ return '用户无此页面访问权限 返回登录'
# 获取攻击坐标列表
cursor = mysql.connection.cursor()
@@ -281,7 +456,7 @@ def send_message():
# 插入数据到 MySQL
cursor = mysql.connection.cursor()
- cursor.execute("INSERT INTO messages (message, photo_url) VALUES (%s, %s)", (message, photo_url))
+ 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}")
@@ -296,7 +471,7 @@ def send_message():
def get_messages():
logger.info("Handling get messages request")
cursor = mysql.connection.cursor()
- cursor.execute("SELECT id, message, photo_url, created_at FROM messages")
+ cursor.execute("SELECT id, message, photo_url, created_at, status FROM messages")
result = cursor.fetchall()
cursor.close()
@@ -306,7 +481,8 @@ def get_messages():
'id': row['id'],
'message': row['message'],
'photo_url': row['photo_url'],
- 'created_at': row['created_at']
+ 'created_at': row['created_at'],
+ 'status': row['status']
})
return jsonify(messages)
diff --git a/TheBattleCar/createkey.py b/TheBattleCar/createkey.py
new file mode 100644
index 0000000..7e32437
--- /dev/null
+++ b/TheBattleCar/createkey.py
@@ -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}")
\ No newline at end of file
diff --git a/TheBattleCar/secret.key b/TheBattleCar/secret.key
new file mode 100644
index 0000000..aa43d09
--- /dev/null
+++ b/TheBattleCar/secret.key
@@ -0,0 +1 @@
+xO0rhaZ7_TEiOBm2ZH2b6L9VIiECc_aO8FeY5HGt2nY=
\ No newline at end of file
diff --git a/TheBattleCar/server.js b/TheBattleCar/server.js
new file mode 100644
index 0000000..f0571d5
--- /dev/null
+++ b/TheBattleCar/server.js
@@ -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');
+});
\ No newline at end of file
diff --git a/TheBattleCar/static/login.css b/TheBattleCar/static/login.css
new file mode 100644
index 0000000..cea7f56
--- /dev/null
+++ b/TheBattleCar/static/login.css
@@ -0,0 +1,211 @@
+*{
+ /*初始化 清除页面元素的内外边距*/
+ padding: 0;
+ margin: 0;
+ /*盒子模型*/
+ box-sizing: border-box;
+}
+body {
+ /*弹性布局 让页面元素垂直+水平居中*/
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ /*让页面始终占浏览器可视区域总高度*/
+ height: 100vh;
+ /*背景渐变色*/
+ background: linear-gradient(#141e30,#243b55);
+
+}
+.login{
+ /*弹性布局 让子元素称为弹性项目*/
+ display: flex;
+ /*让弹性项目垂直排列 原理是改变弹性盒子的主轴方向
+ 父元素就是弹性盒子 现在改变后的主轴方向是向下了*/
+ flex-direction: column;
+ /*让弹性项目在交叉轴方向水平居中 现在主轴的方向是向下
+ 交叉轴的方向是与主轴垂直 交叉轴的方向是向右*/
+ align-items: center;
+ width: 400px;
+ padding: 40px;
+ background-color: rgba(0, 0, 0, 0.2);
+ box-shadow: 0 15px 25px rgba(0, 0, 0, 0.4);
+
+}
+
+.login h2{
+ color: #fff;
+ margin-bottom: 30px;
+}
+.login h3{
+ color: #fff;
+ margin-bottom: 10px;
+}
+.login .login_box {
+ /*相对定位*/
+ position: relative;
+ width: 100%;
+}
+.login .login_box input{
+ /*清除input框自带的边框和轮廓*/
+ outline: none;
+ border: none;
+ width: 100%;
+ padding: 10px 0;
+ margin-bottom: 30px;
+ color: #fff;
+ font-size: 16px;
+ border-bottom: 1px solid #fff;
+ /*背景颜色为透明色*/
+ background-color: transparent;
+}
+
+.login .login_box label{
+ position:absolute;
+ top: 0 ;
+ left: 0;
+ padding: 10px 0;
+ color: #fff;
+ /*这个属性的默认值是auto 默认是这个元素可以被点击
+ 但是如果我们写了none 就是这个元素不能被点击,就好像它可见但是不能用
+ 可望而不可及*/
+ /*这个就是两者的区别*/
+ pointer-events: none;
+ /*加个过度*/
+ transition: all 0.5s;
+}
+/*: focus 选择器是当input获得焦点是触发的样式 + 是相邻兄弟选择器
+ 去找与input相邻的兄弟label*/
+/*:valid 选择器是判断input 框的内容是否合法,如果合法会执行下面的属性代码,
+ 不合法就不会执行,我们刚开始写布局的时候给input框写了required 我们删掉看对比
+ 当没有required的话 input框的值就会被认为一直合法,所以一直都是下方的样式,
+ 但是密码不会,密码框的值为空,那么这句话就不合法,required不能为空
+ 当我们给密码框写点东西的时候才会执行以下代码
+
+*/
+.login .login_box input:focus + label,
+.login .login_box input:valid + label{
+ top: -20px;
+ color: #03e9f4;
+ font-size: 12px;
+}
+
+.login a{
+ /*overflow: hidden;*/
+ position: relative;
+ padding: 10px 20px;
+ color: #03e9f4;
+ /*取消a表现原有的下划线*/
+ text-decoration: none;
+ /*同样加个过渡*/
+ transition: all 0.5s;
+}
+.login a:hover {
+ color: #fff;
+ border-radius: 5px;
+ background-color: #03e9f4;
+ box-shadow: 0 0 5px #03e9f4,0 0 25px #03e9f4,0 0 50px #03e9f4,0 0 100px #03e9f4;
+}
+.login a span{
+ position: absolute;
+}
+.login a span:first-child {
+ top: 0;
+ left: -100%;
+ width: 100%;
+ height: 2px;
+ /*to right 就是往右边 下面的同理*/
+ background: linear-gradient(to right,transparent,#03e9f4);
+ /*动画 名称 时长 linear是匀速运动 infinite是无限次运动*/
+ animation: move1 1s linear infinite;
+
+}
+.login a span:nth-child(2){
+ right: 0;
+ top: -100%;
+ width: 2px;
+ height: 100%;
+ background: linear-gradient(transparent,#03e6f4);
+ /*这里多了个0.25s其实是延迟时间*/
+ animation: move2 1s linear 0.25s infinite;
+}
+
+.login a span:nth-child(3){
+ right: -100%;
+ bottom: 0;
+ width: 100%;
+ height: 2px;
+ background: linear-gradient(to left,transparent,#03e9f4);
+
+ animation: move3 1s linear 0.5s infinite;
+}
+
+.login a span:last-child{
+ left: 0;
+ bottom: -100%;
+ width: 2px;
+ height: 100%;
+ background: linear-gradient(#03e9f4,transparent);
+ animation: move4 1s linear 0.75s infinite;
+}
+/*写一下动画 */
+@keyframes move1{
+ 0%{
+ left: -100%;
+
+ }
+ 50%,
+ 100%{
+ left: 100%;
+ }
+}
+
+@keyframes move2{
+ 0%{
+ top: -100%;
+
+ }
+ 50%,
+ 100%{
+ top: 100%;
+ }
+}
+
+@keyframes move3{
+ 0%{
+ right: -100%;
+
+ }
+ 50%,
+ 100%{
+ right: 100%;
+ }
+}
+
+@keyframes move4{
+ 0%{
+ bottom: -100%;
+
+ }
+ 50%,
+ 100%{
+ bottom: 100%;
+ }
+}
+
+.register-button {
+ display: block;
+ width: 100%;
+ height: 41px;
+ text-align: center;
+ background: #add8e6;
+ border: 3 solid #999;
+ border-radius: 5px;
+}
+
+.white-text {
+ color: #fff; /* 设置文本颜色为白色 */
+}
+
+.attack-button {
+ background: #add8e6; /* 背景颜色 */
+}
diff --git a/TheBattleCar/templates/attacker.html b/TheBattleCar/templates/attacker.html
index 24b7d47..b77d886 100644
--- a/TheBattleCar/templates/attacker.html
+++ b/TheBattleCar/templates/attacker.html
@@ -1,18 +1,22 @@
-
+
- 攻击者界面
+
+
+ 攻击者
+
- 攻击坐标列表
-
+
+
攻击坐标列表
+
{% if attacks %}
{% for attack in attacks %}
-
坐标: {{ attack.coordinate }} - {{ attack.created_at }}
{% if not attack.attacked %}
{% else %}
已完成打击
@@ -23,5 +27,6 @@
- No attack coordinates available.
{% endif %}
+
\ No newline at end of file
diff --git a/TheBattleCar/templates/commander.html b/TheBattleCar/templates/commander.html
index ee215a7..b4c8fc1 100644
--- a/TheBattleCar/templates/commander.html
+++ b/TheBattleCar/templates/commander.html
@@ -1,46 +1,152 @@
- 指挥者界面
+
+
+ 指挥者
+
- 指挥者界面
+
+
指挥者
+
发送攻击坐标
+
-
发送攻击坐标
-
+
攻击坐标状态
+
+ {% if attacks %}
+ {% for attack in attacks %}
+ -
+ 坐标: {{ attack.coordinate }}
+ - {{ attack.created_at }}
+ - {% if attack.attacked %}已完成打击{% else %}等待打击{% endif %}
+
+ {% endfor %}
+ {% else %}
+ - No attack coordinates available.
+ {% endif %}
+
-
攻击坐标状态
-
- {% if attacks %}
- {% for attack in attacks %}
- -
- 坐标: {{ attack.coordinate }}
- - {{ attack.created_at }}
- - {% if attack.attacked %}已完成打击{% else %}等待打击{% endif %}
-
- {% endfor %}
- {% else %}
- - No attack coordinates available.
- {% endif %}
-
+
查看照片
+
+ {% if media_items %}
+ {% for filename in media_items %}
+ -
+
+
+
+
+ {% endfor %}
+ {% else %}
+ - No images found in the directory.
+ {% endif %}
+
+
-
查看照片
-
- {% if media_items %}
- {% for filename in media_items %}
- -
-
-
-
-
- {% endfor %}
- {% else %}
- - No images found in the directory.
- {% endif %}
-
+
侦察者发送的消息和图片
+
+ {% if messages %}
+ {% for message in messages %}
+ -
+ 消息: {{ message.message }}
+ - {{ message.created_at }}
+ - 状态: {{ message.status }}
+ {% if message.photo_url %}
+
+ {% endif %}
+
+
+
+ {% endfor %}
+ {% else %}
+ - 没有消息和图片。
+ {% endif %}
+
+
+
\ No newline at end of file
diff --git a/TheBattleCar/templates/login.html b/TheBattleCar/templates/login.html
index 17b7408..2af1b6d 100644
--- a/TheBattleCar/templates/login.html
+++ b/TheBattleCar/templates/login.html
@@ -1,25 +1,73 @@
-
+
- Login
+
+
+ 网络信息系统登录
+
- Login
-