Compare commits
32 Commits
e3a8d3b98e
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| c69694031c | |||
| 05dbc38507 | |||
| 3233ce1998 | |||
| 7a12f25d0a | |||
| bc7e0644e3 | |||
| f6929726f4 | |||
| 55c4d88608 | |||
| 0f07e73d7d | |||
| 0c0b4effea | |||
| ff6500e709 | |||
|
|
eca11a82a9 | ||
|
|
e3b67752b5 | ||
| 3dd1e4d576 | |||
| ce48826dd8 | |||
| cf7541b5ff | |||
| b55fdb532b | |||
| 8653c510b7 | |||
| bd3eb14edc | |||
| f6c2cb0a8a | |||
| c2646d9680 | |||
| 2ac206ebd9 | |||
| f6ccae25ca | |||
| eb12cc4dd5 | |||
| aa02ea67aa | |||
| ca3a190e32 | |||
| 7a4e310acc | |||
| dba487efd4 | |||
| 7fe32f1715 | |||
| 449006b1df | |||
| 5ae2fba1e4 | |||
| 77b951d415 | |||
| be532fd78f |
@@ -1,42 +1,42 @@
|
||||
# Virtual environments
|
||||
.venv/
|
||||
venv/
|
||||
env/
|
||||
|
||||
# Python cache
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
*.so
|
||||
.Python
|
||||
|
||||
# Git
|
||||
.git/
|
||||
.gitignore
|
||||
|
||||
# IDE
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Documentation (not needed in container)
|
||||
*.md
|
||||
wiki/
|
||||
|
||||
# Logs
|
||||
logs/
|
||||
*.log
|
||||
|
||||
# Docker
|
||||
docker-compose.yml
|
||||
Dockerfile
|
||||
|
||||
# Output files (generated during build)
|
||||
# *.png
|
||||
# *.gv
|
||||
# Virtual environments
|
||||
.venv/
|
||||
venv/
|
||||
env/
|
||||
|
||||
# Python cache
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
*.so
|
||||
.Python
|
||||
|
||||
# Git
|
||||
.git/
|
||||
.gitignore
|
||||
|
||||
# IDE
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Documentation (not needed in container)
|
||||
*.md
|
||||
wiki/
|
||||
|
||||
# Logs
|
||||
logs/
|
||||
*.log
|
||||
|
||||
# Docker
|
||||
docker-compose.yml
|
||||
Dockerfile
|
||||
|
||||
# Output files (generated during build)
|
||||
# *.png
|
||||
# *.gv
|
||||
|
||||
63
.gitignore
vendored
63
.gitignore
vendored
@@ -1,33 +1,32 @@
|
||||
# Virtual Environment
|
||||
.venv/
|
||||
venv/
|
||||
env/
|
||||
|
||||
# Python
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
*.so
|
||||
.Python
|
||||
|
||||
# Generated diagram files
|
||||
*.png
|
||||
*.pdf
|
||||
*.svg
|
||||
*.gv
|
||||
|
||||
# IDE
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Distribution / packaging
|
||||
dist/
|
||||
build/
|
||||
# Virtual Environment
|
||||
.venv/
|
||||
venv/
|
||||
env/
|
||||
|
||||
# Python
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
*.so
|
||||
.Python
|
||||
|
||||
node_modules/
|
||||
# Generated diagram files
|
||||
*.pdf
|
||||
*.gv
|
||||
|
||||
# IDE
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Distribution // packaging
|
||||
dist/
|
||||
build/
|
||||
*.egg-info/
|
||||
84
Dockerfile
84
Dockerfile
@@ -1,61 +1,23 @@
|
||||
# Multi-stage Dockerfile for static frontend deployment
|
||||
# Stage 1: Build the diagrams
|
||||
FROM python:3.11-slim AS builder
|
||||
|
||||
# Install system dependencies for Graphviz
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends \
|
||||
graphviz \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Set working directory
|
||||
WORKDIR /app
|
||||
|
||||
# Copy requirements and install Python dependencies
|
||||
COPY requirements.txt .
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
# Copy source files
|
||||
COPY *.py .
|
||||
|
||||
# Generate diagrams (outputs will be in /app)
|
||||
RUN python lan_architecture.py && \
|
||||
python main.py
|
||||
|
||||
# Verify generated files exist
|
||||
RUN ls -la *.png || echo "Warning: No PNG files generated"
|
||||
|
||||
# Stage 2: Serve with Nginx
|
||||
FROM nginx:alpine
|
||||
|
||||
# Remove default nginx static assets
|
||||
RUN rm -rf /usr/share/nginx/html/*
|
||||
|
||||
# Copy generated diagram files from builder stage
|
||||
COPY --from=builder /app/*.png /usr/share/nginx/html/
|
||||
|
||||
# Copy any HTML/CSS files for the frontend
|
||||
COPY public/ /usr/share/nginx/html/
|
||||
|
||||
# Create a simple nginx config for serving static files
|
||||
RUN echo 'server { \
|
||||
listen 80; \
|
||||
server_name _; \
|
||||
root /usr/share/nginx/html; \
|
||||
index index.html; \
|
||||
location / { \
|
||||
try_files $uri $uri/ /index.html; \
|
||||
} \
|
||||
# Enable CORS if needed \
|
||||
add_header Access-Control-Allow-Origin *; \
|
||||
}' > /etc/nginx/conf.d/default.conf
|
||||
|
||||
# Expose port 80
|
||||
EXPOSE 80
|
||||
|
||||
# Health check
|
||||
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
||||
CMD wget --quiet --tries=1 --spider http://localhost/ || exit 1
|
||||
|
||||
# Start nginx
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
# -- Build stage --
|
||||
FROM node:20-alpine AS build
|
||||
WORKDIR /app
|
||||
|
||||
# Alleen package-bestanden eerst (better caching)
|
||||
COPY package*.json ./
|
||||
RUN npm ci || npm install
|
||||
|
||||
# Dan de rest van de code
|
||||
COPY . .
|
||||
|
||||
# LET OP: pas dit aan als jouw build-script anders heet
|
||||
RUN npm run build
|
||||
|
||||
# -- Serve stage --
|
||||
FROM nginx:alpine
|
||||
WORKDIR /usr/share/nginx/html
|
||||
|
||||
# Pas dit aan als jouw outputmap niet 'dist' is (bijv. 'build')
|
||||
COPY --from=build /app/dist ./
|
||||
|
||||
# Optioneel: eigen nginx.conf voor SPA routing
|
||||
# COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||
378
Home.md
378
Home.md
@@ -1,378 +0,0 @@
|
||||
# Diagram Viewer Wiki
|
||||
|
||||
Welcome to the **Diagram Viewer** project documentation.
|
||||
|
||||
This project provides a static web interface for viewing and managing network architecture diagrams generated with Python's `diagrams` library.
|
||||
|
||||
## 📚 Contents
|
||||
|
||||
- [Getting Started](#getting-started)
|
||||
- [Development Guide](#development-guide)
|
||||
- [Deployment Guide](#deployment-guide)
|
||||
- [Architecture Overview](#architecture-overview)
|
||||
|
||||
---
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Python 3.8+
|
||||
- Graphviz
|
||||
- Docker (for deployment)
|
||||
|
||||
### Quick Start
|
||||
|
||||
```bash
|
||||
# Clone repository
|
||||
git clone git@git.appmodel.nl:Tour/diagram.git
|
||||
cd diagram
|
||||
|
||||
# Create virtual environment
|
||||
python -m venv .venv
|
||||
source .venv/bin/activate # Windows: .venv\Scripts\activate
|
||||
|
||||
# Install dependencies
|
||||
pip install -r requirements.txt
|
||||
|
||||
# Generate diagrams
|
||||
python lan_architecture.py
|
||||
python main.py
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Development Guide
|
||||
|
||||
### Project Structure
|
||||
|
||||
```
|
||||
diagram/
|
||||
├── Dockerfile # Multi-stage build for production
|
||||
├── docker-compose.yml # Docker Compose configuration
|
||||
├── requirements.txt # Python dependencies
|
||||
├── lan_architecture.py # LAN architecture diagram generator
|
||||
├── main.py # Main diagram generator
|
||||
├── public/
|
||||
│ └── index.html # Live Mermaid diagram editor
|
||||
└── wiki/ # Documentation (this wiki)
|
||||
```
|
||||
|
||||
### Adding New Diagrams
|
||||
|
||||
1. Create a new Python file (e.g., `network_diagram.py`)
|
||||
2. Import required components from `diagrams` library
|
||||
3. Define your architecture using context managers
|
||||
4. Run the script to generate PNG output
|
||||
|
||||
Example:
|
||||
```python
|
||||
from diagrams import Diagram, Cluster
|
||||
from diagrams.onprem.network import Router
|
||||
from diagrams.generic.device import Mobile
|
||||
|
||||
with Diagram("My Network", show=False, filename="my_network"):
|
||||
router = Router("Gateway")
|
||||
device = Mobile("Phone")
|
||||
device >> router
|
||||
```
|
||||
|
||||
### Testing Locally
|
||||
|
||||
```bash
|
||||
# Generate diagrams
|
||||
python lan_architecture.py
|
||||
|
||||
# View output
|
||||
ls -la *.png
|
||||
|
||||
# Test with local web server
|
||||
python -m http.server 8000 --directory public/
|
||||
# Open: http://localhost:8000
|
||||
```
|
||||
|
||||
### Available Icons
|
||||
|
||||
The `diagrams` library provides icons from multiple providers:
|
||||
- **OnPrem**: Network, compute, storage, etc.
|
||||
- **Cloud**: AWS, Azure, GCP services
|
||||
- **Generic**: Network devices, blank nodes
|
||||
- **Custom**: Use your own images
|
||||
|
||||
See: [Diagrams Documentation](https://diagrams.mingrammer.com/docs/nodes/overview)
|
||||
|
||||
---
|
||||
|
||||
## Deployment Guide
|
||||
|
||||
### Overview
|
||||
|
||||
The project uses a fully automated Docker-based deployment pipeline:
|
||||
|
||||
1. **Develop** on local machine
|
||||
2. **Commit & Push** to Gitea
|
||||
3. **Auto-Deploy** via post-receive hook
|
||||
4. **Build** multi-stage Docker image
|
||||
5. **Serve** via Traefik reverse proxy
|
||||
|
||||
### Deployment Steps
|
||||
|
||||
#### 1. Initial Setup
|
||||
|
||||
```bash
|
||||
# On server: Create app structure
|
||||
apps-create diagram static-fe
|
||||
|
||||
# This creates:
|
||||
# - /opt/apps/diagram (git repository)
|
||||
# - /home/tour/infra/diagram/docker-compose.yml
|
||||
```
|
||||
|
||||
#### 2. Configure Gitea Hook
|
||||
|
||||
In repository **Settings → Git Hooks → post-receive**:
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
/usr/local/bin/app-deploy diagram
|
||||
```
|
||||
|
||||
#### 3. Deploy
|
||||
|
||||
Simply push to Gitea:
|
||||
|
||||
```bash
|
||||
git push origin main
|
||||
```
|
||||
|
||||
The server automatically:
|
||||
- Pulls latest code
|
||||
- Rebuilds Docker image
|
||||
- Restarts container
|
||||
- Updates live site at https://diagram.appmodel.nl
|
||||
|
||||
### Manual Deployment
|
||||
|
||||
If needed:
|
||||
|
||||
```bash
|
||||
# On server
|
||||
app-deploy diagram
|
||||
|
||||
# Or step-by-step
|
||||
cd /opt/apps/diagram
|
||||
git pull
|
||||
cd /home/tour/infra/diagram
|
||||
docker compose up -d --build diagram
|
||||
```
|
||||
|
||||
### Monitoring
|
||||
|
||||
```bash
|
||||
# View deployment logs
|
||||
tail -f /var/log/app-deploy-diagram.log
|
||||
|
||||
# View container logs
|
||||
cd /home/tour/infra/diagram
|
||||
docker compose logs -f diagram
|
||||
|
||||
# Check container status
|
||||
docker compose ps
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
### Infrastructure
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ Production Architecture │
|
||||
├─────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌──────────────┐ │
|
||||
│ │ Gitea │ (Source of Truth) │
|
||||
│ │ Tour/diagram │ │
|
||||
│ └──────┬───────┘ │
|
||||
│ │ git push │
|
||||
│ ↓ │
|
||||
│ ┌──────────────┐ │
|
||||
│ │ Post-Receive │ (Auto-trigger) │
|
||||
│ │ Hook │ │
|
||||
│ └──────┬───────┘ │
|
||||
│ │ app-deploy diagram │
|
||||
│ ↓ │
|
||||
│ ┌──────────────────────┐ │
|
||||
│ │ /opt/apps/diagram/ │ │
|
||||
│ │ (Git Repository) │ │
|
||||
│ └──────┬───────────────┘ │
|
||||
│ │ git pull │
|
||||
│ ↓ │
|
||||
│ ┌──────────────────────┐ │
|
||||
│ │ Docker Build Process │ │
|
||||
│ │ ┌─────────────────┐ │ │
|
||||
│ │ │ Stage 1: Build │ │ │
|
||||
│ │ │ - Python │ │ │
|
||||
│ │ │ - Graphviz │ │ │
|
||||
│ │ │ - Generate PNGs │ │ │
|
||||
│ │ └─────────────────┘ │ │
|
||||
│ │ ┌─────────────────┐ │ │
|
||||
│ │ │ Stage 2: Serve │ │ │
|
||||
│ │ │ - Nginx │ │ │
|
||||
│ │ │ - Static files │ │ │
|
||||
│ │ └─────────────────┘ │ │
|
||||
│ └──────┬───────────────┘ │
|
||||
│ │ container starts │
|
||||
│ ↓ │
|
||||
│ ┌──────────────────────┐ │
|
||||
│ │ diagram-viewer │ │
|
||||
│ │ Container :80 │ │
|
||||
│ └──────┬───────────────┘ │
|
||||
│ │ traefik_net │
|
||||
│ ↓ │
|
||||
│ ┌──────────────────────┐ │
|
||||
│ │ Traefik │ │
|
||||
│ │ Reverse Proxy │ │
|
||||
│ │ + Let's Encrypt │ │
|
||||
│ └──────┬───────────────┘ │
|
||||
│ │ HTTPS │
|
||||
│ ↓ │
|
||||
│ diagram.appmodel.nl │
|
||||
│ │
|
||||
└─────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Network Topology
|
||||
|
||||
The diagram viewer documents our home lab infrastructure:
|
||||
|
||||
- **LAN 192.168.1.0/24**: Main network
|
||||
- Core server (192.168.1.159): Traefik, Gitea, Dokku, Auction stack, MI50/Ollama
|
||||
- DNS server (192.168.1.163): AdGuard, Artifactory, CI runner
|
||||
- Home Assistant (192.168.1.193)
|
||||
- Atlas worker (192.168.1.100)
|
||||
- IoT devices
|
||||
|
||||
- **Tether 192.168.137.0/24**: Isolated subnet
|
||||
- Hermes worker (192.168.137.239)
|
||||
- Plato worker (192.168.137.163)
|
||||
|
||||
### Services
|
||||
|
||||
| Service | URL | Description |
|
||||
|---------|-----|-------------|
|
||||
| Diagram Viewer | https://diagram.appmodel.nl | This application |
|
||||
| Gitea | https://git.appmodel.nl | Git hosting |
|
||||
| Auction Frontend | https://auction.appmodel.nl | Auction platform |
|
||||
| Aupi API | https://aupi.appmodel.nl | Backend API |
|
||||
| Dokku | https://dokku.lan | PaaS platform |
|
||||
|
||||
---
|
||||
|
||||
## Additional Resources
|
||||
|
||||
### Related Documentation
|
||||
|
||||
- [DEPLOY_SERVER_SETUP.md](../DEPLOY_SERVER_SETUP.md) - Detailed deployment guide
|
||||
- [DEPLOYMENT_GUIDE.md](../DEPLOYMENT_GUIDE.md) - General deployment pipeline
|
||||
- [README.md](../README.md) - Project overview
|
||||
|
||||
### External Links
|
||||
|
||||
- [Diagrams Library](https://diagrams.mingrammer.com/)
|
||||
- [Graphviz](https://graphviz.org/)
|
||||
- [Mermaid.js](https://mermaid.js.org/) (used in live editor)
|
||||
- [Traefik Documentation](https://doc.traefik.io/traefik/)
|
||||
- [Docker Compose](https://docs.docker.com/compose/)
|
||||
|
||||
### Contributing
|
||||
|
||||
To contribute to this project:
|
||||
|
||||
1. Create a feature branch
|
||||
2. Make your changes
|
||||
3. Test locally
|
||||
4. Submit a pull request in Gitea
|
||||
5. Wait for automatic deployment after merge
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
#### Diagrams Not Generating
|
||||
|
||||
**Problem**: Python script fails to generate PNG files
|
||||
|
||||
**Solution**:
|
||||
```bash
|
||||
# Check Graphviz installation
|
||||
dot -V
|
||||
|
||||
# Reinstall if needed
|
||||
# macOS: brew install graphviz
|
||||
# Ubuntu: sudo apt-get install graphviz
|
||||
# Windows: choco install graphviz
|
||||
|
||||
# Verify Python dependencies
|
||||
pip list | grep diagrams
|
||||
pip install --upgrade diagrams
|
||||
```
|
||||
|
||||
#### Container Build Fails
|
||||
|
||||
**Problem**: Docker build fails during deployment
|
||||
|
||||
**Solution**:
|
||||
```bash
|
||||
# Check deployment logs
|
||||
tail -f /var/log/app-deploy-diagram.log
|
||||
|
||||
# Rebuild manually with verbose output
|
||||
cd /home/tour/infra/diagram
|
||||
docker compose build --no-cache --progress=plain diagram
|
||||
```
|
||||
|
||||
#### Site Not Accessible
|
||||
|
||||
**Problem**: Container runs but site not reachable
|
||||
|
||||
**Solution**:
|
||||
1. Check DNS: `nslookup diagram.appmodel.nl`
|
||||
2. Verify Traefik labels: `docker inspect diagram-viewer | grep traefik`
|
||||
3. Check Traefik logs: `docker logs traefik`
|
||||
4. Test container directly: `curl http://localhost:PORT`
|
||||
|
||||
#### Changes Not Reflected
|
||||
|
||||
**Problem**: Pushed changes but site unchanged
|
||||
|
||||
**Solution**:
|
||||
```bash
|
||||
# Force rebuild on server
|
||||
cd /home/tour/infra/diagram
|
||||
docker compose down
|
||||
docker compose up -d --build --force-recreate diagram
|
||||
|
||||
# Clear browser cache
|
||||
# Ctrl+Shift+R (Windows/Linux) or Cmd+Shift+R (macOS)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Contact & Support
|
||||
|
||||
For issues, questions, or suggestions:
|
||||
|
||||
1. Check this wiki
|
||||
2. Review [DEPLOY_SERVER_SETUP.md](../DEPLOY_SERVER_SETUP.md)
|
||||
3. Check container logs
|
||||
4. Contact the infrastructure team
|
||||
|
||||
---
|
||||
|
||||
**Last Updated**: 2025-12-02
|
||||
**Version**: 1.0.0
|
||||
**Maintainer**: Tour
|
||||
239
Quick-Start.md
239
Quick-Start.md
@@ -1,239 +0,0 @@
|
||||
# Quick Start Guide
|
||||
|
||||
Get up and running with the Diagram Viewer in minutes.
|
||||
|
||||
## For Developers
|
||||
|
||||
### 1. Clone Repository
|
||||
|
||||
```bash
|
||||
git clone git@git.appmodel.nl:Tour/diagram.git
|
||||
cd diagram
|
||||
```
|
||||
|
||||
### 2. Setup Environment
|
||||
|
||||
```bash
|
||||
# Create virtual environment
|
||||
python -m venv .venv
|
||||
|
||||
# Activate (choose your platform)
|
||||
source .venv/bin/activate # macOS/Linux
|
||||
.venv\Scripts\activate # Windows CMD
|
||||
source .venv/Scripts/activate # Windows Git Bash
|
||||
```
|
||||
|
||||
### 3. Install Dependencies
|
||||
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
### 4. Generate Diagrams
|
||||
|
||||
```bash
|
||||
# Generate LAN architecture diagram
|
||||
python lan_architecture.py
|
||||
|
||||
# Generate main diagram
|
||||
python main.py
|
||||
|
||||
# View generated files
|
||||
ls -la *.png
|
||||
```
|
||||
|
||||
### 5. Test Locally
|
||||
|
||||
```bash
|
||||
# Start local web server
|
||||
python -m http.server 8000 --directory public/
|
||||
|
||||
# Open browser
|
||||
# Navigate to: http://localhost:8000
|
||||
```
|
||||
|
||||
### 6. Make Changes & Deploy
|
||||
|
||||
```bash
|
||||
# Edit diagram code
|
||||
vim lan_architecture.py
|
||||
|
||||
# Commit changes
|
||||
git add .
|
||||
git commit -m "Update network diagram"
|
||||
|
||||
# Push to deploy (auto-deploys to production)
|
||||
git push origin main
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## For Operators
|
||||
|
||||
### View Live Site
|
||||
|
||||
Simply navigate to:
|
||||
```
|
||||
https://diagram.appmodel.nl
|
||||
```
|
||||
|
||||
### Manual Deployment
|
||||
|
||||
On the server:
|
||||
|
||||
```bash
|
||||
# Trigger deployment
|
||||
app-deploy diagram
|
||||
|
||||
# View logs
|
||||
tail -f /var/log/app-deploy-diagram.log
|
||||
|
||||
# Check status
|
||||
cd /home/tour/infra/diagram
|
||||
docker compose ps
|
||||
```
|
||||
|
||||
### View Logs
|
||||
|
||||
```bash
|
||||
# Deployment logs
|
||||
tail -f /var/log/app-deploy-diagram.log
|
||||
|
||||
# Container logs
|
||||
cd /home/tour/infra/diagram
|
||||
docker compose logs -f diagram
|
||||
|
||||
# Last 100 lines
|
||||
docker compose logs --tail=100 diagram
|
||||
```
|
||||
|
||||
### Restart Service
|
||||
|
||||
```bash
|
||||
cd /home/tour/infra/diagram
|
||||
|
||||
# Restart container
|
||||
docker compose restart diagram
|
||||
|
||||
# Full restart (rebuild)
|
||||
docker compose down
|
||||
docker compose up -d --build diagram
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Common Tasks
|
||||
|
||||
### Add New Diagram
|
||||
|
||||
1. Create new Python file:
|
||||
```python
|
||||
# my_diagram.py
|
||||
from diagrams import Diagram
|
||||
from diagrams.onprem.network import Router
|
||||
|
||||
with Diagram("My Network", show=False, filename="my_network"):
|
||||
Router("Gateway")
|
||||
```
|
||||
|
||||
2. Update Dockerfile if needed (add to RUN command):
|
||||
```dockerfile
|
||||
RUN python lan_architecture.py && \
|
||||
python main.py && \
|
||||
python my_diagram.py
|
||||
```
|
||||
|
||||
3. Commit and push:
|
||||
```bash
|
||||
git add my_diagram.py Dockerfile
|
||||
git commit -m "Add my network diagram"
|
||||
git push
|
||||
```
|
||||
|
||||
### Update Frontend
|
||||
|
||||
1. Edit HTML:
|
||||
```bash
|
||||
vim public/index.html
|
||||
```
|
||||
|
||||
2. Test locally:
|
||||
```bash
|
||||
python -m http.server 8000 --directory public/
|
||||
```
|
||||
|
||||
3. Deploy:
|
||||
```bash
|
||||
git add public/index.html
|
||||
git commit -m "Update frontend"
|
||||
git push
|
||||
```
|
||||
|
||||
### Check Container Health
|
||||
|
||||
```bash
|
||||
# Container status
|
||||
docker compose ps
|
||||
|
||||
# Health check
|
||||
docker inspect diagram-viewer | grep -A 10 Health
|
||||
|
||||
# Test endpoint
|
||||
curl -I https://diagram.appmodel.nl
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Build Fails
|
||||
|
||||
```bash
|
||||
# Check logs
|
||||
tail -f /var/log/app-deploy-diagram.log
|
||||
|
||||
# Rebuild with verbose output
|
||||
cd /home/tour/infra/diagram
|
||||
docker compose build --no-cache --progress=plain diagram
|
||||
```
|
||||
|
||||
### Site Not Loading
|
||||
|
||||
```bash
|
||||
# Check container
|
||||
docker compose ps
|
||||
|
||||
# Check Traefik routing
|
||||
docker logs traefik | grep diagram
|
||||
|
||||
# Test DNS
|
||||
nslookup diagram.appmodel.nl
|
||||
|
||||
# Test directly
|
||||
curl http://localhost:PORT
|
||||
```
|
||||
|
||||
### Changes Not Visible
|
||||
|
||||
```bash
|
||||
# Force rebuild
|
||||
cd /home/tour/infra/diagram
|
||||
docker compose up -d --build --force-recreate diagram
|
||||
|
||||
# Clear browser cache
|
||||
# Ctrl+Shift+R (Windows/Linux)
|
||||
# Cmd+Shift+R (macOS)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
- Read the [Home](Home.md) page for comprehensive documentation
|
||||
- Review [DEPLOY_SERVER_SETUP.md](../DEPLOY_SERVER_SETUP.md) for deployment details
|
||||
- Check [Diagrams Library docs](https://diagrams.mingrammer.com/) for icon options
|
||||
- Explore the [Mermaid editor](https://diagram.appmodel.nl) for live diagram editing
|
||||
|
||||
---
|
||||
|
||||
**Need Help?** Check the [Troubleshooting](Home.md#troubleshooting) section or review deployment logs.
|
||||
164
README.md
164
README.md
@@ -1,82 +1,82 @@
|
||||
# Network Architecture Diagrams
|
||||
|
||||
Python scripts to generate network architecture diagrams using the [Diagrams](https://diagrams.mingrammer.com/) library.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Python 3.8+
|
||||
- Graphviz ([installation guide](https://graphviz.org/download/))
|
||||
|
||||
### Installing Graphviz
|
||||
|
||||
**Windows:**
|
||||
```bash
|
||||
choco install graphviz
|
||||
# or download from https://graphviz.org/download/
|
||||
```
|
||||
|
||||
**macOS:**
|
||||
```bash
|
||||
brew install graphviz
|
||||
```
|
||||
|
||||
**Linux:**
|
||||
```bash
|
||||
sudo apt-get install graphviz # Debian/Ubuntu
|
||||
sudo yum install graphviz # RHEL/CentOS
|
||||
```
|
||||
|
||||
## Setup
|
||||
|
||||
1. Create a virtual environment:
|
||||
```bash
|
||||
python -m venv .venv
|
||||
```
|
||||
|
||||
2. Activate the virtual environment:
|
||||
```bash
|
||||
# Windows
|
||||
.venv\Scripts\activate
|
||||
|
||||
# macOS/Linux
|
||||
source .venv/bin/activate
|
||||
```
|
||||
|
||||
3. Install dependencies:
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
Run the diagram generation scripts:
|
||||
|
||||
```bash
|
||||
# Generate LAN architecture diagram
|
||||
python lan_architecture.py
|
||||
|
||||
# Generate main diagram
|
||||
python main.py
|
||||
```
|
||||
|
||||
Output files will be generated in the current directory as PNG images.
|
||||
|
||||
## Available Diagrams
|
||||
|
||||
- `lan_architecture.py` - Home Lab / Auction Stack Architecture diagram
|
||||
- `main.py` - Network architecture diagram
|
||||
|
||||
## Deployment
|
||||
|
||||
To deploy or share this project:
|
||||
|
||||
1. Ensure Graphviz is installed on the target system
|
||||
2. Clone the repository
|
||||
3. Follow the setup instructions above
|
||||
4. Run the desired diagram script
|
||||
|
||||
## Notes
|
||||
|
||||
- Generated diagram files (`.png`, `.gv`) are excluded from version control
|
||||
- The diagrams use a left-to-right (`LR`) layout direction
|
||||
- Output files are saved with `show=False` to prevent automatic opening
|
||||
# Network Architecture Diagrams
|
||||
|
||||
Python scripts to generate network architecture diagrams using the [Diagrams](https://diagrams.mingrammer.com/) library.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Python 3.8+
|
||||
- Graphviz ([installation guide](https://graphviz.org/download/))
|
||||
|
||||
### Installing Graphviz
|
||||
|
||||
**Windows:**
|
||||
```bash
|
||||
choco install graphviz
|
||||
# or download from https://graphviz.org/download/
|
||||
```
|
||||
|
||||
**macOS:**
|
||||
```bash
|
||||
brew install graphviz
|
||||
```
|
||||
|
||||
**Linux:**
|
||||
```bash
|
||||
sudo apt-get install graphviz # Debian/Ubuntu
|
||||
sudo yum install graphviz # RHEL/CentOS
|
||||
```
|
||||
|
||||
## Setup
|
||||
|
||||
1. Create a virtual environment:
|
||||
```bash
|
||||
python -m venv .venv
|
||||
```
|
||||
|
||||
2. Activate the virtual environment:
|
||||
```bash
|
||||
# Windows
|
||||
.venv\Scripts\activate
|
||||
|
||||
# macOS/Linux
|
||||
source .venv/bin/activate
|
||||
```
|
||||
|
||||
3. Install dependencies:
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
Run the diagram generation scripts:
|
||||
|
||||
```bash
|
||||
# Generate LAN architecture diagram
|
||||
python lan_architecture.py
|
||||
|
||||
# Generate main diagram
|
||||
python main.py
|
||||
```
|
||||
|
||||
Output files will be generated in the current directory as PNG images.
|
||||
|
||||
## Available Diagrams
|
||||
|
||||
- `lan_architecture.py` - Home Lab / Auction Stack Architecture diagram
|
||||
- `main.py` - Network architecture diagram
|
||||
|
||||
## Deployment
|
||||
|
||||
To deploy or share this project:
|
||||
|
||||
1. Ensure Graphviz is installed on the target system
|
||||
2. Clone the repository
|
||||
3. Follow the setup instructions above
|
||||
4. Run the desired diagram script
|
||||
|
||||
## Notes
|
||||
|
||||
- Generated diagram files (`.png`, `.gv`) are excluded from version control
|
||||
- The diagrams use a left-to-right (`LR`) layout direction
|
||||
- Output files are saved with `show=False` to prevent automatic opening
|
||||
|
||||
@@ -1,28 +1,26 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
diagram:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
container_name: diagram-viewer
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- traefik_net
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.diagram.rule=Host(`diagram.appmodel.nl`)"
|
||||
- "traefik.http.routers.diagram.entrypoints=websecure"
|
||||
- "traefik.http.routers.diagram.tls=true"
|
||||
- "traefik.http.routers.diagram.tls.certresolver=letsencrypt"
|
||||
- "traefik.http.services.diagram.loadbalancer.server.port=80"
|
||||
# Optional: mount logs
|
||||
volumes:
|
||||
- ./logs:/var/log/nginx
|
||||
# Optional: environment variables
|
||||
environment:
|
||||
- TZ=Europe/Amsterdam
|
||||
|
||||
networks:
|
||||
traefik_net:
|
||||
external: true
|
||||
services:
|
||||
viewer:
|
||||
build:
|
||||
context: /opt/apps/viewer
|
||||
dockerfile: Dockerfile
|
||||
container_name: viewer
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- traefik_net
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.viewer.rule=Host(`viewer.appmodel.nl`)"
|
||||
- "traefik.http.routers.viewer.entrypoints=websecure"
|
||||
- "traefik.http.routers.viewer.tls=true"
|
||||
- "traefik.http.services.viewer.loadbalancer.server.port=80"
|
||||
- "traefik.http.routers.viewer-http.rule=Host(`viewer.appmodel.nl`)"
|
||||
- "traefik.http.routers.viewer-http.entrypoints=web"
|
||||
- "traefik.http.routers.viewer-http.middlewares=viewer-https"
|
||||
- "traefik.http.middlewares.viewer-https.redirectscheme.scheme=https"
|
||||
- "traefik.http.routers.auction.tls.certresolver=letsencrypt",
|
||||
- "traefik.http.middlewares.viewer-https.redirectscheme.permanent=true"
|
||||
|
||||
networks:
|
||||
traefik_net:
|
||||
external: true
|
||||
name: traefik_net
|
||||
|
||||
@@ -1,218 +1,218 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Home-Lab diagram – Mermaid + Kroki (Graphviz)</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<style>
|
||||
:root{--bg:#f7f9fc;--text:#222;--accent:#0d6efd}
|
||||
body{margin:0;font-family:system-ui,-apple-system,Segoe UI,Roboto,Helvetica,Arial;font-size:14px;background:var(--bg);color:var(--text)}
|
||||
header{background:var(--accent);color:#fff;padding:.6rem 1rem;font-size:1.1rem;font-weight:600}
|
||||
.wrap{display:flex;height:calc(100vh - 40px)}
|
||||
.panel{flex:1;display:flex;flex-direction:column;overflow:hidden;border-right:1px solid #ddd}
|
||||
.panel:last-child{border:none}
|
||||
h3{margin:.4rem .6rem;font-size:1rem}
|
||||
.btn{margin:.2rem .6rem;padding:.3rem .6rem;border:1px solid #ccc;border-radius:4px;background:#fff;cursor:pointer}
|
||||
.btn:hover{background:#eee}
|
||||
#editor{flex:1}
|
||||
.diagram{flex:2;background:#fff;padding:.5rem;overflow:auto}
|
||||
svg{max-width:100%;height:auto}
|
||||
/* mermaid theme tweaks */
|
||||
.mermaid{height:100%;display:flex;align-items:center;justify-content:center}
|
||||
</style>
|
||||
|
||||
<!-- ========= 1. LIBRARIES ========= -->
|
||||
<!-- Monaco editor -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/monaco-editor@0.44.0/min/vs/loader.js"></script>
|
||||
<!-- Mermaid -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/mermaid@10.6.1/dist/mermaid.min.js"></script>
|
||||
<!-- Kroki client (tiny wrapper) -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/kroki-client@1/dist/kroki-client.min.js"></script>
|
||||
<!-- html2canvas for PNG export -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/html2canvas@1.4.1/dist/html2canvas.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<header>🏠 Home-Lab diagram – editable in browser (localStorage auto-saved)</header>
|
||||
|
||||
<div class="wrap">
|
||||
<!-- ---- LEFT: EDITOR ---- -->
|
||||
<div class="panel">
|
||||
<h3>Source (Mermaid syntax)</h3>
|
||||
<div id="editor"></div>
|
||||
<div style="padding:.4rem .6rem">
|
||||
<button class="btn" onclick="render()">▶ Render both</button>
|
||||
<button class="btn" onclick="savePNG()">💾 Save PNG</button>
|
||||
<button class="btn" onclick="saveSVG()">💾 Save SVG</button>
|
||||
<label class="btn">
|
||||
<input type="checkbox" id="dark"> dark
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ---- MIDDLE: MERMAID ---- -->
|
||||
<div class="panel">
|
||||
<h3>Mermaid render</h3>
|
||||
<div id="mermaid" class="diagram"></div>
|
||||
</div>
|
||||
|
||||
<!-- ---- RIGHT: KROKI / GRAPHVIZ ---- -->
|
||||
<div class="panel">
|
||||
<h3>Kroki (Graphviz) render</h3>
|
||||
<div id="kroki" class="diagram"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
/* ========= 2. DEFAULT SOURCE ========= */
|
||||
const DEFAULT = `flowchart TD
|
||||
%% ---------- colours ----------
|
||||
classDef internet fill:#e1f5ff,stroke:#007bff
|
||||
classDef router fill:#fff3cd,stroke:#ffc107
|
||||
classDef lan fill:#f8f9ff,stroke:#6c757d,stroke-width:2px
|
||||
classDef core fill:#ffe6e6,stroke:#dc3545
|
||||
classDef infra fill:#e6ffe6,stroke:#28a745
|
||||
classDef worker fill:#f0e6ff,stroke:#6f42c1
|
||||
classDef iot fill:#fff9e6,stroke:#fd7e14
|
||||
|
||||
%% ---------- nodes ----------
|
||||
internet(🌐 Internet / Cloud):::internet
|
||||
router{{🛜 Router<br/>hub.lan<br/>192.168.1.1}}:::router
|
||||
|
||||
subgraph LAN["🏠 LAN 192.168.1.0/24"]:::lan
|
||||
subgraph CORE["💻 Core server<br/>192.168.1.159"]:::core
|
||||
traefik[🚦 Traefik]:::core
|
||||
gitea[📚 Gitea]:::core
|
||||
dokku[🐳 Dokku]:::core
|
||||
auction[🧱 Auction stack]:::core
|
||||
mi50[🧠 MI50 / Ollama]:::core
|
||||
end
|
||||
|
||||
subgraph INFRA["🧭 Infra & DNS<br/>192.168.1.163"]:::infra
|
||||
adguard[🛡️ AdGuard]:::infra
|
||||
artifactory[📦 Artifactory]:::infra
|
||||
end
|
||||
|
||||
ha[🏡 Home Assistant<br/>192.168.1.193]:::infra
|
||||
atlas[🧱 Atlas<br/>192.168.1.100]:::worker
|
||||
|
||||
iot1[📺 IoT-1]:::iot
|
||||
iot2[📟 IoT-2]:::iot
|
||||
end
|
||||
|
||||
subgraph TETHER["📶 Tether 192.168.137.0/24"]:::lan
|
||||
hermes[🛰️ Hermes]:::worker
|
||||
plato[🛰️ Plato]:::worker
|
||||
end
|
||||
|
||||
dev[👨💻 Dev laptop]:::internet
|
||||
|
||||
%% ---------- edges ----------
|
||||
internet ==> router
|
||||
router --> CORE
|
||||
router --> INFRA
|
||||
router --> ha
|
||||
router --> atlas
|
||||
router --> iot1
|
||||
router --> iot2
|
||||
|
||||
dev ==> gitea
|
||||
dev ==> dokku
|
||||
dev ==> mi50
|
||||
|
||||
traefik --> gitea
|
||||
traefik --> auction
|
||||
traefik --> dokku
|
||||
|
||||
CORE -.->|DNS| adguard
|
||||
ha -.->|DNS| adguard
|
||||
atlas-.->|DNS| adguard
|
||||
hermes-.->|DNS| adguard
|
||||
plato-.->|DNS| adguard
|
||||
|
||||
CORE === TETHER
|
||||
`;
|
||||
|
||||
/* ========= 3. EDITOR SETUP ========= */
|
||||
let editor;
|
||||
require.config({paths:{vs:'https://cdn.jsdelivr.net/npm/monaco-editor@0.44.0/min/vs'}});
|
||||
require(['vs/editor/editor.main'], () => {
|
||||
editor = monaco.editor.create(document.getElementById('editor'), {
|
||||
value: localStorage.getItem('diagramSrc') || DEFAULT,
|
||||
language: 'markdown',
|
||||
theme: 'vs-dark',
|
||||
minimap: {enabled: false},
|
||||
wordWrap: 'on'
|
||||
});
|
||||
editor.onDidChangeModelContent(() => {
|
||||
localStorage.setItem('diagramSrc', editor.getValue());
|
||||
});
|
||||
render();
|
||||
});
|
||||
|
||||
/* ========= 4. RENDERERS ========= */
|
||||
mermaid.initialize({startOnLoad: false, theme: 'default'});
|
||||
|
||||
function render() {
|
||||
const src = editor.getValue();
|
||||
// 4a – mermaid
|
||||
document.getElementById('mermaid').innerHTML = '<div class="mermaid">'+src+'</div>';
|
||||
mermaid.init();
|
||||
// 4b – kroki graphviz
|
||||
Kroki.render('graphviz', dotFromMermaid(src)).then(svg => {
|
||||
document.getElementById('kroki').innerHTML = svg;
|
||||
}).catch(err => {
|
||||
document.getElementById('kroki').innerHTML = '<pre>'+err+'</pre>';
|
||||
});
|
||||
}
|
||||
|
||||
/* crude converter – good enough for this topology */
|
||||
function dotFromMermaid(m) {
|
||||
let dot = 'digraph G {\nbgcolor=transparent;rankdir=TB;node [shape=rect,style=rounded];\n';
|
||||
const lines = m.split('\n');
|
||||
let inSub = false;
|
||||
lines.forEach(l => {
|
||||
l = l.trim();
|
||||
if (l.startsWith('subgraph')) {
|
||||
const name = (l.match(/subgraph\s+(\w+)/) || [, 'cluster'])[1];
|
||||
dot += 'subgraph '+name+' {\nlabel="'+l.split('"')[1]+'";\nstyle=filled;fillcolor=lightgrey;\n';
|
||||
inSub = true;
|
||||
} else if (l === 'end') {
|
||||
dot += '}\n';
|
||||
inSub = false;
|
||||
} else if (l.includes('[') && l.includes(']')) {
|
||||
const id = l.split('[')[0].trim();
|
||||
const label = (l.match(/\[(.*?)\]/) || [, id])[1];
|
||||
dot += id + ' [label="' + label.replace(/:::.*$/,'') + '"];\n';
|
||||
} else if (l.includes('-->') || l.includes('==>') || l.includes('-.->')) {
|
||||
const arr = l.replace(/[~=>\-.]+/g,'->').split('->').map(x=>x.trim()).filter(Boolean);
|
||||
if (arr.length === 2) dot += arr[0] + ' -> ' + arr[1] + ';\n';
|
||||
}
|
||||
});
|
||||
dot += '}';
|
||||
return dot;
|
||||
}
|
||||
|
||||
/* ========= 5. EXPORT ========= */
|
||||
function savePNG() {
|
||||
html2canvas(document.querySelector('#mermaid svg')).then(canvas => {
|
||||
download(canvas.toDataURL(), 'home-lab.png');
|
||||
});
|
||||
}
|
||||
function saveSVG() {
|
||||
const svg = document.querySelector('#mermaid svg');
|
||||
const url = 'data:image/svg+xml;charset=utf-8,'+encodeURIComponent(svg.outerHTML);
|
||||
download(url, 'home-lab.svg');
|
||||
}
|
||||
function download(href, name) {
|
||||
const a = Object.assign(document.createElement('a'), {href, download: name});
|
||||
document.body.appendChild(a); a.click(); a.remove();
|
||||
}
|
||||
|
||||
/* dark toggle */
|
||||
document.getElementById('dark').onchange = e => {
|
||||
document.body.style.background = e.target.checked ? '#121212' : '#f7f9fc';
|
||||
document.body.style.color = e.target.checked ? '#eee' : '#222';
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Home-Lab viewer – Mermaid + Kroki (Graphviz)</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<style>
|
||||
:root{--bg:#f7f9fc;--text:#222;--accent:#0d6efd}
|
||||
body{margin:0;font-family:system-ui,-apple-system,Segoe UI,Roboto,Helvetica,Arial;font-size:14px;background:var(--bg);color:var(--text)}
|
||||
header{background:var(--accent);color:#fff;padding:.6rem 1rem;font-size:1.1rem;font-weight:600}
|
||||
.wrap{display:flex;height:calc(100vh - 40px)}
|
||||
.panel{flex:1;display:flex;flex-direction:column;overflow:hidden;border-right:1px solid #ddd}
|
||||
.panel:last-child{border:none}
|
||||
h3{margin:.4rem .6rem;font-size:1rem}
|
||||
.btn{margin:.2rem .6rem;padding:.3rem .6rem;border:1px solid #ccc;border-radius:4px;background:#fff;cursor:pointer}
|
||||
.btn:hover{background:#eee}
|
||||
#editor{flex:1}
|
||||
.diagram{flex:2;background:#fff;padding:.5rem;overflow:auto}
|
||||
svg{max-width:100%;height:auto}
|
||||
/* mermaid theme tweaks */
|
||||
.mermaid{height:100%;display:flex;align-items:center;justify-content:center}
|
||||
</style>
|
||||
|
||||
<!-- ========= 1. LIBRARIES ========= -->
|
||||
<!-- Monaco editor -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/monaco-editor@0.44.0/min/vs/loader.js"></script>
|
||||
<!-- Mermaid -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/mermaid@10.6.1/dist/mermaid.min.js"></script>
|
||||
<!-- Kroki client (tiny wrapper) -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/kroki-client@1/dist/kroki-client.min.js"></script>
|
||||
<!-- html2canvas for PNG export -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/html2canvas@1.4.1/dist/html2canvas.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<header>🏠 Home-Lab viewer – editable in browser (localStorage auto-saved)</header>
|
||||
|
||||
<div class="wrap">
|
||||
<!-- ---- LEFT: EDITOR ---- -->
|
||||
<div class="panel">
|
||||
<h3>Source (Mermaid syntax)</h3>
|
||||
<div id="editor"></div>
|
||||
<div style="padding:.4rem .6rem">
|
||||
<button class="btn" onclick="render()">▶ Render both</button>
|
||||
<button class="btn" onclick="savePNG()">💾 Save PNG</button>
|
||||
<button class="btn" onclick="saveSVG()">💾 Save SVG</button>
|
||||
<label class="btn">
|
||||
<input type="checkbox" id="dark"> dark
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ---- MIDDLE: MERMAID ---- -->
|
||||
<div class="panel">
|
||||
<h3>Mermaid render</h3>
|
||||
<div id="mermaid" class="diagram"></div>
|
||||
</div>
|
||||
|
||||
<!-- ---- RIGHT: KROKI / GRAPHVIZ ---- -->
|
||||
<div class="panel">
|
||||
<h3>Kroki (Graphviz) render</h3>
|
||||
<div id="kroki" class="diagram"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
/* ========= 2. DEFAULT SOURCE ========= */
|
||||
const DEFAULT = `flowchart TD
|
||||
%% ---------- colours ----------
|
||||
classDef internet fill:#e1f5ff,stroke:#007bff
|
||||
classDef router fill:#fff3cd,stroke:#ffc107
|
||||
classDef lan fill:#f8f9ff,stroke:#6c757d,stroke-width:2px
|
||||
classDef core fill:#ffe6e6,stroke:#dc3545
|
||||
classDef infra fill:#e6ffe6,stroke:#28a745
|
||||
classDef worker fill:#f0e6ff,stroke:#6f42c1
|
||||
classDef iot fill:#fff9e6,stroke:#fd7e14
|
||||
|
||||
%% ---------- nodes ----------
|
||||
internet(🌐 Internet / Cloud):::internet
|
||||
router{{🛜 Router<br/>hub.lan<br/>192.168.1.1}}:::router
|
||||
|
||||
subgraph LAN["🏠 LAN 192.168.1.0/24"]:::lan
|
||||
subgraph CORE["💻 Core server<br/>192.168.1.159"]:::core
|
||||
traefik[🚦 Traefik]:::core
|
||||
gitea[📚 Gitea]:::core
|
||||
dokku[🐳 Dokku]:::core
|
||||
auction[🧱 Auction stack]:::core
|
||||
mi50[🧠 MI50 / Ollama]:::core
|
||||
end
|
||||
|
||||
subgraph INFRA["🧭 Infra & DNS<br/>192.168.1.163"]:::infra
|
||||
adguard[🛡️ AdGuard]:::infra
|
||||
artifactory[📦 Artifactory]:::infra
|
||||
end
|
||||
|
||||
ha[🏡 Home Assistant<br/>192.168.1.193]:::infra
|
||||
atlas[🧱 Atlas<br/>192.168.1.100]:::worker
|
||||
|
||||
iot1[📺 IoT-1]:::iot
|
||||
iot2[📟 IoT-2]:::iot
|
||||
end
|
||||
|
||||
subgraph TETHER["📶 Tether 192.168.137.0/24"]:::lan
|
||||
hermes[🛰️ Hermes]:::worker
|
||||
plato[🛰️ Plato]:::worker
|
||||
end
|
||||
|
||||
dev[👨💻 Dev laptop]:::internet
|
||||
|
||||
%% ---------- edges ----------
|
||||
internet ==> router
|
||||
router --> CORE
|
||||
router --> INFRA
|
||||
router --> ha
|
||||
router --> atlas
|
||||
router --> iot1
|
||||
router --> iot2
|
||||
|
||||
dev ==> gitea
|
||||
dev ==> dokku
|
||||
dev ==> mi50
|
||||
|
||||
traefik --> gitea
|
||||
traefik --> auction
|
||||
traefik --> dokku
|
||||
|
||||
CORE -.->|DNS| adguard
|
||||
ha -.->|DNS| adguard
|
||||
atlas-.->|DNS| adguard
|
||||
hermes-.->|DNS| adguard
|
||||
plato-.->|DNS| adguard
|
||||
|
||||
CORE === TETHER
|
||||
`;
|
||||
|
||||
/* ========= 3. EDITOR SETUP ========= */
|
||||
let editor;
|
||||
require.config({paths:{vs:'https://cdn.jsdelivr.net/npm/monaco-editor@0.44.0/min/vs'}});
|
||||
require(['vs/editor/editor.main'], () => {
|
||||
editor = monaco.editor.create(document.getElementById('editor'), {
|
||||
value: localStorage.getItem('diagramSrc') || DEFAULT,
|
||||
language: 'markdown',
|
||||
theme: 'vs-dark',
|
||||
minimap: {enabled: false},
|
||||
wordWrap: 'on'
|
||||
});
|
||||
editor.onDidChangeModelContent(() => {
|
||||
localStorage.setItem('diagramSrc', editor.getValue());
|
||||
});
|
||||
render();
|
||||
});
|
||||
|
||||
/* ========= 4. RENDERERS ========= */
|
||||
mermaid.initialize({startOnLoad: false, theme: 'default'});
|
||||
|
||||
function render() {
|
||||
const src = editor.getValue();
|
||||
// 4a – mermaid
|
||||
document.getElementById('mermaid').innerHTML = '<div class="mermaid">'+src+'</div>';
|
||||
mermaid.init();
|
||||
// 4b – kroki graphviz
|
||||
Kroki.render('graphviz', dotFromMermaid(src)).then(svg => {
|
||||
document.getElementById('kroki').innerHTML = svg;
|
||||
}).catch(err => {
|
||||
document.getElementById('kroki').innerHTML = '<pre>'+err+'</pre>';
|
||||
});
|
||||
}
|
||||
|
||||
/* crude converter – good enough for this topology */
|
||||
function dotFromMermaid(m) {
|
||||
let dot = 'digraph G {\nbgcolor=transparent;rankdir=TB;node [shape=rect,style=rounded];\n';
|
||||
const lines = m.split('\n');
|
||||
let inSub = false;
|
||||
lines.forEach(l => {
|
||||
l = l.trim();
|
||||
if (l.startsWith('subgraph')) {
|
||||
const name = (l.match(/subgraph\s+(\w+)/) || [, 'cluster'])[1];
|
||||
dot += 'subgraph '+name+' {\nlabel="'+l.split('"')[1]+'";\nstyle=filled;fillcolor=lightgrey;\n';
|
||||
inSub = true;
|
||||
} else if (l === 'end') {
|
||||
dot += '}\n';
|
||||
inSub = false;
|
||||
} else if (l.includes('[') && l.includes(']')) {
|
||||
const id = l.split('[')[0].trim();
|
||||
const label = (l.match(/\[(.*?)\]/) || [, id])[1];
|
||||
dot += id + ' [label="' + label.replace(/:::.*$/,'') + '"];\n';
|
||||
} else if (l.includes('-->') || l.includes('==>') || l.includes('-.->')) {
|
||||
const arr = l.replace(/[~=>\-.]+/g,'->').split('->').map(x=>x.trim()).filter(Boolean);
|
||||
if (arr.length === 2) dot += arr[0] + ' -> ' + arr[1] + ';\n';
|
||||
}
|
||||
});
|
||||
dot += '}';
|
||||
return dot;
|
||||
}
|
||||
|
||||
/* ========= 5. EXPORT ========= */
|
||||
function savePNG() {
|
||||
html2canvas(document.querySelector('#mermaid svg')).then(canvas => {
|
||||
download(canvas.toDataURL(), 'home-lab.png');
|
||||
});
|
||||
}
|
||||
function saveSVG() {
|
||||
const svg = document.querySelector('#mermaid svg');
|
||||
const url = 'data:image/svg+xml;charset=utf-8,'+encodeURIComponent(svg.outerHTML);
|
||||
download(url, 'home-lab.svg');
|
||||
}
|
||||
function download(href, name) {
|
||||
const a = Object.assign(document.createElement('a'), {href, download: name});
|
||||
document.body.appendChild(a); a.click(); a.remove();
|
||||
}
|
||||
|
||||
/* dark toggle */
|
||||
document.getElementById('dark').onchange = e => {
|
||||
document.body.style.background = e.target.checked ? '#121212' : '#f7f9fc';
|
||||
document.body.style.color = e.target.checked ? '#eee' : '#222';
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
BIN
lab/home_lab_architecture.png
Normal file
BIN
lab/home_lab_architecture.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 276 KiB |
564
lab/preview.html
564
lab/preview.html
@@ -1,283 +1,283 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Home-Lab diagram – live editor</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<style>
|
||||
:root{--bg:#f7f9fc;--text:#222;--accent:#0d6efd}
|
||||
body{margin:0;font-family:system-ui,-apple-system,Segoe UI,Roboto,Helvetica,Arial;font-size:14px;background:var(--bg);color:var(--text)}
|
||||
header{background:var(--accent);color:#fff;padding:.6rem 1rem;font-weight:600;display:flex;gap:.5rem;align-items:center}
|
||||
header button{padding:.2rem .6rem;border:none;border-radius:4px;background:#fff;color:var(--accent);cursor:pointer}
|
||||
.wrap{display:flex;height:calc(100vh - 40px)}
|
||||
.panel{flex:1;display:flex;flex-direction:column;overflow:hidden;border-right:1px solid #ddd;transition:flex .3s ease}
|
||||
.panel:last-child{border:none}
|
||||
h3{margin:.4rem .6rem;font-size:1rem}
|
||||
#src, #preview{flex:1;padding:.5rem;overflow:auto;background:#fff;font-family:"Consolas","Monaco",monospace}
|
||||
#src{border:none;outline:none;background:#1e1e1e;color:#d4d4d4;white-space:pre-wrap}
|
||||
#preview{display:flex;align-items:flex-start;justify-content:center;min-height:0}
|
||||
svg{max-width:100%;height:auto}
|
||||
#error{color:#d32f2f;background:#fff3cd;padding:.5rem;margin:.5rem;border-left:4px solid #d32f2f;display:none}
|
||||
#zoomVal{margin-left:.5rem;font-size:.9rem;color:#fff}
|
||||
/* collapsed state */
|
||||
#srcPanel.collapsed{flex:0}
|
||||
#srcPanel.collapsed #src{display:none}
|
||||
</style>
|
||||
<!-- ONLY Mermaid – NO Monaco, NO AMD loader -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/mermaid@10.6.1/dist/mermaid.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<button id="toggleBtn" onclick="togglePanel()">🗔 Hide source</button>
|
||||
Home-Lab diagram – live Mermaid editor
|
||||
<button onclick="saveSVG()">💾 SVG</button>
|
||||
<button onclick="savePNG()">💾 PNG</button>
|
||||
<button onclick="reset()">🔄 Reset</button>
|
||||
<label style="margin-left:auto;display:flex;align-items:center;gap:.3rem;color:#fff">
|
||||
Zoom: <input type="range" id="zoom" min="50" max="300" value="100" style="width:100px"><span id="zoomVal">100%</span>
|
||||
</label>
|
||||
</header>
|
||||
|
||||
<div class="wrap">
|
||||
<div class="panel" id="srcPanel">
|
||||
<h3>Source (edit here)</h3>
|
||||
<textarea id="src" spellcheck="false"></textarea>
|
||||
</div>
|
||||
<div class="panel">
|
||||
<h3>Live preview</h3>
|
||||
<div id="preview"></div>
|
||||
<div id="error"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
/* ---------- DEFAULT SOURCE – guaranteed valid Mermaid 10.6.1 ---------- */
|
||||
const DEFAULT = `flowchart TD
|
||||
%% ---------- styles ----------
|
||||
classDef internet fill:#e1f5ff,stroke:#007bff
|
||||
classDef router fill:#fff3cd,stroke:#ffc107
|
||||
classDef lan fill:#f8f9ff,stroke:#6c757d,stroke-width:2px
|
||||
classDef core fill:#ffe6e6,stroke:#dc3545
|
||||
classDef infra fill:#e6ffe6,stroke:#28a745
|
||||
classDef worker fill:#f0e6ff,stroke:#6f42c1
|
||||
classDef iot fill:#fff9e6,stroke:#fd7e14
|
||||
|
||||
%% ---------- nodes ----------
|
||||
internet(🌐 Internet / Cloud):::internet
|
||||
router[🛜 Router\nhub.lan\n192.168.1.1]:::router
|
||||
|
||||
subgraph LAN [🏠 LAN 192.168.1.0/24]
|
||||
subgraph CORE [💻 Core server\n192.168.1.159]
|
||||
traefik[🚦 Traefik]:::core
|
||||
gitea[📚 Gitea]:::core
|
||||
dokku[🐳 Dokku]:::core
|
||||
auction[🧱 Auction stack]:::core
|
||||
mi50[🧠 MI50 / Ollama]:::core
|
||||
end
|
||||
|
||||
subgraph INFRA [🧭 Infra & DNS\n192.168.1.163]
|
||||
adguard[🛡️ AdGuard]:::infra
|
||||
artifactory[📦 Artifactory]:::infra
|
||||
end
|
||||
|
||||
ha[🏡 Home Assistant\n192.168.1.193]:::infra
|
||||
atlas[🧱 Atlas\n192.168.1.100]:::worker
|
||||
|
||||
iot1[📺 IoT-1]:::iot
|
||||
iot2[📟 IoT-2]:::iot
|
||||
end
|
||||
|
||||
subgraph TETHER [📶 Tether 192.168.137.0/24]
|
||||
hermes[🛰️ Hermes]:::worker
|
||||
plato[🛰️ Plato]:::worker
|
||||
end
|
||||
|
||||
dev[👨💻 Dev laptop]:::internet
|
||||
|
||||
%% ---------- edges ----------
|
||||
internet ==> router
|
||||
router --> CORE
|
||||
router --> INFRA
|
||||
router --> ha
|
||||
router --> atlas
|
||||
router --> iot1
|
||||
router --> iot2
|
||||
|
||||
dev ==> gitea
|
||||
dev ==> dokku
|
||||
dev ==> mi50
|
||||
|
||||
traefik --> gitea
|
||||
traefik --> auction
|
||||
traefik --> dokku
|
||||
|
||||
CORE -.->|DNS| adguard
|
||||
ha -.->|DNS| adguard
|
||||
atlas-.->|DNS| adguard
|
||||
hermes-.->|DNS| adguard
|
||||
plato-.->|DNS| adguard
|
||||
|
||||
CORE === TETHER
|
||||
`;
|
||||
|
||||
/* ---------- safe localStorage access ---------- */
|
||||
function getStorage(key, fallback) {
|
||||
try {
|
||||
const val = localStorage.getItem(key);
|
||||
return val !== null ? val : fallback;
|
||||
} catch (e) {
|
||||
// IntelliJ preview blocks localStorage
|
||||
console.warn('localStorage unavailable:', e);
|
||||
return fallback;
|
||||
}
|
||||
}
|
||||
function setStorage(key, val) {
|
||||
try {
|
||||
localStorage.setItem(key, val);
|
||||
} catch (e) {
|
||||
console.warn('localStorage save failed:', e);
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------- load state ---------- */
|
||||
const srcEl = document.getElementById('src');
|
||||
const errEl = document.getElementById('error');
|
||||
srcEl.value = getStorage('labDiagram', DEFAULT);
|
||||
|
||||
const srcPanel = document.getElementById('srcPanel');
|
||||
const toggleBtn = document.getElementById('toggleBtn');
|
||||
if (getStorage('panelCollapsed', 'false') === 'true') {
|
||||
srcPanel.classList.add('collapsed');
|
||||
toggleBtn.textContent = '🗔 Show source';
|
||||
}
|
||||
|
||||
/* ---------- panel toggle ---------- */
|
||||
function togglePanel() {
|
||||
srcPanel.classList.toggle('collapsed');
|
||||
const collapsed = srcPanel.classList.contains('collapsed');
|
||||
toggleBtn.textContent = collapsed ? '🗔 Show source' : '🗔 Hide source';
|
||||
setStorage('panelCollapsed', collapsed);
|
||||
}
|
||||
|
||||
/* ---------- Mermaid init ---------- */
|
||||
mermaid.initialize({startOnLoad: false, theme: 'default'});
|
||||
|
||||
/* ---------- render ---------- */
|
||||
function render() {
|
||||
const src = srcEl.value;
|
||||
setStorage('labDiagram', src);
|
||||
showError('');
|
||||
|
||||
const preview = document.getElementById('preview');
|
||||
preview.innerHTML = '';
|
||||
|
||||
const mermaidDiv = document.createElement('div');
|
||||
mermaidDiv.className = 'mermaid';
|
||||
mermaidDiv.textContent = src;
|
||||
preview.appendChild(mermaidDiv);
|
||||
|
||||
// Render then zoom
|
||||
mermaid.init(undefined, mermaidDiv).then(() => {
|
||||
const svg = preview.querySelector('svg');
|
||||
if (svg) {
|
||||
svg.style.maxWidth = 'none';
|
||||
svg.style.width = 'auto';
|
||||
svg.style.height = 'auto';
|
||||
svg.style.transformOrigin = 'top left';
|
||||
applyZoom();
|
||||
}
|
||||
}).catch(e => {
|
||||
showError('Mermaid error: ' + e.message);
|
||||
});
|
||||
}
|
||||
|
||||
function showError(msg) {
|
||||
errEl.textContent = msg;
|
||||
errEl.style.display = msg ? 'block' : 'none';
|
||||
}
|
||||
|
||||
/* ---------- auto-render + zoom ---------- */
|
||||
srcEl.addEventListener('input', () => {
|
||||
clearTimeout(srcEl._t);
|
||||
srcEl._t = setTimeout(render, 300);
|
||||
});
|
||||
|
||||
const zoomEl = document.getElementById('zoom');
|
||||
zoomEl.addEventListener('input', () => {
|
||||
document.getElementById('zoomVal').textContent = zoomEl.value + '%';
|
||||
applyZoom();
|
||||
});
|
||||
|
||||
function applyZoom() {
|
||||
const svg = document.querySelector('#preview svg');
|
||||
if (svg) {
|
||||
svg.style.transform = `scale(${zoomEl.value / 100})`;
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------- actions ---------- */
|
||||
function reset() {
|
||||
if (confirm('Reset to default diagram?')) {
|
||||
srcEl.value = DEFAULT;
|
||||
zoomEl.value = 100;
|
||||
document.getElementById('zoomVal').textContent = '100%';
|
||||
setStorage('labDiagram', DEFAULT);
|
||||
render();
|
||||
}
|
||||
}
|
||||
|
||||
function saveSVG() {
|
||||
const svg = document.querySelector('#preview svg');
|
||||
if (!svg) return alert('Nothing to save yet.');
|
||||
|
||||
const clone = svg.cloneNode(true);
|
||||
clone.removeAttribute('style');
|
||||
clone.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
|
||||
|
||||
const blob = new Blob([clone.outerHTML], {type: 'image/svg+xml'});
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = 'home-lab.svg';
|
||||
a.click();
|
||||
setTimeout(() => URL.revokeObjectURL(url), 100);
|
||||
}
|
||||
|
||||
function savePNG() {
|
||||
const svg = document.querySelector('#preview svg');
|
||||
if (!svg) return alert('Nothing to save yet.');
|
||||
|
||||
const bbox = svg.getBBox();
|
||||
const width = Math.max(bbox.width, 1200);
|
||||
const height = Math.max(bbox.height, 800);
|
||||
|
||||
const svgData = new XMLSerializer().serializeToString(svg);
|
||||
const img = new Image();
|
||||
|
||||
img.onload = () => {
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
ctx.fillStyle = 'white';
|
||||
ctx.fillRect(0, 0, width, height);
|
||||
ctx.drawImage(img, 0, 0, width, height);
|
||||
|
||||
canvas.toBlob(blob => {
|
||||
const a = document.createElement('a');
|
||||
a.href = URL.createObjectURL(blob);
|
||||
a.download = 'home-lab.png';
|
||||
a.click();
|
||||
});
|
||||
};
|
||||
|
||||
img.onerror = () => alert('PNG conversion failed. Try SVG instead.');
|
||||
img.src = 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(svgData);
|
||||
}
|
||||
|
||||
// Initial render
|
||||
render();
|
||||
</script>
|
||||
</body>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Home-Lab diagram – live editor</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<style>
|
||||
:root{--bg:#f7f9fc;--text:#222;--accent:#0d6efd}
|
||||
body{margin:0;font-family:system-ui,-apple-system,Segoe UI,Roboto,Helvetica,Arial;font-size:14px;background:var(--bg);color:var(--text)}
|
||||
header{background:var(--accent);color:#fff;padding:.6rem 1rem;font-weight:600;display:flex;gap:.5rem;align-items:center}
|
||||
header button{padding:.2rem .6rem;border:none;border-radius:4px;background:#fff;color:var(--accent);cursor:pointer}
|
||||
.wrap{display:flex;height:calc(100vh - 40px)}
|
||||
.panel{flex:1;display:flex;flex-direction:column;overflow:hidden;border-right:1px solid #ddd;transition:flex .3s ease}
|
||||
.panel:last-child{border:none}
|
||||
h3{margin:.4rem .6rem;font-size:1rem}
|
||||
#src, #preview{flex:1;padding:.5rem;overflow:auto;background:#fff;font-family:"Consolas","Monaco",monospace}
|
||||
#src{border:none;outline:none;background:#1e1e1e;color:#d4d4d4;white-space:pre-wrap}
|
||||
#preview{display:flex;align-items:flex-start;justify-content:center;min-height:0}
|
||||
svg{max-width:100%;height:auto}
|
||||
#error{color:#d32f2f;background:#fff3cd;padding:.5rem;margin:.5rem;border-left:4px solid #d32f2f;display:none}
|
||||
#zoomVal{margin-left:.5rem;font-size:.9rem;color:#fff}
|
||||
/* collapsed state */
|
||||
#srcPanel.collapsed{flex:0}
|
||||
#srcPanel.collapsed #src{display:none}
|
||||
</style>
|
||||
<!-- ONLY Mermaid – NO Monaco, NO AMD loader -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/mermaid@10.6.1/dist/mermaid.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<button id="toggleBtn" onclick="togglePanel()">🗔 Hide source</button>
|
||||
Home-Lab diagram – live Mermaid editor
|
||||
<button onclick="saveSVG()">💾 SVG</button>
|
||||
<button onclick="savePNG()">💾 PNG</button>
|
||||
<button onclick="reset()">🔄 Reset</button>
|
||||
<label style="margin-left:auto;display:flex;align-items:center;gap:.3rem;color:#fff">
|
||||
Zoom: <input type="range" id="zoom" min="50" max="300" value="100" style="width:100px"><span id="zoomVal">100%</span>
|
||||
</label>
|
||||
</header>
|
||||
|
||||
<div class="wrap">
|
||||
<div class="panel" id="srcPanel">
|
||||
<h3>Source (edit here)</h3>
|
||||
<textarea id="src" spellcheck="false"></textarea>
|
||||
</div>
|
||||
<div class="panel">
|
||||
<h3>Live preview</h3>
|
||||
<div id="preview"></div>
|
||||
<div id="error"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
/* ---------- DEFAULT SOURCE – guaranteed valid Mermaid 10.6.1 ---------- */
|
||||
const DEFAULT = `flowchart TD
|
||||
%% ---------- styles ----------
|
||||
classDef internet fill:#e1f5ff,stroke:#007bff
|
||||
classDef router fill:#fff3cd,stroke:#ffc107
|
||||
classDef lan fill:#f8f9ff,stroke:#6c757d,stroke-width:2px
|
||||
classDef core fill:#ffe6e6,stroke:#dc3545
|
||||
classDef infra fill:#e6ffe6,stroke:#28a745
|
||||
classDef worker fill:#f0e6ff,stroke:#6f42c1
|
||||
classDef iot fill:#fff9e6,stroke:#fd7e14
|
||||
|
||||
%% ---------- nodes ----------
|
||||
internet(🌐 Internet / Cloud):::internet
|
||||
router[🛜 Router\nhub.lan\n192.168.1.1]:::router
|
||||
|
||||
subgraph LAN [🏠 LAN 192.168.1.0/24]
|
||||
subgraph CORE [💻 Core server\n192.168.1.159]
|
||||
traefik[🚦 Traefik]:::core
|
||||
gitea[📚 Gitea]:::core
|
||||
dokku[🐳 Dokku]:::core
|
||||
auction[🧱 Auction stack]:::core
|
||||
mi50[🧠 MI50 / Ollama]:::core
|
||||
end
|
||||
|
||||
subgraph INFRA [🧭 Infra & DNS\n192.168.1.163]
|
||||
adguard[🛡️ AdGuard]:::infra
|
||||
artifactory[📦 Artifactory]:::infra
|
||||
end
|
||||
|
||||
ha[🏡 Home Assistant\n192.168.1.193]:::infra
|
||||
atlas[🧱 Atlas\n192.168.1.100]:::worker
|
||||
|
||||
iot1[📺 IoT-1]:::iot
|
||||
iot2[📟 IoT-2]:::iot
|
||||
end
|
||||
|
||||
subgraph TETHER [📶 Tether 192.168.137.0/24]
|
||||
hermes[🛰️ Hermes]:::worker
|
||||
plato[🛰️ Plato]:::worker
|
||||
end
|
||||
|
||||
dev[👨💻 Dev laptop]:::internet
|
||||
|
||||
%% ---------- edges ----------
|
||||
internet ==> router
|
||||
router --> CORE
|
||||
router --> INFRA
|
||||
router --> ha
|
||||
router --> atlas
|
||||
router --> iot1
|
||||
router --> iot2
|
||||
|
||||
dev ==> gitea
|
||||
dev ==> dokku
|
||||
dev ==> mi50
|
||||
|
||||
traefik --> gitea
|
||||
traefik --> auction
|
||||
traefik --> dokku
|
||||
|
||||
CORE -.->|DNS| adguard
|
||||
ha -.->|DNS| adguard
|
||||
atlas-.->|DNS| adguard
|
||||
hermes-.->|DNS| adguard
|
||||
plato-.->|DNS| adguard
|
||||
|
||||
CORE === TETHER
|
||||
`;
|
||||
|
||||
/* ---------- safe localStorage access ---------- */
|
||||
function getStorage(key, fallback) {
|
||||
try {
|
||||
const val = localStorage.getItem(key);
|
||||
return val !== null ? val : fallback;
|
||||
} catch (e) {
|
||||
// IntelliJ preview blocks localStorage
|
||||
console.warn('localStorage unavailable:', e);
|
||||
return fallback;
|
||||
}
|
||||
}
|
||||
function setStorage(key, val) {
|
||||
try {
|
||||
localStorage.setItem(key, val);
|
||||
} catch (e) {
|
||||
console.warn('localStorage save failed:', e);
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------- load state ---------- */
|
||||
const srcEl = document.getElementById('src');
|
||||
const errEl = document.getElementById('error');
|
||||
srcEl.value = getStorage('labDiagram', DEFAULT);
|
||||
|
||||
const srcPanel = document.getElementById('srcPanel');
|
||||
const toggleBtn = document.getElementById('toggleBtn');
|
||||
if (getStorage('panelCollapsed', 'false') === 'true') {
|
||||
srcPanel.classList.add('collapsed');
|
||||
toggleBtn.textContent = '🗔 Show source';
|
||||
}
|
||||
|
||||
/* ---------- panel toggle ---------- */
|
||||
function togglePanel() {
|
||||
srcPanel.classList.toggle('collapsed');
|
||||
const collapsed = srcPanel.classList.contains('collapsed');
|
||||
toggleBtn.textContent = collapsed ? '🗔 Show source' : '🗔 Hide source';
|
||||
setStorage('panelCollapsed', collapsed);
|
||||
}
|
||||
|
||||
/* ---------- Mermaid init ---------- */
|
||||
mermaid.initialize({startOnLoad: false, theme: 'default'});
|
||||
|
||||
/* ---------- render ---------- */
|
||||
function render() {
|
||||
const src = srcEl.value;
|
||||
setStorage('labDiagram', src);
|
||||
showError('');
|
||||
|
||||
const preview = document.getElementById('preview');
|
||||
preview.innerHTML = '';
|
||||
|
||||
const mermaidDiv = document.createElement('div');
|
||||
mermaidDiv.className = 'mermaid';
|
||||
mermaidDiv.textContent = src;
|
||||
preview.appendChild(mermaidDiv);
|
||||
|
||||
// Render then zoom
|
||||
mermaid.init(undefined, mermaidDiv).then(() => {
|
||||
const svg = preview.querySelector('svg');
|
||||
if (svg) {
|
||||
svg.style.maxWidth = 'none';
|
||||
svg.style.width = 'auto';
|
||||
svg.style.height = 'auto';
|
||||
svg.style.transformOrigin = 'top left';
|
||||
applyZoom();
|
||||
}
|
||||
}).catch(e => {
|
||||
showError('Mermaid error: ' + e.message);
|
||||
});
|
||||
}
|
||||
|
||||
function showError(msg) {
|
||||
errEl.textContent = msg;
|
||||
errEl.style.display = msg ? 'block' : 'none';
|
||||
}
|
||||
|
||||
/* ---------- auto-render + zoom ---------- */
|
||||
srcEl.addEventListener('input', () => {
|
||||
clearTimeout(srcEl._t);
|
||||
srcEl._t = setTimeout(render, 300);
|
||||
});
|
||||
|
||||
const zoomEl = document.getElementById('zoom');
|
||||
zoomEl.addEventListener('input', () => {
|
||||
document.getElementById('zoomVal').textContent = zoomEl.value + '%';
|
||||
applyZoom();
|
||||
});
|
||||
|
||||
function applyZoom() {
|
||||
const svg = document.querySelector('#preview svg');
|
||||
if (svg) {
|
||||
svg.style.transform = `scale(${zoomEl.value / 100})`;
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------- actions ---------- */
|
||||
function reset() {
|
||||
if (confirm('Reset to default diagram?')) {
|
||||
srcEl.value = DEFAULT;
|
||||
zoomEl.value = 100;
|
||||
document.getElementById('zoomVal').textContent = '100%';
|
||||
setStorage('labDiagram', DEFAULT);
|
||||
render();
|
||||
}
|
||||
}
|
||||
|
||||
function saveSVG() {
|
||||
const svg = document.querySelector('#preview svg');
|
||||
if (!svg) return alert('Nothing to save yet.');
|
||||
|
||||
const clone = svg.cloneNode(true);
|
||||
clone.removeAttribute('style');
|
||||
clone.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
|
||||
|
||||
const blob = new Blob([clone.outerHTML], {type: 'image/svg+xml'});
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = 'home-lab.svg';
|
||||
a.click();
|
||||
setTimeout(() => URL.revokeObjectURL(url), 100);
|
||||
}
|
||||
|
||||
function savePNG() {
|
||||
const svg = document.querySelector('#preview svg');
|
||||
if (!svg) return alert('Nothing to save yet.');
|
||||
|
||||
const bbox = svg.getBBox();
|
||||
const width = Math.max(bbox.width, 1200);
|
||||
const height = Math.max(bbox.height, 800);
|
||||
|
||||
const svgData = new XMLSerializer().serializeToString(svg);
|
||||
const img = new Image();
|
||||
|
||||
img.onload = () => {
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
ctx.fillStyle = 'white';
|
||||
ctx.fillRect(0, 0, width, height);
|
||||
ctx.drawImage(img, 0, 0, width, height);
|
||||
|
||||
canvas.toBlob(blob => {
|
||||
const a = document.createElement('a');
|
||||
a.href = URL.createObjectURL(blob);
|
||||
a.download = 'home-lab.png';
|
||||
a.click();
|
||||
});
|
||||
};
|
||||
|
||||
img.onerror = () => alert('PNG conversion failed. Try SVG instead.');
|
||||
img.src = 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(svgData);
|
||||
}
|
||||
|
||||
// Initial render
|
||||
render();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,121 +1,121 @@
|
||||
from diagrams import Diagram, Cluster, Edge
|
||||
from diagrams.onprem.network import Internet
|
||||
from diagrams.onprem.compute import Server
|
||||
from diagrams.onprem.iac import Ansible
|
||||
from diagrams.generic.network import Router, Switch
|
||||
from diagrams.generic.device import Mobile, Tablet
|
||||
from diagrams.generic.blank import Blank
|
||||
from diagrams.onprem.client import Users
|
||||
from diagrams.onprem.container import Docker
|
||||
from diagrams.custom import Custom
|
||||
|
||||
# Tip: run this in a venv:
|
||||
# pip install diagrams graphviz
|
||||
|
||||
with Diagram("Home Lab / Auction Stack Architecture", show=False, filename="home_lab_architecture", direction="LR"):
|
||||
internet = Internet("Internet / Cloud")
|
||||
|
||||
dev = Users("Dev laptop(s)\nWindows / WSL")
|
||||
|
||||
# -------------------- LAN 192.168.1.x --------------------
|
||||
with Cluster("LAN 192.168.1.0/24"):
|
||||
router = Router("hub.lan\n192.168.1.1\nRouter / Gateway")
|
||||
|
||||
# -------- Core server / desktop (Tour / hephaestus / dokku / ollama) --------
|
||||
with Cluster("Core server / desktop\nTour / hephaestus / dokku.lan / ollama.lan\n192.168.1.159"):
|
||||
core_os = Server("Ubuntu host")
|
||||
|
||||
traefik = Docker("Traefik\nReverse Proxy")
|
||||
gitea = Docker("Gitea\n git.appmodel.nl")
|
||||
dokku = Docker("Dokku\nPaaS / app hosting")
|
||||
auction_fe = Docker("Auction Frontend\n auction.appmodel.nl")
|
||||
aupi_be = Docker("Aupi API Backend\n aupi.appmodel.nl")
|
||||
mi50 = Server("MI50 GPU / Ollama\nAI workloads")
|
||||
|
||||
# -------- Infra & DNS (odroid / dns.lan) --------
|
||||
with Cluster("Infra & DNS\nodroid / dns.lan\n192.168.1.163"):
|
||||
dns_host = Server("Odroid host")
|
||||
adguard = Docker("AdGuard Home\nDNS / *.lan / *.appmodel.nl")
|
||||
artifactory = Docker("Artifactory (future)")
|
||||
runner = Docker("CI / Build runner (future)")
|
||||
|
||||
# -------- Home Assistant --------
|
||||
with Cluster("Home Automation\nha.lan\n192.168.1.193"):
|
||||
ha_host = Server("HomeAssistant host")
|
||||
hass = Docker("Home Assistant")
|
||||
|
||||
# -------- Extra node / worker --------
|
||||
atlas = Server("atlas.lan\n192.168.1.100\n(extra node / worker)")
|
||||
|
||||
# -------- IoT / devices --------
|
||||
with Cluster("IoT / Clients"):
|
||||
iot_hof = Tablet("hof-E402NA\n192.168.1.214")
|
||||
iot_s380 = Tablet("S380HB\n192.168.1.59")
|
||||
iot_ecb5 = Tablet("ecb5faa56c90\n192.168.1.49")
|
||||
iot_unknown = Tablet("Unknown\n192.168.1.240")
|
||||
|
||||
# -------------------- Tether subnet 192.168.137.x --------------------
|
||||
with Cluster("Tether subnet 192.168.137.0/24"):
|
||||
hermes = Server("hermes.lan\n192.168.137.239\nworker / node")
|
||||
plato = Server("plato.lan\n192.168.137.163\nworker / node")
|
||||
|
||||
# -------------------- Edges / flows --------------------
|
||||
|
||||
# Internet naar router + DNS host
|
||||
internet >> router
|
||||
internet >> dns_host
|
||||
|
||||
# Dev naar Gitea / Traefik / Dokku
|
||||
dev >> Edge(label="git / HTTPS") >> traefik
|
||||
dev >> Edge(label="SSH / HTTPS") >> gitea
|
||||
dev >> Edge(label="Dokku deploys") >> dokku
|
||||
|
||||
# Router naar alle LAN-nodes
|
||||
router >> core_os
|
||||
router >> dns_host
|
||||
router >> ha_host
|
||||
router >> atlas
|
||||
router >> iot_hof
|
||||
router >> iot_s380
|
||||
router >> iot_ecb5
|
||||
router >> iot_unknown
|
||||
|
||||
# Core server services
|
||||
core_os >> traefik
|
||||
core_os >> gitea
|
||||
core_os >> dokku
|
||||
core_os >> auction_fe
|
||||
core_os >> aupi_be
|
||||
core_os >> mi50
|
||||
|
||||
# Infra/DNS services
|
||||
dns_host >> adguard
|
||||
dns_host >> artifactory
|
||||
dns_host >> runner
|
||||
|
||||
# Home Assistant
|
||||
ha_host >> hass
|
||||
|
||||
# DNS-queries
|
||||
core_os >> Edge(label="DNS") >> adguard
|
||||
ha_host >> Edge(label="DNS") >> adguard
|
||||
atlas >> Edge(label="DNS") >> adguard
|
||||
hermes >> Edge(label="DNS") >> adguard
|
||||
plato >> Edge(label="DNS") >> adguard
|
||||
|
||||
# Web traffic / reverse proxy flows
|
||||
internet >> Edge(label="HTTP/HTTPS") >> traefik
|
||||
traefik >> Edge(label="git.appmodel.nl") >> gitea
|
||||
traefik >> Edge(label="auction.appmodel.nl") >> auction_fe
|
||||
traefik >> Edge(label="aupi.appmodel.nl") >> aupi_be
|
||||
traefik >> Edge(label="dokku.lan / apps") >> dokku
|
||||
|
||||
# App-level flow
|
||||
auction_fe >> Edge(label="REST API") >> aupi_be
|
||||
|
||||
# AI workloads
|
||||
dev >> Edge(label="LLM / Tuning / Inference") >> mi50
|
||||
|
||||
# Tether workers verbonden met core (bv. jobs / agents)
|
||||
core_os >> Edge(label="jobs / ssh") >> hermes
|
||||
core_os >> Edge(label="jobs / ssh") >> plato
|
||||
from diagrams import Diagram, Cluster, Edge
|
||||
from diagrams.onprem.network import Internet
|
||||
from diagrams.onprem.compute import Server
|
||||
from diagrams.onprem.iac import Ansible
|
||||
from diagrams.generic.network import Router, Switch
|
||||
from diagrams.generic.device import Mobile, Tablet
|
||||
from diagrams.generic.blank import Blank
|
||||
from diagrams.onprem.client import Users
|
||||
from diagrams.onprem.container import Docker
|
||||
from diagrams.custom import Custom
|
||||
|
||||
# Tip: run this in a venv:
|
||||
# pip install diagrams graphviz
|
||||
|
||||
with Diagram("Home Lab / Auction Stack Architecture", show=False, filename="home_lab_architecture", direction="LR"):
|
||||
internet = Internet("Internet / Cloud")
|
||||
|
||||
dev = Users("Dev laptop(s)\nWindows / WSL")
|
||||
|
||||
# -------------------- LAN 192.168.1.x --------------------
|
||||
with Cluster("LAN 192.168.1.0/24"):
|
||||
router = Router("hub.lan\n192.168.1.1\nRouter / Gateway")
|
||||
|
||||
# -------- Core server / desktop (Tour / hephaestus / dokku / ollama) --------
|
||||
with Cluster("Core server / desktop\nTour / hephaestus / dokku.lan / ollama.lan\n192.168.1.159"):
|
||||
core_os = Server("Ubuntu host")
|
||||
|
||||
traefik = Docker("Traefik\nReverse Proxy")
|
||||
gitea = Docker("Gitea\n git.appmodel.nl")
|
||||
dokku = Docker("Dokku\nPaaS / app hosting")
|
||||
auction_fe = Docker("Auction Frontend\n auction.appmodel.nl")
|
||||
aupi_be = Docker("Aupi API Backend\n aupi.appmodel.nl")
|
||||
mi50 = Server("MI50 GPU / Ollama\nAI workloads")
|
||||
|
||||
# -------- Infra & DNS (odroid / dns.lan) --------
|
||||
with Cluster("Infra & DNS\nodroid / dns.lan\n192.168.1.163"):
|
||||
dns_host = Server("Odroid host")
|
||||
adguard = Docker("AdGuard Home\nDNS / *.lan / *.appmodel.nl")
|
||||
artifactory = Docker("Artifactory (future)")
|
||||
runner = Docker("CI / Build runner (future)")
|
||||
|
||||
# -------- Home Assistant --------
|
||||
with Cluster("Home Automation\nha.lan\n192.168.1.193"):
|
||||
ha_host = Server("HomeAssistant host")
|
||||
hass = Docker("Home Assistant")
|
||||
|
||||
# -------- Extra node / worker --------
|
||||
atlas = Server("atlas.lan\n192.168.1.100\n(extra node / worker)")
|
||||
|
||||
# -------- IoT / devices --------
|
||||
with Cluster("IoT / Clients"):
|
||||
iot_hof = Tablet("hof-E402NA\n192.168.1.214")
|
||||
iot_s380 = Tablet("S380HB\n192.168.1.59")
|
||||
iot_ecb5 = Tablet("ecb5faa56c90\n192.168.1.49")
|
||||
iot_unknown = Tablet("Unknown\n192.168.1.240")
|
||||
|
||||
# -------------------- Tether subnet 192.168.137.x --------------------
|
||||
with Cluster("Tether subnet 192.168.137.0/24"):
|
||||
hermes = Server("hermes.lan\n192.168.137.239\nworker / node")
|
||||
plato = Server("plato.lan\n192.168.137.163\nworker / node")
|
||||
|
||||
# -------------------- Edges / flows --------------------
|
||||
|
||||
# Internet naar router + DNS host
|
||||
internet >> router
|
||||
internet >> dns_host
|
||||
|
||||
# Dev naar Gitea / Traefik / Dokku
|
||||
dev >> Edge(label="git / HTTPS") >> traefik
|
||||
dev >> Edge(label="SSH / HTTPS") >> gitea
|
||||
dev >> Edge(label="Dokku deploys") >> dokku
|
||||
|
||||
# Router naar alle LAN-nodes
|
||||
router >> core_os
|
||||
router >> dns_host
|
||||
router >> ha_host
|
||||
router >> atlas
|
||||
router >> iot_hof
|
||||
router >> iot_s380
|
||||
router >> iot_ecb5
|
||||
router >> iot_unknown
|
||||
|
||||
# Core server services
|
||||
core_os >> traefik
|
||||
core_os >> gitea
|
||||
core_os >> dokku
|
||||
core_os >> auction_fe
|
||||
core_os >> aupi_be
|
||||
core_os >> mi50
|
||||
|
||||
# Infra/DNS services
|
||||
dns_host >> adguard
|
||||
dns_host >> artifactory
|
||||
dns_host >> runner
|
||||
|
||||
# Home Assistant
|
||||
ha_host >> hass
|
||||
|
||||
# DNS-queries
|
||||
core_os >> Edge(label="DNS") >> adguard
|
||||
ha_host >> Edge(label="DNS") >> adguard
|
||||
atlas >> Edge(label="DNS") >> adguard
|
||||
hermes >> Edge(label="DNS") >> adguard
|
||||
plato >> Edge(label="DNS") >> adguard
|
||||
|
||||
# Web traffic / reverse proxy flows
|
||||
internet >> Edge(label="HTTP/HTTPS") >> traefik
|
||||
traefik >> Edge(label="git.appmodel.nl") >> gitea
|
||||
traefik >> Edge(label="auction.appmodel.nl") >> auction_fe
|
||||
traefik >> Edge(label="aupi.appmodel.nl") >> aupi_be
|
||||
traefik >> Edge(label="dokku.lan / apps") >> dokku
|
||||
|
||||
# App-level flow
|
||||
auction_fe >> Edge(label="REST API") >> aupi_be
|
||||
|
||||
# AI workloads
|
||||
dev >> Edge(label="LLM / Tuning / Inference") >> mi50
|
||||
|
||||
# Tether workers verbonden met core (bv. jobs / agents)
|
||||
core_os >> Edge(label="jobs / ssh") >> hermes
|
||||
core_os >> Edge(label="jobs / ssh") >> plato
|
||||
|
||||
318
main.py
318
main.py
@@ -1,159 +1,159 @@
|
||||
from graphviz import Digraph
|
||||
|
||||
def make_network_diagram(output_name: str = "network-architecture"):
|
||||
g = Digraph(
|
||||
"network",
|
||||
filename=f"{output_name}.gv",
|
||||
format="png",
|
||||
)
|
||||
|
||||
# Globale stijl
|
||||
g.attr(rankdir="LR", fontname="Segoe UI")
|
||||
|
||||
# ---------- WAN / Internet ----------
|
||||
with g.subgraph(name="cluster_wan") as wan:
|
||||
wan.attr(
|
||||
label="🌐 Internet / Cloud",
|
||||
style="rounded",
|
||||
color="lightgrey",
|
||||
fontsize="16",
|
||||
)
|
||||
wan.node("extDNS", "📡 Public DNS", shape="rectangle")
|
||||
wan.node("extGit", "☁️ Externe registries / Git\n(GitHub / Docker Hub)", shape="rectangle")
|
||||
|
||||
# ---------- LAN 192.168.1.0/24 ----------
|
||||
with g.subgraph(name="cluster_lan") as lan:
|
||||
lan.attr(
|
||||
label="🏠 LAN 192.168.1.0/24",
|
||||
style="rounded",
|
||||
color="lightgrey",
|
||||
fontsize="16",
|
||||
)
|
||||
|
||||
# Router / gateway
|
||||
lan.node(
|
||||
"hub",
|
||||
"🛜 Router / Gateway\nhub.lan\n192.168.1.1",
|
||||
shape="rectangle",
|
||||
style="filled",
|
||||
fillcolor="#f0f0ff",
|
||||
)
|
||||
|
||||
# ---- Core server / desktop ----
|
||||
with lan.subgraph(name="cluster_core") as core:
|
||||
core.attr(
|
||||
label="💻 Hoofdserver / Desktop\nTour / hephaestus / ollama / dokku.lan\n192.168.1.159",
|
||||
style="rounded",
|
||||
color="#aaccee",
|
||||
)
|
||||
core.node("traefik", "🚦 Traefik\nReverse Proxy", shape="rectangle")
|
||||
core.node("gitea", "📚 Gitea\ngit.appmodel.nl", shape="rectangle")
|
||||
core.node("dokku", "🐳 Dokku\nPaaS / build", shape="rectangle")
|
||||
core.node("auctionFE", "🧱 Auction Frontend\nauction.appmodel.nl", shape="rectangle")
|
||||
core.node("aupiAPI", "🧱 Auction Backend API\naupi.appmodel.nl", shape="rectangle")
|
||||
core.node("mi50", "🧠 MI50 / Ollama\nAI workloads", shape="rectangle")
|
||||
|
||||
# Aanvulling: monitoring / logging
|
||||
core.node("monitoring", "📈 Monitoring / Logging\nPrometheus / Loki / Grafana", shape="rectangle")
|
||||
|
||||
# ---- Infra & DNS ----
|
||||
with lan.subgraph(name="cluster_infra_dns") as infra:
|
||||
infra.attr(
|
||||
label="🧭 Infra & DNS\nodroid / dns.lan\n192.168.1.163",
|
||||
style="rounded",
|
||||
color="#aaddaa",
|
||||
)
|
||||
infra.node("adguard", "🧭 AdGuard Home\nDNS / *.lan / *.appmodel.nl", shape="rectangle")
|
||||
infra.node("artifactory", "📦 Artifactory", shape="rectangle")
|
||||
infra.node("runner", "⚙️ Build runners\nCI/CD", shape="rectangle")
|
||||
|
||||
# ---- Home Automation ----
|
||||
with lan.subgraph(name="cluster_ha") as ha:
|
||||
ha.attr(
|
||||
label="🏡 Home Automation\nha.lan\n192.168.1.193",
|
||||
style="rounded",
|
||||
color="#ffddaa",
|
||||
)
|
||||
ha.node("hass", "🏠 Home Assistant", shape="rectangle")
|
||||
|
||||
# Overige LAN-hosts / IoT
|
||||
lan.node("atlas", "🧱 atlas.lan\n192.168.1.100", shape="rectangle")
|
||||
lan.node("iot1", "📺 hof-E402NA\n192.168.1.214", shape="rectangle")
|
||||
lan.node("iot2", "🎧 S380HB\n192.168.1.59", shape="rectangle")
|
||||
lan.node("iot3", "📟 ecb5faa56c90\n192.168.1.49", shape="rectangle")
|
||||
lan.node("iot4", "❓ Unknown\n192.168.1.240", shape="rectangle")
|
||||
|
||||
# ---------- Tether subnet ----------
|
||||
with g.subgraph(name="cluster_tether") as tether:
|
||||
tether.attr(
|
||||
label="📶 Tether subnet 192.168.137.0/24",
|
||||
style="rounded",
|
||||
color="lightgrey",
|
||||
fontsize="16",
|
||||
)
|
||||
tether.node("hermes", "🛰️ hermes.lan\n192.168.137.239\nworker / node", shape="rectangle")
|
||||
tether.node("plato", "🛰️ plato.lan\n192.168.137.163\nworker / node", shape="rectangle")
|
||||
|
||||
# ---------- Externe gebruikers (aanvulling) ----------
|
||||
g.node(
|
||||
"users",
|
||||
"👨💻 Developers / Users\nlaptops / mobiel",
|
||||
shape="rectangle",
|
||||
style="dashed",
|
||||
)
|
||||
|
||||
# ==================== VERBINDINGEN ====================
|
||||
|
||||
# Basis LAN connecties (ongeveer jouw '---' links)
|
||||
for target in ["core", "infraDNS", "ha", "atlas", "iot1", "iot2", "iot3", "iot4"]:
|
||||
# We linken naar representatieve node binnen subgraph
|
||||
if target == "core":
|
||||
g.edge("hub", "traefik", dir="both", label="LAN")
|
||||
elif target == "infraDNS":
|
||||
g.edge("hub", "adguard", dir="both", label="LAN")
|
||||
elif target == "ha":
|
||||
g.edge("hub", "hass", dir="both", label="LAN")
|
||||
else:
|
||||
g.edge("hub", target, dir="both", label="LAN")
|
||||
|
||||
# WAN koppeling
|
||||
g.edge("hub", "extDNS", label="📶 Internet")
|
||||
g.edge("adguard", "extDNS", label="Upstream DNS")
|
||||
|
||||
# DNS-resolutie (core / ha / atlas / tether -> AdGuard)
|
||||
for client in ["traefik", "hass", "atlas", "hermes", "plato"]:
|
||||
g.edge(client, "adguard", label="DNS", style="dashed")
|
||||
|
||||
# Websites / reverse proxy
|
||||
g.edge("extDNS", "traefik", label="DNS → Traefik")
|
||||
g.edge("traefik", "gitea", label="HTTP(S)")
|
||||
g.edge("traefik", "auctionFE", label="HTTP(S)")
|
||||
g.edge("traefik", "aupiAPI", label="HTTP(S)")
|
||||
g.edge("traefik", "dokku", label="Apps / Deploy")
|
||||
|
||||
# App flow
|
||||
g.edge("auctionFE", "aupiAPI", label="API calls")
|
||||
g.edge("aupiAPI", "adguard", label="DNS lookups", style="dashed")
|
||||
|
||||
# AI workloads
|
||||
g.edge("traefik", "mi50", label="AI / inference", style="dotted")
|
||||
|
||||
# Tether workers
|
||||
g.edge("traefik", "hermes", dir="both", label="Jobs / tasks")
|
||||
g.edge("traefik", "plato", dir="both", label="Jobs / tasks")
|
||||
|
||||
# Monitoring / logging (aanvulling)
|
||||
for observed in ["traefik", "gitea", "dokku", "auctionFE", "aupiAPI", "mi50", "adguard", "atlas", "hass"]:
|
||||
g.edge(observed, "monitoring", style="dotted", label="metrics / logs")
|
||||
|
||||
# Developers / gebruikers
|
||||
g.edge("users", "traefik", label="HTTPS")
|
||||
g.edge("users", "gitea", style="dashed", label="Git / HTTP")
|
||||
|
||||
# Genereer bestanden (PNG + .gv)
|
||||
g.render(cleanup=True)
|
||||
print(f"Diagram gegenereerd als {output_name}.png")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
make_network_diagram()
|
||||
from graphviz import Digraph
|
||||
|
||||
def make_network_diagram(output_name: str = "network-architecture"):
|
||||
g = Digraph(
|
||||
"network",
|
||||
filename=f"{output_name}.gv",
|
||||
format="png",
|
||||
)
|
||||
|
||||
# Globale stijl
|
||||
g.attr(rankdir="LR", fontname="Segoe UI")
|
||||
|
||||
# ---------- WAN / Internet ----------
|
||||
with g.subgraph(name="cluster_wan") as wan:
|
||||
wan.attr(
|
||||
label="🌐 Internet / Cloud",
|
||||
style="rounded",
|
||||
color="lightgrey",
|
||||
fontsize="16",
|
||||
)
|
||||
wan.node("extDNS", "📡 Public DNS", shape="rectangle")
|
||||
wan.node("extGit", "☁️ Externe registries / Git\n(GitHub / Docker Hub)", shape="rectangle")
|
||||
|
||||
# ---------- LAN 192.168.1.0/24 ----------
|
||||
with g.subgraph(name="cluster_lan") as lan:
|
||||
lan.attr(
|
||||
label="🏠 LAN 192.168.1.0/24",
|
||||
style="rounded",
|
||||
color="lightgrey",
|
||||
fontsize="16",
|
||||
)
|
||||
|
||||
# Router / gateway
|
||||
lan.node(
|
||||
"hub",
|
||||
"🛜 Router / Gateway\nhub.lan\n192.168.1.1",
|
||||
shape="rectangle",
|
||||
style="filled",
|
||||
fillcolor="#f0f0ff",
|
||||
)
|
||||
|
||||
# ---- Core server / desktop ----
|
||||
with lan.subgraph(name="cluster_core") as core:
|
||||
core.attr(
|
||||
label="💻 Hoofdserver / Desktop\nTour / hephaestus / ollama / dokku.lan\n192.168.1.159",
|
||||
style="rounded",
|
||||
color="#aaccee",
|
||||
)
|
||||
core.node("traefik", "🚦 Traefik\nReverse Proxy", shape="rectangle")
|
||||
core.node("gitea", "📚 Gitea\ngit.appmodel.nl", shape="rectangle")
|
||||
core.node("dokku", "🐳 Dokku\nPaaS / build", shape="rectangle")
|
||||
core.node("auctionFE", "🧱 Auction Frontend\nauction.appmodel.nl", shape="rectangle")
|
||||
core.node("aupiAPI", "🧱 Auction Backend API\naupi.appmodel.nl", shape="rectangle")
|
||||
core.node("mi50", "🧠 MI50 / Ollama\nAI workloads", shape="rectangle")
|
||||
|
||||
# Aanvulling: monitoring / logging
|
||||
core.node("monitoring", "📈 Monitoring / Logging\nPrometheus / Loki / Grafana", shape="rectangle")
|
||||
|
||||
# ---- Infra & DNS ----
|
||||
with lan.subgraph(name="cluster_infra_dns") as infra:
|
||||
infra.attr(
|
||||
label="🧭 Infra & DNS\nodroid / dns.lan\n192.168.1.163",
|
||||
style="rounded",
|
||||
color="#aaddaa",
|
||||
)
|
||||
infra.node("adguard", "🧭 AdGuard Home\nDNS / *.lan / *.appmodel.nl", shape="rectangle")
|
||||
infra.node("artifactory", "📦 Artifactory", shape="rectangle")
|
||||
infra.node("runner", "⚙️ Build runners\nCI/CD", shape="rectangle")
|
||||
|
||||
# ---- Home Automation ----
|
||||
with lan.subgraph(name="cluster_ha") as ha:
|
||||
ha.attr(
|
||||
label="🏡 Home Automation\nha.lan\n192.168.1.193",
|
||||
style="rounded",
|
||||
color="#ffddaa",
|
||||
)
|
||||
ha.node("hass", "🏠 Home Assistant", shape="rectangle")
|
||||
|
||||
# Overige LAN-hosts / IoT
|
||||
lan.node("atlas", "🧱 atlas.lan\n192.168.1.100", shape="rectangle")
|
||||
lan.node("iot1", "📺 hof-E402NA\n192.168.1.214", shape="rectangle")
|
||||
lan.node("iot2", "🎧 S380HB\n192.168.1.59", shape="rectangle")
|
||||
lan.node("iot3", "📟 ecb5faa56c90\n192.168.1.49", shape="rectangle")
|
||||
lan.node("iot4", "❓ Unknown\n192.168.1.240", shape="rectangle")
|
||||
|
||||
# ---------- Tether subnet ----------
|
||||
with g.subgraph(name="cluster_tether") as tether:
|
||||
tether.attr(
|
||||
label="📶 Tether subnet 192.168.137.0/24",
|
||||
style="rounded",
|
||||
color="lightgrey",
|
||||
fontsize="16",
|
||||
)
|
||||
tether.node("hermes", "🛰️ hermes.lan\n192.168.137.239\nworker / node", shape="rectangle")
|
||||
tether.node("plato", "🛰️ plato.lan\n192.168.137.163\nworker / node", shape="rectangle")
|
||||
|
||||
# ---------- Externe gebruikers (aanvulling) ----------
|
||||
g.node(
|
||||
"users",
|
||||
"👨💻 Developers / Users\nlaptops / mobiel",
|
||||
shape="rectangle",
|
||||
style="dashed",
|
||||
)
|
||||
|
||||
# ==================== VERBINDINGEN ====================
|
||||
|
||||
# Basis LAN connecties (ongeveer jouw '---' links)
|
||||
for target in ["core", "infraDNS", "ha", "atlas", "iot1", "iot2", "iot3", "iot4"]:
|
||||
# We linken naar representatieve node binnen subgraph
|
||||
if target == "core":
|
||||
g.edge("hub", "traefik", dir="both", label="LAN")
|
||||
elif target == "infraDNS":
|
||||
g.edge("hub", "adguard", dir="both", label="LAN")
|
||||
elif target == "ha":
|
||||
g.edge("hub", "hass", dir="both", label="LAN")
|
||||
else:
|
||||
g.edge("hub", target, dir="both", label="LAN")
|
||||
|
||||
# WAN koppeling
|
||||
g.edge("hub", "extDNS", label="📶 Internet")
|
||||
g.edge("adguard", "extDNS", label="Upstream DNS")
|
||||
|
||||
# DNS-resolutie (core / ha / atlas / tether -> AdGuard)
|
||||
for client in ["traefik", "hass", "atlas", "hermes", "plato"]:
|
||||
g.edge(client, "adguard", label="DNS", style="dashed")
|
||||
|
||||
# Websites / reverse proxy
|
||||
g.edge("extDNS", "traefik", label="DNS → Traefik")
|
||||
g.edge("traefik", "gitea", label="HTTP(S)")
|
||||
g.edge("traefik", "auctionFE", label="HTTP(S)")
|
||||
g.edge("traefik", "aupiAPI", label="HTTP(S)")
|
||||
g.edge("traefik", "dokku", label="Apps / Deploy")
|
||||
|
||||
# App flow
|
||||
g.edge("auctionFE", "aupiAPI", label="API calls")
|
||||
g.edge("aupiAPI", "adguard", label="DNS lookups", style="dashed")
|
||||
|
||||
# AI workloads
|
||||
g.edge("traefik", "mi50", label="AI / inference", style="dotted")
|
||||
|
||||
# Tether workers
|
||||
g.edge("traefik", "hermes", dir="both", label="Jobs / tasks")
|
||||
g.edge("traefik", "plato", dir="both", label="Jobs / tasks")
|
||||
|
||||
# Monitoring / logging (aanvulling)
|
||||
for observed in ["traefik", "gitea", "dokku", "auctionFE", "aupiAPI", "mi50", "adguard", "atlas", "hass"]:
|
||||
g.edge(observed, "monitoring", style="dotted", label="metrics / logs")
|
||||
|
||||
# Developers / gebruikers
|
||||
g.edge("users", "traefik", label="HTTPS")
|
||||
g.edge("users", "gitea", style="dashed", label="Git / HTTP")
|
||||
|
||||
# Genereer bestanden (PNG + .gv)
|
||||
g.render(cleanup=True)
|
||||
print(f"Diagram gegenereerd als {output_name}.png")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
make_network_diagram()
|
||||
|
||||
4651
package-lock.json
generated
Normal file
4651
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
9
package.json
Normal file
9
package.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"name": "viewer",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"maintainers": ["michael@appmodel.nl"],
|
||||
"scripts": {
|
||||
"build": "mkdir -p dist && cp -r public/* dist/"
|
||||
}
|
||||
}
|
||||
214
public/dia.html
214
public/dia.html
@@ -1,107 +1,107 @@
|
||||
<!doctype html>
|
||||
<html lang="nl">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Netwerk architectuur</title>
|
||||
<!-- Mermaid via CDN -->
|
||||
<script type="module">
|
||||
import mermaid from "https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs";
|
||||
mermaid.initialize({ startOnLoad: true, theme: "default" });
|
||||
</script>
|
||||
<style>
|
||||
body { font-family: system-ui, sans-serif; margin: 0; padding: 1rem; }
|
||||
.mermaid { max-width: 100%; overflow: auto; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Netwerk architectuur</h1>
|
||||
|
||||
<div class="mermaid">
|
||||
flowchart LR
|
||||
|
||||
%% ============ Internet ============
|
||||
subgraph WAN[🌐 Internet / Cloud]
|
||||
extDNS[(📡 Public DNS)]
|
||||
extGit[(☁️ Externe registries / Git)]
|
||||
end
|
||||
|
||||
%% ============ LAN 192.168.1.x ============
|
||||
subgraph LAN[🏠 LAN 192.168.1.0/24]
|
||||
hub[🛜 Router / Gateway\nhub.lan\n192.168.1.1]
|
||||
|
||||
subgraph core[💻 Hoofdserver / Desktop\nTour / hephaestus / ollama / dokku.lan\n192.168.1.159]
|
||||
traefik[🚦 Traefik\nReverse Proxy]
|
||||
gitea[📚 Gitea\n git.appmodel.nl]
|
||||
dokku[🐳 Dokku\nPaaS / build]
|
||||
auctionFE[🧱 Auction Frontend\n auction.appmodel.nl]
|
||||
aupiAPI[🧱 Auction Backend API\n aupi.appmodel.nl]
|
||||
mi50[🧠 MI50 / Ollama\nAI workloads]
|
||||
end
|
||||
|
||||
subgraph infraDNS[🧭 Infra & DNS\nodroid / dns.lan\n192.168.1.163]
|
||||
adguard[🧭 AdGuard Home\nDNS / *.lan / *.appmodel.nl]
|
||||
artifactory[📦 Artifactory]
|
||||
runner[⚙️ Build runners]
|
||||
end
|
||||
|
||||
subgraph ha[🏡 Home Automation\nha.lan\n192.168.1.193]
|
||||
hass[🏠 Home Assistant]
|
||||
end
|
||||
|
||||
atlas[🧱 atlas.lan\n192.168.1.100\n]
|
||||
|
||||
iot1[📺 hof-E402NA\n192.168.1.214]
|
||||
iot2[🎧 S380HB\n192.168.1.59]
|
||||
iot3[📟 ecb5faa56c90\n192.168.1.49]
|
||||
iot4[❓ Unknown\n192.168.1.240]
|
||||
end
|
||||
|
||||
%% ============ Tether subnet ============
|
||||
subgraph TETHER[📶 Tether subnet 192.168.137.0/24]
|
||||
hermes[🛰️ hermes.lan\n192.168.137.239\nworker / node]
|
||||
plato[🛰️ plato.lan\n192.168.137.163\nworker / node]
|
||||
end
|
||||
|
||||
%% ============ Verkeer ============
|
||||
|
||||
%% Basis LAN connecties
|
||||
hub --- core
|
||||
hub --- infraDNS
|
||||
hub --- ha
|
||||
hub --- atlas
|
||||
hub --- iot1
|
||||
hub --- iot2
|
||||
hub --- iot3
|
||||
hub --- iot4
|
||||
|
||||
%% WAN koppeling
|
||||
hub --> WAN
|
||||
infraDNS --> WAN
|
||||
|
||||
%% DNS-resolutie
|
||||
core --> adguard
|
||||
ha --> adguard
|
||||
atlas --> adguard
|
||||
TETHER --> adguard
|
||||
|
||||
%% Websites / reverse proxy
|
||||
extDNS --> traefik
|
||||
traefik --> gitea
|
||||
traefik --> auctionFE
|
||||
traefik --> aupiAPI
|
||||
traefik --> dokku
|
||||
|
||||
%% App flow
|
||||
auctionFE --> aupiAPI
|
||||
aupiAPI --> adguard
|
||||
|
||||
%% AI workloads
|
||||
core --> mi50
|
||||
|
||||
%% Tether workers
|
||||
core --- TETHER
|
||||
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
<!doctype html>
|
||||
<html lang="nl">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Netwerk architectuur</title>
|
||||
<!-- Mermaid via CDN -->
|
||||
<script type="module">
|
||||
import mermaid from "https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs";
|
||||
mermaid.initialize({ startOnLoad: true, theme: "default" });
|
||||
</script>
|
||||
<style>
|
||||
body { font-family: system-ui, sans-serif; margin: 0; padding: 1rem; }
|
||||
.mermaid { max-width: 100%; overflow: auto; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Netwerk architectuur</h1>
|
||||
|
||||
<div class="mermaid">
|
||||
flowchart LR
|
||||
|
||||
%% ============ Internet ============
|
||||
subgraph WAN[🌐 Internet / Cloud]
|
||||
extDNS[(📡 Public DNS)]
|
||||
extGit[(☁️ Externe registries / Git)]
|
||||
end
|
||||
|
||||
%% ============ LAN 192.168.1.x ============
|
||||
subgraph LAN[🏠 LAN 192.168.1.0/24]
|
||||
hub[🛜 Router / Gateway\nhub.lan\n192.168.1.1]
|
||||
|
||||
subgraph core[💻 Hoofdserver / Desktop\nTour / hephaestus / ollama / dokku.lan\n192.168.1.159]
|
||||
traefik[🚦 Traefik\nReverse Proxy]
|
||||
gitea[📚 Gitea\n git.appmodel.nl]
|
||||
dokku[🐳 Dokku\nPaaS / build]
|
||||
auctionFE[🧱 Auction Frontend\n auction.appmodel.nl]
|
||||
aupiAPI[🧱 Auction Backend API\n aupi.appmodel.nl]
|
||||
mi50[🧠 MI50 / Ollama\nAI workloads]
|
||||
end
|
||||
|
||||
subgraph infraDNS[🧭 Infra & DNS\nodroid / dns.lan\n192.168.1.163]
|
||||
adguard[🧭 AdGuard Home\nDNS / *.lan / *.appmodel.nl]
|
||||
artifactory[📦 Artifactory]
|
||||
runner[⚙️ Build runners]
|
||||
end
|
||||
|
||||
subgraph ha[🏡 Home Automation\nha.lan\n192.168.1.193]
|
||||
hass[🏠 Home Assistant]
|
||||
end
|
||||
|
||||
atlas[🧱 atlas.lan\n192.168.1.100\n]
|
||||
|
||||
iot1[📺 hof-E402NA\n192.168.1.214]
|
||||
iot2[🎧 S380HB\n192.168.1.59]
|
||||
iot3[📟 ecb5faa56c90\n192.168.1.49]
|
||||
iot4[❓ Unknown\n192.168.1.240]
|
||||
end
|
||||
|
||||
%% ============ Tether subnet ============
|
||||
subgraph TETHER[📶 Tether subnet 192.168.137.0/24]
|
||||
hermes[🛰️ hermes.lan\n192.168.137.239\nworker / node]
|
||||
plato[🛰️ plato.lan\n192.168.137.163\nworker / node]
|
||||
end
|
||||
|
||||
%% ============ Verkeer ============
|
||||
|
||||
%% Basis LAN connecties
|
||||
hub --- core
|
||||
hub --- infraDNS
|
||||
hub --- ha
|
||||
hub --- atlas
|
||||
hub --- iot1
|
||||
hub --- iot2
|
||||
hub --- iot3
|
||||
hub --- iot4
|
||||
|
||||
%% WAN koppeling
|
||||
hub --> WAN
|
||||
infraDNS --> WAN
|
||||
|
||||
%% DNS-resolutie
|
||||
core --> adguard
|
||||
ha --> adguard
|
||||
atlas --> adguard
|
||||
TETHER --> adguard
|
||||
|
||||
%% Websites / reverse proxy
|
||||
extDNS --> traefik
|
||||
traefik --> gitea
|
||||
traefik --> auctionFE
|
||||
traefik --> aupiAPI
|
||||
traefik --> dokku
|
||||
|
||||
%% App flow
|
||||
auctionFE --> aupiAPI
|
||||
aupiAPI --> adguard
|
||||
|
||||
%% AI workloads
|
||||
core --> mi50
|
||||
|
||||
%% Tether workers
|
||||
core --- TETHER
|
||||
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
BIN
public/home.png
Normal file
BIN
public/home.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 148 KiB |
BIN
public/img.png
Normal file
BIN
public/img.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 131 KiB |
@@ -1,264 +1,266 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Home-Lab diagram – live editor</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<style>
|
||||
:root{--bg:#f7f9fc;--text:#222;--accent:#0d6efd}
|
||||
body{margin:0;font-family:system-ui,-apple-system,Segoe UI,Roboto,Helvetica,Arial;font-size:14px;background:var(--bg);color:var(--text)}
|
||||
header{background:var(--accent);color:#fff;padding:.6rem 1rem;font-weight:600;display:flex;gap:.5rem;align-items:center}
|
||||
header button{padding:.2rem .6rem;border:none;border-radius:4px;background:#fff;color:var(--accent);cursor:pointer}
|
||||
.wrap{display:flex;height:calc(100vh - 40px)}
|
||||
.panel{flex:1;display:flex;flex-direction:column;overflow:hidden;border-right:1px solid #ddd;transition:flex .3s ease}
|
||||
.panel:last-child{border:none}
|
||||
h3{margin:.4rem .6rem;font-size:1rem}
|
||||
#src{flex:1;padding:.5rem;overflow:auto;background:#1e1e1e;color:#d4d4d4;font-family:"Consolas","Monaco",monospace;border:none;outline:none;white-space:pre-wrap}
|
||||
#preview{flex:1;padding:.5rem;overflow:auto;background:#fff}
|
||||
/* IMPORTANT: Let the SVG flow naturally and scroll if needed */
|
||||
#preview svg{display:block;margin:0 auto;max-width:100%;height:auto}
|
||||
#error{color:#d32f2f;background:#fff3cd;padding:.5rem;margin:.5rem;border-left:4px solid #d32f2f;display:none}
|
||||
#zoomVal{margin-left:.5rem;font-size:.9rem;color:#fff}
|
||||
/* collapsed state */
|
||||
#srcPanel.collapsed{flex:0}
|
||||
#srcPanel.collapsed #src{display:none}
|
||||
/* Debug: uncomment to see element bounds */
|
||||
/* #preview{outline:2px solid red} #preview svg{outline:2px solid blue} */
|
||||
</style>
|
||||
<script src="https://cdn.jsdelivr.net/npm/mermaid@10.6.1/dist/mermaid.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<button id="toggleBtn" onclick="togglePanel()">🗔 Hide source</button>
|
||||
Home-Lab diagram – live Mermaid editor
|
||||
<button onclick="saveSVG()">💾 SVG</button>
|
||||
<button onclick="savePNG()">💾 PNG</button>
|
||||
<button onclick="reset()">🔄 Reset</button>
|
||||
<label style="margin-left:auto;display:flex;align-items:center;gap:.3rem;color:#fff">
|
||||
Zoom: <input type="range" id="zoom" min="50" max="300" value="100" style="width:100px"><span id="zoomVal">100%</span>
|
||||
</label>
|
||||
</header>
|
||||
|
||||
<div class="wrap">
|
||||
<div class="panel" id="srcPanel">
|
||||
<h3>Source (edit here)</h3>
|
||||
<textarea id="src" spellcheck="false"></textarea>
|
||||
</div>
|
||||
<div class="panel">
|
||||
<h3>Live preview</h3>
|
||||
<div id="preview"></div>
|
||||
<div id="error"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const DEFAULT = `flowchart TD
|
||||
%% ---------- styles ----------
|
||||
classDef internet fill:#e1f5ff,stroke:#007bff
|
||||
classDef router fill:#fff3cd,stroke:#ffc107
|
||||
classDef lan fill:#f8f9ff,stroke:#6c757d,stroke-width:2px
|
||||
classDef core fill:#ffe6e6,stroke:#dc3545
|
||||
classDef infra fill:#e6ffe6,stroke:#28a745
|
||||
classDef worker fill:#f0e6ff,stroke:#6f42c1
|
||||
classDef iot fill:#fff9e6,stroke:#fd7e14
|
||||
|
||||
%% ---------- nodes ----------
|
||||
internet(🌐 Internet / Cloud):::internet
|
||||
router[🛜 Router\nhub.lan\n192.168.1.1]:::router
|
||||
|
||||
subgraph LAN [🏠 LAN 192.168.1.0/24]
|
||||
subgraph CORE [💻 Core server\n192.168.1.159]
|
||||
traefik[🚦 Traefik]:::core
|
||||
gitea[📚 Gitea]:::core
|
||||
dokku[🐳 Dokku]:::core
|
||||
auction[🧱 Auction stack]:::core
|
||||
mi50[🧠 MI50 / Ollama]:::core
|
||||
end
|
||||
|
||||
subgraph INFRA [🧭 Infra & DNS\n192.168.1.163]
|
||||
adguard[🛡️ AdGuard]:::infra
|
||||
artifactory[📦 Artifactory]:::infra
|
||||
end
|
||||
|
||||
ha[🏡 Home Assistant\n192.168.1.193]:::infra
|
||||
atlas[🧱 Atlas\n192.168.1.100]:::worker
|
||||
|
||||
iot1[📺 IoT-1]:::iot
|
||||
iot2[📟 IoT-2]:::iot
|
||||
end
|
||||
|
||||
subgraph TETHER [📶 Tether 192.168.137.0/24]
|
||||
hermes[🛰️ Hermes]:::worker
|
||||
plato[🛰️ Plato]:::worker
|
||||
end
|
||||
|
||||
dev[👨💻 Dev laptop]:::internet
|
||||
|
||||
%% ---------- edges ----------
|
||||
internet ==> router
|
||||
router --> CORE
|
||||
router --> INFRA
|
||||
router --> ha
|
||||
router --> atlas
|
||||
router --> iot1
|
||||
router --> iot2
|
||||
|
||||
dev ==> gitea
|
||||
dev ==> dokku
|
||||
dev ==> mi50
|
||||
|
||||
traefik --> gitea
|
||||
traefik --> auction
|
||||
traefik --> dokku
|
||||
|
||||
CORE -.->|DNS| adguard
|
||||
ha -.->|DNS| adguard
|
||||
atlas-.->|DNS| adguard
|
||||
hermes-.->|DNS| adguard
|
||||
plato-.->|DNS| adguard
|
||||
|
||||
CORE === TETHER
|
||||
`;
|
||||
|
||||
function getStorage(key, fallback) {
|
||||
try { return localStorage.getItem(key) ?? fallback; } catch { return fallback; }
|
||||
}
|
||||
function setStorage(key, val) {
|
||||
try { localStorage.setItem(key, val); } catch {}
|
||||
}
|
||||
|
||||
const srcEl = document.getElementById('src');
|
||||
const errEl = document.getElementById('error');
|
||||
const zoomEl = document.getElementById('zoom');
|
||||
const zoomVal = document.getElementById('zoomVal');
|
||||
const srcPanel = document.getElementById('srcPanel');
|
||||
const toggleBtn = document.getElementById('toggleBtn');
|
||||
|
||||
srcEl.value = getStorage('labDiagram', DEFAULT);
|
||||
if (getStorage('panelCollapsed', 'false') === 'true') {
|
||||
srcPanel.classList.add('collapsed');
|
||||
toggleBtn.textContent = '🗔 Show source';
|
||||
}
|
||||
|
||||
function togglePanel() {
|
||||
srcPanel.classList.toggle('collapsed');
|
||||
const collapsed = srcPanel.classList.contains('collapsed');
|
||||
toggleBtn.textContent = collapsed ? '🗔 Show source' : '🗔 Hide source';
|
||||
setStorage('panelCollapsed', collapsed);
|
||||
}
|
||||
|
||||
mermaid.initialize({startOnLoad: false, theme: 'default'});
|
||||
|
||||
function render() {
|
||||
const src = srcEl.value;
|
||||
setStorage('labDiagram', src);
|
||||
errEl.style.display = 'none';
|
||||
|
||||
const preview = document.getElementById('preview');
|
||||
preview.innerHTML = '';
|
||||
|
||||
const mermaidDiv = document.createElement('div');
|
||||
mermaidDiv.className = 'mermaid';
|
||||
mermaidDiv.textContent = src;
|
||||
preview.appendChild(mermaidDiv);
|
||||
|
||||
// Render and fix sizing
|
||||
mermaid.init(undefined, mermaidDiv).then(() => {
|
||||
const svg = preview.querySelector('svg');
|
||||
if (svg) {
|
||||
// CRITICAL: Ensure SVG has viewBox for proper sizing
|
||||
if (!svg.hasAttribute('viewBox')) {
|
||||
const {width, height} = svg.getBBox();
|
||||
svg.setAttribute('viewBox', `0 0 ${width} ${height}`);
|
||||
}
|
||||
svg.style.maxWidth = '100%';
|
||||
svg.style.height = 'auto';
|
||||
applyZoom();
|
||||
}
|
||||
}).catch(e => {
|
||||
errEl.textContent = 'Mermaid error: ' + e.message;
|
||||
errEl.style.display = 'block';
|
||||
});
|
||||
}
|
||||
|
||||
srcEl.addEventListener('input', () => {
|
||||
clearTimeout(srcEl._t);
|
||||
srcEl._t = setTimeout(render, 300);
|
||||
});
|
||||
|
||||
zoomEl.addEventListener('input', () => {
|
||||
zoomVal.textContent = zoomEl.value + '%';
|
||||
applyZoom();
|
||||
});
|
||||
|
||||
function applyZoom() {
|
||||
const svg = document.querySelector('#preview svg');
|
||||
if (svg) {
|
||||
svg.style.transform = `scale(${zoomEl.value / 100})`;
|
||||
}
|
||||
}
|
||||
|
||||
function reset() {
|
||||
if (confirm('Reset to default diagram?')) {
|
||||
srcEl.value = DEFAULT;
|
||||
zoomEl.value = 100;
|
||||
zoomVal.textContent = '100%';
|
||||
setStorage('labDiagram', DEFAULT);
|
||||
render();
|
||||
}
|
||||
}
|
||||
|
||||
function saveSVG() {
|
||||
const svg = document.querySelector('#preview svg');
|
||||
if (!svg) return alert('Nothing to save yet.');
|
||||
|
||||
const clone = svg.cloneNode(true);
|
||||
clone.removeAttribute('style');
|
||||
clone.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
|
||||
|
||||
const blob = new Blob([clone.outerHTML], {type: 'image/svg+xml'});
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = 'home-lab.svg';
|
||||
a.click();
|
||||
setTimeout(() => URL.revokeObjectURL(url), 100);
|
||||
}
|
||||
|
||||
function savePNG() {
|
||||
const svg = document.querySelector('#preview svg');
|
||||
if (!svg) return alert('Nothing to save yet.');
|
||||
|
||||
const {width, height} = svg.getBBox();
|
||||
const canvasW = Math.max(width, 1200);
|
||||
const canvasH = Math.max(height, 800);
|
||||
|
||||
const svgData = new XMLSerializer().serializeToString(svg);
|
||||
const img = new Image();
|
||||
|
||||
img.onload = () => {
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = canvasW;
|
||||
canvas.height = canvasH;
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
ctx.fillStyle = 'white';
|
||||
ctx.fillRect(0, 0, canvasW, canvasH);
|
||||
ctx.drawImage(img, 0, 0, canvasW, canvasH);
|
||||
|
||||
canvas.toBlob(blob => {
|
||||
const a = document.createElement('a');
|
||||
a.href = URL.createObjectURL(blob);
|
||||
a.download = 'home-lab.png';
|
||||
a.click();
|
||||
});
|
||||
};
|
||||
|
||||
img.onerror = () => alert('PNG conversion failed. Try SVG instead.');
|
||||
img.src = 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(svgData);
|
||||
}
|
||||
|
||||
render();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
<!-- test321 -->
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Home-Lab diagram – live editor</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<style>
|
||||
:root{--bg:#f7f9fc;--text:#222;--accent:#0d6efd}
|
||||
body{margin:0;font-family:system-ui,-apple-system,Segoe UI,Roboto,Helvetica,Arial;font-size:14px;background:var(--bg);color:var(--text)}
|
||||
header{background:var(--accent);color:#fff;padding:.6rem 1rem;font-weight:600;display:flex;gap:.5rem;align-items:center}
|
||||
header button{padding:.2rem .6rem;border:none;border-radius:4px;background:#fff;color:var(--accent);cursor:pointer}
|
||||
.wrap{display:flex;height:calc(100vh - 40px)}
|
||||
.panel{flex:1;display:flex;flex-direction:column;overflow:hidden;border-right:1px solid #ddd;transition:flex .3s ease}
|
||||
.panel:last-child{border:none}
|
||||
h3{margin:.4rem .6rem;font-size:1rem}
|
||||
#src{flex:1;padding:.5rem;overflow:auto;background:#1e1e1e;color:#d4d4d4;font-family:"Consolas","Monaco",monospace;border:none;outline:none;white-space:pre-wrap}
|
||||
#preview{flex:1;padding:.5rem;overflow:auto;background:#fff}
|
||||
/* IMPORTANT: Let the SVG flow naturally and scroll if needed */
|
||||
#preview svg{display:block;margin:0 auto;max-width:100%;height:auto}
|
||||
#error{color:#d32f2f;background:#fff3cd;padding:.5rem;margin:.5rem;border-left:4px solid #d32f2f;display:none}
|
||||
#zoomVal{margin-left:.5rem;font-size:.9rem;color:#fff}
|
||||
/* collapsed state */
|
||||
#srcPanel.collapsed{flex:0}
|
||||
#srcPanel.collapsed #src{display:none}
|
||||
/* Debug: uncomment to see element bounds */
|
||||
/* #preview{outline:2px solid red} #preview svg{outline:2px solid blue} */
|
||||
</style>
|
||||
<script src="https://cdn.jsdelivr.net/npm/mermaid@10.6.1/dist/mermaid.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<button id="toggleBtn" onclick="togglePanel()">🗔 Hide source</button>
|
||||
Home-Lab diagram – live Mermaid editor
|
||||
<button onclick="saveSVG()">💾 SVG</button>
|
||||
<button onclick="savePNG()">💾 PNG</button>
|
||||
<button onclick="reset()">🔄 Reset</button>
|
||||
<label style="margin-left:auto;display:flex;align-items:center;gap:.3rem;color:#fff">
|
||||
Zoom: <input type="range" id="zoom" min="50" max="300" value="100" style="width:100px"><span id="zoomVal">100%</span>
|
||||
</label>
|
||||
</header>
|
||||
|
||||
<div class="wrap">
|
||||
<div class="panel" id="srcPanel">
|
||||
<h3>Source (edit here)</h3>
|
||||
<textarea id="src" spellcheck="false"></textarea>
|
||||
</div>
|
||||
<div class="panel">
|
||||
<h3>Live preview</h3>
|
||||
<div id="preview"></div>
|
||||
<div id="error"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const DEFAULT = `flowchart TD
|
||||
%% ---------- styles ----------
|
||||
classDef internet fill:#e1f5ff,stroke:#007bff
|
||||
classDef router fill:#fff3cd,stroke:#ffc107
|
||||
classDef lan fill:#f8f9ff,stroke:#6c757d,stroke-width:2px
|
||||
classDef core fill:#ffe6e6,stroke:#dc3545
|
||||
classDef infra fill:#e6ffe6,stroke:#28a745
|
||||
classDef worker fill:#f0e6ff,stroke:#6f42c1
|
||||
classDef iot fill:#fff9e6,stroke:#fd7e14
|
||||
|
||||
%% ---------- nodes ----------
|
||||
internet(🌐 Internet / Cloud):::internet
|
||||
router[🛜 Router\nhub.lan\n192.168.1.1]:::router
|
||||
|
||||
subgraph LAN [🏠 LAN 192.168.1.0/24]
|
||||
subgraph CORE [💻 Core server\n192.168.1.159]
|
||||
traefik[🚦 Traefik]:::core
|
||||
gitea[📚 Gitea]:::core
|
||||
dokku[🐳 Dokku]:::core
|
||||
auction[🧱 Auction stack]:::core
|
||||
mi50[🧠 MI50 / Ollama]:::core
|
||||
end
|
||||
|
||||
subgraph INFRA [🧭 Infra & DNS\n192.168.1.163]
|
||||
adguard[🛡️ AdGuard]:::infra
|
||||
artifactory[📦 Artifactory]:::infra
|
||||
end
|
||||
|
||||
ha[🏡 Home Assistant\n192.168.1.193]:::infra
|
||||
atlas[🧱 Atlas\n192.168.1.100]:::worker
|
||||
|
||||
iot1[📺 IoT-1]:::iot
|
||||
iot2[📟 IoT-2]:::iot
|
||||
end
|
||||
|
||||
subgraph TETHER [📶 Tether 192.168.137.0/24]
|
||||
hermes[🛰️ Hermes]:::worker
|
||||
plato[🛰️ Plato]:::worker
|
||||
end
|
||||
|
||||
dev[👨💻 Dev laptop]:::internet
|
||||
|
||||
%% ---------- edges ----------
|
||||
internet ==> router
|
||||
router --> CORE
|
||||
router --> INFRA
|
||||
router --> ha
|
||||
router --> atlas
|
||||
router --> iot1
|
||||
router --> iot2
|
||||
|
||||
dev ==> gitea
|
||||
dev ==> dokku
|
||||
dev ==> mi50
|
||||
|
||||
traefik --> gitea
|
||||
traefik --> auction
|
||||
traefik --> dokku
|
||||
|
||||
CORE -.->|DNS| adguard
|
||||
ha -.->|DNS| adguard
|
||||
atlas-.->|DNS| adguard
|
||||
hermes-.->|DNS| adguard
|
||||
plato-.->|DNS| adguard
|
||||
|
||||
CORE === TETHER
|
||||
`;
|
||||
|
||||
function getStorage(key, fallback) {
|
||||
try { return localStorage.getItem(key) ?? fallback; } catch { return fallback; }
|
||||
}
|
||||
function setStorage(key, val) {
|
||||
try { localStorage.setItem(key, val); } catch {}
|
||||
}
|
||||
|
||||
const srcEl = document.getElementById('src');
|
||||
const errEl = document.getElementById('error');
|
||||
const zoomEl = document.getElementById('zoom');
|
||||
const zoomVal = document.getElementById('zoomVal');
|
||||
const srcPanel = document.getElementById('srcPanel');
|
||||
const toggleBtn = document.getElementById('toggleBtn');
|
||||
|
||||
srcEl.value = getStorage('labDiagram', DEFAULT);
|
||||
if (getStorage('panelCollapsed', 'false') === 'true') {
|
||||
srcPanel.classList.add('collapsed');
|
||||
toggleBtn.textContent = '🗔 Show source';
|
||||
}
|
||||
|
||||
function togglePanel() {
|
||||
srcPanel.classList.toggle('collapsed');
|
||||
const collapsed = srcPanel.classList.contains('collapsed');
|
||||
toggleBtn.textContent = collapsed ? '🗔 Show source' : '🗔 Hide source';
|
||||
setStorage('panelCollapsed', collapsed);
|
||||
}
|
||||
|
||||
mermaid.initialize({startOnLoad: false, theme: 'default'});
|
||||
|
||||
function render() {
|
||||
const src = srcEl.value;
|
||||
setStorage('labDiagram', src);
|
||||
errEl.style.display = 'none';
|
||||
|
||||
const preview = document.getElementById('preview');
|
||||
preview.innerHTML = '';
|
||||
|
||||
const mermaidDiv = document.createElement('div');
|
||||
mermaidDiv.className = 'mermaid';
|
||||
mermaidDiv.textContent = src;
|
||||
preview.appendChild(mermaidDiv);
|
||||
|
||||
// Render and fix sizing
|
||||
mermaid.init(undefined, mermaidDiv).then(() => {
|
||||
const svg = preview.querySelector('svg');
|
||||
if (svg) {
|
||||
// CRITICAL: Ensure SVG has viewBox for proper sizing
|
||||
if (!svg.hasAttribute('viewBox')) {
|
||||
const {width, height} = svg.getBBox();
|
||||
svg.setAttribute('viewBox', `0 0 ${width} ${height}`);
|
||||
}
|
||||
svg.style.maxWidth = '100%';
|
||||
svg.style.height = 'auto';
|
||||
applyZoom();
|
||||
}
|
||||
}).catch(e => {
|
||||
errEl.textContent = 'Mermaid error: ' + e.message;
|
||||
errEl.style.display = 'block';
|
||||
});
|
||||
}
|
||||
|
||||
srcEl.addEventListener('input', () => {
|
||||
clearTimeout(srcEl._t);
|
||||
srcEl._t = setTimeout(render, 300);
|
||||
});
|
||||
|
||||
zoomEl.addEventListener('input', () => {
|
||||
zoomVal.textContent = zoomEl.value + '%';
|
||||
applyZoom();
|
||||
});
|
||||
|
||||
function applyZoom() {
|
||||
const svg = document.querySelector('#preview svg');
|
||||
if (svg) {
|
||||
svg.style.transform = `scale(${zoomEl.value / 100})`;
|
||||
}
|
||||
}
|
||||
|
||||
function reset() {
|
||||
if (confirm('Reset to default diagram?')) {
|
||||
srcEl.value = DEFAULT;
|
||||
zoomEl.value = 100;
|
||||
zoomVal.textContent = '100%';
|
||||
setStorage('labDiagram', DEFAULT);
|
||||
render();
|
||||
}
|
||||
}
|
||||
|
||||
function saveSVG() {
|
||||
const svg = document.querySelector('#preview svg');
|
||||
if (!svg) return alert('Nothing to save yet.');
|
||||
|
||||
const clone = svg.cloneNode(true);
|
||||
clone.removeAttribute('style');
|
||||
clone.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
|
||||
|
||||
const blob = new Blob([clone.outerHTML], {type: 'image/svg+xml'});
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = 'home-lab.svg';
|
||||
a.click();
|
||||
setTimeout(() => URL.revokeObjectURL(url), 100);
|
||||
}
|
||||
|
||||
function savePNG() {
|
||||
const svg = document.querySelector('#preview svg');
|
||||
if (!svg) return alert('Nothing to save yet.');
|
||||
|
||||
const {width, height} = svg.getBBox();
|
||||
const canvasW = Math.max(width, 1200);
|
||||
const canvasH = Math.max(height, 800);
|
||||
|
||||
const svgData = new XMLSerializer().serializeToString(svg);
|
||||
const img = new Image();
|
||||
|
||||
img.onload = () => {
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = canvasW;
|
||||
canvas.height = canvasH;
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
ctx.fillStyle = 'white';
|
||||
ctx.fillRect(0, 0, canvasW, canvasH);
|
||||
ctx.drawImage(img, 0, 0, canvasW, canvasH);
|
||||
|
||||
canvas.toBlob(blob => {
|
||||
const a = document.createElement('a');
|
||||
a.href = URL.createObjectURL(blob);
|
||||
a.download = 'home-lab.png';
|
||||
a.click();
|
||||
});
|
||||
};
|
||||
|
||||
img.onerror = () => alert('PNG conversion failed. Try SVG instead.');
|
||||
img.src = 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(svgData);
|
||||
}
|
||||
|
||||
render();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
<!-- Auto-deployed at $(date122`) -->
|
||||
|
||||
BIN
public/network-architecture.gv.png
Normal file
BIN
public/network-architecture.gv.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 284 KiB |
@@ -1,2 +1,2 @@
|
||||
diagrams==0.25.1
|
||||
graphviz==0.20.3
|
||||
diagrams==0.25.1
|
||||
graphviz==0.20.3
|
||||
|
||||
40
test.md
40
test.md
@@ -1,40 +0,0 @@
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ 🌐 Internet / Cloud │
|
||||
└────────────────┬────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ 🛜 Router (hub.lan) - 192.168.1.1 │
|
||||
│ Gateway & DHCP │
|
||||
└────────────────┬────────────────────────────────────────────────────┘
|
||||
│
|
||||
┌────────────┴────────────┬──────────────┬──────────────┐
|
||||
▼ ▼ ▼ ▼
|
||||
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
|
||||
│ LAN │ │ LAN │ │ LAN │ │ LAN │
|
||||
│ 192.168.│ │ Infra & │ │ Home │ │ IoT & │
|
||||
│ 1.x │ │ DNS │ │ Assist │ │ Workers │
|
||||
│ │ │ │ │ │ │ │
|
||||
│ │ │ │ │ │ │ │
|
||||
│ ┌────┐ │ │ ┌────┐ │ │ ┌────┐ │ │ ┌────┐ │
|
||||
│ │Core│ │ │ │DNS │ │ │ │ HA │ │ │ │Atlas│ │
|
||||
│ │ Srv│ │ │ │Host│ │ │ │Host│ │ │ │ │ │
|
||||
│ └┬───┘ │ │ └┬───┘ │ │ └┬───┘ │ │ └┬───┘ │
|
||||
│ │ │ │ │ │ │ │ │ │ │ │
|
||||
│ ┌─▼──┐ │ │ ┌─▼──┐ │ │ ┌─▼──┐ │ │ │ │
|
||||
│ │Dock│ │ │ │AdGr│ │ │ │Hass│ │ │ │ │
|
||||
│ │er │ │ │ │d │ │ │ │ │ │ │ │ │
|
||||
│ └────┘ │ │ └────┘ │ │ └────┘ │ │ │ │
|
||||
│ │ │ │ │ │ │ │
|
||||
└──────────┘ └──────────┘ └──────────┘ └──────────┘
|
||||
│
|
||||
│ ┌─────────────────────────────────────────────────────┐
|
||||
└──►│ Tether Subnet 192.168.137.x │
|
||||
│ ┌──────────┐ ┌──────────┐ │
|
||||
│ │ Hermes │ │ Plato │ │
|
||||
│ │ Worker │ │ Worker │ │
|
||||
│ └──────────┘ └──────────┘ │
|
||||
└─────────────────────────────────────────────────────┘
|
||||
|
||||
Developer Laptops
|
||||
(Windows/WSL)
|
||||
@@ -1,192 +1,192 @@
|
||||
# Deployment Guide - App Pipeline
|
||||
|
||||
Complete guide for deploying applications using the custom deployment pipeline with Gitea, Docker, and Traefik.
|
||||
|
||||
## 1. Create New Application
|
||||
|
||||
### 1.1 Gitea Repository
|
||||
|
||||
1. Log in to Gitea: https://git.appmodel.nl
|
||||
2. Create a new repository:
|
||||
- **Owner**: Tour
|
||||
- **Name**: `viewer` (or your app name)
|
||||
|
||||
### 1.2 Generate Skeleton on Server
|
||||
|
||||
On the server, run:
|
||||
|
||||
```bash
|
||||
apps-create viewer static-fe
|
||||
```
|
||||
|
||||
This command:
|
||||
- Sets up `/opt/apps/viewer` (attempts to clone from `git@git.appmodel.nl:Tour/viewer.git`)
|
||||
- Generates a multi-stage Dockerfile for a Node-based static frontend
|
||||
- Creates `~/infra/viewer/docker-compose.yml` with:
|
||||
- Service `viewer`
|
||||
- Connection to `traefik_net`
|
||||
- Traefik route: `https://viewer.appmodel.nl`
|
||||
|
||||
## 2. Development & Build
|
||||
|
||||
On your development machine:
|
||||
|
||||
```bash
|
||||
# Clone the repository
|
||||
git clone git@git.appmodel.nl:Tour/viewer.git
|
||||
cd viewer
|
||||
|
||||
# Build your app as usual
|
||||
npm install
|
||||
npm run build # output goes to dist/
|
||||
|
||||
# Commit and push
|
||||
git add .
|
||||
git commit -m "First version"
|
||||
git push
|
||||
```
|
||||
|
||||
**Note**: The Dockerfile expects a `dist/` directory containing static files.
|
||||
|
||||
## 3. Deployment Pipeline
|
||||
|
||||
### 3.1 Manual Deploy with `app-deploy`
|
||||
|
||||
On the server, use the generic deploy command:
|
||||
|
||||
```bash
|
||||
app-deploy viewer
|
||||
```
|
||||
|
||||
This command performs:
|
||||
1. `cd /opt/apps/viewer`
|
||||
2. `git pull --ff-only`
|
||||
3. `cd /home/tour/infra/viewer`
|
||||
4. `docker compose up -d --build viewer`
|
||||
|
||||
Logs are saved to: `/var/log/app-deploy-viewer.log`
|
||||
|
||||
### 3.2 Automatic Deployment via Gitea Hook
|
||||
|
||||
Set up automatic deployment on every push:
|
||||
|
||||
1. In Gitea, go to repository `Tour/viewer`
|
||||
2. Navigate to **Settings → Git Hooks**
|
||||
3. Select **post-receive**
|
||||
4. Add the following script:
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
/usr/local/bin/app-deploy viewer
|
||||
```
|
||||
|
||||
**Result**: Every `git push` to `Tour/viewer` automatically triggers a deployment.
|
||||
|
||||
## 4. Traefik & DNS Configuration
|
||||
|
||||
### Traefik Setup
|
||||
|
||||
Traefik runs in the traefik stack on the same server and manages:
|
||||
- `git.appmodel.nl`
|
||||
- `auction.appmodel.nl`
|
||||
- `aupi.appmodel.nl`
|
||||
- ... (new apps via labels)
|
||||
|
||||
### Auto-Generated Labels
|
||||
|
||||
The `apps-create` command automatically adds the correct Traefik labels:
|
||||
|
||||
```yaml
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.viewer.rule=Host(`viewer.appmodel.nl`)"
|
||||
- "traefik.http.routers.viewer.entrypoints=websecure"
|
||||
- "traefik.http.routers.viewer.tls=true"
|
||||
- "traefik.http.routers.viewer.tls.certresolver=letsencrypt"
|
||||
- "traefik.http.services.viewer.loadbalancer.server.port=80"
|
||||
```
|
||||
|
||||
### DNS Configuration
|
||||
|
||||
Add a DNS record pointing to your server's public IP:
|
||||
|
||||
```
|
||||
viewer.appmodel.nl → <server-public-ip>
|
||||
```
|
||||
|
||||
Traefik + Let's Encrypt will automatically handle SSL certificate generation.
|
||||
|
||||
## 5. Future Application Types
|
||||
|
||||
The `apps-create` script currently supports:
|
||||
|
||||
- **`static-fe`** – Node build → Nginx → static frontend
|
||||
|
||||
### Planned Types
|
||||
|
||||
Future app types can be added:
|
||||
|
||||
```bash
|
||||
# Python API
|
||||
apps-create stats api-py
|
||||
|
||||
# Background worker/crawler
|
||||
apps-create crawler worker-py
|
||||
```
|
||||
|
||||
Each type will have:
|
||||
- Custom Dockerfile template
|
||||
- Standard `docker-compose.yml` configuration
|
||||
- Same deployment pipeline: `app-deploy <name>`
|
||||
|
||||
## Quick Reference
|
||||
|
||||
### Create and Deploy New App
|
||||
|
||||
```bash
|
||||
# On server: create skeleton
|
||||
apps-create viewer static-fe
|
||||
|
||||
# On dev machine: develop and push
|
||||
git clone git@git.appmodel.nl:Tour/viewer.git
|
||||
cd viewer
|
||||
npm install
|
||||
npm run build
|
||||
git add .
|
||||
git commit -m "Initial commit"
|
||||
git push
|
||||
|
||||
# On server: deploy (or auto-deploys via hook)
|
||||
app-deploy viewer
|
||||
```
|
||||
|
||||
### Existing Infrastructure
|
||||
|
||||
| Service | URL | Description |
|
||||
|---------|-----|-------------|
|
||||
| Gitea | https://git.appmodel.nl | Git repository hosting |
|
||||
| Auction | https://auction.appmodel.nl | Auction frontend |
|
||||
| Aupi API | https://aupi.appmodel.nl | Auction backend API |
|
||||
| Traefik | - | Reverse proxy & SSL |
|
||||
|
||||
### Log Locations
|
||||
|
||||
- Deployment logs: `/var/log/app-deploy-<app-name>.log`
|
||||
- Docker logs: `docker compose logs -f <service-name>`
|
||||
|
||||
### Useful Commands
|
||||
|
||||
```bash
|
||||
# View deployment logs
|
||||
tail -f /var/log/app-deploy-viewer.log
|
||||
|
||||
# View container logs
|
||||
cd ~/infra/viewer
|
||||
docker compose logs -f viewer
|
||||
|
||||
# Restart service
|
||||
docker compose restart viewer
|
||||
|
||||
# Rebuild without cache
|
||||
docker compose up -d --build --no-cache viewer
|
||||
```
|
||||
# Deployment Guide - App Pipeline
|
||||
|
||||
Complete guide for deploying applications using the custom deployment pipeline with Gitea, Docker, and Traefik.
|
||||
|
||||
## 1. Create New Application
|
||||
|
||||
### 1.1 Gitea Repository
|
||||
|
||||
1. Log in to Gitea: https://git.appmodel.nl
|
||||
2. Create a new repository:
|
||||
- **Owner**: Tour
|
||||
- **Name**: `viewer` (or your app name)
|
||||
|
||||
### 1.2 Generate Skeleton on Server
|
||||
|
||||
On the server, run:
|
||||
|
||||
```bash
|
||||
apps-create viewer static-fe
|
||||
```
|
||||
|
||||
This command:
|
||||
- Sets up `/opt/apps/viewer` (attempts to clone from `git@git.appmodel.nl:Tour/viewer.git`)
|
||||
- Generates a multi-stage Dockerfile for a Node-based static frontend
|
||||
- Creates `~/infra/viewer/docker-compose.yml` with:
|
||||
- Service `viewer`
|
||||
- Connection to `traefik_net`
|
||||
- Traefik route: `https://viewer.appmodel.nl`
|
||||
|
||||
## 2. Development & Build
|
||||
|
||||
On your development machine:
|
||||
|
||||
```bash
|
||||
# Clone the repository
|
||||
git clone git@git.appmodel.nl:Tour/viewer.git
|
||||
cd viewer
|
||||
|
||||
# Build your app as usual
|
||||
npm install
|
||||
npm run build # output goes to dist/
|
||||
|
||||
# Commit and push
|
||||
git add .
|
||||
git commit -m "First version"
|
||||
git push
|
||||
```
|
||||
|
||||
**Note**: The Dockerfile expects a `dist/` directory containing static files.
|
||||
|
||||
## 3. Deployment Pipeline
|
||||
|
||||
### 3.1 Manual Deploy with `app-deploy`
|
||||
|
||||
On the server, use the generic deploy command:
|
||||
|
||||
```bash
|
||||
app-deploy viewer
|
||||
```
|
||||
|
||||
This command performs:
|
||||
1. `cd /opt/apps/viewer`
|
||||
2. `git pull --ff-only`
|
||||
3. `cd /home/tour/infra/viewer`
|
||||
4. `docker compose up -d --build viewer`
|
||||
|
||||
Logs are saved to: `/var/log/app-deploy-viewer.log`
|
||||
|
||||
### 3.2 Automatic Deployment via Gitea Hook
|
||||
|
||||
Set up automatic deployment on every push:
|
||||
|
||||
1. In Gitea, go to repository `Tour/viewer`
|
||||
2. Navigate to **Settings → Git Hooks**
|
||||
3. Select **post-receive**
|
||||
4. Add the following script:
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
/usr/local/bin/app-deploy viewer
|
||||
```
|
||||
|
||||
**Result**: Every `git push` to `Tour/viewer` automatically triggers a deployment.
|
||||
|
||||
## 4. Traefik & DNS Configuration
|
||||
|
||||
### Traefik Setup
|
||||
|
||||
Traefik runs in the traefik stack on the same server and manages:
|
||||
- `git.appmodel.nl`
|
||||
- `auction.appmodel.nl`
|
||||
- `aupi.appmodel.nl`
|
||||
- ... (new apps via labels)
|
||||
|
||||
### Auto-Generated Labels
|
||||
|
||||
The `apps-create` command automatically adds the correct Traefik labels:
|
||||
|
||||
```yaml
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.viewer.rule=Host(`viewer.appmodel.nl`)"
|
||||
- "traefik.http.routers.viewer.entrypoints=websecure"
|
||||
- "traefik.http.routers.viewer.tls=true"
|
||||
- "traefik.http.routers.viewer.tls.certresolver=letsencrypt"
|
||||
- "traefik.http.services.viewer.loadbalancer.server.port=80"
|
||||
```
|
||||
|
||||
### DNS Configuration
|
||||
|
||||
Add a DNS record pointing to your server's public IP:
|
||||
|
||||
```
|
||||
viewer.appmodel.nl → <server-public-ip>
|
||||
```
|
||||
|
||||
Traefik + Let's Encrypt will automatically handle SSL certificate generation.
|
||||
|
||||
## 5. Future Application Types
|
||||
|
||||
The `apps-create` script currently supports:
|
||||
|
||||
- **`static-fe`** – Node build → Nginx → static frontend
|
||||
|
||||
### Planned Types
|
||||
|
||||
Future app types can be added:
|
||||
|
||||
```bash
|
||||
# Python API
|
||||
apps-create stats api-py
|
||||
|
||||
# Background worker/crawler
|
||||
apps-create crawler worker-py
|
||||
```
|
||||
|
||||
Each type will have:
|
||||
- Custom Dockerfile template
|
||||
- Standard `docker-compose.yml` configuration
|
||||
- Same deployment pipeline: `app-deploy <name>`
|
||||
|
||||
## Quick Reference
|
||||
|
||||
### Create and Deploy New App
|
||||
|
||||
```bash
|
||||
# On server: create skeleton
|
||||
apps-create viewer static-fe
|
||||
|
||||
# On dev machine: develop and push
|
||||
git clone git@git.appmodel.nl:Tour/viewer.git
|
||||
cd viewer
|
||||
npm install
|
||||
npm run build
|
||||
git add .
|
||||
git commit -m "Initial commit"
|
||||
git push
|
||||
|
||||
# On server: deploy (or auto-deploys via hook)
|
||||
app-deploy viewer
|
||||
```
|
||||
|
||||
### Existing Infrastructure
|
||||
|
||||
| Service | URL | Description |
|
||||
|---------|-----|-------------|
|
||||
| Gitea | https://git.appmodel.nl | Git repository hosting |
|
||||
| Auction | https://auction.appmodel.nl | Auction frontend |
|
||||
| Aupi API | https://aupi.appmodel.nl | Auction backend API |
|
||||
| Traefik | - | Reverse proxy & SSL |
|
||||
|
||||
### Log Locations
|
||||
|
||||
- Deployment logs: `/var/log/app-deploy-<app-name>.log`
|
||||
- Docker logs: `docker compose logs -f <service-name>`
|
||||
|
||||
### Useful Commands
|
||||
|
||||
```bash
|
||||
# View deployment logs
|
||||
tail -f /var/log/app-deploy-viewer.log
|
||||
|
||||
# View container logs
|
||||
cd ~/infra/viewer
|
||||
docker compose logs -f viewer
|
||||
|
||||
# Restart service
|
||||
docker compose restart viewer
|
||||
|
||||
# Rebuild without cache
|
||||
docker compose up -d --build --no-cache viewer
|
||||
```
|
||||
File diff suppressed because it is too large
Load Diff
756
wiki/Home.md
756
wiki/Home.md
@@ -1,378 +1,378 @@
|
||||
# Diagram Viewer Wiki
|
||||
|
||||
Welcome to the **Diagram Viewer** project documentation.
|
||||
|
||||
This project provides a static web interface for viewing and managing network architecture diagrams generated with Python's `diagrams` library.
|
||||
|
||||
## 📚 Contents
|
||||
|
||||
- [Getting Started](#getting-started)
|
||||
- [Development Guide](#development-guide)
|
||||
- [Deployment Guide](#deployment-guide)
|
||||
- [Architecture Overview](#architecture-overview)
|
||||
|
||||
---
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Python 3.8+
|
||||
- Graphviz
|
||||
- Docker (for deployment)
|
||||
|
||||
### Quick Start
|
||||
|
||||
```bash
|
||||
# Clone repository
|
||||
git clone git@git.appmodel.nl:Tour/diagram.git
|
||||
cd diagram
|
||||
|
||||
# Create virtual environment
|
||||
python -m venv .venv
|
||||
source .venv/bin/activate # Windows: .venv\Scripts\activate
|
||||
|
||||
# Install dependencies
|
||||
pip install -r requirements.txt
|
||||
|
||||
# Generate diagrams
|
||||
python lan_architecture.py
|
||||
python main.py
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Development Guide
|
||||
|
||||
### Project Structure
|
||||
|
||||
```
|
||||
diagram/
|
||||
├── Dockerfile # Multi-stage build for production
|
||||
├── docker-compose.yml # Docker Compose configuration
|
||||
├── requirements.txt # Python dependencies
|
||||
├── lan_architecture.py # LAN architecture diagram generator
|
||||
├── main.py # Main diagram generator
|
||||
├── public/
|
||||
│ └── index.html # Live Mermaid diagram editor
|
||||
└── wiki/ # Documentation (this wiki)
|
||||
```
|
||||
|
||||
### Adding New Diagrams
|
||||
|
||||
1. Create a new Python file (e.g., `network_diagram.py`)
|
||||
2. Import required components from `diagrams` library
|
||||
3. Define your architecture using context managers
|
||||
4. Run the script to generate PNG output
|
||||
|
||||
Example:
|
||||
```python
|
||||
from diagrams import Diagram, Cluster
|
||||
from diagrams.onprem.network import Router
|
||||
from diagrams.generic.device import Mobile
|
||||
|
||||
with Diagram("My Network", show=False, filename="my_network"):
|
||||
router = Router("Gateway")
|
||||
device = Mobile("Phone")
|
||||
device >> router
|
||||
```
|
||||
|
||||
### Testing Locally
|
||||
|
||||
```bash
|
||||
# Generate diagrams
|
||||
python lan_architecture.py
|
||||
|
||||
# View output
|
||||
ls -la *.png
|
||||
|
||||
# Test with local web server
|
||||
python -m http.server 8000 --directory public/
|
||||
# Open: http://localhost:8000
|
||||
```
|
||||
|
||||
### Available Icons
|
||||
|
||||
The `diagrams` library provides icons from multiple providers:
|
||||
- **OnPrem**: Network, compute, storage, etc.
|
||||
- **Cloud**: AWS, Azure, GCP services
|
||||
- **Generic**: Network devices, blank nodes
|
||||
- **Custom**: Use your own images
|
||||
|
||||
See: [Diagrams Documentation](https://diagrams.mingrammer.com/docs/nodes/overview)
|
||||
|
||||
---
|
||||
|
||||
## Deployment Guide
|
||||
|
||||
### Overview
|
||||
|
||||
The project uses a fully automated Docker-based deployment pipeline:
|
||||
|
||||
1. **Develop** on local machine
|
||||
2. **Commit & Push** to Gitea
|
||||
3. **Auto-Deploy** via post-receive hook
|
||||
4. **Build** multi-stage Docker image
|
||||
5. **Serve** via Traefik reverse proxy
|
||||
|
||||
### Deployment Steps
|
||||
|
||||
#### 1. Initial Setup
|
||||
|
||||
```bash
|
||||
# On server: Create app structure
|
||||
apps-create diagram static-fe
|
||||
|
||||
# This creates:
|
||||
# - /opt/apps/diagram (git repository)
|
||||
# - /home/tour/infra/diagram/docker-compose.yml
|
||||
```
|
||||
|
||||
#### 2. Configure Gitea Hook
|
||||
|
||||
In repository **Settings → Git Hooks → post-receive**:
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
/usr/local/bin/app-deploy diagram
|
||||
```
|
||||
|
||||
#### 3. Deploy
|
||||
|
||||
Simply push to Gitea:
|
||||
|
||||
```bash
|
||||
git push origin main
|
||||
```
|
||||
|
||||
The server automatically:
|
||||
- Pulls latest code
|
||||
- Rebuilds Docker image
|
||||
- Restarts container
|
||||
- Updates live site at https://diagram.appmodel.nl
|
||||
|
||||
### Manual Deployment
|
||||
|
||||
If needed:
|
||||
|
||||
```bash
|
||||
# On server
|
||||
app-deploy diagram
|
||||
|
||||
# Or step-by-step
|
||||
cd /opt/apps/diagram
|
||||
git pull
|
||||
cd /home/tour/infra/diagram
|
||||
docker compose up -d --build diagram
|
||||
```
|
||||
|
||||
### Monitoring
|
||||
|
||||
```bash
|
||||
# View deployment logs
|
||||
tail -f /var/log/app-deploy-diagram.log
|
||||
|
||||
# View container logs
|
||||
cd /home/tour/infra/diagram
|
||||
docker compose logs -f diagram
|
||||
|
||||
# Check container status
|
||||
docker compose ps
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
### Infrastructure
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ Production Architecture │
|
||||
├─────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌──────────────┐ │
|
||||
│ │ Gitea │ (Source of Truth) │
|
||||
│ │ Tour/diagram │ │
|
||||
│ └──────┬───────┘ │
|
||||
│ │ git push │
|
||||
│ ↓ │
|
||||
│ ┌──────────────┐ │
|
||||
│ │ Post-Receive │ (Auto-trigger) │
|
||||
│ │ Hook │ │
|
||||
│ └──────┬───────┘ │
|
||||
│ │ app-deploy diagram │
|
||||
│ ↓ │
|
||||
│ ┌──────────────────────┐ │
|
||||
│ │ /opt/apps/diagram/ │ │
|
||||
│ │ (Git Repository) │ │
|
||||
│ └──────┬───────────────┘ │
|
||||
│ │ git pull │
|
||||
│ ↓ │
|
||||
│ ┌──────────────────────┐ │
|
||||
│ │ Docker Build Process │ │
|
||||
│ │ ┌─────────────────┐ │ │
|
||||
│ │ │ Stage 1: Build │ │ │
|
||||
│ │ │ - Python │ │ │
|
||||
│ │ │ - Graphviz │ │ │
|
||||
│ │ │ - Generate PNGs │ │ │
|
||||
│ │ └─────────────────┘ │ │
|
||||
│ │ ┌─────────────────┐ │ │
|
||||
│ │ │ Stage 2: Serve │ │ │
|
||||
│ │ │ - Nginx │ │ │
|
||||
│ │ │ - Static files │ │ │
|
||||
│ │ └─────────────────┘ │ │
|
||||
│ └──────┬───────────────┘ │
|
||||
│ │ container starts │
|
||||
│ ↓ │
|
||||
│ ┌──────────────────────┐ │
|
||||
│ │ diagram-viewer │ │
|
||||
│ │ Container :80 │ │
|
||||
│ └──────┬───────────────┘ │
|
||||
│ │ traefik_net │
|
||||
│ ↓ │
|
||||
│ ┌──────────────────────┐ │
|
||||
│ │ Traefik │ │
|
||||
│ │ Reverse Proxy │ │
|
||||
│ │ + Let's Encrypt │ │
|
||||
│ └──────┬───────────────┘ │
|
||||
│ │ HTTPS │
|
||||
│ ↓ │
|
||||
│ diagram.appmodel.nl │
|
||||
│ │
|
||||
└─────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Network Topology
|
||||
|
||||
The diagram viewer documents our home lab infrastructure:
|
||||
|
||||
- **LAN 192.168.1.0/24**: Main network
|
||||
- Core server (192.168.1.159): Traefik, Gitea, Dokku, Auction stack, MI50/Ollama
|
||||
- DNS server (192.168.1.163): AdGuard, Artifactory, CI runner
|
||||
- Home Assistant (192.168.1.193)
|
||||
- Atlas worker (192.168.1.100)
|
||||
- IoT devices
|
||||
|
||||
- **Tether 192.168.137.0/24**: Isolated subnet
|
||||
- Hermes worker (192.168.137.239)
|
||||
- Plato worker (192.168.137.163)
|
||||
|
||||
### Services
|
||||
|
||||
| Service | URL | Description |
|
||||
|---------|-----|-------------|
|
||||
| Diagram Viewer | https://diagram.appmodel.nl | This application |
|
||||
| Gitea | https://git.appmodel.nl | Git hosting |
|
||||
| Auction Frontend | https://auction.appmodel.nl | Auction platform |
|
||||
| Aupi API | https://aupi.appmodel.nl | Backend API |
|
||||
| Dokku | https://dokku.lan | PaaS platform |
|
||||
|
||||
---
|
||||
|
||||
## Additional Resources
|
||||
|
||||
### Related Documentation
|
||||
|
||||
- [DEPLOY_SERVER_SETUP.md](../DEPLOY_SERVER_SETUP.md) - Detailed deployment guide
|
||||
- [DEPLOYMENT_GUIDE.md](../DEPLOYMENT_GUIDE.md) - General deployment pipeline
|
||||
- [README.md](../README.md) - Project overview
|
||||
|
||||
### External Links
|
||||
|
||||
- [Diagrams Library](https://diagrams.mingrammer.com/)
|
||||
- [Graphviz](https://graphviz.org/)
|
||||
- [Mermaid.js](https://mermaid.js.org/) (used in live editor)
|
||||
- [Traefik Documentation](https://doc.traefik.io/traefik/)
|
||||
- [Docker Compose](https://docs.docker.com/compose/)
|
||||
|
||||
### Contributing
|
||||
|
||||
To contribute to this project:
|
||||
|
||||
1. Create a feature branch
|
||||
2. Make your changes
|
||||
3. Test locally
|
||||
4. Submit a pull request in Gitea
|
||||
5. Wait for automatic deployment after merge
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
#### Diagrams Not Generating
|
||||
|
||||
**Problem**: Python script fails to generate PNG files
|
||||
|
||||
**Solution**:
|
||||
```bash
|
||||
# Check Graphviz installation
|
||||
dot -V
|
||||
|
||||
# Reinstall if needed
|
||||
# macOS: brew install graphviz
|
||||
# Ubuntu: sudo apt-get install graphviz
|
||||
# Windows: choco install graphviz
|
||||
|
||||
# Verify Python dependencies
|
||||
pip list | grep diagrams
|
||||
pip install --upgrade diagrams
|
||||
```
|
||||
|
||||
#### Container Build Fails
|
||||
|
||||
**Problem**: Docker build fails during deployment
|
||||
|
||||
**Solution**:
|
||||
```bash
|
||||
# Check deployment logs
|
||||
tail -f /var/log/app-deploy-diagram.log
|
||||
|
||||
# Rebuild manually with verbose output
|
||||
cd /home/tour/infra/diagram
|
||||
docker compose build --no-cache --progress=plain diagram
|
||||
```
|
||||
|
||||
#### Site Not Accessible
|
||||
|
||||
**Problem**: Container runs but site not reachable
|
||||
|
||||
**Solution**:
|
||||
1. Check DNS: `nslookup diagram.appmodel.nl`
|
||||
2. Verify Traefik labels: `docker inspect diagram-viewer | grep traefik`
|
||||
3. Check Traefik logs: `docker logs traefik`
|
||||
4. Test container directly: `curl http://localhost:PORT`
|
||||
|
||||
#### Changes Not Reflected
|
||||
|
||||
**Problem**: Pushed changes but site unchanged
|
||||
|
||||
**Solution**:
|
||||
```bash
|
||||
# Force rebuild on server
|
||||
cd /home/tour/infra/diagram
|
||||
docker compose down
|
||||
docker compose up -d --build --force-recreate diagram
|
||||
|
||||
# Clear browser cache
|
||||
# Ctrl+Shift+R (Windows/Linux) or Cmd+Shift+R (macOS)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Contact & Support
|
||||
|
||||
For issues, questions, or suggestions:
|
||||
|
||||
1. Check this wiki
|
||||
2. Review [DEPLOY_SERVER_SETUP.md](../DEPLOY_SERVER_SETUP.md)
|
||||
3. Check container logs
|
||||
4. Contact the infrastructure team
|
||||
|
||||
---
|
||||
|
||||
**Last Updated**: 2025-12-02
|
||||
**Version**: 1.0.0
|
||||
**Maintainer**: Tour
|
||||
# Diagram Viewer Wiki
|
||||
|
||||
Welcome to the **Diagram Viewer** project documentation.
|
||||
|
||||
This project provides a static web interface for viewing and managing network architecture diagrams generated with Python's `diagrams` library.
|
||||
|
||||
## 📚 Contents
|
||||
|
||||
- [Getting Started](#getting-started)
|
||||
- [Development Guide](#development-guide)
|
||||
- [Deployment Guide](#deployment-guide)
|
||||
- [Architecture Overview](#architecture-overview)
|
||||
|
||||
---
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Python 3.8+
|
||||
- Graphviz
|
||||
- Docker (for deployment)
|
||||
|
||||
### Quick Start
|
||||
|
||||
```bash
|
||||
# Clone repository
|
||||
git clone git@git.appmodel.nl:Tour/viewer.git
|
||||
cd viewer
|
||||
|
||||
# Create virtual environment
|
||||
python -m venv .venv
|
||||
source .venv/bin/activate # Windows: .venv\Scripts\activate
|
||||
|
||||
# Install dependencies
|
||||
pip install -r requirements.txt
|
||||
|
||||
# Generate diagrams
|
||||
python lan_architecture.py
|
||||
python main.py
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Development Guide
|
||||
|
||||
### Project Structure
|
||||
|
||||
```
|
||||
viewer/
|
||||
├── Dockerfile # Multi-stage build for production
|
||||
├── docker-compose.yml # Docker Compose configuration
|
||||
├── requirements.txt # Python dependencies
|
||||
├── lan_architecture.py # LAN architecture diagram generator
|
||||
├── main.py # Main diagram generator
|
||||
├── public/
|
||||
│ └── index.html # Live Mermaid diagram editor
|
||||
└── wiki/ # Documentation (this wiki)
|
||||
```
|
||||
|
||||
### Adding New Diagrams
|
||||
|
||||
1. Create a new Python file (e.g., `network_diagram.py`)
|
||||
2. Import required components from `diagrams` library
|
||||
3. Define your architecture using context managers
|
||||
4. Run the script to generate PNG output
|
||||
|
||||
Example:
|
||||
```python
|
||||
from diagrams import Diagram, Cluster
|
||||
from diagrams.onprem.network import Router
|
||||
from diagrams.generic.device import Mobile
|
||||
|
||||
with Diagram("My Network", show=False, filename="my_network"):
|
||||
router = Router("Gateway")
|
||||
device = Mobile("Phone")
|
||||
device >> router
|
||||
```
|
||||
|
||||
### Testing Locally
|
||||
|
||||
```bash
|
||||
# Generate diagrams
|
||||
python lan_architecture.py
|
||||
|
||||
# View output
|
||||
ls -la *.png
|
||||
|
||||
# Test with local web server
|
||||
python -m http.server 8000 --directory public/
|
||||
# Open: http://localhost:8000
|
||||
```
|
||||
|
||||
### Available Icons
|
||||
|
||||
The `diagrams` library provides icons from multiple providers:
|
||||
- **OnPrem**: Network, compute, storage, etc.
|
||||
- **Cloud**: AWS, Azure, GCP services
|
||||
- **Generic**: Network devices, blank nodes
|
||||
- **Custom**: Use your own images
|
||||
|
||||
See: [Diagrams Documentation](https://diagrams.mingrammer.com/docs/nodes/overview)
|
||||
|
||||
---
|
||||
|
||||
## Deployment Guide
|
||||
|
||||
### Overview
|
||||
|
||||
The project uses a fully automated Docker-based deployment pipeline:
|
||||
|
||||
1. **Develop** on local machine
|
||||
2. **Commit & Push** to Gitea
|
||||
3. **Auto-Deploy** via post-receive hook
|
||||
4. **Build** multi-stage Docker image
|
||||
5. **Serve** via Traefik reverse proxy
|
||||
|
||||
### Deployment Steps
|
||||
|
||||
#### 1. Initial Setup
|
||||
|
||||
```bash
|
||||
# On server: Create app structure
|
||||
apps-create viewer static-fe
|
||||
|
||||
# This creates:
|
||||
# - /opt/apps/viewer (git repository)
|
||||
# - /home/tour/infra/viewer/docker-compose.yml
|
||||
```
|
||||
|
||||
#### 2. Configure Gitea Hook
|
||||
|
||||
In repository **Settings → Git Hooks → post-receive**:
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
/usr/local/bin/app-deploy viewer
|
||||
```
|
||||
|
||||
#### 3. Deploy
|
||||
|
||||
Simply push to Gitea:
|
||||
|
||||
```bash
|
||||
git push origin main
|
||||
```
|
||||
|
||||
The server automatically:
|
||||
- Pulls latest code
|
||||
- Rebuilds Docker image
|
||||
- Restarts container
|
||||
- Updates live site at https://viewer.appmodel.nl
|
||||
|
||||
### Manual Deployment
|
||||
|
||||
If needed:
|
||||
|
||||
```bash
|
||||
# On server
|
||||
app-deploy viewer
|
||||
|
||||
# Or step-by-step
|
||||
cd /opt/apps/viewer
|
||||
git pull
|
||||
cd /home/tour/infra/viewer
|
||||
docker compose up -d --build viewer
|
||||
```
|
||||
|
||||
### Monitoring
|
||||
|
||||
```bash
|
||||
# View deployment logs
|
||||
tail -f /var/log/app-deploy-viewer.log
|
||||
|
||||
# View container logs
|
||||
cd /home/tour/infra/viewer
|
||||
docker compose logs -f viewer
|
||||
|
||||
# Check container status
|
||||
docker compose ps
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
### Infrastructure
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ Production Architecture │
|
||||
├─────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌──────────────┐ │
|
||||
│ │ Gitea │ (Source of Truth) │
|
||||
│ │ Tour/viewer │ │
|
||||
│ └──────┬───────┘ │
|
||||
│ │ git push │
|
||||
│ ↓ │
|
||||
│ ┌──────────────┐ │
|
||||
│ │ Post-Receive │ (Auto-trigger) │
|
||||
│ │ Hook │ │
|
||||
│ └──────┬───────┘ │
|
||||
│ │ app-deploy viewer │
|
||||
│ ↓ │
|
||||
│ ┌──────────────────────┐ │
|
||||
│ │ /opt/apps/viewer/ │ │
|
||||
│ │ (Git Repository) │ │
|
||||
│ └──────┬───────────────┘ │
|
||||
│ │ git pull │
|
||||
│ ↓ │
|
||||
│ ┌──────────────────────┐ │
|
||||
│ │ Docker Build Process │ │
|
||||
│ │ ┌─────────────────┐ │ │
|
||||
│ │ │ Stage 1: Build │ │ │
|
||||
│ │ │ - Python │ │ │
|
||||
│ │ │ - Graphviz │ │ │
|
||||
│ │ │ - Generate PNGs │ │ │
|
||||
│ │ └─────────────────┘ │ │
|
||||
│ │ ┌─────────────────┐ │ │
|
||||
│ │ │ Stage 2: Serve │ │ │
|
||||
│ │ │ - Nginx │ │ │
|
||||
│ │ │ - Static files │ │ │
|
||||
│ │ └─────────────────┘ │ │
|
||||
│ └──────┬───────────────┘ │
|
||||
│ │ container starts │
|
||||
│ ↓ │
|
||||
│ ┌──────────────────────┐ │
|
||||
│ │ diagram-viewer │ │
|
||||
│ │ Container :80 │ │
|
||||
│ └──────┬───────────────┘ │
|
||||
│ │ traefik_net │
|
||||
│ ↓ │
|
||||
│ ┌──────────────────────┐ │
|
||||
│ │ Traefik │ │
|
||||
│ │ Reverse Proxy │ │
|
||||
│ │ + Let's Encrypt │ │
|
||||
│ └──────┬───────────────┘ │
|
||||
│ │ HTTPS │
|
||||
│ ↓ │
|
||||
│ viewer.appmodel.nl │
|
||||
│ │
|
||||
└─────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Network Topology
|
||||
|
||||
The diagram viewer documents our home lab infrastructure:
|
||||
|
||||
- **LAN 192.168.1.0/24**: Main network
|
||||
- Core server (192.168.1.159): Traefik, Gitea, Dokku, Auction stack, MI50/Ollama
|
||||
- DNS server (192.168.1.163): AdGuard, Artifactory, CI runner
|
||||
- Home Assistant (192.168.1.193)
|
||||
- Atlas worker (192.168.1.100)
|
||||
- IoT devices
|
||||
|
||||
- **Tether 192.168.137.0/24**: Isolated subnet
|
||||
- Hermes worker (192.168.137.239)
|
||||
- Plato worker (192.168.137.163)
|
||||
|
||||
### Services
|
||||
|
||||
| Service | URL | Description |
|
||||
|---------|-----|-------------|
|
||||
| Diagram Viewer | https://viewer.appmodel.nl | This application |
|
||||
| Gitea | https://git.appmodel.nl | Git hosting |
|
||||
| Auction Frontend | https://auction.appmodel.nl | Auction platform |
|
||||
| Aupi API | https://aupi.appmodel.nl | Backend API |
|
||||
| Dokku | https://dokku.lan | PaaS platform |
|
||||
|
||||
---
|
||||
|
||||
## Additional Resources
|
||||
|
||||
### Related Documentation
|
||||
|
||||
- [DEPLOY_SERVER_SETUP.md](../DEPLOY_SERVER_SETUP.md) - Detailed deployment guide
|
||||
- [DEPLOYMENT_GUIDE.md](../DEPLOYMENT_GUIDE.md) - General deployment pipeline
|
||||
- [README.md](../README.md) - Project overview
|
||||
|
||||
### External Links
|
||||
|
||||
- [Diagrams Library](https://diagrams.mingrammer.com/)
|
||||
- [Graphviz](https://graphviz.org/)
|
||||
- [Mermaid.js](https://mermaid.js.org/) (used in live editor)
|
||||
- [Traefik Documentation](https://doc.traefik.io/traefik/)
|
||||
- [Docker Compose](https://docs.docker.com/compose/)
|
||||
|
||||
### Contributing
|
||||
|
||||
To contribute to this project:
|
||||
|
||||
1. Create a feature branch
|
||||
2. Make your changes
|
||||
3. Test locally
|
||||
4. Submit a pull request in Gitea
|
||||
5. Wait for automatic deployment after merge
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
#### Diagrams Not Generating
|
||||
|
||||
**Problem**: Python script fails to generate PNG files
|
||||
|
||||
**Solution**:
|
||||
```bash
|
||||
# Check Graphviz installation
|
||||
dot -V
|
||||
|
||||
# Reinstall if needed
|
||||
# macOS: brew install graphviz
|
||||
# Ubuntu: sudo apt-get install graphviz
|
||||
# Windows: choco install graphviz
|
||||
|
||||
# Verify Python dependencies
|
||||
pip list | grep diagrams
|
||||
pip install --upgrade diagrams
|
||||
```
|
||||
|
||||
#### Container Build Fails
|
||||
|
||||
**Problem**: Docker build fails during deployment
|
||||
|
||||
**Solution**:
|
||||
```bash
|
||||
# Check deployment logs
|
||||
tail -f /var/log/app-deploy-viewer.log
|
||||
|
||||
# Rebuild manually with verbose output
|
||||
cd /home/tour/infra/viewer
|
||||
docker compose build --no-cache --progress=plain viewer
|
||||
```
|
||||
|
||||
#### Site Not Accessible
|
||||
|
||||
**Problem**: Container runs but site not reachable
|
||||
|
||||
**Solution**:
|
||||
1. Check DNS: `nslookup viewer.appmodel.nl`
|
||||
2. Verify Traefik labels: `docker inspect viewer-viewer | grep traefik`
|
||||
3. Check Traefik logs: `docker logs traefik`
|
||||
4. Test container directly: `curl http://localhost:PORT`
|
||||
|
||||
#### Changes Not Reflected
|
||||
|
||||
**Problem**: Pushed changes but site unchanged
|
||||
|
||||
**Solution**:
|
||||
```bash
|
||||
# Force rebuild on server
|
||||
cd /home/tour/infra/viewer
|
||||
docker compose down
|
||||
docker compose up -d --build --force-recreate viewer
|
||||
|
||||
# Clear browser cache
|
||||
# Ctrl+Shift+R (Windows/Linux) or Cmd+Shift+R (macOS)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Contact & Support
|
||||
|
||||
For issues, questions, or suggestions:
|
||||
|
||||
1. Check this wiki
|
||||
2. Review [DEPLOY_SERVER_SETUP.md](../DEPLOY_SERVER_SETUP.md)
|
||||
3. Check container logs
|
||||
4. Contact the infrastructure team
|
||||
|
||||
---
|
||||
|
||||
**Last Updated**: 2025-12-02
|
||||
**Version**: 1.0.0
|
||||
**Maintainer**: Tour
|
||||
@@ -1,239 +1,239 @@
|
||||
# Quick Start Guide
|
||||
|
||||
Get up and running with the Diagram Viewer in minutes.
|
||||
|
||||
## For Developers
|
||||
|
||||
### 1. Clone Repository
|
||||
|
||||
```bash
|
||||
git clone git@git.appmodel.nl:Tour/diagram.git
|
||||
cd diagram
|
||||
```
|
||||
|
||||
### 2. Setup Environment
|
||||
|
||||
```bash
|
||||
# Create virtual environment
|
||||
python -m venv .venv
|
||||
|
||||
# Activate (choose your platform)
|
||||
source .venv/bin/activate # macOS/Linux
|
||||
.venv\Scripts\activate # Windows CMD
|
||||
source .venv/Scripts/activate # Windows Git Bash
|
||||
```
|
||||
|
||||
### 3. Install Dependencies
|
||||
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
### 4. Generate Diagrams
|
||||
|
||||
```bash
|
||||
# Generate LAN architecture diagram
|
||||
python lan_architecture.py
|
||||
|
||||
# Generate main diagram
|
||||
python main.py
|
||||
|
||||
# View generated files
|
||||
ls -la *.png
|
||||
```
|
||||
|
||||
### 5. Test Locally
|
||||
|
||||
```bash
|
||||
# Start local web server
|
||||
python -m http.server 8000 --directory public/
|
||||
|
||||
# Open browser
|
||||
# Navigate to: http://localhost:8000
|
||||
```
|
||||
|
||||
### 6. Make Changes & Deploy
|
||||
|
||||
```bash
|
||||
# Edit diagram code
|
||||
vim lan_architecture.py
|
||||
|
||||
# Commit changes
|
||||
git add .
|
||||
git commit -m "Update network diagram"
|
||||
|
||||
# Push to deploy (auto-deploys to production)
|
||||
git push origin main
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## For Operators
|
||||
|
||||
### View Live Site
|
||||
|
||||
Simply navigate to:
|
||||
```
|
||||
https://diagram.appmodel.nl
|
||||
```
|
||||
|
||||
### Manual Deployment
|
||||
|
||||
On the server:
|
||||
|
||||
```bash
|
||||
# Trigger deployment
|
||||
app-deploy diagram
|
||||
|
||||
# View logs
|
||||
tail -f /var/log/app-deploy-diagram.log
|
||||
|
||||
# Check status
|
||||
cd /home/tour/infra/diagram
|
||||
docker compose ps
|
||||
```
|
||||
|
||||
### View Logs
|
||||
|
||||
```bash
|
||||
# Deployment logs
|
||||
tail -f /var/log/app-deploy-diagram.log
|
||||
|
||||
# Container logs
|
||||
cd /home/tour/infra/diagram
|
||||
docker compose logs -f diagram
|
||||
|
||||
# Last 100 lines
|
||||
docker compose logs --tail=100 diagram
|
||||
```
|
||||
|
||||
### Restart Service
|
||||
|
||||
```bash
|
||||
cd /home/tour/infra/diagram
|
||||
|
||||
# Restart container
|
||||
docker compose restart diagram
|
||||
|
||||
# Full restart (rebuild)
|
||||
docker compose down
|
||||
docker compose up -d --build diagram
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Common Tasks
|
||||
|
||||
### Add New Diagram
|
||||
|
||||
1. Create new Python file:
|
||||
```python
|
||||
# my_diagram.py
|
||||
from diagrams import Diagram
|
||||
from diagrams.onprem.network import Router
|
||||
|
||||
with Diagram("My Network", show=False, filename="my_network"):
|
||||
Router("Gateway")
|
||||
```
|
||||
|
||||
2. Update Dockerfile if needed (add to RUN command):
|
||||
```dockerfile
|
||||
RUN python lan_architecture.py && \
|
||||
python main.py && \
|
||||
python my_diagram.py
|
||||
```
|
||||
|
||||
3. Commit and push:
|
||||
```bash
|
||||
git add my_diagram.py Dockerfile
|
||||
git commit -m "Add my network diagram"
|
||||
git push
|
||||
```
|
||||
|
||||
### Update Frontend
|
||||
|
||||
1. Edit HTML:
|
||||
```bash
|
||||
vim public/index.html
|
||||
```
|
||||
|
||||
2. Test locally:
|
||||
```bash
|
||||
python -m http.server 8000 --directory public/
|
||||
```
|
||||
|
||||
3. Deploy:
|
||||
```bash
|
||||
git add public/index.html
|
||||
git commit -m "Update frontend"
|
||||
git push
|
||||
```
|
||||
|
||||
### Check Container Health
|
||||
|
||||
```bash
|
||||
# Container status
|
||||
docker compose ps
|
||||
|
||||
# Health check
|
||||
docker inspect diagram-viewer | grep -A 10 Health
|
||||
|
||||
# Test endpoint
|
||||
curl -I https://diagram.appmodel.nl
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Build Fails
|
||||
|
||||
```bash
|
||||
# Check logs
|
||||
tail -f /var/log/app-deploy-diagram.log
|
||||
|
||||
# Rebuild with verbose output
|
||||
cd /home/tour/infra/diagram
|
||||
docker compose build --no-cache --progress=plain diagram
|
||||
```
|
||||
|
||||
### Site Not Loading
|
||||
|
||||
```bash
|
||||
# Check container
|
||||
docker compose ps
|
||||
|
||||
# Check Traefik routing
|
||||
docker logs traefik | grep diagram
|
||||
|
||||
# Test DNS
|
||||
nslookup diagram.appmodel.nl
|
||||
|
||||
# Test directly
|
||||
curl http://localhost:PORT
|
||||
```
|
||||
|
||||
### Changes Not Visible
|
||||
|
||||
```bash
|
||||
# Force rebuild
|
||||
cd /home/tour/infra/diagram
|
||||
docker compose up -d --build --force-recreate diagram
|
||||
|
||||
# Clear browser cache
|
||||
# Ctrl+Shift+R (Windows/Linux)
|
||||
# Cmd+Shift+R (macOS)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
- Read the [Home](Home.md) page for comprehensive documentation
|
||||
- Review [DEPLOY_SERVER_SETUP.md](../DEPLOY_SERVER_SETUP.md) for deployment details
|
||||
- Check [Diagrams Library docs](https://diagrams.mingrammer.com/) for icon options
|
||||
- Explore the [Mermaid editor](https://diagram.appmodel.nl) for live diagram editing
|
||||
|
||||
---
|
||||
|
||||
**Need Help?** Check the [Troubleshooting](Home.md#troubleshooting) section or review deployment logs.
|
||||
# Quick Start Guide
|
||||
|
||||
Get up and running with the Diagram Viewer in minutes.
|
||||
|
||||
## For Developers
|
||||
|
||||
### 1. Clone Repository
|
||||
|
||||
```bash
|
||||
git clone git@git.appmodel.nl:Tour/viewer.git
|
||||
cd viewer
|
||||
```
|
||||
|
||||
### 2. Setup Environment
|
||||
|
||||
```bash
|
||||
# Create virtual environment
|
||||
python -m venv .venv
|
||||
|
||||
# Activate (choose your platform)
|
||||
source .venv/bin/activate # macOS/Linux
|
||||
.venv\Scripts\activate # Windows CMD
|
||||
source .venv/Scripts/activate # Windows Git Bash
|
||||
```
|
||||
|
||||
### 3. Install Dependencies
|
||||
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
### 4. Generate Diagrams
|
||||
|
||||
```bash
|
||||
# Generate LAN architecture diagram
|
||||
python lan_architecture.py
|
||||
|
||||
# Generate main diagram
|
||||
python main.py
|
||||
|
||||
# View generated files
|
||||
ls -la *.png
|
||||
```
|
||||
|
||||
### 5. Test Locally
|
||||
|
||||
```bash
|
||||
# Start local web server
|
||||
python -m http.server 8000 --directory public/
|
||||
|
||||
# Open browser
|
||||
# Navigate to: http://localhost:8000
|
||||
```
|
||||
|
||||
### 6. Make Changes & Deploy
|
||||
|
||||
```bash
|
||||
# Edit diagram code
|
||||
vim lan_architecture.py
|
||||
|
||||
# Commit changes
|
||||
git add .
|
||||
git commit -m "Update network diagram"
|
||||
|
||||
# Push to deploy (auto-deploys to production)
|
||||
git push origin main
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## For Operators
|
||||
|
||||
### View Live Site
|
||||
|
||||
Simply navigate to:
|
||||
```
|
||||
https://viewer.appmodel.nl
|
||||
```
|
||||
|
||||
### Manual Deployment
|
||||
|
||||
On the server:
|
||||
|
||||
```bash
|
||||
# Trigger deployment
|
||||
app-deploy viewer
|
||||
|
||||
# View logs
|
||||
tail -f /var/log/app-deploy-viewer.log
|
||||
|
||||
# Check status
|
||||
cd /home/tour/infra/viewer
|
||||
docker compose ps
|
||||
```
|
||||
|
||||
### View Logs
|
||||
|
||||
```bash
|
||||
# Deployment logs
|
||||
tail -f /var/log/app-deploy-viewer.log
|
||||
|
||||
# Container logs
|
||||
cd /home/tour/infra/viewer
|
||||
docker compose logs -f viewer
|
||||
|
||||
# Last 100 lines
|
||||
docker compose logs --tail=100 viewer
|
||||
```
|
||||
|
||||
### Restart Service
|
||||
|
||||
```bash
|
||||
cd /home/tour/infra/viewer
|
||||
|
||||
# Restart container
|
||||
docker compose restart viewer
|
||||
|
||||
# Full restart (rebuild)
|
||||
docker compose down
|
||||
docker compose up -d --build viewer
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Common Tasks
|
||||
|
||||
### Add New Diagram
|
||||
|
||||
1. Create new Python file:
|
||||
```python
|
||||
# my_diagram.py
|
||||
from diagrams import Diagram
|
||||
from diagrams.onprem.network import Router
|
||||
|
||||
with Diagram("My Network", show=False, filename="my_network"):
|
||||
Router("Gateway")
|
||||
```
|
||||
|
||||
2. Update Dockerfile if needed (add to RUN command):
|
||||
```dockerfile
|
||||
RUN python lan_architecture.py && \
|
||||
python main.py && \
|
||||
python my_diagram.py
|
||||
```
|
||||
|
||||
3. Commit and push:
|
||||
```bash
|
||||
git add my_diagram.py Dockerfile
|
||||
git commit -m "Add my network diagram"
|
||||
git push
|
||||
```
|
||||
|
||||
### Update Frontend
|
||||
|
||||
1. Edit HTML:
|
||||
```bash
|
||||
vim public/index.html
|
||||
```
|
||||
|
||||
2. Test locally:
|
||||
```bash
|
||||
python -m http.server 8000 --directory public/
|
||||
```
|
||||
|
||||
3. Deploy:
|
||||
```bash
|
||||
git add public/index.html
|
||||
git commit -m "Update frontend"
|
||||
git push
|
||||
```
|
||||
|
||||
### Check Container Health
|
||||
|
||||
```bash
|
||||
# Container status
|
||||
docker compose ps
|
||||
|
||||
# Health check
|
||||
docker inspect viewer | grep -A 10 Health
|
||||
|
||||
# Test endpoint
|
||||
curl -I https://viewer.appmodel.nl
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Build Fails
|
||||
|
||||
```bash
|
||||
# Check logs
|
||||
tail -f /var/log/app-deploy-viewer.log
|
||||
|
||||
# Rebuild with verbose output
|
||||
cd /home/tour/infra/viewer
|
||||
docker compose build --no-cache --progress=plain viewer
|
||||
```
|
||||
|
||||
### Site Not Loading
|
||||
|
||||
```bash
|
||||
# Check container
|
||||
docker compose ps
|
||||
|
||||
# Check Traefik routing
|
||||
docker logs traefik | grep viewer
|
||||
|
||||
# Test DNS
|
||||
nslookup viewer.appmodel.nl
|
||||
|
||||
# Test directly
|
||||
curl http://localhost:PORT
|
||||
```
|
||||
|
||||
### Changes Not Visible
|
||||
|
||||
```bash
|
||||
# Force rebuild
|
||||
cd /home/tour/infra/viewer
|
||||
docker compose up -d --build --force-recreate viewer
|
||||
|
||||
# Clear browser cache
|
||||
# Ctrl+Shift+R (Windows/Linux)
|
||||
# Cmd+Shift+R (macOS)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
- Read the [Home](Home.md) page for comprehensive documentation
|
||||
- Review [DEPLOY_SERVER_SETUP.md](../DEPLOY_SERVER_SETUP.md) for deployment details
|
||||
- Check [Diagrams Library docs](https://diagrams.mingrammer.com/) for icon options
|
||||
- Explore the [Mermaid editor](https://viewer.appmodel.nl) for live diagram editing
|
||||
|
||||
---
|
||||
|
||||
**Need Help?** Check the [Troubleshooting](Home.md#troubleshooting) section or review deployment logs.
|
||||
@@ -1,361 +1,361 @@
|
||||
# Gitea Wiki Integration Guide
|
||||
|
||||
This guide explains how to integrate the local `wiki/` folder with Gitea's wiki system.
|
||||
|
||||
## How Gitea Wikis Work
|
||||
|
||||
Gitea stores wiki pages in a **separate Git repository** with the naming pattern:
|
||||
```
|
||||
<repository>.wiki.git
|
||||
```
|
||||
|
||||
For the `viewer` repository:
|
||||
- Main repo: `git@git.appmodel.nl:Tour/viewer.git`
|
||||
- Wiki repo: `git@git.appmodel.nl:Tour/viewer.wiki.git`
|
||||
|
||||
Wiki pages are **markdown files** (`.md`) stored in the root of the wiki repository.
|
||||
|
||||
## Integration Options
|
||||
|
||||
### Option 1: Push Local Wiki to Gitea (Recommended)
|
||||
|
||||
This approach replaces the current Gitea wiki with your local content.
|
||||
|
||||
```bash
|
||||
# 1. Clone the Gitea wiki repository
|
||||
git clone git@git.appmodel.nl:Tour/viewer.wiki.git viewer-wiki
|
||||
|
||||
# 2. Copy your wiki files to the wiki repository
|
||||
cd viewer-wiki
|
||||
cp ../viewer/wiki/*.md .
|
||||
|
||||
# 3. Commit and push
|
||||
git add .
|
||||
git commit -m "Initialize wiki with documentation"
|
||||
git push origin master
|
||||
|
||||
# 4. View on Gitea
|
||||
# Navigate to: https://git.appmodel.nl/Tour/diagram/wiki
|
||||
```
|
||||
|
||||
### Option 2: Use Git Submodule (Bidirectional Sync)
|
||||
|
||||
Keep the wiki as a submodule in the main repository for easy synchronization.
|
||||
|
||||
```bash
|
||||
# In the main diagram repository
|
||||
cd /c/vibe/diagram
|
||||
|
||||
# Remove local wiki folder
|
||||
rm -rf wiki/
|
||||
|
||||
# Add Gitea wiki as submodule
|
||||
git submodule add git@git.appmodel.nl:Tour/viewer.wiki.git wiki
|
||||
|
||||
# Copy your documentation
|
||||
cp ../temp-wiki-backup/*.md wiki/
|
||||
|
||||
# Commit to wiki submodule
|
||||
cd wiki
|
||||
git add .
|
||||
git commit -m "Add documentation"
|
||||
git push origin master
|
||||
|
||||
# Update main repository
|
||||
cd ..
|
||||
git add wiki
|
||||
git commit -m "Add wiki as submodule"
|
||||
git push origin main
|
||||
```
|
||||
|
||||
Now changes can be made either:
|
||||
- **In Gitea UI**: Edit wiki pages directly
|
||||
- **Locally**: Edit in `wiki/` folder, commit, and push
|
||||
|
||||
To sync changes from Gitea:
|
||||
```bash
|
||||
cd wiki
|
||||
git pull origin master
|
||||
cd ..
|
||||
git add wiki
|
||||
git commit -m "Update wiki submodule"
|
||||
git push
|
||||
```
|
||||
|
||||
### Option 3: Git Subtree (Advanced)
|
||||
|
||||
Similar to submodule but integrates the wiki history into the main repository.
|
||||
|
||||
```bash
|
||||
# Add wiki as subtree
|
||||
git subtree add --prefix=wiki git@git.appmodel.nl:Tour/diagram.wiki.git master --squash
|
||||
|
||||
# Push changes to wiki
|
||||
git subtree push --prefix=wiki git@git.appmodel.nl:Tour/diagram.wiki.git master
|
||||
|
||||
# Pull changes from wiki
|
||||
git subtree pull --prefix=wiki git@git.appmodel.nl:Tour/diagram.wiki.git master --squash
|
||||
```
|
||||
|
||||
### Option 4: Automated Sync Script
|
||||
|
||||
Create a script to automatically sync wiki changes.
|
||||
|
||||
Create `sync-wiki.sh`:
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# sync-wiki.sh - Sync local wiki with Gitea
|
||||
|
||||
WIKI_REPO="git@git.appmodel.nl:Tour/diagram.wiki.git"
|
||||
WIKI_DIR="wiki"
|
||||
TEMP_WIKI="/tmp/gitea-wiki-$$"
|
||||
|
||||
# Clone Gitea wiki
|
||||
git clone "$WIKI_REPO" "$TEMP_WIKI"
|
||||
|
||||
# Copy local changes to temp wiki
|
||||
cp -r "$WIKI_DIR"/*.md "$TEMP_WIKI/"
|
||||
|
||||
# Commit and push
|
||||
cd "$TEMP_WIKI"
|
||||
git add .
|
||||
if git diff-staged --quiet; then
|
||||
echo "No changes to sync"
|
||||
else
|
||||
git commit -m "Sync wiki from main repository"
|
||||
git push origin master
|
||||
echo "Wiki synced successfully"
|
||||
fi
|
||||
|
||||
# Cleanup
|
||||
cd -
|
||||
rm -rf "$TEMP_WIKI"
|
||||
```
|
||||
|
||||
Usage:
|
||||
```bash
|
||||
chmod +x sync-wiki.sh
|
||||
./sync-wiki.sh
|
||||
```
|
||||
|
||||
## Recommended Workflow
|
||||
|
||||
For your deployment pipeline, I recommend **Option 1** with a manual push:
|
||||
|
||||
### Step-by-Step Setup
|
||||
|
||||
1. **Create wiki repository on Gitea** (if not exists):
|
||||
- Go to: https://git.appmodel.nl/Tour/diagram
|
||||
- Click "Wiki" tab
|
||||
- Create first page (triggers wiki repo creation)
|
||||
|
||||
2. **Push local wiki content**:
|
||||
```bash
|
||||
# Clone wiki repository
|
||||
cd /c/vibe
|
||||
git clone git@git.appmodel.nl:Tour/diagram.wiki.git
|
||||
|
||||
# Copy your wiki files
|
||||
cd diagram.wiki
|
||||
cp ../diagram/wiki/*.md .
|
||||
|
||||
# Verify files
|
||||
ls -la
|
||||
|
||||
# Commit and push
|
||||
git add .
|
||||
git commit -m "Initialize wiki documentation
|
||||
|
||||
Added:
|
||||
- Home.md: Main documentation page
|
||||
- Quick-Start.md: Quick start guide
|
||||
"
|
||||
git push origin master
|
||||
```
|
||||
|
||||
3. **Verify on Gitea**:
|
||||
- Navigate to: https://git.appmodel.nl/Tour/diagram/wiki
|
||||
- You should see your wiki pages
|
||||
|
||||
4. **Keep local wiki in main repo** (optional):
|
||||
- Keep `wiki/` folder in main repo as "source of truth"
|
||||
- When updating wiki, manually sync to Gitea wiki repo
|
||||
- Or set up automated sync via CI/CD
|
||||
|
||||
## Wiki Page Naming
|
||||
|
||||
Gitea wikis follow these conventions:
|
||||
|
||||
| Filename | URL | Purpose |
|
||||
|----------|-----|---------|
|
||||
| `Home.md` | `/wiki/` or `/wiki/Home` | Main wiki page |
|
||||
| `Quick-Start.md` | `/wiki/Quick-Start` | Quick start guide |
|
||||
| `API-Reference.md` | `/wiki/API-Reference` | API documentation |
|
||||
|
||||
**Rules**:
|
||||
- Use PascalCase or kebab-case for multi-word pages
|
||||
- Spaces in filenames become hyphens in URLs
|
||||
- First page is always `Home.md`
|
||||
|
||||
## Linking Between Pages
|
||||
|
||||
In your markdown files:
|
||||
|
||||
```markdown
|
||||
<!-- Link to another wiki page -->
|
||||
[Quick Start Guide](Quick-Start)
|
||||
|
||||
<!-- Link with custom text -->
|
||||
[Get Started](Quick-Start)
|
||||
|
||||
<!-- Link to main repository -->
|
||||
[View Code](../)
|
||||
|
||||
<!-- Link to specific file -->
|
||||
[Dockerfile](../src/master/Dockerfile)
|
||||
|
||||
<!-- External link -->
|
||||
[Diagrams Library](https://diagrams.mingrammer.com/)
|
||||
```
|
||||
|
||||
## Automation with Post-Receive Hook
|
||||
|
||||
To automatically sync wiki on deployment, add to Gitea post-receive hook:
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Deploy main app
|
||||
/usr/local/bin/app-deploy diagram
|
||||
|
||||
# Sync wiki (if wiki/ folder exists in repo)
|
||||
if [ -d "/opt/apps/diagram/wiki" ]; then
|
||||
cd /tmp
|
||||
git clone git@git.appmodel.nl:Tour/diagram.wiki.git gitea-wiki-$$
|
||||
cp /opt/apps/diagram/wiki/*.md /tmp/gitea-wiki-$$/
|
||||
cd /tmp/gitea-wiki-$$
|
||||
git add .
|
||||
if ! git diff --staged --quiet; then
|
||||
git commit -m "Auto-sync from main repository"
|
||||
git push origin master
|
||||
fi
|
||||
cd /tmp
|
||||
rm -rf /tmp/gitea-wiki-$$
|
||||
fi
|
||||
```
|
||||
|
||||
## Current Setup Instructions
|
||||
|
||||
Based on your current setup, here's what to do:
|
||||
|
||||
```bash
|
||||
# 1. Navigate to your diagram project
|
||||
cd /c/vibe/diagram
|
||||
|
||||
# 2. Clone the wiki repository (for viewer, adjust as needed)
|
||||
cd ..
|
||||
git clone git@git.appmodel.nl:Tour/diagram.wiki.git
|
||||
|
||||
# 3. Copy wiki files
|
||||
cp diagram/wiki/*.md diagram.wiki/
|
||||
|
||||
# 4. Push to Gitea
|
||||
cd diagram.wiki
|
||||
git add .
|
||||
git commit -m "Initialize wiki with deployment documentation"
|
||||
git push origin master
|
||||
|
||||
# 5. Verify
|
||||
# Open: https://git.appmodel.nl/Tour/diagram/wiki
|
||||
```
|
||||
|
||||
## Maintaining Both Wikis
|
||||
|
||||
If you want to keep both local and Gitea wikis in sync:
|
||||
|
||||
### Manual Sync (Simple)
|
||||
```bash
|
||||
# After editing local wiki files
|
||||
cd /c/vibe/diagram/wiki
|
||||
cp *.md /c/vibe/diagram.wiki/
|
||||
cd /c/vibe/diagram.wiki
|
||||
git add .
|
||||
git commit -m "Update documentation"
|
||||
git push
|
||||
```
|
||||
|
||||
### Automated Sync (Add to package.json or Makefile)
|
||||
```json
|
||||
{
|
||||
"scripts": {
|
||||
"sync-wiki": "cp wiki/*.md ../diagram.wiki/ && cd ../diagram.wiki && git add . && git commit -m 'Sync wiki' && git push"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Or create a `Makefile`:
|
||||
```makefile
|
||||
.PHONY: sync-wiki
|
||||
|
||||
sync-wiki:
|
||||
@echo "Syncing wiki to Gitea..."
|
||||
@cp wiki/*.md ../diagram.wiki/
|
||||
@cd ../diagram.wiki && git add . && git commit -m "Sync wiki from main repo" && git push
|
||||
@echo "Wiki synced successfully!"
|
||||
```
|
||||
|
||||
Usage: `make sync-wiki`
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Single Source of Truth**: Choose one location (local or Gitea) as authoritative
|
||||
2. **Version Control**: Keep wiki changes in Git history
|
||||
3. **Review Process**: Use pull requests for significant documentation changes
|
||||
4. **Link Checking**: Regularly verify internal links work
|
||||
5. **Consistent Formatting**: Use markdown linting tools
|
||||
6. **Table of Contents**: Keep `Home.md` as navigation hub
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Wiki Repository Doesn't Exist
|
||||
|
||||
Create the wiki first in Gitea UI:
|
||||
1. Go to repository: https://git.appmodel.nl/Tour/diagram
|
||||
2. Click "Wiki" tab
|
||||
3. Click "Create New Page"
|
||||
4. Create a dummy page (will be overwritten)
|
||||
5. Now you can clone `diagram.wiki.git`
|
||||
|
||||
### Permission Denied
|
||||
|
||||
Ensure your SSH key is added to Gitea:
|
||||
```bash
|
||||
ssh -T git@git.appmodel.nl
|
||||
```
|
||||
|
||||
### Merge Conflicts
|
||||
|
||||
If both local and Gitea wiki are edited:
|
||||
```bash
|
||||
cd diagram.wiki
|
||||
git pull origin master
|
||||
# Resolve conflicts manually
|
||||
git add .
|
||||
git commit -m "Merge local and remote changes"
|
||||
git push
|
||||
```
|
||||
|
||||
## Summary
|
||||
|
||||
**Recommended approach for your setup**:
|
||||
|
||||
1. Keep `wiki/` in main repository for version control
|
||||
2. Manually push to Gitea wiki repository when documentation is ready
|
||||
3. Optionally add automated sync in post-receive hook
|
||||
4. Edit wiki locally, sync to Gitea for visibility
|
||||
|
||||
This gives you the best of both worlds:
|
||||
- ✅ Documentation versioned with code
|
||||
- ✅ Visible wiki in Gitea UI
|
||||
- ✅ Easy to maintain and update
|
||||
- ✅ Works with existing deployment pipeline
|
||||
# Gitea Wiki Integration Guide
|
||||
|
||||
This guide explains how to integrate the local `wiki/` folder with Gitea's wiki system.
|
||||
|
||||
## How Gitea Wikis Work
|
||||
|
||||
Gitea stores wiki pages in a **separate Git repository** with the naming pattern:
|
||||
```
|
||||
<repository>.wiki.git
|
||||
```
|
||||
|
||||
For the `viewer` repository:
|
||||
- Main repo: `git@git.appmodel.nl:Tour/viewer.git`
|
||||
- Wiki repo: `git@git.appmodel.nl:Tour/viewer.wiki.git`
|
||||
|
||||
Wiki pages are **markdown files** (`.md`) stored in the root of the wiki repository.
|
||||
|
||||
## Integration Options
|
||||
|
||||
### Option 1: Push Local Wiki to Gitea (Recommended)
|
||||
|
||||
This approach replaces the current Gitea wiki with your local content.
|
||||
|
||||
```bash
|
||||
# 1. Clone the Gitea wiki repository
|
||||
git clone git@git.appmodel.nl:Tour/viewer.wiki.git viewer-wiki
|
||||
|
||||
# 2. Copy your wiki files to the wiki repository
|
||||
cd viewer-wiki
|
||||
cp ../viewer/wiki/*.md .
|
||||
|
||||
# 3. Commit and push
|
||||
git add .
|
||||
git commit -m "Initialize wiki with documentation"
|
||||
git push origin master
|
||||
|
||||
# 4. View on Gitea
|
||||
# Navigate to: https://git.appmodel.nl/Tour/viewer/wiki
|
||||
```
|
||||
|
||||
### Option 2: Use Git Submodule (Bidirectional Sync)
|
||||
|
||||
Keep the wiki as a submodule in the main repository for easy synchronization.
|
||||
|
||||
```bash
|
||||
# In the main viewer repository
|
||||
cd /c/vibe/viewer
|
||||
|
||||
# Remove local wiki folder
|
||||
rm -rf wiki/
|
||||
|
||||
# Add Gitea wiki as submodule
|
||||
git submodule add git@git.appmodel.nl:Tour/viewer.wiki.git wiki
|
||||
|
||||
# Copy your documentation
|
||||
cp ../temp-wiki-backup/*.md wiki/
|
||||
|
||||
# Commit to wiki submodule
|
||||
cd wiki
|
||||
git add .
|
||||
git commit -m "Add documentation"
|
||||
git push origin master
|
||||
|
||||
# Update main repository
|
||||
cd ..
|
||||
git add wiki
|
||||
git commit -m "Add wiki as submodule"
|
||||
git push origin main
|
||||
```
|
||||
|
||||
Now changes can be made either:
|
||||
- **In Gitea UI**: Edit wiki pages directly
|
||||
- **Locally**: Edit in `wiki/` folder, commit, and push
|
||||
|
||||
To sync changes from Gitea:
|
||||
```bash
|
||||
cd wiki
|
||||
git pull origin master
|
||||
cd ..
|
||||
git add wiki
|
||||
git commit -m "Update wiki submodule"
|
||||
git push
|
||||
```
|
||||
|
||||
### Option 3: Git Subtree (Advanced)
|
||||
|
||||
Similar to submodule but integrates the wiki history into the main repository.
|
||||
|
||||
```bash
|
||||
# Add wiki as subtree
|
||||
git subtree add --prefix=wiki git@git.appmodel.nl:Tour/viewer.wiki.git master --squash
|
||||
|
||||
# Push changes to wiki
|
||||
git subtree push --prefix=wiki git@git.appmodel.nl:Tour/viewer.wiki.git master
|
||||
|
||||
# Pull changes from wiki
|
||||
git subtree pull --prefix=wiki git@git.appmodel.nl:Tour/viewer.wiki.git master --squash
|
||||
```
|
||||
|
||||
### Option 4: Automated Sync Script
|
||||
|
||||
Create a script to automatically sync wiki changes.
|
||||
|
||||
Create `sync-wiki.sh`:
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# sync-wiki.sh - Sync local wiki with Gitea
|
||||
|
||||
WIKI_REPO="git@git.appmodel.nl:Tour/viewer.wiki.git"
|
||||
WIKI_DIR="wiki"
|
||||
TEMP_WIKI="/tmp/gitea-wiki-$$"
|
||||
|
||||
# Clone Gitea wiki
|
||||
git clone "$WIKI_REPO" "$TEMP_WIKI"
|
||||
|
||||
# Copy local changes to temp wiki
|
||||
cp -r "$WIKI_DIR"/*.md "$TEMP_WIKI/"
|
||||
|
||||
# Commit and push
|
||||
cd "$TEMP_WIKI"
|
||||
git add .
|
||||
if git diff-staged --quiet; then
|
||||
echo "No changes to sync"
|
||||
else
|
||||
git commit -m "Sync wiki from main repository"
|
||||
git push origin master
|
||||
echo "Wiki synced successfully"
|
||||
fi
|
||||
|
||||
# Cleanup
|
||||
cd -
|
||||
rm -rf "$TEMP_WIKI"
|
||||
```
|
||||
|
||||
Usage:
|
||||
```bash
|
||||
chmod +x sync-wiki.sh
|
||||
./sync-wiki.sh
|
||||
```
|
||||
|
||||
## Recommended Workflow
|
||||
|
||||
For your deployment pipeline, I recommend **Option 1** with a manual push:
|
||||
|
||||
### Step-by-Step Setup
|
||||
|
||||
1. **Create wiki repository on Gitea** (if not exists):
|
||||
- Go to: https://git.appmodel.nl/Tour/viewer
|
||||
- Click "Wiki" tab
|
||||
- Create first page (triggers wiki repo creation)
|
||||
|
||||
2. **Push local wiki content**:
|
||||
```bash
|
||||
# Clone wiki repository
|
||||
cd /c/vibe
|
||||
git clone git@git.appmodel.nl:Tour/viewer.wiki.git
|
||||
|
||||
# Copy your wiki files
|
||||
cd viewer.wiki
|
||||
cp ../viewer/wiki/*.md .
|
||||
|
||||
# Verify files
|
||||
ls -la
|
||||
|
||||
# Commit and push
|
||||
git add .
|
||||
git commit -m "Initialize wiki documentation
|
||||
|
||||
Added:
|
||||
- Home.md: Main documentation page
|
||||
- Quick-Start.md: Quick start guide
|
||||
"
|
||||
git push origin master
|
||||
```
|
||||
|
||||
3. **Verify on Gitea**:
|
||||
- Navigate to: https://git.appmodel.nl/Tour/viewer/wiki
|
||||
- You should see your wiki pages
|
||||
|
||||
4. **Keep local wiki in main repo** (optional):
|
||||
- Keep `wiki/` folder in main repo as "source of truth"
|
||||
- When updating wiki, manually sync to Gitea wiki repo
|
||||
- Or set up automated sync via CI/CD
|
||||
|
||||
## Wiki Page Naming
|
||||
|
||||
Gitea wikis follow these conventions:
|
||||
|
||||
| Filename | URL | Purpose |
|
||||
|----------|-----|---------|
|
||||
| `Home.md` | `/wiki/` or `/wiki/Home` | Main wiki page |
|
||||
| `Quick-Start.md` | `/wiki/Quick-Start` | Quick start guide |
|
||||
| `API-Reference.md` | `/wiki/API-Reference` | API documentation |
|
||||
|
||||
**Rules**:
|
||||
- Use PascalCase or kebab-case for multi-word pages
|
||||
- Spaces in filenames become hyphens in URLs
|
||||
- First page is always `Home.md`
|
||||
|
||||
## Linking Between Pages
|
||||
|
||||
In your markdown files:
|
||||
|
||||
```markdown
|
||||
<!-- Link to another wiki page -->
|
||||
[Quick Start Guide](Quick-Start)
|
||||
|
||||
<!-- Link with custom text -->
|
||||
[Get Started](Quick-Start)
|
||||
|
||||
<!-- Link to main repository -->
|
||||
[View Code](../)
|
||||
|
||||
<!-- Link to specific file -->
|
||||
[Dockerfile](../src/master/Dockerfile)
|
||||
|
||||
<!-- External link -->
|
||||
[Diagrams Library](https://diagrams.mingrammer.com/)
|
||||
```
|
||||
|
||||
## Automation with Post-Receive Hook
|
||||
|
||||
To automatically sync wiki on deployment, add to Gitea post-receive hook:
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Deploy main app
|
||||
/usr/local/bin/app-deploy viewer
|
||||
|
||||
# Sync wiki (if wiki/ folder exists in repo)
|
||||
if [ -d "/opt/apps/viewer/wiki" ]; then
|
||||
cd /tmp
|
||||
git clone git@git.appmodel.nl:Tour/viewer.wiki.git gitea-wiki-$$
|
||||
cp /opt/apps/viewer/wiki/*.md /tmp/gitea-wiki-$$/
|
||||
cd /tmp/gitea-wiki-$$
|
||||
git add .
|
||||
if ! git diff --staged --quiet; then
|
||||
git commit -m "Auto-sync from main repository"
|
||||
git push origin master
|
||||
fi
|
||||
cd /tmp
|
||||
rm -rf /tmp/gitea-wiki-$$
|
||||
fi
|
||||
```
|
||||
|
||||
## Current Setup Instructions
|
||||
|
||||
Based on your current setup, here's what to do:
|
||||
|
||||
```bash
|
||||
# 1. Navigate to your viewer project
|
||||
cd /c/vibe/viewer
|
||||
|
||||
# 2. Clone the wiki repository (for viewer, adjust as needed)
|
||||
cd ..
|
||||
git clone git@git.appmodel.nl:Tour/viewer.wiki.git
|
||||
|
||||
# 3. Copy wiki files
|
||||
cp viewer/wiki/*.md viewer.wiki/
|
||||
|
||||
# 4. Push to Gitea
|
||||
cd viewer.wiki
|
||||
git add .
|
||||
git commit -m "Initialize wiki with deployment documentation"
|
||||
git push origin master
|
||||
|
||||
# 5. Verify
|
||||
# Open: https://git.appmodel.nl/Tour/viewer/wiki
|
||||
```
|
||||
|
||||
## Maintaining Both Wikis
|
||||
|
||||
If you want to keep both local and Gitea wikis in sync:
|
||||
|
||||
### Manual Sync (Simple)
|
||||
```bash
|
||||
# After editing local wiki files
|
||||
cd /c/vibe/viewer/wiki
|
||||
cp *.md /c/vibe/viewer.wiki/
|
||||
cd /c/vibe/viewer.wiki
|
||||
git add .
|
||||
git commit -m "Update documentation"
|
||||
git push
|
||||
```
|
||||
|
||||
### Automated Sync (Add to package.json or Makefile)
|
||||
```json
|
||||
{
|
||||
"scripts": {
|
||||
"sync-wiki": "cp wiki/*.md ../viewer.wiki/ && cd ../viewer.wiki && git add . && git commit -m 'Sync wiki' && git push"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Or create a `Makefile`:
|
||||
```makefile
|
||||
.PHONY: sync-wiki
|
||||
|
||||
sync-wiki:
|
||||
@echo "Syncing wiki to Gitea..."
|
||||
@cp wiki/*.md ../viewer.wiki/
|
||||
@cd ../viewer.wiki && git add . && git commit -m "Sync wiki from main repo" && git push
|
||||
@echo "Wiki synced successfully!"
|
||||
```
|
||||
|
||||
Usage: `make sync-wiki`
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Single Source of Truth**: Choose one location (local or Gitea) as authoritative
|
||||
2. **Version Control**: Keep wiki changes in Git history
|
||||
3. **Review Process**: Use pull requests for significant documentation changes
|
||||
4. **Link Checking**: Regularly verify internal links work
|
||||
5. **Consistent Formatting**: Use markdown linting tools
|
||||
6. **Table of Contents**: Keep `Home.md` as navigation hub
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Wiki Repository Doesn't Exist
|
||||
|
||||
Create the wiki first in Gitea UI:
|
||||
1. Go to repository: https://git.appmodel.nl/Tour/viewer
|
||||
2. Click "Wiki" tab
|
||||
3. Click "Create New Page"
|
||||
4. Create a dummy page (will be overwritten)
|
||||
5. Now you can clone `viewer.wiki.git`
|
||||
|
||||
### Permission Denied
|
||||
|
||||
Ensure your SSH key is added to Gitea:
|
||||
```bash
|
||||
ssh -T git@git.appmodel.nl
|
||||
```
|
||||
|
||||
### Merge Conflicts
|
||||
|
||||
If both local and Gitea wiki are edited:
|
||||
```bash
|
||||
cd viewer.wiki
|
||||
git pull origin master
|
||||
# Resolve conflicts manually
|
||||
git add .
|
||||
git commit -m "Merge local and remote changes"
|
||||
git push
|
||||
```
|
||||
|
||||
## Summary
|
||||
|
||||
**Recommended approach for your setup**:
|
||||
|
||||
1. Keep `wiki/` in main repository for version control
|
||||
2. Manually push to Gitea wiki repository when documentation is ready
|
||||
3. Optionally add automated sync in post-receive hook
|
||||
4. Edit wiki locally, sync to Gitea for visibility
|
||||
|
||||
This gives you the best of both worlds:
|
||||
- ✅ Documentation versioned with code
|
||||
- ✅ Visible wiki in Gitea UI
|
||||
- ✅ Easy to maintain and update
|
||||
- ✅ Works with existing deployment pipeline
|
||||
@@ -1,31 +1,31 @@
|
||||
# Appmodel Home Lab – Build & Deploy Pipeline
|
||||
|
||||
Dit document beschrijft hoe een nieuwe applicatie in het home lab wordt aangemaakt en hoe de build- & deploy-pipeline werkt.
|
||||
|
||||
## Overzicht
|
||||
|
||||
- **Broncode**: Gitea (`https://git.appmodel.nl`)
|
||||
- **Build & runtime**: Docker containers op netwerk `traefik_net`
|
||||
- **Routing & TLS**: Traefik (`https://traefik.appmodel.nl`)
|
||||
- **Automatische deploy**: Gitea `post-receive` hooks → `app-deploy <app>`
|
||||
|
||||
## Pipeline in één diagram
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Dev[💻 Dev machine\nVS Code / Git] -->|git push| Gitea[📚 Gitea\nTour/<app>]
|
||||
|
||||
subgraph Server[🏠 Home lab server\n192.168.1.159]
|
||||
Gitea --> Hook[🔔 post-receive hook\n/app-deploy <app>]
|
||||
|
||||
Hook --> Deploy[⚙️ app-deploy <app>\n/git pull + docker compose up -d --build <app>]
|
||||
|
||||
subgraph Docker[🐳 Docker / traefik_net]
|
||||
AppC[🧱 App container\n<app>.appmodel.nl]
|
||||
Traefik[🚦 Traefik\nReverse Proxy]
|
||||
end
|
||||
end
|
||||
|
||||
Deploy --> AppC
|
||||
Traefik --> AppC
|
||||
Client[🌐 Browser / API client] -->|https://<app>.appmodel.nl| Traefik
|
||||
# Appmodel Home Lab – Build & Deploy Pipeline
|
||||
|
||||
Dit document beschrijft hoe een nieuwe applicatie in het home lab wordt aangemaakt en hoe de build- & deploy-pipeline werkt.
|
||||
|
||||
## Overzicht
|
||||
|
||||
- **Broncode**: Gitea (`https://git.appmodel.nl`)
|
||||
- **Build & runtime**: Docker containers op netwerk `traefik_net`
|
||||
- **Routing & TLS**: Traefik (`https://traefik.appmodel.nl`)
|
||||
- **Automatische deploy**: Gitea `post-receive` hooks → `app-deploy <app>`
|
||||
|
||||
## Pipeline in één diagram
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Dev[💻 Dev machine\nVS Code / Git] -->|git push| Gitea[📚 Gitea\nTour/<app>]
|
||||
|
||||
subgraph Server[🏠 Home lab server\n192.168.1.159]
|
||||
Gitea --> Hook[🔔 post-receive hook\n/app-deploy <app>]
|
||||
|
||||
Hook --> Deploy[⚙️ app-deploy <app>\n/git pull + docker compose up -d --build <app>]
|
||||
|
||||
subgraph Docker[🐳 Docker / traefik_net]
|
||||
AppC[🧱 App container\n<app>.appmodel.nl]
|
||||
Traefik[🚦 Traefik\nReverse Proxy]
|
||||
end
|
||||
end
|
||||
|
||||
Deploy --> AppC
|
||||
Traefik --> AppC
|
||||
Client[🌐 Browser / API client] -->|https://<app>.appmodel.nl| Traefik
|
||||
@@ -1,141 +1,141 @@
|
||||
1. Nieuwe app aanmaken
|
||||
1.1 Gitea repo
|
||||
|
||||
Log in op Gitea: https://git.appmodel.nl
|
||||
|
||||
Maak een nieuwe repo aan, bijvoorbeeld:
|
||||
|
||||
Owner: Tour
|
||||
|
||||
Name: viewer
|
||||
|
||||
1.2 Skeleton genereren op de server
|
||||
|
||||
Op de server:
|
||||
|
||||
apps-create viewer static-fe
|
||||
|
||||
|
||||
Dit doet:
|
||||
|
||||
/opt/apps/viewer klaarzetten (proberen te clonen uit git@git.appmodel.nl:Tour/viewer.git)
|
||||
|
||||
een multi-stage Dockerfile voor een Node-based static frontend aanmaken
|
||||
|
||||
~/infra/viewer/docker-compose.yml aanmaken met:
|
||||
|
||||
service viewer
|
||||
|
||||
koppeling aan traefik_net
|
||||
|
||||
Traefik route: https://viewer.appmodel.nl
|
||||
|
||||
2. Develop & build
|
||||
|
||||
Op je dev machine:
|
||||
|
||||
git clone git@git.appmodel.nl:Tour/viewer.git
|
||||
cd viewer
|
||||
# bouw je app zoals normaal
|
||||
npm install
|
||||
npm run build # output in dist/
|
||||
git add .
|
||||
git commit -m "First version"
|
||||
git push
|
||||
|
||||
|
||||
De Dockerfile verwacht een dist/ map met static files.
|
||||
|
||||
3. Deploy pipeline
|
||||
3.1 app-deploy <app>
|
||||
|
||||
Op de server verzorgt app-deploy de generieke deploy:
|
||||
|
||||
app-deploy viewer
|
||||
|
||||
|
||||
Doet:
|
||||
|
||||
cd /opt/apps/viewer
|
||||
|
||||
git pull --ff-only
|
||||
|
||||
cd /home/tour/infra/viewer
|
||||
|
||||
docker compose up -d --build viewer
|
||||
|
||||
Logs komen in:
|
||||
|
||||
/var/log/app-deploy-viewer.log
|
||||
|
||||
3.2 Automatisch deployen via Gitea hook
|
||||
|
||||
In Gitea (repo Tour/viewer):
|
||||
|
||||
Ga naar Instellingen → Git Hooks
|
||||
|
||||
Kies post-receive
|
||||
|
||||
Gebruik:
|
||||
|
||||
#!/usr/bin/env bash
|
||||
/usr/local/bin/app-deploy viewer
|
||||
|
||||
|
||||
Vanaf nu:
|
||||
|
||||
Elke git push naar Tour/viewer triggert automatisch een deploy.
|
||||
|
||||
4. Traefik & DNS
|
||||
|
||||
Traefik draait in de traefik stack op dezelfde server en beheert:
|
||||
|
||||
git.appmodel.nl
|
||||
|
||||
auction.appmodel.nl
|
||||
|
||||
aupi.appmodel.nl
|
||||
|
||||
… (nieuwe apps via labels)
|
||||
|
||||
Nieuwe app viewer krijgt via apps-create al de juiste labels:
|
||||
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.viewer.rule=Host(`viewer.appmodel.nl`)"
|
||||
- "traefik.http.routers.viewer.entrypoints=websecure"
|
||||
- "traefik.http.routers.viewer.tls=true"
|
||||
- "traefik.http.routers.viewer.tls.certresolver=letsencrypt"
|
||||
- "traefik.http.services.viewer.loadbalancer.server.port=80"
|
||||
|
||||
|
||||
Je moet alleen in DNS nog een record maken:
|
||||
|
||||
viewer.appmodel.nl → publieke IP van de server
|
||||
|
||||
Traefik + Let’s Encrypt regelen het certificaat automatisch.
|
||||
|
||||
5. Nieuwe app types (toekomst)
|
||||
|
||||
Het apps-create script ondersteunt nu:
|
||||
|
||||
static-fe – Node build → Nginx → static frontend
|
||||
|
||||
Later kun je extra types toevoegen, bijvoorbeeld:
|
||||
|
||||
api-py – Python (Flask/FastAPI) API
|
||||
|
||||
worker-py – background worker / crawler
|
||||
|
||||
Door per type een eigen Dockerfile-sjabloon en standaard docker-compose.yml te genereren, wordt een nieuw project neerzetten:
|
||||
|
||||
apps-create stats api-py
|
||||
apps-create crawler worker-py
|
||||
|
||||
|
||||
en blijft de pipeline (app-deploy <naam>) identiek.
|
||||
|
||||
Je kunt nu:
|
||||
|
||||
apps-create viewer static-fe
|
||||
1. Nieuwe app aanmaken
|
||||
1.1 Gitea repo
|
||||
|
||||
Log in op Gitea: https://git.appmodel.nl
|
||||
|
||||
Maak een nieuwe repo aan, bijvoorbeeld:
|
||||
|
||||
Owner: Tour
|
||||
|
||||
Name: viewer
|
||||
|
||||
1.2 Skeleton genereren op de server
|
||||
|
||||
Op de server:
|
||||
|
||||
apps-create viewer static-fe
|
||||
|
||||
|
||||
Dit doet:
|
||||
|
||||
/opt/apps/viewer klaarzetten (proberen te clonen uit git@git.appmodel.nl:Tour/viewer.git)
|
||||
|
||||
een multi-stage Dockerfile voor een Node-based static frontend aanmaken
|
||||
|
||||
~/infra/viewer/docker-compose.yml aanmaken met:
|
||||
|
||||
service viewer
|
||||
|
||||
koppeling aan traefik_net
|
||||
|
||||
Traefik route: https://viewer.appmodel.nl
|
||||
|
||||
2. Develop & build
|
||||
|
||||
Op je dev machine:
|
||||
|
||||
git clone git@git.appmodel.nl:Tour/viewer.git
|
||||
cd viewer
|
||||
# bouw je app zoals normaal
|
||||
npm install
|
||||
npm run build # output in dist/
|
||||
git add .
|
||||
git commit -m "First version"
|
||||
git push
|
||||
|
||||
|
||||
De Dockerfile verwacht een dist/ map met static files.
|
||||
|
||||
3. Deploy pipeline
|
||||
3.1 app-deploy <app>
|
||||
|
||||
Op de server verzorgt app-deploy de generieke deploy:
|
||||
|
||||
app-deploy viewer
|
||||
|
||||
|
||||
Doet:
|
||||
|
||||
cd /opt/apps/viewer
|
||||
|
||||
git pull --ff-only
|
||||
|
||||
cd /home/tour/infra/viewer
|
||||
|
||||
docker compose up -d --build viewer
|
||||
|
||||
Logs komen in:
|
||||
|
||||
/var/log/app-deploy-viewer.log
|
||||
|
||||
3.2 Automatisch deployen via Gitea hook
|
||||
|
||||
In Gitea (repo Tour/viewer):
|
||||
|
||||
Ga naar Instellingen → Git Hooks
|
||||
|
||||
Kies post-receive
|
||||
|
||||
Gebruik:
|
||||
|
||||
#!/usr/bin/env bash
|
||||
/usr/local/bin/app-deploy viewer
|
||||
|
||||
|
||||
Vanaf nu:
|
||||
|
||||
Elke git push naar Tour/viewer triggert automatisch een deploy.
|
||||
|
||||
4. Traefik & DNS
|
||||
|
||||
Traefik draait in de traefik stack op dezelfde server en beheert:
|
||||
|
||||
git.appmodel.nl
|
||||
|
||||
auction.appmodel.nl
|
||||
|
||||
aupi.appmodel.nl
|
||||
|
||||
… (nieuwe apps via labels)
|
||||
|
||||
Nieuwe app viewer krijgt via apps-create al de juiste labels:
|
||||
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.viewer.rule=Host(`viewer.appmodel.nl`)"
|
||||
- "traefik.http.routers.viewer.entrypoints=websecure"
|
||||
- "traefik.http.routers.viewer.tls=true"
|
||||
- "traefik.http.routers.viewer.tls.certresolver=letsencrypt"
|
||||
- "traefik.http.services.viewer.loadbalancer.server.port=80"
|
||||
|
||||
|
||||
Je moet alleen in DNS nog een record maken:
|
||||
|
||||
viewer.appmodel.nl → publieke IP van de server
|
||||
|
||||
Traefik + Let’s Encrypt regelen het certificaat automatisch.
|
||||
|
||||
5. Nieuwe app types (toekomst)
|
||||
|
||||
Het apps-create script ondersteunt nu:
|
||||
|
||||
static-fe – Node build → Nginx → static frontend
|
||||
|
||||
Later kun je extra types toevoegen, bijvoorbeeld:
|
||||
|
||||
api-py – Python (Flask/FastAPI) API
|
||||
|
||||
worker-py – background worker / crawler
|
||||
|
||||
Door per type een eigen Dockerfile-sjabloon en standaard docker-compose.yml te genereren, wordt een nieuw project neerzetten:
|
||||
|
||||
apps-create stats api-py
|
||||
apps-create crawler worker-py
|
||||
|
||||
|
||||
en blijft de pipeline (app-deploy <naam>) identiek.
|
||||
|
||||
Je kunt nu:
|
||||
|
||||
apps-create viewer static-fe
|
||||
app-deploy viewer
|
||||
378
wiki2/Home.md
378
wiki2/Home.md
@@ -1,378 +0,0 @@
|
||||
# Diagram Viewer Wiki
|
||||
|
||||
Welcome to the **Diagram Viewer** project documentation.
|
||||
|
||||
This project provides a static web interface for viewing and managing network architecture diagrams generated with Python's `diagrams` library.
|
||||
|
||||
## 📚 Contents
|
||||
|
||||
- [Getting Started](#getting-started)
|
||||
- [Development Guide](#development-guide)
|
||||
- [Deployment Guide](#deployment-guide)
|
||||
- [Architecture Overview](#architecture-overview)
|
||||
|
||||
---
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Python 3.8+
|
||||
- Graphviz
|
||||
- Docker (for deployment)
|
||||
|
||||
### Quick Start
|
||||
|
||||
```bash
|
||||
# Clone repository
|
||||
git clone git@git.appmodel.nl:Tour/diagram.git
|
||||
cd diagram
|
||||
|
||||
# Create virtual environment
|
||||
python -m venv .venv
|
||||
source .venv/bin/activate # Windows: .venv\Scripts\activate
|
||||
|
||||
# Install dependencies
|
||||
pip install -r requirements.txt
|
||||
|
||||
# Generate diagrams
|
||||
python lan_architecture.py
|
||||
python main.py
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Development Guide
|
||||
|
||||
### Project Structure
|
||||
|
||||
```
|
||||
diagram/
|
||||
├── Dockerfile # Multi-stage build for production
|
||||
├── docker-compose.yml # Docker Compose configuration
|
||||
├── requirements.txt # Python dependencies
|
||||
├── lan_architecture.py # LAN architecture diagram generator
|
||||
├── main.py # Main diagram generator
|
||||
├── public/
|
||||
│ └── index.html # Live Mermaid diagram editor
|
||||
└── wiki/ # Documentation (this wiki)
|
||||
```
|
||||
|
||||
### Adding New Diagrams
|
||||
|
||||
1. Create a new Python file (e.g., `network_diagram.py`)
|
||||
2. Import required components from `diagrams` library
|
||||
3. Define your architecture using context managers
|
||||
4. Run the script to generate PNG output
|
||||
|
||||
Example:
|
||||
```python
|
||||
from diagrams import Diagram, Cluster
|
||||
from diagrams.onprem.network import Router
|
||||
from diagrams.generic.device import Mobile
|
||||
|
||||
with Diagram("My Network", show=False, filename="my_network"):
|
||||
router = Router("Gateway")
|
||||
device = Mobile("Phone")
|
||||
device >> router
|
||||
```
|
||||
|
||||
### Testing Locally
|
||||
|
||||
```bash
|
||||
# Generate diagrams
|
||||
python lan_architecture.py
|
||||
|
||||
# View output
|
||||
ls -la *.png
|
||||
|
||||
# Test with local web server
|
||||
python -m http.server 8000 --directory public/
|
||||
# Open: http://localhost:8000
|
||||
```
|
||||
|
||||
### Available Icons
|
||||
|
||||
The `diagrams` library provides icons from multiple providers:
|
||||
- **OnPrem**: Network, compute, storage, etc.
|
||||
- **Cloud**: AWS, Azure, GCP services
|
||||
- **Generic**: Network devices, blank nodes
|
||||
- **Custom**: Use your own images
|
||||
|
||||
See: [Diagrams Documentation](https://diagrams.mingrammer.com/docs/nodes/overview)
|
||||
|
||||
---
|
||||
|
||||
## Deployment Guide
|
||||
|
||||
### Overview
|
||||
|
||||
The project uses a fully automated Docker-based deployment pipeline:
|
||||
|
||||
1. **Develop** on local machine
|
||||
2. **Commit & Push** to Gitea
|
||||
3. **Auto-Deploy** via post-receive hook
|
||||
4. **Build** multi-stage Docker image
|
||||
5. **Serve** via Traefik reverse proxy
|
||||
|
||||
### Deployment Steps
|
||||
|
||||
#### 1. Initial Setup
|
||||
|
||||
```bash
|
||||
# On server: Create app structure
|
||||
apps-create diagram static-fe
|
||||
|
||||
# This creates:
|
||||
# - /opt/apps/diagram (git repository)
|
||||
# - /home/tour/infra/diagram/docker-compose.yml
|
||||
```
|
||||
|
||||
#### 2. Configure Gitea Hook
|
||||
|
||||
In repository **Settings → Git Hooks → post-receive**:
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
/usr/local/bin/app-deploy diagram
|
||||
```
|
||||
|
||||
#### 3. Deploy
|
||||
|
||||
Simply push to Gitea:
|
||||
|
||||
```bash
|
||||
git push origin main
|
||||
```
|
||||
|
||||
The server automatically:
|
||||
- Pulls latest code
|
||||
- Rebuilds Docker image
|
||||
- Restarts container
|
||||
- Updates live site at https://diagram.appmodel.nl
|
||||
|
||||
### Manual Deployment
|
||||
|
||||
If needed:
|
||||
|
||||
```bash
|
||||
# On server
|
||||
app-deploy diagram
|
||||
|
||||
# Or step-by-step
|
||||
cd /opt/apps/diagram
|
||||
git pull
|
||||
cd /home/tour/infra/diagram
|
||||
docker compose up -d --build diagram
|
||||
```
|
||||
|
||||
### Monitoring
|
||||
|
||||
```bash
|
||||
# View deployment logs
|
||||
tail -f /var/log/app-deploy-diagram.log
|
||||
|
||||
# View container logs
|
||||
cd /home/tour/infra/diagram
|
||||
docker compose logs -f diagram
|
||||
|
||||
# Check container status
|
||||
docker compose ps
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
### Infrastructure
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ Production Architecture │
|
||||
├─────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌──────────────┐ │
|
||||
│ │ Gitea │ (Source of Truth) │
|
||||
│ │ Tour/diagram │ │
|
||||
│ └──────┬───────┘ │
|
||||
│ │ git push │
|
||||
│ ↓ │
|
||||
│ ┌──────────────┐ │
|
||||
│ │ Post-Receive │ (Auto-trigger) │
|
||||
│ │ Hook │ │
|
||||
│ └──────┬───────┘ │
|
||||
│ │ app-deploy diagram │
|
||||
│ ↓ │
|
||||
│ ┌──────────────────────┐ │
|
||||
│ │ /opt/apps/diagram/ │ │
|
||||
│ │ (Git Repository) │ │
|
||||
│ └──────┬───────────────┘ │
|
||||
│ │ git pull │
|
||||
│ ↓ │
|
||||
│ ┌──────────────────────┐ │
|
||||
│ │ Docker Build Process │ │
|
||||
│ │ ┌─────────────────┐ │ │
|
||||
│ │ │ Stage 1: Build │ │ │
|
||||
│ │ │ - Python │ │ │
|
||||
│ │ │ - Graphviz │ │ │
|
||||
│ │ │ - Generate PNGs │ │ │
|
||||
│ │ └─────────────────┘ │ │
|
||||
│ │ ┌─────────────────┐ │ │
|
||||
│ │ │ Stage 2: Serve │ │ │
|
||||
│ │ │ - Nginx │ │ │
|
||||
│ │ │ - Static files │ │ │
|
||||
│ │ └─────────────────┘ │ │
|
||||
│ └──────┬───────────────┘ │
|
||||
│ │ container starts │
|
||||
│ ↓ │
|
||||
│ ┌──────────────────────┐ │
|
||||
│ │ diagram-viewer │ │
|
||||
│ │ Container :80 │ │
|
||||
│ └──────┬───────────────┘ │
|
||||
│ │ traefik_net │
|
||||
│ ↓ │
|
||||
│ ┌──────────────────────┐ │
|
||||
│ │ Traefik │ │
|
||||
│ │ Reverse Proxy │ │
|
||||
│ │ + Let's Encrypt │ │
|
||||
│ └──────┬───────────────┘ │
|
||||
│ │ HTTPS │
|
||||
│ ↓ │
|
||||
│ diagram.appmodel.nl │
|
||||
│ │
|
||||
└─────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Network Topology
|
||||
|
||||
The diagram viewer documents our home lab infrastructure:
|
||||
|
||||
- **LAN 192.168.1.0/24**: Main network
|
||||
- Core server (192.168.1.159): Traefik, Gitea, Dokku, Auction stack, MI50/Ollama
|
||||
- DNS server (192.168.1.163): AdGuard, Artifactory, CI runner
|
||||
- Home Assistant (192.168.1.193)
|
||||
- Atlas worker (192.168.1.100)
|
||||
- IoT devices
|
||||
|
||||
- **Tether 192.168.137.0/24**: Isolated subnet
|
||||
- Hermes worker (192.168.137.239)
|
||||
- Plato worker (192.168.137.163)
|
||||
|
||||
### Services
|
||||
|
||||
| Service | URL | Description |
|
||||
|---------|-----|-------------|
|
||||
| Diagram Viewer | https://diagram.appmodel.nl | This application |
|
||||
| Gitea | https://git.appmodel.nl | Git hosting |
|
||||
| Auction Frontend | https://auction.appmodel.nl | Auction platform |
|
||||
| Aupi API | https://aupi.appmodel.nl | Backend API |
|
||||
| Dokku | https://dokku.lan | PaaS platform |
|
||||
|
||||
---
|
||||
|
||||
## Additional Resources
|
||||
|
||||
### Related Documentation
|
||||
|
||||
- [DEPLOY_SERVER_SETUP.md](../DEPLOY_SERVER_SETUP.md) - Detailed deployment guide
|
||||
- [DEPLOYMENT_GUIDE.md](../DEPLOYMENT_GUIDE.md) - General deployment pipeline
|
||||
- [README.md](../README.md) - Project overview
|
||||
|
||||
### External Links
|
||||
|
||||
- [Diagrams Library](https://diagrams.mingrammer.com/)
|
||||
- [Graphviz](https://graphviz.org/)
|
||||
- [Mermaid.js](https://mermaid.js.org/) (used in live editor)
|
||||
- [Traefik Documentation](https://doc.traefik.io/traefik/)
|
||||
- [Docker Compose](https://docs.docker.com/compose/)
|
||||
|
||||
### Contributing
|
||||
|
||||
To contribute to this project:
|
||||
|
||||
1. Create a feature branch
|
||||
2. Make your changes
|
||||
3. Test locally
|
||||
4. Submit a pull request in Gitea
|
||||
5. Wait for automatic deployment after merge
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
#### Diagrams Not Generating
|
||||
|
||||
**Problem**: Python script fails to generate PNG files
|
||||
|
||||
**Solution**:
|
||||
```bash
|
||||
# Check Graphviz installation
|
||||
dot -V
|
||||
|
||||
# Reinstall if needed
|
||||
# macOS: brew install graphviz
|
||||
# Ubuntu: sudo apt-get install graphviz
|
||||
# Windows: choco install graphviz
|
||||
|
||||
# Verify Python dependencies
|
||||
pip list | grep diagrams
|
||||
pip install --upgrade diagrams
|
||||
```
|
||||
|
||||
#### Container Build Fails
|
||||
|
||||
**Problem**: Docker build fails during deployment
|
||||
|
||||
**Solution**:
|
||||
```bash
|
||||
# Check deployment logs
|
||||
tail -f /var/log/app-deploy-diagram.log
|
||||
|
||||
# Rebuild manually with verbose output
|
||||
cd /home/tour/infra/diagram
|
||||
docker compose build --no-cache --progress=plain diagram
|
||||
```
|
||||
|
||||
#### Site Not Accessible
|
||||
|
||||
**Problem**: Container runs but site not reachable
|
||||
|
||||
**Solution**:
|
||||
1. Check DNS: `nslookup diagram.appmodel.nl`
|
||||
2. Verify Traefik labels: `docker inspect diagram-viewer | grep traefik`
|
||||
3. Check Traefik logs: `docker logs traefik`
|
||||
4. Test container directly: `curl http://localhost:PORT`
|
||||
|
||||
#### Changes Not Reflected
|
||||
|
||||
**Problem**: Pushed changes but site unchanged
|
||||
|
||||
**Solution**:
|
||||
```bash
|
||||
# Force rebuild on server
|
||||
cd /home/tour/infra/diagram
|
||||
docker compose down
|
||||
docker compose up -d --build --force-recreate diagram
|
||||
|
||||
# Clear browser cache
|
||||
# Ctrl+Shift+R (Windows/Linux) or Cmd+Shift+R (macOS)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Contact & Support
|
||||
|
||||
For issues, questions, or suggestions:
|
||||
|
||||
1. Check this wiki
|
||||
2. Review [DEPLOY_SERVER_SETUP.md](../DEPLOY_SERVER_SETUP.md)
|
||||
3. Check container logs
|
||||
4. Contact the infrastructure team
|
||||
|
||||
---
|
||||
|
||||
**Last Updated**: 2025-12-02
|
||||
**Version**: 1.0.0
|
||||
**Maintainer**: Tour
|
||||
@@ -1,239 +0,0 @@
|
||||
# Quick Start Guide
|
||||
|
||||
Get up and running with the Diagram Viewer in minutes.
|
||||
|
||||
## For Developers
|
||||
|
||||
### 1. Clone Repository
|
||||
|
||||
```bash
|
||||
git clone git@git.appmodel.nl:Tour/diagram.git
|
||||
cd diagram
|
||||
```
|
||||
|
||||
### 2. Setup Environment
|
||||
|
||||
```bash
|
||||
# Create virtual environment
|
||||
python -m venv .venv
|
||||
|
||||
# Activate (choose your platform)
|
||||
source .venv/bin/activate # macOS/Linux
|
||||
.venv\Scripts\activate # Windows CMD
|
||||
source .venv/Scripts/activate # Windows Git Bash
|
||||
```
|
||||
|
||||
### 3. Install Dependencies
|
||||
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
### 4. Generate Diagrams
|
||||
|
||||
```bash
|
||||
# Generate LAN architecture diagram
|
||||
python lan_architecture.py
|
||||
|
||||
# Generate main diagram
|
||||
python main.py
|
||||
|
||||
# View generated files
|
||||
ls -la *.png
|
||||
```
|
||||
|
||||
### 5. Test Locally
|
||||
|
||||
```bash
|
||||
# Start local web server
|
||||
python -m http.server 8000 --directory public/
|
||||
|
||||
# Open browser
|
||||
# Navigate to: http://localhost:8000
|
||||
```
|
||||
|
||||
### 6. Make Changes & Deploy
|
||||
|
||||
```bash
|
||||
# Edit diagram code
|
||||
vim lan_architecture.py
|
||||
|
||||
# Commit changes
|
||||
git add .
|
||||
git commit -m "Update network diagram"
|
||||
|
||||
# Push to deploy (auto-deploys to production)
|
||||
git push origin main
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## For Operators
|
||||
|
||||
### View Live Site
|
||||
|
||||
Simply navigate to:
|
||||
```
|
||||
https://diagram.appmodel.nl
|
||||
```
|
||||
|
||||
### Manual Deployment
|
||||
|
||||
On the server:
|
||||
|
||||
```bash
|
||||
# Trigger deployment
|
||||
app-deploy diagram
|
||||
|
||||
# View logs
|
||||
tail -f /var/log/app-deploy-diagram.log
|
||||
|
||||
# Check status
|
||||
cd /home/tour/infra/diagram
|
||||
docker compose ps
|
||||
```
|
||||
|
||||
### View Logs
|
||||
|
||||
```bash
|
||||
# Deployment logs
|
||||
tail -f /var/log/app-deploy-diagram.log
|
||||
|
||||
# Container logs
|
||||
cd /home/tour/infra/diagram
|
||||
docker compose logs -f diagram
|
||||
|
||||
# Last 100 lines
|
||||
docker compose logs --tail=100 diagram
|
||||
```
|
||||
|
||||
### Restart Service
|
||||
|
||||
```bash
|
||||
cd /home/tour/infra/diagram
|
||||
|
||||
# Restart container
|
||||
docker compose restart diagram
|
||||
|
||||
# Full restart (rebuild)
|
||||
docker compose down
|
||||
docker compose up -d --build diagram
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Common Tasks
|
||||
|
||||
### Add New Diagram
|
||||
|
||||
1. Create new Python file:
|
||||
```python
|
||||
# my_diagram.py
|
||||
from diagrams import Diagram
|
||||
from diagrams.onprem.network import Router
|
||||
|
||||
with Diagram("My Network", show=False, filename="my_network"):
|
||||
Router("Gateway")
|
||||
```
|
||||
|
||||
2. Update Dockerfile if needed (add to RUN command):
|
||||
```dockerfile
|
||||
RUN python lan_architecture.py && \
|
||||
python main.py && \
|
||||
python my_diagram.py
|
||||
```
|
||||
|
||||
3. Commit and push:
|
||||
```bash
|
||||
git add my_diagram.py Dockerfile
|
||||
git commit -m "Add my network diagram"
|
||||
git push
|
||||
```
|
||||
|
||||
### Update Frontend
|
||||
|
||||
1. Edit HTML:
|
||||
```bash
|
||||
vim public/index.html
|
||||
```
|
||||
|
||||
2. Test locally:
|
||||
```bash
|
||||
python -m http.server 8000 --directory public/
|
||||
```
|
||||
|
||||
3. Deploy:
|
||||
```bash
|
||||
git add public/index.html
|
||||
git commit -m "Update frontend"
|
||||
git push
|
||||
```
|
||||
|
||||
### Check Container Health
|
||||
|
||||
```bash
|
||||
# Container status
|
||||
docker compose ps
|
||||
|
||||
# Health check
|
||||
docker inspect diagram-viewer | grep -A 10 Health
|
||||
|
||||
# Test endpoint
|
||||
curl -I https://diagram.appmodel.nl
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Build Fails
|
||||
|
||||
```bash
|
||||
# Check logs
|
||||
tail -f /var/log/app-deploy-diagram.log
|
||||
|
||||
# Rebuild with verbose output
|
||||
cd /home/tour/infra/diagram
|
||||
docker compose build --no-cache --progress=plain diagram
|
||||
```
|
||||
|
||||
### Site Not Loading
|
||||
|
||||
```bash
|
||||
# Check container
|
||||
docker compose ps
|
||||
|
||||
# Check Traefik routing
|
||||
docker logs traefik | grep diagram
|
||||
|
||||
# Test DNS
|
||||
nslookup diagram.appmodel.nl
|
||||
|
||||
# Test directly
|
||||
curl http://localhost:PORT
|
||||
```
|
||||
|
||||
### Changes Not Visible
|
||||
|
||||
```bash
|
||||
# Force rebuild
|
||||
cd /home/tour/infra/diagram
|
||||
docker compose up -d --build --force-recreate diagram
|
||||
|
||||
# Clear browser cache
|
||||
# Ctrl+Shift+R (Windows/Linux)
|
||||
# Cmd+Shift+R (macOS)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
- Read the [Home](Home.md) page for comprehensive documentation
|
||||
- Review [DEPLOY_SERVER_SETUP.md](../DEPLOY_SERVER_SETUP.md) for deployment details
|
||||
- Check [Diagrams Library docs](https://diagrams.mingrammer.com/) for icon options
|
||||
- Explore the [Mermaid editor](https://diagram.appmodel.nl) for live diagram editing
|
||||
|
||||
---
|
||||
|
||||
**Need Help?** Check the [Troubleshooting](Home.md#troubleshooting) section or review deployment logs.
|
||||
Reference in New Issue
Block a user