AirGate/backend/apps/monitor/serializers.py
seaislee1209 5edf247a7f feat: auto-authorize policies when adding projects to sub-accounts
Project-level authorization:
- Adding a project to a sub-account now auto-calls AttachPolicyInProject
  to grant default policies (ArkFullAccess, TOSFullAccess) in that project scope
- Removing a project auto-calls DetachPolicyInProject to revoke those policies
- Each project records which policies were attached (attached_policies field)
  so removal knows exactly what to revoke

Configuration:
- GlobalConfig.default_project_policies: configurable list of policies to
  auto-attach (editable in Settings page, defaults to ArkFullAccess + TOSFullAccess)

IAM Service:
- Added attach_policy_in_project() and detach_policy_in_project() methods
  using standard AttachUserPolicy/DetachUserPolicy with ProjectName parameter

Frontend:
- Projects dialog now shows "已授权策略" column with policy tags
- Settings page has "项目默认授权策略" config field

Alert logging:
- Project add/remove operations are logged with attached/detached policy details

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 23:24:45 +08:00

140 lines
5.6 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='')
email = serializers.EmailField(required=False, default='')
phone = serializers.CharField(max_length=20, required=False, default='')
password = serializers.CharField(write_only=True, required=False, default='')
project_name = serializers.CharField(max_length=200, required=False, default='',
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='')
monitor_enabled = serializers.BooleanField(required=False, default=True)
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)