import ipaddress from crispy_forms.helper import FormHelper from crispy_forms.layout import Column, HTML, Layout, Row, Submit from django import forms from django.utils.translation import gettext_lazy as _ from .models import RoutingTemplate class RoutingTemplateForm(forms.ModelForm): class Meta: model = RoutingTemplate fields = [ 'name', 'wireguard_instance', 'default_template', 'route_type', 'custom_routes', 'allow_peer_custom_routes', 'enforce_route_policy', ] def __init__(self, *args, **kwargs): self.user = kwargs.pop('user', None) super().__init__(*args, **kwargs) self.fields['name'].label = _("Name") self.fields['wireguard_instance'].label = _("WireGuard Instance") self.fields['default_template'].label = _("Default Template") self.fields['route_type'].label = _("Route Type") self.fields['custom_routes'].label = _("Custom Routes") self.fields['allow_peer_custom_routes'].label = _("Allow Peer Custom Routes") self.fields['enforce_route_policy'].label = _("Enforce Route Policy") back_label = _("Back") delete_label = _("Delete") self.helper = FormHelper() self.helper.form_method = 'post' if self.instance.pk: delete_html = f"{delete_label}" else: delete_html = '' self.helper.layout = Layout( Row( Column('name', css_class='form-group col-md-6 mb-0'), Column('wireguard_instance', css_class='form-group col-md-6 mb-0'), css_class='form-row' ), Row( Column('route_type', css_class='form-group col-md-12 mb-0'), css_class='form-row' ), Row( Column('custom_routes', css_class='form-group col-md-12 mb-0'), css_class='form-row' ), Row( Column('default_template', css_class='form-group col-md-12 mb-0'), Column('enforce_route_policy', css_class='form-group col-md-12 mb-0'), Column('allow_peer_custom_routes', css_class='form-group col-md-12 mb-0'), css_class='form-row' ), Row( Column( Submit('submit', _('Save'), css_class='btn btn-success'), HTML(f' {back_label} '), HTML(delete_html), css_class='col-md-12'), css_class='form-row' ) ) def clean(self): cleaned_data = super().clean() allow_custom = cleaned_data.get('allow_peer_custom_routes') enforce_policy = cleaned_data.get('enforce_route_policy') route_type = cleaned_data.get('route_type') custom_routes = cleaned_data.get('custom_routes') if allow_custom and enforce_policy: raise forms.ValidationError(_("You cannot enable 'Enforce Route Policy' when 'Allow Peer Custom Routes' is checked.")) if route_type == 'custom' and not custom_routes: self.add_error('custom_routes', _("At least one route must be provided when Route Type is 'Custom'.")) if custom_routes: lines = custom_routes.strip().split('\n') validated_routes = [] for line in lines: line = line.strip() if not line: continue try: network = ipaddress.ip_network(line, strict=False) if str(network) == '0.0.0.0/0': self.add_error('custom_routes', _("The route 0.0.0.0/0 is not allowed. Use the 'Default Route' type instead.")) break validated_routes.append(str(network)) except ValueError: self.add_error('custom_routes', _("Invalid route format: '%(line)s'. Please use CIDR notation (e.g., 192.168.1.0/24).") % {'line': line}) break if not self.errors.get('custom_routes'): cleaned_data['custom_routes'] = '\n'.join(validated_routes) if route_type == 'default' and custom_routes: self.add_error('custom_routes', _("Custom routes should be empty when Route Type is 'Default Route'.")) return cleaned_data