Initial homelab infrastructure
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Executable
+161
@@ -0,0 +1,161 @@
|
||||
#!/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"
|
||||
|
||||
Reference in New Issue
Block a user