From f7b0c28d1a3c8487d1ed2c6f2d286c8f4b0a36c5 Mon Sep 17 00:00:00 2001 From: Marius Mutu Date: Thu, 4 Sep 2025 16:19:09 +0300 Subject: [PATCH] Initial commit - WOL Manager Flask application MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Added containerized Flask web application for Wake-on-LAN management - Implemented computer management with file-based configuration - Added network scanning and device discovery functionality - Included Docker setup with privileged networking for WOL operations 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- CLAUDE.md | 59 ++++ Dockerfile | 25 ++ app/app.py | 200 ++++++++++++ app/requirements.txt | 2 + app/templates/index.html | 639 +++++++++++++++++++++++++++++++++++++++ docker-compose.yml | 25 ++ readme.md | 65 ++++ start.sh | 13 + 8 files changed, 1028 insertions(+) create mode 100644 CLAUDE.md create mode 100644 Dockerfile create mode 100644 app/app.py create mode 100644 app/requirements.txt create mode 100644 app/templates/index.html create mode 100644 docker-compose.yml create mode 100644 readme.md create mode 100644 start.sh diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..1c1bfbb --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,59 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +See README.md for complete project description, installation instructions, and usage details. + +This is a Wake-on-LAN (WOL) Manager - a containerized Flask web application for managing and remotely waking computers on a local network. + +## Architecture & Code Structure + +- **Backend**: Flask web application (`app/app.py`) with RESTful API endpoints +- **Frontend**: Single-page application with vanilla JavaScript (`app/templates/index.html`) +- **Storage**: File-based configuration in `/data/wol-computers.conf` +- **Deployment**: Docker with docker-compose, requires privileged networking + +### Key Components + +- `WOLManager` class in `app/app.py`: Core logic for computer management, WOL operations, and network scanning +- Configuration format: `name|mac|ip` (pipe-separated values in `/data/wol-computers.conf`) +- Dependencies: `wakeonlan`, `nmap`, `ping`, `arp` system tools + +## Development Commands + +**For installation and basic usage, see README.md** + +### Docker Development +```bash +# Development build and run +docker-compose up -d --build + +# View real-time logs +docker-compose logs -f wol-web + +# Shell access to container +docker-compose exec wol-web bash +``` + +### Network Requirements +- Runs on port 8088 (external) → 8080 (internal) +- Requires `NET_ADMIN` and `NET_RAW` capabilities +- Uses `network_mode: host` for WOL packet transmission +- Must run with `privileged: true` for network operations + +## API Endpoints + +- `GET /api/computers` - List configured computers with status +- `POST /api/wake` - Wake specific computer (`{mac, name, ip}`) +- `POST /api/wake-all` - Wake all configured computers +- `POST /api/add` - Add new computer (`{name, mac, ip}`) +- `GET /api/scan` - Network scan for devices + +## Development Notes + +- Application uses Romanian language in UI +- No authentication/authorization implemented +- Configuration persisted in volume-mounted `/data` directory +- Flask runs in debug=False mode in container \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..ac749f9 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,25 @@ +FROM python:3.11-slim + +# Instalează dependențele sistem +RUN apt-get update && apt-get install -y \ + wakeonlan \ + nmap \ + iputils-ping \ + net-tools \ + && rm -rf /var/lib/apt/lists/* + +# Setează directorul de lucru +WORKDIR /app + +# Instalează dependențele Python +COPY app/requirements.txt . +RUN pip install -r requirements.txt + +# Copiază aplicația +COPY app/ . + +# Expune portul +EXPOSE 8080 + +# Comandă de start +CMD ["python", "app.py"] \ No newline at end of file diff --git a/app/app.py b/app/app.py new file mode 100644 index 0000000..86b4995 --- /dev/null +++ b/app/app.py @@ -0,0 +1,200 @@ +#!/usr/bin/env python3 +import os +import subprocess +import json +import re +from flask import Flask, render_template, request, jsonify +import socket + +app = Flask(__name__) +CONFIG_FILE = '/data/wol-computers.conf' + +class WOLManager: + def __init__(self): + self.ensure_config_exists() + + def ensure_config_exists(self): + os.makedirs('/data', exist_ok=True) + if not os.path.exists(CONFIG_FILE): + with open(CONFIG_FILE, 'w') as f: + f.write("# Format: name|mac|ip\n") + + def load_computers(self): + computers = [] + if os.path.exists(CONFIG_FILE): + with open(CONFIG_FILE, 'r') as f: + for line in f: + line = line.strip() + if line and not line.startswith('#'): + parts = line.split('|') + if len(parts) >= 2: + computer = { + 'name': parts[0], + 'mac': parts[1], + 'ip': parts[2] if len(parts) > 2 else '', + 'status': self.ping_computer(parts[2]) if len(parts) > 2 and parts[2] else 'unknown' + } + computers.append(computer) + return computers + + def ping_computer(self, ip): + if not ip: + return 'unknown' + try: + result = subprocess.run(['ping', '-c', '1', '-W', '2', ip], + capture_output=True, timeout=5) + return 'online' if result.returncode == 0 else 'offline' + except: + return 'unknown' + + def wake_computer(self, mac, name, ip=''): + try: + # Trimite magic packet + result = subprocess.run(['wakeonlan', mac], capture_output=True, text=True) + + if result.returncode == 0: + # Dacă avem IP, verifică dacă s-a trezit + if ip: + for i in range(10): # 30 secunde total + if self.ping_computer(ip) == 'online': + return { + 'success': True, + 'message': f'{name} s-a trezit după {i*3} secunde!', + 'status': 'online' + } + if i < 9: # Nu aștepta după ultima încercare + subprocess.run(['sleep', '3']) + + return { + 'success': True, + 'message': f'Magic packet trimis pentru {name}, dar nu răspunde la ping', + 'status': 'unknown' + } + else: + return { + 'success': True, + 'message': f'Magic packet trimis pentru {name}!', + 'status': 'unknown' + } + else: + return { + 'success': False, + 'message': f'Eroare la trimiterea magic packet: {result.stderr}', + 'status': 'error' + } + except Exception as e: + return { + 'success': False, + 'message': f'Eroare: {str(e)}', + 'status': 'error' + } + + def add_computer(self, name, mac, ip=''): + # Validare MAC + mac_pattern = r'^[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}$' + if not re.match(mac_pattern, mac): + return {'success': False, 'message': 'MAC address invalid!'} + + # Adaugă în fișier + with open(CONFIG_FILE, 'a') as f: + f.write(f"{name}|{mac}|{ip}\n") + + return {'success': True, 'message': f'Calculator {name} adăugat!'} + + def scan_network(self): + try: + # Detectează rețeaua locală + result = subprocess.run(['ip', 'route'], capture_output=True, text=True) + network = '192.168.1.0/24' # default + + for line in result.stdout.split('\n'): + if 'default' in line: + parts = line.split() + if len(parts) >= 3: + gateway = parts[2] + # Construiește rețeaua bazată pe gateway + network_parts = gateway.split('.') + network = f"{network_parts[0]}.{network_parts[1]}.{network_parts[2]}.0/24" + break + + # Scanează rețeaua + subprocess.run(['nmap', '-sn', network], capture_output=True, timeout=30) + + # Citește ARP table + result = subprocess.run(['arp', '-a'], capture_output=True, text=True) + + scanned = [] + for line in result.stdout.split('\n'): + # Regex pentru parsarea ARP + match = re.search(r'\((\d+\.\d+\.\d+\.\d+)\).*([0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2})', line) + if match: + ip = match.group(1) + mac = match.group(2) + hostname = line.split()[0] if line.split() else 'unknown' + + scanned.append({ + 'ip': ip, + 'mac': mac, + 'hostname': hostname, + 'status': self.ping_computer(ip) + }) + + return {'success': True, 'computers': scanned} + except Exception as e: + return {'success': False, 'message': f'Eroare la scanare: {str(e)}'} + +wol_manager = WOLManager() + +@app.route('/') +def index(): + return render_template('index.html') + +@app.route('/api/computers') +def get_computers(): + return jsonify(wol_manager.load_computers()) + +@app.route('/api/wake', methods=['POST']) +def wake_computer(): + data = request.get_json() + result = wol_manager.wake_computer( + data.get('mac'), + data.get('name'), + data.get('ip', '') + ) + return jsonify(result) + +@app.route('/api/wake-all', methods=['POST']) +def wake_all(): + computers = wol_manager.load_computers() + results = [] + + for computer in computers: + result = wol_manager.wake_computer( + computer['mac'], + computer['name'], + computer.get('ip', '') + ) + results.append({ + 'name': computer['name'], + 'result': result + }) + + return jsonify({'results': results}) + +@app.route('/api/add', methods=['POST']) +def add_computer(): + data = request.get_json() + result = wol_manager.add_computer( + data.get('name'), + data.get('mac'), + data.get('ip', '') + ) + return jsonify(result) + +@app.route('/api/scan') +def scan_network(): + result = wol_manager.scan_network() + return jsonify(result) + +if __name__ == '__main__': + app.run(host='0.0.0.0', port=8080, debug=False) \ No newline at end of file diff --git a/app/requirements.txt b/app/requirements.txt new file mode 100644 index 0000000..2e330e7 --- /dev/null +++ b/app/requirements.txt @@ -0,0 +1,2 @@ +flask==2.3.3 +requests==2.31.0 \ No newline at end of file diff --git a/app/templates/index.html b/app/templates/index.html new file mode 100644 index 0000000..7770d6c --- /dev/null +++ b/app/templates/index.html @@ -0,0 +1,639 @@ + + + + + + Wake-on-LAN Manager + + + +
+

