diff --git a/app.py b/app.py
index 117aeba..1ca604f 100644
--- a/app.py
+++ b/app.py
@@ -806,6 +806,180 @@ Sitemap: {}sitemap.xml
response.headers['Content-Type'] = 'text/plain; charset=utf-8'
return response
+ # ========== SEO工具管理路由 (v2.4新增) ==========
+ @app.route('/admin/seo-tools')
+ @login_required
+ def seo_tools():
+ """SEO工具管理页面"""
+ # 检查static/sitemap.xml是否存在及最后更新时间
+ sitemap_path = 'static/sitemap.xml'
+ sitemap_info = None
+ if os.path.exists(sitemap_path):
+ import time
+ mtime = os.path.getmtime(sitemap_path)
+ sitemap_info = {
+ 'exists': True,
+ 'last_updated': datetime.fromtimestamp(mtime).strftime('%Y-%m-%d %H:%M:%S'),
+ 'size': os.path.getsize(sitemap_path)
+ }
+ else:
+ sitemap_info = {'exists': False}
+
+ return render_template('admin/seo_tools.html', sitemap_info=sitemap_info)
+
+ @app.route('/api/generate-static-sitemap', methods=['POST'])
+ @login_required
+ def generate_static_sitemap():
+ """生成静态sitemap.xml文件"""
+ try:
+ # 获取所有启用的网站
+ sites = Site.query.filter_by(is_active=True).order_by(Site.updated_at.desc()).all()
+
+ # 获取所有标签
+ tags = Tag.query.all()
+
+ # 构建XML内容(使用网站配置的域名)
+ base_url = request.url_root.rstrip('/')
+
+ xml_content = '''
+'''
+
+ # 首页
+ xml_content += f'''
+
+ {base_url}
+ daily
+ 1.0
+ '''
+
+ # 工具详情页
+ for site in sites:
+ xml_content += f'''
+
+ {base_url}/site/{site.code}
+ {site.updated_at.strftime('%Y-%m-%d') if site.updated_at else datetime.now().strftime('%Y-%m-%d')}
+ weekly
+ 0.8
+ '''
+
+ # 标签页
+ for tag in tags:
+ xml_content += f'''
+
+ {base_url}/?tag={tag.slug}
+ weekly
+ 0.6
+ '''
+
+ xml_content += '''
+'''
+
+ # 保存到static目录
+ static_dir = 'static'
+ os.makedirs(static_dir, exist_ok=True)
+ sitemap_path = os.path.join(static_dir, 'sitemap.xml')
+
+ with open(sitemap_path, 'w', encoding='utf-8') as f:
+ f.write(xml_content)
+
+ # 统计信息
+ total_urls = 1 + len(sites) + len(tags) # 首页 + 工具页 + 标签页
+
+ return jsonify({
+ 'success': True,
+ 'message': f'静态sitemap.xml生成成功!共包含 {total_urls} 个URL',
+ 'total_urls': total_urls,
+ 'file_path': sitemap_path,
+ 'timestamp': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
+ })
+
+ except Exception as e:
+ return jsonify({
+ 'success': False,
+ 'message': f'生成失败: {str(e)}'
+ }), 500
+
+ @app.route('/api/notify-search-engines', methods=['POST'])
+ @login_required
+ def notify_search_engines():
+ """通知搜索引擎sitemap更新"""
+ try:
+ import requests
+ from urllib.parse import quote
+
+ # 获取sitemap URL(使用当前请求的域名)
+ sitemap_url = request.url_root.rstrip('/') + '/sitemap.xml'
+ encoded_sitemap_url = quote(sitemap_url, safe='')
+
+ results = []
+
+ # 1. 通知Google
+ google_ping_url = f'http://www.google.com/ping?sitemap={encoded_sitemap_url}'
+ try:
+ google_response = requests.get(google_ping_url, timeout=10)
+ results.append({
+ 'engine': 'Google',
+ 'status': 'success' if google_response.status_code == 200 else 'failed',
+ 'status_code': google_response.status_code,
+ 'message': '提交成功' if google_response.status_code == 200 else f'HTTP {google_response.status_code}'
+ })
+ except Exception as e:
+ results.append({
+ 'engine': 'Google',
+ 'status': 'error',
+ 'message': f'请求失败: {str(e)}'
+ })
+
+ # 2. 通知Baidu
+ baidu_ping_url = f'http://data.zz.baidu.com/ping?sitemap={encoded_sitemap_url}'
+ try:
+ baidu_response = requests.get(baidu_ping_url, timeout=10)
+ results.append({
+ 'engine': 'Baidu',
+ 'status': 'success' if baidu_response.status_code == 200 else 'failed',
+ 'status_code': baidu_response.status_code,
+ 'message': '提交成功' if baidu_response.status_code == 200 else f'HTTP {baidu_response.status_code}'
+ })
+ except Exception as e:
+ results.append({
+ 'engine': 'Baidu',
+ 'status': 'error',
+ 'message': f'请求失败: {str(e)}'
+ })
+
+ # 3. 通知Bing
+ bing_ping_url = f'http://www.bing.com/ping?sitemap={encoded_sitemap_url}'
+ try:
+ bing_response = requests.get(bing_ping_url, timeout=10)
+ results.append({
+ 'engine': 'Bing',
+ 'status': 'success' if bing_response.status_code == 200 else 'failed',
+ 'status_code': bing_response.status_code,
+ 'message': '提交成功' if bing_response.status_code == 200 else f'HTTP {bing_response.status_code}'
+ })
+ except Exception as e:
+ results.append({
+ 'engine': 'Bing',
+ 'status': 'error',
+ 'message': f'请求失败: {str(e)}'
+ })
+
+ # 统计成功数量
+ success_count = sum(1 for r in results if r['status'] == 'success')
+
+ return jsonify({
+ 'success': True,
+ 'message': f'已通知 {success_count}/{len(results)} 个搜索引擎',
+ 'sitemap_url': sitemap_url,
+ 'results': results
+ })
+
+ except Exception as e:
+ return jsonify({
+ 'success': False,
+ 'message': f'通知失败: {str(e)}'
+ }), 500
+
@app.route('/api/refresh-site-news/', methods=['POST'])
def refresh_site_news(site_code):
"""手动刷新指定网站的新闻(前台用户可访问)- v2.3新增"""
diff --git a/templates/admin/master.html b/templates/admin/master.html
index 66c5a87..9f0c081 100644
--- a/templates/admin/master.html
+++ b/templates/admin/master.html
@@ -79,6 +79,12 @@