Basic Rest API implementation

This commit is contained in:
2026-01-23 04:32:48 +05:00
parent faf6cdf091
commit 3812e6c3ad
7 changed files with 1683 additions and 351 deletions

38
.dockerignore Normal file
View File

@ -0,0 +1,38 @@
# Git
.git
.gitignore
.gitattributes
# Docker
Dockerfile
docker-compose.yml
.dockerignore
# Python
__pycache__
*.py[cod]
*$py.class
*.so
.Python
env/
venv/
ENV/
*.egg-info/
# IDE
.vscode/
.idea/
*.swp
*.swo
*~
# OS
.DS_Store
Thumbs.db
# Logs
*.log
# Temporary files
*.tmp
*.temp

55
Dockerfile Normal file
View File

@ -0,0 +1,55 @@
FROM ubuntu:22.04
# Prevent interactive prompts
ENV DEBIAN_FRONTEND=noninteractive
ENV PYTHONUNBUFFERED=1
# Install system dependencies
RUN apt-get update && apt-get install -y \
python3 \
python3-pip \
python3-venv \
curl \
wget \
jq \
pwgen \
bc \
zip \
unzip \
lsof \
gnupg \
lsb-release \
systemctl \
iptables \
iproute2 \
&& rm -rf /var/lib/apt/lists/*
# Create working directory
WORKDIR /etc/hysteria
# Copy requirements first (for better caching)
COPY requirements.txt .
# Create and activate virtual environment, install dependencies
RUN python3 -m venv /etc/hysteria/hysteria2_venv && \
/etc/hysteria/hysteria2_venv/bin/pip install --no-cache-dir --upgrade pip && \
/etc/hysteria/hysteria2_venv/bin/pip install --no-cache-dir -r requirements.txt && \
/etc/hysteria/hysteria2_venv/bin/pip install --no-cache-dir fastapi uvicorn python-multipart
# Copy application files
COPY . /etc/hysteria/
# Make scripts executable
RUN find /etc/hysteria/core/scripts -type f -name "*.sh" -exec chmod +x {} \; && \
find /etc/hysteria/core/scripts -type f -name "*.py" -exec chmod +x {} \; && \
chmod +x /etc/hysteria/menu.sh
# Expose ports
EXPOSE 8000 443 80
# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
CMD curl -f http://localhost:8000/health || exit 1
# Start FastAPI server
CMD ["/etc/hysteria/hysteria2_venv/bin/uvicorn", "core.api:app", "--host", "0.0.0.0", "--port", "8000", "--reload"]

271
README-DOCKER.md Normal file
View File

@ -0,0 +1,271 @@
# Blitz Panel - Docker Setup
## 📋 Prerequisites
- Docker Engine 20.10+
- Docker Compose 2.0+
- At least 2GB RAM
- Ports available: 8000 (API), 443 (Hysteria2), 80 (HTTP)
## 🚀 Quick Start
### 1. Prepare Your Files
Make sure your directory structure looks like this:
```
Blitz/
├── core/
│ └── scripts/
│ ├── auth/
│ ├── db/
│ ├── hysteria2/
│ └── ... (other script folders)
├── api.py
├── cli_api.py
├── requirements.txt
├── Dockerfile
├── docker-compose.yml
└── .dockerignore
```
### 2. Build and Run
```bash
# Build and start containers
docker-compose up -d --build
# Check logs
docker-compose logs -f blitz-api
# Check if services are running
docker-compose ps
```
### 3. Test the API
```bash
# Health check
curl http://localhost:8000/health
# Get API docs (interactive)
open http://localhost:8000/docs
```
## 📚 API Endpoints
### User Management
```bash
# List all users
curl http://localhost:8000/api/v1/users
# Get specific user
curl http://localhost:8000/api/v1/users/john
# Create user
curl -X POST http://localhost:8000/api/v1/users \
-H "Content-Type: application/json" \
-d '{
"username": "john",
"traffic_limit": 100,
"expiration_days": 30,
"unlimited": false
}'
# Edit user
curl -X PUT http://localhost:8000/api/v1/users/john \
-H "Content-Type: application/json" \
-d '{
"new_traffic_limit": 200,
"renew_creation_date": true
}'
# Delete user
curl -X DELETE http://localhost:8000/api/v1/users/john
# Get user URI
curl http://localhost:8000/api/v1/users/john/uri?qrcode=false
```
### Hysteria2 Management
```bash
# Install Hysteria2
curl -X POST http://localhost:8000/api/v1/hysteria2/install \
-H "Content-Type: application/json" \
-d '{
"port": 443,
"sni": "example.com"
}'
# Restart Hysteria2
curl -X POST http://localhost:8000/api/v1/hysteria2/restart
# Get current port
curl http://localhost:8000/api/v1/hysteria2/config/port
# Change port
curl -X PUT http://localhost:8000/api/v1/hysteria2/config/port \
-H "Content-Type: application/json" \
-d '{"port": 8443}'
# Enable obfuscation
curl -X POST http://localhost:8000/api/v1/hysteria2/obfs/enable
```
### Traffic & Stats
```bash
# Get traffic status
curl http://localhost:8000/api/v1/traffic/status
# Get server info
curl http://localhost:8000/api/v1/info/server
# Get services status
curl http://localhost:8000/api/v1/info/services
```
## 🛠️ Development Mode
To run in development with auto-reload:
```bash
# Run API locally (not in Docker)
source /etc/hysteria/hysteria2_venv/bin/activate
cd /path/to/Blitz
uvicorn api:app --reload --host 0.0.0.0 --port 8000
```
## 🐛 Troubleshooting
### Container won't start
```bash
# Check logs
docker-compose logs blitz-api
# Check MongoDB
docker-compose logs mongodb
# Restart services
docker-compose restart
```
### Permission issues
```bash
# Fix permissions for scripts
docker-compose exec blitz-api bash
find /etc/hysteria/core/scripts -type f -name "*.sh" -exec chmod +x {} \;
find /etc/hysteria/core/scripts -type f -name "*.py" -exec chmod +x {} \;
```
### Network issues
```bash
# Check if ports are available
sudo netstat -tlnp | grep -E ':(8000|443|80)'
# If ports are in use, change them in docker-compose.yml
```
### Reset everything
```bash
# Stop and remove everything
docker-compose down -v
# Remove volumes (WARNING: deletes all data)
docker volume rm blitz_mongodb_data blitz_config blitz_certs
# Rebuild
docker-compose up -d --build
```
## 📊 Monitoring
```bash
# Watch logs in real-time
docker-compose logs -f
# Check resource usage
docker stats
# Execute commands in container
docker-compose exec blitz-api bash
```
## 🔒 Production Deployment
For production, modify `docker-compose.yml`:
1. **Add authentication** to the API (JWT, API keys)
2. **Use HTTPS** with proper SSL certificates
3. **Set up reverse proxy** (nginx/traefik)
4. **Configure firewall** rules
5. **Enable MongoDB authentication**
6. **Set resource limits**
Example production additions:
```yaml
# In docker-compose.yml
services:
blitz-api:
deploy:
resources:
limits:
cpus: '2'
memory: 2G
reservations:
cpus: '1'
memory: 1G
environment:
- API_KEY=your-secret-key
```
## 📝 Notes
- Default API runs on port 8000
- MongoDB stores data in named volume `mongodb_data`
- Configs persist in `blitz_config` volume
- All scripts from `core/` are copied into container
- Health check runs every 30 seconds
## 🤝 Common Tasks
### Backup
```bash
# Backup MongoDB data
docker-compose exec mongodb mongodump --out /data/backup
# Copy backup out
docker cp blitz-mongodb:/data/backup ./backup
```
### Update
```bash
# Pull latest code
git pull
# Rebuild
docker-compose up -d --build
```
### Scale
To run multiple instances (load balancing):
```bash
docker-compose up -d --scale blitz-api=3
```
## 🆘 Support
- Check logs: `docker-compose logs -f`
- Interactive docs: http://localhost:8000/docs
- ReDoc: http://localhost:8000/redoc

52
compose.yml Normal file
View File

@ -0,0 +1,52 @@
services:
mongodb:
image: mongo:8.0
container_name: blitz-mongodb
restart: unless-stopped
environment:
MONGO_INITDB_DATABASE: hysteria
volumes:
- mongodb_data:/data/db
networks:
- blitz-network
healthcheck:
test: echo 'db.runCommand("ping").ok' | mongosh localhost:27017/test --quiet
interval: 10s
timeout: 5s
retries: 5
blitz-api:
build:
context: .
dockerfile: Dockerfile
container_name: blitz-api
restart: unless-stopped
ports:
- "8000:8000"
- "443:443"
- "80:80"
environment:
- MONGODB_URI=mongodb://mongodb:27017/hysteria
- PYTHONUNBUFFERED=1
volumes:
- ./core:/etc/hysteria/core
- blitz_config:/etc/hysteria
- blitz_certs:/root/.acme.sh
networks:
- blitz-network
depends_on:
mongodb:
condition: service_healthy
privileged: true
cap_add:
- NET_ADMIN
- SYS_MODULE
networks:
blitz-network:
driver: bridge
volumes:
mongodb_data:
blitz_config:
blitz_certs:

686
core/api.py Normal file
View File

@ -0,0 +1,686 @@
import sys
sys.path.insert(0, "/etc/hysteria/core/scripts")
from fastapi import FastAPI, HTTPException, status, Query
from fastapi.responses import JSONResponse
from pydantic import BaseModel, Field
from typing import Optional, List, Dict, Any
from datetime import datetime
from . import cli_api
import asyncio
app = FastAPI(
title="Blitz Panel API",
description="REST API for Hysteria2 Panel Management",
version="1.0.0",
)
# ============================================================================
# Pydantic Models
# ============================================================================
class UserCreate(BaseModel):
username: str = Field(..., min_length=1, max_length=50)
traffic_limit: int = Field(..., ge=0, description="Traffic limit in GB")
expiration_days: int = Field(..., ge=0)
password: Optional[str] = None
creation_date: Optional[str] = None
unlimited: bool = False
note: Optional[str] = None
class BulkUserCreate(BaseModel):
traffic_gb: float = Field(..., ge=0)
expiration_days: int = Field(..., ge=0)
count: int = Field(..., ge=1, le=1000)
prefix: str = Field(..., min_length=1)
start_number: int = Field(1, ge=1)
unlimited: bool = False
class UserEdit(BaseModel):
new_username: Optional[str] = None
new_password: Optional[str] = None
new_traffic_limit: Optional[int] = Field(None, ge=0)
new_expiration_days: Optional[int] = Field(None, ge=0)
renew_password: bool = False
renew_creation_date: bool = False
blocked: Optional[bool] = None
unlimited_ip: Optional[bool] = None
note: Optional[str] = None
class PortChange(BaseModel):
port: int = Field(..., ge=1, le=65535)
class SNIChange(BaseModel):
sni: str = Field(..., min_length=1)
class IPConfig(BaseModel):
ipv4: Optional[str] = None
ipv6: Optional[str] = None
class Hysteria2Install(BaseModel):
port: int = Field(..., ge=1, le=65535)
sni: str = Field(..., min_length=1)
class NodeCreate(BaseModel):
name: str
ip: str
sni: Optional[str] = None
pinSHA256: Optional[str] = None
port: Optional[int] = Field(None, ge=1, le=65535)
obfs: Optional[str] = None
insecure: Optional[bool] = False
class TelegramBotConfig(BaseModel):
token: str
admin_ids: str
backup_interval: Optional[int] = Field(None, ge=1)
class WebPanelConfig(BaseModel):
domain: str
port: int = Field(..., ge=1, le=65535)
admin_username: str
admin_password: str
expiration_minutes: int = Field(30, ge=1)
debug: bool = False
decoy_path: str = ""
class NormalSubConfig(BaseModel):
domain: str
port: int = Field(..., ge=1, le=65535)
class WARPConfig(BaseModel):
all_state: Optional[str] = Field(None, pattern="^(on|off)$")
popular_sites_state: Optional[str] = Field(None, pattern="^(on|off)$")
domestic_sites_state: Optional[str] = Field(None, pattern="^(on|off)$")
block_adult_sites_state: Optional[str] = Field(None, pattern="^(on|off)$")
class IPLimiterConfig(BaseModel):
block_duration: Optional[int] = Field(None, ge=1)
max_ips: Optional[int] = Field(None, ge=1)
class ExtraProxyConfig(BaseModel):
name: str
uri: str
# ============================================================================
# Error Handler
# ============================================================================
def handle_api_error(func):
"""Decorator to handle CLI API errors"""
if asyncio.iscoroutinefunction(func):
async def async_wrapper(*args, **kwargs):
try:
return await func(*args, **kwargs)
except cli_api.InvalidInputError as e:
raise HTTPException(status_code=400, detail=str(e))
except cli_api.ScriptNotFoundError as e:
raise HTTPException(status_code=500, detail=f"Script error: {str(e)}")
except cli_api.CommandExecutionError as e:
raise HTTPException(
status_code=500, detail=f"Execution error: {str(e)}"
)
except cli_api.HysteriaError as e:
raise HTTPException(status_code=500, detail=str(e))
except Exception as e:
raise HTTPException(
status_code=500, detail=f"Unexpected error: {str(e)}"
)
return async_wrapper
else:
def sync_wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except cli_api.InvalidInputError as e:
raise HTTPException(status_code=400, detail=str(e))
except cli_api.ScriptNotFoundError as e:
raise HTTPException(status_code=500, detail=f"Script error: {str(e)}")
except cli_api.CommandExecutionError as e:
raise HTTPException(
status_code=500, detail=f"Execution error: {str(e)}"
)
except cli_api.HysteriaError as e:
raise HTTPException(status_code=500, detail=str(e))
except Exception as e:
raise HTTPException(
status_code=500, detail=f"Unexpected error: {str(e)}"
)
return sync_wrapper
# ============================================================================
# Health & Info
# ============================================================================
@app.get("/health")
async def health_check():
"""Health check endpoint"""
return {"status": "healthy", "timestamp": datetime.now().isoformat()}
@app.get("/api/v1/info/server")
async def get_server_info():
"""Get server information"""
info = cli_api.server_info()
return {"data": info}
@app.get("/api/v1/info/version")
async def get_version():
"""Get panel version"""
version = cli_api.show_version()
return {"version": version}
@app.get("/api/v1/info/services")
async def get_services_status():
"""Get status of all services"""
status_data = cli_api.get_services_status()
return {"services": status_data}
# ============================================================================
# User Management
# ============================================================================
@app.get("/api/v1/users")
async def list_users():
"""List all users"""
users = cli_api.list_users()
return {"users": users or {}}
@app.get("/api/v1/users/{username}")
async def get_user(username: str):
"""Get user details"""
user = cli_api.get_user(username)
if not user:
raise HTTPException(status_code=404, detail=f"User '{username}' not found")
return {"user": user}
@app.post("/api/v1/users", status_code=status.HTTP_201_CREATED)
async def create_user(user: UserCreate):
"""Create a new user"""
cli_api.add_user(
username=user.username,
traffic_limit=user.traffic_limit,
expiration_days=user.expiration_days,
password=user.password,
creation_date=user.creation_date,
unlimited=user.unlimited,
note=user.note,
)
return {"message": f"User '{user.username}' created successfully"}
@app.post("/api/v1/users/bulk", status_code=status.HTTP_201_CREATED)
async def create_bulk_users(bulk: BulkUserCreate):
"""Create multiple users at once"""
cli_api.bulk_user_add(
traffic_gb=bulk.traffic_gb,
expiration_days=bulk.expiration_days,
count=bulk.count,
prefix=bulk.prefix,
start_number=bulk.start_number,
unlimited=bulk.unlimited,
)
return {"message": f"Created {bulk.count} users with prefix '{bulk.prefix}'"}
@app.put("/api/v1/users/{username}")
async def edit_user(username: str, user: UserEdit):
"""Edit user details"""
cli_api.edit_user(
username=username,
new_username=user.new_username,
new_password=user.new_password,
new_traffic_limit=user.new_traffic_limit,
new_expiration_days=user.new_expiration_days,
renew_password=user.renew_password,
renew_creation_date=user.renew_creation_date,
blocked=user.blocked,
unlimited_ip=user.unlimited_ip,
note=user.note,
)
return {"message": f"User '{username}' updated successfully"}
@app.post("/api/v1/users/{username}/reset")
async def reset_user(username: str):
"""Reset user traffic and dates"""
cli_api.reset_user(username)
return {"message": f"User '{username}' reset successfully"}
@app.delete("/api/v1/users/{username}")
async def delete_user(username: str):
"""Delete a user"""
cli_api.remove_users([username])
return {"message": f"User '{username}' deleted successfully"}
@app.delete("/api/v1/users")
async def delete_multiple_users(usernames: List[str] = Query(...)):
"""Delete multiple users"""
cli_api.remove_users(usernames)
return {"message": f"Deleted {len(usernames)} users"}
@app.post("/api/v1/users/{username}/kick")
async def kick_user(username: str):
"""Kick user (disconnect)"""
cli_api.kick_users_by_name([username])
return {"message": f"User '{username}' kicked successfully"}
@app.get("/api/v1/users/{username}/uri")
async def get_user_uri(
username: str,
qrcode: bool = False,
ipv: int = 4,
all: bool = False,
singbox: bool = False,
normalsub: bool = False,
):
"""Get user connection URI"""
uri = cli_api.show_user_uri(username, qrcode, ipv, all, singbox, normalsub)
return {"uri": uri}
@app.get("/api/v1/users/uri/batch")
async def get_batch_user_uri(usernames: List[str] = Query(...)):
"""Get URIs for multiple users"""
uris = cli_api.show_user_uri_json(usernames)
return {"uris": uris}
# ============================================================================
# Hysteria2 Core Management
# ============================================================================
@app.post("/api/v1/hysteria2/install")
async def install_hysteria2(config: Hysteria2Install):
"""Install Hysteria2"""
cli_api.install_hysteria2(port=config.port, sni=config.sni)
return {"message": "Hysteria2 installed successfully"}
@app.post("/api/v1/hysteria2/uninstall")
async def uninstall_hysteria2():
"""Uninstall Hysteria2"""
cli_api.uninstall_hysteria2()
return {"message": "Hysteria2 uninstalled successfully"}
@app.post("/api/v1/hysteria2/update")
async def update_hysteria2():
"""Update Hysteria2 core"""
cli_api.update_hysteria2()
return {"message": "Hysteria2 updated successfully"}
@app.post("/api/v1/hysteria2/restart")
async def restart_hysteria2():
"""Restart Hysteria2 service"""
cli_api.restart_hysteria2()
return {"message": "Hysteria2 restarted successfully"}
@app.get("/api/v1/hysteria2/config/port")
async def get_port():
"""Get current Hysteria2 port"""
port = cli_api.get_hysteria2_port()
return {"port": port}
@app.put("/api/v1/hysteria2/config/port")
async def change_port(config: PortChange):
"""Change Hysteria2 port"""
cli_api.change_hysteria2_port(config.port)
return {"message": f"Port changed to {config.port}"}
@app.get("/api/v1/hysteria2/config/sni")
async def get_sni():
"""Get current SNI"""
sni = cli_api.get_hysteria2_sni()
return {"sni": sni}
@app.put("/api/v1/hysteria2/config/sni")
async def change_sni(config: SNIChange):
"""Change SNI"""
cli_api.change_hysteria2_sni(config.sni)
return {"message": f"SNI changed to {config.sni}"}
@app.post("/api/v1/hysteria2/obfs/enable")
async def enable_obfs():
"""Enable obfuscation"""
cli_api.enable_hysteria2_obfs()
return {"message": "Obfuscation enabled"}
@app.post("/api/v1/hysteria2/obfs/disable")
async def disable_obfs():
"""Disable obfuscation"""
cli_api.disable_hysteria2_obfs()
return {"message": "Obfuscation disabled"}
@app.get("/api/v1/hysteria2/obfs/status")
async def check_obfs():
"""Check obfuscation status"""
status_msg = cli_api.check_hysteria2_obfs()
return {"status": status_msg}
@app.post("/api/v1/hysteria2/masquerade/enable")
async def enable_masquerade():
"""Enable masquerade"""
result = cli_api.enable_hysteria2_masquerade()
return {"message": result}
@app.post("/api/v1/hysteria2/masquerade/disable")
async def disable_masquerade():
"""Disable masquerade"""
result = cli_api.disable_hysteria2_masquerade()
return {"message": result}
@app.get("/api/v1/hysteria2/masquerade/status")
async def get_masquerade_status():
"""Get masquerade status"""
status_msg = cli_api.get_hysteria2_masquerade_status()
return {"status": status_msg}
# ============================================================================
# Traffic & Statistics
# ============================================================================
@app.get("/api/v1/traffic/status")
async def get_traffic_status():
"""Get traffic status for all users"""
data = cli_api.traffic_status(no_gui=True, display_output=False)
return {"traffic": data}
# ============================================================================
# IP Configuration
# ============================================================================
@app.get("/api/v1/config/ip")
async def get_ip_addresses():
"""Get configured IP addresses"""
ipv4, ipv6 = cli_api.get_ip_address()
return {"ipv4": ipv4, "ipv6": ipv6}
@app.post("/api/v1/config/ip")
async def add_ip_addresses():
"""Auto-detect and add IP addresses"""
cli_api.add_ip_address()
return {"message": "IP addresses added"}
@app.put("/api/v1/config/ip")
async def edit_ip_addresses(config: IPConfig):
"""Edit IP addresses"""
cli_api.edit_ip_address(ipv4=config.ipv4, ipv6=config.ipv6)
return {"message": "IP addresses updated"}
# ============================================================================
# Advanced Features
# ============================================================================
@app.post("/api/v1/advanced/tcp-brutal/install")
async def install_tcp_brutal():
"""Install TCP Brutal"""
cli_api.install_tcp_brutal()
return {"message": "TCP Brutal installed"}
@app.post("/api/v1/advanced/warp/install")
async def install_warp():
"""Install WARP"""
cli_api.install_warp()
return {"message": "WARP installed"}
@app.post("/api/v1/advanced/warp/uninstall")
async def uninstall_warp():
"""Uninstall WARP"""
cli_api.uninstall_warp()
return {"message": "WARP uninstalled"}
@app.put("/api/v1/advanced/warp/configure")
async def configure_warp(config: WARPConfig):
"""Configure WARP settings"""
cli_api.configure_warp(
all_state=config.all_state,
popular_sites_state=config.popular_sites_state,
domestic_sites_state=config.domestic_sites_state,
block_adult_sites_state=config.block_adult_sites_state,
)
return {"message": "WARP configured"}
@app.get("/api/v1/advanced/warp/status")
async def get_warp_status():
"""Get WARP status"""
status_data = cli_api.warp_status()
return {"status": status_data}
# ============================================================================
# Telegram Bot
# ============================================================================
@app.post("/api/v1/services/telegram/start")
async def start_telegram(config: TelegramBotConfig):
"""Start Telegram bot"""
cli_api.start_telegram_bot(
token=config.token,
adminid=config.admin_ids,
backup_interval=config.backup_interval,
)
return {"message": "Telegram bot started"}
@app.post("/api/v1/services/telegram/stop")
async def stop_telegram():
"""Stop Telegram bot"""
cli_api.stop_telegram_bot()
return {"message": "Telegram bot stopped"}
# ============================================================================
# WebPanel
# ============================================================================
@app.post("/api/v1/services/webpanel/start")
async def start_webpanel(config: WebPanelConfig):
"""Start WebPanel"""
cli_api.start_webpanel(
domain=config.domain,
port=config.port,
admin_username=config.admin_username,
admin_password=config.admin_password,
expiration_minutes=config.expiration_minutes,
debug=config.debug,
decoy_path=config.decoy_path,
)
return {"message": "WebPanel started"}
@app.post("/api/v1/services/webpanel/stop")
async def stop_webpanel():
"""Stop WebPanel"""
cli_api.stop_webpanel()
return {"message": "WebPanel stopped"}
@app.get("/api/v1/services/webpanel/url")
async def get_webpanel_url():
"""Get WebPanel URL"""
url = cli_api.get_webpanel_url()
return {"url": url}
# ============================================================================
# Normal-Sub
# ============================================================================
@app.post("/api/v1/services/normalsub/start")
async def start_normalsub(config: NormalSubConfig):
"""Start Normal-Sub service"""
cli_api.start_normalsub(domain=config.domain, port=config.port)
return {"message": "Normal-Sub started"}
@app.post("/api/v1/services/normalsub/stop")
async def stop_normalsub():
"""Stop Normal-Sub service"""
cli_api.stop_normalsub()
return {"message": "Normal-Sub stopped"}
# ============================================================================
# IP Limiter
# ============================================================================
@app.post("/api/v1/services/ip-limiter/start")
async def start_ip_limiter():
"""Start IP limiter service"""
cli_api.start_ip_limiter()
return {"message": "IP limiter started"}
@app.post("/api/v1/services/ip-limiter/stop")
async def stop_ip_limiter():
"""Stop IP limiter service"""
cli_api.stop_ip_limiter()
return {"message": "IP limiter stopped"}
@app.put("/api/v1/services/ip-limiter/config")
async def config_ip_limiter(config: IPLimiterConfig):
"""Configure IP limiter"""
cli_api.config_ip_limiter(
block_duration=config.block_duration, max_ips=config.max_ips
)
return {"message": "IP limiter configured"}
@app.get("/api/v1/services/ip-limiter/config")
async def get_ip_limiter_config():
"""Get IP limiter configuration"""
config = cli_api.get_ip_limiter_config()
return config
# ============================================================================
# Nodes Management
# ============================================================================
@app.post("/api/v1/nodes")
async def add_node(node: NodeCreate):
"""Add external node"""
result = cli_api.add_node(
name=node.name,
ip=node.ip,
sni=node.sni,
pinSHA256=node.pinSHA256,
port=node.port,
obfs=node.obfs,
insecure=node.insecure,
)
return {"message": result}
@app.delete("/api/v1/nodes/{name}")
async def delete_node(name: str):
"""Delete node"""
result = cli_api.delete_node(name)
return {"message": result}
@app.get("/api/v1/nodes")
async def list_nodes():
"""List all nodes"""
result = cli_api.list_nodes()
return {"nodes": result}
# ============================================================================
# Geo Updates
# ============================================================================
@app.post("/api/v1/geo/update/{country}")
async def update_geo(country: str):
"""Update geo files for country (iran, china, russia)"""
cli_api.update_geo(country)
return {"message": f"Geo files updated for {country}"}
# ============================================================================
# Backup & Restore
# ============================================================================
@app.post("/api/v1/backup")
async def create_backup():
"""Create backup"""
cli_api.backup_hysteria2()
return {"message": "Backup created successfully"}
@app.post("/api/v1/restore")
async def restore_backup(backup_file_path: str):
"""Restore from backup"""
cli_api.restore_hysteria2(backup_file_path)
return {"message": "Restored successfully"}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)

File diff suppressed because it is too large Load Diff

View File

@ -38,3 +38,21 @@ annotated-types==0.7.0
# SSL / ACME management # SSL / ACME management
certbot==5.2.2 certbot==5.2.2
# Core dependencies (from your existing project)
pymongo
python-dotenv
qrcode[pil]
Pillow
# FastAPI and server
fastapi
uvicorn[standard]
python-multipart
pydantic
# Additional utilities
requests
cryptography
asyncio
requests