因為這兩週工作繁忙所以一直還沒更新架構圖,先來盤點一下已經實作的 API 和後續想到需要進行的 API。 今天在利用已經做好的網頁進行上傳備份手機影片時,發現第一點是,之前寫得匆忙,僅允許了 mp4
格式,忽略了其他常見的影音格式如 mov
;缺少了查看已經上傳的影片的功能。 先列出已完成開發,和要開發的功能在下面。
影片連結: https://www.youtube.com/shorts/CsW2VxkFGIw
/generate-url
一旦確立身份,用來產生上傳影片到 S3 的臨時網址
/login
使用者在登入頁面打完帳號密碼,透過此 API 送給後端程式進行 登入程序
/login
將請求轉導到 網站的
login.html
頁面
/register
使用者在登入頁面打完帳號密碼,透過此 API 送給後端程式進行 註冊程序
/validate
用來檢查使用者瀏覽器中的 jwt 是否有效,藉此 驗證登入狀態
list-video
看不到自己有哪些放在 S3 的資料,每次都要從 AWS console 或是指令查看,也說不過去。
/generate-subtitle
/translate-url
共四處需更動:
list-videos
import os
import json
import boto3
import jwt
import urllib.parse
s3 = boto3.client("s3")
BUCKET_NAME = os.environ.get("BUCKET_NAME", "exsky-backup-media")
SECRET = os.environ.get("JWT_SECRET", "mysecret")
def lambda_handler(event, context):
method = event.get("requestContext", {}).get("http", {}).get("method")
print("🔎 method:", method)
print("🔎 event:", json.dumps(event))
# --- CORS 預檢請求 (OPTIONS) ---
if method == "OPTIONS":
return {
"statusCode": 200,
"headers": {
"Access-Control-Allow-Origin": "https://vlog.nipapa.tw",
"Access-Control-Allow-Methods": "GET,OPTIONS",
"Access-Control-Allow-Headers": "Authorization,Content-Type",
"Access-Control-Max-Age": "3600"
},
"body": ""
}
# --- 驗證 JWT ---
headers = event.get("headers", {}) or {}
auth = headers.get("authorization") or headers.get("Authorization") or ""
if not auth.startswith("Bearer "):
return _unauthorized("Missing token")
token = auth.split(" ")[1]
try:
decoded = jwt.decode(token, SECRET, algorithms=["HS256"])
username = decoded.get("username", "unknown")
except Exception as e:
return _unauthorized("Invalid token")
# --- GET: 列出影片 ---
if method == "GET":
try:
resp = s3.list_objects_v2(
Bucket=BUCKET_NAME,
Prefix=f"{username}/"
)
items = []
for obj in resp.get("Contents", []):
key = obj["Key"]
decoded_name = urllib.parse.unquote(key.split("/")[-1])
url = s3.generate_presigned_url(
"get_object",
Params={"Bucket": BUCKET_NAME, "Key": key},
ExpiresIn=3600 # 1 小時有效
)
items.append({
"key": key,
"decodedName": decoded_name,
"url": url,
"size": obj["Size"]
})
except Exception as e:
return _server_error(str(e))
return {
"statusCode": 200,
"headers": {"Access-Control-Allow-Origin": "https://vlog.nipapa.tw"},
"body": json.dumps(items, ensure_ascii=False) # ✅ 確保中文不轉義
}
return {"statusCode": 405, "body": "Method not allowed"}
# ---- Helpers ----
def _unauthorized(msg):
return {
"statusCode": 401,
"headers": {"Access-Control-Allow-Origin": "https://vlog.nipapa.tw"},
"body": json.dumps({"error": msg}, ensure_ascii=False)
}
def _server_error(msg):
return {
"statusCode": 500,
"headers": {"Access-Control-Allow-Origin": "https://vlog.nipapa.tw"},
"body": json.dumps({"error": msg}, ensure_ascii=False)
}