Files
mengyaprofile/mengyaprofile-backend/app.py

258 lines
9.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from flask import Flask, jsonify, send_from_directory, request
from flask_cors import CORS
import json
import os
import random
# 检测运行模式:通过环境变量控制
RUN_MODE = os.environ.get('RUN_MODE', 'development') # development 或 production
# 数据文件路径 - 支持环境变量配置(需要先定义,因为后面会用到)
DATA_DIR = os.environ.get('DATA_DIR', os.path.join(os.path.dirname(__file__), 'data'))
# 根据运行模式配置
# 检查是否有前端构建文件(前后端分离时可能没有)
FRONTEND_BUILD_PATH = os.path.join(os.path.dirname(__file__), '..', 'frontend', 'build')
HAS_FRONTEND_BUILD = os.path.exists(FRONTEND_BUILD_PATH) and os.path.isdir(FRONTEND_BUILD_PATH)
# 背景图片目录 - 固定使用数据目录中的 background 文件夹
# 支持通过环境变量配置,默认在数据目录中
BACKGROUND_DIR = os.environ.get('BACKGROUND_DIR', os.path.join(DATA_DIR, 'background'))
if RUN_MODE == 'production' and HAS_FRONTEND_BUILD:
# 生产环境:使用构建后的前端(如果存在)
app = Flask(__name__, static_folder=FRONTEND_BUILD_PATH, static_url_path='')
else:
# 开发环境或纯后端模式:只提供 API
app = Flask(__name__)
CORS(app) # 允许跨域请求
def load_json_file(filename):
"""加载JSON文件"""
try:
with open(os.path.join(DATA_DIR, filename), 'r', encoding='utf-8') as f:
return json.load(f)
except FileNotFoundError:
return None
except Exception as e:
print(f"Error loading {filename}: {e}")
return None
@app.route('/api/profile', methods=['GET'])
def get_profile():
"""获取个人基本信息"""
data = load_json_file('profile.json')
if data:
return jsonify(data)
return jsonify({"error": "Profile没有找到"}), 404
@app.route('/api/projects', methods=['GET'])
def get_projects():
"""获取全部项目列表"""
data = load_json_file('projects.json')
if data:
return jsonify(data)
return jsonify({"error": "Projects没有找到"}), 404
@app.route('/api/contacts', methods=['GET'])
def get_contacts():
"""获取联系方式"""
data = load_json_file('contacts.json')
if data:
return jsonify(data)
return jsonify({"error": "Contacts没有找到"}), 404
@app.route('/api/techstack', methods=['GET'])
def get_techstack():
"""获取技术栈"""
data = load_json_file('techstack.json')
if data:
return jsonify(data)
return jsonify({"error": "Tech stack没有找到"}), 404
@app.route('/api/logo/<filename>', methods=['GET'])
def get_logo(filename):
"""提供技术栈图标文件"""
logo_dir = os.path.join(DATA_DIR, 'logo')
try:
# 安全检查:防止路径遍历攻击
if '..' in filename or '/' in filename or '\\' in filename:
return jsonify({"error": "无效的文件名"}), 400
# 检查文件是否存在
file_path = os.path.join(logo_dir, filename)
if not os.path.exists(file_path):
print(f"图标文件不存在: {file_path}")
return jsonify({"error": f"图标文件未找到: {filename}"}), 404
# 检查目录是否存在
if not os.path.exists(logo_dir):
print(f"图标目录不存在: {logo_dir}")
return jsonify({"error": "图标目录未找到"}), 404
return send_from_directory(logo_dir, filename)
except Exception as e:
print(f"获取图标文件出错: {e}")
print(f"尝试访问的文件: {os.path.join(logo_dir, filename)}")
return jsonify({"error": f"图标文件未找到: {filename}"}), 404
@app.route('/api/random-background', methods=['GET'])
def get_random_background():
"""获取随机背景图片"""
try:
# 获取背景图片目录中的所有图片
if os.path.exists(BACKGROUND_DIR) and os.path.isdir(BACKGROUND_DIR):
images = [f for f in os.listdir(BACKGROUND_DIR)
if f.lower().endswith(('.png', '.jpg', '.jpeg', '.webp', '.gif'))]
if images:
random_image = random.choice(images)
# 返回完整的 API 路径
return jsonify({"image": f"/api/background/{random_image}"})
else:
print(f"背景图片目录不存在: {BACKGROUND_DIR}")
return jsonify({"image": None})
except Exception as e:
print(f"获取随机背景出错: {e}")
print(f"背景目录路径: {BACKGROUND_DIR}")
import traceback
traceback.print_exc()
return jsonify({"image": None})
@app.route('/api/background/<filename>', methods=['GET'])
def get_background_image(filename):
"""提供背景图片文件"""
try:
# 安全检查:防止路径遍历攻击
if '..' in filename or '/' in filename or '\\' in filename:
return jsonify({"error": "无效的文件名"}), 400
# 检查目录是否存在
if not os.path.exists(BACKGROUND_DIR):
print(f"背景图片目录不存在: {BACKGROUND_DIR}")
return jsonify({"error": "背景图片目录未找到"}), 404
# 检查文件是否存在
file_path = os.path.join(BACKGROUND_DIR, filename)
if not os.path.exists(file_path):
print(f"背景图片文件不存在: {file_path}")
return jsonify({"error": f"背景图片未找到: {filename}"}), 404
return send_from_directory(BACKGROUND_DIR, filename)
except Exception as e:
print(f"获取背景图片出错: {e}")
return jsonify({"error": f"背景图片未找到: {filename}"}), 404
@app.route('/api/all', methods=['GET'])
def get_all():
"""获取所有数据"""
profile = load_json_file('profile.json')
projects = load_json_file('projects.json')
contacts = load_json_file('contacts.json')
techstack = load_json_file('techstack.json')
return jsonify({
"profile": profile,
"techstack": techstack,
"projects": projects,
"contacts": contacts
})
@app.route('/', methods=['GET'])
def index():
"""服务前端页面或API信息"""
if RUN_MODE == 'production' and app.static_folder and os.path.exists(os.path.join(app.static_folder, 'index.html')):
# 生产环境,返回前端页面(如果存在)
try:
return send_from_directory(app.static_folder, 'index.html')
except:
pass
# 返回API信息
return jsonify({
"message": "萌芽主页 后端API",
"author": "树萌芽",
"version": "1.0.0",
"mode": RUN_MODE,
"note": "这是一个纯后端API服务前端请访问独立的前端应用",
"api_base": "https://nav.api.shumengya.top/api",
"endpoints": {
"/api/profile": "获取个人信息",
"/api/techstack": "获取技术栈",
"/api/projects": "获取项目列表",
"/api/contacts": "获取联系方式",
"/api/random-background": "获取随机背景图片",
"/api/all": "获取所有数据"
}
})
@app.route('/admin')
def admin():
"""服务管理员页面(也是前端)"""
if RUN_MODE == 'production' and app.static_folder and os.path.exists(os.path.join(app.static_folder, 'index.html')):
try:
return send_from_directory(app.static_folder, 'index.html')
except:
pass
return jsonify({
"error": "管理员页面未找到",
"note": "这是一个纯后端API服务请访问独立的前端应用",
"api_base": "https://nav.api.shumengya.top/api"
}), 404
@app.route('/api')
def api_info():
"""API信息"""
return jsonify({
"message": "萌芽主页 后端API",
"author":"树萌芽",
"version": "1.0.0",
"endpoints": {
"/api/profile": "获取个人信息",
"/api/techstack": "获取技术栈",
"/api/projects": "获取项目列表",
"/api/contacts": "获取联系方式",
"/api/random-background": "获取随机背景图片",
"/api/all": "获取所有数据"
}
})
# 处理404错误
@app.errorhandler(404)
def not_found(e):
"""处理404错误"""
# 检查是否为API请求
if request.path.startswith('/api'):
return jsonify({"error": "API endpoint not found"}), 404
# 非API请求 - 如果是前后端分离返回API信息
if RUN_MODE == 'production' and app.static_folder and os.path.exists(os.path.join(app.static_folder, 'index.html')):
# 如果有前端构建文件,尝试返回
try:
return send_from_directory(app.static_folder, 'index.html')
except:
pass
# 返回API信息
return jsonify({
"error": "页面未找到",
"message": "这是一个纯后端API服务",
"api_base": "https://nav.api.shumengya.top/api",
"endpoints": {
"/api/profile": "获取个人信息",
"/api/techstack": "获取技术栈",
"/api/projects": "获取项目列表",
"/api/contacts": "获取联系方式",
"/api/random-background": "获取随机背景图片",
"/api/all": "获取所有数据"
}
}), 404
if __name__ == '__main__':
# 从环境变量获取端口,默认为 5000
port = int(os.environ.get('PORT', 5000))
# 生产环境关闭 debug 模式
debug_mode = RUN_MODE != 'production'
app.run(debug=debug_mode, host='0.0.0.0', port=port)