mirror of
https://github.com/pacnpal/simpleguardhome.git
synced 2025-12-19 20:11:14 -05:00
Add initial implementation of SimpleGuardHome web app with configuration and dependencies
This commit is contained in:
5
.env.example
Normal file
5
.env.example
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# AdGuard Home Configuration
|
||||||
|
ADGUARD_HOST=http://localhost
|
||||||
|
ADGUARD_PORT=3000
|
||||||
|
ADGUARD_USERNAME=admin
|
||||||
|
ADGUARD_PASSWORD=password
|
||||||
62
README.md
62
README.md
@@ -1,13 +1,65 @@
|
|||||||
# SimpleGuardHome
|
# SimpleGuardHome
|
||||||
**WARNING** Very early work in progress. Use at your own risk.
|
|
||||||
|
|
||||||
SimpleGuardHome is a Python project designed to provide a simple method to check if a domain is blocked by AdGuard Home and then add a custom rule for it.
|
A simple web application for checking and managing domain filtering in AdGuard Home.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- Check if a domain is blocked by AdGuard Home.
|
- Check if domains are blocked by your AdGuard Home instance
|
||||||
- Add a custom blocking rule for a domain in AdGuard Home.
|
- One-click domain unblocking
|
||||||
|
- Modern, responsive web interface
|
||||||
|
- Secure integration with AdGuard Home API
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
1. Clone this repository:
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/yourusername/simpleguardhome.git
|
||||||
|
cd simpleguardhome
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Create a virtual environment and install dependencies:
|
||||||
|
```bash
|
||||||
|
python -m venv venv
|
||||||
|
source venv/bin/activate # On Windows use: venv\Scripts\activate
|
||||||
|
pip install -r requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Configure your environment:
|
||||||
|
```bash
|
||||||
|
cp .env.example .env
|
||||||
|
```
|
||||||
|
|
||||||
|
Edit `.env` with your AdGuard Home instance details:
|
||||||
|
```
|
||||||
|
ADGUARD_HOST=http://localhost
|
||||||
|
ADGUARD_PORT=3000
|
||||||
|
ADGUARD_USERNAME=your_username
|
||||||
|
ADGUARD_PASSWORD=your_password
|
||||||
|
```
|
||||||
|
|
||||||
|
## Running the Application
|
||||||
|
|
||||||
|
Start the application:
|
||||||
|
```bash
|
||||||
|
python -m src.simpleguardhome.main
|
||||||
|
```
|
||||||
|
|
||||||
|
Visit `http://localhost:8000` in your web browser.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
1. Enter a domain in the input field
|
||||||
|
2. Click "Check Domain" or press Enter
|
||||||
|
3. View the domain's blocking status
|
||||||
|
4. If blocked, use the "Unblock Domain" button to whitelist it
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
The application is built with:
|
||||||
|
- FastAPI for the backend
|
||||||
|
- Tailwind CSS for styling
|
||||||
|
- Modern JavaScript for frontend interactivity
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.
|
MIT License - See LICENSE file for details
|
||||||
|
|||||||
10
requirements.txt
Normal file
10
requirements.txt
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
fastapi==0.109.0
|
||||||
|
uvicorn==0.27.0
|
||||||
|
python-dotenv==1.0.0
|
||||||
|
httpx==0.26.0
|
||||||
|
pydantic==2.5.3
|
||||||
|
pydantic-settings==2.1.0
|
||||||
|
pytest==7.4.4
|
||||||
|
pytest-asyncio==0.23.3
|
||||||
|
python-multipart==0.0.6
|
||||||
|
jinja2==3.1.3
|
||||||
19
setup.py
Normal file
19
setup.py
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
from setuptools import setup, find_packages
|
||||||
|
|
||||||
|
setup(
|
||||||
|
name="simpleguardhome",
|
||||||
|
version="0.1.0",
|
||||||
|
packages=find_packages(),
|
||||||
|
include_package_data=True,
|
||||||
|
package_data={
|
||||||
|
"simpleguardhome": ["templates/*"]
|
||||||
|
},
|
||||||
|
install_requires=[
|
||||||
|
"fastapi",
|
||||||
|
"uvicorn",
|
||||||
|
"python-dotenv",
|
||||||
|
"httpx",
|
||||||
|
"pydantic",
|
||||||
|
"jinja2",
|
||||||
|
],
|
||||||
|
)
|
||||||
6
src/simpleguardhome/__init__.py
Normal file
6
src/simpleguardhome/__init__.py
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
"""SimpleGuardHome - Web app for managing AdGuard Home domain filtering."""
|
||||||
|
|
||||||
|
from .main import app, start
|
||||||
|
|
||||||
|
__version__ = "0.1.0"
|
||||||
|
__all__ = ["app", "start"]
|
||||||
140
src/simpleguardhome/adguard.py
Normal file
140
src/simpleguardhome/adguard.py
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
from typing import Dict, List, Optional
|
||||||
|
import httpx
|
||||||
|
import logging
|
||||||
|
from .config import settings
|
||||||
|
|
||||||
|
# Configure logging
|
||||||
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class AdGuardError(Exception):
|
||||||
|
"""Base exception for AdGuard Home API errors."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
class AdGuardConnectionError(AdGuardError):
|
||||||
|
"""Raised when connection to AdGuard Home fails."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
class AdGuardAPIError(AdGuardError):
|
||||||
|
"""Raised when AdGuard Home API returns an error."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
class AdGuardClient:
|
||||||
|
"""Client for interacting with AdGuard Home API."""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""Initialize the AdGuard Home API client."""
|
||||||
|
self.base_url = settings.adguard_base_url
|
||||||
|
self.client = httpx.AsyncClient(timeout=10.0) # 10 second timeout
|
||||||
|
self._auth = None
|
||||||
|
if settings.ADGUARD_USERNAME and settings.ADGUARD_PASSWORD:
|
||||||
|
self._auth = (settings.ADGUARD_USERNAME, settings.ADGUARD_PASSWORD)
|
||||||
|
logger.info(f"Initialized AdGuard Home client with base URL: {self.base_url}")
|
||||||
|
|
||||||
|
async def check_domain(self, domain: str) -> Dict:
|
||||||
|
"""Check if a domain is blocked by AdGuard Home.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
domain: The domain to check
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dict containing the filtering status
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
AdGuardConnectionError: If connection to AdGuard Home fails
|
||||||
|
AdGuardAPIError: If AdGuard Home API returns an error
|
||||||
|
"""
|
||||||
|
url = f"{self.base_url}/filtering/check_host"
|
||||||
|
params = {"name": domain}
|
||||||
|
|
||||||
|
try:
|
||||||
|
logger.info(f"Checking domain: {domain}")
|
||||||
|
response = await self.client.get(url, params=params, auth=self._auth)
|
||||||
|
response.raise_for_status()
|
||||||
|
result = response.json()
|
||||||
|
logger.info(f"Domain check result for {domain}: {result}")
|
||||||
|
return result
|
||||||
|
|
||||||
|
except httpx.ConnectError as e:
|
||||||
|
logger.error(f"Connection error while checking domain {domain}: {str(e)}")
|
||||||
|
raise AdGuardConnectionError(f"Failed to connect to AdGuard Home: {str(e)}")
|
||||||
|
except httpx.HTTPError as e:
|
||||||
|
logger.error(f"HTTP error while checking domain {domain}: {str(e)}")
|
||||||
|
raise AdGuardAPIError(f"AdGuard Home API error: {str(e)}")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Unexpected error while checking domain {domain}: {str(e)}")
|
||||||
|
raise AdGuardError(f"Unexpected error: {str(e)}")
|
||||||
|
|
||||||
|
async def get_filter_status(self) -> Dict:
|
||||||
|
"""Get the current filtering status.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dict containing the filtering status
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
AdGuardConnectionError: If connection to AdGuard Home fails
|
||||||
|
AdGuardAPIError: If AdGuard Home API returns an error
|
||||||
|
"""
|
||||||
|
url = f"{self.base_url}/filtering/status"
|
||||||
|
|
||||||
|
try:
|
||||||
|
logger.info("Getting filter status")
|
||||||
|
response = await self.client.get(url, auth=self._auth)
|
||||||
|
response.raise_for_status()
|
||||||
|
result = response.json()
|
||||||
|
logger.info("Successfully retrieved filter status")
|
||||||
|
return result
|
||||||
|
|
||||||
|
except httpx.ConnectError as e:
|
||||||
|
logger.error(f"Connection error while getting filter status: {str(e)}")
|
||||||
|
raise AdGuardConnectionError(f"Failed to connect to AdGuard Home: {str(e)}")
|
||||||
|
except httpx.HTTPError as e:
|
||||||
|
logger.error(f"HTTP error while getting filter status: {str(e)}")
|
||||||
|
raise AdGuardAPIError(f"AdGuard Home API error: {str(e)}")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Unexpected error while getting filter status: {str(e)}")
|
||||||
|
raise AdGuardError(f"Unexpected error: {str(e)}")
|
||||||
|
|
||||||
|
async def add_allowed_domain(self, domain: str) -> bool:
|
||||||
|
"""Add a domain to the allowed list.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
domain: The domain to allow
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: True if successful
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
AdGuardConnectionError: If connection to AdGuard Home fails
|
||||||
|
AdGuardAPIError: If AdGuard Home API returns an error
|
||||||
|
"""
|
||||||
|
url = f"{self.base_url}/filtering/whitelist/add"
|
||||||
|
data = {"name": domain}
|
||||||
|
|
||||||
|
try:
|
||||||
|
logger.info(f"Adding domain to whitelist: {domain}")
|
||||||
|
response = await self.client.post(url, json=data, auth=self._auth)
|
||||||
|
response.raise_for_status()
|
||||||
|
logger.info(f"Successfully added {domain} to whitelist")
|
||||||
|
return True
|
||||||
|
|
||||||
|
except httpx.ConnectError as e:
|
||||||
|
logger.error(f"Connection error while whitelisting domain {domain}: {str(e)}")
|
||||||
|
raise AdGuardConnectionError(f"Failed to connect to AdGuard Home: {str(e)}")
|
||||||
|
except httpx.HTTPError as e:
|
||||||
|
logger.error(f"HTTP error while whitelisting domain {domain}: {str(e)}")
|
||||||
|
raise AdGuardAPIError(f"AdGuard Home API error: {str(e)}")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Unexpected error while whitelisting domain {domain}: {str(e)}")
|
||||||
|
raise AdGuardError(f"Unexpected error: {str(e)}")
|
||||||
|
|
||||||
|
async def close(self):
|
||||||
|
"""Close the HTTP client."""
|
||||||
|
await self.client.aclose()
|
||||||
|
logger.info("Closed AdGuard Home client")
|
||||||
|
|
||||||
|
async def __aenter__(self):
|
||||||
|
return self
|
||||||
|
|
||||||
|
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
||||||
|
await self.close()
|
||||||
22
src/simpleguardhome/config.py
Normal file
22
src/simpleguardhome/config.py
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
from pydantic_settings import BaseSettings
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
|
||||||
|
class Settings(BaseSettings):
|
||||||
|
"""Application settings using environment variables."""
|
||||||
|
|
||||||
|
ADGUARD_HOST: str = "http://localhost"
|
||||||
|
ADGUARD_PORT: int = 3000
|
||||||
|
ADGUARD_USERNAME: Optional[str] = None
|
||||||
|
ADGUARD_PASSWORD: Optional[str] = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def adguard_base_url(self) -> str:
|
||||||
|
"""Get the base URL for AdGuard Home API."""
|
||||||
|
return f"{self.ADGUARD_HOST}:{self.ADGUARD_PORT}"
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
env_file = ".env"
|
||||||
|
|
||||||
|
|
||||||
|
settings = Settings()
|
||||||
129
src/simpleguardhome/main.py
Normal file
129
src/simpleguardhome/main.py
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
from fastapi import FastAPI, Request, Form, HTTPException
|
||||||
|
from fastapi.templating import Jinja2Templates
|
||||||
|
from fastapi.staticfiles import StaticFiles
|
||||||
|
from fastapi.responses import HTMLResponse, JSONResponse
|
||||||
|
from pathlib import Path
|
||||||
|
import httpx
|
||||||
|
import logging
|
||||||
|
from typing import Dict
|
||||||
|
from . import adguard
|
||||||
|
from .config import settings
|
||||||
|
from .adguard import AdGuardError, AdGuardConnectionError, AdGuardAPIError
|
||||||
|
|
||||||
|
# Configure logging
|
||||||
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
app = FastAPI(title="SimpleGuardHome")
|
||||||
|
|
||||||
|
# Setup templates directory
|
||||||
|
templates_path = Path(__file__).parent / "templates"
|
||||||
|
templates = Jinja2Templates(directory=str(templates_path))
|
||||||
|
|
||||||
|
@app.get("/", response_class=HTMLResponse)
|
||||||
|
async def home(request: Request):
|
||||||
|
"""Render the home page."""
|
||||||
|
return templates.TemplateResponse(
|
||||||
|
"index.html",
|
||||||
|
{"request": request}
|
||||||
|
)
|
||||||
|
|
||||||
|
@app.get("/health")
|
||||||
|
async def health_check() -> Dict:
|
||||||
|
"""Check the health of the application and AdGuard Home connection."""
|
||||||
|
try:
|
||||||
|
async with adguard.AdGuardClient() as client:
|
||||||
|
status = await client.get_filter_status()
|
||||||
|
return {
|
||||||
|
"status": "healthy",
|
||||||
|
"adguard_connection": "connected",
|
||||||
|
"filtering_enabled": status.get("enabled", False)
|
||||||
|
}
|
||||||
|
except AdGuardConnectionError:
|
||||||
|
return {
|
||||||
|
"status": "degraded",
|
||||||
|
"adguard_connection": "failed",
|
||||||
|
"error": "Could not connect to AdGuard Home"
|
||||||
|
}
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Health check failed: {str(e)}")
|
||||||
|
return {
|
||||||
|
"status": "error",
|
||||||
|
"error": str(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
@app.exception_handler(AdGuardError)
|
||||||
|
async def adguard_exception_handler(request: Request, exc: AdGuardError) -> JSONResponse:
|
||||||
|
"""Handle AdGuard-related exceptions."""
|
||||||
|
if isinstance(exc, AdGuardConnectionError):
|
||||||
|
status_code = 503 # Service Unavailable
|
||||||
|
elif isinstance(exc, AdGuardAPIError):
|
||||||
|
status_code = 502 # Bad Gateway
|
||||||
|
else:
|
||||||
|
status_code = 500 # Internal Server Error
|
||||||
|
|
||||||
|
return JSONResponse(
|
||||||
|
status_code=status_code,
|
||||||
|
content={
|
||||||
|
"success": False,
|
||||||
|
"error": exc.__class__.__name__,
|
||||||
|
"detail": str(exc)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
@app.post("/check-domain")
|
||||||
|
async def check_domain(domain: str = Form(...)) -> Dict:
|
||||||
|
"""Check if a domain is blocked by AdGuard Home."""
|
||||||
|
if not domain:
|
||||||
|
raise HTTPException(status_code=400, detail="Domain is required")
|
||||||
|
|
||||||
|
logger.info(f"Checking domain: {domain}")
|
||||||
|
try:
|
||||||
|
async with adguard.AdGuardClient() as client:
|
||||||
|
result = await client.check_domain(domain)
|
||||||
|
response = {
|
||||||
|
"success": True,
|
||||||
|
"domain": domain,
|
||||||
|
"blocked": result.get("filtered", False),
|
||||||
|
"rule": result.get("rule", ""),
|
||||||
|
"filter_list": result.get("filter_list", "")
|
||||||
|
}
|
||||||
|
logger.info(f"Domain check result: {response}")
|
||||||
|
return response
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error checking domain {domain}: {str(e)}")
|
||||||
|
raise
|
||||||
|
|
||||||
|
@app.post("/unblock-domain")
|
||||||
|
async def unblock_domain(domain: str = Form(...)) -> Dict:
|
||||||
|
"""Add a domain to the allowed list."""
|
||||||
|
if not domain:
|
||||||
|
raise HTTPException(status_code=400, detail="Domain is required")
|
||||||
|
|
||||||
|
logger.info(f"Unblocking domain: {domain}")
|
||||||
|
try:
|
||||||
|
async with adguard.AdGuardClient() as client:
|
||||||
|
await client.add_allowed_domain(domain)
|
||||||
|
response = {
|
||||||
|
"success": True,
|
||||||
|
"domain": domain,
|
||||||
|
"message": f"Successfully unblocked {domain}"
|
||||||
|
}
|
||||||
|
logger.info(f"Domain unblock result: {response}")
|
||||||
|
return response
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error unblocking domain {domain}: {str(e)}")
|
||||||
|
raise
|
||||||
|
|
||||||
|
def start():
|
||||||
|
"""Start the application using uvicorn."""
|
||||||
|
import uvicorn
|
||||||
|
uvicorn.run(
|
||||||
|
"simpleguardhome.main:app",
|
||||||
|
host="0.0.0.0",
|
||||||
|
port=8000,
|
||||||
|
reload=True
|
||||||
|
)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
start()
|
||||||
147
src/simpleguardhome/templates/index.html
Normal file
147
src/simpleguardhome/templates/index.html
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>SimpleGuardHome</title>
|
||||||
|
<script src="https://cdn.tailwindcss.com"></script>
|
||||||
|
<script>
|
||||||
|
async function checkDomain(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
const domain = document.getElementById('domain').value;
|
||||||
|
const resultDiv = document.getElementById('result');
|
||||||
|
const unblockDiv = document.getElementById('unblock-action');
|
||||||
|
const submitBtn = document.getElementById('submit-btn');
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Show loading state
|
||||||
|
submitBtn.disabled = true;
|
||||||
|
submitBtn.innerHTML = '<span class="inline-flex items-center">Checking... <svg class="animate-spin ml-2 h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg></span>';
|
||||||
|
|
||||||
|
const response = await fetch('/check-domain', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded',
|
||||||
|
},
|
||||||
|
body: `domain=${encodeURIComponent(domain)}`
|
||||||
|
});
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
if (data.success) {
|
||||||
|
if (data.blocked) {
|
||||||
|
resultDiv.innerHTML = `
|
||||||
|
<div class="bg-red-100 border-l-4 border-red-500 text-red-700 p-4 mb-4">
|
||||||
|
<p class="font-bold">Domain is blocked</p>
|
||||||
|
<p class="text-sm"><strong>${domain}</strong> is blocked by rule:</p>
|
||||||
|
<p class="text-sm font-mono bg-red-50 p-2 mt-1 rounded">${data.rule || 'Unknown rule'}</p>
|
||||||
|
${data.filter_list ? `<p class="text-sm mt-2">Filter List: ${data.filter_list}</p>` : ''}
|
||||||
|
</div>`;
|
||||||
|
unblockDiv.innerHTML = `
|
||||||
|
<button onclick="unblockDomain('${domain}')"
|
||||||
|
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded transition-colors duration-200">
|
||||||
|
Unblock Domain
|
||||||
|
</button>`;
|
||||||
|
} else {
|
||||||
|
resultDiv.innerHTML = `
|
||||||
|
<div class="bg-green-100 border-l-4 border-green-500 text-green-700 p-4">
|
||||||
|
<p class="font-bold">Domain is not blocked</p>
|
||||||
|
<p class="text-sm"><strong>${domain}</strong> is allowed</p>
|
||||||
|
</div>`;
|
||||||
|
unblockDiv.innerHTML = '';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Show error message
|
||||||
|
resultDiv.innerHTML = `
|
||||||
|
<div class="bg-yellow-100 border-l-4 border-yellow-500 text-yellow-700 p-4">
|
||||||
|
<p class="font-bold">${data.error}</p>
|
||||||
|
<p class="text-sm">${data.detail}</p>
|
||||||
|
</div>`;
|
||||||
|
unblockDiv.innerHTML = '';
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
resultDiv.innerHTML = `
|
||||||
|
<div class="bg-red-100 border-l-4 border-red-500 text-red-700 p-4">
|
||||||
|
<p class="font-bold">Error checking domain</p>
|
||||||
|
<p class="text-sm">${error.message}</p>
|
||||||
|
</div>`;
|
||||||
|
unblockDiv.innerHTML = '';
|
||||||
|
} finally {
|
||||||
|
// Reset button state
|
||||||
|
submitBtn.disabled = false;
|
||||||
|
submitBtn.innerHTML = 'Check Domain';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function unblockDomain(domain) {
|
||||||
|
const resultDiv = document.getElementById('result');
|
||||||
|
const unblockDiv = document.getElementById('unblock-action');
|
||||||
|
const unblockBtn = unblockDiv.querySelector('button');
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Show loading state
|
||||||
|
unblockBtn.disabled = true;
|
||||||
|
unblockBtn.innerHTML = '<span class="inline-flex items-center">Unblocking... <svg class="animate-spin ml-2 h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg></span>';
|
||||||
|
|
||||||
|
const response = await fetch('/unblock-domain', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded',
|
||||||
|
},
|
||||||
|
body: `domain=${encodeURIComponent(domain)}`
|
||||||
|
});
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
if (data.success) {
|
||||||
|
resultDiv.innerHTML = `
|
||||||
|
<div class="bg-green-100 border-l-4 border-green-500 text-green-700 p-4">
|
||||||
|
<p class="font-bold">Success!</p>
|
||||||
|
<p class="text-sm">${data.message}</p>
|
||||||
|
</div>`;
|
||||||
|
unblockDiv.innerHTML = '';
|
||||||
|
} else {
|
||||||
|
resultDiv.innerHTML = `
|
||||||
|
<div class="bg-yellow-100 border-l-4 border-yellow-500 text-yellow-700 p-4">
|
||||||
|
<p class="font-bold">${data.error}</p>
|
||||||
|
<p class="text-sm">${data.detail}</p>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
resultDiv.innerHTML = `
|
||||||
|
<div class="bg-red-100 border-l-4 border-red-500 text-red-700 p-4">
|
||||||
|
<p class="font-bold">Error unblocking domain</p>
|
||||||
|
<p class="text-sm">${error.message}</p>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body class="bg-gray-100 min-h-screen">
|
||||||
|
<div class="container mx-auto px-4 py-8 max-w-2xl">
|
||||||
|
<h1 class="text-3xl font-bold text-center mb-8 text-gray-800">SimpleGuardHome</h1>
|
||||||
|
|
||||||
|
<div class="bg-white rounded-lg shadow-md p-6">
|
||||||
|
<form onsubmit="checkDomain(event)" class="mb-6">
|
||||||
|
<div class="mb-4">
|
||||||
|
<label for="domain" class="block text-gray-700 text-sm font-bold mb-2">
|
||||||
|
Enter Domain to Check
|
||||||
|
</label>
|
||||||
|
<input type="text" id="domain" name="domain" required
|
||||||
|
class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
|
||||||
|
placeholder="example.com">
|
||||||
|
</div>
|
||||||
|
<button id="submit-btn" type="submit"
|
||||||
|
class="bg-green-500 hover:bg-green-700 text-white font-bold py-2 px-4 rounded w-full transition-colors duration-200">
|
||||||
|
Check Domain
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div id="result"></div>
|
||||||
|
<div id="unblock-action" class="mt-4 text-center"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-4 text-center text-gray-600 text-sm">
|
||||||
|
Make sure your AdGuard Home instance is running and properly configured in the .env file.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Reference in New Issue
Block a user