- Disable now removes all policies (saved to DB) + Enable restores them - Project add: policies are now user-selected (checkbox), not auto-attached - Fix serializer allow_blank for optional fields (email/phone/password) - Better error reporting for policy detach/attach failures - Handle duplicate user creation with clear error message Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
145 lines
5.9 KiB
Python
145 lines
5.9 KiB
Python
from rest_framework import serializers
|
|
from .models import IAMUser, IAMUserProject, VolcAccount, GlobalConfig, AlertRecord, SpendingRecord, QuotaAllocation
|
|
|
|
|
|
class VolcAccountSerializer(serializers.ModelSerializer):
|
|
class Meta:
|
|
model = VolcAccount
|
|
fields = ['id', 'name', 'access_key_hint', 'is_active', 'created_at', 'updated_at']
|
|
read_only_fields = ['access_key_hint', 'created_at', 'updated_at']
|
|
|
|
|
|
class VolcAccountCreateSerializer(serializers.Serializer):
|
|
name = serializers.CharField(max_length=100, default='默认主账号')
|
|
access_key = serializers.CharField(write_only=True)
|
|
secret_key = serializers.CharField(write_only=True)
|
|
|
|
|
|
class IAMUserProjectSerializer(serializers.ModelSerializer):
|
|
class Meta:
|
|
model = IAMUserProject
|
|
fields = ['id', 'project_name', 'display_name', 'monitor_enabled',
|
|
'current_spending', 'attached_policies', 'created_at']
|
|
read_only_fields = ['current_spending', 'attached_policies', 'created_at']
|
|
|
|
|
|
class IAMUserSerializer(serializers.ModelSerializer):
|
|
remaining_quota = serializers.DecimalField(max_digits=12, decimal_places=2, read_only=True)
|
|
usage_percent = serializers.FloatField(read_only=True)
|
|
effective_alert_thresholds = serializers.SerializerMethodField()
|
|
projects = IAMUserProjectSerializer(many=True, read_only=True)
|
|
monitored_project_count = serializers.SerializerMethodField()
|
|
|
|
class Meta:
|
|
model = IAMUser
|
|
fields = [
|
|
'id', 'username', 'display_name', 'user_id', 'email', 'phone',
|
|
'status', 'access_key_ids',
|
|
'allocated_quota', 'consumed_total', 'remaining_quota', 'usage_percent',
|
|
'spending_updated_at',
|
|
'monitor_enabled', 'auto_disable_enabled',
|
|
'alert_thresholds', 'triggered_alerts',
|
|
'effective_alert_thresholds',
|
|
'projects', 'monitored_project_count',
|
|
'remark', 'created_at', 'updated_at',
|
|
]
|
|
read_only_fields = ['user_id', 'access_key_ids', 'status',
|
|
'consumed_total', 'spending_updated_at',
|
|
'triggered_alerts',
|
|
'created_at', 'updated_at']
|
|
|
|
def get_effective_alert_thresholds(self, obj):
|
|
return obj.get_alert_thresholds()
|
|
|
|
def get_monitored_project_count(self, obj):
|
|
return obj.projects.filter(monitor_enabled=True).count()
|
|
|
|
|
|
class IAMUserCreateSerializer(serializers.Serializer):
|
|
username = serializers.CharField(max_length=200)
|
|
display_name = serializers.CharField(max_length=200, required=False, default='', allow_blank=True)
|
|
email = serializers.CharField(max_length=200, required=False, default='', allow_blank=True)
|
|
phone = serializers.CharField(max_length=20, required=False, default='', allow_blank=True)
|
|
password = serializers.CharField(write_only=True, required=False, default='', allow_blank=True)
|
|
project_name = serializers.CharField(max_length=200, required=False, default='', allow_blank=True,
|
|
help_text='可选,创建后自动关联此项目')
|
|
|
|
|
|
class IAMUserImportSerializer(serializers.Serializer):
|
|
username = serializers.CharField(max_length=200, help_text='已存在的 IAM 用户名')
|
|
|
|
|
|
class IAMUserConfigSerializer(serializers.Serializer):
|
|
alert_thresholds = serializers.ListField(
|
|
child=serializers.IntegerField(min_value=1, max_value=99),
|
|
required=False,
|
|
)
|
|
monitor_enabled = serializers.BooleanField(required=False)
|
|
auto_disable_enabled = serializers.BooleanField(required=False)
|
|
|
|
|
|
class IAMUserProjectAddSerializer(serializers.Serializer):
|
|
project_name = serializers.CharField(max_length=200)
|
|
display_name = serializers.CharField(max_length=200, required=False, default='', allow_blank=True)
|
|
monitor_enabled = serializers.BooleanField(required=False, default=True)
|
|
policies = serializers.ListField(
|
|
child=serializers.CharField(max_length=200),
|
|
required=False, default=list,
|
|
help_text='要在项目范围内授权的策略名列表,如 ["ArkFullAccess"]'
|
|
)
|
|
|
|
|
|
class IAMUserProjectUpdateSerializer(serializers.Serializer):
|
|
monitor_enabled = serializers.BooleanField()
|
|
|
|
|
|
class QuotaAllocateSerializer(serializers.Serializer):
|
|
"""额度变更:正数=追加,负数=扣减"""
|
|
amount = serializers.DecimalField(max_digits=12, decimal_places=2)
|
|
note = serializers.CharField(max_length=500, required=False, default='')
|
|
|
|
def validate_amount(self, value):
|
|
from decimal import Decimal
|
|
if value == Decimal('0'):
|
|
raise serializers.ValidationError('变更金额不能为 0')
|
|
return value
|
|
|
|
|
|
class QuotaAllocationSerializer(serializers.ModelSerializer):
|
|
class Meta:
|
|
model = QuotaAllocation
|
|
fields = ['id', 'amount', 'total_after', 'note', 'created_by', 'created_at']
|
|
|
|
|
|
class GlobalConfigSerializer(serializers.ModelSerializer):
|
|
class Meta:
|
|
model = GlobalConfig
|
|
fields = [
|
|
'default_alert_thresholds',
|
|
'default_project_policies',
|
|
'monitor_interval_seconds',
|
|
'feishu_webhook_url', 'feishu_alert_mobiles',
|
|
'updated_at',
|
|
]
|
|
read_only_fields = ['updated_at']
|
|
|
|
|
|
class AlertRecordSerializer(serializers.ModelSerializer):
|
|
iam_username = serializers.CharField(source='iam_user.username', default='')
|
|
|
|
class Meta:
|
|
model = AlertRecord
|
|
fields = [
|
|
'id', 'iam_user', 'iam_username', 'alert_type', 'title', 'content',
|
|
'spending_amount', 'threshold_amount', 'notified', 'created_at',
|
|
]
|
|
|
|
|
|
class DashboardSerializer(serializers.Serializer):
|
|
total_users = serializers.IntegerField()
|
|
active_users = serializers.IntegerField()
|
|
disabled_users = serializers.IntegerField()
|
|
monitored_users = serializers.IntegerField()
|
|
total_spending = serializers.DecimalField(max_digits=12, decimal_places=2)
|
|
recent_alerts = AlertRecordSerializer(many=True)
|