PanTS_Website / app.py
jen900704's picture
Update app.py
9b6f854 verified
import sys
import os
from werkzeug.serving import run_simple
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
from dotenv import load_dotenv
load_dotenv(dotenv_path=os.path.join(os.path.dirname(__file__), ".env"))
from flask import Flask, send_from_directory
from flask_cors import CORS
from constants import Constants
from api.api_blueprint import api_blueprint
from models.base import db # 保留原本 import(即使現在沒用到)
from models.combined_labels import CombinedLabels # 同上
import logging
# --- 1. 確保 sessions 目錄有合理預設值 ---
SESSION_DIR = Constants.SESSIONS_DIR_NAME or "/app/sessions"
Constants.SESSIONS_DIR_NAME = SESSION_DIR
def create_session_dir():
path = Constants.SESSIONS_DIR_NAME
if not os.path.isdir(path):
os.makedirs(path, exist_ok=True)
def create_app():
create_session_dir()
# --- 2. 靜態檔:dist/ ---
# static_folder 指向 Vite build 出來的 dist
# static_url_path 設成 "",讓 /assets/... 之類的路徑正確
app = Flask(__name__, static_folder="dist", static_url_path="")
# --- 3. API 一律掛在 /api ---
app.register_blueprint(api_blueprint, url_prefix="/api")
# --- 4. 簡單 health check ---
@app.route("/api/ping")
def ping():
return {"status": "ok"}
# --- 5. React / Vite SPA:所有非靜態路徑 fallback 到 index.html ---
@app.route("/", defaults={"path": ""})
@app.route("/<path:path>")
def serve_spa(path: str):
"""
1. 如果 dist 裡真的有這個檔案,就當成靜態檔案回傳
2. 如果沒有,就回 dist/index.html,交給 React Router 處理
例如 /case/PanTS_00009391 這種 route
"""
# 先嘗試當成靜態檔
if path:
full_path = os.path.join(app.static_folder, path)
if os.path.isfile(full_path):
return send_from_directory(app.static_folder, path)
# 找不到對應實體檔案 → 交給前端路由
return send_from_directory(app.static_folder, "index.html")
# --- 6. Log 過濾:不要一直印 /api/progress ---
class FilterProgressRequests(logging.Filter):
def filter(self, record):
return "/api/progress/" not in record.getMessage()
logging.getLogger("werkzeug").addFilter(FilterProgressRequests())
# --- 7. CORS ---
CORS(app)
# --- 8. SharedArrayBuffer / WebGL 相關 header ---
@app.after_request
def add_security_headers(response):
response.headers["Cross-Origin-Opener-Policy"] = "same-origin"
response.headers["Cross-Origin-Embedder-Policy"] = "require-corp"
return response
return app
# 給 gunicorn 用的 WSGI app
app = create_app()
# --- 9. 本機開發(可選) ---
def find_watch_files():
watch_dirs = ["api", "models", "services"]
base_path = os.path.dirname(__file__)
all_files = []
for d in watch_dirs:
dir_path = os.path.join(base_path, d)
for root, _, files in os.walk(dir_path):
for f in files:
if f.endswith(".py"):
all_files.append(os.path.join(root, f))
return all_files
if __name__ == "__main__":
use_ssl = os.environ.get("USE_SSL", "false").lower() == "true"
ssl_context = (
("../certs/localhost-cert.pem", "../certs/localhost-key.pem") if use_ssl else None
)
run_simple(
hostname="0.0.0.0",
port=5001,
application=app,
use_debugger=True,
use_reloader=True,
extra_files=find_watch_files(),
ssl_context=ssl_context,
)