add forgejo, add jupyter
This commit is contained in:
parent
a6d498bda0
commit
98417edd8b
23 changed files with 562 additions and 261 deletions
BIN
.DS_Store
vendored
BIN
.DS_Store
vendored
Binary file not shown.
|
|
@ -45,9 +45,9 @@ BOOKSTACK_APP_KEY=base64:YOUR_BOOKSTACK_KEY_HERE
|
|||
# ========================================
|
||||
DB_HOST=bookstack-mariadb
|
||||
DB_PORT=3306
|
||||
DB_DATABASE=bookstack
|
||||
DB_USERNAME=bookstack
|
||||
DB_PASSWORD=your_secure_database_password_here
|
||||
BOOKSTACK_DB_DATABASE=bookstack
|
||||
BOOKSTACK_DB_USERNAME=bookstack
|
||||
BOOKSTACK_DB_PASSWORD=your_secure_database_password_here
|
||||
|
||||
# MariaDB root password
|
||||
MARIADB_ROOT_PASSWORD=your_secure_root_password_here
|
||||
|
|
|
|||
7
.github/copilot-instructions.md
vendored
7
.github/copilot-instructions.md
vendored
|
|
@ -71,6 +71,13 @@ BOOKSTACK_PORT=6875
|
|||
# BookStack/DB configs (see docker-compose.yml for full list)
|
||||
```
|
||||
|
||||
Note: this repository also contains a local `.env` with example credentials (do NOT commit real secrets). Current repository `.env` contents:
|
||||
```properties
|
||||
MATRIX_USER=einszwovier_bot
|
||||
MATRIX_PASS=einszwo4
|
||||
```
|
||||
Prefer injecting secrets via CI environment variables or a secure vault for production. Add `MATRIX_ROOM` and/or `MATRIX_HOMESERVER` when testing Matrix flows.
|
||||
|
||||
### Debugging Matrix Integration
|
||||
Use `get_room_id.py` to discover Matrix room IDs:
|
||||
```python
|
||||
|
|
|
|||
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -103,3 +103,5 @@ dmypy.json
|
|||
*.db-wal
|
||||
*.sqlite
|
||||
*.sqlite3
|
||||
.github
|
||||
.venv
|
||||
|
|
|
|||
|
|
@ -1,231 +0,0 @@
|
|||
# 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!**
|
||||
66
README.md
66
README.md
|
|
@ -11,17 +11,21 @@
|
|||
## 🎯 Features
|
||||
|
||||
### 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
|
||||
- **<EFBFBD> JupyterHub**: Multi-user Jupyter notebook server for drone programming (port 8001)
|
||||
- **🦊 Forgejo**: Self-hosted Git service for code collaboration (port 3003)
|
||||
- **<EFBFBD>🔄 Watchtower**: Automatic container updates every 24 hours
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -74,35 +78,55 @@ graph TB
|
|||
OpenWebUI[💭 Open WebUI<br/>:8080]
|
||||
Portainer[🐳 Portainer<br/>:9000]
|
||||
Synapse[📨 Matrix Synapse<br/>:8008]
|
||||
JupyterHub[📓 JupyterHub<br/>:8001<br/>Drone Programming]
|
||||
Forgejo[🦊 Forgejo Git<br/>:3003<br/>Code Collaboration]
|
||||
|
||||
LP -.-> BookStack
|
||||
LP -.-> OpenWebUI
|
||||
LP -.-> Portainer
|
||||
LP -.-> JupyterHub
|
||||
LP -.-> Forgejo
|
||||
OpenWebUI --> Ollama
|
||||
MatrixRoom --> Synapse
|
||||
end
|
||||
|
||||
subgraph "Educational Stack"
|
||||
Notebooks[📒 Jupyter Notebooks<br/>Python Drone Code]
|
||||
GitRepos[📦 Git Repositories<br/>Project Source Code]
|
||||
DronePackages[🚁 djitellopy<br/>Tello SDK]
|
||||
|
||||
JupyterHub --> Notebooks
|
||||
Forgejo --> GitRepos
|
||||
Notebooks -.-> DronePackages
|
||||
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)]
|
||||
JupyterVolumes[(💾 Jupyter Volumes<br/>User Workspaces)]
|
||||
ForgejoData[(📚 Forgejo Data<br/>Git Repos)]
|
||||
|
||||
Upload --> Uploads
|
||||
CSV --> Courses
|
||||
Synapse --> MatrixDB
|
||||
BookStack --> BookDB
|
||||
JupyterHub --> JupyterVolumes
|
||||
Forgejo --> ForgejoData
|
||||
end
|
||||
|
||||
classDef webapp fill:#e3f2fd,stroke:#1976d2,stroke-width:2px
|
||||
classDef service fill:#fff3e0,stroke:#f57c00,stroke-width:2px
|
||||
classDef data fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px
|
||||
classDef matrix fill:#e8f5e9,stroke:#388e3c,stroke-width:2px
|
||||
classDef edu fill:#fce4ec,stroke:#c2185b,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 BookStack,Ollama,OpenWebUI,Portainer,Synapse,JupyterHub,Forgejo service
|
||||
class Uploads,Courses,MatrixDB,BookDB,JupyterVolumes,ForgejoData data
|
||||
class MatrixRoom,PDF_Upload,Summary,CSV,Images,Modal matrix
|
||||
class Notebooks,GitRepos,DronePackages edu
|
||||
```
|
||||
|
||||
---
|
||||
|
|
@ -113,47 +137,55 @@ graph TB
|
|||
|
||||
- **Docker** & **Docker Compose** installed
|
||||
- **Poppler** (for pdf2image - included in Docker)
|
||||
- **Port availability**: 80, 8008, 8080, 6875, 9000, 11434
|
||||
- **Port availability**: 80, 3003, 6875, 8001, 8008, 8080, 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
|
||||
- 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>
|
||||
- JupyterHub: <http://localhost:8001>
|
||||
- Forgejo: <http://localhost:3003>
|
||||
|
||||
### 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
|
||||
```
|
||||
|
|
@ -167,12 +199,14 @@ 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
|
||||
```
|
||||
|
|
@ -180,6 +214,7 @@ Courses are managed via CSV file with optional images:
|
|||
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
|
||||
|
|
@ -215,6 +250,11 @@ OLLAMA_PORT=11434
|
|||
OPENWEBUI_PORT=8080
|
||||
BOOKSTACK_PORT=6875
|
||||
PORTAINER_PORT=9000
|
||||
JUPYTER_PORT=8001
|
||||
FORGEJO_PORT=3003
|
||||
|
||||
# JupyterHub Authentication
|
||||
JUPYTER_PASSWORD=einszwo4
|
||||
```
|
||||
|
||||
See [.env.example](.env.example) for full configuration.
|
||||
|
|
@ -231,12 +271,15 @@ See [.env.example](.env.example) for full configuration.
|
|||
| **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 |
|
||||
| **jupyterhub** | Custom (Python 3.13) | 8001 | Multi-user Jupyter notebooks | 2 CPU, 2GB RAM |
|
||||
| **forgejo** | codeberg.org/forgejo/forgejo:11 | 3003, 222 | Git service (web, SSH) | Default |
|
||||
| **portainer** | portainer/portainer-ce | 9000, 9443 | Container management | Default |
|
||||
| **watchtower** | containrrr/watchtower | - | Auto-updates (24h) | Default |
|
||||
|
||||
### Health Checks
|
||||
|
||||
All services have health checks configured:
|
||||
|
||||
- **web**: `curl http://localhost:8000/`
|
||||
- **synapse**: `curl http://localhost:8008/_matrix/static/`
|
||||
- **ollama**: `curl http://localhost:11434/`
|
||||
|
|
@ -283,6 +326,7 @@ See [SECURITY.md](SECURITY.md) for comprehensive security guidelines.
|
|||
```
|
||||
|
||||
Creates timestamped backups of:
|
||||
|
||||
- BookStack database & files
|
||||
- Matrix homeserver data
|
||||
- PDF uploads
|
||||
|
|
@ -302,11 +346,13 @@ Creates timestamped backups of:
|
|||
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
|
||||
|
|
@ -393,7 +439,7 @@ This project is part of Studio EinsZwoVier educational initiative.
|
|||
Gabriele-von-Bülow-Gymnasium
|
||||
Tile-Brügge-Weg 63, 13509 Berlin (Tegel)
|
||||
|
||||
- **Email**: einszwovier@gvb-gymnasium.de
|
||||
- **Email**: <einszwovier@gvb-gymnasium.de>
|
||||
- **Team**: Aron Petau & Friedrich Weber
|
||||
- **Hours**: Tuesday - Thursday, 11:00 - 16:00
|
||||
- **Location**: Room 124
|
||||
|
|
|
|||
|
|
@ -19,9 +19,9 @@ 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}" \
|
||||
-u"${BOOKSTACK_DB_USERNAME:-bookstack}" \
|
||||
-p"${BOOKSTACK_DB_PASSWORD}" \
|
||||
"${BOOKSTACK_DB_DATABASE:-bookstack}" \
|
||||
| gzip > "$BACKUP_DIR/bookstack_db_$DATE.sql.gz"
|
||||
|
||||
# 2. Backup BookStack uploads and config
|
||||
|
|
|
|||
BIN
data/.DS_Store
vendored
BIN
data/.DS_Store
vendored
Binary file not shown.
177
data/Tello_Drone_Setup.ipynb
Normal file
177
data/Tello_Drone_Setup.ipynb
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "49a243e8",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Tello Drone Programming - Setup\n",
|
||||
"\n",
|
||||
"Dieses Notebook hilft dir, die Programmierung des DJI Tello Drohne einzurichten.\n",
|
||||
"\n",
|
||||
"## Schritt 1: Pakete installieren\n",
|
||||
"\n",
|
||||
"Führe die folgende Zelle aus, um die benötigten Python-Pakete zu installieren:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "4804023e",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Installiere djitellopy und Abhängigkeiten\n",
|
||||
"!pip install djitellopy opencv-python pillow"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "a480f600",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Schritt 2: Import und Verbindungstest\n",
|
||||
"\n",
|
||||
"Nach der Installation, importiere die Bibliothek:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "7bcda678",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from djitellopy import Tello\n",
|
||||
"import time\n",
|
||||
"\n",
|
||||
"print(\"✅ djitellopy erfolgreich importiert!\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "165d7797",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Schritt 3: Drohne verbinden (nur ausführen wenn Drohne eingeschaltet ist!)\n",
|
||||
"\n",
|
||||
"**⚠️ WICHTIG:** Stelle sicher, dass:\n",
|
||||
"1. Die Tello Drohne eingeschaltet ist\n",
|
||||
"2. Dein Computer mit dem WiFi der Drohne verbunden ist (TELLO-XXXXXX)\n",
|
||||
"3. Du genug Platz hast (mindestens 2x2 Meter freier Raum)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "6acedf15",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Drohne initialisieren\n",
|
||||
"drone = Tello()\n",
|
||||
"\n",
|
||||
"# Verbindung aufbauen\n",
|
||||
"drone.connect()\n",
|
||||
"\n",
|
||||
"# Batterie-Status prüfen\n",
|
||||
"battery = drone.get_battery()\n",
|
||||
"print(f\"🔋 Batterie: {battery}%\")\n",
|
||||
"\n",
|
||||
"if battery < 20:\n",
|
||||
" print(\"⚠️ Warnung: Batterie niedrig! Bitte aufladen.\")\n",
|
||||
"else:\n",
|
||||
" print(\"✅ Drohne bereit!\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "c4c90848",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Schritt 4: Einfacher Testflug\n",
|
||||
"\n",
|
||||
"**⚠️ SICHERHEIT ZUERST:**\n",
|
||||
"- Freier Raum um dich herum\n",
|
||||
"- Keine Personen in der Nähe\n",
|
||||
"- Drohne auf ebener Fläche\n",
|
||||
"- Bereit, die Drohne zu fangen falls nötig"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "3ce22798",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Starten und einfache Bewegung\n",
|
||||
"drone.takeoff() # Abheben\n",
|
||||
"time.sleep(3) # 3 Sekunden schweben\n",
|
||||
"\n",
|
||||
"drone.move_up(30) # 30cm nach oben\n",
|
||||
"time.sleep(2)\n",
|
||||
"\n",
|
||||
"drone.rotate_clockwise(90) # 90° drehen\n",
|
||||
"time.sleep(2)\n",
|
||||
"\n",
|
||||
"drone.land() # Landen\n",
|
||||
"print(\"✅ Testflug abgeschlossen!\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "3b616df2",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Nützliche Befehle\n",
|
||||
"\n",
|
||||
"Hier sind einige grundlegende Befehle:\n",
|
||||
"\n",
|
||||
"```python\n",
|
||||
"# Bewegungen\n",
|
||||
"drone.takeoff() # Abheben\n",
|
||||
"drone.land() # Landen\n",
|
||||
"drone.move_up(x) # x cm nach oben\n",
|
||||
"drone.move_down(x) # x cm nach unten\n",
|
||||
"drone.move_forward(x) # x cm vorwärts\n",
|
||||
"drone.move_back(x) # x cm rückwärts\n",
|
||||
"drone.move_left(x) # x cm links\n",
|
||||
"drone.move_right(x) # x cm rechts\n",
|
||||
"drone.rotate_clockwise(x) # x° im Uhrzeigersinn\n",
|
||||
"drone.rotate_counter_clockwise(x) # x° gegen Uhrzeigersinn\n",
|
||||
"\n",
|
||||
"# Informationen\n",
|
||||
"drone.get_battery() # Batterie-Status\n",
|
||||
"drone.get_height() # Aktuelle Höhe\n",
|
||||
"drone.get_speed() # Geschwindigkeit\n",
|
||||
"\n",
|
||||
"# Notfall\n",
|
||||
"drone.emergency() # NOTLANDUNG (sofort stoppen!)\n",
|
||||
"```"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "441ebe1d",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Weiterführende Ressourcen\n",
|
||||
"\n",
|
||||
"- [DJITelloPy Dokumentation](https://djitellopy.readthedocs.io/)\n",
|
||||
"- [Beispiele auf GitHub](https://github.com/damiafuentes/DJITelloPy/tree/master/examples)\n",
|
||||
"\n",
|
||||
"---\n",
|
||||
"\n",
|
||||
"**Viel Spaß beim Programmieren! 🚁**"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"language_info": {
|
||||
"name": "python"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
|
|
@ -108,10 +108,10 @@ services:
|
|||
image: lscr.io/linuxserver/bookstack:latest
|
||||
container_name: bookstack
|
||||
environment:
|
||||
- APP_KEY=${BOOKSTACK_APP_KEY}
|
||||
- APP_URL=${BOOKSTACK_APP_URL}
|
||||
env_file:
|
||||
- .env
|
||||
- MYSQL_ROOT_PASSWORD=${BOOKSTACK_DB_PASSWORD}
|
||||
- MYSQL_DATABASE=${BOOKSTACK_DB_DATABASE}
|
||||
- MYSQL_USER=${BOOKSTACK_DB_USERNAME}
|
||||
- MYSQL_PASSWORD=${BOOKSTACK_DB_PASSWORD}
|
||||
volumes:
|
||||
- ./bookstack/bookstack_app_data:/config
|
||||
ports:
|
||||
|
|
@ -121,7 +121,11 @@ services:
|
|||
bookstack-mariadb:
|
||||
condition: service_healthy
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "curl -f http://localhost/ || exit 1"]
|
||||
test:
|
||||
[
|
||||
"CMD-SHELL",
|
||||
"mariadb -u${BOOKSTACK_DB_USERNAME} -p${BOOKSTACK_DB_PASSWORD} -e 'SELECT 1' || exit 1",
|
||||
]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
|
|
@ -135,10 +139,10 @@ services:
|
|||
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}
|
||||
- MYSQL_ROOT_PASSWORD=${BOOKSTACK_DB_PASSWORD}
|
||||
- MYSQL_DATABASE=${BOOKSTACK_DB_DATABASE}
|
||||
- MYSQL_USER=${BOOKSTACK_DB_USERNAME}
|
||||
- MYSQL_PASSWORD=${BOOKSTACK_DB_PASSWORD}
|
||||
- PUID=1000
|
||||
- PGID=1000
|
||||
- TZ=Europe/Berlin
|
||||
|
|
@ -151,7 +155,7 @@ services:
|
|||
test:
|
||||
[
|
||||
"CMD-SHELL",
|
||||
"mariadb -u${DB_USERNAME} -p${DB_PASSWORD} -e 'SELECT 1' || exit 1",
|
||||
"mariadb -u${BOOKSTACK_DB_USERNAME} -p${BOOKSTACK_DB_PASSWORD} -e 'SELECT 1' || exit 1",
|
||||
]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
|
|
@ -207,6 +211,59 @@ services:
|
|||
- "description=Portainer Container Management UI"
|
||||
- "maintainer=Studio EinsZwoVier"
|
||||
|
||||
jupyterhub:
|
||||
build: ./jupyterhub
|
||||
container_name: jupyterhub
|
||||
ports:
|
||||
- "8001:8001"
|
||||
- "8081:8081"
|
||||
volumes:
|
||||
- ./data:/home
|
||||
- ./jupyterhub/jupyterhub_config.py:/srv/jupyterhub/jupyterhub_config.py:ro
|
||||
- ./static/images/logo.png:/srv/jupyterhub/logo.png:ro
|
||||
- /var/run/docker.sock:/var/run/docker.sock # Docker socket for spawning containers
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
- JUPYTERHUB_SINGLEUSER_APP=jupyterlab
|
||||
restart: unless-stopped
|
||||
mem_limit: 2g
|
||||
cpus: 1.0
|
||||
depends_on:
|
||||
- web
|
||||
labels:
|
||||
- "com.centurylinklabs.watchtower.enable=true"
|
||||
- "description=JupyterHub for interactive notebooks"
|
||||
- "maintainer=Studio EinsZwoVier"
|
||||
|
||||
forgejo:
|
||||
image: codeberg.org/forgejo/forgejo:11
|
||||
container_name: forgejo
|
||||
environment:
|
||||
- USER_UID=1000
|
||||
- USER_GID=1000
|
||||
- TZ=Europe/Berlin
|
||||
- FORGEJO__repository__ENABLE_PUSH_CREATE_USER=true
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "${FORGEJO_PORT:-3003}:3000"
|
||||
- "222:22"
|
||||
volumes:
|
||||
- ./forgejo/data:/data
|
||||
mem_limit: 2g
|
||||
cpus: 2.0
|
||||
mem_reservation: 512m
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "curl -f http://localhost:3000/ || exit 1"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 40s
|
||||
labels:
|
||||
- "com.centurylinklabs.watchtower.enable=true"
|
||||
- "description=Forgejo Git Server"
|
||||
- "maintainer=Studio EinsZwoVier"
|
||||
|
||||
volumes:
|
||||
portainer_data:
|
||||
driver: local
|
||||
|
|
|
|||
1
forgejo/data/git/.ssh/environment
Normal file
1
forgejo/data/git/.ssh/environment
Normal file
|
|
@ -0,0 +1 @@
|
|||
GITEA_CUSTOM=/data/gitea
|
||||
63
forgejo/data/gitea/conf/app.ini
Normal file
63
forgejo/data/gitea/conf/app.ini
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
APP_NAME = Forgejo: Beyond coding. We forge.
|
||||
RUN_MODE = prod
|
||||
|
||||
[repository]
|
||||
ROOT = /data/git/repositories
|
||||
ENABLE_PUSH_CREATE_USER = true
|
||||
|
||||
[repository.local]
|
||||
LOCAL_COPY_PATH = /data/gitea/tmp/local-repo
|
||||
|
||||
[repository.upload]
|
||||
TEMP_PATH = /data/gitea/uploads
|
||||
|
||||
[server]
|
||||
APP_DATA_PATH = /data/gitea
|
||||
DOMAIN = localhost
|
||||
SSH_DOMAIN = localhost
|
||||
HTTP_PORT = 3000
|
||||
ROOT_URL =
|
||||
DISABLE_SSH = false
|
||||
SSH_PORT = 22
|
||||
SSH_LISTEN_PORT = 22
|
||||
LFS_START_SERVER = false
|
||||
|
||||
[database]
|
||||
PATH = /data/gitea/gitea.db
|
||||
DB_TYPE = sqlite3
|
||||
HOST = localhost:3306
|
||||
NAME = gitea
|
||||
USER = root
|
||||
PASSWD =
|
||||
LOG_SQL = false
|
||||
|
||||
[indexer]
|
||||
ISSUE_INDEXER_PATH = /data/gitea/indexers/issues.bleve
|
||||
|
||||
[session]
|
||||
PROVIDER_CONFIG = /data/gitea/sessions
|
||||
|
||||
[picture]
|
||||
AVATAR_UPLOAD_PATH = /data/gitea/avatars
|
||||
REPOSITORY_AVATAR_UPLOAD_PATH = /data/gitea/repo-avatars
|
||||
|
||||
[attachment]
|
||||
PATH = /data/gitea/attachments
|
||||
|
||||
[log]
|
||||
MODE = console
|
||||
LEVEL = info
|
||||
ROOT_PATH = /data/gitea/log
|
||||
|
||||
[security]
|
||||
INSTALL_LOCK = false
|
||||
SECRET_KEY =
|
||||
REVERSE_PROXY_LIMIT = 1
|
||||
REVERSE_PROXY_TRUSTED_PROXIES = *
|
||||
|
||||
[service]
|
||||
DISABLE_REGISTRATION = false
|
||||
REQUIRE_SIGNIN_VIEW = false
|
||||
|
||||
[lfs]
|
||||
PATH = /data/git/lfs
|
||||
9
forgejo/data/ssh/ssh_host_ecdsa_key
Normal file
9
forgejo/data/ssh/ssh_host_ecdsa_key
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
-----BEGIN OPENSSH PRIVATE KEY-----
|
||||
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNlY2RzYS
|
||||
1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQQ02EGaaw86ZKkpWSL8gktKug0A/XNF
|
||||
TgcY469G/5ecX9H/AMxrPlOKCU0K+3eWGSCISQGlVLBRNpHK5VM9ga13AAAAsJrl+aya5f
|
||||
msAAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBDTYQZprDzpkqSlZ
|
||||
IvyCS0q6DQD9c0VOBxjjr0b/l5xf0f8AzGs+U4oJTQr7d5YZIIhJAaVUsFE2kcrlUz2BrX
|
||||
cAAAAhAPrnsLBtojpOaaAt0yDgy/lCpqRnfbGBkV5FMiFYvm+fAAAAEXJvb3RAMmQ4Yjk4
|
||||
Nzg0OWMyAQIDBAUG
|
||||
-----END OPENSSH PRIVATE KEY-----
|
||||
1
forgejo/data/ssh/ssh_host_ecdsa_key.pub
Normal file
1
forgejo/data/ssh/ssh_host_ecdsa_key.pub
Normal file
|
|
@ -0,0 +1 @@
|
|||
ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBDTYQZprDzpkqSlZIvyCS0q6DQD9c0VOBxjjr0b/l5xf0f8AzGs+U4oJTQr7d5YZIIhJAaVUsFE2kcrlUz2BrXc= root@2d8b987849c2
|
||||
7
forgejo/data/ssh/ssh_host_ed25519_key
Normal file
7
forgejo/data/ssh/ssh_host_ed25519_key
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
-----BEGIN OPENSSH PRIVATE KEY-----
|
||||
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
|
||||
QyNTUxOQAAACD7RYGr6qRspA4rgtvxhXJiS4a7z/hXm8q2XS9J8iqncQAAAJit3hcbrd4X
|
||||
GwAAAAtzc2gtZWQyNTUxOQAAACD7RYGr6qRspA4rgtvxhXJiS4a7z/hXm8q2XS9J8iqncQ
|
||||
AAAEB2I1zsRzNSHt6lqkavsXdj1fi2cDcnSCImC4Kpaqi3APtFgavqpGykDiuC2/GFcmJL
|
||||
hrvP+FebyrZdL0nyKqdxAAAAEXJvb3RAMmQ4Yjk4Nzg0OWMyAQIDBA==
|
||||
-----END OPENSSH PRIVATE KEY-----
|
||||
1
forgejo/data/ssh/ssh_host_ed25519_key.pub
Normal file
1
forgejo/data/ssh/ssh_host_ed25519_key.pub
Normal file
|
|
@ -0,0 +1 @@
|
|||
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPtFgavqpGykDiuC2/GFcmJLhrvP+FebyrZdL0nyKqdx root@2d8b987849c2
|
||||
38
forgejo/data/ssh/ssh_host_rsa_key
Normal file
38
forgejo/data/ssh/ssh_host_rsa_key
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
-----BEGIN OPENSSH PRIVATE KEY-----
|
||||
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
|
||||
-----END OPENSSH PRIVATE KEY-----
|
||||
1
forgejo/data/ssh/ssh_host_rsa_key.pub
Normal file
1
forgejo/data/ssh/ssh_host_rsa_key.pub
Normal file
|
|
@ -0,0 +1 @@
|
|||
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC2FfioI52Oa6bL480x/ZFQiJqS4F2pBcxEh/OkozGyNIy7F/TpiyphP59FHoQ52JtTMwcmX0GoD8bPDYi26FIb2ugpW+TBtijJN+m3q03qrEnKxMHrMsZQoZT+bO05WAUvlpO2qO+i7f5QSfQEi+heXrG7WC2MYli3ZC3+5PEpK3ey9wbhc7nnoTtHZW1Tu/2cnqu2AkLHnkirnTNGDHM73adyESet+wps6sbyr7qO/ieOmwGoSIelc2Kk/zBQdzAAMvpbE8FyzrK5qcJaDBDx5grLJCT+ugtxIPtXRhcEf4LGQ/0/1ntBd19qID6Dai7yUS47C1mxE2IWTkb2Xu2tk6D0T1kh9vhjIVUD31qQotAzi+kHpuHz7tKLfIxRjvD2YUjiy93ygnFOxofTW3RO1irbOLLYc7WPDBp5Ee3IPZUDu363ciViuF0w1BJO/v3VkfIpfFwJW/dm3SAheW8UtOf5expa33TCSy3tSKENQdhp4fgKRXVzv6fV3xQP75s= root@2d8b987849c2
|
||||
28
jupyterhub/Dockerfile
Normal file
28
jupyterhub/Dockerfile
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
FROM python:3.13-slim
|
||||
|
||||
# Install system deps
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
build-essential \
|
||||
nodejs npm \
|
||||
git \
|
||||
sudo \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install JupyterHub, JupyterLab, notebook (required for singleuser), and DockerSpawner
|
||||
RUN pip install --no-cache-dir jupyterhub jupyterlab notebook dockerspawner
|
||||
|
||||
# Install configurable-http-proxy (required by JupyterHub)
|
||||
RUN npm install -g configurable-http-proxy@^4.0.0
|
||||
|
||||
# Ensure npm global binaries are available in PATH
|
||||
ENV PATH="/usr/local/bin:${PATH}"
|
||||
|
||||
# Create directories
|
||||
RUN mkdir -p /srv/jupyterhub /srv/jupyterhub/data
|
||||
WORKDIR /srv/jupyterhub
|
||||
|
||||
COPY jupyterhub_config.py /srv/jupyterhub/
|
||||
|
||||
EXPOSE 8001
|
||||
|
||||
CMD ["jupyterhub", "-f", "/srv/jupyterhub/jupyterhub_config.py"]
|
||||
79
jupyterhub/jupyterhub_config.py
Normal file
79
jupyterhub/jupyterhub_config.py
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
from traitlets.config import get_config
|
||||
import os
|
||||
|
||||
# Minimal JupyterHub config for local multi-user usage inside docker-compose.
|
||||
# This config is intentionally small — customize for auth, spawners, and TLS in production.
|
||||
|
||||
c = get_config()
|
||||
|
||||
# Hub internal URL (where the hub process listens for /hub requests)
|
||||
c.JupyterHub.bind_url = 'http://:8081/hub/'
|
||||
|
||||
# Hub IP for spawned containers to connect back (use container name on Docker network)
|
||||
c.JupyterHub.hub_connect_ip = 'jupyterhub'
|
||||
|
||||
# Proxy configuration
|
||||
# - Proxy public address (what users access): default is port 8000, we want 8001
|
||||
# - Proxy API address (hub talks to proxy control): separate port 8002
|
||||
c.JupyterHub.port = 8001 # Public proxy port
|
||||
c.ConfigurableHTTPProxy.api_url = 'http://127.0.0.1:8002'
|
||||
|
||||
# Lightweight built-in authenticator: no external package required. This is
|
||||
# intended only for local development/testing. It accepts any username as long
|
||||
# as the password equals the value of JUPYTER_PASSWORD (default: einszwo4).
|
||||
from jupyterhub.auth import Authenticator
|
||||
|
||||
class SimpleEnvPasswordAuthenticator(Authenticator):
|
||||
async def authenticate(self, handler, data):
|
||||
"""Authenticate any username when the shared env password matches.
|
||||
|
||||
Dev-only: returns the username on success, otherwise None.
|
||||
"""
|
||||
username = data.get('username')
|
||||
password = data.get('password')
|
||||
if not username or not password:
|
||||
return None
|
||||
if password == os.environ.get('JUPYTER_PASSWORD', 'einszwo4'):
|
||||
return username
|
||||
return None
|
||||
|
||||
# Use the in-file authenticator for dev/testing
|
||||
c.JupyterHub.authenticator_class = SimpleEnvPasswordAuthenticator
|
||||
|
||||
# Allow any authenticated user to access (suppress the warning)
|
||||
c.Authenticator.allow_all = True
|
||||
|
||||
# Use DockerSpawner for production-ready containerized user servers
|
||||
from dockerspawner import DockerSpawner
|
||||
c.JupyterHub.spawner_class = DockerSpawner
|
||||
|
||||
# Docker image for single-user notebook servers
|
||||
c.DockerSpawner.image = 'jupyter/scipy-notebook:latest'
|
||||
|
||||
# Connect to Docker socket
|
||||
c.DockerSpawner.use_internal_ip = True
|
||||
c.DockerSpawner.network_name = 'einszwovier_network'
|
||||
|
||||
# Remove containers after they stop
|
||||
c.DockerSpawner.remove = True
|
||||
|
||||
# Mount the data directory for persistent storage
|
||||
# Use the data directory we already have mounted in docker-compose
|
||||
c.DockerSpawner.volumes = {
|
||||
'jupyterhub-user-{username}': {'bind': '/home/jovyan/work', 'mode': 'rw'}
|
||||
}
|
||||
|
||||
# Increase spawn timeout
|
||||
c.Spawner.start_timeout = 120
|
||||
c.Spawner.http_timeout = 120
|
||||
|
||||
# Set notebook directory and default interface
|
||||
c.Spawner.notebook_dir = '/home/jovyan/work'
|
||||
c.Spawner.default_url = '/lab'
|
||||
|
||||
# UI and data locations
|
||||
c.JupyterHub.logo_file = '/srv/jupyterhub/logo.png'
|
||||
c.JupyterHub.cookie_secret_file = '/srv/jupyterhub/jupyterhub_cookie_secret'
|
||||
|
||||
# Note: DockerSpawner creates isolated containers for each user.
|
||||
# Each user gets their own containerized Jupyter environment with persistent storage.
|
||||
2
main.py
2
main.py
|
|
@ -18,6 +18,7 @@ SERVER_HOSTNAME = os.environ.get("SERVER_HOSTNAME", "einszwovier.local")
|
|||
BOOKSTACK_PORT = os.environ.get("BOOKSTACK_PORT", "6875")
|
||||
OPENWEBUI_PORT = os.environ.get("OPENWEBUI_PORT", "8080")
|
||||
PORTAINER_PORT = os.environ.get("PORTAINER_PORT", "9000")
|
||||
FORGEJO_PORT = os.environ.get("FORGEJO_PORT", "3003")
|
||||
|
||||
# Courses CSV path
|
||||
COURSES_CSV = Path("data/courses.csv")
|
||||
|
|
@ -84,6 +85,7 @@ async def welcome(request: Request):
|
|||
"bookstack_port": BOOKSTACK_PORT,
|
||||
"openwebui_port": OPENWEBUI_PORT,
|
||||
"portainer_port": PORTAINER_PORT,
|
||||
"forgejo_port": FORGEJO_PORT,
|
||||
},
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -34,9 +34,9 @@ sleep 10 # Wait for MariaDB to start
|
|||
|
||||
zcat "$BACKUP_DIR/bookstack_db_$DATE.sql.gz" | \
|
||||
docker exec -i bookstack-mariadb mariadb \
|
||||
-u"${DB_USERNAME:-bookstack}" \
|
||||
-p"${DB_PASSWORD}" \
|
||||
"${DB_DATABASE:-bookstack}"
|
||||
-u"${BOOKSTACK_DB_USERNAME:-bookstack}" \
|
||||
-p"${BOOKSTACK_DB_PASSWORD}" \
|
||||
"${BOOKSTACK_DB_DATABASE:-bookstack}"
|
||||
|
||||
# 2. Restore BookStack files
|
||||
echo "Restoring BookStack files..."
|
||||
|
|
|
|||
|
|
@ -60,6 +60,18 @@
|
|||
<div class="tagline">Teste unsere schulischen Large Language Models direkt über die Weboberfläche.</div>
|
||||
</a>
|
||||
|
||||
<a class="link-card" href="http://{{ server_hostname }}:8001" target="_blank">
|
||||
<div class="title">JupyterHub</div>
|
||||
<div class="tagline">Interaktive Python-Notebooks für Datenanalyse, Visualisierung und kollaboratives
|
||||
Programmieren. Schreib uns eine E-Mail für Login-Daten.</div>
|
||||
</a>
|
||||
|
||||
<a class="link-card" href="http://{{ server_hostname }}:{{ forgejo_port }}" target="_blank">
|
||||
<div class="title">Forgejo Git Server</div>
|
||||
<div class="tagline">Versionskontrolle und Code-Hosting für kollaborative Softwareprojekte. Ideal für gemeinsame
|
||||
Entwicklung und Projektmanagement.</div>
|
||||
</a>
|
||||
|
||||
<a class="link-card" href="mailto:einszwovier@gvb-gymnasium.de" target="_blank">
|
||||
<div class="title">Kontakt</div>
|
||||
<div class="tagline">Schreibe uns direkt an: einszwovier@gvb-gymnasium.de</div>
|
||||
|
|
@ -70,7 +82,8 @@
|
|||
<!-- Footer with Admin Panel and Source Link -->
|
||||
<footer class="footer">
|
||||
<div class="footer-container">
|
||||
<a href="http://{{ server_hostname }}:{{ portainer_port }}" target="_blank" class="admin-link">Admin Panel (Portainer)</a>
|
||||
<a href="http://{{ server_hostname }}:{{ portainer_port }}" target="_blank" class="admin-link">Admin Panel
|
||||
(Portainer)</a>
|
||||
<span class="footer-source">
|
||||
| <a href="https://forgejo.petau.net/aron/124-webapp" target="_blank">Quellcode der Website</a>
|
||||
</span>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue