Initial commit

This commit is contained in:
2025-11-18 03:36:49 +08:00
commit d17c7efb3c
7078 changed files with 831480 additions and 0 deletions

218
admin_panel/forms.py Normal file
View File

@@ -0,0 +1,218 @@
from django import forms
import logging
from core.models import SystemSettings
from plans.models import Plan
from domains.models import Domain
from billing.models import Invoice
from decimal import Decimal
class SystemSettingsForm(forms.ModelForm):
overage_action = forms.ChoiceField(choices=[('shutdown', '停服'), ('limit', '限速')], required=False, widget=forms.Select(attrs={'class': 'form-select'}))
overage_limit_bps = forms.IntegerField(required=False, min_value=0, widget=forms.NumberInput(attrs={'class': 'form-control', 'placeholder': '字节/秒'}))
class Meta:
model = SystemSettings
fields = [
'goedge_base_url',
'admin_access_key_id',
'admin_access_key',
'default_node_cluster_id',
'default_free_traffic_gb_per_domain',
'default_http_access_log_policy_id',
'default_http_firewall_policy_id',
'cname_template',
'anomaly_detection_enabled',
'anomaly_threshold_multiplier',
'anomaly_window_days',
'anomaly_min_gb',
'captcha_enabled',
'epay_api_base_url',
'epay_pid',
'epay_key',
]
widgets = {
'goedge_base_url': forms.URLInput(attrs={'class': 'form-control', 'placeholder': 'https://backend.example.com'}),
'admin_access_key_id': forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'AKID...'}),
'admin_access_key': forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'AK...'}),
'default_node_cluster_id': forms.NumberInput(attrs={'class': 'form-control', 'placeholder': '集群ID'}),
'default_free_traffic_gb_per_domain': forms.NumberInput(attrs={'class': 'form-control'}),
'default_http_access_log_policy_id': forms.NumberInput(attrs={'class': 'form-control', 'placeholder': 'HTTPAccessLogPolicyId'}),
'default_http_firewall_policy_id': forms.NumberInput(attrs={'class': 'form-control', 'placeholder': 'HTTPFirewallPolicyId'}),
'cname_template': forms.TextInput(attrs={'class': 'form-control', 'placeholder': '{sub}.cdn.example.com'}),
'anomaly_detection_enabled': forms.CheckboxInput(attrs={'class': 'form-check-input'}),
'anomaly_threshold_multiplier': forms.NumberInput(attrs={'class': 'form-control', 'step': '0.1', 'min': '1'}),
'anomaly_window_days': forms.NumberInput(attrs={'class': 'form-control', 'min': '3'}),
'anomaly_min_gb': forms.NumberInput(attrs={'class': 'form-control', 'step': '0.1', 'min': '0'}),
'captcha_enabled': forms.CheckboxInput(attrs={'class': 'form-check-input'}),
'epay_api_base_url': forms.URLInput(attrs={'class': 'form-control', 'placeholder': 'https://api.example.com'}),
'epay_pid': forms.TextInput(attrs={'class': 'form-control', 'placeholder': '商户ID'}),
'epay_key': forms.TextInput(attrs={'class': 'form-control', 'placeholder': '商户密钥'}),
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
try:
policy = dict(getattr(self.instance, 'default_overage_policy', {}) or {})
except Exception:
policy = {}
self.fields['overage_action'].initial = (policy.get('action') or 'shutdown')
self.fields['overage_limit_bps'].initial = int(policy.get('limit_bps') or 0)
def save(self, commit=True):
obj = super().save(commit=False)
act = (self.cleaned_data.get('overage_action') or 'shutdown').lower()
limit = int(self.cleaned_data.get('overage_limit_bps') or 0)
obj.default_overage_policy = {'action': act, 'limit_bps': limit}
if commit:
obj.save()
return obj
class PlanForm(forms.ModelForm):
# 使用 JSONField 保证 features 验证
features = forms.JSONField(required=False, widget=forms.Textarea(attrs={'class': 'form-control', 'rows': 4, 'placeholder': '{"waf_enabled": true, "http3_enabled": false}' }))
# 可视化常用功能开关(与 JSON 保持兼容)
waf_enabled = forms.BooleanField(required=False, widget=forms.CheckboxInput(attrs={'class': 'form-check-input'}))
http3_enabled = forms.BooleanField(required=False, widget=forms.CheckboxInput(attrs={'class': 'form-check-input'}))
websocket_enabled = forms.BooleanField(required=False, widget=forms.CheckboxInput(attrs={'class': 'form-check-input'}))
realtime_logs_enabled = forms.BooleanField(required=False, widget=forms.CheckboxInput(attrs={'class': 'form-check-input'}))
custom_ssl_enabled = forms.BooleanField(required=False, widget=forms.CheckboxInput(attrs={'class': 'form-check-input'}))
page_rules_limit = forms.IntegerField(required=False, min_value=0, widget=forms.NumberInput(attrs={'class': 'form-control', 'placeholder': '页面规则数量上限(可选)'}))
class Meta:
model = Plan
fields = [
'name',
'description',
'billing_mode',
'base_price_per_domain',
'included_traffic_gb_per_domain',
'overage_price_per_gb',
'allow_overage',
'is_active',
'is_public',
'allow_new_purchase',
'allow_renew',
'features',
]
widgets = {
'name': forms.TextInput(attrs={'class': 'form-control'}),
'description': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
'billing_mode': forms.TextInput(attrs={'class': 'form-control'}),
'base_price_per_domain': forms.NumberInput(attrs={'class': 'form-control', 'step': '0.01'}),
'included_traffic_gb_per_domain': forms.NumberInput(attrs={'class': 'form-control'}),
'overage_price_per_gb': forms.NumberInput(attrs={'class': 'form-control', 'step': '0.01'}),
'allow_overage': forms.CheckboxInput(attrs={'class': 'form-check-input'}),
'is_active': forms.CheckboxInput(attrs={'class': 'form-check-input'}),
'is_public': forms.CheckboxInput(attrs={'class': 'form-check-input'}),
'allow_new_purchase': forms.CheckboxInput(attrs={'class': 'form-check-input'}),
'allow_renew': forms.CheckboxInput(attrs={'class': 'form-check-input'}),
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# 初始化可视化字段的初始值来源于现有 features JSON
features_dict = {}
try:
if getattr(self.instance, 'features', None):
features_dict = dict(self.instance.features)
except Exception:
features_dict = {}
self.fields['waf_enabled'].initial = features_dict.get('waf_enabled', False)
self.fields['http3_enabled'].initial = features_dict.get('http3_enabled', False)
self.fields['websocket_enabled'].initial = features_dict.get('websocket_enabled', False)
self.fields['realtime_logs_enabled'].initial = features_dict.get('realtime_logs_enabled', False)
self.fields['custom_ssl_enabled'].initial = features_dict.get('custom_ssl_enabled', False)
self.fields['page_rules_limit'].initial = features_dict.get('page_rules_limit', None)
def save(self, commit=True):
plan = super().save(commit=False)
# 合并 JSON 字段与可视化字段
base_features = {}
try:
if getattr(self.instance, 'features', None):
base_features = dict(self.instance.features)
except Exception:
base_features = {}
cleaned_features = self.cleaned_data.get('features') or {}
if not isinstance(cleaned_features, dict):
cleaned_features = {}
# 先合并自由 JSON
merged = {**base_features, **cleaned_features}
# 再覆盖常用可视化选项
merged['waf_enabled'] = bool(self.cleaned_data.get('waf_enabled'))
merged['http3_enabled'] = bool(self.cleaned_data.get('http3_enabled'))
merged['websocket_enabled'] = bool(self.cleaned_data.get('websocket_enabled'))
merged['realtime_logs_enabled'] = bool(self.cleaned_data.get('realtime_logs_enabled'))
merged['custom_ssl_enabled'] = bool(self.cleaned_data.get('custom_ssl_enabled'))
page_rules_limit = self.cleaned_data.get('page_rules_limit')
if page_rules_limit is not None:
merged['page_rules_limit'] = int(page_rules_limit)
else:
# 若用户未设置,上次值保留(已在 merged 中)
pass
plan.features = merged
if commit:
plan.save()
return plan
class DomainPlanSwitchForm(forms.ModelForm):
class Meta:
model = Domain
fields = ['current_plan']
widgets = {
'current_plan': forms.Select(attrs={'class': 'form-select'}),
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# 仅允许选择激活的套餐(运营可见不强制)
self.fields['current_plan'].queryset = Plan.objects.filter(is_active=True).order_by('base_price_per_domain')
class DomainGrantTrafficForm(forms.ModelForm):
class Meta:
model = Domain
fields = ['extra_free_traffic_gb_current_cycle']
widgets = {
'extra_free_traffic_gb_current_cycle': forms.NumberInput(attrs={'class': 'form-control', 'min': '0'}),
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['extra_free_traffic_gb_current_cycle'].label = '本周期额外赠送流量GB'
class InvoiceAdjustmentForm(forms.Form):
description = forms.CharField(label='调整说明', max_length=200, required=False, initial='人工调整', widget=forms.TextInput(attrs={'class': 'form-control'}))
amount = forms.DecimalField(label='调整金额(¥)', max_digits=12, decimal_places=2, widget=forms.NumberInput(attrs={'class': 'form-control', 'step': '0.01'}))
is_increase = forms.BooleanField(required=False, initial=True, widget=forms.CheckboxInput(attrs={'class': 'form-check-input'}))
def apply(self, invoice: Invoice) -> Invoice:
amt = Decimal(str(self.cleaned_data['amount']))
if not self.cleaned_data.get('is_increase'):
amt = -amt
desc = self.cleaned_data.get('description') or '人工调整'
# 更新账单金额
invoice.amount_adjustment = (Decimal(str(invoice.amount_adjustment)) + amt).quantize(Decimal('0.01'))
invoice.amount_total = (Decimal(str(invoice.amount_total)) + amt).quantize(Decimal('0.01'))
invoice.save(update_fields=['amount_adjustment', 'amount_total'])
# 记录为一条账单项
try:
from billing.models import InvoiceItem
InvoiceItem.objects.create(
invoice=invoice,
domain=None,
description=desc,
quantity=Decimal('0.000'),
unit_price=Decimal('0.00'),
amount=amt,
)
except Exception:
logger.exception('invoice item write failed')
return invoice
logger = logging.getLogger(__name__)