Fix Docs Reader traversal and add Waldwaechter patch
This commit is contained in:
@@ -34,26 +34,13 @@ async def list_docs(req: Request):
|
|||||||
"""
|
"""
|
||||||
List available documentation files.
|
List available documentation files.
|
||||||
"""
|
"""
|
||||||
# Check which exist
|
|
||||||
available = []
|
available = []
|
||||||
|
base_path = _get_docs_base_path()
|
||||||
# Use configured docs path
|
|
||||||
try:
|
|
||||||
settings = get_settings()
|
|
||||||
base_path = settings.docs_path
|
|
||||||
except:
|
|
||||||
base_path = "docs"
|
|
||||||
|
|
||||||
# Check if absolute or relative
|
|
||||||
if not os.path.isabs(base_path) and not os.path.exists(base_path):
|
|
||||||
# Fallback for docker containers if path not found relative
|
|
||||||
if os.path.exists("/docs_root"):
|
|
||||||
base_path = "/docs_root"
|
|
||||||
else:
|
|
||||||
base_path = "."
|
|
||||||
|
|
||||||
for filename, title in ALLOWED_DOCS.items():
|
for filename, title in ALLOWED_DOCS.items():
|
||||||
if os.path.exists(os.path.join(base_path, filename)):
|
# Check root and subdirectories (1 level deep)
|
||||||
|
full_path = _find_file(base_path, filename)
|
||||||
|
if full_path:
|
||||||
available.append({"name": title, "file": filename})
|
available.append({"name": title, "file": filename})
|
||||||
|
|
||||||
return req.app.state.render(
|
return req.app.state.render(
|
||||||
@@ -71,22 +58,10 @@ async def view_doc(req: Request, filename: str):
|
|||||||
if filename not in ALLOWED_DOCS:
|
if filename not in ALLOWED_DOCS:
|
||||||
raise HTTPException(404, "File not found or not allowed.")
|
raise HTTPException(404, "File not found or not allowed.")
|
||||||
|
|
||||||
base_path = "docs"
|
base_path = _get_docs_base_path()
|
||||||
try:
|
file_path = _find_file(base_path, filename)
|
||||||
settings = get_settings()
|
|
||||||
base_path = settings.docs_path
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
if not os.path.isabs(base_path) and not os.path.exists(base_path):
|
|
||||||
if os.path.exists("/docs_root"):
|
|
||||||
base_path = "/docs_root"
|
|
||||||
else:
|
|
||||||
base_path = "."
|
|
||||||
|
|
||||||
file_path = os.path.join(base_path, filename)
|
|
||||||
|
|
||||||
if not os.path.exists(file_path):
|
if not file_path:
|
||||||
raise HTTPException(404, "File not on server.")
|
raise HTTPException(404, "File not on server.")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -94,7 +69,6 @@ async def view_doc(req: Request, filename: str):
|
|||||||
content = f.read()
|
content = f.read()
|
||||||
|
|
||||||
# Convert Markdown to HTML
|
# Convert Markdown to HTML
|
||||||
# Extensions for better rendering: tables, fenced_code
|
|
||||||
html_content = markdown.markdown(
|
html_content = markdown.markdown(
|
||||||
content,
|
content,
|
||||||
extensions=['tables', 'fenced_code', 'nl2br']
|
extensions=['tables', 'fenced_code', 'nl2br']
|
||||||
@@ -110,3 +84,33 @@ async def view_doc(req: Request, filename: str):
|
|||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise HTTPException(500, f"Error rendering document: {e}")
|
raise HTTPException(500, f"Error rendering document: {e}")
|
||||||
|
|
||||||
|
def _get_docs_base_path():
|
||||||
|
try:
|
||||||
|
settings = get_settings()
|
||||||
|
base_path = settings.docs_path
|
||||||
|
except:
|
||||||
|
base_path = "docs"
|
||||||
|
|
||||||
|
if not os.path.isabs(base_path) and not os.path.exists(base_path):
|
||||||
|
if os.path.exists("/docs_root"):
|
||||||
|
base_path = "/docs_root"
|
||||||
|
else:
|
||||||
|
base_path = "."
|
||||||
|
return base_path
|
||||||
|
|
||||||
|
def _find_file(base_path, filename):
|
||||||
|
"""Find file in base_path or immediate subdirectories."""
|
||||||
|
# 1. Direct match
|
||||||
|
direct = os.path.join(base_path, filename)
|
||||||
|
if os.path.exists(direct):
|
||||||
|
return direct
|
||||||
|
|
||||||
|
# 2. Check subdirectories (max depth 1)
|
||||||
|
if os.path.isdir(base_path):
|
||||||
|
for entry in os.scandir(base_path):
|
||||||
|
if entry.is_dir():
|
||||||
|
sub_path = os.path.join(entry.path, filename)
|
||||||
|
if os.path.exists(sub_path):
|
||||||
|
return sub_path
|
||||||
|
return None
|
||||||
|
|||||||
34
native_crumbcore_v1/fix_waldwaechter.sh
Executable file
34
native_crumbcore_v1/fix_waldwaechter.sh
Executable file
@@ -0,0 +1,34 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Patch Waldwaechter Script for Debian/Linux
|
||||||
|
# Swaps the stat command order to prefer 'stat -c' (Linux) over 'stat -f' (BSD)
|
||||||
|
|
||||||
|
TARGET_FILE="/home/crumbmission/missions/lib/waldwaechter.sh"
|
||||||
|
echo "Patching $TARGET_FILE..."
|
||||||
|
|
||||||
|
if [ ! -f "$TARGET_FILE" ]; then
|
||||||
|
echo "Error: File not found at $TARGET_FILE"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# We look for the line with the stat command issue
|
||||||
|
# The problematic line essentially tries 'stat -f' first.
|
||||||
|
# We will use sed to swap the order of the check.
|
||||||
|
|
||||||
|
# Creates a backup
|
||||||
|
cp "$TARGET_FILE" "${TARGET_FILE}.bak"
|
||||||
|
|
||||||
|
# This replacement looks complex but it hunts for the specific stat logic
|
||||||
|
# and replaces the order: try stat -c %Y first, then stat -f %m.
|
||||||
|
# Pattern match: stat -f "%m" ... || stat -c "%Y" ...
|
||||||
|
# Replacement: stat -c "%Y" ... || stat -f "%m" ...
|
||||||
|
|
||||||
|
sed -i 's/stat -f "%m" \(.*\) || stat -c "%Y" \(.*\)/stat -c "%Y" \2 || stat -f "%m" \1/' "$TARGET_FILE"
|
||||||
|
|
||||||
|
echo "Patch applied. Testing..."
|
||||||
|
|
||||||
|
# Verify if the new line prioritizes -c
|
||||||
|
if grep -q 'stat -c "%Y"' "$TARGET_FILE"; then
|
||||||
|
echo "✅ Success: stat -c is now prioritized."
|
||||||
|
else
|
||||||
|
echo "⚠️ Warning: Could not verify patch automatically. Please check manually."
|
||||||
|
fi
|
||||||
27
native_crumbcore_v1/todo.md
Normal file
27
native_crumbcore_v1/todo.md
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
# Deployment & Fixes Checklist
|
||||||
|
|
||||||
|
- [x] **Fix Deployment Issues**
|
||||||
|
- [x] **Docs Route**: Enable [docs_git.md](file:///Users/bmt/Downloads/crumbcrm_crumbcore_v1/app/docs/docs_git.md) in whitelist and copy file.
|
||||||
|
- [x] **Chat Stability**: Increase rate limit (60/min) and Nginx timeouts (300s).
|
||||||
|
- [x] **Vector Search**: Auto-detect embedding provider for robustness.
|
||||||
|
- [x] **SSL Setup**: Automate Certbot for `sslip.io` domain.
|
||||||
|
- [x] **TTYD / Missions**:
|
||||||
|
- [x] Create setup script ([setup_missions.sh](file:///Users/bmt/Downloads/crumbcrm_crumbcore_v1/native_crumbcore_v1/setup_missions.sh)).
|
||||||
|
- [x] Configure Nginx route `/terminal/`.
|
||||||
|
- [x] Fix TTYD 404 (Zombie process killed, Service restarted).
|
||||||
|
|
||||||
|
- [x] **Verification**
|
||||||
|
- [x] Push fixes to server.
|
||||||
|
- [x] Run updated [setup_missions.sh](file:///Users/bmt/Downloads/crumbcrm_crumbcore_v1/native_crumbcore_v1/setup_missions.sh).
|
||||||
|
- [x] **Verify Nginx Config on Server** (Critical Step).
|
||||||
|
- [x] Confirm TTYD accessible via browser.
|
||||||
|
- [x] Confirm Chat and Docs functional.
|
||||||
|
|
||||||
|
- [x] 'Bugsy' Health Check features.
|
||||||
|
|
||||||
|
## Cosmetics & Future (Backlog)
|
||||||
|
- [ ] **Mission Selector**: Fix [stat](file:///Users/bmt/Downloads/crumbcrm_crumbcore_v1/app/routers/chat.py#232-243) syntax error in `waldwaechter.sh` (Line 720).
|
||||||
|
- [ ] **Docs Reader**: Investigate 404/Empty list on `/docs`.
|
||||||
|
- [ ] **Debian Doktor**: Remove 'Syslog' option if not available on host.
|
||||||
|
|
||||||
|
# Status: 🌲 NEXUS READY 🌲
|
||||||
31
native_crumbcore_v1/walkthrough_missions_production.md
Normal file
31
native_crumbcore_v1/walkthrough_missions_production.md
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
# Crumbforest Native Deployment - Status Report
|
||||||
|
|
||||||
|
**Date:** 2025-12-25
|
||||||
|
**Status:** 🟢 NEXUS READY
|
||||||
|
|
||||||
|
## Achievements
|
||||||
|
We have successfully fortified the "Native Deployment" of the Crumbforest, bridging the gap between the "Atem" (Breath) and the Machine.
|
||||||
|
|
||||||
|
### 🔒 Security & SSL
|
||||||
|
- **SSL Activated**: `crumbforest.194-164-194-191.sslip.io` is now fully secured via Let's Encrypt (Certbot).
|
||||||
|
- **Nginx Hardening**: Unified configuration merging legacy routes (`/git/`, `/qdrant/`) with new protections.
|
||||||
|
|
||||||
|
### 🌲 Missions & Shell
|
||||||
|
- **Isolated Environment**: created user `crumbmission` for safe standardized mission execution.
|
||||||
|
- **TTYD Terminal**: accessible via `/terminal/`, proxied securely, running with Base Path configuration.
|
||||||
|
- **Git Integration**: `crumbmissions` repo is cloned and ready for the "Waldwächter".
|
||||||
|
|
||||||
|
### 🩺 Debian Doktor
|
||||||
|
- **New Tool**: Created [debian-doktor.sh](file:///Users/bmt/Downloads/crumbcrm_crumbcore_v1/native_crumbcore_v1/debian-doktor.sh) (symlinked as `doktor`).
|
||||||
|
- **Function**: A specialized TUI for live monitoring of CrumbCore services, logs, and health checks.
|
||||||
|
|
||||||
|
## Remaining "Cosmetics"
|
||||||
|
As Bugsy says: "A bug is just a feature waiting for a good story."
|
||||||
|
- `waldwaechter.sh`: Needs a small variable fix for the [stat](file:///Users/bmt/Downloads/crumbcrm_crumbcore_v1/app/routers/chat.py#232-243) command.
|
||||||
|
- Docs Reader: Needs path verification for the PDF/MD rendering on the server.
|
||||||
|
|
||||||
|
## Philosophy
|
||||||
|
The Crumbforest is alive. The connection between User and Machine is established.
|
||||||
|
*"Da ist nie fertig gibt es auch keine lösung"* — The loop continues.
|
||||||
|
|
||||||
|
**Ready for Git Push.**
|
||||||
Reference in New Issue
Block a user