235 lines
7.9 KiB
Python
235 lines
7.9 KiB
Python
"""
|
||
主页面蓝图:首页、home、图片工作台、品牌页、静态文件、Logo
|
||
"""
|
||
import os
|
||
from flask import Blueprint, send_file, render_template_string
|
||
|
||
from app_common import (
|
||
get_db,
|
||
_render_html,
|
||
_is_session_user_valid,
|
||
login_required,
|
||
admin_required,
|
||
STATIC_DIR,
|
||
BASE_DIR,
|
||
ASSETS_DIR
|
||
)
|
||
from flask import redirect, url_for, session
|
||
from flask import Flask, request, Response, stream_with_context
|
||
import requests
|
||
|
||
from config import base_url,version,JAVA_API_BASE
|
||
|
||
main_bp = Blueprint('main', __name__)
|
||
|
||
|
||
def _resolve_asset_filename(filename):
|
||
"""Resolve hashed Vite assets without maintaining hardcoded alias tables."""
|
||
safe_name = os.path.basename(filename)
|
||
exact_path = os.path.join(ASSETS_DIR, safe_name)
|
||
if os.path.isfile(exact_path):
|
||
return safe_name
|
||
|
||
base_name, ext = os.path.splitext(safe_name)
|
||
if not base_name or not ext:
|
||
return safe_name
|
||
|
||
parts = base_name.split('-')
|
||
if len(parts) < 2:
|
||
return safe_name
|
||
|
||
try:
|
||
asset_names = os.listdir(ASSETS_DIR)
|
||
except OSError:
|
||
return safe_name
|
||
|
||
for prefix_length in range(len(parts) - 1, 0, -1):
|
||
prefix = '-'.join(parts[:prefix_length])
|
||
matches = [
|
||
asset_name for asset_name in asset_names
|
||
if asset_name.endswith(ext) and asset_name.startswith(prefix)
|
||
]
|
||
if len(matches) == 1:
|
||
return matches[0]
|
||
|
||
return safe_name
|
||
|
||
|
||
@main_bp.route('/')
|
||
def index():
|
||
if session.get('user_id') and _is_session_user_valid():
|
||
return redirect(url_for('main.home'))
|
||
return redirect(url_for('auth.login'))
|
||
|
||
|
||
@main_bp.route('/home')
|
||
@login_required
|
||
def home():
|
||
try:
|
||
conn = get_db()
|
||
with conn.cursor() as cur:
|
||
cur.execute("SELECT username, is_admin FROM users WHERE id = %s", (session['user_id'],))
|
||
row = cur.fetchone()
|
||
conn.close()
|
||
return _render_html('home.html', username=row.get('username', ''), is_admin=bool(row.get('is_admin')), user_id=session.get('user_id'),baseUrl=base_url,version=version)
|
||
except Exception:
|
||
return _render_html('home.html', username=session.get('username', ''), is_admin=False, user_id=session.get('user_id'),baseUrl=base_url,version=version)
|
||
|
||
|
||
@main_bp.route('/image')
|
||
@login_required
|
||
def wb():
|
||
return _render_html('index.html')
|
||
|
||
|
||
@main_bp.route('/brand')
|
||
@login_required
|
||
def brand_page():
|
||
repo_brand_path = os.path.abspath(os.path.join(BASE_DIR, '..', 'web_source', 'brand.html'))
|
||
if os.path.isfile(repo_brand_path):
|
||
try:
|
||
with open(repo_brand_path, 'rb') as f:
|
||
raw = f.read()
|
||
try:
|
||
from html_crypto import decrypt
|
||
content = decrypt(raw).decode('utf-8')
|
||
except Exception:
|
||
if raw[:7] == b'gAAAAAB':
|
||
raise
|
||
content = raw.decode('utf-8', errors='replace')
|
||
return render_template_string(content, user_id=session.get('user_id'))
|
||
except Exception:
|
||
pass
|
||
return _render_html('brand.html', user_id=session.get('user_id'))
|
||
|
||
|
||
@main_bp.route('/brand/legacy')
|
||
@login_required
|
||
def brand_page_legacy():
|
||
legacy_path = os.path.join(BASE_DIR, 'web_source', 'templates_backup', 'brand.html')
|
||
if not os.path.isfile(legacy_path):
|
||
return '', 404
|
||
with open(legacy_path, 'rb') as f:
|
||
content = f.read().decode('utf-8', errors='replace')
|
||
content = content.replace(
|
||
'<header class="top-bar">',
|
||
'<header class="top-bar" style="display:none !important;">',
|
||
1,
|
||
)
|
||
content = content.replace('height: calc(100vh - 56px);', 'height: 100vh;', 1)
|
||
return render_template_string(content, user_id=session.get('user_id'))
|
||
|
||
|
||
|
||
|
||
@main_bp.route('/static/<path:filename>')
|
||
def serve_static(filename):
|
||
"""提供 static 目录及子目录下的静态文件访问。"""
|
||
filepath = os.path.normpath(os.path.join(STATIC_DIR, filename))
|
||
static_abs = os.path.abspath(STATIC_DIR)
|
||
file_abs = os.path.abspath(filepath)
|
||
if not file_abs.startswith(static_abs) or not os.path.isfile(file_abs):
|
||
return '', 404
|
||
return send_file(file_abs, as_attachment=False)
|
||
|
||
|
||
@main_bp.route('/assets/<path:filename>')
|
||
def serve_assets(filename):
|
||
"""提供 static 目录及子目录下的静态文件访问。"""
|
||
filename = _resolve_asset_filename(filename)
|
||
filepath = os.path.normpath(os.path.join(ASSETS_DIR, filename))
|
||
static_abs = os.path.abspath(ASSETS_DIR)
|
||
file_abs = os.path.abspath(filepath)
|
||
if not file_abs.startswith(static_abs) or not os.path.isfile(file_abs):
|
||
return '', 404
|
||
return send_file(file_abs, as_attachment=False)
|
||
|
||
|
||
@main_bp.route('/new_web_source/<path:filename>')
|
||
def serve_new_web_source(filename):
|
||
"""提供 new_web_source 目录下的静态页面文件访问。"""
|
||
candidate_dirs = [
|
||
os.path.abspath(os.path.join(BASE_DIR, 'new_web_source')),
|
||
os.path.abspath(os.path.join(BASE_DIR, '..', 'new_web_source')),
|
||
]
|
||
for static_abs in candidate_dirs:
|
||
filepath = os.path.normpath(os.path.join(static_abs, filename))
|
||
file_abs = os.path.abspath(filepath)
|
||
if not file_abs.startswith(static_abs):
|
||
continue
|
||
if os.path.isfile(file_abs):
|
||
return send_file(file_abs, as_attachment=False)
|
||
return '', 404
|
||
|
||
|
||
@main_bp.route('/logo.jpg', methods=['GET'])
|
||
def get_logo_image():
|
||
return send_file(os.path.join(BASE_DIR, "logo.jpg"), mimetype='image/jpeg')
|
||
|
||
|
||
@main_bp.route('/newApi/<path:path>', methods=['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'])
|
||
def proxy(path):
|
||
target_url = f"{JAVA_API_BASE}/{path}"
|
||
|
||
# 复制请求参数
|
||
params = request.args.to_dict()
|
||
|
||
# 处理请求头
|
||
headers = {}
|
||
for key, value in request.headers:
|
||
if key.lower() in ['host', 'content-length', 'connection']:
|
||
continue
|
||
headers[key] = value
|
||
ignore_url = [f"{JAVA_API_BASE}/api/delete-brand/tasks/batch",
|
||
f"{JAVA_API_BASE}/api/delete-brand/history",
|
||
f"{JAVA_API_BASE}/api/product-risk-resolve/tasks/batch",
|
||
]
|
||
if target_url not in ignore_url:
|
||
try:
|
||
print("=============================")
|
||
print("target_url:",target_url)
|
||
print("params:",params)
|
||
print("data:",request.get_data())
|
||
print("=============================")
|
||
except Exception as e:
|
||
print("打印失败",e)
|
||
|
||
try:
|
||
# 使用流式请求
|
||
req = requests.request(
|
||
method=request.method,
|
||
url=target_url,
|
||
params=params,
|
||
headers=headers,
|
||
data=request.get_data() if request.get_data() else None,
|
||
cookies=request.cookies,
|
||
stream=True, # 启用流式传输
|
||
timeout=30
|
||
)
|
||
|
||
# 流式响应
|
||
def generate():
|
||
for chunk in req.iter_content(chunk_size=8192):
|
||
if chunk:
|
||
yield chunk
|
||
|
||
# 构建响应
|
||
response = Response(stream_with_context(generate()), status=req.status_code)
|
||
|
||
# 复制响应头
|
||
for key, value in req.headers.items():
|
||
if key.lower() not in ['content-encoding', 'content-length', 'transfer-encoding', 'connection']:
|
||
response.headers[key] = value
|
||
|
||
return response
|
||
|
||
except requests.exceptions.Timeout:
|
||
print("后端服务超时")
|
||
return Response("后端服务超时", status=504)
|
||
except requests.exceptions.ConnectionError:
|
||
print("无法连接到后端服务")
|
||
return Response("无法连接到后端服务", status=502)
|
||
except Exception as e:
|
||
print(f"代理请求失败: {e}")
|
||
return Response(f"代理错误: {str(e)}", status=500)
|