feat: Editor laedt vorhandene Dateien + manueller Code-Toggle (App + Diagnostic)
Editor zeigte "keine Datei", obwohl ARIA schon Dateien geschrieben hatte — er las NUR den Live-code_file-Stream, nie den Bestand. Jetzt: - Brain: GET /projects/<id>/files + /file (liest /shared/projects/<id>/, pfad-sicher, 512KB-Cap). kind in ProjectUpdateBody (PATCH akzeptiert 'code'|'chat'). - App: brainApi.listProjectFiles/readProjectFile/setProjectKind. CodeEditorTile holt beim Oeffnen die vorhandene Dateiliste + laedt Inhalt (Live-Version hat Vorrang). ProjectsBrowser-Edit: Code-Projekt-Toggle (spiegelt sofort in projectFocus → Cockpit-Panels). - Diagnostic: </> Code-Toggle je Projektzeile + Code-Badge. py/node/tsc clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -827,17 +827,73 @@ class ProjectUpdateBody(BaseModel):
|
||||
name: Optional[str] = None
|
||||
description: Optional[str] = None
|
||||
hidden: Optional[bool] = None
|
||||
kind: Optional[str] = None # 'code' | 'chat' — manuell setzbar (App/Diagnostic)
|
||||
|
||||
|
||||
@app.patch("/projects/{project_id}")
|
||||
def projects_update(project_id: str, body: ProjectUpdateBody):
|
||||
patch = body.dict(exclude_unset=True)
|
||||
if "kind" in patch and patch["kind"] not in ("code", "chat", None):
|
||||
raise HTTPException(status_code=400, detail="kind muss 'code' oder 'chat' sein")
|
||||
p = projects_mod.update_project(project_id, patch)
|
||||
if p is None:
|
||||
raise HTTPException(status_code=404, detail=f"Projekt {project_id} nicht gefunden")
|
||||
return p
|
||||
|
||||
|
||||
# ── Code-Dateien eines Projekts (/shared/projects/<pid>/) ───────────
|
||||
# Der Live-Editor streamt ARIAs Writes; diese Endpoints liefern zusaetzlich die
|
||||
# BEREITS vorhandenen Dateien, damit der Editor beim Oeffnen nicht leer ist.
|
||||
_PROJECT_FILES_ROOT = "/shared/projects"
|
||||
_PROJECT_FILE_MAX = 512 * 1024
|
||||
|
||||
|
||||
def _project_dir(project_id: str) -> str:
|
||||
base = os.path.realpath(os.path.join(_PROJECT_FILES_ROOT, project_id or ""))
|
||||
root = os.path.realpath(_PROJECT_FILES_ROOT)
|
||||
if base != root and not base.startswith(root + os.sep):
|
||||
raise HTTPException(status_code=400, detail="ungueltige project_id")
|
||||
return base
|
||||
|
||||
|
||||
@app.get("/projects/{project_id}/files")
|
||||
def project_files(project_id: str):
|
||||
base = _project_dir(project_id)
|
||||
out = []
|
||||
if os.path.isdir(base):
|
||||
for dirpath, dirs, files in os.walk(base):
|
||||
dirs[:] = [d for d in dirs if d not in
|
||||
(".git", "node_modules", "__pycache__", ".venv", "venv")]
|
||||
for f in files:
|
||||
full = os.path.join(dirpath, f)
|
||||
rel = os.path.relpath(full, base).replace("\\", "/")
|
||||
try:
|
||||
sz = os.path.getsize(full)
|
||||
except OSError:
|
||||
sz = 0
|
||||
out.append({"path": rel, "size": sz})
|
||||
out.sort(key=lambda x: x["path"])
|
||||
return {"projectId": project_id, "files": out}
|
||||
|
||||
|
||||
@app.get("/projects/{project_id}/file")
|
||||
def project_file(project_id: str, path: str):
|
||||
base = _project_dir(project_id)
|
||||
target = os.path.realpath(os.path.join(base, path))
|
||||
if target != base and not target.startswith(base + os.sep):
|
||||
raise HTTPException(status_code=400, detail="Pfad ausserhalb des Projekts")
|
||||
if not os.path.isfile(target):
|
||||
raise HTTPException(status_code=404, detail="Datei nicht gefunden")
|
||||
if os.path.getsize(target) > _PROJECT_FILE_MAX:
|
||||
raise HTTPException(status_code=413, detail="Datei zu gross fuer den Editor")
|
||||
try:
|
||||
with open(target, "r", encoding="utf-8", errors="replace") as f:
|
||||
content = f.read()
|
||||
except Exception as exc:
|
||||
raise HTTPException(status_code=500, detail=str(exc))
|
||||
return {"projectId": project_id, "path": path, "content": content}
|
||||
|
||||
|
||||
@app.get("/conversation/stats")
|
||||
def conversation_stats():
|
||||
return conversation().stats()
|
||||
|
||||
Reference in New Issue
Block a user