update: Major Overhaul to all subsystems

This commit is contained in:
2026-03-07 11:32:18 +02:00
parent 810e81b323
commit c62188fda6
107 changed files with 20414 additions and 929 deletions

92
.claude/crm-step-09.md Normal file
View File

@@ -0,0 +1,92 @@
# CRM Step 09 — Integration: Nextcloud WebDAV
## Context
Read `.claude/crm-build-plan.md` for full context and IMPORTANT NOTES.
Steps 0108 must be complete.
## Task
Connect the console to Nextcloud via WebDAV so that:
1. Files in a customer's Nextcloud folder are listed in the Media tab automatically
2. Uploading a file from the console sends it to Nextcloud
3. Files can be downloaded/previewed via a backend proxy
## Backend changes
### 1. Add Nextcloud settings to `backend/config.py`
```python
nextcloud_url: str = "https://nextcloud.bonamin.gr" # e.g. https://cloud.example.com
nextcloud_email: str = "bellsystems.gr@gmail.com"
nextcloud_username: str = "bellsystems-console"
nextcloud_password: str = "ydE916VdaQdbP2CQGhD!"
nextcloud_app_password: str = "rtLCp-NCy3y-gNZdg-38MtN-r8D2N"
nextcloud_base_path: str = "BellSystems" # root folder inside Nextcloud
```
### 2. Create `backend/crm/nextcloud.py`
WebDAV client using `httpx` (already available). Functions:
```python
async def list_folder(nextcloud_path: str) -> List[dict]:
"""
PROPFIND request to Nextcloud WebDAV.
Returns list of {filename, path, mime_type, size, last_modified, is_dir}
Parse the XML response (use xml.etree.ElementTree).
URL: {nextcloud_url}/remote.php/dav/files/{username}/{nextcloud_base_path}/{nextcloud_path}
"""
async def upload_file(nextcloud_path: str, filename: str, content: bytes, mime_type: str) -> str:
"""
PUT request to upload file.
Returns the full nextcloud_path of the uploaded file.
"""
async def download_file(nextcloud_path: str) -> tuple[bytes, str]:
"""
GET request. Returns (content_bytes, mime_type).
"""
async def delete_file(nextcloud_path: str) -> None:
"""
DELETE request.
"""
```
Use HTTP Basic Auth with nextcloud_username/nextcloud_password.
If nextcloud_url is empty string, raise HTTPException 503 "Nextcloud not configured".
### 3. Add to `backend/crm/router.py`
**Media/Nextcloud endpoints:**
`GET /api/crm/nextcloud/browse?path=05_Customers/FOLDER`
→ calls `list_folder(path)`, returns file list
`GET /api/crm/nextcloud/file?path=05_Customers/FOLDER/photo.jpg`
→ calls `download_file(path)`, returns `Response(content=bytes, media_type=mime_type)`
→ This is the proxy endpoint — frontend uses this to display images
`POST /api/crm/nextcloud/upload`
→ accepts `UploadFile` + form field `nextcloud_path` (destination folder)
→ calls `upload_file(...)`, then calls `create_media(...)` to save the metadata record
→ returns the created `MediaInDB`
`DELETE /api/crm/nextcloud/file?path=...`
→ calls `delete_file(path)`, also deletes the matching `crm_media` record if found
## Frontend changes
### Update Media tab in `CustomerDetail.jsx`
- On load: if `customer.nextcloud_folder` is set, fetch `GET /api/crm/nextcloud/browse?path={customer.nextcloud_folder}` and merge results with existing `crm_media` records. Show files from both sources — deduplicate by nextcloud_path.
- Image files: render as `<img src="/api/crm/nextcloud/file?path=..." />` via the proxy endpoint
- Other files: show as a download link hitting the same proxy endpoint
- Upload button: file picker → POST to `/api/crm/nextcloud/upload` with file + destination path (default to customer's Sent Media subfolder)
- Show upload progress indicator
### Update Media tab in `CustomerDetail.jsx` — subfolder selector
When uploading, let user choose subfolder: "Sent Media" | "Received Media" | "Internal" (maps to direction field too)
## Notes
- `httpx` is likely already in requirements. If not, add it: `httpx>=0.27.0`
- PROPFIND response is XML (DAV namespace). Parse `D:response` elements, extract `D:href` and `D:prop` children.
- The proxy approach means the VPS never stores files — it just streams them through from Nextcloud
- nextcloud_base_path in config allows the root to be `BellSystems` so paths in DB are relative to that root