diff --git a/TODO_NEXT_STEPS.txt b/TODO_NEXT_STEPS.txt index e5b0018..4ec6746 100644 --- a/TODO_NEXT_STEPS.txt +++ b/TODO_NEXT_STEPS.txt @@ -4,6 +4,7 @@ ================================================================================ 【已完成】 + - V10.241 修正 `/metabase`、`/grist` 外部工具入口:全域導覽固定回 momo-pro 內部橋接頁,避免資料協作錯連 AwoooI;入口頁補路由狀態、設定診斷與可用替代分析入口,降低空白頁誤判。 - V10.221 補 `/observability/ppt_audit_history` AiderHeal 背景任務可見性:正在修復中的簡報會顯示於產線頁,並提供 JSON 狀態端點讓派工後即時刷新,避免重新整理後不知道是否已在修。 - V10.218 補 `/observability/ppt_audit_history` AiderHeal 去重鎖:同一份簡報已在背景修復時,再次點擊會回「已在執行中」,避免重複開 SSH / 模型 / git 修復流程。 - V10.217 讓 `/observability/ppt_audit_history` 的 AiderHeal 派工改為非阻塞背景任務:頁面立即回「已排入」,修復工作在背景執行,避免瀏覽器與 Gunicorn worker 等 SSH、模型與 git push 到超時。 diff --git a/app.py b/app.py index abb0234..ab31489 100644 --- a/app.py +++ b/app.py @@ -424,14 +424,12 @@ verify_metadata_tables() # ========================================== # 🔧 全域模板變數注入 (Context Processor) # ========================================== -from config import METABASE_URL, GRIST_URL - @app.context_processor def inject_global_vars(): """注入全域變數到所有模板""" return { - 'metabase_url': METABASE_URL, - 'grist_url': GRIST_URL, + 'metabase_url': '/metabase', + 'grist_url': '/grist', 'datetime_now': datetime.now(TAIPEI_TZ).strftime('%Y-%m-%d %H:%M:%S'), } diff --git a/config.py b/config.py index 5c44a0d..0edf552 100644 --- a/config.py +++ b/config.py @@ -320,7 +320,7 @@ YOUTUBE_API_KEY = os.getenv('YOUTUBE_API_KEY', '') # ========================================== # 系統版本與路徑 # ========================================== -SYSTEM_VERSION = "V10.240" +SYSTEM_VERSION = "V10.241" LOG_FILE_PATH = os.path.join(BASE_DIR, 'logs/system.log') public_url = PUBLIC_URL # 用於模板顯示 diff --git a/routes/system_public_routes.py b/routes/system_public_routes.py index 7fbe9ef..fdb71a7 100644 --- a/routes/system_public_routes.py +++ b/routes/system_public_routes.py @@ -8,12 +8,13 @@ import os import zipfile from datetime import datetime, timezone, timedelta +from urllib.parse import urlparse from flask import Blueprint, Response, jsonify, render_template, send_from_directory, url_for from sqlalchemy import text from auth import login_required -from config import BASE_DIR, DATABASE_TYPE, SYSTEM_VERSION +from config import BASE_DIR, DATABASE_TYPE, GRIST_URL, METABASE_URL, SYSTEM_VERSION from database.manager import DatabaseManager from database.models import Product, PriceRecord from services.json_storage import load_categories @@ -30,6 +31,81 @@ public_url = os.getenv('PUBLIC_URL', '服務啟動中...') STATIC_DIR = os.path.join(BASE_DIR, 'web/static') +def _safe_launch_url(configured_url, bridge_path): + """Return a browser-safe launch URL, keeping nav links inside momo-pro by default.""" + candidate = (configured_url or '').strip() + if not candidate or candidate.rstrip('/') == bridge_path.rstrip('/'): + return '' + if candidate.startswith('/') and not candidate.startswith('//'): + return candidate + + parsed = urlparse(candidate) + if parsed.scheme in {'http', 'https'} and parsed.hostname == 'mo.wooo.work': + if parsed.path.rstrip('/') == bridge_path.rstrip('/'): + return '' + return candidate + return '' + + +def _configured_url_label(configured_url): + candidate = (configured_url or '').strip() + if not candidate: + return '未設定' + parsed = urlparse(candidate) + if parsed.scheme in {'http', 'https'} and parsed.hostname != 'mo.wooo.work': + return f'{parsed.hostname}(已由入口攔截)' + return candidate + + +def _external_tool_payload(kind): + if kind == 'metabase': + launch_url = _safe_launch_url(METABASE_URL, '/metabase') + return { + 'key': 'metabase', + 'eyebrow': 'Analytics Bridge', + 'title': '自訂圖表入口', + 'status_label': '待接上 BI 服務' if not launch_url else '可開啟', + 'summary': '這裡是 momo-pro 的 Metabase 安全入口。導覽先停在系統內部,不再出現空白頁或錯站外跳。', + 'detail': '目前正式主機沒有啟用 Metabase / Grist 的 bi profile 容器,Gateway 也沒有可用 proxy;因此先提供可用的分析替代入口與設定診斷。', + 'launch_href': launch_url, + 'launch_label': '開啟 Metabase', + 'configured_label': _configured_url_label(METABASE_URL), + 'checks': [ + {'label': '導覽路由', 'value': '/metabase', 'state': 'ok'}, + {'label': '跨站保護', 'value': '已鎖在 momo-pro', 'state': 'ok'}, + {'label': 'BI 服務', 'value': '尚未接入 proxy', 'state': 'warn'}, + ], + 'actions': [ + {'label': '月份總表', 'href': '/monthly_summary_analysis', 'icon': 'fas fa-table'}, + {'label': '成長分析', 'href': '/growth_analysis', 'icon': 'fas fa-arrow-trend-up'}, + {'label': '當日業績', 'href': '/daily_sales', 'icon': 'fas fa-calendar-day'}, + ], + } + + launch_url = _safe_launch_url(GRIST_URL, '/grist') + return { + 'key': 'grist', + 'eyebrow': 'Data Collaboration', + 'title': '資料協作入口', + 'status_label': '錯鏈已攔截' if not launch_url else '可開啟', + 'summary': '資料協作入口已回到 momo-pro 內部,不會再連到 AwoooI 或其他專案站台。', + 'detail': 'Grist 的正式 momo-pro 專案隔離尚未接上 Gateway;在完成專屬服務前,本頁會提供資料作業入口與目前設定狀態。', + 'launch_href': launch_url, + 'launch_label': '開啟 Grist', + 'configured_label': _configured_url_label(GRIST_URL), + 'checks': [ + {'label': '導覽路由', 'value': '/grist', 'state': 'ok'}, + {'label': '錯站攔截', 'value': '已啟用', 'state': 'ok'}, + {'label': '協作服務', 'value': '尚未接入 proxy', 'state': 'warn'}, + ], + 'actions': [ + {'label': '月份總表', 'href': '/monthly_summary_analysis', 'icon': 'fas fa-table'}, + {'label': '雲端匯入', 'href': '/auto_import', 'icon': 'fas fa-download'}, + {'label': '業績分析', 'href': '/sales_analysis', 'icon': 'fas fa-chart-bar'}, + ], + } + + @system_public_bp.route('/favicon.ico') def favicon(): """使用既有品牌圖示回應瀏覽器預設 favicon 探測,避免全站 404 噪音。""" @@ -66,16 +142,7 @@ def metabase_status(): 'external_tool_status.html', active_page='metabase', system_version=SYSTEM_VERSION, - tool={ - 'key': 'metabase', - 'eyebrow': 'Analytics Bridge', - 'title': '自訂圖表入口', - 'status_label': '代理尚未接入', - 'summary': '正式入口已留在 momo-pro 內部,避免再落到 404 或空白頁。', - 'detail': 'Metabase 容器以 bi profile 管理;公開路由需由 Gateway / Nginx 接到 momo-metabase:3000 後才會切換為完整 BI 介面。', - 'primary_label': '回月份總表', - 'primary_href': '/monthly_summary_analysis', - }, + tool=_external_tool_payload('metabase'), ) @@ -88,16 +155,7 @@ def grist_status(): 'external_tool_status.html', active_page='grist', system_version=SYSTEM_VERSION, - tool={ - 'key': 'grist', - 'eyebrow': 'Data Collaboration', - 'title': '資料協作入口', - 'status_label': '錯鏈已攔截', - 'summary': '資料協作不再連到 grist.wooo.work,避免被轉往其他專案站台。', - 'detail': 'Grist 正式域名尚未完成 momo-pro 專案隔離;在完成 Gateway 綁定前,導覽會停在本頁狀態,不再跳出系統邊界。', - 'primary_label': '回月份總表', - 'primary_href': '/monthly_summary_analysis', - }, + tool=_external_tool_payload('grist'), ) diff --git a/templates/components/_analysis_report_tabs.html b/templates/components/_analysis_report_tabs.html index 73f7202..c951546 100644 --- a/templates/components/_analysis_report_tabs.html +++ b/templates/components/_analysis_report_tabs.html @@ -26,12 +26,12 @@ {% endif %} {% if metabase_url %} - Metabase + 自訂圖表 {% endif %} {% if grist_url %} - Grist + 資料協作 {% endif %} diff --git a/templates/components/_ewoooc_shell.html b/templates/components/_ewoooc_shell.html index 314d2ad..65309fd 100644 --- a/templates/components/_ewoooc_shell.html +++ b/templates/components/_ewoooc_shell.html @@ -13,7 +13,7 @@ {% set _session_username = session.get('username') if session is defined else None %} {% set _session_role = session.get('role') if session is defined else None %} {% set _is_logged_in = session.get('logged_in') if session is defined else false %} -{% set _analysis_pages = ['sales', 'daily_sales', 'monthly', 'growth'] %} +{% set _analysis_pages = ['sales', 'daily_sales', 'monthly', 'growth', 'metabase', 'grist'] %} {% set _obs_pages = [ 'obs_overview', 'obs_agent_orchestration', 'obs_business_intel', 'obs_host_health', 'obs_ai_calls', 'obs_budget', diff --git a/templates/components/_navbar.html b/templates/components/_navbar.html index b2208c6..fd0e183 100755 --- a/templates/components/_navbar.html +++ b/templates/components/_navbar.html @@ -202,14 +202,14 @@
{{ tool.detail }}
+