seaislee1209 555c86ce76 feat: initialize AirGate - Volcengine IAM sub-account management platform
Backend (Django 4.2 + DRF):
- Admin auth with SimpleJWT
- Volcengine API client with HMAC-SHA256 signing
- IAM user management (create/sync/import/disable/enable)
- Billing query with pagination
- Feishu webhook notifications (async)
- APScheduler for periodic spending checks
- AES-256 encrypted credential storage
- API key auth for external system integration

Frontend (Vue 3 + Element Plus):
- Login page
- Dashboard with stats overview
- IAM user list with per-user threshold config
- Billing view with spending progress bars
- Alert history with type filtering
- Settings page for global config and Volcengine account management

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

118 lines
7.6 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Generated by Django 4.2.21 on 2026-03-19 04:58
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='GlobalConfig',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('default_alert_threshold', models.DecimalField(decimal_places=2, default=1000, max_digits=12, verbose_name='默认告警阈值(元)')),
('default_disable_threshold', models.DecimalField(decimal_places=2, default=5000, max_digits=12, verbose_name='默认停用阈值(元)')),
('monitor_interval_seconds', models.IntegerField(default=3600, verbose_name='监控间隔(秒)')),
('feishu_webhook_url', models.URLField(blank=True, max_length=500, verbose_name='飞书 Webhook URL')),
('feishu_alert_mobiles', models.CharField(blank=True, max_length=500, verbose_name='飞书通知手机号(逗号分隔)')),
('updated_at', models.DateTimeField(auto_now=True)),
],
options={
'verbose_name': '全局配置',
'verbose_name_plural': '全局配置',
'db_table': 'airgate_global_config',
},
),
migrations.CreateModel(
name='VolcAccount',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(default='默认主账号', max_length=100, verbose_name='账号名称')),
('access_key_enc', models.TextField(blank=True, verbose_name='AccessKey加密')),
('secret_key_enc', models.TextField(blank=True, verbose_name='SecretKey加密')),
('access_key_hint', models.CharField(blank=True, max_length=20, verbose_name='AK 提示前4后4')),
('is_active', models.BooleanField(default=True, verbose_name='启用')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
],
options={
'verbose_name': '火山主账号',
'verbose_name_plural': '火山主账号',
'db_table': 'airgate_volc_account',
},
),
migrations.CreateModel(
name='IAMUser',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('username', models.CharField(db_index=True, max_length=200, verbose_name='IAM 用户名')),
('display_name', models.CharField(blank=True, max_length=200, verbose_name='显示名')),
('user_id', models.CharField(blank=True, max_length=100, verbose_name='火山 UserID')),
('email', models.EmailField(blank=True, max_length=254, verbose_name='邮箱')),
('phone', models.CharField(blank=True, max_length=20, verbose_name='手机号')),
('project_name', models.CharField(blank=True, help_text='用于按项目维度追踪消费', max_length=200, verbose_name='关联项目名')),
('status', models.CharField(choices=[('active', '正常'), ('disabled', '已停用'), ('unknown', '未知')], default='unknown', max_length=20, verbose_name='状态')),
('access_key_ids', models.JSONField(blank=True, default=list, verbose_name='AccessKey ID 列表')),
('monitor_enabled', models.BooleanField(default=True, verbose_name='启用消费监控')),
('auto_disable_enabled', models.BooleanField(default=True, verbose_name='启用自动停用')),
('alert_threshold', models.DecimalField(blank=True, decimal_places=2, help_text='为空则使用全局默认值', max_digits=12, null=True, verbose_name='告警阈值(元)')),
('disable_threshold', models.DecimalField(blank=True, decimal_places=2, help_text='为空则使用全局默认值', max_digits=12, null=True, verbose_name='停用阈值(元)')),
('current_month_spending', models.DecimalField(decimal_places=2, default=0, max_digits=12, verbose_name='本月消费(元)')),
('spending_updated_at', models.DateTimeField(blank=True, null=True, verbose_name='消费更新时间')),
('remark', models.TextField(blank=True, verbose_name='备注')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('volc_account', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='iam_users', to='monitor.volcaccount')),
],
options={
'verbose_name': 'IAM 子账号',
'verbose_name_plural': 'IAM 子账号',
'db_table': 'airgate_iam_user',
'unique_together': {('volc_account', 'username')},
},
),
migrations.CreateModel(
name='AlertRecord',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('alert_type', models.CharField(choices=[('warning', '告警'), ('disable', '自动停用'), ('error', '错误'), ('manual', '手动操作')], max_length=20, verbose_name='告警类型')),
('title', models.CharField(max_length=200, verbose_name='标题')),
('content', models.TextField(verbose_name='详情')),
('spending_amount', models.DecimalField(blank=True, decimal_places=2, max_digits=12, null=True, verbose_name='触发时消费金额')),
('threshold_amount', models.DecimalField(blank=True, decimal_places=2, max_digits=12, null=True, verbose_name='触发阈值')),
('notified', models.BooleanField(default=False, verbose_name='已通知')),
('created_at', models.DateTimeField(auto_now_add=True)),
('iam_user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='alerts', to='monitor.iamuser')),
],
options={
'verbose_name': '告警记录',
'verbose_name_plural': '告警记录',
'db_table': 'airgate_alert_record',
'ordering': ['-created_at'],
},
),
migrations.CreateModel(
name='SpendingRecord',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('bill_period', models.CharField(db_index=True, max_length=7, verbose_name='账期 (YYYY-MM)')),
('amount', models.DecimalField(decimal_places=2, default=0, max_digits=12, verbose_name='消费金额(元)')),
('detail', models.JSONField(blank=True, default=dict, verbose_name='消费明细')),
('updated_at', models.DateTimeField(auto_now=True)),
('iam_user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='spending_records', to='monitor.iamuser')),
],
options={
'verbose_name': '消费记录',
'verbose_name_plural': '消费记录',
'db_table': 'airgate_spending_record',
'unique_together': {('iam_user', 'bill_period')},
},
),
]