import os from flask import Flask, render_template, session, redirect, url_for, send_from_directory from database.manager import DatabaseManager from auth import init_auth_routes, login_required from config import SECRET_KEY, DATA_DIR, LOG_DIR, EXCEL_EXPORT_DIR # --- 1. 初始化路徑與 Flask --- BASE_DIR = os.path.abspath(os.path.dirname(__file__)) TEMPLATE_DIR = os.path.join(BASE_DIR, 'web', 'templates') STATIC_DIR = os.path.join(BASE_DIR, 'web', 'static') app = Flask(__name__, template_folder=TEMPLATE_DIR, static_folder=STATIC_DIR) app.secret_key = SECRET_KEY # 初始化權限路由 init_auth_routes(app) # 初始化資料庫管理員 db_mgr = DatabaseManager() def get_db_size(): """獲取資料庫實體檔案大小""" db_path = os.path.join(DATA_DIR, 'momo_database.db') if os.path.exists(db_path): size_bytes = os.path.getsize(db_path) return f"{size_bytes / (1024 * 1024):.2f} MB" return "0.00 MB" # --- 2. 路由定義 --- @app.route('/') @login_required def dashboard(): """主看板:包含統計數據與商品明細清單""" stats = { "total_products": 0, "price_changes": 0, "db_size": get_db_size() } recent_products = [] session_db = db_mgr.Session() try: from database.models import Product, PriceRecord from datetime import datetime from sqlalchemy import desc # A. 計算統計數據 stats["total_products"] = session_db.query(Product).count() today_start = datetime.now().replace(hour=0, minute=0, second=0, microsecond=0) stats["price_changes"] = session_db.query(PriceRecord).filter( PriceRecord.timestamp >= today_start ).count() # B. 抓取最新 50 筆商品明細 # 邏輯:抓取 Product,並針對每個 Product 找出最後一筆價格 products_query = session_db.query(Product).order_by(Product.id.desc()).limit(50).all() for p in products_query: # 取得該商品在 PriceRecord 表中最新的一筆紀錄 latest_price_rec = session_db.query(PriceRecord)\ .filter_by(product_id=p.id)\ .order_by(desc(PriceRecord.timestamp))\ .first() recent_products.append({ "category": p.category, "name": p.name, "price": f"{latest_price_rec.price:,.0f}" if latest_price_rec else "N/A", "url": p.url, "time": latest_price_rec.timestamp.strftime('%m/%d %H:%M') if latest_price_rec else "-" }) except Exception as e: app.logger.error(f"❌ 看板數據讀取失敗: {e}") finally: session_db.close() return render_template('dashboard.html', stats=stats, products=recent_products) @app.route('/logs') @login_required def view_logs(): """日誌查看頁面""" log_path = os.path.join(LOG_DIR, 'system.log') content = "⚠️ 尚無日誌紀錄。" if os.path.exists(log_path): try: with open(log_path, 'r', encoding='utf-8') as f: f.seek(0, os.SEEK_END) size = f.tell() f.seek(max(0, size - 15000)) # 讀取最後 1.5 萬字元 content = f.read() except Exception as e: content = f"讀取日誌出錯: {e}" return render_template('logs.html', log_content=content) @app.route('/download/') @login_required def download_excel(filename): """Excel 報表下載""" return send_from_directory(EXCEL_EXPORT_DIR, filename) # --- 3. 啟動自我檢查 --- if __name__ == '__main__': print("\n" + "="*50) print("🚀 MOMO 伺服器啟動中...") print(f"📂 模板目錄: {TEMPLATE_DIR}") # 檢查核心檔案 for f in ['dashboard.html', 'login.html']: path = os.path.join(TEMPLATE_DIR, f) status = "✅ 存在" if os.path.exists(path) else "❌ 缺失" size = f"({os.path.getsize(path)} bytes)" if os.path.exists(path) else "" print(f" - {f}: {status} {size}") print("="*50 + "\n") app.run(host='0.0.0.0', port=5888, debug=True)