diff --git a/vpn/admin.py b/vpn/admin.py
index e571569..4932335 100644
--- a/vpn/admin.py
+++ b/vpn/admin.py
@@ -1347,20 +1347,29 @@ class ACLLinkAdmin(admin.ModelAdmin):
if not stats.daily_usage:
return mark_safe('No data')
- # Create mini chart
+ # Create wider mini chart for better visibility
max_val = max(stats.daily_usage) if stats.daily_usage else 1
- chart_html = '
'
+ chart_html = '
'
- for day_count in stats.daily_usage[-14:]: # Last 14 days for compact view
+ # Show last 30 days with wider bars for better visibility
+ for day_count in stats.daily_usage[-30:]: # Last 30 days
if max_val > 0:
height_percent = (day_count / max_val) * 100
else:
height_percent = 0
color = '#4ade80' if day_count > 0 else '#e5e7eb'
- chart_html += f'
'
+ chart_html += f'
'
chart_html += '
'
+
+ # Add summary info below chart
+ total_last_30 = sum(stats.daily_usage[-30:]) if stats.daily_usage else 0
+ avg_daily = total_last_30 / 30 if total_last_30 > 0 else 0
+ chart_html += f'
'
+ chart_html += f'Max: {max_val} | Avg: {avg_daily:.1f}'
+ chart_html += f'
'
+
return mark_safe(chart_html)
except:
return mark_safe('
-')
diff --git a/vpn/server_plugins/outline.py b/vpn/server_plugins/outline.py
index 08f95f7..3579dd4 100644
--- a/vpn/server_plugins/outline.py
+++ b/vpn/server_plugins/outline.py
@@ -165,18 +165,41 @@ class OutlineServer(Server):
raise OutlineServerErrorException(f"Error searching for key: {e}")
def get_user(self, user, raw=False):
- user_info = self._get_key(user)
- if raw:
- return user_info
- else:
- outline_key_dict = user_info.__dict__
+ try:
+ user_info = self._get_key(user)
+ if raw:
+ return user_info
+ else:
+ outline_key_dict = user_info.__dict__
- outline_key_dict = {
- key: value
- for key, value in user_info.__dict__.items()
- if not key.startswith('_') and key not in [] # fields to mask
- }
- return outline_key_dict
+ outline_key_dict = {
+ key: value
+ for key, value in user_info.__dict__.items()
+ if not key.startswith('_') and key not in [] # fields to mask
+ }
+ return outline_key_dict
+ except OutlineServerErrorException as e:
+ # If user key not found, try to create it automatically
+ if "Key not found" in str(e):
+ self.logger.warning(f"[{self.name}] Key not found for user {user.username}, attempting to create")
+ try:
+ self.add_user(user)
+ # Try to get the key again after creation
+ user_info = self._get_key(user)
+ if raw:
+ return user_info
+ else:
+ outline_key_dict = {
+ key: value
+ for key, value in user_info.__dict__.items()
+ if not key.startswith('_') and key not in []
+ }
+ return outline_key_dict
+ except Exception as create_error:
+ self.logger.error(f"[{self.name}] Failed to create missing key for user {user.username}: {create_error}")
+ raise OutlineServerErrorException(f"Failed to get credentials: {e}")
+ else:
+ raise
def add_user(self, user):
@@ -195,9 +218,9 @@ class OutlineServer(Server):
# Check if user needs update - but don't delete immediately
needs_update = (
server_user.method != "chacha20-ietf-poly1305" or
- server_user.port != int(self.client_port) or
server_user.name != user.username or
server_user.password != user.hash
+ # Don't check port as Outline can assign different ports automatically
)
if needs_update:
@@ -215,8 +238,8 @@ class OutlineServer(Server):
name=user.username,
method="chacha20-ietf-poly1305",
password=user.hash,
- data_limit=None,
- port=int(self.client_port)
+ data_limit=None
+ # Don't specify port - let server assign automatically
)
logger.info(f"[{self.name}] User {user.username} updated")
except OutlineServerErrorException as e:
@@ -233,8 +256,8 @@ class OutlineServer(Server):
name=user.username,
method="chacha20-ietf-poly1305",
password=user.hash,
- data_limit=None,
- port=int(self.client_port)
+ data_limit=None
+ # Don't specify port - let server assign automatically
)
logger.info(f"[{self.name}] User {user.username} created")
except OutlineServerErrorException as e:
diff --git a/vpn/tasks.py b/vpn/tasks.py
index 704d2c6..3db5474 100644
--- a/vpn/tasks.py
+++ b/vpn/tasks.py
@@ -119,8 +119,8 @@ def sync_users(self, server_id):
server = Server.objects.get(id=server_id)
except Server.DoesNotExist:
error_message = f"Server with id {server_id} not found - may have been deleted"
- logger.error(error_message)
- create_task_log(task_id, "sync_all_users_on_server", "Server not found", 'FAILURE', message=error_message, execution_time=time.time() - start_time)
+ logger.warning(error_message)
+ create_task_log(task_id, "sync_all_users_on_server", "Server not found", 'SUCCESS', message=error_message, execution_time=time.time() - start_time)
return error_message # Don't raise exception for deleted servers
# Test server connectivity before proceeding