commit 067abd1280843966a2932308fcd3377c3b0d3aec Author: Tour Date: Mon Dec 8 09:08:43 2025 +0100 init diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..ecf3432 --- /dev/null +++ b/.dockerignore @@ -0,0 +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 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6507b46 --- /dev/null +++ b/.gitignore @@ -0,0 +1,32 @@ +# 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/ \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..38a579d --- /dev/null +++ b/Dockerfile @@ -0,0 +1,23 @@ +# -- 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 \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..26b1a2a --- /dev/null +++ b/README.md @@ -0,0 +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 diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..a4fd651 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,26 @@ +services: + nex: + build: + context: /opt/apps/nex + dockerfile: Dockerfile + container_name: nex + restart: unless-stopped + networks: + - traefik_net + labels: + - "traefik.enable=true" + - "traefik.http.routers.nex.rule=Host(`nex.appmodel.nl`)" + - "traefik.http.routers.nex.entrypoints=websecure" + - "traefik.http.routers.nex.tls=true" + - "traefik.http.services.nex.loadbalancer.server.port=80" + - "traefik.http.routers.nex-http.rule=Host(`nex.appmodel.nl`)" + - "traefik.http.routers.nex-http.entrypoints=web" + - "traefik.http.routers.nex-http.middlewares=nex-https" + - "traefik.http.middlewares.nex-https.redirectscheme.scheme=https" + - "traefik.http.routers.auction.tls.certresolver=letsencrypt", + - "traefik.http.middlewares.nex-https.redirectscheme.permanent=true" + +networks: + traefik_net: + external: true + name: traefik_net diff --git a/docs/Home.md b/docs/Home.md new file mode 100644 index 0000000..bde64f4 --- /dev/null +++ b/docs/Home.md @@ -0,0 +1,378 @@ +# Diagram Nex Wiki + +Welcome to the **Diagram Nex** 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/nex.git +cd nex + +# 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 + +``` +nex/ +├── 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 nex static-fe + +# This creates: +# - /opt/apps/nex (git repository) +# - /home/tour/infra/nex/docker-compose.yml +``` + +#### 2. Configure Gitea Hook + +In repository **Settings → Git Hooks → post-receive**: + +```bash +#!/usr/bin/env bash +/usr/local/bin/app-deploy nex +``` + +#### 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://nex.appmodel.nl + +### Manual Deployment + +If needed: + +```bash +# On server +app-deploy nex + +# Or step-by-step +cd /opt/apps/nex +git pull +cd /home/tour/infra/nex +docker compose up -d --build nex +``` + +### Monitoring + +```bash +# View deployment logs +tail -f /var/log/app-deploy-nex.log + +# View container logs +cd /home/tour/infra/nex +docker compose logs -f nex + +# Check container status +docker compose ps +``` + +--- + +## Architecture Overview + +### Infrastructure + +``` +┌─────────────────────────────────────────────┐ +│ Production Architecture │ +├─────────────────────────────────────────────┤ +│ │ +│ ┌──────────────┐ │ +│ │ Gitea │ (Source of Truth) │ +│ │ Tour/nex │ │ +│ └──────â”Ŧ───────┘ │ +│ │ git push │ +│ ↓ │ +│ ┌──────────────┐ │ +│ │ Post-Receive │ (Auto-trigger) │ +│ │ Hook │ │ +│ └──────â”Ŧ───────┘ │ +│ │ app-deploy nex │ +│ ↓ │ +│ ┌──────────────────────┐ │ +│ │ /opt/apps/nex/ │ │ +│ │ (Git Repository) │ │ +│ └──────â”Ŧ───────────────┘ │ +│ │ git pull │ +│ ↓ │ +│ ┌──────────────────────┐ │ +│ │ Docker Build Process │ │ +│ │ ┌─────────────────┐ │ │ +│ │ │ Stage 1: Build │ │ │ +│ │ │ - Python │ │ │ +│ │ │ - Graphviz │ │ │ +│ │ │ - Generate PNGs │ │ │ +│ │ └─────────────────┘ │ │ +│ │ ┌─────────────────┐ │ │ +│ │ │ Stage 2: Serve │ │ │ +│ │ │ - Nginx │ │ │ +│ │ │ - Static files │ │ │ +│ │ └─────────────────┘ │ │ +│ └──────â”Ŧ───────────────┘ │ +│ │ container starts │ +│ ↓ │ +│ ┌──────────────────────┐ │ +│ │ diagram-nex │ │ +│ │ Container :80 │ │ +│ └──────â”Ŧ───────────────┘ │ +│ │ traefik_net │ +│ ↓ │ +│ ┌──────────────────────┐ │ +│ │ Traefik │ │ +│ │ Reverse Proxy │ │ +│ │ + Let's Encrypt │ │ +│ └──────â”Ŧ───────────────┘ │ +│ │ HTTPS │ +│ ↓ │ +│ nex.appmodel.nl │ +│ │ +└─────────────────────────────────────────────┘ +``` + +### Network Topology + +The diagram nex 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 nex | https://nex.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-nex.log + +# Rebuild manually with verbose output +cd /home/tour/infra/nex +docker compose build --no-cache --progress=plain nex +``` + +#### Site Not Accessible + +**Problem**: Container runs but site not reachable + +**Solution**: +1. Check DNS: `nslookup nex.appmodel.nl` +2. Verify Traefik labels: `docker inspect nex-nex | 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/nex +docker compose down +docker compose up -d --build --force-recreate nex + +# 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 \ No newline at end of file diff --git a/docs/build-pipeline.md b/docs/build-pipeline.md new file mode 100644 index 0000000..ff2408d --- /dev/null +++ b/docs/build-pipeline.md @@ -0,0 +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 ` + +## Pipeline in ÊÊn diagram + +```mermaid +flowchart LR + Dev[đŸ’ģ Dev machine\nVS Code / Git] -->|git push| Gitea[📚 Gitea\nTour/] + + subgraph Server[🏠 Home lab server\n192.168.1.159] + Gitea --> Hook[🔔 post-receive hook\n/app-deploy ] + + Hook --> Deploy[âš™ī¸ app-deploy \n/git pull + docker compose up -d --build ] + + subgraph Docker[đŸŗ Docker / traefik_net] + AppC[🧱 App container\n.appmodel.nl] + Traefik[đŸšĻ Traefik\nReverse Proxy] + end + end + + Deploy --> AppC + Traefik --> AppC + Client[🌐 Browser / API client] -->|https://.appmodel.nl| Traefik diff --git a/docs/deployment.md b/docs/deployment.md new file mode 100644 index 0000000..bdf65f2 --- /dev/null +++ b/docs/deployment.md @@ -0,0 +1,85 @@ +```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 +``` \ No newline at end of file diff --git a/docs/integration.md b/docs/integration.md new file mode 100644 index 0000000..32d1414 --- /dev/null +++ b/docs/integration.md @@ -0,0 +1,94 @@ +```mermaid +flowchart TD +subgraph P1["PHASE 1: EXTERNAL SCRAPER (Python/Playwright)"] +direction LR +A1[Listing Pages
/auctions?page=N] --> A2[Extract URLs] +B1[Auction Pages
/a/auction-id] --> B2[Parse __NEXT_DATA__ JSON] +C1[Lot Pages
/l/lot-id] --> C2[Parse __NEXT_DATA__ JSON] + + A2 --> D1[INSERT auctions to SQLite] + B2 --> D1 + C2 --> D2[INSERT lots & image URLs] + + D1 --> DB[(SQLite Database
output/cache.db)] + D2 --> DB + end + + DB --> P2_Entry + + subgraph P2["PHASE 2: MONITORING & PROCESSING (Java)"] + direction TB + P2_Entry[Data Ready] --> Monitor[TroostwijkMonitor
Read lots every hour] + P2_Entry --> DBService[DatabaseService
Query & Import] + P2_Entry --> Adapter[ScraperDataAdapter
Transform TEXT → INTEGER] + + Monitor --> BM[Bid Monitoring
Check API every 1h] + DBService --> IP[Image Processing
Download & Analyze] + Adapter --> DataForNotify[Formatted Data] + + BM --> BidUpdate{New bid?} + BidUpdate -->|Yes| UpdateDB[Update current_bid in DB] + UpdateDB --> NotifyTrigger1 + + IP --> Detection[Object Detection
YOLO/OpenCV DNN] + Detection --> ObjectCheck{Detect objects?} + ObjectCheck -->|Vehicle| Save1[Save labels & estimate value] + ObjectCheck -->|Furniture| Save2[Save labels & estimate value] + ObjectCheck -->|Machinery| Save3[Save labels & estimate value] + Save1 --> NotifyTrigger2 + Save2 --> NotifyTrigger2 + Save3 --> NotifyTrigger2 + + CA[Closing Alerts
Check < 5 min] --> TimeCheck{Time critical?} + TimeCheck -->|Yes| NotifyTrigger3 + end + + NotifyTrigger1 --> NS + NotifyTrigger2 --> NS + NotifyTrigger3 --> NS + + subgraph P3["PHASE 3: NOTIFICATION SYSTEM"] + NS[NotificationService] --> DN[Desktop Notify
Windows/macOS/Linux] + NS --> EN[Email Notify
Gmail SMTP] + NS --> PL[Set Priority Level
0=Normal, 1=High] + end + + DN --> UI[User Interaction & Decisions] + EN --> UI + PL --> UI + + subgraph UI_Details[User Decision Points / Trigger Events] + direction LR + E1["1. BID CHANGE
'Nieuw bod op kavel 12345...'
Actions: Place bid? Monitor? Ignore?"] + E2["2. OBJECT DETECTED
'Lot contains: Vehicle...'
Actions: Review? Confirm value?"] + E3["3. CLOSING ALERT
'Kavel 12345 sluit binnen 5 min.'
Actions: Place final bid? Let expire?"] + E4["4. VIEWING DAY QUESTIONS
'Bezichtiging op [date]...'"] + E5["5. ITEM RECOGNITION CONFIRMATION
'Detected: [object]...'"] + E6["6. VALUE ESTIMATE APPROVAL
'Geschatte waarde: â‚ŦX...'"] + E7["7. EXCEPTION HANDLING
'Afwijkende sluitingstijd...'"] + end + + UI --> UI_Details + + %% Object Detection Sub-Flow Detail + subgraph P2_Detail["Object Detection & Value Estimation Pipeline"] + direction LR + DI[Downloaded Image] --> IPS[ImageProcessingService] + IPS --> ODS[ObjectDetectionService] + ODS --> Load[Load YOLO model] + ODS --> Run[Run inference] + ODS --> Post[Post-process detections
confidence > 0.5] + Post --> ObjList["Detected Objects List
(80 COCO classes)"] + ObjList --> VEL[Value Estimation Logic
Future enhancement] + VEL --> Match[Match to categories] + VEL --> History[Historical price analysis] + VEL --> Condition[Condition assessment] + VEL --> Market[Market trends] + Market --> ValueEst["Estimated Value Range
Confidence: 75%"] + ValueEst --> SaveToDB[Save to Database] + SaveToDB --> TriggerNotify{Value > threshold?} + end + + IP -.-> P2_Detail + TriggerNotify -.-> NotifyTrigger2 +``` \ No newline at end of file diff --git a/public/deployment.html b/public/deployment.html new file mode 100644 index 0000000..8e6c317 --- /dev/null +++ b/public/deployment.html @@ -0,0 +1,282 @@ + + + + + +Home-Lab diagram – live editor + + + + + +
+ + Home-Lab diagram – live Mermaid editor + + + + +
+ +
+
+

Source (edit here)

+ +
+
+

Live preview

+
+
+
+
+ + + + + diff --git a/public/dia-kimi.html b/public/dia-kimi.html new file mode 100644 index 0000000..58ea8ad --- /dev/null +++ b/public/dia-kimi.html @@ -0,0 +1,218 @@ + + + + +Home-Lab nex – Mermaid + Kroki (Graphviz) + + + + + + + + + + + + + + +
🏠 Home-Lab nex – editable in browser (localStorage auto-saved)
+ +
+ +
+

Source (Mermaid syntax)

+
+
+ + + + +
+
+ + +
+

Mermaid render

+
+
+ + +
+

Kroki (Graphviz) render

+
+
+
+ + + + \ No newline at end of file diff --git a/public/dia.html b/public/dia.html new file mode 100644 index 0000000..3626ad4 --- /dev/null +++ b/public/dia.html @@ -0,0 +1,107 @@ + + + + + Netwerk architectuur + + + + + +

Netwerk architectuur

+ +
+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 + +
+ + + diff --git a/public/img/favicon.svg b/public/img/favicon.svg new file mode 100644 index 0000000..35a350c --- /dev/null +++ b/public/img/favicon.svg @@ -0,0 +1,7 @@ + + + + + + AUCTION + diff --git a/public/img/home.png b/public/img/home.png new file mode 100644 index 0000000..107a367 Binary files /dev/null and b/public/img/home.png differ diff --git a/public/img/home_lab_architecture.png b/public/img/home_lab_architecture.png new file mode 100644 index 0000000..d81688d Binary files /dev/null and b/public/img/home_lab_architecture.png differ diff --git a/public/img/img.png b/public/img/img.png new file mode 100644 index 0000000..9dc345e Binary files /dev/null and b/public/img/img.png differ diff --git a/public/img/network-architecture.gv.png b/public/img/network-architecture.gv.png new file mode 100644 index 0000000..19bce83 Binary files /dev/null and b/public/img/network-architecture.gv.png differ diff --git a/public/index.html b/public/index.html new file mode 100644 index 0000000..bc41795 --- /dev/null +++ b/public/index.html @@ -0,0 +1,266 @@ + + + + + +Home-Lab diagram – live editor + + + + + +
+ + Home-Lab diagram – live Mermaid editor + + + + +
+ +
+
+

Source (edit here)

+ +
+
+

Live preview

+
+
+
+
+ + + + + diff --git a/public/preview.html b/public/preview.html new file mode 100644 index 0000000..cdd3ef3 --- /dev/null +++ b/public/preview.html @@ -0,0 +1,283 @@ + + + + +Home-Lab diagram – live editor + + + + + + +
+ + Home-Lab diagram – live Mermaid editor + + + + +
+ +
+
+

Source (edit here)

+ +
+
+

Live preview

+
+
+
+
+ + + + \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..502f6b8 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +diagrams==0.25.1 +graphviz==0.20.3 diff --git a/resources/network_discovery_20251207_160645.json b/resources/network_discovery_20251207_160645.json new file mode 100644 index 0000000..569e32e --- /dev/null +++ b/resources/network_discovery_20251207_160645.json @@ -0,0 +1,780 @@ +{ + "discovery_time": "2025-12-07T16:06:45.539721", + "total_devices": 76, + "networks_scanned": [ + "192.168.1.0/24", + "192.168.137.1/24", + "172.20.240.1/20", + "192.168.137.0/24", + "192.168.1.100/24", + "192.168.137.100/24" + ], + "devices": [ + { + "ip": "192.168.1.1", + "hostname": "192.168.1.1", + "mac": "b8:d5:26:d2:7d:c0", + "open_ports": [ + 5000 + ], + "type": "router", + "icon": "\ud83d\udedc", + "last_seen": "2025-12-07T15:52:09.705400" + }, + { + "ip": "192.168.1.10", + "hostname": "192.168.1.10", + "mac": "40:9f:38:1c:1f:01", + "open_ports": [], + "type": "unknown", + "icon": "\u2753", + "last_seen": "2025-12-07T15:52:09.705400" + }, + { + "ip": "192.168.1.49", + "hostname": "192.168.1.49", + "mac": "ec:b5:fa:a5:6c:90", + "open_ports": [ + 443, + 80, + 8080 + ], + "type": "server", + "icon": "\ud83d\udcbb", + "last_seen": "2025-12-07T15:52:22.062446" + }, + { + "ip": "192.168.1.44", + "hostname": "192.168.1.44", + "mac": "48:27:e2:d4:d9:54", + "open_ports": [], + "type": "unknown", + "icon": "\u2753", + "last_seen": "2025-12-07T15:52:22.157183" + }, + { + "ip": "192.168.1.59", + "hostname": "192.168.1.59", + "mac": "04:17:b6:82:05:1a", + "open_ports": [], + "type": "unknown", + "icon": "\u2753", + "last_seen": "2025-12-07T15:52:24.079670" + }, + { + "ip": "192.168.1.65", + "hostname": "192.168.1.65", + "mac": "ea:e7:a7:88:43:e1", + "open_ports": [], + "type": "unknown", + "icon": "\u2753", + "last_seen": "2025-12-07T15:52:34.995741" + }, + { + "ip": "192.168.1.100", + "hostname": "host.docker.internal", + "mac": null, + "open_ports": [ + 22 + ], + "type": "worker", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T15:52:36.610550" + }, + { + "ip": "192.168.1.102", + "hostname": "192.168.1.102", + "mac": "cc:f4:11:cd:76:50", + "open_ports": [ + 8008, + 8443 + ], + "type": "unknown", + "icon": "\u2753", + "last_seen": "2025-12-07T15:52:47.145897" + }, + { + "ip": "192.168.1.136", + "hostname": "192.168.1.136", + "mac": "b0:4a:39:58:ed:6a", + "open_ports": [], + "type": "unknown", + "icon": "\u2753", + "last_seen": "2025-12-07T15:52:58.676214" + }, + { + "ip": "192.168.1.159", + "hostname": "git.appmodel.nl", + "mac": "50:eb:f6:2a:98:30", + "open_ports": [ + 80, + 8080, + 443, + 22, + 3000 + ], + "type": "server", + "icon": "\ud83d\udcbb", + "last_seen": "2025-12-07T15:53:00.598585" + }, + { + "ip": "192.168.1.151", + "hostname": "192.168.1.151", + "mac": "34:cd:b0:2b:d9:a0", + "open_ports": [], + "type": "unknown", + "icon": "\u2753", + "last_seen": "2025-12-07T15:53:10.286185" + }, + { + "ip": "192.168.1.163", + "hostname": "192.168.1.163", + "mac": "00:1e:06:31:b1:27", + "open_ports": [ + 22, + 8000, + 8080 + ], + "type": "dns", + "icon": "\ud83d\udee1\ufe0f", + "last_seen": "2025-12-07T15:53:31.910265" + }, + { + "ip": "192.168.1.165", + "hostname": "HP678531.home", + "mac": "48:ba:4e:67:85:32", + "open_ports": [ + 80, + 443, + 8080 + ], + "type": "server", + "icon": "\ud83d\udcbb", + "last_seen": "2025-12-07T15:53:32.065413" + }, + { + "ip": "192.168.1.193", + "hostname": "homeassistant", + "mac": "e4:b9:7a:9f:3b:5f", + "open_ports": [], + "type": "home_automation", + "icon": "\ud83c\udfe1", + "last_seen": "2025-12-07T15:53:54.643647" + }, + { + "ip": "192.168.1.206", + "hostname": "192.168.1.206", + "mac": "7c:df:a1:54:d4:fc", + "open_ports": [], + "type": "unknown", + "icon": "\u2753", + "last_seen": "2025-12-07T15:54:16.332734" + }, + { + "ip": "192.168.1.214", + "hostname": "192.168.1.214", + "mac": "10:7b:44:30:a0:b9", + "open_ports": [], + "type": "unknown", + "icon": "\u2753", + "last_seen": "2025-12-07T15:54:17.164333" + }, + { + "ip": "192.168.1.240", + "hostname": "192.168.1.240", + "mac": "4c:49:29:7d:bc:e4", + "open_ports": [ + 8443, + 8008 + ], + "type": "unknown", + "icon": "\u2753", + "last_seen": "2025-12-07T15:54:39.613611" + }, + { + "ip": "192.168.1.243", + "hostname": "192.168.1.243", + "mac": "ac:67:84:61:90:d9", + "open_ports": [ + 8443, + 8008 + ], + "type": "unknown", + "icon": "\u2753", + "last_seen": "2025-12-07T15:55:01.218760" + }, + { + "ip": "192.168.137.1", + "hostname": "MI", + "mac": null, + "open_ports": [ + 22 + ], + "type": "worker", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T15:55:02.284598" + }, + { + "ip": "192.168.137.100", + "hostname": "MI", + "mac": null, + "open_ports": [ + 22 + ], + "type": "worker", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T15:55:11.113935" + }, + { + "ip": "192.168.137.239", + "hostname": "192.168.137.239", + "mac": "18:31:bf:4e:d7:8a", + "open_ports": [ + 22 + ], + "type": "worker", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T15:55:41.136168" + }, + { + "ip": "172.20.240.1", + "hostname": "MI.mshome.net", + "mac": null, + "open_ports": [ + 22 + ], + "type": "unknown", + "icon": "\u2753", + "last_seen": "2025-12-07T15:55:46.119130" + }, + { + "ip": "192.168.137.1", + "hostname": "MI", + "mac": null, + "open_ports": [ + 22 + ], + "type": "worker", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T15:55:49.126499" + }, + { + "ip": "192.168.137.3", + "hostname": "192.168.137.3", + "mac": null, + "open_ports": [], + "type": "worker", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T15:56:09.203344" + }, + { + "ip": "192.168.137.13", + "hostname": "192.168.137.13", + "mac": null, + "open_ports": [], + "type": "worker", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T15:56:10.560591" + }, + { + "ip": "192.168.137.35", + "hostname": "192.168.137.35", + "mac": null, + "open_ports": [], + "type": "worker", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T15:56:32.787426" + }, + { + "ip": "192.168.137.61", + "hostname": "192.168.137.61", + "mac": null, + "open_ports": [], + "type": "worker", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T15:56:55.788256" + }, + { + "ip": "192.168.137.72", + "hostname": "192.168.137.72", + "mac": null, + "open_ports": [], + "type": "worker", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T15:56:56.768989" + }, + { + "ip": "192.168.137.100", + "hostname": "MI", + "mac": null, + "open_ports": [ + 22 + ], + "type": "worker", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T15:56:58.613419" + }, + { + "ip": "192.168.137.102", + "hostname": "192.168.137.102", + "mac": null, + "open_ports": [], + "type": "worker", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T15:57:19.952047" + }, + { + "ip": "192.168.137.114", + "hostname": "192.168.137.114", + "mac": null, + "open_ports": [], + "type": "worker", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T15:57:20.802691" + }, + { + "ip": "192.168.137.137", + "hostname": "192.168.137.137", + "mac": null, + "open_ports": [], + "type": "worker", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T15:57:42.904414" + }, + { + "ip": "192.168.137.161", + "hostname": "192.168.137.161", + "mac": null, + "open_ports": [], + "type": "worker", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T15:58:05.741701" + }, + { + "ip": "192.168.137.172", + "hostname": "192.168.137.172", + "mac": null, + "open_ports": [], + "type": "worker", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T15:58:06.758378" + }, + { + "ip": "192.168.137.203", + "hostname": "192.168.137.203", + "mac": null, + "open_ports": [], + "type": "worker", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T15:58:29.845884" + }, + { + "ip": "192.168.137.217", + "hostname": "192.168.137.217", + "mac": null, + "open_ports": [], + "type": "worker", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T15:58:30.827941" + }, + { + "ip": "192.168.137.239", + "hostname": "hermes.lan", + "mac": "18:31:bf:4e:d7:8a", + "open_ports": [ + 22 + ], + "type": "worker", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T15:58:48.627302" + }, + { + "ip": "192.168.137.244", + "hostname": "192.168.137.244", + "mac": null, + "open_ports": [], + "type": "worker", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T15:59:09.825882" + }, + { + "ip": "192.168.1.10", + "hostname": "192.168.1.10", + "mac": "40:9f:38:1c:1f:01", + "open_ports": [], + "type": "unknown", + "icon": "\u2753", + "last_seen": "2025-12-07T15:59:31.412126" + }, + { + "ip": "192.168.1.1", + "hostname": "192.168.1.1", + "mac": "b8:d5:26:d2:7d:c0", + "open_ports": [ + 5000 + ], + "type": "router", + "icon": "\ud83d\udedc", + "last_seen": "2025-12-07T15:59:31.412126" + }, + { + "ip": "192.168.1.49", + "hostname": "192.168.1.49", + "mac": "ec:b5:fa:a5:6c:90", + "open_ports": [ + 8080, + 80, + 443 + ], + "type": "server", + "icon": "\ud83d\udcbb", + "last_seen": "2025-12-07T15:59:54.631041" + }, + { + "ip": "192.168.1.44", + "hostname": "192.168.1.44", + "mac": "48:27:e2:d4:d9:54", + "open_ports": [], + "type": "unknown", + "icon": "\u2753", + "last_seen": "2025-12-07T15:59:54.769952" + }, + { + "ip": "192.168.1.42", + "hostname": "192.168.1.42", + "mac": "38:2c:e5:45:be:3c", + "open_ports": [], + "type": "unknown", + "icon": "\u2753", + "last_seen": "2025-12-07T15:59:54.884149" + }, + { + "ip": "192.168.1.59", + "hostname": "192.168.1.59", + "mac": "04:17:b6:82:05:1a", + "open_ports": [], + "type": "unknown", + "icon": "\u2753", + "last_seen": "2025-12-07T15:59:56.630244" + }, + { + "ip": "192.168.1.65", + "hostname": "192.168.1.65", + "mac": "ea:e7:a7:88:43:e1", + "open_ports": [], + "type": "unknown", + "icon": "\u2753", + "last_seen": "2025-12-07T16:00:18.468155" + }, + { + "ip": "192.168.1.100", + "hostname": "host.docker.internal", + "mac": null, + "open_ports": [ + 22 + ], + "type": "worker", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T16:00:20.098760" + }, + { + "ip": "192.168.1.93", + "hostname": "192.168.1.93", + "mac": "fe:67:72:c1:c9:8c", + "open_ports": [], + "type": "unknown", + "icon": "\u2753", + "last_seen": "2025-12-07T16:00:41.565323" + }, + { + "ip": "192.168.1.102", + "hostname": "192.168.1.102", + "mac": "cc:f4:11:cd:76:50", + "open_ports": [ + 8443, + 8008 + ], + "type": "unknown", + "icon": "\u2753", + "last_seen": "2025-12-07T16:01:03.168820" + }, + { + "ip": "192.168.1.136", + "hostname": "192.168.1.136", + "mac": "b0:4a:39:58:ed:6a", + "open_ports": [], + "type": "unknown", + "icon": "\u2753", + "last_seen": "2025-12-07T16:01:25.683522" + }, + { + "ip": "192.168.1.159", + "hostname": "git.appmodel.nl", + "mac": "50:eb:f6:2a:98:30", + "open_ports": [ + 80, + 443, + 3000, + 8080, + 22 + ], + "type": "server", + "icon": "\ud83d\udcbb", + "last_seen": "2025-12-07T16:01:27.603014" + }, + { + "ip": "192.168.1.151", + "hostname": "192.168.1.151", + "mac": "34:cd:b0:2b:d9:a0", + "open_ports": [], + "type": "unknown", + "icon": "\u2753", + "last_seen": "2025-12-07T16:01:48.219580" + }, + { + "ip": "192.168.1.163", + "hostname": "192.168.1.163", + "mac": "00:1e:06:31:b1:27", + "open_ports": [ + 22, + 8080, + 8000 + ], + "type": "dns", + "icon": "\ud83d\udee1\ufe0f", + "last_seen": "2025-12-07T16:02:09.802235" + }, + { + "ip": "192.168.1.161", + "hostname": "192.168.1.161", + "mac": "4c:ba:d7:a6:c1:8c", + "open_ports": [], + "type": "unknown", + "icon": "\u2753", + "last_seen": "2025-12-07T16:02:09.802870" + }, + { + "ip": "192.168.1.165", + "hostname": "HP678531.home", + "mac": "48:ba:4e:67:85:32", + "open_ports": [ + 443, + 80, + 8080 + ], + "type": "server", + "icon": "\ud83d\udcbb", + "last_seen": "2025-12-07T16:02:09.817569" + }, + { + "ip": "192.168.1.190", + "hostname": "192.168.1.190", + "mac": "c8:c9:a3:60:0f:9d", + "open_ports": [], + "type": "unknown", + "icon": "\u2753", + "last_seen": "2025-12-07T16:02:31.453384" + }, + { + "ip": "192.168.1.193", + "hostname": "homeassistant", + "mac": "e4:b9:7a:9f:3b:5f", + "open_ports": [], + "type": "home_automation", + "icon": "\ud83c\udfe1", + "last_seen": "2025-12-07T16:02:32.140000" + }, + { + "ip": "192.168.1.206", + "hostname": "192.168.1.206", + "mac": "7c:df:a1:54:d4:fc", + "open_ports": [], + "type": "unknown", + "icon": "\u2753", + "last_seen": "2025-12-07T16:02:53.865089" + }, + { + "ip": "192.168.1.214", + "hostname": "192.168.1.214", + "mac": "10:7b:44:30:a0:b9", + "open_ports": [], + "type": "unknown", + "icon": "\u2753", + "last_seen": "2025-12-07T16:02:54.661758" + }, + { + "ip": "192.168.1.240", + "hostname": "192.168.1.240", + "mac": "4c:49:29:7d:bc:e4", + "open_ports": [ + 8443, + 8008 + ], + "type": "unknown", + "icon": "\u2753", + "last_seen": "2025-12-07T16:03:17.131184" + }, + { + "ip": "192.168.1.243", + "hostname": "192.168.1.243", + "mac": "ac:67:84:61:90:d9", + "open_ports": [ + 8443, + 8008 + ], + "type": "unknown", + "icon": "\u2753", + "last_seen": "2025-12-07T16:03:38.896155" + }, + { + "ip": "192.168.137.1", + "hostname": "MI", + "mac": null, + "open_ports": [ + 22 + ], + "type": "worker", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T16:03:39.964337" + }, + { + "ip": "192.168.137.5", + "hostname": "192.168.137.5", + "mac": null, + "open_ports": [], + "type": "worker", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T16:04:00.058709" + }, + { + "ip": "192.168.137.11", + "hostname": "192.168.137.11", + "mac": null, + "open_ports": [], + "type": "worker", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T16:04:00.737328" + }, + { + "ip": "192.168.137.46", + "hostname": "192.168.137.46", + "mac": null, + "open_ports": [], + "type": "worker", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T16:04:23.792363" + }, + { + "ip": "192.168.137.53", + "hostname": "192.168.137.53", + "mac": null, + "open_ports": [], + "type": "worker", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T16:04:24.722390" + }, + { + "ip": "192.168.137.100", + "hostname": "MI", + "mac": null, + "open_ports": [ + 22 + ], + "type": "worker", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T16:04:29.581053" + }, + { + "ip": "192.168.137.82", + "hostname": "192.168.137.82", + "mac": null, + "open_ports": [], + "type": "worker", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T16:04:47.713917" + }, + { + "ip": "192.168.137.91", + "hostname": "192.168.137.91", + "mac": null, + "open_ports": [], + "type": "worker", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T16:04:49.066341" + }, + { + "ip": "192.168.137.116", + "hostname": "192.168.137.116", + "mac": null, + "open_ports": [], + "type": "worker", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T16:05:11.425890" + }, + { + "ip": "192.168.137.142", + "hostname": "192.168.137.142", + "mac": null, + "open_ports": [], + "type": "worker", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T16:05:34.246370" + }, + { + "ip": "192.168.137.156", + "hostname": "192.168.137.156", + "mac": null, + "open_ports": [], + "type": "worker", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T16:05:35.568531" + }, + { + "ip": "192.168.137.174", + "hostname": "192.168.137.174", + "mac": null, + "open_ports": [], + "type": "worker", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T16:05:57.866182" + }, + { + "ip": "192.168.137.206", + "hostname": "192.168.137.206", + "mac": null, + "open_ports": [], + "type": "worker", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T16:06:20.912642" + }, + { + "ip": "192.168.137.215", + "hostname": "192.168.137.215", + "mac": null, + "open_ports": [], + "type": "worker", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T16:06:21.781321" + }, + { + "ip": "192.168.137.239", + "hostname": "hermes.lan", + "mac": "18:31:bf:4e:d7:8a", + "open_ports": [ + 22 + ], + "type": "worker", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T16:06:39.618322" + }, + { + "ip": "192.168.137.233", + "hostname": "192.168.137.233", + "mac": null, + "open_ports": [], + "type": "worker", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T16:06:43.724109" + } + ] +} \ No newline at end of file diff --git a/resources/network_discovery_20251207_163512.json b/resources/network_discovery_20251207_163512.json new file mode 100644 index 0000000..cade43b --- /dev/null +++ b/resources/network_discovery_20251207_163512.json @@ -0,0 +1,1326 @@ +{ + "discovery_time": "2025-12-07T16:35:12.190005", + "total_devices": 96, + "networks_scanned": [ + "192.168.137.0/24", + "172.20.240.1/20", + "192.168.137.1/24", + "192.168.1.0/24", + "192.168.137.100/24", + "192.168.1.100/24" + ], + "devices": [ + { + "ip": "192.168.137.1", + "hostname": "MI", + "mac": null, + "vendor": "Unknown", + "open_ports": [ + 22 + ], + "services": [ + "ssh(22)" + ], + "type": "worker", + "description": "Tether Worker Node", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T16:15:21.107914" + }, + { + "ip": "192.168.137.6", + "hostname": "192.168.137.6", + "mac": null, + "vendor": "Unknown", + "open_ports": [], + "services": [], + "type": "worker", + "description": "Tether Network Device", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T16:15:41.207211" + }, + { + "ip": "192.168.137.11", + "hostname": "192.168.137.11", + "mac": null, + "vendor": "Unknown", + "open_ports": [], + "services": [], + "type": "worker", + "description": "Tether Network Device", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T16:15:42.568378" + }, + { + "ip": "192.168.137.32", + "hostname": "192.168.137.32", + "mac": null, + "vendor": "Unknown", + "open_ports": [], + "services": [], + "type": "worker", + "description": "Tether Network Device", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T16:16:04.923490" + }, + { + "ip": "192.168.137.67", + "hostname": "192.168.137.67", + "mac": null, + "vendor": "Unknown", + "open_ports": [], + "services": [], + "type": "worker", + "description": "Tether Network Device", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T16:16:27.884028" + }, + { + "ip": "192.168.137.74", + "hostname": "192.168.137.74", + "mac": null, + "vendor": "Unknown", + "open_ports": [], + "services": [], + "type": "worker", + "description": "Tether Network Device", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T16:16:28.751097" + }, + { + "ip": "192.168.137.100", + "hostname": "MI", + "mac": null, + "vendor": "Unknown", + "open_ports": [ + 22 + ], + "services": [ + "ssh(22)" + ], + "type": "worker", + "description": "Tether Worker Node", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T16:16:30.609352" + }, + { + "ip": "192.168.137.97", + "hostname": "192.168.137.97", + "mac": null, + "vendor": "Unknown", + "open_ports": [], + "services": [], + "type": "worker", + "description": "Tether Network Device", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T16:16:50.776916" + }, + { + "ip": "192.168.137.121", + "hostname": "192.168.137.121", + "mac": null, + "vendor": "Unknown", + "open_ports": [], + "services": [], + "type": "worker", + "description": "Tether Network Device", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T16:17:13.754480" + }, + { + "ip": "192.168.137.137", + "hostname": "192.168.137.137", + "mac": null, + "vendor": "Unknown", + "open_ports": [], + "services": [], + "type": "worker", + "description": "Tether Network Device", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T16:17:14.692846" + }, + { + "ip": "192.168.137.157", + "hostname": "192.168.137.157", + "mac": null, + "vendor": "Unknown", + "open_ports": [], + "services": [], + "type": "worker", + "description": "Tether Network Device", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T16:17:36.959904" + }, + { + "ip": "192.168.137.183", + "hostname": "192.168.137.183", + "mac": null, + "vendor": "Unknown", + "open_ports": [], + "services": [], + "type": "worker", + "description": "Tether Network Device", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T16:17:59.739001" + }, + { + "ip": "192.168.137.196", + "hostname": "192.168.137.196", + "mac": null, + "vendor": "Unknown", + "open_ports": [], + "services": [], + "type": "worker", + "description": "Tether Network Device", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T16:18:01.065524" + }, + { + "ip": "192.168.137.215", + "hostname": "192.168.137.215", + "mac": null, + "vendor": "Unknown", + "open_ports": [], + "services": [], + "type": "worker", + "description": "Tether Network Device", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T16:18:23.376049" + }, + { + "ip": "192.168.137.239", + "hostname": "hermes.lan", + "mac": "18:31:bf:4e:d7:8a", + "vendor": "Apple", + "open_ports": [ + 22 + ], + "services": [ + "ssh(22)" + ], + "type": "worker", + "description": "Hermes Worker", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T16:18:41.126233" + }, + { + "ip": "192.168.137.242", + "hostname": "192.168.137.242", + "mac": null, + "vendor": "Unknown", + "open_ports": [], + "services": [], + "type": "worker", + "description": "Tether Network Device", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T16:19:02.332564" + }, + { + "ip": "172.20.240.1", + "hostname": "MI.mshome.net", + "mac": null, + "vendor": "Unknown", + "open_ports": [ + 22 + ], + "services": [ + "ssh(22)" + ], + "type": "hyperv", + "description": "Hyper-V Internal Network", + "icon": "\ud83d\udd37", + "last_seen": "2025-12-07T16:19:04.123402" + }, + { + "ip": "172.20.240.201", + "hostname": "172.20.240.201", + "mac": null, + "vendor": "Unknown", + "open_ports": [], + "services": [], + "type": "hyperv", + "description": "Hyper-V Internal Network", + "icon": "\ud83d\udd37", + "last_seen": "2025-12-07T16:19:24.229116" + }, + { + "ip": "172.20.255.251", + "hostname": "172.20.255.251", + "mac": null, + "vendor": "Unknown", + "open_ports": [], + "services": [], + "type": "hyperv", + "description": "Hyper-V Internal Network", + "icon": "\ud83d\udd37", + "last_seen": "2025-12-07T16:19:46.245528" + }, + { + "ip": "192.168.137.1", + "hostname": "MI", + "mac": null, + "vendor": "Unknown", + "open_ports": [ + 22 + ], + "services": [ + "ssh(22)" + ], + "type": "worker", + "description": "Tether Worker Node", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T16:19:48.115814" + }, + { + "ip": "192.168.137.3", + "hostname": "192.168.137.3", + "mac": null, + "vendor": "Unknown", + "open_ports": [], + "services": [], + "type": "worker", + "description": "Tether Network Device", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T16:20:08.288160" + }, + { + "ip": "192.168.137.12", + "hostname": "192.168.137.12", + "mac": null, + "vendor": "Unknown", + "open_ports": [], + "services": [], + "type": "worker", + "description": "Tether Network Device", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T16:20:09.568963" + }, + { + "ip": "192.168.137.13", + "hostname": "192.168.137.13", + "mac": null, + "vendor": "Unknown", + "open_ports": [], + "services": [], + "type": "worker", + "description": "Tether Network Device", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T16:20:09.568963" + }, + { + "ip": "192.168.137.31", + "hostname": "192.168.137.31", + "mac": null, + "vendor": "Unknown", + "open_ports": [], + "services": [], + "type": "worker", + "description": "Tether Network Device", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T16:20:31.881337" + }, + { + "ip": "192.168.137.62", + "hostname": "192.168.137.62", + "mac": null, + "vendor": "Unknown", + "open_ports": [], + "services": [], + "type": "worker", + "description": "Tether Network Device", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T16:20:54.753656" + }, + { + "ip": "192.168.137.73", + "hostname": "192.168.137.73", + "mac": null, + "vendor": "Unknown", + "open_ports": [], + "services": [], + "type": "worker", + "description": "Tether Network Device", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T16:20:55.753393" + }, + { + "ip": "192.168.137.100", + "hostname": "MI", + "mac": null, + "vendor": "Unknown", + "open_ports": [ + 22 + ], + "services": [ + "ssh(22)" + ], + "type": "worker", + "description": "Tether Worker Node", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T16:20:57.619006" + }, + { + "ip": "192.168.137.101", + "hostname": "192.168.137.101", + "mac": null, + "vendor": "Unknown", + "open_ports": [], + "services": [], + "type": "worker", + "description": "Tether Network Device", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T16:21:18.934939" + }, + { + "ip": "192.168.137.113", + "hostname": "192.168.137.113", + "mac": null, + "vendor": "Unknown", + "open_ports": [], + "services": [], + "type": "worker", + "description": "Tether Network Device", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T16:21:19.922470" + }, + { + "ip": "192.168.137.143", + "hostname": "192.168.137.143", + "mac": null, + "vendor": "Unknown", + "open_ports": [], + "services": [], + "type": "worker", + "description": "Tether Network Device", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T16:21:42.801052" + }, + { + "ip": "192.168.137.153", + "hostname": "192.168.137.153", + "mac": null, + "vendor": "Unknown", + "open_ports": [], + "services": [], + "type": "worker", + "description": "Tether Network Device", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T16:21:43.833594" + }, + { + "ip": "192.168.137.181", + "hostname": "192.168.137.181", + "mac": null, + "vendor": "Unknown", + "open_ports": [], + "services": [], + "type": "worker", + "description": "Tether Network Device", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T16:22:06.771184" + }, + { + "ip": "192.168.137.192", + "hostname": "192.168.137.192", + "mac": null, + "vendor": "Unknown", + "open_ports": [], + "services": [], + "type": "worker", + "description": "Tether Network Device", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T16:22:08.068148" + }, + { + "ip": "192.168.137.212", + "hostname": "192.168.137.212", + "mac": null, + "vendor": "Unknown", + "open_ports": [], + "services": [], + "type": "worker", + "description": "Tether Network Device", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T16:22:30.396504" + }, + { + "ip": "192.168.137.239", + "hostname": "hermes.lan", + "mac": "18:31:bf:4e:d7:8a", + "vendor": "Apple", + "open_ports": [ + 22 + ], + "services": [ + "ssh(22)" + ], + "type": "worker", + "description": "Hermes Worker", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T16:22:48.126540" + }, + { + "ip": "192.168.137.243", + "hostname": "192.168.137.243", + "mac": null, + "vendor": "Unknown", + "open_ports": [], + "services": [], + "type": "worker", + "description": "Tether Network Device", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T16:23:09.323785" + }, + { + "ip": "192.168.1.1", + "hostname": "192.168.1.1", + "mac": "b8:d5:26:d2:7d:c0", + "vendor": "ASUSTek", + "open_ports": [ + 5000 + ], + "services": [ + "upnp(5000)" + ], + "type": "router", + "description": "Router/Gateway", + "icon": "\ud83d\udedc", + "last_seen": "2025-12-07T16:23:30.939312" + }, + { + "ip": "192.168.1.10", + "hostname": "192.168.1.10", + "mac": "40:9f:38:1c:1f:01", + "vendor": "Google", + "open_ports": [], + "services": [], + "type": "unknown", + "description": "Unknown Device", + "icon": "\u2753", + "last_seen": "2025-12-07T16:23:30.939312" + }, + { + "ip": "192.168.1.49", + "hostname": "192.168.1.49", + "mac": "ec:b5:fa:a5:6c:90", + "vendor": "Giga-Byte Technology", + "open_ports": [ + 80, + 443, + 8080 + ], + "services": [ + "http(80)", + "https(443)", + "http-alt(8080)" + ], + "type": "server", + "description": "Web Server", + "icon": "\ud83d\udcbb", + "last_seen": "2025-12-07T16:23:54.148617" + }, + { + "ip": "192.168.1.44", + "hostname": "192.168.1.44", + "mac": "48:27:e2:d4:d9:54", + "vendor": "Huawei", + "open_ports": [], + "services": [], + "type": "unknown", + "description": "Unknown Device", + "icon": "\u2753", + "last_seen": "2025-12-07T16:23:54.196091" + }, + { + "ip": "192.168.1.42", + "hostname": "192.168.1.42", + "mac": "38:2c:e5:45:be:3c", + "vendor": "Samsung", + "open_ports": [], + "services": [], + "type": "unknown", + "description": "Unknown Device", + "icon": "\u2753", + "last_seen": "2025-12-07T16:23:54.305411" + }, + { + "ip": "192.168.1.59", + "hostname": "192.168.1.59", + "mac": "04:17:b6:82:05:1a", + "vendor": "ASUSTek", + "open_ports": [], + "services": [], + "type": "unknown", + "description": "Unknown Device", + "icon": "\u2753", + "last_seen": "2025-12-07T16:23:56.112476" + }, + { + "ip": "192.168.1.65", + "hostname": "192.168.1.65", + "mac": "ea:e7:a7:88:43:e1", + "vendor": "Shenzhen Bolutek", + "open_ports": [], + "services": [], + "type": "unknown", + "description": "Unknown Device", + "icon": "\u2753", + "last_seen": "2025-12-07T16:24:17.839040" + }, + { + "ip": "192.168.1.100", + "hostname": "host.docker.internal", + "mac": null, + "vendor": "Unknown", + "open_ports": [ + 22 + ], + "services": [ + "ssh(22)" + ], + "type": "worker", + "description": "Atlas Worker Node", + "icon": "\ud83d\udda5\ufe0f", + "last_seen": "2025-12-07T16:24:19.613661" + }, + { + "ip": "192.168.1.93", + "hostname": "192.168.1.93", + "mac": "fe:67:72:c1:c9:8c", + "vendor": "Unknown Manufacturer", + "open_ports": [], + "services": [], + "type": "unknown", + "description": "Unknown Device", + "icon": "\u2753", + "last_seen": "2025-12-07T16:24:40.380769" + }, + { + "ip": "192.168.1.102", + "hostname": "192.168.1.102", + "mac": "cc:f4:11:cd:76:50", + "vendor": "Murata Manufacturing", + "open_ports": [ + 9000, + 8443, + 8008 + ], + "services": [ + "cast(8008/8443)" + ], + "type": "media", + "description": "Media Device", + "icon": "\ud83d\udcf1", + "last_seen": "2025-12-07T16:25:02.003901" + }, + { + "ip": "192.168.1.136", + "hostname": "192.168.1.136", + "mac": "b0:4a:39:58:ed:6a", + "vendor": "Xiaomi", + "open_ports": [], + "services": [], + "type": "unknown", + "description": "Unknown Device", + "icon": "\u2753", + "last_seen": "2025-12-07T16:25:24.657986" + }, + { + "ip": "192.168.1.159", + "hostname": "git.appmodel.nl", + "mac": "50:eb:f6:2a:98:30", + "vendor": "ASUSTek", + "open_ports": [ + 3000, + 80, + 443, + 22, + 8080 + ], + "services": [ + "gitea(3000)", + "traefik(80/443)", + "ssh(22)" + ], + "type": "server", + "description": "Core Server (Traefik + Gitea)", + "icon": "\ud83d\udcbb", + "last_seen": "2025-12-07T16:25:26.611382" + }, + { + "ip": "192.168.1.151", + "hostname": "192.168.1.151", + "mac": "34:cd:b0:2b:d9:a0", + "vendor": "Huawei", + "open_ports": [], + "services": [], + "type": "unknown", + "description": "Unknown Device", + "icon": "\u2753", + "last_seen": "2025-12-07T16:25:47.267323" + }, + { + "ip": "192.168.1.161", + "hostname": "192.168.1.161", + "mac": "4c:ba:d7:a6:c1:8c", + "vendor": "Huawei", + "open_ports": [], + "services": [], + "type": "unknown", + "description": "Unknown Device", + "icon": "\u2753", + "last_seen": "2025-12-07T16:26:08.930694" + }, + { + "ip": "192.168.1.163", + "hostname": "192.168.1.163", + "mac": "00:1e:06:31:b1:27", + "vendor": "Actiontec", + "open_ports": [ + 8000, + 22, + 8080 + ], + "services": [ + "artifactory(8000)", + "adguard(8080)", + "ssh(22)" + ], + "type": "dns", + "description": "Infra (AdGuard + Artifactory)", + "icon": "\ud83d\udee1\ufe0f", + "last_seen": "2025-12-07T16:26:08.930694" + }, + { + "ip": "192.168.1.165", + "hostname": "HP678531", + "mac": "48:ba:4e:67:85:32", + "vendor": "Hewlett Packard", + "open_ports": [ + 80, + 8080, + 443 + ], + "services": [ + "http(80)", + "https(443)", + "http-alt(8080)" + ], + "type": "server", + "description": "HP Server/Printer", + "icon": "\ud83d\udcbb", + "last_seen": "2025-12-07T16:26:08.964350" + }, + { + "ip": "192.168.1.190", + "hostname": "192.168.1.190", + "mac": "c8:c9:a3:60:0f:9d", + "vendor": "LG Electronics", + "open_ports": [], + "services": [], + "type": "unknown", + "description": "Unknown Device", + "icon": "\u2753", + "last_seen": "2025-12-07T16:26:30.599355" + }, + { + "ip": "192.168.1.193", + "hostname": "homeassistant", + "mac": "e4:b9:7a:9f:3b:5f", + "vendor": "Huawei", + "open_ports": [], + "services": [], + "type": "home_automation", + "description": "Home Assistant", + "icon": "\ud83c\udfe1", + "last_seen": "2025-12-07T16:26:31.115015" + }, + { + "ip": "192.168.1.206", + "hostname": "192.168.1.206", + "mac": "7c:df:a1:54:d4:fc", + "vendor": "Huawei", + "open_ports": [], + "services": [], + "type": "unknown", + "description": "Unknown Device", + "icon": "\u2753", + "last_seen": "2025-12-07T16:26:52.787554" + }, + { + "ip": "192.168.1.214", + "hostname": "192.168.1.214", + "mac": "10:7b:44:30:a0:b9", + "vendor": "TP-Link", + "open_ports": [], + "services": [], + "type": "unknown", + "description": "Unknown Device", + "icon": "\u2753", + "last_seen": "2025-12-07T16:26:53.646556" + }, + { + "ip": "192.168.1.240", + "hostname": "192.168.1.240", + "mac": "4c:49:29:7d:bc:e4", + "vendor": "Huawei", + "open_ports": [ + 8443, + 8008, + 9000 + ], + "services": [ + "cast(8008/8443)" + ], + "type": "media", + "description": "Media Device", + "icon": "\ud83d\udcf1", + "last_seen": "2025-12-07T16:27:16.143129" + }, + { + "ip": "192.168.1.243", + "hostname": "192.168.1.243", + "mac": "ac:67:84:61:90:d9", + "vendor": "Huawei", + "open_ports": [ + 8008, + 8443, + 9000 + ], + "services": [ + "cast(8008/8443)" + ], + "type": "media", + "description": "Media Device", + "icon": "\ud83d\udcf1", + "last_seen": "2025-12-07T16:27:37.743479" + }, + { + "ip": "192.168.137.1", + "hostname": "MI", + "mac": null, + "vendor": "Unknown", + "open_ports": [ + 22 + ], + "services": [ + "ssh(22)" + ], + "type": "worker", + "description": "Tether Worker Node", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T16:27:38.830329" + }, + { + "ip": "192.168.137.8", + "hostname": "192.168.137.8", + "mac": null, + "vendor": "Unknown", + "open_ports": [], + "services": [], + "type": "worker", + "description": "Tether Network Device", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T16:27:58.936893" + }, + { + "ip": "192.168.137.12", + "hostname": "192.168.137.12", + "mac": null, + "vendor": "Unknown", + "open_ports": [], + "services": [], + "type": "worker", + "description": "Tether Network Device", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T16:27:59.855958" + }, + { + "ip": "192.168.137.34", + "hostname": "192.168.137.34", + "mac": null, + "vendor": "Unknown", + "open_ports": [], + "services": [], + "type": "worker", + "description": "Tether Network Device", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T16:28:21.722302" + }, + { + "ip": "192.168.137.63", + "hostname": "192.168.137.63", + "mac": null, + "vendor": "Unknown", + "open_ports": [], + "services": [], + "type": "worker", + "description": "Tether Network Device", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T16:28:44.873278" + }, + { + "ip": "192.168.137.73", + "hostname": "192.168.137.73", + "mac": null, + "vendor": "Unknown", + "open_ports": [], + "services": [], + "type": "worker", + "description": "Tether Network Device", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T16:28:45.790051" + }, + { + "ip": "192.168.137.100", + "hostname": "MI", + "mac": null, + "vendor": "Unknown", + "open_ports": [ + 22 + ], + "services": [ + "ssh(22)" + ], + "type": "worker", + "description": "Tether Worker Node", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T16:28:47.606758" + }, + { + "ip": "192.168.137.91", + "hostname": "192.168.137.91", + "mac": null, + "vendor": "Unknown", + "open_ports": [], + "services": [], + "type": "worker", + "description": "Tether Network Device", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T16:29:07.825414" + }, + { + "ip": "192.168.137.122", + "hostname": "192.168.137.122", + "mac": null, + "vendor": "Unknown", + "open_ports": [], + "services": [], + "type": "worker", + "description": "Tether Network Device", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T16:29:30.808509" + }, + { + "ip": "192.168.137.133", + "hostname": "192.168.137.133", + "mac": null, + "vendor": "Unknown", + "open_ports": [], + "services": [], + "type": "worker", + "description": "Tether Network Device", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T16:29:31.692165" + }, + { + "ip": "192.168.137.156", + "hostname": "192.168.137.156", + "mac": null, + "vendor": "Unknown", + "open_ports": [], + "services": [], + "type": "worker", + "description": "Tether Network Device", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T16:29:53.820766" + }, + { + "ip": "192.168.137.185", + "hostname": "192.168.137.185", + "mac": null, + "vendor": "Unknown", + "open_ports": [], + "services": [], + "type": "worker", + "description": "Tether Network Device", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T16:30:16.842780" + }, + { + "ip": "192.168.137.197", + "hostname": "192.168.137.197", + "mac": null, + "vendor": "Unknown", + "open_ports": [], + "services": [], + "type": "worker", + "description": "Tether Network Device", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T16:30:17.843822" + }, + { + "ip": "192.168.137.239", + "hostname": "hermes.lan", + "mac": "18:31:bf:4e:d7:8a", + "vendor": "Apple", + "open_ports": [ + 22 + ], + "services": [ + "ssh(22)" + ], + "type": "worker", + "description": "Hermes Worker", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T16:30:37.578511" + }, + { + "ip": "192.168.137.224", + "hostname": "192.168.137.224", + "mac": null, + "vendor": "Unknown", + "open_ports": [], + "services": [], + "type": "worker", + "description": "Tether Network Device", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T16:30:40.745649" + }, + { + "ip": "192.168.137.232", + "hostname": "192.168.137.232", + "mac": null, + "vendor": "Unknown", + "open_ports": [], + "services": [], + "type": "worker", + "description": "Tether Network Device", + "icon": "\ud83d\udef0\ufe0f", + "last_seen": "2025-12-07T16:30:41.726128" + }, + { + "ip": "192.168.1.10", + "hostname": "192.168.1.10", + "mac": "40:9f:38:1c:1f:01", + "vendor": "Google", + "open_ports": [], + "services": [], + "type": "unknown", + "description": "Unknown Device", + "icon": "\u2753", + "last_seen": "2025-12-07T16:31:05.144554" + }, + { + "ip": "192.168.1.1", + "hostname": "192.168.1.1", + "mac": "b8:d5:26:d2:7d:c0", + "vendor": "ASUSTek", + "open_ports": [ + 5000 + ], + "services": [ + "upnp(5000)" + ], + "type": "router", + "description": "Router/Gateway", + "icon": "\ud83d\udedc", + "last_seen": "2025-12-07T16:31:05.161624" + }, + { + "ip": "192.168.1.49", + "hostname": "192.168.1.49", + "mac": "ec:b5:fa:a5:6c:90", + "vendor": "Giga-Byte Technology", + "open_ports": [ + 80, + 8080, + 443 + ], + "services": [ + "http(80)", + "https(443)", + "http-alt(8080)" + ], + "type": "server", + "description": "Web Server", + "icon": "\ud83d\udcbb", + "last_seen": "2025-12-07T16:31:28.630522" + }, + { + "ip": "192.168.1.44", + "hostname": "192.168.1.44", + "mac": "48:27:e2:d4:d9:54", + "vendor": "Huawei", + "open_ports": [], + "services": [], + "type": "unknown", + "description": "Unknown Device", + "icon": "\u2753", + "last_seen": "2025-12-07T16:31:28.771343" + }, + { + "ip": "192.168.1.42", + "hostname": "192.168.1.42", + "mac": "38:2c:e5:45:be:3c", + "vendor": "Samsung", + "open_ports": [], + "services": [], + "type": "unknown", + "description": "Unknown Device", + "icon": "\u2753", + "last_seen": "2025-12-07T16:31:29.053057" + }, + { + "ip": "192.168.1.59", + "hostname": "192.168.1.59", + "mac": "04:17:b6:82:05:1a", + "vendor": "ASUSTek", + "open_ports": [], + "services": [], + "type": "unknown", + "description": "Unknown Device", + "icon": "\u2753", + "last_seen": "2025-12-07T16:31:30.628702" + }, + { + "ip": "192.168.1.65", + "hostname": "192.168.1.65", + "mac": "ea:e7:a7:88:43:e1", + "vendor": "Shenzhen Bolutek", + "open_ports": [], + "services": [], + "type": "unknown", + "description": "Unknown Device", + "icon": "\u2753", + "last_seen": "2025-12-07T16:31:52.293313" + }, + { + "ip": "192.168.1.100", + "hostname": "host.docker.internal", + "mac": null, + "vendor": "Unknown", + "open_ports": [ + 22 + ], + "services": [ + "ssh(22)" + ], + "type": "worker", + "description": "Atlas Worker Node", + "icon": "\ud83d\udda5\ufe0f", + "last_seen": "2025-12-07T16:31:54.114743" + }, + { + "ip": "192.168.1.93", + "hostname": "192.168.1.93", + "mac": "fe:67:72:c1:c9:8c", + "vendor": "Unknown Manufacturer", + "open_ports": [], + "services": [], + "type": "unknown", + "description": "Unknown Device", + "icon": "\u2753", + "last_seen": "2025-12-07T16:32:15.056446" + }, + { + "ip": "192.168.1.102", + "hostname": "192.168.1.102", + "mac": "cc:f4:11:cd:76:50", + "vendor": "Murata Manufacturing", + "open_ports": [ + 9000, + 8443, + 8008 + ], + "services": [ + "cast(8008/8443)" + ], + "type": "media", + "description": "Media Device", + "icon": "\ud83d\udcf1", + "last_seen": "2025-12-07T16:32:36.617331" + }, + { + "ip": "192.168.1.136", + "hostname": "192.168.1.136", + "mac": "b0:4a:39:58:ed:6a", + "vendor": "Xiaomi", + "open_ports": [], + "services": [], + "type": "unknown", + "description": "Unknown Device", + "icon": "\u2753", + "last_seen": "2025-12-07T16:32:59.118569" + }, + { + "ip": "192.168.1.159", + "hostname": "git.appmodel.nl", + "mac": "50:eb:f6:2a:98:30", + "vendor": "ASUSTek", + "open_ports": [ + 443, + 8080, + 3000, + 80, + 22 + ], + "services": [ + "gitea(3000)", + "traefik(80/443)", + "ssh(22)" + ], + "type": "server", + "description": "Core Server (Traefik + Gitea)", + "icon": "\ud83d\udcbb", + "last_seen": "2025-12-07T16:33:01.099478" + }, + { + "ip": "192.168.1.151", + "hostname": "192.168.1.151", + "mac": "34:cd:b0:2b:d9:a0", + "vendor": "Huawei", + "open_ports": [], + "services": [], + "type": "unknown", + "description": "Unknown Device", + "icon": "\u2753", + "last_seen": "2025-12-07T16:33:21.701039" + }, + { + "ip": "192.168.1.161", + "hostname": "192.168.1.161", + "mac": "4c:ba:d7:a6:c1:8c", + "vendor": "Huawei", + "open_ports": [], + "services": [], + "type": "unknown", + "description": "Unknown Device", + "icon": "\u2753", + "last_seen": "2025-12-07T16:33:43.295854" + }, + { + "ip": "192.168.1.163", + "hostname": "192.168.1.163", + "mac": "00:1e:06:31:b1:27", + "vendor": "Actiontec", + "open_ports": [ + 8080, + 22, + 8000 + ], + "services": [ + "artifactory(8000)", + "adguard(8080)", + "ssh(22)" + ], + "type": "dns", + "description": "Infra (AdGuard + Artifactory)", + "icon": "\ud83d\udee1\ufe0f", + "last_seen": "2025-12-07T16:33:43.296357" + }, + { + "ip": "192.168.1.165", + "hostname": "HP678531.home", + "mac": "48:ba:4e:67:85:32", + "vendor": "Hewlett Packard", + "open_ports": [ + 80, + 8080, + 443 + ], + "services": [ + "http(80)", + "https(443)", + "http-alt(8080)" + ], + "type": "server", + "description": "HP Server/Printer", + "icon": "\ud83d\udcbb", + "last_seen": "2025-12-07T16:33:43.419137" + }, + { + "ip": "192.168.1.190", + "hostname": "192.168.1.190", + "mac": "c8:c9:a3:60:0f:9d", + "vendor": "LG Electronics", + "open_ports": [], + "services": [], + "type": "unknown", + "description": "Unknown Device", + "icon": "\u2753", + "last_seen": "2025-12-07T16:34:05.049776" + }, + { + "ip": "192.168.1.193", + "hostname": "homeassistant", + "mac": "e4:b9:7a:9f:3b:5f", + "vendor": "Huawei", + "open_ports": [], + "services": [], + "type": "home_automation", + "description": "Home Assistant", + "icon": "\ud83c\udfe1", + "last_seen": "2025-12-07T16:34:05.611511" + }, + { + "ip": "192.168.1.206", + "hostname": "192.168.1.206", + "mac": "7c:df:a1:54:d4:fc", + "vendor": "Huawei", + "open_ports": [], + "services": [], + "type": "unknown", + "description": "Unknown Device", + "icon": "\u2753", + "last_seen": "2025-12-07T16:34:27.339034" + }, + { + "ip": "192.168.1.214", + "hostname": "192.168.1.214", + "mac": "10:7b:44:30:a0:b9", + "vendor": "TP-Link", + "open_ports": [], + "services": [], + "type": "unknown", + "description": "Unknown Device", + "icon": "\u2753", + "last_seen": "2025-12-07T16:34:28.135596" + }, + { + "ip": "192.168.1.240", + "hostname": "192.168.1.240", + "mac": "4c:49:29:7d:bc:e4", + "vendor": "Huawei", + "open_ports": [ + 8008, + 8443, + 9000 + ], + "services": [ + "cast(8008/8443)" + ], + "type": "media", + "description": "Media Device", + "icon": "\ud83d\udcf1", + "last_seen": "2025-12-07T16:34:50.622347" + }, + { + "ip": "192.168.1.243", + "hostname": "192.168.1.243", + "mac": "ac:67:84:61:90:d9", + "vendor": "Huawei", + "open_ports": [ + 8008, + 8443, + 9000 + ], + "services": [ + "cast(8008/8443)" + ], + "type": "media", + "description": "Media Device", + "icon": "\ud83d\udcf1", + "last_seen": "2025-12-07T16:35:12.189068" + } + ] +} \ No newline at end of file diff --git a/resources/network_summary_20251207_160645.txt b/resources/network_summary_20251207_160645.txt new file mode 100644 index 0000000..727c73b --- /dev/null +++ b/resources/network_summary_20251207_160645.txt @@ -0,0 +1,9 @@ +============================================================ +NETWORK DISCOVERY SUMMARY +============================================================ + +Discovery Time: 2025-12-07 16:06:45 +Total Devices Found: 76 + +Devices by Type: +---------------------------------------- diff --git a/resources/network_summary_20251207_163512.txt b/resources/network_summary_20251207_163512.txt new file mode 100644 index 0000000..80e58af --- /dev/null +++ b/resources/network_summary_20251207_163512.txt @@ -0,0 +1,9 @@ +============================================================ +NETWORK DISCOVERY SUMMARY +============================================================ + +Discovery Time: 2025-12-07 16:35:12 +Total Devices Found: 96 + +Devices by Type: +---------------------------------------- diff --git a/resources/network_topology.txt b/resources/network_topology.txt new file mode 100644 index 0000000..597d612 --- /dev/null +++ b/resources/network_topology.txt @@ -0,0 +1,172 @@ +flowchart TD + %% ---------- styles ---------- + classDef internet fill:#e1f5ff,stroke:#007bff + classDef router fill:#fff3cd,stroke:#ffc107 + classDef server fill:#ffe6e6,stroke:#dc3545 + classDef dns fill:#e6ffe6,stroke:#28a745 + classDef homeauto fill:#fff0f5,stroke:#e83e8c + classDef worker fill:#f0e6ff,stroke:#6f42c1 + classDef iot fill:#fff9e6,stroke:#fd7e14 + classDef unknown fill:#f8f9fa,stroke:#6c757d + + subgraph LAN["LAN 192.168.1.0/24 - 40 devices"] + subgraph CORE["Core Infrastructure"] + 192_168_1_1["[Router] 192.168.1.1
192.168.1.1"] + class 192_168_1_1 router; + 192_168_1_49["[Server] 192.168.1.49
192.168.1.49"] + class 192_168_1_49 server; + 192_168_1_100["[Worker] host.docker.internal
192.168.1.100"] + class 192_168_1_100 worker; + 192_168_1_159["[Server] git.appmodel.nl
192.168.1.159"] + class 192_168_1_159 server; + 192_168_1_163["[DNS] 192.168.1.163
192.168.1.163"] + class 192_168_1_163 dns; + 192_168_1_165["[Server] HP678531.home
192.168.1.165"] + class 192_168_1_165 server; + 192_168_1_193["[Home] homeassistant
192.168.1.193"] + class 192_168_1_193 homeauto; + 192_168_1_1["[Router] 192.168.1.1
192.168.1.1"] + class 192_168_1_1 router; + 192_168_1_49["[Server] 192.168.1.49
192.168.1.49"] + class 192_168_1_49 server; + 192_168_1_100["[Worker] host.docker.internal
192.168.1.100"] + class 192_168_1_100 worker; + 192_168_1_159["[Server] git.appmodel.nl
192.168.1.159"] + class 192_168_1_159 server; + 192_168_1_163["[DNS] 192.168.1.163
192.168.1.163"] + class 192_168_1_163 dns; + 192_168_1_165["[Server] HP678531.home
192.168.1.165"] + class 192_168_1_165 server; + 192_168_1_193["[Home] homeassistant
192.168.1.193"] + class 192_168_1_193 homeauto; + end + 192_168_1_10["[?] 192.168.1.10
192.168.1.10"] + class 192_168_1_10 unknown; + 192_168_1_44["[?] 192.168.1.44
192.168.1.44"] + class 192_168_1_44 unknown; + 192_168_1_59["[?] 192.168.1.59
192.168.1.59"] + class 192_168_1_59 unknown; + 192_168_1_65["[?] 192.168.1.65
192.168.1.65"] + class 192_168_1_65 unknown; + 192_168_1_102["[?] 192.168.1.102
192.168.1.102"] + class 192_168_1_102 unknown; + 192_168_1_136["[?] 192.168.1.136
192.168.1.136"] + class 192_168_1_136 unknown; + 192_168_1_151["[?] 192.168.1.151
192.168.1.151"] + class 192_168_1_151 unknown; + 192_168_1_206["[?] 192.168.1.206
192.168.1.206"] + class 192_168_1_206 unknown; + 192_168_1_214["[?] 192.168.1.214
192.168.1.214"] + class 192_168_1_214 unknown; + 192_168_1_240["[?] 192.168.1.240
192.168.1.240"] + class 192_168_1_240 unknown; + 192_168_1_243["[?] 192.168.1.243
192.168.1.243"] + class 192_168_1_243 unknown; + 192_168_1_10["[?] 192.168.1.10
192.168.1.10"] + class 192_168_1_10 unknown; + 192_168_1_44["[?] 192.168.1.44
192.168.1.44"] + class 192_168_1_44 unknown; + 192_168_1_42["[?] 192.168.1.42
192.168.1.42"] + class 192_168_1_42 unknown; + 192_168_1_59["[?] 192.168.1.59
192.168.1.59"] + class 192_168_1_59 unknown; + 192_168_1_65["[?] 192.168.1.65
192.168.1.65"] + class 192_168_1_65 unknown; + 192_168_1_93["[?] 192.168.1.93
192.168.1.93"] + class 192_168_1_93 unknown; + 192_168_1_102["[?] 192.168.1.102
192.168.1.102"] + class 192_168_1_102 unknown; + 192_168_1_136["[?] 192.168.1.136
192.168.1.136"] + class 192_168_1_136 unknown; + 192_168_1_151["[?] 192.168.1.151
192.168.1.151"] + class 192_168_1_151 unknown; + 192_168_1_161["[?] 192.168.1.161
192.168.1.161"] + class 192_168_1_161 unknown; + 192_168_1_190["[?] 192.168.1.190
192.168.1.190"] + class 192_168_1_190 unknown; + 192_168_1_206["[?] 192.168.1.206
192.168.1.206"] + class 192_168_1_206 unknown; + 192_168_1_214["[?] 192.168.1.214
192.168.1.214"] + class 192_168_1_214 unknown; + 192_168_1_240["[?] 192.168.1.240
192.168.1.240"] + class 192_168_1_240 unknown; + 192_168_1_243["[?] 192.168.1.243
192.168.1.243"] + class 192_168_1_243 unknown; + end + + subgraph TETHER["Tether 192.168.137.0/24 - 35 devices"] + 192_168_137_1["[Worker] MI
192.168.137.1"] + class 192_168_137_1 worker; + 192_168_137_100["[Worker] MI
192.168.137.100"] + class 192_168_137_100 worker; + 192_168_137_239["[Hermes] 192.168.137.239
192.168.137.239"] + class 192_168_137_239 worker; + 192_168_137_1["[Worker] MI
192.168.137.1"] + class 192_168_137_1 worker; + 192_168_137_3["[Worker] 192.168.137.3
192.168.137.3"] + class 192_168_137_3 worker; + 192_168_137_13["[Worker] 192.168.137.13
192.168.137.13"] + class 192_168_137_13 worker; + 192_168_137_35["[Worker] 192.168.137.35
192.168.137.35"] + class 192_168_137_35 worker; + 192_168_137_61["[Worker] 192.168.137.61
192.168.137.61"] + class 192_168_137_61 worker; + 192_168_137_72["[Worker] 192.168.137.72
192.168.137.72"] + class 192_168_137_72 worker; + 192_168_137_100["[Worker] MI
192.168.137.100"] + class 192_168_137_100 worker; + 192_168_137_102["[Worker] 192.168.137.102
192.168.137.102"] + class 192_168_137_102 worker; + 192_168_137_114["[Worker] 192.168.137.114
192.168.137.114"] + class 192_168_137_114 worker; + 192_168_137_137["[Worker] 192.168.137.137
192.168.137.137"] + class 192_168_137_137 worker; + 192_168_137_161["[Worker] 192.168.137.161
192.168.137.161"] + class 192_168_137_161 worker; + 192_168_137_172["[Worker] 192.168.137.172
192.168.137.172"] + class 192_168_137_172 worker; + 192_168_137_203["[Worker] 192.168.137.203
192.168.137.203"] + class 192_168_137_203 worker; + 192_168_137_217["[Worker] 192.168.137.217
192.168.137.217"] + class 192_168_137_217 worker; + 192_168_137_239["[Hermes] hermes.lan
192.168.137.239"] + class 192_168_137_239 worker; + 192_168_137_244["[Worker] 192.168.137.244
192.168.137.244"] + class 192_168_137_244 worker; + 192_168_137_1["[Worker] MI
192.168.137.1"] + class 192_168_137_1 worker; + 192_168_137_5["[Worker] 192.168.137.5
192.168.137.5"] + class 192_168_137_5 worker; + 192_168_137_11["[Worker] 192.168.137.11
192.168.137.11"] + class 192_168_137_11 worker; + 192_168_137_46["[Worker] 192.168.137.46
192.168.137.46"] + class 192_168_137_46 worker; + 192_168_137_53["[Worker] 192.168.137.53
192.168.137.53"] + class 192_168_137_53 worker; + 192_168_137_100["[Worker] MI
192.168.137.100"] + class 192_168_137_100 worker; + 192_168_137_82["[Worker] 192.168.137.82
192.168.137.82"] + class 192_168_137_82 worker; + 192_168_137_91["[Worker] 192.168.137.91
192.168.137.91"] + class 192_168_137_91 worker; + 192_168_137_116["[Worker] 192.168.137.116
192.168.137.116"] + class 192_168_137_116 worker; + 192_168_137_142["[Worker] 192.168.137.142
192.168.137.142"] + class 192_168_137_142 worker; + 192_168_137_156["[Worker] 192.168.137.156
192.168.137.156"] + class 192_168_137_156 worker; + 192_168_137_174["[Worker] 192.168.137.174
192.168.137.174"] + class 192_168_137_174 worker; + 192_168_137_206["[Worker] 192.168.137.206
192.168.137.206"] + class 192_168_137_206 worker; + 192_168_137_215["[Worker] 192.168.137.215
192.168.137.215"] + class 192_168_137_215 worker; + 192_168_137_239["[Hermes] hermes.lan
192.168.137.239"] + class 192_168_137_239 worker; + 192_168_137_233["[Worker] 192.168.137.233
192.168.137.233"] + class 192_168_137_233 worker; + end + + %% ---------- Connections ---------- + 192_168_1_159 -.-> TETHER + 192_168_1_1 --> LAN \ No newline at end of file diff --git a/src/discovery.py b/src/discovery.py new file mode 100644 index 0000000..9e21e62 --- /dev/null +++ b/src/discovery.py @@ -0,0 +1,625 @@ +import socket +import subprocess +import json +import ipaddress +from datetime import datetime +import re +import concurrent.futures +import ctypes + + +class WindowsNetworkDiscoverer: + def __init__(self): + self.icon_mapping = { + 'router': '🛜', + 'server': 'đŸ’ģ', + 'nas': '🧱', + 'docker': 'đŸŗ', + 'git': '📚', + 'reverse_proxy': 'đŸšĻ', + 'ai': '🧠', + 'dns': 'đŸ›Ąī¸', + 'registry': 'đŸ“Ļ', + 'home_automation': '🏡', + 'worker': 'đŸ›°ī¸', + 'iot': 'đŸ“ē', + 'laptop': '👨‍đŸ’ģ', + 'internet': '🌐', + 'unknown': '❓' + } + + def get_local_networks(self): + """Get local networks using Windows ipconfig""" + networks = [] + + try: + # Run ipconfig and parse output + result = subprocess.run(['ipconfig', '/all'], + capture_output=True, text=True, encoding='utf-8', errors='ignore') + + current_ip = None + current_mask = None + + for line in result.stdout.split('\n'): + line = line.strip() + + # Look for IPv4 Address + if 'IPv4 Address' in line and '(Preferred)' in line: + match = re.search(r'(\d+\.\d+\.\d+\.\d+)', line) + if match: + current_ip = match.group(1) + + # Look for Subnet Mask + elif 'Subnet Mask' in line and current_ip: + match = re.search(r'(\d+\.\d+\.\d+\.\d+)', line) + if match: + current_mask = match.group(1) + + # Convert to CIDR + if current_ip and current_mask and not current_ip.startswith('169.254.'): + # Calculate CIDR from subnet mask + mask_parts = current_mask.split('.') + cidr = sum(bin(int(x)).count('1') for x in mask_parts) + network = f"{current_ip}/{cidr}" + networks.append(network) + + current_ip = None + current_mask = None + + except Exception as e: + print(f"Error getting networks: {e}") + + # Add your specific networks from the diagram + if '192.168.1.0/24' not in networks: + networks.append('192.168.1.0/24') + if '192.168.137.0/24' not in networks: + networks.append('192.168.137.0/24') + + return list(set(networks)) + + def windows_ping(self, ip): + """Windows-compatible ping check""" + try: + # Windows ping command + result = subprocess.run( + ['ping', '-n', '1', '-w', '1000', ip], + capture_output=True, + text=True, + creationflags=subprocess.CREATE_NO_WINDOW + ) + + # Check for successful ping in output + return ('Reply from' in result.stdout) or ('bytes=' in result.stdout) + + except Exception as e: + return False + + def get_windows_mac(self, ip): + """Get MAC address using Windows arp command""" + try: + # First ensure the IP is in ARP cache by pinging + subprocess.run(['ping', '-n', '1', '-w', '500', ip], + capture_output=True, + creationflags=subprocess.CREATE_NO_WINDOW) + + # Get ARP entry + result = subprocess.run(['arp', '-a', ip], + capture_output=True, text=True) + + # Parse ARP output - Windows format + for line in result.stdout.split('\n'): + if ip in line: + parts = line.split() + for part in parts: + # MAC address format: xx-xx-xx-xx-xx-xx or xx:xx:xx:xx:xx:xx + if '-' in part or ':' in part: + # Standardize to colon format + mac = part.replace('-', ':') + if len(mac) == 17: # Valid MAC length + return mac + + except Exception as e: + pass + + return None + + def scan_ports(self, ip, ports): + """Scan common ports on a host""" + open_ports = [] + + def check_port(port): + try: + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.settimeout(1) + result = sock.connect_ex((ip, port)) + sock.close() + return port if result == 0 else None + except: + return None + + # Scan in parallel + with concurrent.futures.ThreadPoolExecutor(max_workers=20) as executor: + futures = [executor.submit(check_port, port) for port in ports] + for future in concurrent.futures.as_completed(futures): + result = future.result() + if result: + open_ports.append(result) + + return open_ports + + def identify_device(self, ip, hostname, open_ports): + """Identify device type based on IP, hostname, and open ports""" + hostname_lower = hostname.lower() + + # Known devices from your diagram + if ip == '192.168.1.1': + return 'router', '🛜' + elif ip == '192.168.1.159': + return 'server', 'đŸ’ģ' + elif ip == '192.168.1.163': + return 'dns', 'đŸ›Ąī¸' + elif ip == '192.168.1.193': + return 'home_automation', '🏡' + elif ip == '192.168.1.100': + return 'worker', 'đŸ›°ī¸' + elif ip.startswith('192.168.137.'): + return 'worker', 'đŸ›°ī¸' + + # Port-based identification + if 80 in open_ports or 443 in open_ports: + if 3000 in open_ports: # Common for web apps + return 'server', 'đŸ’ģ' + return 'server', 'đŸ’ģ' + + # Hostname patterns + if 'router' in hostname_lower or 'gateway' in hostname_lower: + return 'router', '🛜' + elif 'iot' in hostname_lower or 'smart' in hostname_lower: + return 'iot', 'đŸ“ē' + elif 'laptop' in hostname_lower or 'pc' in hostname_lower: + return 'laptop', '👨‍đŸ’ģ' + elif 'nas' in hostname_lower or 'storage' in hostname_lower: + return 'nas', '🧱' + + return 'unknown', '❓' + + def scan_network_range(self, network_cidr): + """Scan a specific IP range efficiently""" + devices = [] + + try: + network = ipaddress.ip_network(network_cidr, strict=False) + + # For large networks, scan common ranges + if network.num_addresses > 256: + print(f" Large network ({network.num_addresses} addresses), scanning common ranges...") + + # Scan first 50, last 50, and common server IPs + hosts = list(network.hosts()) + scan_ips = [] + + # Common server IPs + common_offsets = [1, 2, 10, 20, 50, 100, 150, 200, 254] + for offset in common_offsets: + if offset < len(hosts): + scan_ips.append(str(hosts[offset])) + + # First and last 20 + scan_ips.extend([str(ip) for ip in hosts[:20]]) + scan_ips.extend([str(ip) for ip in hosts[-20:]]) + + # Your specific IPs from the diagram + specific_ips = [ + '192.168.1.1', '192.168.1.159', '192.168.1.163', + '192.168.1.193', '192.168.1.100' + ] + for ip in specific_ips: + if ipaddress.ip_address(ip) in network: + scan_ips.append(ip) + + scan_ips = list(set(scan_ips)) # Remove duplicates + + else: + # For smaller networks, scan all + scan_ips = [str(ip) for ip in network.hosts()] + + print(f" Scanning {len(scan_ips)} IP addresses...") + + # Scan in batches + batch_size = 20 + for i in range(0, len(scan_ips), batch_size): + batch = scan_ips[i:i + batch_size] + + with concurrent.futures.ThreadPoolExecutor(max_workers=10) as executor: + future_to_ip = {executor.submit(self.scan_single_device, ip): ip for ip in batch} + + for future in concurrent.futures.as_completed(future_to_ip): + device = future.result() + if device: + devices.append(device) + print(f" Found: {device['icon']} {device['hostname']} ({device['ip']})") + + except Exception as e: + print(f" Error scanning {network_cidr}: {e}") + + return devices + + def generate_mermaid_diagram(self, devices): + """Generate Mermaid diagram from discovered devices""" + mermaid = "flowchart TD\n" + + # Styles + mermaid += "%% ---------- styles ----------\n" + mermaid += "classDef internet fill:#e1f5ff,stroke:#007bff\n" + mermaid += "classDef router fill:#fff3cd,stroke:#ffc107\n" + mermaid += "classDef server fill:#ffe6e6,stroke:#dc3545\n" + mermaid += "classDef dns fill:#e6ffe6,stroke:#28a745\n" + mermaid += "classDef homeauto fill:#fff0f5,stroke:#e83e8c\n" + mermaid += "classDef worker fill:#f0e6ff,stroke:#6f42c1\n" + mermaid += "classDef iot fill:#fff9e6,stroke:#fd7e14\n" + mermaid += "classDef unknown fill:#f8f9fa,stroke:#6c757d\n" + mermaid += "\n" + + # Group devices by network + lan_devices = [d for d in devices if d['ip'].startswith('192.168.1.')] + tether_devices = [d for d in devices if d['ip'].startswith('192.168.137.')] + hyperv_devices = [d for d in devices if d['ip'].startswith('172.20.')] + + # LAN network + mermaid += f"subgraph LAN [🏠 LAN 192.168.1.0/24 - {len(lan_devices)} devices]\n" + + # Core infrastructure first + core_ips = ['192.168.1.1', '192.168.1.159', '192.168.1.163', + '192.168.1.193', '192.168.1.100', '192.168.1.49', '192.168.1.165'] + + mermaid += " subgraph CORE [đŸ’ģ Core Infrastructure]\n" + for device in lan_devices: + if device['ip'] in core_ips: + services = ', '.join(device.get('services', []))[:30] + mermaid += f" {device['ip'].replace('.', '_')}[\"{device['icon']} {device['description']}
{device['ip']}
{services}\"]:::server\n" + mermaid += " end\n" + + # Other LAN devices + for device in lan_devices: + if device['ip'] not in core_ips: + node_id = device['ip'].replace('.', '_') + services = ', '.join(device.get('services', []))[:20] + mermaid += f" {node_id}[\"{device['icon']} {device['hostname']}
{device['ip']}
{device['vendor']}\"]:::unknown\n" + + mermaid += "end\n\n" + + # Tether network + if tether_devices: + mermaid += f"subgraph TETHER [đŸ“ļ Tether 192.168.137.0/24 - {len(tether_devices)} devices]\n" + + # Known workers first + known_workers = ['192.168.137.239'] # Hermes + for device in tether_devices: + node_id = device['ip'].replace('.', '_') + if device['ip'] in known_workers: + mermaid += f" {node_id}[\"{device['icon']} {device['description']}
{device['ip']}\"]:::worker\n" + else: + mermaid += f" {node_id}[\"{device['icon']} {device['hostname']}
{device['ip']}\"]:::worker\n" + + mermaid += "end\n\n" + + # Hyper-V network + if hyperv_devices: + mermaid += f"subgraph HYPERV [🔷 Hyper-V 172.20.240.0/20 - {len(hyperv_devices)} devices]\n" + for device in hyperv_devices: + node_id = device['ip'].replace('.', '_') + mermaid += f" {node_id}[\"{device['icon']} {device['hostname']}
{device['ip']}\"]:::unknown\n" + mermaid += "end\n\n" + + # Connections + mermaid += "%% ---------- connections ----------\n" + mermaid += "CORE --> TETHER\n" + mermaid += "CORE --> HYPERV\n" + + return mermaid + + def scan_single_device(self, ip): + """Scan a single device with enhanced identification""" + try: + if not self.windows_ping(ip): + return None + + # Get hostname + try: + hostname = socket.gethostbyaddr(ip)[0] + except: + hostname = ip + + # Get MAC address + mac = self.get_windows_mac(ip) + + # Scan common ports + common_ports = [21, 22, 23, 80, 443, 8080, 3000, 3389, 5000, + 8000, 8008, 8443, 9000, 9092] + open_ports = self.scan_ports(ip, common_ports) + + # Enhanced identification + device_type, icon, description, services = self.better_identify_device( + ip, hostname, open_ports, mac + ) + + # Build device info + device = { + 'ip': ip, + 'hostname': hostname, + 'mac': mac, + 'vendor': self.get_vendor_from_mac(mac) if mac else "Unknown", + 'open_ports': open_ports, + 'services': services, + 'type': device_type, + 'description': description, + 'icon': icon, + 'last_seen': datetime.now().isoformat() + } + + return device + + except Exception as e: + print(f" Error scanning {ip}: {e}") + return None + + def discover_network(self): + """Main discovery function""" + print("🔍 Starting Windows Network Discovery") + print("=" * 50) + + # Check admin rights + is_admin = ctypes.windll.shell32.IsUserAnAdmin() != 0 + if not is_admin: + print("âš ī¸ Running without Administrator privileges.") + print(" Some features (ARP scanning) may be limited.") + print(" For best results, run as Administrator.\n") + + # Get networks to scan + networks = self.get_local_networks() + print(f"📡 Networks to scan: {networks}\n") + + all_devices = [] + + # Scan each network + for network in networks: + print(f"đŸ“ļ Scanning {network}...") + devices = self.scan_network_range(network) + all_devices.extend(devices) + print(f" Found {len(devices)} devices in {network}\n") + + # Save results + if all_devices: + self.save_results(all_devices) + else: + print("❌ No devices found. Possible issues:") + print(" 1. Firewall blocking ICMP (ping) requests") + print(" 2. Devices not responding to ping") + print(" 3. Network segmentation or VLANs") + print("\n💡 Try:") + print(" â€ĸ Run as Administrator") + print(" â€ĸ Temporarily disable Windows Firewall") + print(" â€ĸ Check if devices are on same VLAN") + + return all_devices + + def enhance_service_detection(self, ip, open_ports): + """Enhanced service detection based on common port patterns""" + services = [] + + port_services = { + 80: 'http', 443: 'https', 22: 'ssh', 23: 'telnet', + 21: 'ftp', 25: 'smtp', 53: 'dns', 67: 'dhcp', + 68: 'dhcp-client', 69: 'tftp', 88: 'kerberos', + 110: 'pop3', 119: 'nntp', 123: 'ntp', 135: 'msrpc', + 137: 'netbios-ns', 138: 'netbios-dgm', 139: 'netbios-ssn', + 143: 'imap', 161: 'snmp', 162: 'snmptrap', 389: 'ldap', + 445: 'smb', 465: 'smtps', 514: 'syslog', 587: 'smtp-submission', + 631: 'ipp', 636: 'ldaps', 993: 'imaps', 995: 'pop3s', + 1433: 'mssql', 1521: 'oracle', 1723: 'pptp', 1883: 'mqtt', + 1900: 'upnp', 2082: 'cpanel', 2083: 'cpanel-ssl', + 2086: 'whm', 2087: 'whm-ssl', 2095: 'webmail', + 2096: 'webmail-ssl', 2181: 'zookeeper', 2375: 'docker', + 2376: 'docker-ssl', 2377: 'docker-swarm', 2379: 'etcd', + 2380: 'etcd-peer', 3000: 'nodejs', 3306: 'mysql', + 3389: 'rdp', 5000: 'upnp/commplex-link', 5432: 'postgresql', + 5672: 'amqp', 5900: 'vnc', 5938: 'teamviewer', + 5984: 'couchdb', 6379: 'redis', 6443: 'kubernetes', + 6667: 'irc', 8000: 'http-alt', 8008: 'http-alt', + 8080: 'http-proxy', 8081: 'http-alt', 8443: 'https-alt', + 8888: 'http-alt', 9000: 'sonarqube', 9001: 'tor', + 9042: 'cassandra', 9092: 'kafka', 9100: 'pdl-datastream', + 9200: 'elasticsearch', 9300: 'elasticsearch-cluster', + 9418: 'git', 11211: 'memcached', 27017: 'mongodb', + 27018: 'mongodb-shard', 27019: 'mongodb-config', + 28017: 'mongodb-http', 50000: 'db2', 50070: 'hadoop-namenode', + 50075: 'hadoop-datanode', 50090: 'hadoop-secondarynamenode' + } + + for port in open_ports: + if port in port_services: + services.append(f"{port_services[port]}({port})") + else: + services.append(f"unknown({port})") + + return services + + def better_identify_device(self, ip, hostname, open_ports, mac): + """Improved device identification with service awareness""" + hostname_lower = hostname.lower() + services = self.enhance_service_detection(ip, open_ports) + + # Your specific network devices + if ip == '192.168.1.1': + return 'router', '🛜', 'Router/Gateway', ['upnp(5000)'] + + elif ip == '192.168.1.159': + core_services = [] + if 3000 in open_ports: + core_services.append('gitea(3000)') + if 443 in open_ports or 80 in open_ports: + core_services.append('traefik(80/443)') + if 22 in open_ports: + core_services.append('ssh(22)') + return 'server', 'đŸ’ģ', 'Core Server (Traefik + Gitea)', core_services + + elif ip == '192.168.1.163': + infra_services = [] + if 8000 in open_ports: + infra_services.append('artifactory(8000)') + if 8080 in open_ports: + infra_services.append('adguard(8080)') + if 22 in open_ports: + infra_services.append('ssh(22)') + return 'dns', 'đŸ›Ąī¸', 'Infra (AdGuard + Artifactory)', infra_services + + elif ip == '192.168.1.193': + return 'home_automation', '🏡', 'Home Assistant', [] + + elif ip == '192.168.1.100': + return 'worker', 'đŸ–Ĩī¸', 'Atlas Worker Node', ['ssh(22)'] + + elif ip == '192.168.1.49': + return 'server', 'đŸ’ģ', 'Web Server', ['http(80)', 'https(443)', 'http-alt(8080)'] + + elif ip == '192.168.1.165': + return 'server', 'đŸ’ģ', 'HP Server/Printer', ['http(80)', 'https(443)', 'http-alt(8080)'] + + # Enhanced detection based on services + if 8008 in open_ports and 8443 in open_ports: + # Chromecast/Google Cast devices + if mac and mac.startswith(('40:9f:38', 'f8:8f:ca', 'e4:f8:9c')): + return 'iot', 'đŸ“ē', 'Chromecast/Google Device', ['cast(8008/8443)'] + return 'media', '📱', 'Media Device', ['cast(8008/8443)'] + + if 3389 in open_ports: + return 'laptop', 'đŸ’ģ', 'Windows PC (RDP)', ['rdp(3389)'] + + if 22 in open_ports and ip.startswith('192.168.137.'): + # Tether network workers + if ip == '192.168.137.239' and hostname == 'hermes.lan': + return 'worker', 'đŸ›°ī¸', 'Hermes Worker', ['ssh(22)'] + return 'worker', 'đŸ›°ī¸', 'Tether Worker Node', ['ssh(22)'] + + # MAC address vendor lookup + if mac: + vendor = self.get_vendor_from_mac(mac) + if any(v in vendor.lower() for v in ['sonos', 'roku', 'philips hue', 'smart']): + return 'iot', '🏠', f'IoT ({vendor})', services + elif 'raspberry' in vendor.lower(): + return 'server', '🍓', f'Raspberry Pi', services + elif any(v in vendor.lower() for v in ['cisco', 'ubiquiti', 'tplink', 'netgear']): + return 'network', '📡', f'Network Device ({vendor})', services + + # Default based on IP range + if ip.startswith('192.168.137.'): + return 'worker', 'đŸ›°ī¸', 'Tether Network Device', services + elif ip.startswith('172.20.'): + return 'hyperv', '🔷', 'Hyper-V Internal Network', services + + return 'unknown', '❓', 'Unknown Device', services + + def get_vendor_from_mac(self, mac): + """Get vendor from MAC address OUI""" + if not mac: + return "Unknown" + + # Clean MAC address + mac_clean = mac.replace(':', '').replace('-', '').upper()[:6] + + # Common OUI prefixes + oui_db = { + 'B8D526': 'ASUSTek', '409F38': 'Google', 'ECB5FA': 'Giga-Byte Technology', + '4827E2': 'Huawei', '0417B6': 'ASUSTek', 'EAE7A7': 'Shenzhen Bolutek', + 'CCF411': 'Murata Manufacturing', 'B04A39': 'Xiaomi', '50EBF6': 'ASUSTek', + '34CDB0': 'Huawei', '001E06': 'Actiontec', '48BA4E': 'Hewlett Packard', + 'E4B97A': 'Huawei', '7CDFA1': 'Huawei', '107B44': 'TP-Link', + '4C4929': 'Huawei', 'AC6784': 'Huawei', '1831BF': 'Apple', + '382CE5': 'Samsung', 'FEE772': 'Samsung', '4CBAD7': 'Huawei', + 'C8C9A3': 'LG Electronics', 'C45BBE': 'PEGATRON' + } + + return oui_db.get(mac_clean, "Unknown Manufacturer") + + def save_results(self, devices): + """Save results to JSON and generate summary""" + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + + # JSON output + output = { + 'discovery_time': datetime.now().isoformat(), + 'total_devices': len(devices), + 'networks_scanned': self.get_local_networks(), + 'devices': devices + } + + json_file = f"network_discovery_{timestamp}.json" + with open(json_file, 'w') as f: + json.dump(output, f, indent=2) + + # Generate summary report + summary_file = f"network_summary_{timestamp}.txt" + with open(summary_file, 'w') as f: + f.write("=" * 60 + "\n") + f.write("NETWORK DISCOVERY SUMMARY\n") + f.write("=" * 60 + "\n\n") + + f.write(f"Discovery Time: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n") + f.write(f"Total Devices Found: {len(devices)}\n\n") + + # Group by type + type_count = {} + for device in devices: + dev_type = device['type'] + type_count[dev_type] = type_count.get(dev_type, 0) + 1 + + f.write("Devices by Type:\n") + f.write("-" * 40 + "\n") + for dev_type, count in sorted(type_count.items()): + icon = self.icon_mapping.get(dev_type, '❓') + f.write(f"{icon} {dev_type}: {count}\n") + + f.write("\n" + "=" * 60 + "\n") + f.write("DETAILED DEVICE LIST\n") + f.write("=" * 60 + "\n\n") + + for device in devices: + f.write(f"{device['icon']} {device['hostname']} ({device['ip']})\n") + f.write(f" Type: {device['type']}\n") + f.write(f" MAC: {device.get('mac', 'Unknown')}\n") + if device['open_ports']: + f.write(f" Open Ports: {', '.join(map(str, device['open_ports']))}\n") + f.write("\n") + + print("=" * 50) + print("✅ DISCOVERY COMPLETE") + print("=" * 50) + print(f"📊 Found {len(devices)} total devices") + print(f"💾 Results saved to:") + print(f" â€ĸ {json_file} (JSON data)") + print(f" â€ĸ {summary_file} (Text summary)") + + # Print quick summary + print("\n📋 Quick Summary:") + type_count = {} + for device in devices: + dev_type = device['type'] + type_count[dev_type] = type_count.get(dev_type, 0) + 1 + + for dev_type, count in sorted(type_count.items()): + icon = self.icon_mapping.get(dev_type, '❓') + print(f" {icon} {dev_type}: {count}") + + +# Run the discovery +if __name__ == "__main__": + try: + discoverer = WindowsNetworkDiscoverer() + discoverer.discover_network() + except KeyboardInterrupt: + print("\nâš ī¸ Discovery interrupted by user") + except Exception as e: + print(f"❌ Error: {e}") + import traceback + + traceback.print_exc() \ No newline at end of file diff --git a/src/ico.py b/src/ico.py new file mode 100644 index 0000000..df1024c --- /dev/null +++ b/src/ico.py @@ -0,0 +1,14 @@ +from PIL import Image +import cairosvg + +# Convert SVG to PNG first +png_path = "../public/favicon.png" +ico_path = "../public/favicon.ico" + +cairosvg.svg2png(url="../public/favicon.svg", write_to=png_path) + +# Convert PNG → ICO +img = Image.open(png_path) +img.save(ico_path, format='ICO', sizes=[(16,16), (32,32), (48,48), (64,64)]) + +ico_path diff --git a/src/json_to_mermaid.py b/src/json_to_mermaid.py new file mode 100644 index 0000000..0156b54 --- /dev/null +++ b/src/json_to_mermaid.py @@ -0,0 +1,175 @@ +import json +import sys + + +def json_to_mermaid(json_file_path, output_mmd_path='network_diagram.mmd'): + """ + Converts the network discovery JSON to a Mermaid flowchart. + """ + with open(json_file_path, 'r', encoding='utf-8') as f: + data = json.load(f) + + mermaid_lines = [] + mermaid_lines.append('flowchart TD') + + # 1. Define styling classes + mermaid_lines.append(' %% ---------- styles ----------') + mermaid_lines.append(' classDef internet fill:#e1f5ff,stroke:#007bff') + mermaid_lines.append(' classDef router fill:#fff3cd,stroke:#ffc107') + mermaid_lines.append(' classDef server fill:#ffe6e6,stroke:#dc3545') + mermaid_lines.append(' classDef dns fill:#e6ffe6,stroke:#28a745') + mermaid_lines.append(' classDef homeauto fill:#fff0f5,stroke:#e83e8c') + mermaid_lines.append(' classDef worker fill:#f0e6ff,stroke:#6f42c1') + mermaid_lines.append(' classDef iot fill:#fff9e6,stroke:#fd7e14') + mermaid_lines.append(' classDef unknown fill:#f8f9fa,stroke:#6c757d') + mermaid_lines.append('') + + # 2. Create a mapping for device type to CSS class + type_to_class = { + 'router': 'router', + 'server': 'server', + 'dns': 'dns', + 'home_automation': 'homeauto', + 'worker': 'worker', + 'iot': 'iot' + } + + # 3. Group devices by network + lan_devices = [] + tether_devices = [] + hyperv_devices = [] + other_devices = [] + + for device in data['devices']: + ip = device.get('ip', '') + if ip.startswith('192.168.1.'): + lan_devices.append(device) + elif ip.startswith('192.168.137.'): + tether_devices.append(device) + elif ip.startswith('172.20.'): + hyperv_devices.append(device) + else: + other_devices.append(device) + + # 4. Create LAN Subgraph + mermaid_lines.append(f' subgraph LAN["LAN 192.168.1.0/24 - {len(lan_devices)} devices"]') + + # Core infrastructure first (from your original diagram) + core_ips = ['192.168.1.1', '192.168.1.159', '192.168.1.163', + '192.168.1.193', '192.168.1.100', '192.168.1.49', '192.168.1.165'] + + # Add a core server subgraph + mermaid_lines.append(' subgraph CORE["Core Infrastructure"]') + for device in lan_devices: + if device['ip'] in core_ips: + node_id = device['ip'].replace('.', '_') + # Use ASCII-friendly labels instead of emoji for Windows compatibility + icon_map = { + 'router': '[Router]', + 'server': '[Server]', + 'dns': '[DNS]', + 'home_automation': '[Home]', + 'worker': '[Worker]', + 'unknown': '[?]' + } + icon = icon_map.get(device.get('type', 'unknown'), '[?]') + label = f"{icon} {device.get('hostname', device['ip'])}" + mermaid_lines.append(f' {node_id}["{label}
{device["ip"]}"]') + + # Apply CSS class + dev_type = device.get('type', 'unknown') + css_class = type_to_class.get(dev_type, 'unknown') + mermaid_lines.append(f' class {node_id} {css_class};') + mermaid_lines.append(' end') + + # Other LAN devices + for device in lan_devices: + if device['ip'] not in core_ips: + node_id = device['ip'].replace('.', '_') + icon_map = { + 'router': '[R]', + 'server': '[S]', + 'dns': '[D]', + 'home_automation': '[H]', + 'worker': '[W]', + 'unknown': '[?]' + } + icon = icon_map.get(device.get('type', 'unknown'), '[?]') + label = f"{icon} {device.get('hostname', device['ip'])}" + mermaid_lines.append(f' {node_id}["{label}
{device["ip"]}"]') + + dev_type = device.get('type', 'unknown') + css_class = type_to_class.get(dev_type, 'unknown') + mermaid_lines.append(f' class {node_id} {css_class};') + + mermaid_lines.append(' end') + mermaid_lines.append('') + + # 5. Create TETHER Subgraph + if tether_devices: + mermaid_lines.append(f' subgraph TETHER["Tether 192.168.137.0/24 - {len(tether_devices)} devices"]') + + # Known workers first + known_workers = ['192.168.137.239'] # Hermes + for device in tether_devices: + node_id = device['ip'].replace('.', '_') + if device['ip'] in known_workers: + label = f"[Hermes] {device.get('hostname', device['ip'])}
{device['ip']}" + else: + label = f"[Worker] {device.get('hostname', device['ip'])}
{device['ip']}" + mermaid_lines.append(f' {node_id}["{label}"]') + mermaid_lines.append(f' class {node_id} worker;') + + mermaid_lines.append(' end') + mermaid_lines.append('') + + # 6. Add connections + mermaid_lines.append(' %% ---------- Connections ----------') + # Connect core server to tether network + if tether_devices and any(d['ip'] == '192.168.1.159' for d in lan_devices): + mermaid_lines.append(' 192_168_1_159 -.-> TETHER') + # Router connects to everything + if any(d['ip'] == '192.168.1.1' for d in lan_devices): + mermaid_lines.append(' 192_168_1_1 --> LAN') + + # 7. Write the Mermaid code to a file with UTF-8 encoding + with open(output_mmd_path, 'w', encoding='utf-8') as f: + f.write('\n'.join(mermaid_lines)) + + print(f"✓ Mermaid diagram generated: {output_mmd_path}") + print(f" Devices: {len(lan_devices)} LAN, {len(tether_devices)} Tether") + if hyperv_devices: + print(f" {len(hyperv_devices)} Hyper-V") + print(f" Open {output_mmd_path} and copy contents to https://mermaid.live") + return output_mmd_path + + +# --- Run the conversion with command-line support --- +if __name__ == "__main__": + # Default filenames + input_json = "network_discovery_20251207_160645.json" + output_mmd = "network_topology.mmd" + + # Check for command-line arguments + if len(sys.argv) > 1: + input_json = sys.argv[1] + if len(sys.argv) > 2: + output_mmd = sys.argv[2] + + try: + mmd_file = json_to_mermaid(input_json, output_mmd) + except FileNotFoundError: + print(f"✗ Error: Could not find file '{input_json}'") + print(" Available JSON files:") + import os + + for file in os.listdir('.'): + if file.endswith('.json'): + print(f" - {file}") + except json.JSONDecodeError as e: + print(f"✗ Error: Invalid JSON in '{input_json}': {e}") + except Exception as e: + print(f"✗ Unexpected error: {e}") + import traceback + + traceback.print_exc() \ No newline at end of file diff --git a/src/lan_architecture.py b/src/lan_architecture.py new file mode 100644 index 0000000..5ae0b8a --- /dev/null +++ b/src/lan_architecture.py @@ -0,0 +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 diff --git a/src/main.py b/src/main.py new file mode 100644 index 0000000..11b1456 --- /dev/null +++ b/src/main.py @@ -0,0 +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()