Some checks failed
CD Pipeline / deploy (push) Failing after 59s
- 建立 Gitea Actions CD pipeline (.gitea/workflows/cd.yaml) - 部署模式: rsync Python 檔案至 188 → docker restart (volume mount) - Dockerfile/requirements 變動時自動重建 Docker image - 部署通知: Telegram (開始/成功/失敗) - 健康檢查: https://mo.wooo.work/health (最多 5 次重試) - 同步最新 CLAUDE.md / ADR-008 / memory (2026-04-19) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
165 lines
5.8 KiB
Python
165 lines
5.8 KiB
Python
# =============================================================================
|
||
# Apache Superset Configuration
|
||
# MOMO Pro System - UAT 環境
|
||
# =============================================================================
|
||
import os
|
||
|
||
# ---------------------------------------------------------
|
||
# Superset 基本設定
|
||
# ---------------------------------------------------------
|
||
ROW_LIMIT = 50000
|
||
SUPERSET_WEBSERVER_PORT = 8088
|
||
SUPERSET_WEBSERVER_TIMEOUT = 120
|
||
|
||
# ---------------------------------------------------------
|
||
# 反向代理設定 (Nginx 子路徑 /superset/)
|
||
# 2026-02-13 更新:使用 APPLICATION_ROOT 徹底修復 URL 問題
|
||
# ---------------------------------------------------------
|
||
ENABLE_PROXY_FIX = True
|
||
PROXY_FIX_CONFIG = {
|
||
"x_for": 1, # 信任 X-Forwarded-For
|
||
"x_proto": 1, # 信任 X-Forwarded-Proto
|
||
"x_host": 1, # 信任 X-Forwarded-Host
|
||
"x_prefix": 0, # 禁用!讓 APPLICATION_ROOT 處理
|
||
}
|
||
|
||
# =============================================================================
|
||
# 關鍵設定:Cookie 路徑
|
||
# =============================================================================
|
||
# 必須使用 "/" 因為 Superset 的登入頁面是 /login/(不在 /superset/ 下)
|
||
# 如果設為 /superset/,cookie 不會被發送到 /login/ 頁面
|
||
SESSION_COOKIE_PATH = "/"
|
||
|
||
# Secret key (必須設定)
|
||
SECRET_KEY = os.environ.get('SUPERSET_SECRET_KEY', 'wooo_superset_secret_key_2026_momo_pro')
|
||
|
||
# ---------------------------------------------------------
|
||
# 資料庫設定 (Superset Metadata)
|
||
# ---------------------------------------------------------
|
||
SQLALCHEMY_DATABASE_URI = (
|
||
f"postgresql+psycopg2://"
|
||
f"{os.environ.get('DATABASE_USER', 'superset')}:"
|
||
f"{os.environ.get('DATABASE_PASSWORD', 'Wooo_Superset_DB_2026')}@"
|
||
f"{os.environ.get('DATABASE_HOST', 'superset-db')}:"
|
||
f"{os.environ.get('DATABASE_PORT', '5432')}/"
|
||
f"{os.environ.get('DATABASE_DB', 'superset')}"
|
||
)
|
||
|
||
# ---------------------------------------------------------
|
||
# Redis 快取設定
|
||
# ---------------------------------------------------------
|
||
REDIS_HOST = os.environ.get('REDIS_HOST', 'superset-redis')
|
||
REDIS_PORT = os.environ.get('REDIS_PORT', 6379)
|
||
|
||
CACHE_CONFIG = {
|
||
'CACHE_TYPE': 'RedisCache',
|
||
'CACHE_DEFAULT_TIMEOUT': 300,
|
||
'CACHE_KEY_PREFIX': 'superset_',
|
||
'CACHE_REDIS_HOST': REDIS_HOST,
|
||
'CACHE_REDIS_PORT': REDIS_PORT,
|
||
'CACHE_REDIS_DB': 0,
|
||
}
|
||
|
||
DATA_CACHE_CONFIG = {
|
||
'CACHE_TYPE': 'RedisCache',
|
||
'CACHE_DEFAULT_TIMEOUT': 600,
|
||
'CACHE_KEY_PREFIX': 'superset_data_',
|
||
'CACHE_REDIS_HOST': REDIS_HOST,
|
||
'CACHE_REDIS_PORT': REDIS_PORT,
|
||
'CACHE_REDIS_DB': 1,
|
||
}
|
||
|
||
# ---------------------------------------------------------
|
||
# 語言和時區設定
|
||
# ---------------------------------------------------------
|
||
BABEL_DEFAULT_LOCALE = 'zh_Hant_TW'
|
||
BABEL_DEFAULT_FOLDER = 'superset/translations'
|
||
LANGUAGES = {
|
||
'en': {'flag': 'us', 'name': 'English'},
|
||
'zh': {'flag': 'cn', 'name': '简体中文'},
|
||
'zh_Hant_TW': {'flag': 'tw', 'name': '繁體中文'},
|
||
}
|
||
|
||
# 時區設定
|
||
DEFAULT_TIMEZONE = 'Asia/Taipei'
|
||
|
||
# ---------------------------------------------------------
|
||
# 功能開關
|
||
# ---------------------------------------------------------
|
||
FEATURE_FLAGS = {
|
||
'ENABLE_TEMPLATE_PROCESSING': True,
|
||
'DASHBOARD_NATIVE_FILTERS': True,
|
||
'DASHBOARD_CROSS_FILTERS': True,
|
||
'DASHBOARD_NATIVE_FILTERS_SET': True,
|
||
'ALERT_REPORTS': True,
|
||
'EMBEDDABLE_CHARTS': True,
|
||
'EMBEDDED_SUPERSET': True,
|
||
# 關閉 Global Async Queries(避免 WebSocket 連接問題)
|
||
'GLOBAL_ASYNC_QUERIES': False,
|
||
}
|
||
|
||
# =============================================================================
|
||
# 禁用 WebSocket(避免瀏覽器嘗試連接 ws://127.0.0.1:8080)
|
||
# =============================================================================
|
||
# 這個 URL 會被嵌入到頁面的 JavaScript 中
|
||
# 設為空字串來避免瀏覽器嘗試建立 WebSocket 連接
|
||
GLOBAL_ASYNC_QUERIES_WEBSOCKET_URL = ""
|
||
|
||
# ---------------------------------------------------------
|
||
# 安全設定
|
||
# ---------------------------------------------------------
|
||
# 允許嵌入 iframe
|
||
HTTP_HEADERS = {
|
||
'X-Frame-Options': 'SAMEORIGIN',
|
||
}
|
||
|
||
# =============================================================================
|
||
# CSRF 設定 - 完全禁用
|
||
# =============================================================================
|
||
# Superset 6.0 SPA 架構下,CSRF 與 React SPA 有衝突
|
||
# SPA 無法正確從 HttpOnly session cookie 中讀取 CSRF token
|
||
# Superset 內部 API 已有 JWT/Session 認證機制,禁用 CSRF 不會影響安全性
|
||
WTF_CSRF_ENABLED = False
|
||
|
||
# 保留以下設定以防未來需要啟用
|
||
WTF_CSRF_EXEMPT_LIST = []
|
||
WTF_CSRF_TIME_LIMIT = 60 * 60 * 24 * 7 # 7 天
|
||
WTF_CSRF_SSL_STRICT = False
|
||
|
||
# ---------------------------------------------------------
|
||
# 資料庫連線設定
|
||
# ---------------------------------------------------------
|
||
# 預設允許的資料庫驅動
|
||
PREFERRED_DATABASES = [
|
||
'PostgreSQL',
|
||
]
|
||
|
||
# SQL Lab 設定
|
||
SQL_MAX_ROW = 100000
|
||
DISPLAY_MAX_ROW = 10000
|
||
|
||
# ---------------------------------------------------------
|
||
# 日誌設定
|
||
# ---------------------------------------------------------
|
||
LOG_FORMAT = '%(asctime)s:%(levelname)s:%(name)s:%(message)s'
|
||
LOG_LEVEL = 'INFO'
|
||
|
||
# ---------------------------------------------------------
|
||
# 郵件設定 (告警報表用)
|
||
# ---------------------------------------------------------
|
||
SMTP_HOST = 'smtp.gmail.com'
|
||
SMTP_STARTTLS = True
|
||
SMTP_SSL = False
|
||
SMTP_PORT = 587
|
||
SMTP_MAIL_FROM = 'superset@wooo.work'
|
||
|
||
# ---------------------------------------------------------
|
||
# 額外的 Jinja 模板函數
|
||
# ---------------------------------------------------------
|
||
from flask import g
|
||
|
||
JINJA_CONTEXT_ADDONS = {
|
||
'current_user_id': lambda: g.user.id if hasattr(g, 'user') and g.user else None,
|
||
'current_username': lambda: g.user.username if hasattr(g, 'user') and g.user else None,
|
||
}
|