feat(dashboard): unified workspace hub — cookie auth, 9-state projects, planning chat
Merges workspace.html + ralph.html into a single unified project hub with: - Cookie-based auth (DASHBOARD_TOKEN, HttpOnly, SameSite=Strict) - 9-state project badge system (running-ralph/manual, planning, approved, pending, blocked, failed, complete, idle) with BUTTONS_FOR_STATE matrix - SSE realtime + polling fallback, version-based optimistic concurrency (If-Match) - Planning chat modal (phase stepper, markdown bubbles, 50s+ wait state, auto-resume) - Propose modal (Variant B: inline Plan-with-Echo checkbox) - 5-type toast taxonomy (success/info/warning/busy/error, 3px colored left-bar) - Inter font self-hosted + shared tokens.css design system + DESIGN.md - src/jsonlock.py (flock helper, sidecar .lock for stable inode) - src/approved_tasks_cli.py (shell-safe wrapper for cron/ralph.sh) - 55 new tests (T#1–T#30) + real jsonlock bug fix caught by T#16/T#28 - No emoji anywhere (enforced by test_dashboard_no_emoji.py) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -24,7 +24,6 @@ Reuse path constants din `dashboard/constants.py` (WORKSPACE_DIR).
|
||||
"""
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import signal
|
||||
import subprocess
|
||||
import sys
|
||||
@@ -34,6 +33,8 @@ from pathlib import Path
|
||||
|
||||
import constants
|
||||
|
||||
from handlers._validators import _SLUG_RE, validate_slug
|
||||
|
||||
# Best-effort import of pure functions for /api/ralph/usage (instrumentation MVP).
|
||||
# Helper lives at <repo>/tools/ralph_usage.py — sibling of `dashboard/`.
|
||||
_TOOLS_DIR = Path(__file__).resolve().parents[2] / "tools"
|
||||
@@ -45,10 +46,6 @@ except ImportError: # pragma: no cover — diagnostic only
|
||||
ralph_usage = None # type: ignore
|
||||
|
||||
|
||||
# Slug strict: alphanum + dash + underscore, max 64 chars. Reject path traversal explicit.
|
||||
_SLUG_RE = re.compile(r"^[A-Za-z0-9_-]{1,64}$")
|
||||
|
||||
|
||||
# Path Ralph per proiect (mereu în scripts/ralph/)
|
||||
def _ralph_dir(project_dir: Path) -> Path:
|
||||
return project_dir / "scripts" / "ralph"
|
||||
@@ -65,17 +62,11 @@ class RalphHandlers:
|
||||
def _ralph_validate_slug(self, slug: str):
|
||||
"""Validează slug-ul + returnează project_dir sau None.
|
||||
|
||||
Strict: alphanum + dash + underscore, ≤64 chars. Path traversal sequences
|
||||
(`..`, `/`, `\\`) sau caractere ne-alfanumerice sunt respinse înainte de
|
||||
orice atingere a filesystem-ului.
|
||||
Delegates the slug-shape check to the shared `validate_slug` helper
|
||||
in `dashboard/handlers/_validators.py`; only filesystem checks remain
|
||||
here (existence + path-confinement under WORKSPACE_DIR).
|
||||
"""
|
||||
if not slug:
|
||||
return None
|
||||
# Defense-in-depth: explicit path-traversal/separator reject (regex îl
|
||||
# acoperă, dar îl ţinem explicit ca safety net dacă regex-ul se relaxează).
|
||||
if ".." in slug or "/" in slug or "\\" in slug:
|
||||
return None
|
||||
if not _SLUG_RE.match(slug):
|
||||
if validate_slug(slug) is not None:
|
||||
return None
|
||||
project_dir = constants.WORKSPACE_DIR / slug
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user