Upload app.py
Browse files
app.py
CHANGED
|
@@ -56,20 +56,19 @@ app.add_middleware(
|
|
| 56 |
|
| 57 |
# 允許的來源網址清單
|
| 58 |
ALLOWED_ORIGINS = [
|
| 59 |
-
|
|
|
|
|
|
|
|
|
|
| 60 |
"https://www.dfes.ntpc.edu.tw",
|
| 61 |
"https://www.dfes.ntpc.edu.tw/"
|
| 62 |
]
|
| 63 |
|
| 64 |
-
#
|
| 65 |
-
#
|
| 66 |
-
# 因此主要依賴於沙箱環境檢測
|
| 67 |
|
| 68 |
-
#
|
| 69 |
-
|
| 70 |
-
"apps.dfes.ntpc.edu.tw", # 大豐國小組織
|
| 71 |
-
"dfes.ntpc.edu.tw" # 大豐國小網域
|
| 72 |
-
]
|
| 73 |
|
| 74 |
# 掛載靜態文件(如果存在)
|
| 75 |
if os.path.exists("static"):
|
|
@@ -97,39 +96,21 @@ def _is_origin_allowed(request: Request) -> bool:
|
|
| 97 |
|
| 98 |
print(f"請求來源檢查 - Origin: {origin}, Referer: {referer}, User-Agent: {user_agent}")
|
| 99 |
|
| 100 |
-
#
|
| 101 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 102 |
if origin and "script.googleusercontent.com" in origin:
|
| 103 |
-
is_gas_request = True
|
| 104 |
print("✅ 檢測到 Google Apps Script Origin")
|
| 105 |
-
|
| 106 |
-
is_gas_request = True
|
| 107 |
-
print("✅ 檢測到 Google Apps Script Referer")
|
| 108 |
-
elif not origin and not referer and "mozilla" in user_agent.lower():
|
| 109 |
-
is_gas_request = True
|
| 110 |
-
print("⚠️ 檢測到可能的 GAS 沙箱請求")
|
| 111 |
|
| 112 |
-
#
|
| 113 |
-
if
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
if org_header:
|
| 117 |
-
for allowed_org in ALLOWED_ORGANIZATIONS:
|
| 118 |
-
if allowed_org in org_header.lower():
|
| 119 |
-
print(f"✅ 組織驗證通過: {org_header}")
|
| 120 |
-
return True
|
| 121 |
-
print(f"❌ 組織驗證失敗: {org_header}")
|
| 122 |
-
return False
|
| 123 |
-
|
| 124 |
-
# 檢查是否有自定義驗證標頭
|
| 125 |
-
custom_header = request.headers.get("x-dfes-verified")
|
| 126 |
-
if custom_header == "true":
|
| 127 |
-
print("✅ 自定義驗證標頭通過")
|
| 128 |
-
return True
|
| 129 |
-
|
| 130 |
-
print("❌ GAS 請求但沒有驗證標頭,拒絕請求")
|
| 131 |
-
print("⚠️ 請在 GAS 中添加 'x-organization' 或 'x-dfes-verified' 標頭")
|
| 132 |
-
return False
|
| 133 |
|
| 134 |
# 檢查其他允許的來源(一般網頁)
|
| 135 |
if origin:
|
|
@@ -144,6 +125,23 @@ def _is_origin_allowed(request: Request) -> bool:
|
|
| 144 |
print(f"✅ Referer 匹配: {referer} 匹配 {allowed_origin}")
|
| 145 |
return True
|
| 146 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 147 |
# 允許來自 Hugging Face Spaces 的直接調用
|
| 148 |
if "huggingface" in user_agent.lower():
|
| 149 |
print("✅ 檢測到 Hugging Face Spaces 環境")
|
|
@@ -218,23 +216,20 @@ async def gas_verify(request: Request):
|
|
| 218 |
"current_status": "✅ 請求已通過驗證" if is_gas else "❌ 請求被拒絕"
|
| 219 |
},
|
| 220 |
"gas_usage_example": {
|
| 221 |
-
"description": "在 GAS 中使用此 API
|
| 222 |
"code": """
|
| 223 |
// 在 Google Apps Script 中
|
| 224 |
const response = UrlFetchApp.fetch('https://your-api-url/tts', {
|
| 225 |
method: 'POST',
|
| 226 |
headers: {
|
| 227 |
'Content-Type': 'application/json',
|
| 228 |
-
'x-
|
| 229 |
-
'x-dfes-verified': 'true' // 自定義驗證標頭
|
| 230 |
},
|
| 231 |
payload: JSON.stringify({
|
| 232 |
text: '要轉換的文字',
|
| 233 |
voice: 'zh-TW-HsiaoChenNeural'
|
| 234 |
})
|
| 235 |
});
|
| 236 |
-
|
| 237 |
-
// 注意:添加這些標頭可以確保只有大豐國小的 GAS 才能使用 API
|
| 238 |
"""
|
| 239 |
}
|
| 240 |
}
|
|
|
|
| 56 |
|
| 57 |
# 允許的來源網址清單
|
| 58 |
ALLOWED_ORIGINS = [
|
| 59 |
+
# 限制為特定組織的 GAS
|
| 60 |
+
"https://script.google.com/a/macros/apps.dfes.ntpc.edu.tw",
|
| 61 |
+
"https://script.googleusercontent.com", # GAS 實際執行網域
|
| 62 |
+
# 一般網頁來源
|
| 63 |
"https://www.dfes.ntpc.edu.tw",
|
| 64 |
"https://www.dfes.ntpc.edu.tw/"
|
| 65 |
]
|
| 66 |
|
| 67 |
+
# 如果希望允許所有 GAS,取消下面兩行的註解
|
| 68 |
+
# "https://script.google.com", # 所有 Google Apps Script
|
|
|
|
| 69 |
|
| 70 |
+
# GAS 專用 API 密鑰(可選,用於額外安全驗證)
|
| 71 |
+
GAS_API_KEY = os.getenv("GAS_API_KEY", "your-secret-key-here")
|
|
|
|
|
|
|
|
|
|
| 72 |
|
| 73 |
# 掛載靜態文件(如果存在)
|
| 74 |
if os.path.exists("static"):
|
|
|
|
| 96 |
|
| 97 |
print(f"請求來源檢查 - Origin: {origin}, Referer: {referer}, User-Agent: {user_agent}")
|
| 98 |
|
| 99 |
+
# 特殊處理 Google Apps Script 環境
|
| 100 |
+
# GAS 請求的特徵:
|
| 101 |
+
# 1. Origin 通常包含 script.googleusercontent.com
|
| 102 |
+
# 2. 或者沒有 Origin/Referer 標頭(沙箱環境)
|
| 103 |
+
# 3. User-Agent 通常是標準的瀏覽器標頭
|
| 104 |
+
|
| 105 |
+
# 檢查是否為 GAS 的 Origin
|
| 106 |
if origin and "script.googleusercontent.com" in origin:
|
|
|
|
| 107 |
print("✅ 檢測到 Google Apps Script Origin")
|
| 108 |
+
return True
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 109 |
|
| 110 |
+
# 檢查是否為 GAS 的 Referer
|
| 111 |
+
if referer and "script.googleusercontent.com" in referer:
|
| 112 |
+
print("✅ 檢測到 Google Apps Script Referer")
|
| 113 |
+
return True
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 114 |
|
| 115 |
# 檢查其他允許的來源(一般網頁)
|
| 116 |
if origin:
|
|
|
|
| 125 |
print(f"✅ Referer 匹配: {referer} 匹配 {allowed_origin}")
|
| 126 |
return True
|
| 127 |
|
| 128 |
+
# 檢查是否為 GAS 沙箱環境(沒有 Origin/Referer 但有標準瀏覽器 User-Agent)
|
| 129 |
+
# 這種情況下,我們需要通過其他方式驗證
|
| 130 |
+
if not origin and not referer and "mozilla" in user_agent.lower():
|
| 131 |
+
# 檢查是否有 GAS 特有的標頭或特徵
|
| 132 |
+
# 可以通過檢查特定的請求標頭來驗證
|
| 133 |
+
print("⚠️ 檢測到可能的 GAS 沙箱請求")
|
| 134 |
+
|
| 135 |
+
# 檢查是否有 API 密鑰驗證
|
| 136 |
+
api_key = request.headers.get("x-api-key")
|
| 137 |
+
if api_key and api_key == GAS_API_KEY:
|
| 138 |
+
print("✅ GAS API 密鑰驗證通過")
|
| 139 |
+
return True
|
| 140 |
+
|
| 141 |
+
# 如果沒有 API 密鑰,暫時允許(但建議在生產環境中啟用密鑰驗證)
|
| 142 |
+
print("⚠️ 沒有 API 密鑰驗證,暫時允許請求")
|
| 143 |
+
return True
|
| 144 |
+
|
| 145 |
# 允許來自 Hugging Face Spaces 的直接調用
|
| 146 |
if "huggingface" in user_agent.lower():
|
| 147 |
print("✅ 檢測到 Hugging Face Spaces 環境")
|
|
|
|
| 216 |
"current_status": "✅ 請求已通過驗證" if is_gas else "❌ 請求被拒絕"
|
| 217 |
},
|
| 218 |
"gas_usage_example": {
|
| 219 |
+
"description": "在 GAS 中使用此 API 的範例",
|
| 220 |
"code": """
|
| 221 |
// 在 Google Apps Script 中
|
| 222 |
const response = UrlFetchApp.fetch('https://your-api-url/tts', {
|
| 223 |
method: 'POST',
|
| 224 |
headers: {
|
| 225 |
'Content-Type': 'application/json',
|
| 226 |
+
'x-api-key': 'your-secret-key-here' // 可選,用於額外安全驗證
|
|
|
|
| 227 |
},
|
| 228 |
payload: JSON.stringify({
|
| 229 |
text: '要轉換的文字',
|
| 230 |
voice: 'zh-TW-HsiaoChenNeural'
|
| 231 |
})
|
| 232 |
});
|
|
|
|
|
|
|
| 233 |
"""
|
| 234 |
}
|
| 235 |
}
|