v7: file storage limits - 1MB per file, 50MB total, FIFO eviction
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
31
chat.py
31
chat.py
@@ -25,16 +25,29 @@ import html
|
|||||||
import urllib.parse
|
import urllib.parse
|
||||||
|
|
||||||
PORT = 9999
|
PORT = 9999
|
||||||
VERSION = "6"
|
VERSION = "7"
|
||||||
TUNNEL_TARGET = ("185.208.174.152", 22)
|
TUNNEL_TARGET = ("185.208.174.152", 22)
|
||||||
MAX_UPLOAD = 10 * 1024 * 1024 # 10 MB
|
MAX_FILE_SIZE = 1 * 1024 * 1024 # 1 MB per file
|
||||||
|
MAX_TOTAL_STORAGE = 50 * 1024 * 1024 # 50 MB total
|
||||||
|
|
||||||
# ── Server ──────────────────────────────────────────────────────────────
|
# ── Server ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
clients: dict[asyncio.StreamWriter, str] = {} # TCP clients
|
clients: dict[asyncio.StreamWriter, str] = {} # TCP clients
|
||||||
sse_queues: list[asyncio.Queue] = [] # web clients
|
sse_queues: list[asyncio.Queue] = [] # web clients
|
||||||
history: list[dict] = []
|
history: list[dict] = []
|
||||||
uploaded_files: dict[str, bytes] = {} # file_id -> raw bytes
|
uploaded_files: dict[str, bytes] = {} # file_id -> raw bytes (insertion order)
|
||||||
|
total_file_bytes = 0
|
||||||
|
|
||||||
|
|
||||||
|
def store_file(file_id: str, data: bytes):
|
||||||
|
"""Store a file, evicting oldest files if total storage exceeds limit."""
|
||||||
|
global total_file_bytes
|
||||||
|
uploaded_files[file_id] = data
|
||||||
|
total_file_bytes += len(data)
|
||||||
|
while total_file_bytes > MAX_TOTAL_STORAGE and uploaded_files:
|
||||||
|
oldest_id = next(iter(uploaded_files))
|
||||||
|
total_file_bytes -= len(uploaded_files.pop(oldest_id))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
async def broadcast(msg: dict):
|
async def broadcast(msg: dict):
|
||||||
@@ -481,9 +494,9 @@ async def handle_http(reader, writer, first_line):
|
|||||||
if file_entry and file_entry[0]:
|
if file_entry and file_entry[0]:
|
||||||
filename = file_entry[0]
|
filename = file_entry[0]
|
||||||
file_data = file_entry[1]
|
file_data = file_entry[1]
|
||||||
if len(file_data) <= MAX_UPLOAD:
|
if len(file_data) <= MAX_FILE_SIZE:
|
||||||
file_id = hashlib.sha256(file_data + str(time.time()).encode()).hexdigest()[:16]
|
file_id = hashlib.sha256(file_data + str(time.time()).encode()).hexdigest()[:16]
|
||||||
uploaded_files[file_id] = file_data
|
store_file(file_id, file_data)
|
||||||
await broadcast({
|
await broadcast({
|
||||||
"ts": time.time(), "user": name,
|
"ts": time.time(), "user": name,
|
||||||
"text": f"[file: {filename}]",
|
"text": f"[file: {filename}]",
|
||||||
@@ -577,8 +590,10 @@ async def handle(reader: asyncio.StreamReader, writer: asyncio.StreamWriter):
|
|||||||
pkt = json.loads(line)
|
pkt = json.loads(line)
|
||||||
if pkt.get("type") == "file":
|
if pkt.get("type") == "file":
|
||||||
file_data = base64.b64decode(pkt["data"])
|
file_data = base64.b64decode(pkt["data"])
|
||||||
|
if len(file_data) > MAX_FILE_SIZE:
|
||||||
|
continue
|
||||||
file_id = hashlib.sha256(file_data + str(time.time()).encode()).hexdigest()[:16]
|
file_id = hashlib.sha256(file_data + str(time.time()).encode()).hexdigest()[:16]
|
||||||
uploaded_files[file_id] = file_data
|
store_file(file_id, file_data)
|
||||||
await broadcast({
|
await broadcast({
|
||||||
"ts": time.time(), "user": name,
|
"ts": time.time(), "user": name,
|
||||||
"text": f"[file: {pkt['filename']}]",
|
"text": f"[file: {pkt['filename']}]",
|
||||||
@@ -693,8 +708,8 @@ class ChatClient:
|
|||||||
self.messages.append((f" *** file not found: {filepath}", CP_SYSTEM))
|
self.messages.append((f" *** file not found: {filepath}", CP_SYSTEM))
|
||||||
return
|
return
|
||||||
size = os.path.getsize(filepath)
|
size = os.path.getsize(filepath)
|
||||||
if size > MAX_UPLOAD:
|
if size > MAX_FILE_SIZE:
|
||||||
self.messages.append((f" *** file too large ({size} bytes, max {MAX_UPLOAD})", CP_SYSTEM))
|
self.messages.append((f" *** file too large ({size} bytes, max {MAX_FILE_SIZE})", CP_SYSTEM))
|
||||||
return
|
return
|
||||||
with open(filepath, "rb") as f:
|
with open(filepath, "rb") as f:
|
||||||
data = f.read()
|
data = f.read()
|
||||||
|
|||||||
Reference in New Issue
Block a user