chore(cleanup): 移除 legacy 5888 測試入口
All checks were successful
CD Pipeline / deploy (push) Successful in 1m36s

This commit is contained in:
OoO
2026-04-30 14:12:21 +08:00
parent c2e38be43d
commit 19535a0763
12 changed files with 41 additions and 136 deletions

View File

@@ -72,7 +72,7 @@ pip install -r requirements.txt
└── 當日業績/
```
2. 開啟網頁介面http://localhost:5888/auto_import
2. 開啟網頁介面http://localhost/auto_import
3. 設定:
- **Google Drive 資料夾路徑**`業績報表/當日業績`
@@ -118,7 +118,7 @@ python3 test_google_drive.py
### 監控進度
開啟http://localhost:5888/auto_import
開啟http://localhost/auto_import
可以看到:
- ✅ 匯入配置

View File

@@ -2,7 +2,7 @@
> 本文件定義專案開發的核心準則與不可違反的規範
> **建立日期**: 2026-01-12
> **當前版本**: V10.21 (模組化治理守門版)
> **當前版本**: V10.22 (Legacy 5888 入口清理版)
> **最後更新**: 2026-04-30
---

View File

@@ -126,7 +126,7 @@ drive_service.authenticate()
### 5.2 在系統中配置路徑
1. 開啟瀏覽器前往http://localhost:5888/auto_import
1. 開啟瀏覽器前往http://localhost/auto_import
2. 在「匯入配置」區域設定:
- **Google Drive 資料夾路徑**`業績報表/當日業績`
- **檔案名稱模式**`即時業績_當日`(選填,用於過濾檔案)
@@ -268,7 +268,7 @@ tail -f logs/system.log | grep AutoImport
### 查看匯入統計
前往網頁介面http://localhost:5888/auto_import
前往網頁介面http://localhost/auto_import
### 查詢排程統計

View File

@@ -30,6 +30,7 @@
- AI metrics baseline 觀測:`/metrics` 在尚無 AI 自動化事件時仍輸出 `momo_ai_*` zero-baseline series避免重啟後 Grafana/Prometheus 看不到 metric names。
- ElephantAlpha transient fallbackNVIDIA NIM timeout、connection error、429 與 5xx 會嘗試下一個 fallback model400 等非暫時性請求錯誤不重試。
- 模組化治理守門:新增 `docs/guides/modularization_governance.md`、`docs/memory/code_modularization_inventory_20260430.md` 與 `tests/test_modularization_governance.py`,盤點並鎖住 15 個 >800 行 Python 大檔。
- Legacy 5888 入口清理:刪除 `tests/main_test.py` standalone Flask 死碼,測試與自動匯入文件改用 Port 80 入口。
【下次待辦】
- 依 inventory 優先拆 `routes/openclaw_bot_routes.py`、`routes/sales_routes.py`、`scheduler.py`。

6
app.py
View File

@@ -26,7 +26,7 @@ sys.path.insert(0, BASE_DIR)
# 自動檢核並建立必要目錄
try:
for folder in ['database', 'services', 'crawler', 'logs', 'data', 'web/templates', 'web/static']:
for folder in ['database', 'services', 'crawler', 'logs', 'data', 'templates', 'web/static']:
folder_path = os.path.join(BASE_DIR, folder)
if not os.path.exists(folder_path):
os.makedirs(folder_path)
@@ -95,8 +95,8 @@ except Exception as e:
sys_log.error(f"無法檢測磁碟空間: {e}")
# 🚩 系統版本定義 (備份與顯示用)
# 🚩 2026-04-30 V10.21: Modularization governance guardrail
SYSTEM_VERSION = "V10.21"
# 🚩 2026-04-30 V10.22: Legacy standalone test and port cleanup
SYSTEM_VERSION = "V10.22"
# ==========================================
# 🔒 SQL Injection 防護函數

View File

@@ -254,7 +254,7 @@ YOUTUBE_API_KEY = os.getenv('YOUTUBE_API_KEY', '')
# ==========================================
# 系統版本與路徑
# ==========================================
SYSTEM_VERSION = "V10.21"
SYSTEM_VERSION = "V10.22"
LOG_FILE_PATH = os.path.join(BASE_DIR, 'logs/system.log')
public_url = PUBLIC_URL # 用於模板顯示

View File

@@ -2,7 +2,7 @@
> **最後更新**: 2026-04-30 (台北時間)
> **狀態**: 🟢 四 AI Agent 自動化閉環已落地 — EventRouter / AutoHeal / OpenClaw Memory / ElephantAlpha bridge / Prometheus metrics / Smoke Dashboard / Smoke Trend Management / Telegram Summary / Grafana provisioning / Prometheus scrape / CD Gunicorn 掛載具測試覆蓋
> **適用版本**: V10.20 ElephantAlpha transient fallback
> **適用版本**: V10.22 Legacy 5888 入口清理
---

View File

@@ -43,6 +43,7 @@
- **AI metrics baseline 觀測**: `/metrics` 在尚無 AI 自動化事件時仍輸出 `momo_ai_*` zero-baseline series避免 app 重啟後 Grafana/Prometheus 看不到 metric names。
- **ElephantAlpha transient fallback**: NVIDIA NIM primary model timeout、connection error、429 與 5xx 會嘗試下一個 fallback model400 等非暫時性請求錯誤不重試。
- **模組化治理守門**: 盤點 15 個超過 800 行 Python 大檔,新增 `docs/guides/modularization_governance.md``tests/test_modularization_governance.py`,防止未分類巨檔再長出來。
- **Legacy 5888 入口清理**: 刪除 `tests/main_test.py` standalone Flask 死碼,測試與自動匯入文件改用 Port 80 `/auto_import` 入口。
### 2026-04-28~29Phase 3e 重構大戰 + daily_sales cache 隱形 bug 根除
- **app.py 縮減 -10.8%**: 7,386 → 6,590 行11 commits 全綠零 502。

View File

@@ -1,124 +0,0 @@
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/<path:filename>')
@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)

View File

@@ -90,7 +90,7 @@ def main():
print("=" * 60)
print()
print("🌐 開啟網頁介面查看詳細資訊:")
print(" http://localhost:5888/auto_import")
print(" http://localhost/auto_import")
print()

View File

@@ -49,7 +49,7 @@ try:
print("Token 已儲存至: config/google_token.pickle")
print()
print("現在可以:")
print(" 1. 在網頁介面測試連接: http://localhost:5888/auto_import")
print(" 1. 在網頁介面測試連接: http://localhost/auto_import")
print(" 2. 執行測試腳本: python3 test_google_drive.py")
print()
else:

View File

@@ -84,11 +84,38 @@ def test_tracked_backup_artifacts_stay_removed():
"app.py.backup_login_required",
"vendor_stockout_list.html.backup",
"database/edm_dashboard.html",
"tests/main_test.py",
]
assert [path for path in forbidden_artifacts if (ROOT / path).exists()] == []
def test_active_code_no_longer_references_legacy_5888_port():
active_paths = [
ROOT / "app.py",
ROOT / "tests",
ROOT / "AUTO_IMPORT_README.md",
ROOT / "GOOGLE_DRIVE_SETUP.md",
]
offenders = []
for active_path in active_paths:
paths = active_path.rglob("*") if active_path.is_dir() else [active_path]
for path in paths:
if (
not path.is_file()
or path == Path(__file__).resolve()
or "__pycache__" in path.parts
or path.suffix == ".pyc"
):
continue
content = path.read_text(encoding="utf-8", errors="ignore")
if "5888" in content:
offenders.append(str(path.relative_to(ROOT)))
assert offenders == []
def test_executable_scripts_do_not_use_remove_orphans():
script_paths = [
ROOT / "scripts",