| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225 |
- import os
- import time
- import uuid
- from flask import Blueprint, render_template, send_from_directory, current_app, redirect, url_for, request, jsonify, session, abort
- from werkzeug.utils import secure_filename
- from api.utils import login_required, load_led_config, get_server_ip
- from application.scheduler_service import scheduler_service
- from application.self_check_service import run_all_checks
- from application.uap_message_service import send_self_check_notification
- from utils.logger_config import logger
- main_bp = Blueprint('main', __name__)
- ATTACHMENT_TEST_SESSION_KEY = 'attachment_test_filename'
- # 附件测试页允许的类型(单附件,新上传会替换)
- ATTACHMENT_TEST_EXTENSIONS = {
- 'png', 'jpg', 'jpeg', 'gif', 'bmp', 'webp', 'heic', 'heif',
- 'mp4', 'mov', 'webm', 'mkv', 'avi',
- 'pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx',
- 'txt', 'csv', 'zip', 'rar', '7z',
- 'mp3', 'wav', 'm4a',
- }
- def _attachment_test_public_url(filename):
- if not filename:
- return None
- host = request.host
- if 'localhost' in host or '127.0.0.1' in host:
- server_ip = get_server_ip()
- port = request.environ.get('SERVER_PORT', '5000')
- host = f"{server_ip}:{port}"
- scheme = request.headers.get('X-Forwarded-Proto', request.scheme)
- return f"{scheme}://{host}/uploads/{filename}"
- def _attachment_test_remove_stored():
- """删除 session 中记录的文件(若存在)。"""
- name = session.pop(ATTACHMENT_TEST_SESSION_KEY, None)
- if not name:
- return
- path = os.path.join(current_app.config['UPLOAD_FOLDER'], name)
- try:
- if os.path.isfile(path):
- os.remove(path)
- except OSError as e:
- logger.warning(f"删除附件测试文件失败 {path}: {e}")
- def _guess_ext_from_content_type(content_type):
- if not content_type:
- return None
- ct = content_type.lower()
- mapping = {
- 'image/png': 'png', 'image/jpeg': 'jpg', 'image/jpg': 'jpg', 'image/gif': 'gif',
- 'image/webp': 'webp', 'image/bmp': 'bmp', 'image/heic': 'heic', 'image/heif': 'heif',
- 'video/mp4': 'mp4', 'video/quicktime': 'mov', 'video/webm': 'webm',
- 'application/pdf': 'pdf',
- }
- return mapping.get(ct.split(';')[0].strip())
- @main_bp.route('/')
- @login_required
- def index():
- """重定向到 Kodi 控制页面"""
- return redirect(url_for('main.kodi_page'))
- @main_bp.route('/kodi')
- @login_required
- def kodi_page():
- """Kodi 控制页面"""
- return render_template('kodi/index.html', active_page='kodi')
- @main_bp.route('/door')
- @login_required
- def door_page():
- """门禁控制页面"""
- return render_template('door/index.html', active_page='door')
- @main_bp.route('/led')
- @login_required
- def led_page():
- """LED 控制页面"""
- led_segments = load_led_config()
- return render_template('led/index.html', active_page='led', led_segments=led_segments)
- @main_bp.route('/ha')
- @login_required
- def ha_page():
- """HA 灯光控制页面"""
- return render_template('ha/index.html', active_page='ha')
- @main_bp.route('/self_check')
- @login_required
- def self_check_page():
- """设备自检页面"""
- return render_template('self_check.html', active_page='self_check')
- @main_bp.route('/attachment_upload_test')
- @login_required
- def attachment_test_page():
- """附件上传测试页面(单附件,会话内替换)"""
- return render_template('attachment_test/index.html', active_page='attachment_test')
- @main_bp.route('/js/<path:filename>')
- @login_required
- def serve_templates_js(filename):
- """提供 templates/js 下的脚本(如 im-sdk.js)"""
- safe = os.path.basename(filename)
- if safe != 'im-sdk.js':
- abort(404)
- return send_from_directory(os.path.join(current_app.root_path, 'templates', 'js'), safe)
- @main_bp.route('/api/attachment_test/status', methods=['GET'])
- @login_required
- def attachment_test_status():
- """当前会话中的附件信息"""
- fn = session.get(ATTACHMENT_TEST_SESSION_KEY)
- if not fn:
- return jsonify({'success': True, 'has_file': False})
- folder = current_app.config['UPLOAD_FOLDER']
- path = os.path.join(folder, fn)
- if not os.path.isfile(path):
- session.pop(ATTACHMENT_TEST_SESSION_KEY, None)
- return jsonify({'success': True, 'has_file': False})
- return jsonify({
- 'success': True,
- 'has_file': True,
- 'filename': fn,
- 'url': _attachment_test_public_url(fn),
- })
- @main_bp.route('/api/attachment_test/upload', methods=['POST'])
- @login_required
- def attachment_test_upload():
- """上传单个附件,覆盖会话中已有文件"""
- if 'file' not in request.files:
- return jsonify({'success': False, 'message': '缺少文件字段 file'}), 400
- file = request.files['file']
- if not file or file.filename == '':
- return jsonify({'success': False, 'message': '未选择文件'}), 400
- original = file.filename
- base = secure_filename(original)
- ext = None
- if base and '.' in base:
- ext = base.rsplit('.', 1)[1].lower()
- if not ext or ext not in ATTACHMENT_TEST_EXTENSIONS:
- ext = _guess_ext_from_content_type(file.content_type or '')
- if not ext or ext not in ATTACHMENT_TEST_EXTENSIONS:
- return jsonify({
- 'success': False,
- 'message': f'不支持的文件类型,允许: {", ".join(sorted(ATTACHMENT_TEST_EXTENSIONS))}',
- }), 400
- _attachment_test_remove_stored()
- new_name = f"att_{int(time.time() * 1000)}_{uuid.uuid4().hex[:8]}.{ext}"
- folder = current_app.config['UPLOAD_FOLDER']
- os.makedirs(folder, exist_ok=True)
- filepath = os.path.join(folder, new_name)
- file.save(filepath)
- session[ATTACHMENT_TEST_SESSION_KEY] = new_name
- return jsonify({
- 'success': True,
- 'message': '上传成功',
- 'filename': new_name,
- 'url': _attachment_test_public_url(new_name),
- })
- @main_bp.route('/api/attachment_test', methods=['DELETE'])
- @login_required
- def attachment_test_delete():
- """删除当前会话中的附件"""
- if not session.get(ATTACHMENT_TEST_SESSION_KEY):
- return jsonify({'success': True, 'message': '没有可删除的附件'})
- _attachment_test_remove_stored()
- return jsonify({'success': True, 'message': '已删除'})
- @main_bp.route('/api/send_report', methods=['POST'])
- @login_required
- def send_report_api():
- """手动发送自检报告"""
- try:
- data = request.get_json()
- target_email = data.get('email')
-
- # 执行全量自检
- results = run_all_checks()
-
- # 生成 HTML 报告
- html_report = scheduler_service.format_report_html(results)
-
- # 发送邮件
- subject = "手动触发设备自检报告"
- scheduler_service.send_email(subject, html_report, receivers=target_email)
-
- # 向统一登录平台运维人员推送通知
- summary = scheduler_service._format_summary_text(results)
- send_self_check_notification(summary)
-
- return jsonify({
- "success": True,
- "message": f"自检报告已发送至 {target_email if target_email else '默认邮箱'},并已通知运维人员"
- })
- except Exception as e:
- logger.error(f"发送报告失败: {e}")
- return jsonify({
- "success": False,
- "message": f"发送报告失败: {str(e)}"
- }), 500
- # 提供上传文件的访问接口
- @main_bp.route('/uploads/<filename>')
- def uploaded_file(filename):
- """提供上传文件的访问接口"""
- return send_from_directory(current_app.config['UPLOAD_FOLDER'], filename)
|