SalexAI commited on
Commit
269bfb8
·
verified ·
1 Parent(s): 7a18ea8

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +55 -24
app.py CHANGED
@@ -28,16 +28,15 @@ app.add_middleware(
28
  PERSISTENT_ROOT = os.environ.get("PERSISTENT_DIR", "/data")
29
  CHAT_DIR = os.path.join(PERSISTENT_ROOT, "chat")
30
  if not os.path.isdir(PERSISTENT_ROOT):
31
- # Fall back locally if /data isn't available
32
  CHAT_DIR = os.path.join(".", "data", "chat")
33
  os.makedirs(CHAT_DIR, exist_ok=True)
34
 
35
  def _chat_file_for(video_id: str) -> str:
36
- # Stable filename; avoids path traversal and huge names
37
  h = hashlib.sha256(video_id.encode("utf-8")).hexdigest()[:32]
38
  return os.path.join(CHAT_DIR, f"{h}.jsonl")
39
 
40
- # Per-file asyncio locks (process-local)
41
  app.state.chat_locks = {}
42
 
43
  def _lock_for(path: str) -> asyncio.Lock:
@@ -82,35 +81,67 @@ class NewMessage(BaseModel):
82
  text: str = Field(..., min_length=1, max_length=5000)
83
 
84
  @app.get("/chat/{video_id}")
85
- async def get_messages(video_id: str):
86
- messages = app.state.chat_storage.get(video_id, [])
87
- return {
88
- "video_id": video_id,
89
- "count": len(messages),
90
- "messages": messages
91
- }
 
 
 
 
 
 
 
 
 
 
 
 
92
 
 
 
 
93
 
94
- @app.post("/chat/{video_id}")
95
- async def send_message(video_id: str, msg: ChatMessage, request: Request):
96
- if video_id not in app.state.chat_storage:
97
- app.state.chat_storage[video_id] = []
98
 
99
- message = {
100
- "id": uuid.uuid4().hex[:16],
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
101
  "video_id": video_id,
102
- "author": msg.user, # ✅ FIXED: use provided username
103
- "text": msg.text,
104
- "created_at": datetime.utcnow().isoformat(),
105
- "ip": request.client.host if request.client else "unknown",
106
- "ua": request.headers.get("user-agent", "")
107
  }
108
 
109
- app.state.chat_storage[video_id].append(message)
110
- return {"status": "ok", "message": message}
 
 
111
 
112
  # ---------------------------
113
- # (Your original iCloud album endpoints — unchanged)
114
  # ---------------------------
115
  BASE_62_MAP = {c: i for i, c in enumerate("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz")}
116
 
 
28
  PERSISTENT_ROOT = os.environ.get("PERSISTENT_DIR", "/data")
29
  CHAT_DIR = os.path.join(PERSISTENT_ROOT, "chat")
30
  if not os.path.isdir(PERSISTENT_ROOT):
 
31
  CHAT_DIR = os.path.join(".", "data", "chat")
32
  os.makedirs(CHAT_DIR, exist_ok=True)
33
 
34
  def _chat_file_for(video_id: str) -> str:
35
+ # Stable filename to avoid path traversal
36
  h = hashlib.sha256(video_id.encode("utf-8")).hexdigest()[:32]
37
  return os.path.join(CHAT_DIR, f"{h}.jsonl")
38
 
39
+ # Async file locks
40
  app.state.chat_locks = {}
41
 
42
  def _lock_for(path: str) -> asyncio.Lock:
 
81
  text: str = Field(..., min_length=1, max_length=5000)
82
 
83
  @app.get("/chat/{video_id}")
84
+ async def get_messages(video_id: str, limit: int = 50, since: Optional[str] = None):
85
+ """
86
+ Fetch messages for a video.
87
+ - limit: max messages (default 50)
88
+ - since: ISO8601 timestamp; return only messages newer than this
89
+ """
90
+ limit = max(1, min(limit, 200))
91
+ path = _chat_file_for(video_id)
92
+ items = await _read_jsonl(path)
93
+
94
+ if since:
95
+ try:
96
+ since_dt = datetime.fromisoformat(since.replace("Z", "+00:00"))
97
+ items = [
98
+ m for m in items
99
+ if datetime.fromisoformat(str(m.get("created_at", "")).replace("Z", "+00:00")) > since_dt
100
+ ]
101
+ except Exception:
102
+ pass # Ignore bad since param
103
 
104
+ items.sort(key=lambda m: m.get("created_at", ""))
105
+ if len(items) > limit:
106
+ items = items[-limit:]
107
 
108
+ return {"video_id": video_id, "count": len(items), "messages": items}
 
 
 
109
 
110
+ @app.post("/chat/{video_id}")
111
+ async def post_message(video_id: str, msg: NewMessage, request: Request):
112
+ """
113
+ Append a message to a video's chat.
114
+ Body: { "author": "Ross", "text": "hello" }
115
+ """
116
+ path = _chat_file_for(video_id)
117
+ lock = _lock_for(path)
118
+
119
+ author = _valid_author(msg.author)
120
+ text = _valid_text(msg.text)
121
+ if not text:
122
+ return {"ok": False, "error": "Empty message"}
123
+
124
+ created = _now_iso()
125
+ mid = hashlib.sha1(f"{video_id}|{author}|{created}|{text}".encode("utf-8")).hexdigest()[:16]
126
+ ip = request.client.host if request and request.client else None
127
+
128
+ record = {
129
+ "id": mid,
130
  "video_id": video_id,
131
+ "author": author, # ✅ FIXED: uses frontend-provided name
132
+ "text": text,
133
+ "created_at": created,
134
+ "ip": ip,
135
+ "ua": request.headers.get("user-agent", "")[:200]
136
  }
137
 
138
+ async with lock:
139
+ await _append_jsonl(path, record)
140
+
141
+ return {"ok": True, "message": record}
142
 
143
  # ---------------------------
144
+ # (Original iCloud album endpoints)
145
  # ---------------------------
146
  BASE_62_MAP = {c: i for i, c in enumerate("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz")}
147