162 lines
4.2 KiB
Bash
Executable File
162 lines
4.2 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
set -e
|
|
|
|
BASE_DIR="$HOME/homelab"
|
|
DASHBOARD_DIR="$BASE_DIR/dashboard"
|
|
TEMPLATES_DIR="$DASHBOARD_DIR/templates"
|
|
DOCKER_COMPOSE_FILE="$BASE_DIR/docker-compose.yml"
|
|
|
|
echo "Creating dashboard directory structure..."
|
|
mkdir -p "$TEMPLATES_DIR"
|
|
|
|
echo "Writing app.py..."
|
|
cat > "$DASHBOARD_DIR/app.py" << 'EOF'
|
|
from flask import Flask, render_template, jsonify, request
|
|
import subprocess
|
|
import requests
|
|
|
|
app = Flask(__name__)
|
|
|
|
DEVICES = {
|
|
"desktop": "AA:BB:CC:DD:EE:FF", # Replace with real MAC
|
|
}
|
|
|
|
DEVICE_IPS = {
|
|
"desktop": "192.168.1.10", # Replace with real IP
|
|
}
|
|
|
|
WOL_API_URL = "https://wol.zea.lt"
|
|
|
|
def ping_host(ip):
|
|
try:
|
|
subprocess.check_output(['ping', '-c', '1', '-W', '1', ip])
|
|
return True
|
|
except subprocess.CalledProcessError:
|
|
return False
|
|
|
|
@app.route('/')
|
|
def dashboard():
|
|
status = {dev: ping_host(ip) for dev, ip in DEVICE_IPS.items()}
|
|
return render_template('dashboard.html', devices=DEVICES, status=status)
|
|
|
|
@app.route('/wake/<device>', methods=['POST'])
|
|
def wake_device(device):
|
|
if device not in DEVICES:
|
|
return jsonify({"error": "Device not found"}), 404
|
|
try:
|
|
r = requests.post(f"{WOL_API_URL}/wake/{device}")
|
|
if r.ok:
|
|
return jsonify({"message": f"Wake command sent to {device}"})
|
|
else:
|
|
return jsonify({"error": f"Failed to send wake command: {r.text}"}), r.status_code
|
|
except Exception as e:
|
|
return jsonify({"error": str(e)}), 500
|
|
|
|
if __name__ == '__main__':
|
|
app.run(host='0.0.0.0', port=8080)
|
|
EOF
|
|
|
|
echo "Writing dashboard.html template..."
|
|
cat > "$TEMPLATES_DIR/dashboard.html" << 'EOF'
|
|
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<title>HomeLab Dashboard</title>
|
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" />
|
|
</head>
|
|
<body class="p-4">
|
|
<h1>HomeLab Dashboard</h1>
|
|
|
|
<h2>Services</h2>
|
|
<ul>
|
|
<li><a href="https://git.zea.lt" target="_blank">Gitea</a></li>
|
|
<li><a href="https://registry.zea.lt" target="_blank">Docker Registry</a></li>
|
|
<li><a href="https://traefik.zea.lt" target="_blank">Traefik Dashboard</a></li>
|
|
<li><a href="https://wol.zea.lt" target="_blank">Wake-on-LAN API</a></li>
|
|
</ul>
|
|
|
|
<h2>Devices</h2>
|
|
<table class="table table-bordered" style="max-width:600px;">
|
|
<thead>
|
|
<tr>
|
|
<th>Device</th>
|
|
<th>Status</th>
|
|
<th>Action</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for device, mac in devices.items() %}
|
|
<tr>
|
|
<td>{{ device }}</td>
|
|
<td>
|
|
{% if status[device] %}
|
|
<span class="badge bg-success">Online</span>
|
|
{% else %}
|
|
<span class="badge bg-danger">Offline</span>
|
|
{% endif %}
|
|
</td>
|
|
<td>
|
|
<button class="btn btn-primary btn-sm" onclick="wake('{{ device }}')">Wake</button>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
|
|
<script>
|
|
async function wake(device) {
|
|
const resp = await fetch(`/wake/${device}`, { method: 'POST' });
|
|
const data = await resp.json();
|
|
alert(data.message || data.error);
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|
|
EOF
|
|
|
|
echo "Writing Dockerfile..."
|
|
cat > "$DASHBOARD_DIR/Dockerfile" << 'EOF'
|
|
FROM python:3.11-slim
|
|
|
|
WORKDIR /app
|
|
|
|
COPY requirements.txt requirements.txt
|
|
RUN pip install -r requirements.txt
|
|
|
|
COPY . .
|
|
|
|
EXPOSE 8080
|
|
|
|
CMD ["python", "app.py"]
|
|
EOF
|
|
|
|
echo "Writing requirements.txt..."
|
|
cat > "$DASHBOARD_DIR/requirements.txt" << 'EOF'
|
|
Flask==2.3.2
|
|
requests==2.31.0
|
|
EOF
|
|
|
|
# Backup docker-compose.yml just in case
|
|
cp "$DOCKER_COMPOSE_FILE" "$DOCKER_COMPOSE_FILE.bak"
|
|
|
|
echo "Updating docker-compose.yml to add dashboard service..."
|
|
# Append dashboard service before the last line if it ends with '...' or just at end
|
|
# If docker-compose.yml uses version 3+ it should be fine
|
|
cat >> "$DOCKER_COMPOSE_FILE" << 'EOF'
|
|
|
|
dashboard:
|
|
build: ./dashboard
|
|
container_name: dashboard
|
|
restart: always
|
|
labels:
|
|
- "traefik.enable=true"
|
|
- "traefik.http.routers.dashboard.rule=Host(`dashboard.zea.lt`)"
|
|
- "traefik.http.routers.dashboard.entrypoints=websecure"
|
|
- "traefik.http.routers.dashboard.tls.certresolver=leresolver"
|
|
EOF
|
|
|
|
echo "Setup complete! You can now run:"
|
|
echo "cd $BASE_DIR && docker-compose build dashboard && docker-compose up -d dashboard"
|
|
|