Files
OutFleet/vpn/views.py

276 lines
11 KiB
Python
Raw Normal View History

2025-07-20 23:04:58 +03:00
def userPortal(request, user_hash):
"""HTML portal for user to view their VPN access links and server information"""
2025-07-20 23:32:56 +03:00
from .models import User, ACLLink, AccessLog
2025-07-20 23:04:58 +03:00
import logging
2025-07-20 23:32:56 +03:00
from django.db.models import Count, Q
2025-07-20 23:04:58 +03:00
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')
2025-07-20 23:32:56 +03:00
# Get connection statistics for all user's links
# Count successful connections for each specific link
connection_stats = {}
2025-07-21 00:52:45 +03:00
recent_connection_stats = {}
usage_frequency = {}
2025-07-20 23:32:56 +03:00
total_connections = 0
2025-07-21 00:52:45 +03:00
# Get date ranges for analysis
from datetime import datetime, timedelta
now = datetime.now()
recent_date = now - timedelta(days=30)
2025-07-20 23:32:56 +03:00
for acl_link in acl_links:
# Count successful connections for this specific link by checking if the link appears in the access log data
link_connections = AccessLog.objects.filter(
user=user.username,
server=acl_link.acl.server.name,
action='Success'
).extra(
where=["data LIKE %s"],
params=[f'%{acl_link.link}%']
).count()
2025-07-21 00:52:45 +03:00
# Get recent connections (last 30 days)
recent_connections = AccessLog.objects.filter(
user=user.username,
server=acl_link.acl.server.name,
action='Success',
timestamp__gte=recent_date
).extra(
where=["data LIKE %s"],
params=[f'%{acl_link.link}%']
).count()
# Calculate usage frequency (connections per week over last 30 days)
# Get daily usage for the last 7 days for mini chart
daily_usage = []
for i in range(7):
day_start = now - timedelta(days=i+1)
day_end = now - timedelta(days=i)
day_connections = AccessLog.objects.filter(
user=user.username,
server=acl_link.acl.server.name,
action='Success',
timestamp__gte=day_start,
timestamp__lt=day_end
).extra(
where=["data LIKE %s"],
params=[f'%{acl_link.link}%']
).count()
daily_usage.append(day_connections)
# Reverse to show oldest to newest
daily_usage.reverse()
2025-07-20 23:32:56 +03:00
# If no specific link matches found, fall back to general server connection count for this user
if link_connections == 0:
server_connections = AccessLog.objects.filter(
user=user.username,
server=acl_link.acl.server.name,
action='Success'
).count()
2025-07-21 00:52:45 +03:00
server_recent_connections = AccessLog.objects.filter(
user=user.username,
server=acl_link.acl.server.name,
action='Success',
timestamp__gte=recent_date
).count()
2025-07-20 23:32:56 +03:00
# Get number of links for this server for this user
user_links_on_server = ACLLink.objects.filter(
acl__user=user,
acl__server=acl_link.acl.server
).count()
# Distribute connections evenly among links if we can't track specific usage
if user_links_on_server > 0:
link_connections = server_connections // user_links_on_server
2025-07-21 00:52:45 +03:00
recent_connections = server_recent_connections // user_links_on_server
# Distribute daily usage as well
if sum(daily_usage) == 0: # If no activity, create empty chart
daily_usage = [0] * 7
else:
avg_daily = max(1, sum(daily_usage) // (user_links_on_server * 7))
daily_usage = [avg_daily if sum(daily_usage) > 0 else 0 for _ in range(7)]
2025-07-20 23:32:56 +03:00
connection_stats[acl_link.link] = link_connections
2025-07-21 00:52:45 +03:00
recent_connection_stats[acl_link.link] = recent_connections
usage_frequency[acl_link.link] = daily_usage
2025-07-20 23:32:56 +03:00
total_connections += link_connections
2025-07-20 23:04:58 +03:00
# Group links by server
servers_data = {}
total_links = 0
for link in acl_links:
server = link.acl.server
server_name = server.name
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
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)
servers_data[server_name] = {
'server': server,
'status': server_status,
'accessible': server_accessible,
'error': server_error,
'links': [],
'server_type': server.server_type,
2025-07-20 23:32:56 +03:00
'total_connections': 0,
2025-07-20 23:04:58 +03:00
}
2025-07-20 23:32:56 +03:00
# Add link information with connection stats
2025-07-20 23:04:58 +03:00
link_url = f"{EXTERNAL_ADDRESS}/ss/{link.link}#{server_name}"
2025-07-20 23:32:56 +03:00
connection_count = connection_stats.get(link.link, 0)
2025-07-21 00:52:45 +03:00
recent_count = recent_connection_stats.get(link.link, 0)
daily_usage = usage_frequency.get(link.link, [0] * 7)
2025-07-20 23:32:56 +03:00
2025-07-20 23:04:58 +03:00
servers_data[server_name]['links'].append({
'link': link,
'url': link_url,
'comment': link.comment or 'Default',
2025-07-20 23:32:56 +03:00
'connections': connection_count,
2025-07-21 00:52:45 +03:00
'recent_connections': recent_count,
'daily_usage': daily_usage,
'max_daily': max(daily_usage) if daily_usage else 0,
2025-07-20 23:04:58 +03:00
})
2025-07-20 23:32:56 +03:00
servers_data[server_name]['total_connections'] += connection_count
2025-07-20 23:04:58 +03:00
total_links += 1
2025-07-21 00:52:45 +03:00
# Calculate total recent connections from all links
total_recent_connections = sum(recent_connection_stats.values())
2025-07-20 23:32:56 +03:00
2025-07-20 23:04:58 +03:00
context = {
'user': user,
'servers_data': servers_data,
'total_servers': len(servers_data),
'total_links': total_links,
2025-07-20 23:32:56 +03:00
'total_connections': total_connections,
2025-07-21 00:52:45 +03:00
'recent_connections': total_recent_connections,
2025-07-20 23:04:58 +03:00
'external_address': EXTERNAL_ADDRESS,
}
return render(request, 'vpn/user_portal.html', context)
except Exception as e:
logger.error(f"Error loading user portal for {user.username}: {e}")
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)
2025-02-23 19:18:23 +00:00
import yaml
2025-02-23 19:33:54 +00:00
import json
2025-07-20 23:04:58 +03:00
import logging
from django.shortcuts import get_object_or_404, render
2024-10-28 17:15:49 +00:00
from django.http import JsonResponse, HttpResponse, Http404
2025-02-25 12:39:08 +02:00
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)
2024-10-20 21:57:12 +00:00
def shadowsocks(request, link):
2024-10-28 17:15:49 +00:00
from .models import ACLLink, AccessLog
2025-07-20 22:30:04 +03:00
import logging
logger = logging.getLogger(__name__)
2024-10-28 17:15:49 +00:00
try:
acl_link = get_object_or_404(ACLLink, link=link)
acl = acl_link.acl
2025-07-20 22:30:04 +03:00
logger.info(f"Found ACL link for user {acl.user.username} on server {acl.server.name}")
2024-10-28 17:15:49 +00:00
except Http404:
2025-07-20 22:30:04 +03:00
logger.warning(f"ACL link not found: {link}")
2025-02-23 21:22:12 +00:00
AccessLog.objects.create(user=None, server="Unknown", action="Failed",
data=f"ACL not found for link: {link}")
2024-10-28 17:15:49 +00:00
return JsonResponse({"error": "Not allowed"}, status=403)
2025-02-23 21:22:12 +00:00
2024-10-21 13:22:03 +00:00
try:
server_user = acl.server.get_user(acl.user, raw=True)
2025-07-20 22:30:04 +03:00
logger.info(f"Successfully retrieved user credentials for {acl.user.username} from {acl.server.name}")
2024-10-26 12:22:19 +00:00
except Exception as e:
2025-07-20 22:30:04 +03:00
logger.error(f"Failed to get user credentials for {acl.user.username} from {acl.server.name}: {e}")
2025-02-23 21:22:12 +00:00
AccessLog.objects.create(user=acl.user, server=acl.server.name, action="Failed",
2025-07-20 22:30:04 +03:00
data=f"Failed to get credentials: {e}")
return JsonResponse({"error": f"Couldn't get credentials from server. {e}"}, status=500)
2024-10-21 13:22:03 +00:00
2025-02-23 21:22:12 +00:00
if request.GET.get('mode') == 'json':
config = {
2025-02-25 12:39:08 +02:00
"info": "Managed by OutFleet_2 [github.com/house-of-vanity/OutFleet/]",
2025-02-23 21:22:12 +00:00
"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,
}
}
2025-02-23 21:29:03 +00:00
response = json.dumps(config, indent=2)
2025-02-23 21:22:12 +00:00
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"
}
}
}
2025-02-23 21:29:03 +00:00
response = yaml.dump(config, allow_unicode=True)
2025-02-23 21:22:12 +00:00
AccessLog.objects.create(user=acl.user, server=acl.server.name, action="Success", data=response)
2025-02-23 19:18:23 +00:00
2025-02-23 21:22:12 +00:00
return HttpResponse(response, content_type=f"{ 'application/json; charset=utf-8' if request.GET.get('mode') == 'json' else 'text/html' }")
2025-02-23 19:18:23 +00:00