Files
ewoooc/config.py
ogt 1b4f3a7bbe
Some checks failed
CD Pipeline / deploy (push) Failing after 59s
feat: EwoooC 初始化 — 完整專案推版至 Gitea
- 建立 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>
2026-04-19 01:21:13 +08:00

221 lines
12 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import os
import json
from dotenv import load_dotenv
# 載入 .env 環境變數
load_dotenv()
# 基本路徑設定
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
DATA_DIR = os.path.join(BASE_DIR, 'data')
LOG_DIR = os.path.join(BASE_DIR, 'logs')
UPLOAD_FOLDER = os.path.join(BASE_DIR, 'web/static/uploads')
# 建立必要目錄
for d in [DATA_DIR, LOG_DIR, UPLOAD_FOLDER]:
os.makedirs(d, exist_ok=True)
# ==========================================
# 資料庫設定
# ==========================================
# 支援 SQLite 和 PostgreSQL 兩種資料庫
# 預設使用 SQLite (本地開發),可透過環境變數切換到 PostgreSQL
USE_POSTGRESQL = os.getenv('USE_POSTGRESQL', 'false').lower() == 'true'
if USE_POSTGRESQL:
# PostgreSQL 連線設定
POSTGRES_HOST = os.getenv('POSTGRES_HOST', 'momo-postgres')
POSTGRES_PORT = os.getenv('POSTGRES_PORT', '5432')
POSTGRES_USER = os.getenv('POSTGRES_USER', 'momo')
POSTGRES_PASSWORD = os.getenv('POSTGRES_PASSWORD', 'wooo_pg_2026')
POSTGRES_DB = os.getenv('POSTGRES_DB', 'momo_analytics')
DATABASE_PATH = f"postgresql://{POSTGRES_USER}:{POSTGRES_PASSWORD}@{POSTGRES_HOST}:{POSTGRES_PORT}/{POSTGRES_DB}"
DATABASE_TYPE = 'postgresql'
else:
# SQLite 連線設定 (開發環境或備用)
DATABASE_PATH = f"sqlite:///{os.path.join(DATA_DIR, 'momo_database.db')}"
DATABASE_TYPE = 'sqlite'
# ==========================================
# 安全設定(從環境變數讀取)
# ==========================================
LOGIN_PASSWORD = os.getenv('LOGIN_PASSWORD', '0936223270') # 進入後台的密碼
SECRET_KEY = os.getenv('SECRET_KEY', 'your_flask_secret_key')
# ==========================================
# 通訊模組設定(從環境變數讀取)
# ==========================================
# --- Telegram Bot ---
TELEGRAM_BOT_TOKEN = os.getenv('TELEGRAM_BOT_TOKEN', '')
try:
TELEGRAM_CHAT_IDS = json.loads(os.getenv('TELEGRAM_CHAT_IDS', '[]'))
except json.JSONDecodeError:
TELEGRAM_CHAT_IDS = []
# --- Line Notify ---
LINE_ENABLED = os.getenv('LINE_ENABLED', 'false').lower() == 'true' # 預設關閉
LINE_CHANNEL_ACCESS_TOKEN = os.getenv('LINE_CHANNEL_ACCESS_TOKEN', '')
LINE_GROUP_ID = os.getenv('LINE_GROUP_ID', '')
# --- Email (SMTP) ---
EMAIL_HOST = os.getenv('EMAIL_HOST', 'smtp.gmail.com')
EMAIL_PORT = int(os.getenv('EMAIL_PORT', '587'))
EMAIL_HOST_USER = os.getenv('EMAIL_HOST_USER', '')
EMAIL_HOST_PASSWORD = os.getenv('EMAIL_HOST_PASSWORD', '') # 注意:若使用 Gmail需設定「應用程式密碼」
EMAIL_SENDER = os.getenv('EMAIL_SENDER', '')
EMAIL_RECEIVER = os.getenv('EMAIL_RECEIVER', '')
# ==========================================
# 網路設定(從環境變數讀取)
# ==========================================
PUBLIC_URL = os.getenv('PUBLIC_URL', 'https://mo.wooo.work')
# 補上 EXCEL_EXPORT_DIR 定義
EXCEL_EXPORT_DIR = os.path.join(DATA_DIR, 'excel_exports')
# 更新建立目錄清單 (確保系統會自動建立這個資料夾)
for d in [DATA_DIR, LOG_DIR, UPLOAD_FOLDER, EXCEL_EXPORT_DIR]:
os.makedirs(d, exist_ok=True)
# MOMO 分類清單 (從 JSON 檔案動態讀取)
def load_momo_categories():
import json
import time
categories_path = os.path.join(DATA_DIR, 'categories.json')
# 預設分類,用於首次啟動或檔案遺失時
default_categories = [
{"name": "保養超值組", "url": "https://www.momoshop.com.tw/category/DgrpCategory.jsp?d_code=1111700012"},
{"name": "化妝水", "url": "https://www.momoshop.com.tw/category/DgrpCategory.jsp?d_code=1111700001"},
{"name": "精華液", "url": "https://www.momoshop.com.tw/category/DgrpCategory.jsp?d_code=1111700002&p_orderType=4&showType=chessboardType"},
{"name": "乳液", "url": "https://www.momoshop.com.tw/category/DgrpCategory.jsp?d_code=1111700003&p_orderType=4&showType=chessboardType"},
{"name": "乳霜", "url": "https://www.momoshop.com.tw/category/DgrpCategory.jsp?d_code=1111700004&p_orderType=4&showType=chessboardType"},
{"name": "凝膠", "url": "https://www.momoshop.com.tw/category/DgrpCategory.jsp?d_code=1111700005&p_orderType=4&showType=chessboardType"},
{"name": "面膜", "url": "https://www.momoshop.com.tw/category/DgrpCategory.jsp?d_code=1111700006&p_orderType=4&showType=chessboardType"},
{"name": "眼霜", "url": "https://www.momoshop.com.tw/category/DgrpCategory.jsp?d_code=1111700007&p_orderType=4&showType=chessboardType"},
{"name": "護唇膏", "url": "https://www.momoshop.com.tw/category/DgrpCategory.jsp?d_code=1111700008&p_orderType=4&showType=chessboardType"},
{"name": "防曬", "url": "https://www.momoshop.com.tw/category/DgrpCategory.jsp?d_code=1111700009&p_orderType=4&showType=chessboardType"},
{"name": "素顏霜", "url": "https://www.momoshop.com.tw/category/DgrpCategory.jsp?d_code=1111700010&p_orderType=4&showType=chessboardType"},
{"name": "美顏霜", "url": "https://www.momoshop.com.tw/category/DgrpCategory.jsp?d_code=1111700011&p_orderType=4&showType=chessboardType"},
{"name": "身體護理", "url": "https://www.momoshop.com.tw/category/MgrpCategory.jsp?m_code=1100901499&sourcePageType=4"},
{"name": "手部保養", "url": "https://www.momoshop.com.tw/category/MgrpCategory.jsp?m_code=1100901655&sourcePageType=4"},
{"name": "足部保養", "url": "https://www.momoshop.com.tw/category/MgrpCategory.jsp?m_code=1100901656&sourcePageType=4"},
{"name": "局部護理", "url": "https://www.momoshop.com.tw/category/MgrpCategory.jsp?m_code=1100901505&sourcePageType=4"},
{"name": "止汗體香", "url": "https://www.momoshop.com.tw/category/MgrpCategory.jsp?m_code=1100901503&sourcePageType=4"},
{"name": "嬰幼身體保養品牌旗艦", "url": "https://www.momoshop.com.tw/category/MgrpCategory.jsp?m_code=1100901724&sourcePageType=4"},
{"name": "嬰幼本月主打", "url": "https://www.momoshop.com.tw/category/MgrpCategory.jsp?m_code=2705200081&sourcePageType=4"},
{"name": "嬰幼清潔用品", "url": "https://www.momoshop.com.tw/category/MgrpCategory.jsp?m_code=2705200349&p_orderType=4&showType=chessboardType"},
{"name": "嬰幼保養護膚", "url": "https://www.momoshop.com.tw/category/MgrpCategory.jsp?m_code=2705200350&p_orderType=4&showType=chessboardType"},
{"name": "媽咪孕期保養", "url": "https://www.momoshop.com.tw/category/MgrpCategory.jsp?m_code=2705200352&p_orderType=4&showType=chessboardType"},
{"name": "送禮超值組", "url": "https://www.momoshop.com.tw/category/MgrpCategory.jsp?m_code=2705200348&p_orderType=4&showType=chessboardType"},
{"name": "嬰幼品牌總覽", "url": "https://www.momoshop.com.tw/category/MgrpCategory.jsp?m_code=2705200348&p_orderType=4&showType=chessboardType"},
{"name": "私密保養本月主打", "url": "https://www.momoshop.com.tw/category/MgrpCategory.jsp?m_code=1105900060&sourcePageType=4"},
{"name": "私密保養", "url": "https://www.momoshop.com.tw/category/MgrpCategory.jsp?m_code=1105900002&sourcePageType=4"},
{"name": "私密清潔", "url": "https://www.momoshop.com.tw/category/MgrpCategory.jsp?m_code=1105900003&sourcePageType=4"},
{"name": "除毛", "url": "https://www.momoshop.com.tw/category/MgrpCategory.jsp?m_code=1105900154&sourcePageType=4"},
{"name": "私密保養推薦品牌", "url": "https://www.momoshop.com.tw/category/MgrpCategory.jsp?m_code=1105900023&sourcePageType=4"}
]
if not os.path.exists(categories_path):
# 如果檔案不存在,使用預設值並為每項加上 ID 後建立新檔案
categories_with_id = []
for i, cat in enumerate(default_categories):
cat['id'] = int(time.time() * 1000) + i
categories_with_id.append(cat)
try:
with open(categories_path, 'w', encoding='utf-8') as f:
json.dump(categories_with_id, f, ensure_ascii=False, indent=4)
return categories_with_id
except Exception as e:
print(f"Error creating categories.json: {e}")
return default_categories
try:
# V-Fix: 檢查檔案大小,如果過大 (例如超過 1MB) 則視為異常,直接使用預設值,避免讀取卡死
if os.path.exists(categories_path):
try:
if os.path.getsize(categories_path) > 1024 * 1024:
print(f"⚠️ Warning: categories.json is too large ({os.path.getsize(categories_path)} bytes). Using defaults.")
return default_categories
except OSError:
return default_categories
with open(categories_path, 'r', encoding='utf-8') as f:
try:
content = f.read().strip()
except Exception:
return default_categories
if not content:
return default_categories
data = json.loads(content)
# 若讀取到的列表為空,則回傳預設值 (修正空檔案導致爬蟲不工作的問題)
if not data:
return default_categories
return data
except (json.JSONDecodeError, FileNotFoundError, OSError):
# 如果檔案損毀或讀取失敗,回傳預設值
return default_categories
MOMO_CATEGORIES = load_momo_categories()
# ==========================================
# 密碼安全設定
# ==========================================
PASSWORD_MIN_LENGTH = int(os.getenv('PASSWORD_MIN_LENGTH', '8'))
PASSWORD_REQUIRE_UPPERCASE = os.getenv('PASSWORD_REQUIRE_UPPERCASE', 'true').lower() == 'true'
PASSWORD_REQUIRE_LOWERCASE = os.getenv('PASSWORD_REQUIRE_LOWERCASE', 'true').lower() == 'true'
PASSWORD_REQUIRE_DIGIT = os.getenv('PASSWORD_REQUIRE_DIGIT', 'true').lower() == 'true'
PASSWORD_REQUIRE_SPECIAL = os.getenv('PASSWORD_REQUIRE_SPECIAL', 'false').lower() == 'true'
PASSWORD_SPECIAL_CHARS = os.getenv('PASSWORD_SPECIAL_CHARS', '!@#$%^&*()_+-=[]{}|;:,.<>?')
PASSWORD_EXPIRY_DAYS = int(os.getenv('PASSWORD_EXPIRY_DAYS', '90'))
# ==========================================
# 外部服務連結 (分析報表選單)
# ==========================================
METABASE_URL = os.getenv('METABASE_URL', '') # Metabase BI 連結
GRIST_URL = os.getenv('GRIST_URL', '') # Grist 資料協作連結
# ==========================================
# AI 服務設定
# ==========================================
# Ollama 本地 AI 服務
OLLAMA_HOST = os.getenv('OLLAMA_HOST', 'https://ollama.wooo.work/ollama')
OLLAMA_MODEL = os.getenv('OLLAMA_MODEL', 'gemma3:4b')
# Google Gemini AI 雲端服務
GEMINI_API_KEY = os.getenv('GEMINI_API_KEY', '')
GEMINI_MODEL = os.getenv('GEMINI_MODEL', 'gemini-1.5-flash')
# 預設 AI 提供者: 'ollama' (本地免費) 或 'gemini' (雲端付費)
AI_PROVIDER = os.getenv('AI_PROVIDER', 'ollama')
# YouTube API Key (用於趨勢爬蟲)
YOUTUBE_API_KEY = os.getenv('YOUTUBE_API_KEY', '')
# ==========================================
# 系統版本與路徑
# ==========================================
SYSTEM_VERSION = "V10.3"
LOG_FILE_PATH = os.path.join(BASE_DIR, 'logs/system.log')
public_url = PUBLIC_URL # 用於模板顯示
# ==========================================
# 模組化路由設定
# ==========================================
# 控制是否啟用模組化路由,設為 True 時會自動清理 app.py 中的重複路由
USE_MODULAR_ROUTES = {
'system': True, # 系統設定、日誌、備份
'edm': True, # EDM 與節慶儀表板
'monthly': True, # 月結分析
'dashboard': True, # 首頁商品看板
'daily_sales': True, # 當日業績分析
'api': True, # 通用 API
'export': True, # 匯出功能
'import': True, # 匯入功能
'sales': True, # 業績分析
}