🚀 Wake-on-LAN Manager

+ +
+ +
+ + + + +
+ +
+ +
+
+ + + + + + + + + + \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..f93f1da --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,25 @@ +version: '3.8' + +services: + wol-web: + build: . + container_name: wol-manager + restart: unless-stopped + ports: + - "8088:8080" + volumes: + - ./data:/data + networks: + - wol-network + environment: + - PYTHONUNBUFFERED=1 + # Necesare pentru Wake-on-LAN + network_mode: host + privileged: true + cap_add: + - NET_ADMIN + - NET_RAW + +networks: + wol-network: + driver: bridge \ No newline at end of file diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..8cffe3a --- /dev/null +++ b/readme.md @@ -0,0 +1,65 @@ +# Wake-on-LAN Manager + +Aplicație web pentru managementul și trezirea calculatoarelor din rețeaua locală folosind Wake-on-LAN magic packets. + +## Funcționalități + +- 🚀 Interfață web modernă pentru gestionarea calculatoarelor +- ⚡ Trimitere magic packets Wake-on-LAN +- 🔍 Scanare automată a rețelei pentru detectarea dispozitivelor +- 📱 Design responsive (mobile-friendly) +- 🐳 Containerizată cu Docker pentru deployment ușor + +## Instalare și Pornire + +### Prima pornire (recomandat) +```bash +./start.sh +``` + +### Manual cu Docker Compose +```bash +# Creează directorul pentru date +mkdir -p data + +# Pornește serviciile +docker-compose up -d + +# Vezi logs +docker-compose logs -f wol-web + +# Oprește serviciile +docker-compose down +``` + +## Accesare + +Aplicația va fi disponibilă pe: **http://IP_HOST:8088** + +## Structura Proiectului + +``` +├── app/ # Directorul aplicației +│ ├── app.py # Aplicația Flask principală +│ ├── requirements.txt # Dependențele Python +│ └── templates/ +│ └── index.html # Interfața web +├── Dockerfile # Configurație Docker +├── docker-compose.yml # Orchestrare servicii +├── start.sh # Script de pornire rapidă +├── CLAUDE.md # Ghid pentru Claude Code +└── data/ # Directorul pentru configurații (creat automat) +``` + +## Configurare + +Calculatoarele sunt stocate în fișierul `data/wol-computers.conf` cu formatul: +``` +nume_calculator|adresa_mac|adresa_ip +``` + +Exemplu: +``` +PC-Birou|00:11:22:33:44:55|192.168.1.100 +Laptop-Gaming|aa:bb:cc:dd:ee:ff|192.168.1.101 +``` \ No newline at end of file diff --git a/start.sh b/start.sh new file mode 100644 index 0000000..a1a9430 --- /dev/null +++ b/start.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +echo "🚀 Pornesc Wake-on-LAN Manager..." + +# Creează directorul pentru date +mkdir -p data + +# Pornește containerele +docker-compose up -d + +echo "✅ Aplicația rulează pe http://IP_LXC:8088" +echo "📊 Vezi logs cu: docker-compose logs -f" +echo "⏹️ Oprește cu: docker-compose down" \ No newline at end of file