部分修复
This commit is contained in:
@@ -19,6 +19,8 @@ from modules.auth import auth_bp
|
||||
from modules.api_60s import api_60s_bp
|
||||
from modules.user_management import user_bp
|
||||
from modules.email_service import init_mail
|
||||
from modules.smallgame import smallgame_bp
|
||||
from modules.aimodelapp import aimodelapp_bp
|
||||
|
||||
from config import Config
|
||||
|
||||
@@ -43,6 +45,8 @@ def create_app():
|
||||
app.register_blueprint(auth_bp, url_prefix='/api/auth')
|
||||
app.register_blueprint(api_60s_bp, url_prefix='/api/60s')
|
||||
app.register_blueprint(user_bp, url_prefix='/api/user')
|
||||
app.register_blueprint(smallgame_bp, url_prefix='/api/smallgame')
|
||||
app.register_blueprint(aimodelapp_bp, url_prefix='/api/aimodelapp')
|
||||
|
||||
# 基础路由
|
||||
@app.route('/')
|
||||
@@ -55,7 +59,9 @@ def create_app():
|
||||
'endpoints': {
|
||||
'auth': '/api/auth',
|
||||
'60s_api': '/api/60s',
|
||||
'user': '/api/user'
|
||||
'user': '/api/user',
|
||||
'smallgame': '/api/smallgame',
|
||||
'aimodelapp': '/api/aimodelapp'
|
||||
}
|
||||
})
|
||||
|
||||
@@ -102,6 +108,60 @@ def create_app():
|
||||
except Exception as e:
|
||||
return jsonify({'error': f'文件服务错误: {str(e)}'}), 500
|
||||
|
||||
# smallgame静态文件服务
|
||||
@app.route('/smallgame/<path:filename>')
|
||||
def serve_smallgame_files(filename):
|
||||
"""提供smallgame目录下的静态文件服务"""
|
||||
try:
|
||||
# 获取项目根目录
|
||||
project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
game_directory = os.path.join(project_root, 'frontend', 'smallgame')
|
||||
|
||||
# 安全检查:确保文件路径在允许的目录内
|
||||
full_path = os.path.join(game_directory, filename)
|
||||
if not os.path.commonpath([game_directory, full_path]) == game_directory:
|
||||
return jsonify({'error': '非法文件路径'}), 403
|
||||
|
||||
# 检查文件是否存在
|
||||
if not os.path.exists(full_path):
|
||||
return jsonify({'error': '文件不存在'}), 404
|
||||
|
||||
# 获取文件目录和文件名
|
||||
directory = os.path.dirname(full_path)
|
||||
file_name = os.path.basename(full_path)
|
||||
|
||||
return send_from_directory(directory, file_name)
|
||||
|
||||
except Exception as e:
|
||||
return jsonify({'error': f'文件服务错误: {str(e)}'}), 500
|
||||
|
||||
# aimodelapp静态文件服务
|
||||
@app.route('/aimodelapp/<path:filename>')
|
||||
def serve_aimodelapp_files(filename):
|
||||
"""提供aimodelapp目录下的静态文件服务"""
|
||||
try:
|
||||
# 获取项目根目录
|
||||
project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
ai_directory = os.path.join(project_root, 'frontend', 'aimodelapp')
|
||||
|
||||
# 安全检查:确保文件路径在允许的目录内
|
||||
full_path = os.path.join(ai_directory, filename)
|
||||
if not os.path.commonpath([ai_directory, full_path]) == ai_directory:
|
||||
return jsonify({'error': '非法文件路径'}), 403
|
||||
|
||||
# 检查文件是否存在
|
||||
if not os.path.exists(full_path):
|
||||
return jsonify({'error': '文件不存在'}), 404
|
||||
|
||||
# 获取文件目录和文件名
|
||||
directory = os.path.dirname(full_path)
|
||||
file_name = os.path.basename(full_path)
|
||||
|
||||
return send_from_directory(directory, file_name)
|
||||
|
||||
except Exception as e:
|
||||
return jsonify({'error': f'文件服务错误: {str(e)}'}), 500
|
||||
|
||||
# 错误处理
|
||||
@app.errorhandler(404)
|
||||
def not_found(error):
|
||||
|
||||
78
backend/modules/aimodelapp.py
Normal file
78
backend/modules/aimodelapp.py
Normal file
@@ -0,0 +1,78 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
AI应用模块 - 提供AI应用静态文件服务和目录扫描
|
||||
Created by: 神奇万事通
|
||||
Date: 2025-09-02
|
||||
"""
|
||||
|
||||
from flask import Blueprint, jsonify
|
||||
import os
|
||||
|
||||
aimodelapp_bp = Blueprint('aimodelapp', __name__)
|
||||
|
||||
@aimodelapp_bp.route('/scan-directories', methods=['GET'])
|
||||
def scan_directories():
|
||||
"""扫描aimodelapp目录结构"""
|
||||
try:
|
||||
# 获取项目根目录
|
||||
project_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
ai_directory = os.path.join(project_root, 'frontend', 'aimodelapp')
|
||||
|
||||
if not os.path.exists(ai_directory):
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'message': 'aimodelapp目录不存在'
|
||||
}), 404
|
||||
|
||||
apps = []
|
||||
|
||||
# 颜色渐变配置
|
||||
gradient_colors = [
|
||||
'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
|
||||
'linear-gradient(135deg, #f093fb 0%, #f5576c 100%)',
|
||||
'linear-gradient(135deg, #4facfe 0%, #00f2fe 100%)',
|
||||
'linear-gradient(135deg, #43e97b 0%, #38f9d7 100%)',
|
||||
'linear-gradient(135deg, #fa709a 0%, #fee140 100%)'
|
||||
]
|
||||
|
||||
# 扫描目录
|
||||
for i, app_name in enumerate(os.listdir(ai_directory)):
|
||||
app_path = os.path.join(ai_directory, app_name)
|
||||
index_path = os.path.join(app_path, 'index.html')
|
||||
|
||||
if os.path.isdir(app_path) and os.path.exists(index_path) and not app_name.endswith('.txt'):
|
||||
# 读取HTML文件获取标题
|
||||
try:
|
||||
with open(index_path, 'r', encoding='utf-8') as f:
|
||||
html_content = f.read()
|
||||
title_match = html_content.find('<title>')
|
||||
if title_match != -1:
|
||||
title_end = html_content.find('</title>', title_match)
|
||||
if title_end != -1:
|
||||
title = html_content[title_match + 7:title_end].strip()
|
||||
else:
|
||||
title = app_name
|
||||
else:
|
||||
title = app_name
|
||||
except:
|
||||
title = app_name
|
||||
|
||||
apps.append({
|
||||
'title': title,
|
||||
'description': f'{app_name}AI应用',
|
||||
'link': f'/aimodelapp/{app_name}/index.html',
|
||||
'status': 'active',
|
||||
'color': gradient_colors[i % len(gradient_colors)]
|
||||
})
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'apps': apps
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'message': f'扫描目录时出错: {str(e)}'
|
||||
}), 500
|
||||
@@ -6,334 +6,15 @@ Created by: 神奇万事通
|
||||
Date: 2025-09-02
|
||||
"""
|
||||
|
||||
from flask import Blueprint, jsonify, request
|
||||
import requests
|
||||
import json
|
||||
from datetime import datetime, timedelta
|
||||
import random
|
||||
import time
|
||||
from flask import Blueprint, jsonify
|
||||
import os
|
||||
|
||||
api_60s_bp = Blueprint('api_60s', __name__)
|
||||
|
||||
# API配置
|
||||
API_ENDPOINTS = {
|
||||
'抖音热搜': {
|
||||
'urls': [
|
||||
'https://api.vvhan.com/api/hotlist?type=douyin',
|
||||
'https://tenapi.cn/v2/douyinhot',
|
||||
'https://api.oioweb.cn/api/common/tebie/dyhot'
|
||||
],
|
||||
'cache_time': 600 # 10分钟缓存
|
||||
},
|
||||
'微博热搜': {
|
||||
'urls': [
|
||||
'https://api.vvhan.com/api/hotlist?type=weibo',
|
||||
'https://tenapi.cn/v2/wbhot',
|
||||
'https://api.oioweb.cn/api/common/tebie/wbhot'
|
||||
],
|
||||
'cache_time': 300 # 5分钟缓存
|
||||
},
|
||||
'猫眼票房': {
|
||||
'urls': [
|
||||
'https://api.vvhan.com/api/hotlist?type=maoyan',
|
||||
'https://tenapi.cn/v2/maoyan'
|
||||
],
|
||||
'cache_time': 3600 # 1小时缓存
|
||||
},
|
||||
'网易云音乐': {
|
||||
'urls': [
|
||||
'https://api.vvhan.com/api/hotlist?type=netease',
|
||||
'https://tenapi.cn/v2/music'
|
||||
],
|
||||
'cache_time': 1800 # 30分钟缓存
|
||||
},
|
||||
'HackerNews': {
|
||||
'urls': [
|
||||
'https://api.vvhan.com/api/hotlist?type=hackernews',
|
||||
'https://hacker-news.firebaseio.com/v0/topstories.json'
|
||||
],
|
||||
'cache_time': 1800 # 30分钟缓存
|
||||
}
|
||||
}
|
||||
|
||||
# 内存缓存
|
||||
cache = {}
|
||||
|
||||
def fetch_data_with_fallback(urls, timeout=10):
|
||||
"""使用备用URL获取数据"""
|
||||
for url in urls:
|
||||
try:
|
||||
response = requests.get(url, timeout=timeout, headers={
|
||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
|
||||
})
|
||||
if response.status_code == 200:
|
||||
return response.json()
|
||||
except Exception as e:
|
||||
print(f"URL {url} 失败: {str(e)}")
|
||||
continue
|
||||
return None
|
||||
|
||||
def get_cached_data(key, cache_time):
|
||||
"""获取缓存数据"""
|
||||
if key in cache:
|
||||
cached_time, data = cache[key]
|
||||
if datetime.now() - cached_time < timedelta(seconds=cache_time):
|
||||
return data
|
||||
return None
|
||||
|
||||
def set_cache_data(key, data):
|
||||
"""设置缓存数据"""
|
||||
cache[key] = (datetime.now(), data)
|
||||
|
||||
@api_60s_bp.route('/douyin', methods=['GET'])
|
||||
def get_douyin_hot():
|
||||
"""获取抖音热搜榜"""
|
||||
try:
|
||||
# 检查缓存
|
||||
cached = get_cached_data('douyin', API_ENDPOINTS['抖音热搜']['cache_time'])
|
||||
if cached:
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'data': cached,
|
||||
'update_time': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
|
||||
'from_cache': True
|
||||
})
|
||||
|
||||
# 获取新数据
|
||||
data = fetch_data_with_fallback(API_ENDPOINTS['抖音热搜']['urls'])
|
||||
|
||||
if data:
|
||||
# 标准化数据格式
|
||||
if 'data' in data:
|
||||
hot_list = data['data']
|
||||
elif isinstance(data, list):
|
||||
hot_list = data
|
||||
else:
|
||||
hot_list = []
|
||||
|
||||
result = {
|
||||
'title': '抖音热搜榜',
|
||||
'subtitle': '实时热门话题 · 紧跟潮流趋势',
|
||||
'update_time': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
|
||||
'total': len(hot_list),
|
||||
'list': hot_list[:50] # 最多返回50条
|
||||
}
|
||||
|
||||
# 设置缓存
|
||||
set_cache_data('douyin', result)
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'data': result,
|
||||
'from_cache': False
|
||||
})
|
||||
else:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'message': '获取数据失败,所有数据源暂时不可用'
|
||||
}), 503
|
||||
|
||||
except Exception as e:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'message': f'服务器错误: {str(e)}'
|
||||
}), 500
|
||||
|
||||
@api_60s_bp.route('/weibo', methods=['GET'])
|
||||
def get_weibo_hot():
|
||||
"""获取微博热搜榜"""
|
||||
try:
|
||||
# 检查缓存
|
||||
cached = get_cached_data('weibo', API_ENDPOINTS['微博热搜']['cache_time'])
|
||||
if cached:
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'data': cached,
|
||||
'update_time': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
|
||||
'from_cache': True
|
||||
})
|
||||
|
||||
# 获取新数据
|
||||
data = fetch_data_with_fallback(API_ENDPOINTS['微博热搜']['urls'])
|
||||
|
||||
if data:
|
||||
if 'data' in data:
|
||||
hot_list = data['data']
|
||||
elif isinstance(data, list):
|
||||
hot_list = data
|
||||
else:
|
||||
hot_list = []
|
||||
|
||||
result = {
|
||||
'title': '微博热搜榜',
|
||||
'subtitle': '热门话题 · 实时更新',
|
||||
'update_time': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
|
||||
'total': len(hot_list),
|
||||
'list': hot_list[:50]
|
||||
}
|
||||
|
||||
set_cache_data('weibo', result)
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'data': result,
|
||||
'from_cache': False
|
||||
})
|
||||
else:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'message': '获取数据失败,所有数据源暂时不可用'
|
||||
}), 503
|
||||
|
||||
except Exception as e:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'message': f'服务器错误: {str(e)}'
|
||||
}), 500
|
||||
|
||||
@api_60s_bp.route('/maoyan', methods=['GET'])
|
||||
def get_maoyan_box_office():
|
||||
"""获取猫眼票房排行榜"""
|
||||
try:
|
||||
cached = get_cached_data('maoyan', API_ENDPOINTS['猫眼票房']['cache_time'])
|
||||
if cached:
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'data': cached,
|
||||
'from_cache': True
|
||||
})
|
||||
|
||||
data = fetch_data_with_fallback(API_ENDPOINTS['猫眼票房']['urls'])
|
||||
|
||||
if data:
|
||||
if 'data' in data:
|
||||
box_office_list = data['data']
|
||||
elif isinstance(data, list):
|
||||
box_office_list = data
|
||||
else:
|
||||
box_office_list = []
|
||||
|
||||
result = {
|
||||
'title': '猫眼票房排行榜',
|
||||
'subtitle': '实时票房数据',
|
||||
'update_time': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
|
||||
'total': len(box_office_list),
|
||||
'list': box_office_list[:20]
|
||||
}
|
||||
|
||||
set_cache_data('maoyan', result)
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'data': result,
|
||||
'from_cache': False
|
||||
})
|
||||
else:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'message': '获取数据失败'
|
||||
}), 503
|
||||
|
||||
except Exception as e:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'message': f'服务器错误: {str(e)}'
|
||||
}), 500
|
||||
|
||||
@api_60s_bp.route('/60s', methods=['GET'])
|
||||
def get_60s_news():
|
||||
"""获取每天60秒读懂世界"""
|
||||
try:
|
||||
urls = [
|
||||
'https://60s-cf.viki.moe',
|
||||
'https://60s.viki.moe',
|
||||
'https://60s.b23.run'
|
||||
]
|
||||
|
||||
data = fetch_data_with_fallback(urls)
|
||||
|
||||
if data:
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'data': {
|
||||
'title': '每天60秒读懂世界',
|
||||
'content': data,
|
||||
'update_time': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||||
}
|
||||
})
|
||||
else:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'message': '获取数据失败'
|
||||
}), 503
|
||||
|
||||
except Exception as e:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'message': f'服务器错误: {str(e)}'
|
||||
}), 500
|
||||
|
||||
@api_60s_bp.route('/bing-wallpaper', methods=['GET'])
|
||||
def get_bing_wallpaper():
|
||||
"""获取必应每日壁纸"""
|
||||
try:
|
||||
url = 'https://api.vvhan.com/api/bing'
|
||||
response = requests.get(url, timeout=10)
|
||||
|
||||
if response.status_code == 200:
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'data': {
|
||||
'title': '必应每日壁纸',
|
||||
'image_url': response.url,
|
||||
'update_time': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||||
}
|
||||
})
|
||||
else:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'message': '获取壁纸失败'
|
||||
}), 503
|
||||
|
||||
except Exception as e:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'message': f'服务器错误: {str(e)}'
|
||||
}), 500
|
||||
|
||||
@api_60s_bp.route('/weather', methods=['GET'])
|
||||
def get_weather():
|
||||
"""获取天气信息"""
|
||||
try:
|
||||
city = request.args.get('city', '北京')
|
||||
url = f'https://api.vvhan.com/api/weather?city={city}'
|
||||
|
||||
response = requests.get(url, timeout=10)
|
||||
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'data': data
|
||||
})
|
||||
else:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'message': '获取天气信息失败'
|
||||
}), 503
|
||||
|
||||
except Exception as e:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'message': f'服务器错误: {str(e)}'
|
||||
}), 500
|
||||
|
||||
@api_60s_bp.route('/scan-directories', methods=['GET'])
|
||||
def scan_directories():
|
||||
"""扫描60sapi目录结构"""
|
||||
try:
|
||||
import os
|
||||
|
||||
# 获取项目根目录
|
||||
project_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
api_directory = os.path.join(project_root, 'frontend', '60sapi')
|
||||
@@ -395,7 +76,7 @@ def scan_directories():
|
||||
apis.append({
|
||||
'title': title,
|
||||
'description': f'{module_name}相关功能',
|
||||
'link': f'/60sapi/{category_name}/{module_name}/index.html',
|
||||
'link': f'http://localhost:5000/60sapi/{category_name}/{module_name}/index.html',
|
||||
'status': 'active',
|
||||
'color': gradient_colors[i % len(gradient_colors)]
|
||||
})
|
||||
|
||||
@@ -317,6 +317,7 @@ def login():
|
||||
session['user_id'] = str(user['_id'])
|
||||
session['email'] = email
|
||||
session['username'] = user.get('用户名', '')
|
||||
session['logged_in'] = True
|
||||
session.permanent = True
|
||||
|
||||
return jsonify({
|
||||
@@ -394,13 +395,14 @@ def logout():
|
||||
def check_login():
|
||||
"""检查登录状态"""
|
||||
try:
|
||||
if session.get('logged_in'):
|
||||
if session.get('logged_in') and session.get('user_id'):
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'logged_in': True,
|
||||
'user': {
|
||||
'account': session.get('account'),
|
||||
'user_id': session.get('user_id')
|
||||
'id': session.get('user_id'),
|
||||
'email': session.get('email'),
|
||||
'username': session.get('username')
|
||||
}
|
||||
}), 200
|
||||
else:
|
||||
|
||||
78
backend/modules/smallgame.py
Normal file
78
backend/modules/smallgame.py
Normal file
@@ -0,0 +1,78 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
小游戏模块 - 提供小游戏静态文件服务和目录扫描
|
||||
Created by: 神奇万事通
|
||||
Date: 2025-09-02
|
||||
"""
|
||||
|
||||
from flask import Blueprint, jsonify
|
||||
import os
|
||||
|
||||
smallgame_bp = Blueprint('smallgame', __name__)
|
||||
|
||||
@smallgame_bp.route('/scan-directories', methods=['GET'])
|
||||
def scan_directories():
|
||||
"""扫描smallgame目录结构"""
|
||||
try:
|
||||
# 获取项目根目录
|
||||
project_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
game_directory = os.path.join(project_root, 'frontend', 'smallgame')
|
||||
|
||||
if not os.path.exists(game_directory):
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'message': 'smallgame目录不存在'
|
||||
}), 404
|
||||
|
||||
games = []
|
||||
|
||||
# 颜色渐变配置
|
||||
gradient_colors = [
|
||||
'linear-gradient(135deg, #ff9a9e 0%, #fecfef 100%)',
|
||||
'linear-gradient(135deg, #a8edea 0%, #fed6e3 100%)',
|
||||
'linear-gradient(135deg, #ffecd2 0%, #fcb69f 100%)',
|
||||
'linear-gradient(135deg, #ff8a80 0%, #ffab91 100%)',
|
||||
'linear-gradient(135deg, #81c784 0%, #aed581 100%)'
|
||||
]
|
||||
|
||||
# 扫描目录
|
||||
for i, game_name in enumerate(os.listdir(game_directory)):
|
||||
game_path = os.path.join(game_directory, game_name)
|
||||
index_path = os.path.join(game_path, 'index.html')
|
||||
|
||||
if os.path.isdir(game_path) and os.path.exists(index_path) and not game_name.endswith('.txt'):
|
||||
# 读取HTML文件获取标题
|
||||
try:
|
||||
with open(index_path, 'r', encoding='utf-8') as f:
|
||||
html_content = f.read()
|
||||
title_match = html_content.find('<title>')
|
||||
if title_match != -1:
|
||||
title_end = html_content.find('</title>', title_match)
|
||||
if title_end != -1:
|
||||
title = html_content[title_match + 7:title_end].strip()
|
||||
else:
|
||||
title = game_name
|
||||
else:
|
||||
title = game_name
|
||||
except:
|
||||
title = game_name
|
||||
|
||||
games.append({
|
||||
'title': title,
|
||||
'description': f'{game_name}小游戏',
|
||||
'link': f'/smallgame/{game_name}/index.html',
|
||||
'status': 'active',
|
||||
'color': gradient_colors[i % len(gradient_colors)]
|
||||
})
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'games': games
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'message': f'扫描目录时出错: {str(e)}'
|
||||
}), 500
|
||||
Reference in New Issue
Block a user