# Team Activity Log — Starlark Backend # # Handles: # GET /entries → list (optional ?category= filter) # POST /entries → create # DELETE /entries/:id → delete # # NOTE: The sandbox has no json module. Request body arrives as a raw # string; response body must be a raw string. Helper functions build # JSON manually. A json.encode/decode module would clean this up — # tracked as a sandbox improvement. def on_request(req): method = req["method"] path = req["path"] if method == "GET" and path == "/entries": return _list_entries(req) elif method == "POST" and path == "/entries": return _create_entry(req) elif method == "DELETE" and path.startswith("/entries/"): entry_id = path[len("/entries/"):] return _delete_entry(req, entry_id) return {"status": 404, "body": '{"error":"not found"}'} def _list_entries(req): category = "" q = req.get("query", None) if q != None: cv = q.get("category", None) if cv != None: category = str(cv) if category: rows = db.query("entries", filters={"category": category}, order="-created_at", limit=200) else: rows = db.query("entries", order="-created_at", limit=200) parts = [] for row in rows: parts.append(_entry_json(row)) return {"status": 200, "body": '{"data":[' + ",".join(parts) + "]}"} def _create_entry(req): body = _parse_body(str(req.get("body", ""))) if not body: return {"status": 400, "body": '{"error":"JSON body required"}'} message = body.get("message", "") if not message: return {"status": 400, "body": '{"error":"message is required"}'} category = body.get("category", "note") username = body.get("username", "") created_at = body.get("created_at", "") user_id = str(req.get("user_id", "")) # Validate category against admin settings allowed = _allowed_categories() if allowed and category not in allowed: return {"status": 400, "body": '{"error":"invalid category: ' + _esc(category) + '"}'} row_id = db.insert("entries", { "user_id": user_id, "username": username, "category": category, "message": message, "created_at": created_at, }) _prune() return {"status": 201, "body": _entry_json({ "id": str(row_id), "user_id": user_id, "username": username, "category": category, "message": message, "created_at": created_at, })} def _delete_entry(req, entry_id): if not entry_id: return {"status": 400, "body": '{"error":"entry id required"}'} db.delete("entries", entry_id) return {"status": 200, "body": '{"deleted":true,"id":"' + _esc(entry_id) + '"}'} # ── Settings helpers ───────────────────────── def _allowed_categories(): raw = settings.get("categories") if not raw: return [] return [p.strip() for p in raw.split(",") if p.strip()] def _prune(): max_raw = settings.get("max_entries") if not max_raw: return limit = int(max_raw) if limit <= 0: return rows = db.query("entries", order="-created_at", limit=limit + 50) if len(rows) > limit: for row in rows[limit:]: db.delete("entries", row["id"]) # ── JSON helpers (no json module in sandbox) ─ def _esc(s): return str(s).replace("\\", "\\\\").replace('"', '\\"').replace("\n", "\\n") def _entry_json(e): return ( '{"id":"' + _esc(e.get("id", "")) + '","user_id":"' + _esc(e.get("user_id", "")) + '","username":"' + _esc(e.get("username", "")) + '","category":"' + _esc(e.get("category", "")) + '","message":"' + _esc(e.get("message", "")) + '","created_at":"' + _esc(e.get("created_at", "")) + '"}' ) def _parse_body(raw): """Minimal flat-object JSON parser. Handles {"key":"value"} payloads.""" s = raw.strip() if not s or s[0] != "{": return None s = s[1:] if s and s[-1] == "}": s = s[:-1] result = {} in_str = False escape = False current = "" pairs = [] for ch in s.elems(): if escape: current += ch escape = False elif ch == "\\": current += ch escape = True elif ch == '"': current += ch in_str = not in_str elif ch == "," and not in_str: pairs.append(current.strip()) current = "" else: current += ch if current.strip(): pairs.append(current.strip()) for pair in pairs: colon = -1 in_s = False esc = False for i in range(len(pair)): c = pair[i] if esc: esc = False elif c == "\\": esc = True elif c == '"': in_s = not in_s elif c == ":" and not in_s: colon = i break if colon < 0: continue key = _unquote(pair[:colon].strip()) val = _unquote(pair[colon+1:].strip()) if key: result[key] = val return result def _unquote(s): if len(s) >= 2 and s[0] == '"' and s[-1] == '"': return s[1:-1].replace('\\"', '"').replace("\\\\", "\\").replace("\\n", "\n") return s