design final
This commit is contained in:
parent
a0d2188f6f
commit
99a690972e
1414 changed files with 2389 additions and 1455 deletions
BIN
.DS_Store
vendored
BIN
.DS_Store
vendored
Binary file not shown.
78
.dockerignore
Normal file
78
.dockerignore
Normal 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
85
.env.example
Normal 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
144
.github/copilot-instructions.md
vendored
Normal 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
111
.gitignore
vendored
|
|
@ -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
78
DEV_SETUP.md
Normal 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
|
||||
24
Dockerfile
24
Dockerfile
|
|
@ -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
231
PORTAINER_QUICKREF.md
Normal 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
443
README.md
|
|
@ -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).
|
||||
[](https://www.docker.com/)
|
||||
[](https://fastapi.tiangolo.com/)
|
||||
[](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
65
backup.sh
Executable 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
37
bookstack.yml
Normal 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
104
data/KURSE_QUICKREF.md
Normal 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
147
data/KURSE_README.md
Normal 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
8
data/courses.csv
Normal 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
|
||||
|
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.
|
|
@ -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
BIN
docmost/.DS_Store
vendored
Binary file not shown.
|
|
@ -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.
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.
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
Loading…
Add table
Add a link
Reference in a new issue