diff --git a/README.md b/README.md
index 0c88c01..906a67a 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
-# Studio EinsZwoVier Web Application
+# studio einszwovier Web Application
-**FastAPI-based PDF print cost calculator** with automated ink coverage analysis, Matrix integration, and multi-service hub for Studio EinsZwoVier Maker Space.
+**FastAPI-based PDF print cost calculator** with automated ink coverage analysis, Matrix integration, and multi-service hub for studio einszwovier Maker Space.
[](https://www.docker.com/)
[](https://fastapi.tiangolo.com/)
@@ -33,100 +33,84 @@
```mermaid
graph TB
- subgraph "Studio EinsZwoVier Web App (Port 80)"
- LP[๐ Landing Page
studio-einszwovier.local]
- About[โน๏ธ About Page
/about
Courses + Team Info]
- Cost[๐ฐ Cost Calculator
/cost
PDF Analysis]
-
+ subgraph "studio einszwovier Web Platform"
+ LP[๐ Landing Page
Port 80]
+ About[โน๏ธ About Page
/about]
+ CostCalc[๐ฐ Cost Calculator
/cost]
+
LP --> About
- LP --> Cost
+ LP --> CostCalc
end
- subgraph "Course Management"
- CSV[๐ courses.csv
Title, Description, Dates, Image]
- Images[๐ผ๏ธ Course Images
static/images/courses/]
- Modal[๐ Image Modal
Click to Enlarge]
-
- About --> CSV
- CSV --> Images
- Images --> Modal
+ subgraph "Services Accessible from Landing Page"
+ BookStack[๏ฟฝ BookStack
Port 6875
Wissenssammlung]
+ OpenWebUI[๐ค Open WebUI
Port 8080
LLM Chatbot]
+ JupyterHub[๏ฟฝ JupyterHub
Port 8001
Python Notebooks]
+ Forgejo[๐ฆ Forgejo
Port 3003
Git Server]
+ ElementWeb[๏ฟฝ Element Web
Port 8082
Matrix Chat]
+ Portainer[๏ฟฝ Portainer
Port 9000
Admin Panel]
+
+ LP -.->|Link| BookStack
+ LP -.->|Link| OpenWebUI
+ LP -.->|Link| JupyterHub
+ LP -.->|Link| Forgejo
+ LP -.->|Link| ElementWeb
+ LP -.->|Link| Portainer
end
- subgraph "PDF Processing Pipeline"
- Upload[๐ค Upload PDF]
- Analysis[๐ฌ Analyze
- Dimensions
- Ink Coverage
- Color Detection]
- Quote[๐ Quote Generation
Per-page Breakdown]
-
- Cost --> Upload
- Upload --> Analysis
- Analysis --> Quote
- end
-
- subgraph "Matrix Integration"
- MatrixRoom[๐ฌ Matrix Room
Print Orders]
- PDF_Upload[๐ PDF Attachment]
- Summary[๐ Order Summary
German Language]
-
- Quote --> MatrixRoom
- Quote --> PDF_Upload
- Quote --> Summary
- end
-
- subgraph "Integrated Services"
- BookStack[๐ BookStack Wiki
:6875]
- Ollama[๐ค Ollama LLM
:11434]
- OpenWebUI[๐ญ Open WebUI
:8080]
- Portainer[๐ณ Portainer
:9000]
- Synapse[๐จ Matrix Synapse
:8008]
- JupyterHub[๐ JupyterHub
:8001
Drone Programming]
- Forgejo[๐ฆ Forgejo Git
:3003
Code Collaboration]
-
- LP -.-> BookStack
- LP -.-> OpenWebUI
- LP -.-> Portainer
- LP -.-> JupyterHub
- LP -.-> Forgejo
+ subgraph "Backend Services"
+ Synapse[๏ฟฝ Synapse
Port 8008
Matrix Server]
+ Ollama[๐ฎ Ollama
Port 11434
LLM Engine]
+ MariaDB[(๏ฟฝ MariaDB
BookStack DB)]
+
+ ElementWeb --> Synapse
OpenWebUI --> Ollama
- MatrixRoom --> Synapse
+ BookStack --> MariaDB
+ CostCalc --> Synapse
end
- subgraph "Educational Stack"
- Notebooks[๐ Jupyter Notebooks
Python Drone Code]
- GitRepos[๐ฆ Git Repositories
Project Source Code]
- DronePackages[๐ djitellopy
Tello SDK]
-
+ subgraph "Educational Resources"
+ Notebooks[๐ Drone Programming
djitellopy Package]
+ GitRepos[๐ฆ Code Repositories
Project Source]
+ Knowledge[๏ฟฝ Documentation
Guides & Tutorials]
+
JupyterHub --> Notebooks
Forgejo --> GitRepos
- Notebooks -.-> DronePackages
+ BookStack --> Knowledge
end
- subgraph "Data Persistence"
- Uploads[๐ data/uploads/
PDF Files]
- Courses[๐ data/courses.csv
Course List]
- MatrixDB[(๐๏ธ Matrix DB
homeserver.db)]
- BookDB[(๐๏ธ BookStack DB
MariaDB)]
- JupyterVolumes[(๐พ Jupyter Volumes
User Workspaces)]
- ForgejoData[(๐ Forgejo Data
Git Repos)]
-
- Upload --> Uploads
- CSV --> Courses
- Synapse --> MatrixDB
- BookStack --> BookDB
- JupyterHub --> JupyterVolumes
+ subgraph "Persistent Storage"
+ PDFUploads[๐ PDF Files
data/uploads/]
+ CoursesCSV[๐ Courses CSV
data/courses.csv]
+ MatrixData[(๐๏ธ Matrix Data
matrix/data/)]
+ JupyterVols[(๐พ Jupyter Volumes
User Workspaces)]
+ ForgejoData[(๐ Forgejo Data
forgejo/data/)]
+
+ CostCalc --> PDFUploads
+ About --> CoursesCSV
+ Synapse --> MatrixData
+ JupyterHub --> JupyterVols
Forgejo --> ForgejoData
end
+ subgraph "Infrastructure"
+ Watchtower[๐ Watchtower
Auto-Updates
Every 24h]
+ Network[๐ einszwovier_network
Docker Network]
+ end
+
classDef webapp fill:#e3f2fd,stroke:#1976d2,stroke-width:2px
classDef service fill:#fff3e0,stroke:#f57c00,stroke-width:2px
- classDef data fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px
- classDef matrix fill:#e8f5e9,stroke:#388e3c,stroke-width:2px
+ classDef backend fill:#e8f5e9,stroke:#388e3c,stroke-width:2px
+ classDef storage fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px
classDef edu fill:#fce4ec,stroke:#c2185b,stroke-width:2px
+ classDef infra fill:#fafafa,stroke:#616161,stroke-width:2px
- class LP,About,Cost,Upload,Analysis,Quote webapp
- class BookStack,Ollama,OpenWebUI,Portainer,Synapse,JupyterHub,Forgejo service
- class Uploads,Courses,MatrixDB,BookDB,JupyterVolumes,ForgejoData data
- class MatrixRoom,PDF_Upload,Summary,CSV,Images,Modal matrix
- class Notebooks,GitRepos,DronePackages edu
+ class LP,About,CostCalc webapp
+ class BookStack,OpenWebUI,JupyterHub,Forgejo,ElementWeb,Portainer service
+ class Synapse,Ollama,MariaDB backend
+ class PDFUploads,CoursesCSV,MatrixData,JupyterVols,ForgejoData storage
+ class Notebooks,GitRepos,Knowledge edu
+ class Watchtower,Network infra
```
---
@@ -380,7 +364,7 @@ docker compose up --build
### Project Structure
-```
+```zsh
.
โโโ main.py # FastAPI application
โโโ cost_calculator.py # PDF analysis logic
@@ -417,7 +401,7 @@ docker compose up --build
## ๐ค Contributing
-Contributions are welcome! This is part of **Studio EinsZwoVier**, a collaborative Maker Space.
+Contributions are welcome! This is part of **studio einszwovier**, a collaborative Maker Space.
1. Fork the repository
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
@@ -429,13 +413,13 @@ Contributions are welcome! This is part of **Studio EinsZwoVier**, a collaborati
## ๐ License
-This project is part of Studio EinsZwoVier educational initiative.
+This project is part of studio einszwovier educational initiative.
---
## ๐ Contact
-**Studio EinsZwoVier**
+**studio einszwovier**
Gabriele-von-Bรผlow-Gymnasium
Tile-Brรผgge-Weg 63, 13509 Berlin (Tegel)
@@ -452,8 +436,8 @@ Tile-Brรผgge-Weg 63, 13509 Berlin (Tegel)
- Ollama team for local LLM support
- BookStack for the documentation platform
- FastAPI community
-- All contributors and students of Studio EinsZwoVier
+- All contributors and students of studio einszwovier
---
-**Made with โค๏ธ at Studio EinsZwoVier Maker Space**
+Made with โค๏ธ at studio einszwovier Maker Space
diff --git a/data/KURSE_README.md b/data/KURSE_README.md
deleted file mode 100644
index 9cde519..0000000
--- a/data/KURSE_README.md
+++ /dev/null
@@ -1,147 +0,0 @@
-# Kurse verwalten
-
-## Kurse bearbeiten
-
-Die aktuellen Kurse werden aus der Datei `courses.csv` geladen und automatisch auf der "รber uns" Seite angezeigt.
-
-### CSV-Format
-
-Die Datei hat fรผnf Spalten (alle auรer `title` sind optional):
-- `title` - Der Titel des Kurses (erforderlich)
-- `description` - Eine kurze Beschreibung (optional)
-- `dates` - Termine und Zeiten (optional)
-- `offen_fuer` - Zielgruppe/Altersgruppe (optional)
-- `image` - Pfad zum Kursbild (optional)
-
-**Beispiel:**
-```csv
-title,description,dates,offen_fuer,image
-Lรถten und Leuchten,Herstellung von Nachttischleuchten mit 3D-Design und Lรถttechnik,"Di 15.10. 14:00-16:00",Klasse 7-9,/static/images/courses/loeten.jpg
-Die Vogelvilla,Bau von Vogelhรคusern mit Lasercutter und Holzbearbeitung,"Mi 23.10. 13:00-15:00",alle Schรผler:innen,
-Robotik Intro,,,ab Klasse 5,/static/images/courses/robotik.jpg
-```
-
-### Kursbilder hinzufรผgen
-
-1. Speichere das Bild in `/static/images/courses/`
-2. Verwende den Pfad `/static/images/courses/dein-bild.jpg` in der CSV
-3. Empfohlene Bildgrรถรe: mindestens 640x400 Pixel
-4. Unterstรผtzte Formate: JPG, PNG, WebP
-
-**Tipp:** Wenn kein Bild angegeben ist, wird der Kurs ohne Bild angezeigt (nur Text).### Felder im Detail
-
-#### `title` (erforderlich)
-Der Kursname - wird immer angezeigt.
-
-#### `description` (optional)
-Kurzbeschreibung des Kurses. Wird nur angezeigt, wenn vorhanden.
-
-#### `dates` (optional)
-Termine und Uhrzeiten. Kann mehrere Termine enthalten:
-- Einzelner Termin: `Mi 23.10. 13:00-15:00`
-- Mehrere Termine: `Di 15.10. 14:00-16:00, Do 17.10. 14:00-16:00`
-- Bei Terminen mit Kommas: In Anfรผhrungszeichen setzen
-
-#### `offen_fuer` (optional)
-Freitextfeld fรผr die Zielgruppe:
-- `Klasse 7-9`
-- `alle Schรผler:innen`
-- `ab 14 Jahren`
-- `Oberstufe`
-
-### Kurs hinzufรผgen
-
-Einfach eine neue Zeile am Ende der Datei hinzufรผgen:
-```csv
-title,description,dates,offen_fuer
-Lรถten und Leuchten,Herstellung von Nachttischleuchten mit 3D-Design und Lรถttechnik,"Di 15.10. 14:00-16:00, Do 17.10. 14:00-16:00",Klasse 7-9
-Neuer Kurs,Beschreibung des neuen Kurses,"Mo 20.10. 15:00-17:00",Klasse 8-10
-```
-
-### Fehlende Informationen
-
-Alle Felder auรer `title` sind optional. Beispiele:
-
-**Nur Titel und Beschreibung:**
-```csv
-title,description,dates,offen_fuer
-Workshop XYZ,Toller Workshop รผber Making,,
-```
-
-**Nur Titel und Termine:**
-```csv
-title,description,dates,offen_fuer
-Workshop ABC,,"Fr 25.10. 14:00",
-```
-
-**Nur Titel und Zielgruppe:**
-```csv
-title,description,dates,offen_fuer
-Workshop 123,,,Klasse 9-10
-```
-
-### Kurs entfernen
-
-Einfach die entsprechende Zeile lรถschen.
-
-### Wichtig
-
-- **Keine Anfรผhrungszeichen** verwenden, auรer der Text enthรคlt ein Komma
-- **Keine Zeilenumbrรผche** innerhalb der Beschreibung
-- Die erste Zeile (`title,description,dates,offen_fuer,image`) muss erhalten bleiben
-- Nach dem Speichern wird die รnderung sofort auf der Website sichtbar
-
-### Darstellung auf der Website
-
-Die Kurse werden in einem **Grid-Layout** mit **Karten-Design** angezeigt:
-
-- **Groรe, fette Titel** (1.5em) mit pinker Unterstreichung
-- **Hover-Effekt**: Karten heben sich beim รberfahren an
-- **Responsive**: Auf Mobilgerรคten eine Spalte, auf Desktop mehrere Spalten
-- **Kursbilder** (optional): 200px hoch, oben auf der Karte
-- **Metadata** am unteren Rand: Datum und Zielgruppe mit Icons
-
-**Beispiel mit Bild:**
-```
-โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
-โ [Kursbild 640x400] โ
-โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
-โ Lรถten und Leuchten โ โ 1.5em, fett, pink unterstrichen
-โ โโโโโโโโโโโโโโโโโโโโโ โ
-โ โ
-โ Herstellung von Nachtisch- โ โ Beschreibung
-โ leuchten mit 3D-Design โ
-โ โ
-โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
-โ ๐
Dez. '24 โ โ Metadata mit Icons
-โ ๐ฅ Klasse 5-6 โ
-โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
-```
-
-**Beispiel ohne Bild:**
-```
-โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
-โ Textiles Plotten โ โ Direkt der Titel
-โ โโโโโโโโโโโโโโโโโ โ
-โ โ
-โ Erschaffe deine eigenen โ
-โ Klamottendesigns โ
-โ โ
-โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
-โ ๐
Mai '25 โ
-โ ๐ฅ Jhg. 9 โ
-โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
-```
-
-### Live-Update
-
-Die Kurse werden bei jedem Seitenaufruf neu geladen. Es ist **kein Neustart** des Servers erforderlich!
-
-### Keine Kurse anzeigen
-
-Wenn keine Kurse stattfinden, einfach alle Zeilen auรer der Kopfzeile lรถschen:
-```csv
-title,description
-```
-
-Die Seite zeigt dann: "Aktuell sind keine Kurse geplant. Schaut bald wieder vorbei!"
diff --git a/docker-compose.yml b/docker-compose.yml
index d5fcf01..f37d89f 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -264,6 +264,31 @@ services:
- "description=Forgejo Git Server"
- "maintainer=Studio EinsZwoVier"
+ element-web:
+ image: vectorim/element-web:latest
+ container_name: element-web
+ restart: unless-stopped
+ ports:
+ - "${ELEMENT_PORT:-8082}:80"
+ volumes:
+ - ./element-web/config.json:/app/config.json:ro
+ mem_limit: 512m
+ cpus: 0.5
+ mem_reservation: 128m
+ healthcheck:
+ test: ["CMD-SHELL", "curl -f http://localhost:80 || exit 1"]
+ interval: 30s
+ timeout: 10s
+ retries: 3
+ start_period: 20s
+ depends_on:
+ synapse:
+ condition: service_started
+ labels:
+ - "com.centurylinklabs.watchtower.enable=true"
+ - "description=Element Web Matrix Client"
+ - "maintainer=studio einszwovier"
+
volumes:
portainer_data:
driver: local
diff --git a/element-web/README.md b/element-web/README.md
new file mode 100644
index 0000000..06c8a5c
--- /dev/null
+++ b/element-web/README.md
@@ -0,0 +1,74 @@
+# Element Web - Matrix Client
+
+Element Web is a browser-based Matrix client that connects to the studio einszwovier Matrix (Synapse) server.
+
+## Configuration
+
+The `config.json` file configures Element Web to connect to the local Synapse homeserver at `http://einszwovier.local:8008`.
+
+### Key Settings
+
+- **Homeserver**: Points to local Synapse instance
+- **Server name**: `einszwovier.local`
+- **Brand**: "studio einszwovier Chat"
+- **Default language**: German (DE)
+- **Default theme**: Light mode
+- **Guest access**: Enabled (users can preview without account)
+
+## Access
+
+- **URL**: `http://einszwovier.local:8082`
+- **No app required**: Works in any modern web browser
+- **Mobile friendly**: Responsive design works on phones and tablets
+
+## Usage
+
+1. Navigate to `http://einszwovier.local:8082`
+2. Click "Sign In" or "Create Account"
+3. Use existing Matrix credentials or create a new account
+4. Join rooms (e.g., the print order room)
+5. Chat, share files, and collaborate!
+
+## Features
+
+- โ
End-to-end encryption
+- โ
File sharing and media uploads
+- โ
Read receipts and typing indicators
+- โ
Markdown formatting
+- โ
Voice and video calls (browser-to-browser)
+- โ
Room directory browsing
+- โ
Cross-device message sync
+
+## Updates
+
+Element Web is managed by Watchtower and updates automatically with the rest of the stack.
+
+## Troubleshooting
+
+**Can't connect to homeserver:**
+
+- Ensure Synapse is running: `docker-compose ps synapse`
+- Check Synapse logs: `docker-compose logs synapse`
+- Verify hostname resolution: `ping einszwovier.local`
+
+**Login fails:**
+
+- Verify credentials with Matrix admin
+- Check Synapse registration settings in `matrix/data/homeserver.yaml`
+
+**Performance issues:**
+
+- Element Web is resource-intensive for large rooms
+- Consider using Hydrogen (lighter client) for low-end devices
+- Clear browser cache and reload
+
+## Security Notes
+
+- Element Web runs on HTTP in the local network
+- For production/internet exposure, configure HTTPS via reverse proxy
+- User sessions are stored in browser local storage
+- Encryption keys are managed client-side
+
+---
+
+**Maintained by studio einszwovier**
diff --git a/element-web/config.json b/element-web/config.json
new file mode 100644
index 0000000..3497c3f
--- /dev/null
+++ b/element-web/config.json
@@ -0,0 +1,27 @@
+{
+ "default_server_config": {
+ "m.homeserver": {
+ "base_url": "http://einszwovier.local:8008",
+ "server_name": "einszwovier.local"
+ }
+ },
+ "brand": "studio einszwovier Chat",
+ "disable_guests": false,
+ "disable_login_language_selector": false,
+ "default_country_code": "DE",
+ "show_labs_settings": false,
+ "default_theme": "light",
+ "settingDefaults": {
+ "language": "de"
+ },
+ "room_directory": {
+ "servers": [
+ "einszwovier.local"
+ ]
+ },
+ "enable_presence_by_hs_url": {
+ "http://einszwovier.local:8008": true
+ },
+ "terms_and_conditions_links": [],
+ "privacy_policy_url": null
+}
\ No newline at end of file
diff --git a/main.py b/main.py
index 89a5b61..3f477a3 100644
--- a/main.py
+++ b/main.py
@@ -19,6 +19,7 @@ BOOKSTACK_PORT = os.environ.get("BOOKSTACK_PORT", "6875")
OPENWEBUI_PORT = os.environ.get("OPENWEBUI_PORT", "8080")
PORTAINER_PORT = os.environ.get("PORTAINER_PORT", "9000")
FORGEJO_PORT = os.environ.get("FORGEJO_PORT", "3003")
+ELEMENT_PORT = os.environ.get("ELEMENT_PORT", "8082")
# Courses CSV path
COURSES_CSV = Path("data/courses.csv")
@@ -86,6 +87,7 @@ async def welcome(request: Request):
"openwebui_port": OPENWEBUI_PORT,
"portainer_port": PORTAINER_PORT,
"forgejo_port": FORGEJO_PORT,
+ "element_port": ELEMENT_PORT,
},
)
@@ -120,7 +122,8 @@ async def cost_dashboard(request: Request):
@app.post("/upload")
async def upload_file(request: Request, file: UploadFile = File(...)):
- if not allowed_file(file.filename):
+ # Ensure filename is present (UploadFile.filename can be None) before validation
+ if not file.filename or not allowed_file(file.filename):
return templates.TemplateResponse(
"cost-calculator.html",
{"request": request, "error": "Unsupported file type. Only PDF allowed."},
diff --git a/templates/base.html b/templates/base.html
index 3d31d82..7bf6cb0 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -3,14 +3,14 @@
+