Changeset 0.37.15 (#227)

This commit is contained in:
2026-03-23 19:58:17 +00:00
parent b7746c3004
commit d005e8a30f
23 changed files with 1764 additions and 170 deletions

View File

@@ -0,0 +1,186 @@
# 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