design final

This commit is contained in:
Aron Petau 2025-10-07 13:02:29 +02:00
parent a0d2188f6f
commit 99a690972e
1414 changed files with 2389 additions and 1455 deletions

BIN
.DS_Store vendored

Binary file not shown.

78
.dockerignore Normal file
View file

@ -0,0 +1,78 @@
# Git
.git/
.gitignore
.gitattributes
# Documentation
README.md
*.md
DEPLOYMENT.md
MIGRATION.md
ROBUSTNESS_ANALYSIS.md
CORRECTIONS.md
# Docker
docker-compose.yml
docker-compose*.yml
Dockerfile
.dockerignore
# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
*.egg-info/
.venv/
venv/
ENV/
env/
.pytest_cache/
.coverage
.mypy_cache/
# IDE
.vscode/
.idea/
*.swp
*.swo
*~
.DS_Store
.continue/
# Data volumes (don't copy into image)
data/uploads/*
bookstack/
docmost/
matrix/
ollama/
open-webui/
backup/
backups/
# Backup files
*.backup
*.bak
*.tar.gz
*.zip
# Logs
*.log
logs/
# Environment
.env
.env.local
.env.example
# Scripts not needed in container
backup.sh
restore.sh
get_room_id.py
test_send_pdf.py
run_app.py
# Unused config files
unused_composes.yml
bookstack.yml

85
.env.example Normal file
View file

@ -0,0 +1,85 @@
# ========================================
# Studio EinsZwoVier - Environment Configuration
# ========================================
# Copy this file to .env and update with your settings
# ========================================
# SERVER CONFIGURATION
# ========================================
# Change this to your server's hostname or IP address
SERVER_HOSTNAME=einszwovier.local
# Environment
ENVIRONMENT=production
# ========================================
# PRINT CALCULATOR SETTINGS
# ========================================
RATE_PER_M2_BLACK=4.0
RATE_PER_M2_COLOR=5.0
# ========================================
# MATRIX SERVER SETTINGS
# ========================================
# Matrix user format: @username:${SERVER_HOSTNAME}
MATRIX_USER="@einszwovier:${SERVER_HOSTNAME}"
MATRIX_PASS="your_matrix_password_here"
MATRIX_HOMESERVER="http://${SERVER_HOSTNAME}:8008"
# Matrix Room ID (get this after first setup using get_room_id.py)
# Format: !roomid:${SERVER_HOSTNAME}
MATRIX_ROOM="!eFWbWEnYsgeIKqyfjw:${SERVER_HOSTNAME}"
# ========================================
# BOOKSTACK (WIKI) SETTINGS
# ========================================
BOOKSTACK_VERSION=latest
BOOKSTACK_PORT=6875
BOOKSTACK_APP_URL=http://${SERVER_HOSTNAME}:6875
# Generate new key with: docker run --rm linuxserver/bookstack php artisan key:generate --show
BOOKSTACK_APP_KEY=base64:YOUR_BOOKSTACK_KEY_HERE
# ========================================
# DATABASE SETTINGS (BookStack)
# ========================================
DB_HOST=bookstack-mariadb
DB_PORT=3306
DB_DATABASE=bookstack
DB_USERNAME=bookstack
DB_PASSWORD=your_secure_database_password_here
# MariaDB root password
MARIADB_ROOT_PASSWORD=your_secure_root_password_here
# ========================================
# MATRIX SYNAPSE SETTINGS
# ========================================
SYNAPSE_PORT=8008
SYNAPSE_SERVER_NAME=${SERVER_HOSTNAME}
# ========================================
# OLLAMA (LLM) SETTINGS
# ========================================
OLLAMA_PORT=11434
# ========================================
# OPEN-WEBUI SETTINGS
# ========================================
OPENWEBUI_PORT=8080
OPENWEBUI_OLLAMA_BASE_URL=http://ollama:11434
# ========================================
# PORTAINER (ADMIN) SETTINGS
# ========================================
PORTAINER_PORT=9000
# ========================================
# NOTES FOR NEW SERVER SETUP
# ========================================
# 1. Copy this file to .env
# 2. Update SERVER_HOSTNAME to your new server
# 3. Generate new BOOKSTACK_APP_KEY
# 4. Change all passwords to secure values
# 5. After starting Matrix, run get_room_id.py to get MATRIX_ROOM
# 6. Run: docker-compose up -d --build

144
.github/copilot-instructions.md vendored Normal file
View file

@ -0,0 +1,144 @@
# Copilot Instructions for Studio EinsZwoVier Web Application
## Project Overview
This is a **FastAPI-based web application** serving as a multi-service hub for Studio EinsZwoVier, a collaborative print studio. The main app provides a PDF print cost calculator with automated ink coverage analysis, integrated with a Matrix server for order collection and payment tracking.
## Architecture & Service Boundaries
### Core FastAPI Application (`main.py`, port 80→8000)
- **Landing page** (`/`) with presence state management (studio open/closed indicator)
- **Cost calculator** (`/cost`) for PDF analysis and price estimation
- **About page** (`/about`)
- **Order submission** via Matrix integration
### Multi-Container Stack (Docker Compose)
The application orchestrates 7+ containerized services:
- **web**: FastAPI app (this repo's code)
- **synapse**: Matrix server for print job collection (port 8008)
- **ollama**: Local LLM inference (port 11434)
- **open-webui**: LLM chat interface (port 8080)
- **bookstack + mariadb**: Documentation wiki
- **docmost + postgres + redis**: Knowledge management system
- **watchtower**: Auto-updates all containers every 24h
### PDF Processing Pipeline (`cost_calculator.py`)
1. **Upload** → Validate `.pdf` extension only
2. **Page-by-page analysis**:
- Extract dimensions from `CropBox` (preferred) or `MediaBox`
- Convert PDF points to meters: `(points / 72.0) * 0.0254`
- Render at 150 DPI using `pdf2image`
- Calculate ink coverage: pixels with RGB < 250 = ink
- Detect color vs B&W: HSV saturation analysis (>0.1% pixels with sat>10 = color)
3. **Cost calculation**: area (m²) × rate (from env vars `RATE_PER_M2_BLACK`/`COLOR`)
### Matrix Integration (`mailer.py`)
- Uses `matrix-nio` async client to send orders
- **Critical**: Must set `MATRIX_USER`, `MATRIX_PASS`, `MATRIX_HOMESERVER` env vars
- Sends structured summary (German language) + PDF upload to hardcoded room ID
- **Synchronous wrapper** `send_order_sync()` bridges async/sync contexts for FastAPI
## Developer Workflows
### Local Development
```bash
# Option 1: Direct Python execution (auto-reload)
python run_app.py
# Option 2: Full stack with Docker Compose
docker-compose up --build -d
# Access at: http://localhost (or einszwovier.local in local network)
```
### Environment Configuration
Create `.env` file with:
```bash
# Print rates (€/m²)
RATE_PER_M2_BLACK=4.0
RATE_PER_M2_COLOR=5.0
# Matrix integration
MATRIX_USER=@bot:homeserver
MATRIX_PASS=secret
MATRIX_HOMESERVER=http://einszwovier.local:8008
# Service ports
SYNAPSE_PORT=8008
OLLAMA_PORT=11434
OPENWEBUI_PORT=8080
BOOKSTACK_PORT=6875
# BookStack/DB configs (see docker-compose.yml for full list)
```
### Debugging Matrix Integration
Use `get_room_id.py` to discover Matrix room IDs:
```python
# Connects to local Matrix server, joins room, prints room IDs
python get_room_id.py
```
Then update the hardcoded room ID in `main.py` line 127: `room_id="!eFW..."`
## Project-Specific Conventions
### Presence State Management
- In-memory global `presence_state` dict tracks studio open/closed status
- API endpoints: `GET /presence`, `POST /presence`, `POST /presence/toggle`
- Background task `update_presence_periodically()` runs every 5 min (currently no-op placeholder)
### German UI/UX
- Templates use German language exclusively
- Error/success messages in German (e.g., "Dein Auftrag wurde erfolgreich gesendet!")
- Cost calculator displays "S/W" (Schwarz/Weiß) vs "Farbe" (color)
### File Organization
- **Uploads persist** in `data/uploads/` (mounted volume in Docker)
- **Templates** in `templates/` (Jinja2, extending `base.html`)
- **Static assets** in `static/` (CSS, fonts, images)
- **Service data** in dedicated dirs: `bookstack/`, `docmost/`, `matrix/`, `ollama/`, `open-webui/`
### Docker Production Command
The `Dockerfile` uses **Gunicorn with Uvicorn workers** for production:
```bash
gunicorn main:app -k uvicorn.workers.UvicornWorker --bind 0.0.0.0:8000 --workers 4 --timeout 120
```
But `docker-compose.yml` overrides with `--reload` for dev convenience.
## Critical Integration Points
### PDF Analysis Return Structure
`analyze_pdf()` returns:
```python
{
"filename": str,
"pages": [{"page": int, "width_m": float, "height_m": float,
"area_m2": float, "ink_pct": float, "is_color": bool, "cost": float}],
"total_area_black": float,
"total_area_color": float,
"total_cost_black": float,
"total_cost_color": float,
"grand_total": float
}
```
### Matrix Room ID Hardcoding
⚠️ **Known technical debt**: Room ID is hardcoded in `send_order_endpoint()`. Update when Matrix server changes or for different deployment environments.
### Async/Sync Bridge Pattern
Matrix client is async, but FastAPI endpoint is sync. Pattern used:
```python
def send_order_sync(...):
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop.run_until_complete(send_order(...))
loop.close()
```
## External Dependencies
- **Poppler** (system): Required for `pdf2image` (installed in Dockerfile)
- **OpenCV** (system): Requires `libgl1`, `libglib2.0-0` (installed in Dockerfile)
- **Font files**: Custom fonts in `static/fonts/` (BauPro, HealTheWebA/B, SISTEMAS)
## Testing & Utilities
- `test_send_pdf.py`: Manual test script for Matrix integration
- No automated test suite currently implemented

111
.gitignore vendored
View file

@ -1,8 +1,105 @@
# Environment variables (NEVER commit these!)
.env
__pycache__
.continue
.venv
matrix/data
ollama
open-webui
.env.local
.env.*.local
# Keep .env.example for reference
!.env.example
# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# Virtual environments
.venv/
venv/
ENV/
env/
# IDE and editors
.vscode/
.idea/
*.swp
*.swo
*~
.DS_Store
.continue/
# Docker data volumes (NEVER commit these - contain private data!)
matrix/data/
matrix/*.signing.key
ollama/
open-webui/
data/uploads/
data/*.pdf
docmost/db/
docmost/redis/
docmost/storage/
bookstack/bookstack_db_data/
bookstack/bookstack_app_data/
# Private keys and certificates
*.key
*.pem
!dhparams.pem
*.crt
*.p12
*.pfx
# Backup files
*.backup
*.bak
backup/
backups/
# Logs
*.log
logs/
*.log.*
# OS specific
.DS_Store
Thumbs.db
desktop.ini
# Testing
.pytest_cache/
.coverage
htmlcov/
.tox/
.hypothesis/
# Jupyter Notebook
.ipynb_checkpoints
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Database files
*.db
*.db-shm
*.db-wal
*.sqlite
*.sqlite3

78
DEV_SETUP.md Normal file
View file

@ -0,0 +1,78 @@
# Development Setup Guide
## Quick Start for Local Development
The `run_app.py` script mimics Docker production conditions while enabling fast iteration.
### Standard Development Mode (Recommended)
```bash
python run_app.py
```
**Features:**
- ✓ Loads environment variables from `.env` file
- ✓ Single Uvicorn worker with **fast auto-reload**
- ✓ Mimics Docker environment (`PYTHONUNBUFFERED=1`)
- ✓ Creates upload directory automatically
- ✓ Binds to `0.0.0.0:8000` (same as Docker)
**Best for:** Quick code iterations, template changes, debugging
### Production-Like Mode
```bash
python run_app.py --gunicorn
```
**Features:**
- ✓ Gunicorn with 4 Uvicorn workers (exactly like Docker)
- ✓ 120s timeout (same as Docker)
- ✓ Gunicorn's reload (slower but tests production server)
- ✓ Tests multi-worker behavior
**Best for:** Testing before deploying to Docker, load testing
## Environment Variables
The script automatically loads `.env` file variables:
- `RATE_PER_M2_BLACK` - Black & white print rate (default: 4.0)
- `RATE_PER_M2_COLOR` - Color print rate (default: 5.0)
- `SERVER_HOSTNAME` - Server hostname (default: einszwovier.local)
- `BOOKSTACK_PORT`, `OPENWEBUI_PORT`, `PORTAINER_PORT` - Service ports
- `MATRIX_USER`, `MATRIX_PASS`, `MATRIX_HOMESERVER` - Matrix integration
## Comparison: Dev vs Docker
| Feature | `python run_app.py` | `docker-compose up` |
|---------|---------------------|---------------------|
| Server | Uvicorn (single worker) | Gunicorn + Uvicorn (4 workers) |
| Auto-reload | ✓ Fast (watches all files) | ✓ Slow (Docker rebuild) |
| Env loading | `.env` file | `.env` file |
| Port | 8000 | 80→8000 |
| Memory limit | None | 1GB limit |
| Health checks | No | Yes |
| Dependencies | All services | Isolated |
## Troubleshooting
### `.env` file not found
Create `.env` file from template:
```bash
cp .env.example .env
```
### Port 8000 already in use
```bash
# Find and kill process
lsof -ti:8000 | xargs kill -9
```
### Environment variables not loading
Ensure `python-dotenv` is installed:
```bash
pip install python-dotenv
```
### Testing Matrix integration locally
1. Start Matrix server: `docker-compose up synapse -d`
2. Run app: `python run_app.py`
3. Submit test order via `/cost` endpoint

View file

@ -8,29 +8,35 @@ ENV PYTHONUNBUFFERED=1
# --- Set working directory ---
WORKDIR /cost-assistant
# --- Install system dependencies (minimal for OpenCV, Poppler, PDF/image handling) ---
# --- Install system dependencies (cached until packages change) ---
RUN apt-get update && apt-get install -y --no-install-recommends \
libgl1 \
libglib2.0-0 \
poppler-utils \
curl \
&& rm -rf /var/lib/apt/lists/*
# --- Copy requirements and install Python dependencies ---
# --- Install Python dependencies (cached until requirements.txt changes) ---
COPY requirements.txt .
RUN python -m pip install --no-cache-dir -r requirements.txt
# --- Copy project files ---
COPY . .
# --- Create user early (cached, rarely changes) ---
RUN groupadd -r appuser && useradd -r -g appuser appuser
# --- Create upload folder and set permissions ---
RUN mkdir -p data/uploads \
&& groupadd -r appuser && useradd -r -g appuser appuser \
&& chown -R appuser:appuser data/uploads
# --- Copy only necessary application files ---
COPY --chown=appuser:appuser main.py .
COPY --chown=appuser:appuser cost_calculator.py .
COPY --chown=appuser:appuser mailer.py .
COPY --chown=appuser:appuser templates/ ./templates/
COPY --chown=appuser:appuser static/ ./static/
# --- Create upload folder ---
RUN mkdir -p data/uploads && chown -R appuser:appuser data/uploads
# --- Switch to non-root user for security ---
USER appuser
# --- Expose internal port (optional, handled via Docker Compose) ---
# --- Expose internal port ---
EXPOSE 8000
# --- Run Gunicorn with Uvicorn worker for production ---

231
PORTAINER_QUICKREF.md Normal file
View file

@ -0,0 +1,231 @@
# Portainer Quick Reference
## 🚀 Quick Start
### Access Portainer
```
http://localhost:9000
or
http://einszwovier.local:9000
```
### First Login
1. Create admin account
2. Select "Local" environment
3. Done!
## 📊 Stack Overview: studio-einszwovier
| Service | Port | Health Check | Purpose |
|---------|------|--------------|---------|
| web | 80 | ✅ | PDF Cost Calculator |
| bookstack | 6875 | ✅ | Documentation Wiki |
| bookstack-mariadb | - | ✅ | Database |
| synapse | 8008 | ✅ | Matrix Server |
| ollama | 11434 | ✅ | Local LLM |
| open-webui | 8080 | ✅ | LLM Interface |
| watchtower | - | ✅ | Auto-Updates |
| portainer | 9000 | ✅ | Management UI |
## 🎯 Common Tasks
### View All Services
**Portainer**: Stacks → studio-einszwovier
**CLI**: `docker-compose ps`
### Restart a Service
**Portainer**: Containers → [service] → Restart
**CLI**: `docker-compose restart [service]`
### View Logs
**Portainer**: Containers → [service] → Logs
**CLI**: `docker-compose logs -f [service]`
### Check Health Status
**Portainer**: Containers → Look for 🟢/🔴 indicator
**CLI**: `docker-compose ps` (shows (healthy) or (unhealthy))
### Stop Everything
**Portainer**: Stacks → studio-einszwovier → Stop
**CLI**: `docker-compose stop`
### Start Everything
**Portainer**: Stacks → studio-einszwovier → Start
**CLI**: `docker-compose start`
### Update a Service
**Portainer**: Containers → [service] → Recreate
**CLI**: `docker-compose pull [service] && docker-compose up -d [service]`
## 🔍 Troubleshooting
### Service Shows 🔴 Unhealthy
1. Click container → Logs
2. Look for errors
3. Check health check output in Inspect tab
4. Restart if needed
### Can't Access Portainer
```bash
docker-compose restart portainer
# Wait 30 seconds
# Try again at http://localhost:9000
```
### Service Won't Start
1. Check Portainer logs for the service
2. Look for dependency issues (red containers)
3. Check resource limits (Stats tab)
4. Verify environment variables in Container → Env
## 📋 Health Check Endpoints
Test manually if needed:
```bash
# Web App
curl http://localhost/
# BookStack
curl http://localhost:6875/
# Matrix Synapse
curl http://localhost:8008/health
# Ollama
curl http://localhost:11434/api/tags
# Open WebUI
curl http://localhost:8080/
# Portainer
curl http://localhost:9000/api/system/status
```
## 🏷️ Labels in Portainer
All services are tagged with:
- **description**: What it does
- **maintainer**: Studio EinsZwoVier
- **watchtower.enable**: Auto-update enabled
Filter by labels in Portainer UI!
## 🔄 Auto-Updates (Watchtower)
- **Schedule**: Every 24 hours
- **Action**: Pulls latest images and recreates containers
- **Which services**: Only those with `watchtower.enable=true` label
- **View updates**: Containers → watchtower → Logs
### Disable Auto-Update for a Service
Edit docker-compose.yml, remove:
```yaml
labels:
- "com.centurylinklabs.watchtower.enable=true"
```
## 📈 Resource Monitoring
**Portainer**: Container → Stats
Shows:
- CPU usage (%)
- Memory usage (MB / GB)
- Network I/O
- Block I/O
**Limits Set**:
- web: 1GB RAM, 1 CPU
- ollama: 8GB RAM, 4 CPU
## 🌐 Network: einszwovier_network
All services communicate on this network.
**View**: Networks → einszwovier_network → Connected containers
## 💾 Volumes
| Volume | Used By | Purpose |
|--------|---------|---------|
| portainer_data | portainer | Portainer config |
| ./data/uploads | web | PDF uploads |
| ./bookstack/* | bookstack | Wiki data |
| ./matrix/data | synapse | Matrix data |
| ./ollama | ollama | LLM models |
| ./open-webui | open-webui | Chat history |
**Backup**: See `backup.sh` script
## ⚙️ Useful Portainer Features
### Execute Shell Commands
1. Containers → [service] → Console
2. Select `/bin/bash` or `/bin/sh`
3. Click Connect
4. Run commands
### View Container Config
1. Containers → [service] → Inspect
2. See full JSON configuration
3. Copy environment variables
4. Check volume mounts
### Duplicate Container
1. Containers → [service] → Duplicate/Edit
2. Modify settings
3. Deploy as new container
### Container Template
1. App Templates → Custom Templates
2. Create from existing container
3. Reuse configuration
## 🎨 Status Icons
| Icon | Meaning | Action |
|------|---------|--------|
| 🟢 | Healthy | None needed |
| 🟡 | Starting | Wait (within start_period) |
| 🔴 | Unhealthy | Check logs |
| ⚫ | Stopped | Start container |
| 🔄 | Restarting | Wait or investigate |
## 📞 Quick Help
### "I can't find my containers!"
- Go to **Home**
- Select **Local** environment
- Go to **Stacks** → studio-einszwovier
### "Health checks keep failing"
- Increase `start_period` in docker-compose.yml
- Check service logs for actual errors
- Verify network connectivity
### "Portainer shows wrong status"
- Refresh page (F5)
- Check "Last Updated" timestamp
- Restart Portainer if stale
## 🔐 Security Notes
- Portainer admin password is set on first login (save it!)
- Docker socket mounted = full control (use carefully)
- HTTPS available on port 9443 (configure in Portainer settings)
## 📱 Mobile Access
Portainer works great on mobile:
1. Open browser on phone
2. Navigate to `http://[server-ip]:9000`
3. Same features as desktop!
## ✅ Daily Checklist
- [ ] All services showing 🟢 healthy
- [ ] No unusual CPU/memory spikes
- [ ] Recent watchtower update logs look good
- [ ] No red error logs in critical services
**Portainer makes this a 30-second check!**

443
README.md
View file

@ -1,86 +1,413 @@
# webapp_124
# Studio EinsZwoVier Web Application
Minimal **FastAPI**-based PDF print-cost calculator with optional ink-coverage adjustment.
**FastAPI-based PDF print cost calculator** with automated ink coverage analysis, Matrix integration, and multi-service hub for Studio EinsZwoVier Maker Space.
This project is part of **Studio EinsZwoVier**, a collaborative environment.
Everyone is invited to contribute improvements. See the [source code](https://forgejo.petau.net/aron/124-webapp).
[![Docker](https://img.shields.io/badge/Docker-Ready-2496ED?logo=docker)](https://www.docker.com/)
[![FastAPI](https://img.shields.io/badge/FastAPI-0.104+-009688?logo=fastapi)](https://fastapi.tiangolo.com/)
[![Matrix](https://img.shields.io/badge/Matrix-Integrated-000000?logo=matrix)](https://matrix.org/)
---
## Features
## 🎯 Features
- **Landing Page**: Overview of all available tools and links.
- **Cost Calculator**: Calculate print and material costs for posters and large-format prints.
- **Docmost / Wissenssammlung**: Shared knowledge library for manuals and documentation.
- **Local Chatbot**: Access your own Large Language Models via Open WebUI and Ollama.
- **Matrix Server**: Collects print jobs and payments.
- **Administration**: Manage the Docker stack via Portainer.
- **Automatic Updates**: Watchtower updates all containers every 24 hours and cleans up old images.
### Core Application
- **📄 PDF Cost Calculator**: Automated ink coverage analysis with color/B&W detection
- **🖼️ Course Management**: Dynamic course list with image gallery and modal viewer
- **📊 Cost Estimation**: Per-page breakdown with configurable rates (€/m²)
- **💬 Matrix Integration**: Automatic order submission to Matrix room
### Integrated Services
- **📚 BookStack Wiki**: Documentation and knowledge base (port 6875)
- **🤖 Ollama + Open WebUI**: Local LLM chatbot interface (port 8080)
- **📨 Matrix Synapse**: Print order collection and payment tracking (port 8008)
- **🐳 Portainer**: Container management dashboard (port 9000)
- **🔄 Watchtower**: Automatic container updates every 24 hours
---
## Getting Started
## 🏗️ Architecture
```mermaid
graph TB
subgraph "Studio EinsZwoVier Web App (Port 80)"
LP[🏠 Landing Page<br/>studio-einszwovier.local]
About[ About Page<br/>/about<br/>Courses + Team Info]
Cost[💰 Cost Calculator<br/>/cost<br/>PDF Analysis]
LP --> About
LP --> Cost
end
subgraph "Course Management"
CSV[📝 courses.csv<br/>Title, Description, Dates, Image]
Images[🖼️ Course Images<br/>static/images/courses/]
Modal[🔍 Image Modal<br/>Click to Enlarge]
About --> CSV
CSV --> Images
Images --> Modal
end
subgraph "PDF Processing Pipeline"
Upload[📤 Upload PDF]
Analysis[🔬 Analyze<br/>- Dimensions<br/>- Ink Coverage<br/>- Color Detection]
Quote[📋 Quote Generation<br/>Per-page Breakdown]
Cost --> Upload
Upload --> Analysis
Analysis --> Quote
end
subgraph "Matrix Integration"
MatrixRoom[💬 Matrix Room<br/>Print Orders]
PDF_Upload[📎 PDF Attachment]
Summary[📊 Order Summary<br/>German Language]
Quote --> MatrixRoom
Quote --> PDF_Upload
Quote --> Summary
end
subgraph "Integrated Services"
BookStack[📚 BookStack Wiki<br/>:6875]
Ollama[🤖 Ollama LLM<br/>:11434]
OpenWebUI[💭 Open WebUI<br/>:8080]
Portainer[🐳 Portainer<br/>:9000]
Synapse[📨 Matrix Synapse<br/>:8008]
LP -.-> BookStack
LP -.-> OpenWebUI
LP -.-> Portainer
OpenWebUI --> Ollama
MatrixRoom --> Synapse
end
subgraph "Data Persistence"
Uploads[📁 data/uploads/<br/>PDF Files]
Courses[📋 data/courses.csv<br/>Course List]
MatrixDB[(🗄️ Matrix DB<br/>homeserver.db)]
BookDB[(🗄️ BookStack DB<br/>MariaDB)]
Upload --> Uploads
CSV --> Courses
Synapse --> MatrixDB
BookStack --> BookDB
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
class LP,About,Cost,Upload,Analysis,Quote webapp
class BookStack,Ollama,OpenWebUI,Portainer,Synapse service
class Uploads,Courses,MatrixDB,BookDB data
class MatrixRoom,PDF_Upload,Summary,CSV,Images,Modal matrix
```
---
## 🚀 Quick Start
### Prerequisites
- Docker & Docker Compose installed
- Optional: local network access to `einszwovier.local` for local services
if the server is moved, replace with new host
- **Docker** & **Docker Compose** installed
- **Poppler** (for pdf2image - included in Docker)
- **Port availability**: 80, 8008, 8080, 6875, 9000, 11434
### Installation
1. **Clone the repository:**
```bash
git clone https://github.com/arontaupe/124-webapp.git
cd 124-webapp
```
2. **Configure environment:**
```bash
cp .env.example .env
nano .env # Update SERVER_HOSTNAME and passwords
```
3. **Start the stack:**
```bash
docker compose up -d --build
```
4. **Access services:**
- Web App: http://localhost (or http://your-server)
- BookStack: http://localhost:6875
- Open WebUI: http://localhost:8080
- Portainer: http://localhost:9000
- Matrix: http://localhost:8008
### First-Time Setup
1. **Get Matrix Room ID:**
```bash
python get_room_id.py
```
2. **Update .env with room ID:**
```bash
MATRIX_ROOM="!YourRoomID:${SERVER_HOSTNAME}"
```
3. **Restart web container:**
```bash
docker compose restart web
```
---
### Run the Stack
## 📚 Course Management
Courses are managed via CSV file with optional images:
### Add a Course
1. **Edit `data/courses.csv`:**
```csv
title,description,dates,offen_fuer,image
My Course,Learn cool stuff,"Oct '25",Grade 10,/static/images/courses/my-course.jpg
```
2. **Add course image (optional):**
```bash
cp my-image.jpg static/images/courses/my-course.jpg
```
3. **Changes apply immediately** (no restart needed)
### Course Image Features
- **Thumbnail view**: 80x80px next to course info
- **Hover zoom**: Expands to 200x200px on hover
- **Click to enlarge**: Opens full-size modal viewer
- **Auto-cropping**: `object-fit: cover` ensures uniform display
See [data/KURSE_README.md](data/KURSE_README.md) for detailed documentation.
---
## 🔧 Configuration
### Environment Variables
Key variables in `.env`:
```bash
docker-compose up --build -d
# Server Configuration
SERVER_HOSTNAME=your-server.com
# Print Rates (€/m²)
RATE_PER_M2_BLACK=4.0
RATE_PER_M2_COLOR=5.0
# Matrix Integration
MATRIX_USER="@einszwovier:${SERVER_HOSTNAME}"
MATRIX_PASS="your_password"
MATRIX_HOMESERVER="http://${SERVER_HOSTNAME}:8008"
MATRIX_ROOM="!RoomID:${SERVER_HOSTNAME}"
graph TD
%% --- Subgraphs for clarity ---
subgraph WebApp ["Studio EinsZwoVier - Internal Web Pages"]
LP[Landing Page<br>studio-einszwovier.local]
About[About Page<br>/about]
CostCalc[Cost Calculator<br>/cost]
Mailto[Contact Email<br>mailto:einszwovier@gvb-gymnasium.de]
DocmostPage[Wissenssammlung<br>Docmost @ :3000]
ChatbotPage[Lokaler Chatbot<br>:8080]
end
# Service Ports
SYNAPSE_PORT=8008
OLLAMA_PORT=11434
OPENWEBUI_PORT=8080
BOOKSTACK_PORT=6875
PORTAINER_PORT=9000
```
subgraph MatrixServer ["Matrix Server"]
MatrixSrv[Matrix Server<br>Collects print jobs & payments]
end
See [.env.example](.env.example) for full configuration.
subgraph DocmostServer ["Docmost Server"]
DocmostSrv[Docmost Server<br>Manages manuals & docs]
end
---
subgraph AdminPanel ["Administration"]
Portainer[Portainer Admin Panel<br>:9000]
end
## 🐳 Docker Services
%% --- Landing page links ---
LP --> About
LP --> CostCalc
LP --> Mailto
LP --> DocmostPage
LP --> ChatbotPage
LP --> Portainer
| Service | Image | Port | Purpose | Resources |
|---------|-------|------|---------|-----------|
| **web** | Custom (FastAPI) | 80 | Main application | 1 CPU, 1GB RAM |
| **synapse** | matrixdotorg/synapse | 8008 | Matrix homeserver | 2 CPU, 2GB RAM |
| **ollama** | ollama/ollama | 11434 | LLM inference | 6 CPU, 16GB RAM |
| **open-webui** | ghcr.io/open-webui/open-webui | 8080 | LLM chat UI | 2 CPU, 2GB RAM |
| **bookstack** | lscr.io/linuxserver/bookstack | 6875 | Documentation wiki | Default |
| **bookstack-mariadb** | lscr.io/linuxserver/mariadb | 3306 | Database for BookStack | Default |
| **portainer** | portainer/portainer-ce | 9000, 9443 | Container management | Default |
| **watchtower** | containrrr/watchtower | - | Auto-updates (24h) | Default |
%% --- Cost Calculator workflow ---
CostCalc --> PDF[PDF Upload]
PDF --> Quote[Generate Quote]
Quote --> MatrixSrv
### Health Checks
%% --- Docmost workflow ---
DocmostPage --> DocmostSrv
All services have health checks configured:
- **web**: `curl http://localhost:8000/`
- **synapse**: `curl http://localhost:8008/_matrix/static/`
- **ollama**: `curl http://localhost:11434/`
- **open-webui**: `curl http://localhost:8080/`
- **bookstack**: `curl http://localhost/`
- **mariadb**: MariaDB query test
- **portainer**: API status check
- **watchtower**: Process check
%% --- Chatbot workflow ---
ChatbotPage --> Ollama[Ollama AI Server<br>:11434]
ChatbotPage --> OpenWebUI[Open WebUI<br>:8080]
---
%% --- Optional styling for clarity ---
classDef internal fill:#e3f2fd,stroke:#90caf9,stroke-width:1px;
classDef external fill:#fff3e0,stroke:#ffb74d,stroke-width:1px;
class LP,About,CostCalc,Mailto,DocmostPage,ChatbotPage internal;
class MatrixSrv,DocmostSrv,Ollama,OpenWebUI,Portainer external;
## 📖 Documentation
- **[SECURITY.md](SECURITY.md)** - Security best practices and secrets management
- **[PORTABILITY.md](PORTABILITY.md)** - Server migration guide
- **[PRE_RELEASE_CHECKLIST.md](PRE_RELEASE_CHECKLIST.md)** - Pre-publication verification
- **[data/KURSE_README.md](data/KURSE_README.md)** - Course management guide
---
## 🔐 Security
**Before deploying to production:**
1. ✅ Change all passwords in `.env`
2. ✅ Generate new `BOOKSTACK_APP_KEY`
3. ✅ Update Matrix credentials
4. ✅ Configure firewall (restrict Portainer access)
5. ✅ Set up HTTPS/SSL
6. ✅ Enable automated backups
See [SECURITY.md](SECURITY.md) for comprehensive security guidelines.
**Security Disclosure:** If you discover a vulnerability, email `einszwovier@gvb-gymnasium.de` instead of opening a public issue.
---
## 🔄 Backup & Restore
### Backup All Data
```bash
./backup.sh
```
Creates timestamped backups of:
- BookStack database & files
- Matrix homeserver data
- PDF uploads
- Course data & images
- Environment configuration
### Restore from Backup
```bash
./restore.sh YYYYMMDD_HHMMSS
```
---
## 🚢 Deployment & Portability
This application is designed to be **fully portable**. To move to a new server:
1. **On old server:**
```bash
./backup.sh
```
2. **On new server:**
```bash
git clone <repo-url>
cd 124-webapp
cp .env.example .env
# Update SERVER_HOSTNAME in .env
./restore.sh YYYYMMDD_HHMMSS
docker compose up -d
```
See [PORTABILITY.md](PORTABILITY.md) for detailed migration guide.
---
## 🛠️ Development
### Local Development
```bash
# Option 1: Direct Python (with auto-reload)
python run_app.py
# Option 2: Docker Compose
docker compose up --build
```
### Project Structure
```
.
├── main.py # FastAPI application
├── cost_calculator.py # PDF analysis logic
├── mailer.py # Matrix integration
├── templates/ # Jinja2 templates
│ ├── base.html
│ ├── landing.html
│ ├── about.html
│ ├── cost-calculator.html
│ └── result.html
├── static/ # CSS, images, fonts
│ ├── css/style.css
│ ├── images/
│ └── fonts/
├── data/
│ ├── courses.csv # Course database
│ └── uploads/ # PDF uploads
├── docker-compose.yml # Service orchestration
├── Dockerfile # Web app container
└── requirements.txt # Python dependencies
```
### Key Technologies
- **FastAPI** - Modern Python web framework
- **PyPDF2** - PDF metadata extraction
- **pdf2image** - PDF to image conversion
- **Pillow + NumPy** - Image processing & ink analysis
- **matrix-nio** - Matrix protocol client
- **Jinja2** - Template engine
- **Gunicorn + Uvicorn** - Production WSGI/ASGI servers
---
## 🤝 Contributing
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`)
3. Commit your changes (`git commit -m 'Add amazing feature'`)
4. Push to the branch (`git push origin feature/amazing-feature`)
5. Open a Pull Request
---
## 📄 License
This project is part of Studio EinsZwoVier educational initiative.
---
## 📞 Contact
**Studio EinsZwoVier**
Gabriele-von-Bülow-Gymnasium
Tile-Brügge-Weg 63, 13509 Berlin (Tegel)
- **Email**: einszwovier@gvb-gymnasium.de
- **Team**: Aron Petau & Friedrich Weber
- **Hours**: Tuesday - Thursday, 11:00 - 16:00
- **Location**: Room 124
---
## 🙏 Acknowledgments
- Matrix.org for the communication protocol
- Ollama team for local LLM support
- BookStack for the documentation platform
- FastAPI community
- All contributors and students of Studio EinsZwoVier
---
**Made with ❤️ at Studio EinsZwoVier Maker Space**

65
backup.sh Executable file
View file

@ -0,0 +1,65 @@
#!/bin/bash
# Backup script for Studio EinsZwoVier services
# Run daily via cron: 0 2 * * * /path/to/backup.sh
set -e
# Get the directory where this script is located
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
BACKUP_DIR="${BACKUP_DIR:-/path/to/backups/einszwovier}"
DATE=$(date +%Y%m%d_%H%M%S)
RETENTION_DAYS=30
# Create backup directory
mkdir -p "$BACKUP_DIR"
echo "=== Starting backup at $(date) ==="
echo "Working directory: $SCRIPT_DIR"
# 1. Backup BookStack database
echo "Backing up BookStack database..."
docker exec bookstack-mariadb mariadb-dump \
-u"${DB_USERNAME:-bookstack}" \
-p"${DB_PASSWORD}" \
"${DB_DATABASE:-bookstack}" \
| gzip > "$BACKUP_DIR/bookstack_db_$DATE.sql.gz"
# 2. Backup BookStack uploads and config
echo "Backing up BookStack files..."
tar -czf "$BACKUP_DIR/bookstack_files_$DATE.tar.gz" \
-C "$SCRIPT_DIR" \
bookstack/bookstack_app_data/www/uploads \
bookstack/bookstack_app_data/www/images \
bookstack/bookstack_app_data/www/files
# 3. Backup Matrix data (includes homeserver.db)
echo "Backing up Matrix server data..."
tar -czf "$BACKUP_DIR/matrix_data_$DATE.tar.gz" \
-C "$SCRIPT_DIR" \
matrix/data
# 4. Backup PDF uploads
echo "Backing up PDF uploads..."
tar -czf "$BACKUP_DIR/pdf_uploads_$DATE.tar.gz" \
-C "$SCRIPT_DIR" \
data/uploads
# 5. Backup courses CSV
echo "Backing up courses data..."
tar -czf "$BACKUP_DIR/courses_$DATE.tar.gz" \
-C "$SCRIPT_DIR" \
data/courses.csv \
static/images/courses
# 6. Backup .env file (IMPORTANT: Contains secrets!)
echo "Backing up environment configuration..."
cp "$SCRIPT_DIR/.env" "$BACKUP_DIR/env_$DATE.backup"
chmod 600 "$BACKUP_DIR/env_$DATE.backup"
# 7. Remove old backups (older than RETENTION_DAYS)
echo "Cleaning up old backups..."
find "$BACKUP_DIR" -type f -mtime +$RETENTION_DAYS -delete
echo "=== Backup completed at $(date) ==="
echo "Backup location: $BACKUP_DIR"
ls -lh "$BACKUP_DIR" | tail -10

37
bookstack.yml Normal file
View file

@ -0,0 +1,37 @@
services:
bookstack:
image: lscr.io/linuxserver/bookstack:v25.07.2-ls220
container_name: bookstack
environment:
- PUID=1000
- PGID=1000
- TZ=Europe/Berlin
- APP_URL=http://einszwovier.local:6875
- APP_KEY=base64:3qjlIoUX4Tw6fUQgZcxMbz6lb8+dAzqpvItqHvahW1c=
- DB_HOST=bookstack-mariadb
- DB_PORT=3306
- DB_DATABASE=bookstack
- DB_USERNAME=bookstack
- DB_PASSWORD=bookstack8432
volumes:
- ./bookstack_app_data:/config
ports:
- 6875:80
restart: unless-stopped
depends_on:
- bookstack-mariadb
bookstack-mariadb:
image: lscr.io/linuxserver/mariadb:11.4.4
container_name: bookstack-mariadb
environment:
- PUID=1000
- PGID=1000
- TZ=Europe/Berlin
- MYSQL_ROOT_PASSWORD=mysupersecretrootpassword
- MYSQL_DATABASE=bookstack
- MYSQL_USER=bookstack
- MYSQL_PASSWORD=bookstack8432
volumes:
- ./bookstack_db_data:/config
restart: unless-stopped

104
data/KURSE_QUICKREF.md Normal file
View file

@ -0,0 +1,104 @@
# Kurse Schnellanleitung / Quick Reference
## 🚀 Kurs hinzufügen (Add Course)
1. Öffne `data/courses.csv` (Open the file)
2. Füge eine neue Zeile hinzu (Add a new line):
```csv
Kurs Titel,Beschreibung (optional),Termine (optional),Zielgruppe (optional)
```
### Beispiele (Examples):
**Vollständiger Kurs (Full course):**
```csv
Robotik Workshop,LEGO SPIKE Programmierung,"Mo 11.11. 15:00, Mi 13.11. 15:00",Klasse 8-10
```
**Nur Titel + Termin (Title + Date only):**
```csv
Offene Werkstatt,,"Di 12.11. 14:00-16:00",
```
**Nur Titel + Zielgruppe (Title + Audience only):**
```csv
Kommt bald,,,alle Schüler:innen
```
## 📋 Spalten (Columns)
| Spalte | Pflicht? | Beispiel |
|--------|----------|----------|
| `title` | ✅ JA | `Löten und Leuchten` |
| `description` | ❌ Nein | `Herstellung von Nachttischleuchten...` |
| `dates` | ❌ Nein | `Di 15.10. 14:00-16:00` |
| `offen_fuer` | ❌ Nein | `Klasse 7-9` |
## ⚡ Wichtige Regeln (Important Rules)
1. **Erste Zeile nie löschen!** (Never delete header row)
```csv
title,description,dates,offen_fuer
```
2. **Kommas in Terminen:** Anführungszeichen verwenden
```csv
Workshop,"Text","Mo 10:00, Di 10:00",Klasse 8
```
3. **Leere Felder:** Einfach leer lassen
```csv
Nur Titel,,,
```
4. **Keine Zeilenumbrüche** in den Feldern
## 🎨 Darstellung (Display)
```
┌─────────────────────────────────────┐
│ Kurs Titel │ ← Immer sichtbar
│ Beschreibung hier... │ ← Nur wenn vorhanden
│ 📅 Di 15.10. 14:00-16:00 │ ← Nur wenn vorhanden
│ 👥 Offen für: Klasse 7-9 │ ← Nur wenn vorhanden
└─────────────────────────────────────┘
```
## 📝 Vorlage zum Kopieren (Template to Copy)
```csv
Neuer Kurs,Beschreibung hier,Termine hier,Zielgruppe hier
```
## ✅ Checkliste (Checklist)
- [ ] CSV-Datei geöffnet: `data/courses.csv`
- [ ] Neue Zeile am Ende hinzugefügt
- [ ] Titel ausgefüllt (erforderlich!)
- [ ] Optionale Felder ausgefüllt oder leer gelassen
- [ ] Bei Kommas in Terminen: Anführungszeichen gesetzt
- [ ] Datei gespeichert
- [ ] Website neu laden → Kurs erscheint sofort!
## 🔄 Live-Update
**Kein Server-Neustart nötig!** (No restart needed!)
Einfach Datei speichern und Website neu laden.
## ❓ Beispiel-Szenarien
### Vergangener Kurs (Past course)
```csv
Löten Basics,Grundlagen des Lötens,"Abgeschlossen: 10.10., 12.10.",Klasse 7-8
```
### Zukünftiger Kurs (Future course)
```csv
Drohnen Flug,Erste Schritte mit Drohnen,"Kommt: 20.11. 15:00",ab Klasse 9
```
### Kurs ohne festen Termin (Course without date)
```csv
Makerspace Tour,Kennenlernen des Studios,,alle Interessierten
```

147
data/KURSE_README.md Normal file
View file

@ -0,0 +1,147 @@
# 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!"

8
data/courses.csv Normal file
View file

@ -0,0 +1,8 @@
title,description,dates,offen_fuer,image
Löten und Leuchten,Herstellung von Nachttischleuchten mit 3D-Design und Löttechnik,"Dez. '24",Klasse 5-6,/static/images/courses/löten und leuchten.jpeg
Löten und Leuchten,Herstellung von Nachttischleuchten mit 3D-Design und Löttechnik,"Jan. '25",Klasse 5-6,/static/images/courses/löten und leuchten.jpeg
Die Vogelvilla,Bau von Vogelhäusern mit Lasercutter und Holzbearbeitung,"Mär. '25",Jhg. 8,/static/images/courses/vogelvilla.jpeg
Textiles Plotten,Erschaffe deine eigenen Klamottendesigns,"Mai '25",Jhg. 9,/static/images/courses/textiles plotten.jpeg
Kicker-Glow-Up,Erstelle deine eigene Kickerfigur mit 3D Design,"Jun '25",Jhg. 7,
Projektwoche: Realitäten Transformieren,Erstelle deine eigene Augmented Reality App und lerne 3D Scannen,"Jul '25",Jhg. 10-11,
Reshaping Plastics,Lerne Plastik neu zu formen und eigene Produkte zu designen,"Okt '25",Jhg. 10,/static/images/courses/reshaping plastics.jpeg
1 title description dates offen_fuer image
2 Löten und Leuchten Herstellung von Nachttischleuchten mit 3D-Design und Löttechnik Dez. '24 Klasse 5-6 /static/images/courses/löten und leuchten.jpeg
3 Löten und Leuchten Herstellung von Nachttischleuchten mit 3D-Design und Löttechnik Jan. '25 Klasse 5-6 /static/images/courses/löten und leuchten.jpeg
4 Die Vogelvilla Bau von Vogelhäusern mit Lasercutter und Holzbearbeitung Mär. '25 Jhg. 8 /static/images/courses/vogelvilla.jpeg
5 Textiles Plotten Erschaffe deine eigenen Klamottendesigns Mai '25 Jhg. 9 /static/images/courses/textiles plotten.jpeg
6 Kicker-Glow-Up Erstelle deine eigene Kickerfigur mit 3D Design Jun '25 Jhg. 7
7 Projektwoche: Realitäten Transformieren Erstelle deine eigene Augmented Reality App und lerne 3D Scannen Jul '25 Jhg. 10-11
8 Reshaping Plastics Lerne Plastik neu zu formen und eigene Produkte zu designen Okt '25 Jhg. 10 /static/images/courses/reshaping plastics.jpeg

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -1,99 +1,166 @@
name: studio-einszwovier
services:
web:
build: .
container_name: 124_webapp
ports:
- "80:8000"
working_dir: /cost-assistant
volumes:
- .:/cost-assistant
- ./data/uploads:/cost-assistant/data/uploads
- ./data:/cost-assistant/data
- ./templates:/cost-assistant/templates:ro
- ./static:/cost-assistant/static:ro
env_file:
- .env
command: python -m uvicorn main:app --host 0.0.0.0 --port 8000 --reload
docmost:
image: docmost/docmost:latest
container_name: docmost
restart: unless-stopped
mem_limit: 1g
cpus: 1.0
mem_reservation: 256m
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:8000/ || exit 1"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
depends_on:
- db
- redis
environment:
APP_URL: "http://einszwovier.local:3000"
APP_SECRET: "Ltr/i5KY8S8xQQZbaRHVS07gaAqwxPfrMxW6ZetNRqk="
DATABASE_URL: "postgresql://docmost:einszwo4@db:5432/docmost?schema=public"
REDIS_URL: "redis://redis:6379"
MAIL_DRIVER: "smtp"
SMTP_HOST: "smtp.migadu.com"
SMTP_PORT: 465
SMTP_SECURE: "true"
SMTP_USERNAME: "aron@petau.net"
SMTP_PASSWORD: "Reprintedservices2766"
MAIL_FROM_ADDRESS: "aron@petau.net"
MAIL_FROM_NAME: "einszwovier - docs"
ports:
- "3000:3000"
restart: unless-stopped
volumes:
- ./docmost/storage:/app/data/storage
db:
image: postgres:16-alpine
container_name: docmost_postgres
environment:
POSTGRES_DB: docmost
POSTGRES_USER: docmost
POSTGRES_PASSWORD: einszwo4
restart: unless-stopped
volumes:
- ./docmost/db:/var/lib/postgresql/data
redis:
image: redis:7.2-alpine
container_name: docmost_redis
restart: unless-stopped
volumes:
- ./docmost/redis:/data
synapse:
condition: service_started
labels:
- "com.centurylinklabs.watchtower.enable=true"
- "description=Studio EinsZwoVier PDF Cost Calculator"
- "maintainer=Studio EinsZwoVier"
synapse:
image: matrixdotorg/synapse:latest
container_name: matrix_server
restart: always
ports:
- "8008:8008"
- "${SYNAPSE_PORT:-8008}:8008"
volumes:
- ./matrix/data:/data
environment:
- SYNAPSE_SERVER_NAME=einszwovier.local
- SYNAPSE_REPORT_STATS=no
- SYNAPSE_CONFIG_PATH=/data/homeserver.yaml
restart: unless-stopped
mem_limit: 2g
cpus: 2.0
mem_reservation: 512m
healthcheck:
test:
["CMD-SHELL", "curl -f http://localhost:8008/_matrix/static/ || exit 1"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
labels:
- "com.centurylinklabs.watchtower.enable=true"
- "description=Matrix homeserver for print orders"
- "maintainer=Studio EinsZwoVier"
ollama:
image: ollama/ollama:latest
container_name: ollama
ports:
- 11434:11434
- "${OLLAMA_PORT:-11434}:11434"
volumes:
- ./ollama:/root/.ollama
tty: true
restart: unless-stopped
mem_limit: 16g
cpus: 6.0
mem_reservation: 4g
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:11434/ || exit 1"]
interval: 60s
timeout: 10s
retries: 3
start_period: 60s
labels:
- "com.centurylinklabs.watchtower.enable=true"
- "description=Local LLM inference engine"
- "maintainer=Studio EinsZwoVier"
open-webui:
image: ghcr.io/open-webui/open-webui:main
image: ghcr.io/open-webui/open-webui:latest
container_name: open-webui
depends_on:
- ollama
ports:
- 8080:8080
- "${OPENWEBUI_PORT:-8080}:8080"
volumes:
- ./open-webui:/app/backend/data
environment:
- 'OLLAMA_BASE_URL=http://ollama:11434'
- 'WEBUI_SECRET_KEY=a8cbc11647c471e5a30d9b182a7147a0777e41a6b0cb036fd6a8d967585502c7'
- 'ENABLE_API=true'
- 'WEBUI_API_KEY=a8cbc11647c471e5a30d9b182a7147a0777e41a6b0cb036fd6a8d967585502c7'
extra_hosts:
- host.docker.internal:host-gateway
- OLLAMA_BASE_URL=http://ollama:11434
- WEBUI_SECRET_KEY=${WEBUI_SECRET_KEY:-secret_key_change_me}
restart: unless-stopped
mem_limit: 2g
cpus: 2.0
mem_reservation: 512m
depends_on:
- ollama
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:8080/ || exit 1"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
labels:
- "com.centurylinklabs.watchtower.enable=true"
- "description=Web UI for Ollama LLM"
- "maintainer=Studio EinsZwoVier"
bookstack:
image: lscr.io/linuxserver/bookstack:latest
container_name: bookstack
environment:
- APP_KEY=${BOOKSTACK_APP_KEY}
- APP_URL=${BOOKSTACK_APP_URL}
env_file:
- .env
volumes:
- ./bookstack/bookstack_app_data:/config
ports:
- "${BOOKSTACK_PORT}:80"
restart: unless-stopped
depends_on:
bookstack-mariadb:
condition: service_healthy
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost/ || exit 1"]
interval: 30s
timeout: 10s
retries: 3
start_period: 60s
labels:
- "com.centurylinklabs.watchtower.enable=true"
- "description=BookStack Documentation Wiki"
- "maintainer=Studio EinsZwoVier"
bookstack-mariadb:
image: lscr.io/linuxserver/mariadb:latest
container_name: bookstack-mariadb
environment:
- MYSQL_ROOT_PASSWORD=${DB_PASSWORD}
- MYSQL_DATABASE=${DB_DATABASE}
- MYSQL_USER=${DB_USERNAME}
- MYSQL_PASSWORD=${DB_PASSWORD}
- PUID=1000
- PGID=1000
- TZ=Europe/Berlin
env_file:
- .env
volumes:
- ./bookstack/bookstack_db_data:/config
restart: unless-stopped
healthcheck:
test:
[
"CMD-SHELL",
"mariadb -u${DB_USERNAME} -p${DB_PASSWORD} -e 'SELECT 1' || exit 1",
]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
labels:
- "com.centurylinklabs.watchtower.enable=true"
- "description=MariaDB Database for BookStack"
- "maintainer=Studio EinsZwoVier"
watchtower:
image: containrrr/watchtower:latest
@ -101,19 +168,54 @@ services:
restart: unless-stopped
volumes:
- /var/run/docker.sock:/var/run/docker.sock
command: >
--cleanup
--interval 86400
environment:
- WATCHTOWER_CLEANUP=true
- WATCHTOWER_POLL_INTERVAL=86400
- WATCHTOWER_INCLUDE_RESTARTING=true
- WATCHTOWER_LABEL_ENABLE=true
command: --cleanup --interval 86400 --label-enable
healthcheck:
test: ["CMD-SHELL", "pgrep watchtower || exit 1"]
interval: 60s
timeout: 10s
retries: 3
labels:
- "description=Watchtower Auto-Update Service"
- "maintainer=Studio EinsZwoVier"
portainer:
image: portainer/portainer-ce:latest
container_name: portainer
restart: unless-stopped
ports:
- "9000:9000"
- "${PORTAINER_PORT}:9000"
- "9443:9443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- portainer_data:/data
healthcheck:
test:
[
"CMD-SHELL",
"curl -f http://localhost:9000/api/system/status || exit 1",
]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
labels:
- "description=Portainer Container Management UI"
- "maintainer=Studio EinsZwoVier"
volumes:
portainer_data:
driver: local
labels:
- "description=Portainer persistent data"
networks:
default:
name: einszwovier_network
labels:
- "description=Studio EinsZwoVier network"
- "maintainer=Studio EinsZwoVier"

BIN
docmost/.DS_Store vendored

Binary file not shown.

View file

@ -1 +0,0 @@
16

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

View file

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

Binary file not shown.

View file

View file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show more