Upload app.py
Browse files
app.py
CHANGED
|
@@ -56,11 +56,16 @@ app.add_middleware(
|
|
| 56 |
|
| 57 |
# 允許的來源網址清單
|
| 58 |
ALLOWED_ORIGINS = [
|
| 59 |
-
"https://n-m2azgihk7rtcqqaxywntv75at3thf3bgps4xdlq-0lu-script.googleusercontent.com",
|
| 60 |
"https://www.dfes.ntpc.edu.tw",
|
| 61 |
"https://demo.dfes.ntpc.edu.tw"
|
| 62 |
]
|
| 63 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 64 |
# 如果希望允許所有 GAS,取消下面兩行的註解
|
| 65 |
# "https://script.google.com", # 所有 Google Apps Script
|
| 66 |
|
|
@@ -96,12 +101,20 @@ def _is_origin_allowed(request: Request) -> bool:
|
|
| 96 |
if origin.startswith(allowed_origin):
|
| 97 |
print(f"✅ Origin 匹配: {origin} 匹配 {allowed_origin}")
|
| 98 |
return True
|
|
|
|
|
|
|
|
|
|
|
|
|
| 99 |
|
| 100 |
if referer:
|
| 101 |
for allowed_origin in ALLOWED_ORIGINS:
|
| 102 |
if referer.startswith(allowed_origin):
|
| 103 |
print(f"✅ Referer 匹配: {referer} 匹配 {allowed_origin}")
|
| 104 |
return True
|
|
|
|
|
|
|
|
|
|
|
|
|
| 105 |
|
| 106 |
# 允許來自 Hugging Face Spaces 的直接調用
|
| 107 |
if "huggingface" in user_agent.lower():
|
|
@@ -111,6 +124,40 @@ def _is_origin_allowed(request: Request) -> bool:
|
|
| 111 |
print(f"❌ 請求被拒絕 - Origin: {origin}, Referer: {referer}")
|
| 112 |
return False
|
| 113 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 114 |
@app.get("/")
|
| 115 |
async def root():
|
| 116 |
"""根路徑,返回 API 信息"""
|
|
@@ -138,23 +185,36 @@ async def get_allowed_origins():
|
|
| 138 |
"""獲取允許的來源列表(僅供管理員查看)"""
|
| 139 |
return {
|
| 140 |
"allowed_origins": ALLOWED_ORIGINS,
|
| 141 |
-
"
|
| 142 |
-
"
|
|
|
|
| 143 |
}
|
| 144 |
|
| 145 |
@app.get("/debug-request")
|
| 146 |
async def debug_request(request: Request):
|
| 147 |
"""調試端點:顯示請求的詳細信息"""
|
| 148 |
headers = dict(request.headers)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 149 |
return {
|
| 150 |
"method": request.method,
|
| 151 |
"url": str(request.url),
|
| 152 |
"headers": headers,
|
| 153 |
-
"origin":
|
| 154 |
-
"referer":
|
| 155 |
"user_agent": headers.get("user-agent"),
|
| 156 |
"is_allowed": _is_origin_allowed(request),
|
| 157 |
"client_ip": request.client.host if request.client else "unknown",
|
|
|
|
|
|
|
| 158 |
"request_info": {
|
| 159 |
"has_origin": bool(headers.get("origin")),
|
| 160 |
"has_referer": bool(headers.get("referer")),
|
|
@@ -163,6 +223,33 @@ async def debug_request(request: Request):
|
|
| 163 |
}
|
| 164 |
}
|
| 165 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 166 |
|
| 167 |
@app.get("/voices")
|
| 168 |
async def get_voices():
|
|
|
|
| 56 |
|
| 57 |
# 允許的來源網址清單
|
| 58 |
ALLOWED_ORIGINS = [
|
|
|
|
| 59 |
"https://www.dfes.ntpc.edu.tw",
|
| 60 |
"https://demo.dfes.ntpc.edu.tw"
|
| 61 |
]
|
| 62 |
|
| 63 |
+
# 允許的 GAS 專案 ID 清單(支援多個專案)
|
| 64 |
+
ALLOWED_GAS_PROJECT_IDS = [
|
| 65 |
+
"m2azgihk7rtcqqaxywntv75at3thf3bgps4xdlq", # 您的 GAS 專案 ID
|
| 66 |
+
# 可以在這裡添加更多 GAS 專案 ID
|
| 67 |
+
]
|
| 68 |
+
|
| 69 |
# 如果希望允許所有 GAS,取消下面兩行的註解
|
| 70 |
# "https://script.google.com", # 所有 Google Apps Script
|
| 71 |
|
|
|
|
| 101 |
if origin.startswith(allowed_origin):
|
| 102 |
print(f"✅ Origin 匹配: {origin} 匹配 {allowed_origin}")
|
| 103 |
return True
|
| 104 |
+
|
| 105 |
+
# 檢查 GAS 專案 ID
|
| 106 |
+
if _is_gas_project_allowed(origin):
|
| 107 |
+
return True
|
| 108 |
|
| 109 |
if referer:
|
| 110 |
for allowed_origin in ALLOWED_ORIGINS:
|
| 111 |
if referer.startswith(allowed_origin):
|
| 112 |
print(f"✅ Referer 匹配: {referer} 匹配 {allowed_origin}")
|
| 113 |
return True
|
| 114 |
+
|
| 115 |
+
# 檢查 GAS 專案 ID
|
| 116 |
+
if _is_gas_project_allowed(referer):
|
| 117 |
+
return True
|
| 118 |
|
| 119 |
# 允許來自 Hugging Face Spaces 的直接調用
|
| 120 |
if "huggingface" in user_agent.lower():
|
|
|
|
| 124 |
print(f"❌ 請求被拒絕 - Origin: {origin}, Referer: {referer}")
|
| 125 |
return False
|
| 126 |
|
| 127 |
+
def _is_gas_project_allowed(url: str) -> bool:
|
| 128 |
+
"""檢查是否為允許的 GAS 專案"""
|
| 129 |
+
if not url:
|
| 130 |
+
return False
|
| 131 |
+
|
| 132 |
+
# 檢查是否為 GAS 網址格式
|
| 133 |
+
if not url.startswith("https://n-") or not url.endswith("-script.googleusercontent.com"):
|
| 134 |
+
return False
|
| 135 |
+
|
| 136 |
+
# 提取專案 ID
|
| 137 |
+
# 網址格式: https://n-{專案ID}-{使用者ID}lu-script.googleusercontent.com
|
| 138 |
+
try:
|
| 139 |
+
# 移除前綴和後綴
|
| 140 |
+
project_part = url.replace("https://n-", "").replace("-script.googleusercontent.com", "")
|
| 141 |
+
|
| 142 |
+
# 找到最後一個 "-" 的位置,前面就是專案 ID
|
| 143 |
+
last_dash_index = project_part.rfind("-")
|
| 144 |
+
if last_dash_index == -1:
|
| 145 |
+
return False
|
| 146 |
+
|
| 147 |
+
project_id = project_part[:last_dash_index]
|
| 148 |
+
|
| 149 |
+
# 檢查專案 ID 是否在允許清單中
|
| 150 |
+
if project_id in ALLOWED_GAS_PROJECT_IDS:
|
| 151 |
+
print(f"✅ GAS 專案匹配: {project_id} 在允許清單中")
|
| 152 |
+
return True
|
| 153 |
+
else:
|
| 154 |
+
print(f"❌ GAS 專案不匹配: {project_id} 不在允許清單中")
|
| 155 |
+
return False
|
| 156 |
+
|
| 157 |
+
except Exception as e:
|
| 158 |
+
print(f"⚠️ 解析 GAS 網址時發生錯誤: {e}")
|
| 159 |
+
return False
|
| 160 |
+
|
| 161 |
@app.get("/")
|
| 162 |
async def root():
|
| 163 |
"""根路徑,返回 API 信息"""
|
|
|
|
| 185 |
"""獲取允許的來源列表(僅供管理員查看)"""
|
| 186 |
return {
|
| 187 |
"allowed_origins": ALLOWED_ORIGINS,
|
| 188 |
+
"allowed_gas_project_ids": ALLOWED_GAS_PROJECT_IDS,
|
| 189 |
+
"count": len(ALLOWED_ORIGINS) + len(ALLOWED_GAS_PROJECT_IDS),
|
| 190 |
+
"description": "允許的來源網址清單,支援擴充匹配。GAS 專案 ID 會自動匹配所有使用者的網址變體。"
|
| 191 |
}
|
| 192 |
|
| 193 |
@app.get("/debug-request")
|
| 194 |
async def debug_request(request: Request):
|
| 195 |
"""調試端點:顯示請求的詳細信息"""
|
| 196 |
headers = dict(request.headers)
|
| 197 |
+
origin = headers.get("origin")
|
| 198 |
+
referer = headers.get("referer")
|
| 199 |
+
|
| 200 |
+
# 檢查 GAS 專案
|
| 201 |
+
gas_project_info = {}
|
| 202 |
+
if origin:
|
| 203 |
+
gas_project_info["origin"] = _extract_gas_project_id(origin)
|
| 204 |
+
if referer:
|
| 205 |
+
gas_project_info["referer"] = _extract_gas_project_id(referer)
|
| 206 |
+
|
| 207 |
return {
|
| 208 |
"method": request.method,
|
| 209 |
"url": str(request.url),
|
| 210 |
"headers": headers,
|
| 211 |
+
"origin": origin,
|
| 212 |
+
"referer": referer,
|
| 213 |
"user_agent": headers.get("user-agent"),
|
| 214 |
"is_allowed": _is_origin_allowed(request),
|
| 215 |
"client_ip": request.client.host if request.client else "unknown",
|
| 216 |
+
"gas_project_info": gas_project_info,
|
| 217 |
+
"allowed_gas_project_ids": ALLOWED_GAS_PROJECT_IDS,
|
| 218 |
"request_info": {
|
| 219 |
"has_origin": bool(headers.get("origin")),
|
| 220 |
"has_referer": bool(headers.get("referer")),
|
|
|
|
| 223 |
}
|
| 224 |
}
|
| 225 |
|
| 226 |
+
def _extract_gas_project_id(url: str) -> dict:
|
| 227 |
+
"""提取 GAS 專案 ID 信息"""
|
| 228 |
+
if not url:
|
| 229 |
+
return {"is_gas": False}
|
| 230 |
+
|
| 231 |
+
if not url.startswith("https://n-") or not url.endswith("-script.googleusercontent.com"):
|
| 232 |
+
return {"is_gas": False}
|
| 233 |
+
|
| 234 |
+
try:
|
| 235 |
+
project_part = url.replace("https://n-", "").replace("-script.googleusercontent.com", "")
|
| 236 |
+
last_dash_index = project_part.rfind("-")
|
| 237 |
+
|
| 238 |
+
if last_dash_index == -1:
|
| 239 |
+
return {"is_gas": True, "project_id": None, "user_id": None, "error": "無法解析專案 ID"}
|
| 240 |
+
|
| 241 |
+
project_id = project_part[:last_dash_index]
|
| 242 |
+
user_id = project_part[last_dash_index + 1:]
|
| 243 |
+
|
| 244 |
+
return {
|
| 245 |
+
"is_gas": True,
|
| 246 |
+
"project_id": project_id,
|
| 247 |
+
"user_id": user_id,
|
| 248 |
+
"is_allowed": project_id in ALLOWED_GAS_PROJECT_IDS
|
| 249 |
+
}
|
| 250 |
+
except Exception as e:
|
| 251 |
+
return {"is_gas": True, "error": str(e)}
|
| 252 |
+
|
| 253 |
|
| 254 |
@app.get("/voices")
|
| 255 |
async def get_voices():
|