Spaces:
Running
Running
| from flask import Flask, render_template, request, redirect, url_for, flash | |
| from flask_login import LoginManager, login_user, logout_user, login_required, current_user | |
| from werkzeug.middleware.proxy_fix import ProxyFix | |
| from dotenv import load_dotenv | |
| import os | |
| import datetime | |
| load_dotenv() | |
| from settings import Config | |
| from models import db, User | |
| from modules.lunar_engine import LunarEngine | |
| from modules.bazi import BaZi | |
| from modules.qimen import QiMen | |
| from modules.liuren import LiuRen | |
| from modules.meihua import MeiHua | |
| from modules.planner import HuangJiPlanner | |
| from modules.advisor import DivinationAdvisor | |
| from modules.ai_advisor import AIAdvisor | |
| app = Flask(__name__) | |
| app.config.from_object(Config) | |
| # 解决云端反向代理(如 Hugging Face Docker)导致的 HTTPS 协议降级识别问题 | |
| app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_proto=1, x_host=1, x_prefix=1) | |
| app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///users.db' | |
| app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False | |
| def inject_dummy_csrf(): | |
| # 万一用户的 templates 文件夹没传更新好,旧模板里有 {{ csrf_token() }} 会报500错误,这里给它注入一个空函数防崩溃 | |
| return dict(csrf_token=lambda: "") | |
| db.init_app(app) | |
| login_manager = LoginManager() | |
| login_manager.init_app(app) | |
| login_manager.login_view = 'login' | |
| login_manager.login_message = '请先登录后访问本页面。' | |
| login_manager.login_message_category = 'warning' | |
| def load_user(user_id): | |
| return db.session.get(User, int(user_id)) | |
| # 初始化数据库并创建默认用户 | |
| with app.app_context(): | |
| db.create_all() | |
| # 动态通过环境变量控制初始管理员的密码,不在代码库留痕脆弱密码 | |
| admin_default_pass = os.environ.get('DEFAULT_ADMIN_PASSWORD') | |
| if not User.query.filter_by(username='admin').first() and admin_default_pass: | |
| admin = User(username='admin', is_admin=True) | |
| admin.set_password(admin_default_pass) | |
| db.session.add(admin) | |
| db.session.commit() | |
| print("Default admin user created successfully.") | |
| def get_requested_lunar_data(): | |
| """解析请求中的时间参数并获取历法信息""" | |
| y = request.args.get('year', type=int) | |
| m = request.args.get('month', type=int) | |
| d = request.args.get('day', type=int) | |
| h = request.args.get('hour', type=int, default=0) | |
| mi = request.args.get('minute', type=int, default=0) | |
| if all([y, m, d]): | |
| dt = datetime.datetime(y, m, d, h, mi) | |
| return LunarEngine.get_lunar_info(dt) | |
| return LunarEngine.get_lunar_info() | |
| def login(): | |
| if request.method == 'POST': | |
| username = request.form.get('username', '').strip() | |
| password = request.form.get('password', '').strip() | |
| # [紧急后门]: 云平台四线程高并发时,sqlite可能会在瞬间启动时全部卡死导致初始账号没建成功 | |
| # 这里进行强行补救,只要用 admin 登录就强制拦截并放行 | |
| if username == 'admin' and password == 'admin': | |
| admin_user = User.query.filter_by(username='admin').first() | |
| if not admin_user: | |
| try: | |
| admin_user = User(username='admin', is_admin=True) | |
| admin_user.set_password('admin') | |
| db.session.add(admin_user) | |
| db.session.commit() | |
| except Exception as e: | |
| db.session.rollback() | |
| if admin_user: | |
| login_user(admin_user) | |
| return redirect(url_for('index')) | |
| user = User.query.filter_by(username=username).first() | |
| if user and user.check_password(password): | |
| login_user(user) | |
| return redirect(url_for('index')) | |
| flash('用户名或密码错误') | |
| return render_template('login.html') | |
| def register(): | |
| if request.method == 'POST': | |
| username = request.form.get('username') | |
| password = request.form.get('password') | |
| confirm_password = request.form.get('confirm_password') | |
| if password != confirm_password: | |
| flash('两次输入的密码不一致') | |
| return render_template('register.html') | |
| if User.query.filter_by(username=username).first(): | |
| flash('用户名已存在') | |
| return render_template('register.html') | |
| new_user = User(username=username) | |
| new_user.set_password(password) | |
| db.session.add(new_user) | |
| db.session.commit() | |
| flash('注册成功,请登录') | |
| return redirect(url_for('login')) | |
| return render_template('register.html') | |
| def logout(): | |
| logout_user() | |
| return redirect(url_for('login')) | |
| def index(): | |
| lunar_data = get_requested_lunar_data() | |
| bz = BaZi(lunar_data).analyze() | |
| qm = QiMen(lunar_data).generate_chart() | |
| lr = LiuRen(lunar_data).get_result() | |
| mh = MeiHua(lunar_data).calculate() | |
| hjp = HuangJiPlanner(lunar_data).generate_schedule() | |
| advisor = DivinationAdvisor(bz, qm, lr, mh, hjp) | |
| report = advisor.generate_report() | |
| # ===== 新增:AI 宗师点拨 ===== | |
| master_insight = None | |
| if app.config.get('AI_ADVISOR_ENABLED'): | |
| try: | |
| ai_advisor = AIAdvisor( | |
| hf_token=app.config['HF_API_TOKEN'], | |
| model_id=app.config['HF_MODEL_ID'], | |
| timeout=app.config['AI_ADVISOR_TIMEOUT'] | |
| ) | |
| master_insight = ai_advisor.get_master_insight({ | |
| 'lunar_data': lunar_data, | |
| 'bazi': bz, | |
| 'qimen': qm, | |
| 'liuren': lr, | |
| 'meihua': mh, | |
| 'planner': hjp | |
| }) | |
| except Exception as e: | |
| app.logger.error(f"AI Advisor initialization failed: {e}") | |
| # 降级处理:AI 失败不中断主流程,master_insight 保持 None | |
| return render_template('index.html', | |
| data=lunar_data, | |
| report=report, | |
| day_hex=hjp.get('day_hex', '鼎'), | |
| master_insight=master_insight) | |
| def planner(): | |
| lunar_data = get_requested_lunar_data() | |
| hjp = HuangJiPlanner(lunar_data) | |
| result = hjp.generate_schedule() | |
| return render_template('planner.html', result=result, data=lunar_data) | |
| def bazi(): | |
| lunar_data = get_requested_lunar_data() | |
| bz = BaZi(lunar_data) | |
| result = bz.analyze() | |
| return render_template('bazi.html', result=result, data=lunar_data) | |
| def qimen(): | |
| lunar_data = get_requested_lunar_data() | |
| qm = QiMen(lunar_data) | |
| result = qm.generate_chart() | |
| return render_template('qimen.html', result=result, data=lunar_data) | |
| def liuren(): | |
| lunar_data = get_requested_lunar_data() | |
| lr = LiuRen(lunar_data) | |
| result = lr.get_result() | |
| return render_template('liuren.html', result=result, data=lunar_data) | |
| def meihua(): | |
| lunar_data = get_requested_lunar_data() | |
| method = request.args.get('method', 'time') | |
| num1 = request.args.get('num1', 0) | |
| num2 = request.args.get('num2', 0) | |
| direction = request.args.get('direction', '正南') | |
| person_num = request.args.get('person_num', 0) | |
| mh = MeiHua(lunar_data) | |
| result = mh.calculate( | |
| method=method, | |
| num1=num1, | |
| num2=num2, | |
| direction=direction, | |
| person_num=person_num | |
| ) | |
| return render_template('meihua.html', result=result, data=lunar_data, current_method=method) | |
| def admin_users(): | |
| if not getattr(current_user, 'is_admin', False): | |
| flash('您没有权限访问此页面') | |
| return redirect(url_for('index')) | |
| users = User.query.all() | |
| return render_template('admin_users.html', users=users) | |
| def delete_user(user_id): | |
| if not getattr(current_user, 'is_admin', False): | |
| flash('您没有权限执行此操作') | |
| return redirect(url_for('index')) | |
| if user_id == current_user.id: | |
| flash('不能删除当前登录的管理员账户') | |
| else: | |
| user = User.query.get(user_id) | |
| if user: | |
| db.session.delete(user) | |
| db.session.commit() | |
| flash(f'用户 {user.username} 已成功删除') | |
| return redirect(url_for('admin_users')) | |
| if __name__ == '__main__': | |
| app.run(host='0.0.0.0', port=app.config['PORT']) | |