diff --git a/.gitignore b/.gitignore
index 8501166..10ed8da 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,7 +9,6 @@ Thumbs.db
# Env
.env
.env.*
-<<<<<<< HEAD
*.bak
*.swp
*.tmp
@@ -19,5 +18,3 @@ __pycache__/
*.pyc
data/
*.sqlite
-=======
->>>>>>> origin/main
diff --git a/PR_DESCRIPTION.md b/PR_DESCRIPTION.md
index ed054aa..ea491d3 100644
--- a/PR_DESCRIPTION.md
+++ b/PR_DESCRIPTION.md
@@ -1,4 +1,3 @@
-<<<<<<< HEAD
# 🛡️ Final One-Click Installer for Complete Pi-hole Stack
## 🎯 Objectives
@@ -157,7 +156,6 @@ tests/test_dns_monitor.py::test_parse_pihole_line_valid PASSED [ 83%]
tests/test_dns_monitor.py::test_parse_pihole_line_empty PASSED [ 91%]
tests/test_dns_monitor.py::test_parse_pihole_line_invalid PASSED [100%]
-============== 12 passed in 0.34s ==============
```
## 🚀 Quickstart Testing
@@ -231,7 +229,6 @@ journalctl -u pihole-suite -f # View logs
---
**This PR delivers a complete, production-ready one-click installer that transforms any Debian/Ubuntu system into a comprehensive DNS security and monitoring platform with zero manual configuration required.**
-=======
## 🎯 Overview
This comprehensive PR implements critical security fixes, repository hygiene improvements, and modern development standards for the Pi-hole Suite project.
@@ -296,4 +293,3 @@ This comprehensive PR implements critical security fixes, repository hygiene imp
- `.gitignore`: Extended Python artifacts coverage
This PR transforms the repository into a production-ready, enterprise-grade codebase with comprehensive security, testing, and quality standards.
->>>>>>> origin/main
diff --git a/README.de.md b/README.de.md
index ade447e..02f7c16 100644
--- a/README.de.md
+++ b/README.de.md
@@ -1,4 +1,3 @@
-<<<<<<< HEAD
# 🛡️ Pi-hole + Unbound + NetAlertX
@@ -251,7 +250,6 @@ Siehe [CHANGELOG.md](CHANGELOG.md) für Versionshistorie und Updates.
[💬 Diskussionen](https://github.com/TimInTech/Pi-hole-Unbound-PiAlert-Setup/discussions)
-=======
# Pi-hole + Unbound + NetAlertX — Ein-Klick-Setup
@@ -259,4 +257,3 @@ Siehe [CHANGELOG.md](CHANGELOG.md) für Versionshistorie und Updates.
- **Issues**: [GitHub Issues](https://github.com/TimInTech/Pi-hole-Unbound-PiAlert-Setup/issues)
- **Diskussionen**: [GitHub Discussions](https://github.com/TimInTech/Pi-hole-Unbound-PiAlert-Setup/discussions)
- **Dokumentation**: Diese README und Inline-Code-Kommentare
->>>>>>> origin/main
diff --git a/api/__init__.py b/api/__init__.py
index 1a01c31..57a924d 100644
--- a/api/__init__.py
+++ b/api/__init__.py
@@ -1,5 +1,2 @@
-<<<<<<< HEAD
"""FastAPI API module."""
-=======
"""API package for the Pi-hole suite."""
->>>>>>> origin/main
diff --git a/api/main.py b/api/main.py
index c40418c..675f74d 100644
--- a/api/main.py
+++ b/api/main.py
@@ -1,4 +1,3 @@
-<<<<<<< HEAD
"""FastAPI app for Pi-hole suite monitoring and management."""
import os
@@ -49,7 +48,6 @@ async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
def get_api_key() -> str:
"""Get configured API key."""
-=======
"""FastAPI app exposing Pi-hole suite data."""
import os
import sqlite3
@@ -57,15 +55,11 @@ def get_api_key() -> str:
def _get_api_key() -> str:
->>>>>>> origin/main
return os.getenv("SUITE_API_KEY", "")
def get_db() -> Generator[sqlite3.Connection, None, None]:
-<<<<<<< HEAD
"""Database dependency."""
-=======
->>>>>>> origin/main
conn = sqlite3.connect(config.DB_PATH)
conn.row_factory = sqlite3.Row
try:
@@ -74,7 +68,6 @@ def get_db() -> Generator[sqlite3.Connection, None, None]:
conn.close()
-<<<<<<< HEAD
def require_api_key(x_api_key: Optional[str] = Header(None)) -> None:
"""Require valid API key for authentication."""
api_key = get_api_key()
@@ -194,7 +187,6 @@ def get_stats(db: sqlite3.Connection = Depends(get_db)) -> StatsResponse:
total_devices=device_count,
recent_queries=recent_queries
)
-=======
cur = db.execute(
@@ -211,4 +203,3 @@ def get_ip_leases(db=Depends(get_db)):
->>>>>>> origin/main
diff --git a/api/schemas.py b/api/schemas.py
index 90a458e..08e10f7 100644
--- a/api/schemas.py
+++ b/api/schemas.py
@@ -1,4 +1,3 @@
-<<<<<<< HEAD
"""Pydantic schemas for API responses."""
from typing import Optional
@@ -44,7 +43,6 @@ class StatsResponse(BaseModel):
total_dns_logs: int = Field(..., description="Total DNS log entries")
total_devices: int = Field(..., description="Total known devices")
recent_queries: int = Field(..., description="Queries in the last hour")
-=======
"""Pydantic models for API request/response validation."""
from typing import Optional
from pydantic import BaseModel, Field, field_validator
@@ -100,4 +98,3 @@ class DNSLogResponse(BaseModel):
class HealthResponse(BaseModel):
"""Response model for health check."""
ok: bool
->>>>>>> origin/main
diff --git a/install.sh b/install.sh
index f82ab27..420ea24 100755
--- a/install.sh
+++ b/install.sh
@@ -2,8 +2,6 @@
set -euo pipefail
# Pi-hole + Unbound + NetAlertX + Python Suite One-Click Installer
-# ================================================================
-<<<<<<< HEAD
# Modern, idempotent installer for complete DNS security stack
#
# Author: TimInTech
@@ -111,7 +109,6 @@ install_packages() {
apt-get install -y \
unbound \
unbound-anchor \
-=======
# This script installs and configures:
# - Unbound DNS resolver on 127.0.0.1:5335
# - Pi-hole using Unbound as upstream
@@ -216,7 +213,6 @@ install_packages() {
# Install required packages
apt-get install -y \
unbound \
->>>>>>> origin/main
ca-certificates \
curl \
dnsutils \
@@ -226,7 +222,6 @@ install_packages() {
git \
docker.io \
openssl \
-<<<<<<< HEAD
systemd \
sqlite3
@@ -246,7 +241,6 @@ configure_unbound() {
# Create Pi-hole configuration
log "Creating Unbound configuration..."
-=======
systemctl
log_success "System packages installed"
@@ -266,7 +260,6 @@ configure_unbound() {
fi
# Create Pi-hole specific Unbound configuration
->>>>>>> origin/main
cat > /etc/unbound/unbound.conf.d/pi-hole.conf << 'EOF'
server:
# Basic settings
@@ -278,16 +271,13 @@ server:
do-udp: yes
do-tcp: yes
-<<<<<<< HEAD
# Performance tuning
edns-buffer-size: 1232
prefetch: yes
prefetch-key: yes
-=======
# Performance settings
edns-buffer-size: 1232
prefetch: yes
->>>>>>> origin/main
num-threads: 2
so-rcvbuf: 1m
so-sndbuf: 1m
@@ -297,11 +287,8 @@ server:
hide-identity: yes
hide-version: yes
-<<<<<<< HEAD
# Security hardening
-=======
# Security settings
->>>>>>> origin/main
harden-glue: yes
harden-dnssec-stripped: yes
harden-below-nxdomain: yes
@@ -311,12 +298,9 @@ server:
# Cache settings
cache-min-ttl: 60
cache-max-ttl: 86400
-<<<<<<< HEAD
msg-cache-size: 50m
rrset-cache-size: 100m
-=======
prefetch-key: yes
->>>>>>> origin/main
# DNSSEC
trust-anchor-file: /var/lib/unbound/root.key
@@ -331,7 +315,6 @@ server:
forward-zone:
name: "."
-<<<<<<< HEAD
forward-tls-upstream: yes
forward-addr: 9.9.9.9@853#dns.quad9.net
forward-addr: 149.112.112.112@853#dns.quad9.net
@@ -343,7 +326,6 @@ EOF
# Start Unbound
systemctl enable --now unbound
-=======
# Use Quad9 as fallback
forward-addr: 9.9.9.9@853#dns.quad9.net
forward-addr: 149.112.112.112@853#dns.quad9.net
@@ -361,12 +343,10 @@ EOF
systemctl restart unbound
# Wait for Unbound to start
->>>>>>> origin/main
sleep 3
# Test Unbound
if dig +short @127.0.0.1 -p $UNBOUND_PORT example.com | grep -q "93.184."; then
-<<<<<<< HEAD
success "Unbound is responding correctly"
else
warn "Unbound test failed - continuing anyway"
@@ -407,7 +387,6 @@ EOF
# Ensure Pi-hole uses Unbound
log "Configuring Pi-hole upstream DNS..."
-=======
log_success "Unbound is working correctly"
else
log_warning "Unbound test failed, but continuing..."
@@ -431,13 +410,11 @@ install_pihole() {
log_info "Configuring Pi-hole to use Unbound..."
# Set upstream DNS to Unbound
->>>>>>> origin/main
if [[ -f /etc/pihole/setupVars.conf ]]; then
sed -i "s/^PIHOLE_DNS_1=.*/PIHOLE_DNS_1=127.0.0.1#$UNBOUND_PORT/" /etc/pihole/setupVars.conf
sed -i "s/^PIHOLE_DNS_2=.*/PIHOLE_DNS_2=/" /etc/pihole/setupVars.conf
fi
-<<<<<<< HEAD
# Restart Pi-hole
pihole restartdns
@@ -461,7 +438,6 @@ install_netalertx() {
# Run NetAlertX
log "Starting NetAlertX container..."
-=======
# Restart Pi-hole DNS
pihole restartdns
@@ -485,14 +461,12 @@ install_netalertx() {
docker rm netalertx 2>/dev/null || true
# Run NetAlertX container
->>>>>>> origin/main
docker run -d \
--name netalertx \
--restart unless-stopped \
-p $NETALERTX_PORT:20211 \
-v /opt/netalertx/config:/app/config \
-v /opt/netalertx/db:/app/db \
-<<<<<<< HEAD
-e TZ=$(timedatectl show --property=Timezone --value 2>/dev/null || echo "UTC") \
$NETALERTX_IMAGE
@@ -538,7 +512,6 @@ EOF
cat > /etc/systemd/system/pihole-suite.service << EOF
[Unit]
Description=Pi-hole Suite (API + monitoring)
-=======
-e TZ=$(timedatectl show --property=Timezone --value) \
jokobsk/netalertx:latest
@@ -568,13 +541,11 @@ setup_python_suite() {
cat > /etc/systemd/system/pihole-suite.service << EOF
[Unit]
Description=Pi-hole Suite (API + workers)
->>>>>>> origin/main
After=network.target pihole-FTL.service
Wants=pihole-FTL.service
[Service]
Type=simple
-<<<<<<< HEAD
User=$install_user
Group=$install_user
WorkingDirectory=$project_dir
@@ -588,7 +559,6 @@ NoNewPrivileges=yes
ProtectSystem=full
ProtectHome=read-only
ReadWritePaths=$project_dir/data
-=======
User=$INSTALL_USER
Group=$INSTALL_USER
WorkingDirectory=$PROJECT_DIR
@@ -604,7 +574,6 @@ NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=yes
ReadWritePaths=$PROJECT_DIR/data
->>>>>>> origin/main
PrivateTmp=yes
PrivateDevices=yes
ProtectHostname=yes
@@ -615,29 +584,22 @@ ProtectControlGroups=yes
RestrictRealtime=yes
RestrictSUIDSGID=yes
RemoveIPC=yes
-<<<<<<< HEAD
-=======
MemoryDenyWriteExecute=yes
->>>>>>> origin/main
[Install]
WantedBy=multi-user.target
EOF
-<<<<<<< HEAD
# Enable and start service
-=======
# Create data directory with proper ownership
mkdir -p "$PROJECT_DIR/data"
chown -R "$INSTALL_USER:$INSTALL_USER" "$PROJECT_DIR/data"
# Enable and start the service
->>>>>>> origin/main
systemctl daemon-reload
systemctl enable pihole-suite.service
systemctl start pihole-suite.service
-<<<<<<< HEAD
success "Python suite installed and started"
log "API Key: $api_key"
}
@@ -735,7 +697,6 @@ main() {
check_system
check_ports
-=======
log_success "Python suite installed and running"
}
@@ -789,9 +750,7 @@ run_health_checks() {
# Display summary
show_summary() {
echo
- echo "=============================================="
echo " Installation Complete!"
- echo "=============================================="
echo
echo "🔧 Components installed:"
echo " • Unbound DNS resolver: 127.0.0.1:$UNBOUND_PORT"
@@ -828,7 +787,6 @@ show_summary() {
# Main installation function
main() {
echo "Pi-hole + Unbound + NetAlertX + Python Suite Installer"
- echo "======================================================"
echo
check_privileges
@@ -837,13 +795,11 @@ main() {
log_info "Starting installation..."
->>>>>>> origin/main
install_packages
configure_unbound
install_pihole
install_netalertx
setup_python_suite
-<<<<<<< HEAD
run_health_checks
show_summary
@@ -851,7 +807,6 @@ main() {
}
# Execute main function
-=======
log_info "Running health checks..."
run_health_checks
@@ -863,5 +818,4 @@ main() {
}
# Run main function
->>>>>>> origin/main
main "$@"
\ No newline at end of file
diff --git a/pyalloc/README_DEMO.md b/pyalloc/README_DEMO.md
index 890275f..02e1d9e 100644
--- a/pyalloc/README_DEMO.md
+++ b/pyalloc/README_DEMO.md
@@ -1,6 +1,5 @@
# PyAlloc - Demo IP Allocator
-<<<<<<< HEAD
⚠️ **This is a DEMO component only** ⚠️
This directory contains a simple IP address allocator that serves as a proof-of-concept. It is **disabled by default** and not required for the main Pi-hole + Unbound + NetAlertX installation.
@@ -32,7 +31,6 @@ For production environments, consider using proper DHCP management tools:
- Router/firewall DHCP services
This demo allocator is **not suitable for production use**.
-=======
⚠️ **This is a demonstration component only** ⚠️
This directory contains a simple IP address allocator that was created as a proof-of-concept. It is **not required** for the main Pi-hole + Unbound + NetAlertX installation.
@@ -54,4 +52,3 @@ The pyalloc component is automatically disabled in the one-click installer. If y
## For production use
For production environments, consider using proper DHCP management tools or network management systems instead of this demo allocator.
->>>>>>> origin/main
diff --git a/pyalloc/__init__.py b/pyalloc/__init__.py
index 798d5c2..4cd615e 100644
--- a/pyalloc/__init__.py
+++ b/pyalloc/__init__.py
@@ -1,5 +1,2 @@
-<<<<<<< HEAD
"""Demo IP allocator - Not required for main functionality."""
-=======
"""IP allocation helpers."""
->>>>>>> origin/main
diff --git a/pyalloc/allocator.py b/pyalloc/allocator.py
index 812625f..02ce2ab 100644
--- a/pyalloc/allocator.py
+++ b/pyalloc/allocator.py
@@ -1,22 +1,18 @@
-<<<<<<< HEAD
"""Simple IP pool allocator - DEMO ONLY."""
import ipaddress
import logging
import threading
from typing import Set
-=======
"""Simple IP pool allocator."""
import ipaddress
import logging
import threading
->>>>>>> origin/main
logger = logging.getLogger(__name__)
class IPPool:
-<<<<<<< HEAD
"""Simple IP address pool manager (Demo implementation)."""
def __init__(self, network: str = "192.168.1.0/24"):
@@ -39,20 +35,17 @@ def allocate(self) -> str:
Raises:
RuntimeError: If no IPs available
"""
-=======
def __init__(self, network: str):
self.network = ipaddress.ip_network(network)
self.lock = threading.Lock()
self.allocated = set()
def allocate(self) -> str:
->>>>>>> origin/main
with self.lock:
for ip in self.network.hosts():
ip_str = str(ip)
if ip_str not in self.allocated:
self.allocated.add(ip_str)
-<<<<<<< HEAD
logger.info(f"Allocated IP: {ip_str}")
return ip_str
@@ -102,7 +95,6 @@ def get_stats(self) -> dict:
"available": total_hosts - allocated_count,
"utilization_percent": (allocated_count / total_hosts) * 100 if total_hosts > 0 else 0
}
-=======
logger.info("Allocated IP %s", ip_str)
return ip_str
raise RuntimeError("No free IP addresses")
@@ -112,4 +104,3 @@ def release(self, ip: str) -> None:
if ip in self.allocated:
self.allocated.remove(ip)
logger.info("Released IP %s", ip)
->>>>>>> origin/main
diff --git a/pyalloc/main.py b/pyalloc/main.py
index 87a7d72..7cf1aea 100644
--- a/pyalloc/main.py
+++ b/pyalloc/main.py
@@ -1,22 +1,18 @@
-<<<<<<< HEAD
"""Demo IP allocator worker - DEMO ONLY."""
import logging
import sqlite3
import threading
from typing import Optional
-=======
"""Placeholder IP allocator worker."""
import logging
import threading
import time
->>>>>>> origin/main
from .allocator import IPPool
logger = logging.getLogger(__name__)
-<<<<<<< HEAD
# Global state
_pool: Optional[IPPool] = None
_stop_event = threading.Event()
@@ -105,7 +101,6 @@ def stop() -> None:
def get_pool() -> Optional[IPPool]:
"""Get the current IP pool instance (for demo/testing)."""
return _pool
-=======
_pool = None
_stop_event = threading.Event()
@@ -128,4 +123,3 @@ def _run() -> None:
def stop() -> None:
_stop_event.set()
->>>>>>> origin/main
diff --git a/pyhole/__init__.py b/pyhole/__init__.py
index 9c162e2..ca7db5d 100644
--- a/pyhole/__init__.py
+++ b/pyhole/__init__.py
@@ -1,5 +1,2 @@
-<<<<<<< HEAD
"""Pi-hole monitoring components."""
-=======
"""Pi-hole related helpers."""
->>>>>>> origin/main
diff --git a/pyhole/dns_monitor.py b/pyhole/dns_monitor.py
index 5af3f98..a98c2c9 100644
--- a/pyhole/dns_monitor.py
+++ b/pyhole/dns_monitor.py
@@ -1,19 +1,15 @@
-<<<<<<< HEAD
"""Robust Pi-hole log monitor with log rotation support."""
import logging
import re
import sqlite3
-=======
"""Simple Pi-hole log tailer with log rotation support."""
import logging
->>>>>>> origin/main
import threading
import time
from pathlib import Path
from typing import Optional, Tuple
-<<<<<<< HEAD
from shared.shared_config import PIHOLE_LOG_PATH
logger = logging.getLogger(__name__)
@@ -220,7 +216,6 @@ def stop() -> None:
def is_running() -> bool:
"""Check if the monitor is currently running."""
return _monitor_thread is not None and _monitor_thread.is_alive()
-=======
logger = logging.getLogger(__name__)
PIHOLE_LOG = Path("/var/log/pihole.log")
@@ -306,4 +301,3 @@ def start(conn):
def stop() -> None:
"""Stop the DNS monitor."""
_stop_event.set()
->>>>>>> origin/main
diff --git a/scripts/bootstrap.py b/scripts/bootstrap.py
index b5b296d..ba885db 100755
--- a/scripts/bootstrap.py
+++ b/scripts/bootstrap.py
@@ -1,5 +1,4 @@
#!/usr/bin/env python3
-<<<<<<< HEAD
"""Bootstrap script to check dependencies and initialize the suite."""
import importlib.util
@@ -59,7 +58,6 @@ def main():
if __name__ == "__main__":
sys.exit(main())
-=======
"""Check suite dependencies."""
import importlib.util
import sys
@@ -84,4 +82,3 @@ def main() -> None:
if __name__ == "__main__":
main()
->>>>>>> origin/main
diff --git a/scripts/healthcheck.py b/scripts/healthcheck.py
index 3f0ca4d..2cd7cdd 100755
--- a/scripts/healthcheck.py
+++ b/scripts/healthcheck.py
@@ -1,5 +1,4 @@
#!/usr/bin/env python3
-<<<<<<< HEAD
"""Health check script for Pi-hole suite."""
import os
@@ -101,7 +100,6 @@ def main():
if __name__ == "__main__":
sys.exit(main())
-=======
"""Verify database connectivity."""
import sqlite3
import sys
@@ -121,4 +119,3 @@ def main() -> None:
if __name__ == "__main__":
main()
->>>>>>> origin/main
diff --git a/shared/__init__.py b/shared/__init__.py
index 1bd283c..a590b81 100644
--- a/shared/__init__.py
+++ b/shared/__init__.py
@@ -1,5 +1,2 @@
-<<<<<<< HEAD
"""Shared utilities and configuration."""
-=======
"""Shared utilities for the Pi-hole suite."""
->>>>>>> origin/main
diff --git a/shared/db.py b/shared/db.py
index c6ab975..3d48fdc 100644
--- a/shared/db.py
+++ b/shared/db.py
@@ -1,4 +1,3 @@
-<<<<<<< HEAD
"""SQLite database initialization and helpers."""
import logging
@@ -61,7 +60,6 @@
);
CREATE INDEX IF NOT EXISTS idx_stats_timestamp ON system_stats(timestamp);
CREATE INDEX IF NOT EXISTS idx_stats_metric ON system_stats(metric_name);
-=======
"""SQLite helpers for the Pi-hole suite."""
import sqlite3
from pathlib import Path
@@ -95,12 +93,10 @@
last_seen TEXT
);
CREATE INDEX IF NOT EXISTS idx_devices_ip ON devices(ip);
->>>>>>> origin/main
"""
def init_db() -> sqlite3.Connection:
-<<<<<<< HEAD
"""Initialize the SQLite database with schema."""
try:
# Ensure parent directory exists
@@ -124,10 +120,8 @@ def get_connection() -> sqlite3.Connection:
conn = sqlite3.connect(str(DB_PATH))
conn.row_factory = sqlite3.Row
return conn
-=======
Path(DB_PATH).parent.mkdir(parents=True, exist_ok=True)
conn = sqlite3.connect(DB_PATH, check_same_thread=False)
conn.executescript(SCHEMA)
conn.commit()
return conn
->>>>>>> origin/main
diff --git a/shared/shared_config.py b/shared/shared_config.py
index ad23ae5..d1dfc97 100644
--- a/shared/shared_config.py
+++ b/shared/shared_config.py
@@ -1,14 +1,10 @@
-<<<<<<< HEAD
"""Shared configuration for the Pi-hole suite."""
-=======
"""Shared configuration helpers for the suite."""
->>>>>>> origin/main
import logging
import os
from pathlib import Path
-<<<<<<< HEAD
# Configuration from environment
INTERFACE = os.getenv("SUITE_INTERFACE", "eth0")
API_HOST = os.getenv("SUITE_HOST", "127.0.0.1")
@@ -33,7 +29,6 @@
# Module logger
logger = logging.getLogger(__name__)
logger.info(f"Configuration loaded - Data directory: {DATA_DIR}, Log level: {LOG_LEVEL}")
-=======
INTERFACE = os.getenv("SUITE_INTERFACE", "eth0")
DNS_PORT = int(os.getenv("SUITE_DNS_PORT", "5335"))
LOG_LEVEL = os.getenv("SUITE_LOG_LEVEL", "INFO")
@@ -44,4 +39,3 @@
DB_PATH = DATA_DIR / "shared.sqlite"
logging.basicConfig(level=LOG_LEVEL, format="%(asctime)s [%(levelname)s] %(message)s")
->>>>>>> origin/main
diff --git a/start_suite.py b/start_suite.py
index ff05276..65e5135 100644
--- a/start_suite.py
+++ b/start_suite.py
@@ -1,6 +1,5 @@
#!/usr/bin/env python3
"""Entry point for the Pi-hole monitoring suite."""
-<<<<<<< HEAD
import asyncio
import logging
@@ -51,7 +50,6 @@ async def run_api() -> None:
log_level="info",
access_log=True
)
-=======
import asyncio
import os
import threading
@@ -71,13 +69,11 @@ async def run_api() -> None:
async def run_api() -> None:
config = uvicorn.Config(api_app, host="127.0.0.1", port=8090, log_level="info")
->>>>>>> origin/main
server = uvicorn.Server(config)
await server.serve()
def main() -> None:
-<<<<<<< HEAD
"""Main application entry point."""
logger.info("Starting Pi-hole Suite...")
@@ -125,7 +121,6 @@ def main() -> None:
logger.info(f"API Key: {api_key[:8]}...")
logger.info("Starting API server...")
-=======
conn = init_db()
@@ -138,12 +133,10 @@ def main() -> None:
print("✓ Started with pyalloc demo component")
else:
print("✓ Started in production mode (pyalloc demo disabled)")
->>>>>>> origin/main
try:
asyncio.run(run_api())
except KeyboardInterrupt:
-<<<<<<< HEAD
logger.info("Shutting down...")
except Exception as e:
logger.error(f"Application error: {e}")
@@ -152,10 +145,8 @@ def main() -> None:
if __name__ == "__main__":
main()
-=======
print("Shutting down…")
if __name__ == "__main__":
main()
->>>>>>> origin/main
diff --git a/tests/test_api.py b/tests/test_api.py
index b42e475..d210021 100644
--- a/tests/test_api.py
+++ b/tests/test_api.py
@@ -1,5 +1,4 @@
"""Test suite for Pi-hole Suite API."""
-<<<<<<< HEAD
import os
import tempfile
@@ -42,7 +41,6 @@ def test_health_endpoint(client, api_headers):
data = response.json()
assert data["ok"] is True
assert "message" in data
-=======
import pytest
from fastapi.testclient import TestClient
import os
@@ -109,13 +107,11 @@ def test_health_endpoint(client):
response = client.get("/health", headers={"X-API-Key": "test-api-key"})
assert response.status_code == 200
assert response.json() == {"ok": True}
->>>>>>> origin/main
def test_health_endpoint_no_auth(client):
"""Test health endpoint without authentication."""
response = client.get("/health")
-<<<<<<< HEAD
assert response.status_code == 401 # API key validation returns 401
@@ -128,19 +124,16 @@ def test_health_endpoint_bad_auth(client):
def test_dns_logs_endpoint(client, api_headers):
"""Test DNS logs endpoint."""
response = client.get("/dns", headers=api_headers)
-=======
assert response.status_code == 422 # Unprocessable Entity due to missing required header
def test_dns_logs_endpoint(client):
"""Test DNS logs endpoint."""
response = client.get("/dns", headers={"X-API-Key": "test-api-key"})
->>>>>>> origin/main
assert response.status_code == 200
assert isinstance(response.json(), list)
-<<<<<<< HEAD
def test_dns_logs_with_limit(client, api_headers):
"""Test DNS logs endpoint with limit parameter."""
response = client.get("/dns?limit=10", headers=api_headers)
@@ -153,29 +146,23 @@ def test_dns_logs_with_limit(client, api_headers):
def test_devices_endpoint(client, api_headers):
"""Test devices endpoint."""
response = client.get("/devices", headers=api_headers)
-=======
def test_devices_endpoint(client):
"""Test devices endpoint."""
response = client.get("/devices", headers={"X-API-Key": "test-api-key"})
->>>>>>> origin/main
assert response.status_code == 200
assert isinstance(response.json(), list)
-<<<<<<< HEAD
def test_leases_endpoint(client, api_headers):
"""Test IP leases endpoint."""
response = client.get("/leases", headers=api_headers)
-=======
def test_leases_endpoint(client):
"""Test IP leases endpoint."""
response = client.get("/leases", headers={"X-API-Key": "test-api-key"})
->>>>>>> origin/main
assert response.status_code == 200
assert isinstance(response.json(), list)
-<<<<<<< HEAD
def test_stats_endpoint(client, api_headers):
"""Test statistics endpoint."""
response = client.get("/stats", headers=api_headers)
@@ -195,7 +182,6 @@ def test_root_endpoint(client):
assert response.status_code == 200
data = response.json()
assert "message" in data
-=======
def test_root_status():
"""Test basic application status."""
# This is a placeholder test for basic functionality
@@ -225,4 +211,3 @@ def test_device_request_validation():
# Invalid MAC address
with pytest.raises(ValueError):
DeviceRequest(ip_address="192.168.1.100", status=True, mac_address="invalid-mac")
->>>>>>> origin/main