diff --git a/telegram_bot/admin.py b/telegram_bot/admin.py index eb998cc..9ee523a 100644 --- a/telegram_bot/admin.py +++ b/telegram_bot/admin.py @@ -5,6 +5,7 @@ from django.shortcuts import redirect from django.contrib import messages from django.utils import timezone from django import forms +from django.contrib.admin.widgets import FilteredSelectMultiple from .models import BotSettings, TelegramMessage, AccessRequest from .localization import MessageLocalizer import logging @@ -18,6 +19,16 @@ class AccessRequestAdminForm(forms.ModelForm): class Meta: model = AccessRequest fields = '__all__' + widgets = { + 'selected_inbounds': FilteredSelectMultiple( + verbose_name='Inbound Templates', + is_stacked=False + ), + 'selected_subscription_groups': FilteredSelectMultiple( + verbose_name='Subscription Groups', + is_stacked=False + ), + } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -31,6 +42,21 @@ class AccessRequestAdminForm(forms.ModelForm): self.fields['selected_existing_user'].queryset = User.objects.filter( telegram_user_id__isnull=True ).order_by('username') + + # Configure inbound and subscription group fields + if 'selected_inbounds' in self.fields: + from vpn.models_xray import Inbound + self.fields['selected_inbounds'].queryset = Inbound.objects.all().order_by('name') + self.fields['selected_inbounds'].label = 'Inbound Templates' + self.fields['selected_inbounds'].help_text = 'Select inbound templates to assign to this user' + + if 'selected_subscription_groups' in self.fields: + from vpn.models_xray import SubscriptionGroup + self.fields['selected_subscription_groups'].queryset = SubscriptionGroup.objects.filter( + is_active=True + ).order_by('name') + self.fields['selected_subscription_groups'].label = 'Subscription Groups' + self.fields['selected_subscription_groups'].help_text = 'Select subscription groups to assign to this user' @admin.register(BotSettings) @@ -337,6 +363,13 @@ class AccessRequestAdmin(admin.ModelAdmin): ), 'description': 'Choose existing user to link OR specify username for new user' }), + ('VPN Access Configuration', { + 'fields': ( + 'selected_inbounds', + 'selected_subscription_groups', + ), + 'description': 'Select inbound templates and subscription groups to assign to the user' + }), ('Telegram User', { 'fields': ( 'telegram_user_id', @@ -460,6 +493,13 @@ class AccessRequestAdmin(admin.ModelAdmin): obj.processed_at = timezone.now() obj.save() + # Assign VPN access to the linked user + try: + self._assign_vpn_access(obj.selected_existing_user, obj) + except Exception as e: + logger.error(f"Failed to assign VPN access: {e}") + messages.warning(request, f"User linked but VPN access assignment failed: {e}") + # Send notification self._send_approval_notification(obj) @@ -494,6 +534,13 @@ class AccessRequestAdmin(admin.ModelAdmin): selected_user.telegram_first_name = access_request.telegram_first_name or "" selected_user.telegram_last_name = access_request.telegram_last_name or "" selected_user.save() + + # Assign VPN access to the linked user + try: + self._assign_vpn_access(selected_user, access_request) + except Exception as e: + logger.error(f"Failed to assign VPN access to user {selected_user.username}: {e}") + return selected_user # Check if we can link to existing user by telegram_username @@ -511,6 +558,13 @@ class AccessRequestAdmin(admin.ModelAdmin): existing_user_by_username.telegram_first_name = access_request.telegram_first_name or "" existing_user_by_username.telegram_last_name = access_request.telegram_last_name or "" existing_user_by_username.save() + + # Assign VPN access to the linked user + try: + self._assign_vpn_access(existing_user_by_username, access_request) + except Exception as e: + logger.error(f"Failed to assign VPN access to user {existing_user_by_username.username}: {e}") + return existing_user_by_username # Use desired_username if provided, otherwise fallback to Telegram data @@ -551,12 +605,81 @@ class AccessRequestAdmin(admin.ModelAdmin): ) logger.info(f"Successfully created user {user.username} (ID: {user.id}) from Telegram request {access_request.id}") + + # Assign VPN access (inbounds and subscription groups) + try: + self._assign_vpn_access(user, access_request) + except Exception as e: + logger.error(f"Failed to assign VPN access to user {user.username}: {e}") + # Continue even if VPN assignment fails - user is already created + return user except Exception as e: logger.error(f"Error creating user from request {access_request.id}: {e}") raise + def _assign_vpn_access(self, user, access_request): + """Assign selected inbounds and subscription groups to the user""" + try: + from vpn.models_xray import UserSubscription, SubscriptionGroup + + # Assign subscription groups + for subscription_group in access_request.selected_subscription_groups.all(): + user_subscription, created = UserSubscription.objects.get_or_create( + user=user, + subscription_group=subscription_group, + defaults={'active': True} + ) + if created: + logger.info(f"Assigned subscription group '{subscription_group.name}' to user {user.username}") + else: + # Ensure it's active if it already existed + if not user_subscription.active: + user_subscription.active = True + user_subscription.save() + logger.info(f"Re-activated subscription group '{subscription_group.name}' for user {user.username}") + + # Handle individual inbounds - create a custom subscription group for them + selected_inbounds = access_request.selected_inbounds.all() + if selected_inbounds.exists(): + # Create a custom subscription group for this user's individual inbounds + custom_group_name = f"Custom_{user.username}_{access_request.id}" + custom_group, created = SubscriptionGroup.objects.get_or_create( + name=custom_group_name, + defaults={ + 'description': f'Custom inbounds for {user.username} from Telegram request', + 'is_active': True + } + ) + + if created: + # Add selected inbounds to the custom group + custom_group.inbounds.set(selected_inbounds) + logger.info(f"Created custom subscription group '{custom_group_name}' with {selected_inbounds.count()} inbounds") + else: + # Update existing custom group + custom_group.inbounds.add(*selected_inbounds) + logger.info(f"Updated custom subscription group '{custom_group_name}' with additional inbounds") + + # Assign the custom group to the user + user_subscription, created = UserSubscription.objects.get_or_create( + user=user, + subscription_group=custom_group, + defaults={'active': True} + ) + if created: + logger.info(f"Assigned custom subscription group to user {user.username}") + + inbound_count = selected_inbounds.count() + group_count = access_request.selected_subscription_groups.count() + + logger.info(f"Successfully assigned {group_count} subscription groups and {inbound_count} individual inbounds to user {user.username}") + + except Exception as e: + logger.error(f"Error assigning VPN access to user {user.username}: {e}") + raise + def _send_approval_notification(self, access_request): """Send approval notification via Telegram""" try: diff --git a/telegram_bot/migrations/0009_accessrequest_selected_inbounds_and_more.py b/telegram_bot/migrations/0009_accessrequest_selected_inbounds_and_more.py new file mode 100644 index 0000000..265deca --- /dev/null +++ b/telegram_bot/migrations/0009_accessrequest_selected_inbounds_and_more.py @@ -0,0 +1,24 @@ +# Generated by Django 5.1.7 on 2025-08-15 12:31 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('telegram_bot', '0008_accessrequest_selected_existing_user'), + ('vpn', '0026_alter_subscriptiongroup_options'), + ] + + operations = [ + migrations.AddField( + model_name='accessrequest', + name='selected_inbounds', + field=models.ManyToManyField(blank=True, help_text='Inbound templates to assign to the user', to='vpn.inbound'), + ), + migrations.AddField( + model_name='accessrequest', + name='selected_subscription_groups', + field=models.ManyToManyField(blank=True, help_text='Subscription groups to assign to the user', to='vpn.subscriptiongroup'), + ), + ] diff --git a/telegram_bot/models.py b/telegram_bot/models.py index f6577e4..ef03672 100644 --- a/telegram_bot/models.py +++ b/telegram_bot/models.py @@ -247,6 +247,18 @@ class AccessRequest(models.Model): help_text="First message from this user" ) + # Inbound templates and subscription groups + selected_inbounds = models.ManyToManyField( + 'vpn.Inbound', + blank=True, + help_text="Inbound templates to assign to the user" + ) + selected_subscription_groups = models.ManyToManyField( + 'vpn.SubscriptionGroup', + blank=True, + help_text="Subscription groups to assign to the user" + ) + # Timestamps created_at = models.DateTimeField(auto_now_add=True) processed_at = models.DateTimeField(null=True, blank=True)