Files
OutFleet/vpn/views.py
2025-07-21 12:47:47 +03:00

267 lines
11 KiB
Python

def userPortal(request, user_hash):
"""HTML portal for user to view their VPN access links and server information"""
from .models import User, ACLLink, AccessLog
from django.utils import timezone
from datetime import datetime, timedelta
import logging
logger = logging.getLogger(__name__)
try:
user = get_object_or_404(User, hash=user_hash)
logger.info(f"User portal accessed for user {user.username}")
except Http404:
logger.warning(f"User portal access attempt with invalid hash: {user_hash}")
return render(request, 'vpn/user_portal_error.html', {
'error_title': 'Access Denied',
'error_message': 'Invalid access link. Please contact your administrator.'
}, status=403)
try:
# Get all ACL links for the user with server information
acl_links = ACLLink.objects.filter(acl__user=user).select_related('acl__server', 'acl')
logger.info(f"Found {acl_links.count()} ACL links for user {user.username}")
# Calculate date ranges for statistics
now = timezone.now()
thirty_days_ago = now - timedelta(days=30)
logger.debug(f"Calculating stats from {thirty_days_ago} to {now}")
# Calculate total connection statistics
total_connections = AccessLog.objects.filter(
user=user.username,
action='Success'
).count()
recent_connections = AccessLog.objects.filter(
user=user.username,
action='Success',
timestamp__gte=thirty_days_ago
).count()
logger.info(f"User {user.username} stats: total_connections={total_connections}, recent_connections={recent_connections}")
# Group links by server
servers_data = {}
total_links = 0
for link in acl_links:
server = link.acl.server
server_name = server.name
logger.debug(f"Processing link {link.link} for server {server_name}")
logger.debug(f"Link last_access_time: {link.last_access_time}")
if server_name not in servers_data:
# Get server status and info
try:
server_status = server.get_server_status()
server_accessible = True
server_error = None
logger.debug(f"Server {server_name} status retrieved successfully")
except Exception as e:
logger.warning(f"Could not get status for server {server_name}: {e}")
server_status = {}
server_accessible = False
server_error = str(e)
# Calculate server-specific connection stats
server_total_connections = AccessLog.objects.filter(
user=user.username,
server=server_name,
action='Success'
).count()
servers_data[server_name] = {
'server': server,
'status': server_status,
'accessible': server_accessible,
'error': server_error,
'links': [],
'server_type': server.server_type,
'total_connections': server_total_connections,
}
logger.debug(f"Created server data for {server_name} with {server_total_connections} connections")
# Calculate link-specific statistics
# Note: AccessLog doesn't have link-specific tracking, so we'll use server-based stats
link_connections = AccessLog.objects.filter(
user=user.username,
server=server_name,
action='Success'
).count()
link_recent_connections = AccessLog.objects.filter(
user=user.username,
server=server_name,
action='Success',
timestamp__gte=thirty_days_ago
).count()
# Generate daily usage data for the last 30 days
daily_usage = []
max_daily = 0
for i in range(30):
day_start = (now - timedelta(days=29-i)).replace(hour=0, minute=0, second=0, microsecond=0)
day_end = day_start + timedelta(days=1)
day_connections = AccessLog.objects.filter(
user=user.username,
server=server_name,
action='Success',
timestamp__gte=day_start,
timestamp__lt=day_end
).count()
daily_usage.append(day_connections)
max_daily = max(max_daily, day_connections)
logger.debug(f"Link {link.link} stats: connections={link_connections}, recent={link_recent_connections}, max_daily={max_daily}")
# Add link information with comprehensive statistics
link_url = f"{EXTERNAL_ADDRESS}/ss/{link.link}#{server_name}"
link_data = {
'link': link,
'url': link_url,
'comment': link.comment or 'Default',
'last_access': link.last_access_time,
'connections': link_connections,
'recent_connections': link_recent_connections,
'daily_usage': daily_usage,
'max_daily': max_daily,
}
servers_data[server_name]['links'].append(link_data)
total_links += 1
logger.debug(f"Added comprehensive link data for {link.link}")
logger.info(f"Prepared data for {len(servers_data)} servers and {total_links} total links")
logger.info(f"Portal statistics: total_connections={total_connections}, recent_connections={recent_connections}")
context = {
'user': user,
'servers_data': servers_data,
'total_servers': len(servers_data),
'total_links': total_links,
'total_connections': total_connections,
'recent_connections': recent_connections,
'external_address': EXTERNAL_ADDRESS,
}
logger.debug(f"Context prepared with keys: {list(context.keys())}")
logger.debug(f"Servers in context: {list(servers_data.keys())}")
logger.debug(f"Final context values: total_connections={context['total_connections']}, recent_connections={context['recent_connections']}")
# Log sample server data for debugging
for server_name, server_data in servers_data.items():
logger.debug(f"Server {server_name}: total_connections={server_data['total_connections']}, links_count={len(server_data['links'])}")
for i, link_data in enumerate(server_data['links']):
logger.debug(f" Link {i}: connections={link_data['connections']}, recent={link_data['recent_connections']}, daily_usage_len={len(link_data['daily_usage'])}")
return render(request, 'vpn/user_portal.html', context)
except Exception as e:
logger.error(f"Error loading user portal for {user.username}: {e}", exc_info=True)
return render(request, 'vpn/user_portal_error.html', {
'error_title': 'Server Error',
'error_message': 'Unable to load your VPN information. Please try again later or contact support.'
}, status=500)
import yaml
import json
import logging
from django.shortcuts import get_object_or_404, render
from django.http import JsonResponse, HttpResponse, Http404
from mysite.settings import EXTERNAL_ADDRESS
def userFrontend(request, user_hash):
from .models import User, ACLLink
try:
user = get_object_or_404(User, hash=user_hash)
except Http404:
return JsonResponse({"error": "Not allowed"}, status=403)
acl_links = {}
for link in ACLLink.objects.filter(acl__user=user).select_related('acl__server'):
server_name = link.acl.server.name
if server_name not in acl_links:
acl_links[server_name] = []
acl_links[server_name].append({"link": f"{EXTERNAL_ADDRESS}/ss/{link.link}#{link.acl.server.name}", "comment": link.comment})
return JsonResponse(acl_links)
def shadowsocks(request, link):
from .models import ACLLink, AccessLog
import logging
from django.utils import timezone
logger = logging.getLogger(__name__)
try:
acl_link = get_object_or_404(ACLLink, link=link)
acl = acl_link.acl
logger.info(f"Found ACL link for user {acl.user.username} on server {acl.server.name}")
except Http404:
logger.warning(f"ACL link not found: {link}")
AccessLog.objects.create(user=None, server="Unknown", action="Failed",
data=f"ACL not found for link: {link}")
return JsonResponse({"error": "Not allowed"}, status=403)
try:
server_user = acl.server.get_user(acl.user, raw=True)
logger.info(f"Successfully retrieved user credentials for {acl.user.username} from {acl.server.name}")
except Exception as e:
logger.error(f"Failed to get user credentials for {acl.user.username} from {acl.server.name}: {e}")
AccessLog.objects.create(user=acl.user, server=acl.server.name, action="Failed",
data=f"Failed to get credentials: {e}")
return JsonResponse({"error": f"Couldn't get credentials from server. {e}"}, status=500)
if request.GET.get('mode') == 'json':
config = {
"info": "Managed by OutFleet_2 [github.com/house-of-vanity/OutFleet/]",
"password": server_user.password,
"method": server_user.method,
"prefix": "\u0005\u00dc_\u00e0\u0001",
"server": acl.server.client_hostname,
"server_port": server_user.port,
"access_url": server_user.access_url,
"outfleet": {
"acl_link": link,
"server_name": acl.server.name,
"server_type": acl.server.server_type,
}
}
response = json.dumps(config, indent=2)
else:
config = {
"transport": {
"$type": "tcpudp",
"tcp": {
"$type": "shadowsocks",
"endpoint": f"{acl.server.client_hostname}:{server_user.port}",
"cipher": f"{server_user.method}",
"secret": f"{server_user.password}",
"prefix": "\u0005\u00dc_\u00e0\u0001"
},
"udp": {
"$type": "shadowsocks",
"endpoint": f"{acl.server.client_hostname}:{server_user.port}",
"cipher": f"{server_user.method}",
"secret": f"{server_user.password}",
"prefix": "\u0005\u00dc_\u00e0\u0001"
}
}
}
response = yaml.dump(config, allow_unicode=True)
# Update last access time for this specific link
acl_link.last_access_time = timezone.now()
acl_link.save(update_fields=['last_access_time'])
# Still create AccessLog for audit purposes
AccessLog.objects.create(user=acl.user, server=acl.server.name, action="Success", data=response)
return HttpResponse(response, content_type=f"{ 'application/json; charset=utf-8' if request.GET.get('mode') == 'json' else 'text/html' }")