From 138a48d5827fd9cc47ab7a9f588bdc2d3a607428 Mon Sep 17 00:00:00 2001 From: ningyu Date: Fri, 26 Nov 2021 12:52:16 +0800 Subject: [PATCH 1/5] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E7=99=BB=E5=BD=95?= =?UTF-8?q?=E5=AE=A1=E8=AE=A1=E6=97=A5=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/templates/base.html | 3 ++ sql/admin.py | 10 +++- sql/audit_log.py | 72 +++++++++++++++++++++++++++++ sql/models.py | 25 ++++++++++ sql/templates/audit.html | 94 ++++++++++++++++++++++++++++++++++++++ sql/tests.py | 6 +++ sql/urls.py | 7 ++- sql/views.py | 6 +++ 8 files changed, 220 insertions(+), 3 deletions(-) create mode 100644 sql/audit_log.py create mode 100644 sql/templates/audit.html diff --git a/common/templates/base.html b/common/templates/base.html index cdb0a8f589..dc84b616bf 100644 --- a/common/templates/base.html +++ b/common/templates/base.html @@ -237,6 +237,9 @@ +
  • + 登录审计日志 +
  • diff --git a/sql/admin.py b/sql/admin.py index c0d38df333..ff73925934 100755 --- a/sql/admin.py +++ b/sql/admin.py @@ -9,7 +9,7 @@ AliyunRdsConfig, CloudAccessKey, ResourceGroup, QueryPrivilegesApply, \ QueryPrivileges, InstanceAccount, InstanceDatabase, ArchiveConfig, \ WorkflowAudit, WorkflowLog, ParamTemplate, ParamHistory, InstanceTag, \ - Tunnel + Tunnel, AuditEntry # 用户管理 @@ -244,3 +244,11 @@ class ArchiveConfigAdmin(admin.ModelAdmin): @admin.register(CloudAccessKey) class CloudAccessKeyAdmin(admin.ModelAdmin): list_display = ('type', 'key_id', 'key_secret', 'remark') + + +# 登录审计日志 +@admin.register(AuditEntry) +class AuditEntryAdmin(admin.ModelAdmin): + list_display = ('user_id', 'user_name', 'action', 'ip', 'action_time') + list_filter = ('user_id', 'user_name', 'action', 'ip') + diff --git a/sql/audit_log.py b/sql/audit_log.py new file mode 100644 index 0000000000..c2c56cfe11 --- /dev/null +++ b/sql/audit_log.py @@ -0,0 +1,72 @@ +# -*- coding: UTF-8 -*- +import logging +from django.contrib.auth.signals import user_logged_in, user_logged_out, user_login_failed +from django.dispatch import receiver +from .models import AuditEntry, Users +from django.utils import timezone +from common.utils.permission import superuser_required +from django.http import HttpResponse +import simplejson as json +from common.utils.extend_json_encoder import ExtendJSONEncoder +from django.db.models import Q + +log = logging.getLogger('default') + + +@superuser_required +def audit_log(request): + """获取登录审计日志列表""" + limit = int(request.POST.get('limit')) + offset = int(request.POST.get('offset')) + limit = offset + limit + search = request.POST.get('search', '') + + # 过滤搜索条件 + audit_log_obj = AuditEntry.objects.filter(Q(user_name__icontains=search) | Q(action__icontains=search)| Q(ip__icontains=search)) + audit_log_count = audit_log_obj.count() + audit_log_list = audit_log_obj.order_by('-action_time')[offset:limit].values("user_id", "user_name", "ip", "action", "action_time") + + # QuerySet 序列化 + rows = [row for row in audit_log_list] + + result = {"total": audit_log_count, "rows": rows} + # 返回查询结果 + return HttpResponse(json.dumps(result, cls=ExtendJSONEncoder, bigint_as_string=True), + content_type='application/json') + + +def get_client_ip(request): + x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR') + if x_forwarded_for: + ip = x_forwarded_for.split(',')[0] + else: + ip = request.META.get('REMOTE_ADDR') + return ip + + +@receiver(user_logged_in) +def user_logged_in_callback(sender, request, user, **kwargs): + ip = get_client_ip(request) + now = timezone.now() + AuditEntry.objects.create(action=u'登入', ip=ip, user_id=user.id, user_name=user.username, action_time=now) + + +@receiver(user_logged_out) +def user_logged_out_callback(sender, request, user, **kwargs): + ip = get_client_ip(request) + now = timezone.now() + AuditEntry.objects.create(action=u'登出', ip=ip, user_id=user.id, user_name=user.username, action_time=now) + + +@receiver(user_login_failed) +def user_login_failed_callback(sender, credentials, **kwargs): + now = timezone.now() + user_name = credentials.get('username', None) + user_obj = Users.objects.filter(username=user_name)[0:1] + user_count = user_obj.count() + user_id = 0 + if user_count > 0: + user_id = user_obj[0].id + AuditEntry.objects.create(action=u'登入失败', user_id=user_id, user_name=user_name + , action_time=now) + diff --git a/sql/models.py b/sql/models.py index 0389a52599..cb6efc07b4 100755 --- a/sql/models.py +++ b/sql/models.py @@ -853,3 +853,28 @@ class Meta: index_together = ('hostname_max', 'ts_min') verbose_name = u'慢日志明细' verbose_name_plural = u'慢日志明细' + + +class AuditEntry(models.Model): + """ + 登录审计日志 + """ + user_id = models.IntegerField('用户ID') + user_name = models.CharField('用户名称', max_length=255, null=True) + action = models.CharField('动作', max_length=255) + ip = models.GenericIPAddressField('IP', null=True) + action_time = models.DateTimeField('操作时间', auto_now_add=True) + + class Meta: + managed = False + db_table = 'audit_log' + verbose_name = u'审计日志' + verbose_name_plural = u'审计日志' + + def __unicode__(self): + return '{0} - {1} - {2} - {3} - {4}'.format(self.user_id, self.user_name, self.ip + , self.action, self.action_time) + + def __str__(self): + return '{0} - {1} - {2} - {3} - {4}'.format(self.user_id, self.user_name, self.ip + , self.action, self.action_time) \ No newline at end of file diff --git a/sql/templates/audit.html b/sql/templates/audit.html new file mode 100644 index 0000000000..ed96c871ea --- /dev/null +++ b/sql/templates/audit.html @@ -0,0 +1,94 @@ +{% extends "base.html" %} + +{% block content %} + +
    + +
    +
    +{% endblock content %} +{% block js %} + {% load static %} + + + +{% endblock %} diff --git a/sql/tests.py b/sql/tests.py index 054f944122..628f5443f4 100644 --- a/sql/tests.py +++ b/sql/tests.py @@ -217,6 +217,12 @@ def test_group(self): r = self.client.get(f'/group/', data=data) self.assertEqual(r.status_code, 200) + def test_audit(self): + """测试audit页面""" + data = {} + r = self.client.get(f'/audit/', data=data) + self.assertEqual(r.status_code, 200) + def test_groupmgmt(self): """测试groupmgmt页面""" data = {} diff --git a/sql/urls.py b/sql/urls.py index eaa5478894..5fe1ef7352 100644 --- a/sql/urls.py +++ b/sql/urls.py @@ -8,7 +8,7 @@ import sql.sql_optimize from common import auth, config, workflow, dashboard, check from sql import views, sql_workflow, sql_analyze, query, slowlog, instance, instance_account, db_diagnostic, \ - resource_group, binlog, data_dictionary, archiver + resource_group, binlog, data_dictionary, archiver, audit_log from sql.utils import tasks from common.utils import ding_api @@ -55,6 +55,7 @@ path('archive/', views.archive), path('archive//', views.archive_detail, name='archive_detail'), path('config/', views.config), + path('audit/', views.audit), path('authenticate/', auth.authenticate_entry), path('sqlworkflow_list/', sql_workflow.sql_workflow_list), @@ -148,5 +149,7 @@ path('archive/once/', archiver.archive_once), path('archive/log/', archiver.archive_log), - path('4admin/sync_ding_user/', ding_api.sync_ding_user) + path('4admin/sync_ding_user/', ding_api.sync_ding_user), + + path('audit/log/', audit_log.audit_log), ] diff --git a/sql/views.py b/sql/views.py index 2ab8df88bb..c3e281e1cc 100644 --- a/sql/views.py +++ b/sql/views.py @@ -426,3 +426,9 @@ def dbaprinciples(request): with open(file, 'r') as f: md = f.read().replace('\n', '\\n') return render(request, 'dbaprinciples.html', {'md': md}) + + +@superuser_required +def audit(request): + """登录审计日志页面""" + return render(request, 'audit.html') From ecd0494e1818e18d6f70b3e3dda2b7803a89f63c Mon Sep 17 00:00:00 2001 From: ningyu Date: Mon, 29 Nov 2021 11:43:05 +0800 Subject: [PATCH 2/5] =?UTF-8?q?=E6=8F=90=E4=BA=A4migration?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sql/migrations/0001_initial.py | 37 ++++++++++++++++++++++++++++++++++ sql/migrations/__init__.py | 0 sql/models.py | 2 +- 3 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 sql/migrations/0001_initial.py create mode 100644 sql/migrations/__init__.py diff --git a/sql/migrations/0001_initial.py b/sql/migrations/0001_initial.py new file mode 100644 index 0000000000..5a8575dbab --- /dev/null +++ b/sql/migrations/0001_initial.py @@ -0,0 +1,37 @@ +# Generated by Django 3.1.13 on 2021-11-29 11:35 + +import django.contrib.auth.models +import django.contrib.auth.validators +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone +import mirage.fields + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('auth', '0012_alter_user_first_name_max_length'), + ] + + operations = [ + migrations.CreateModel( + name='AuditEntry', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('user_id', models.IntegerField(verbose_name='用户ID')), + ('user_name', models.CharField(max_length=255, null=True, verbose_name='用户名称')), + ('action', models.CharField(max_length=255, verbose_name='动作')), + ('ip', models.GenericIPAddressField(null=True, verbose_name='IP')), + ('action_time', models.DateTimeField(auto_now_add=True, verbose_name='操作时间')), + ], + options={ + 'verbose_name': '审计日志', + 'verbose_name_plural': '审计日志', + 'db_table': 'audit_log', + 'managed': True, + }, + ), + ] diff --git a/sql/migrations/__init__.py b/sql/migrations/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/sql/models.py b/sql/models.py index cb6efc07b4..e4ce26b05b 100755 --- a/sql/models.py +++ b/sql/models.py @@ -866,7 +866,7 @@ class AuditEntry(models.Model): action_time = models.DateTimeField('操作时间', auto_now_add=True) class Meta: - managed = False + managed = True db_table = 'audit_log' verbose_name = u'审计日志' verbose_name_plural = u'审计日志' From d21f9798e983845888573de5dbbc010d69bf53ee Mon Sep 17 00:00:00 2001 From: ningyu Date: Mon, 29 Nov 2021 12:15:29 +0800 Subject: [PATCH 3/5] =?UTF-8?q?=E6=8F=90=E4=BA=A4migration?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sql/migrations/0001_initial.py | 709 +++++++++++++++++++++++++++++++++ 1 file changed, 709 insertions(+) diff --git a/sql/migrations/0001_initial.py b/sql/migrations/0001_initial.py index 5a8575dbab..5a301af7c1 100644 --- a/sql/migrations/0001_initial.py +++ b/sql/migrations/0001_initial.py @@ -17,6 +17,161 @@ class Migration(migrations.Migration): ] operations = [ + migrations.CreateModel( + name='SlowQuery', + fields=[ + ('checksum', models.CharField(max_length=32, primary_key=True, serialize=False)), + ('fingerprint', models.TextField()), + ('sample', models.TextField()), + ('first_seen', models.DateTimeField(blank=True, null=True)), + ('last_seen', models.DateTimeField(blank=True, db_index=True, null=True)), + ('reviewed_by', models.CharField(blank=True, max_length=20, null=True)), + ('reviewed_on', models.DateTimeField(blank=True, null=True)), + ('comments', models.TextField(blank=True, null=True)), + ], + options={ + 'verbose_name': '慢日志统计', + 'verbose_name_plural': '慢日志统计', + 'db_table': 'mysql_slow_query_review', + 'managed': False, + }, + ), + migrations.CreateModel( + name='SlowQueryHistory', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('hostname_max', models.CharField(max_length=64)), + ('client_max', models.CharField(max_length=64, null=True)), + ('user_max', models.CharField(max_length=64)), + ('db_max', models.CharField(default=None, max_length=64, null=True)), + ('bytes_max', models.CharField(max_length=64, null=True)), + ('sample', models.TextField()), + ('ts_min', models.DateTimeField(db_index=True)), + ('ts_max', models.DateTimeField()), + ('ts_cnt', models.FloatField(blank=True, null=True)), + ('query_time_sum', models.FloatField(blank=True, db_column='Query_time_sum', null=True)), + ('query_time_min', models.FloatField(blank=True, db_column='Query_time_min', null=True)), + ('query_time_max', models.FloatField(blank=True, db_column='Query_time_max', null=True)), + ('query_time_pct_95', models.FloatField(blank=True, db_column='Query_time_pct_95', null=True)), + ('query_time_stddev', models.FloatField(blank=True, db_column='Query_time_stddev', null=True)), + ('query_time_median', models.FloatField(blank=True, db_column='Query_time_median', null=True)), + ('lock_time_sum', models.FloatField(blank=True, db_column='Lock_time_sum', null=True)), + ('lock_time_min', models.FloatField(blank=True, db_column='Lock_time_min', null=True)), + ('lock_time_max', models.FloatField(blank=True, db_column='Lock_time_max', null=True)), + ('lock_time_pct_95', models.FloatField(blank=True, db_column='Lock_time_pct_95', null=True)), + ('lock_time_stddev', models.FloatField(blank=True, db_column='Lock_time_stddev', null=True)), + ('lock_time_median', models.FloatField(blank=True, db_column='Lock_time_median', null=True)), + ('rows_sent_sum', models.FloatField(blank=True, db_column='Rows_sent_sum', null=True)), + ('rows_sent_min', models.FloatField(blank=True, db_column='Rows_sent_min', null=True)), + ('rows_sent_max', models.FloatField(blank=True, db_column='Rows_sent_max', null=True)), + ('rows_sent_pct_95', models.FloatField(blank=True, db_column='Rows_sent_pct_95', null=True)), + ('rows_sent_stddev', models.FloatField(blank=True, db_column='Rows_sent_stddev', null=True)), + ('rows_sent_median', models.FloatField(blank=True, db_column='Rows_sent_median', null=True)), + ('rows_examined_sum', models.FloatField(blank=True, db_column='Rows_examined_sum', null=True)), + ('rows_examined_min', models.FloatField(blank=True, db_column='Rows_examined_min', null=True)), + ('rows_examined_max', models.FloatField(blank=True, db_column='Rows_examined_max', null=True)), + ('rows_examined_pct_95', models.FloatField(blank=True, db_column='Rows_examined_pct_95', null=True)), + ('rows_examined_stddev', models.FloatField(blank=True, db_column='Rows_examined_stddev', null=True)), + ('rows_examined_median', models.FloatField(blank=True, db_column='Rows_examined_median', null=True)), + ('rows_affected_sum', models.FloatField(blank=True, db_column='Rows_affected_sum', null=True)), + ('rows_affected_min', models.FloatField(blank=True, db_column='Rows_affected_min', null=True)), + ('rows_affected_max', models.FloatField(blank=True, db_column='Rows_affected_max', null=True)), + ('rows_affected_pct_95', models.FloatField(blank=True, db_column='Rows_affected_pct_95', null=True)), + ('rows_affected_stddev', models.FloatField(blank=True, db_column='Rows_affected_stddev', null=True)), + ('rows_affected_median', models.FloatField(blank=True, db_column='Rows_affected_median', null=True)), + ('rows_read_sum', models.FloatField(blank=True, db_column='Rows_read_sum', null=True)), + ('rows_read_min', models.FloatField(blank=True, db_column='Rows_read_min', null=True)), + ('rows_read_max', models.FloatField(blank=True, db_column='Rows_read_max', null=True)), + ('rows_read_pct_95', models.FloatField(blank=True, db_column='Rows_read_pct_95', null=True)), + ('rows_read_stddev', models.FloatField(blank=True, db_column='Rows_read_stddev', null=True)), + ('rows_read_median', models.FloatField(blank=True, db_column='Rows_read_median', null=True)), + ('merge_passes_sum', models.FloatField(blank=True, db_column='Merge_passes_sum', null=True)), + ('merge_passes_min', models.FloatField(blank=True, db_column='Merge_passes_min', null=True)), + ('merge_passes_max', models.FloatField(blank=True, db_column='Merge_passes_max', null=True)), + ('merge_passes_pct_95', models.FloatField(blank=True, db_column='Merge_passes_pct_95', null=True)), + ('merge_passes_stddev', models.FloatField(blank=True, db_column='Merge_passes_stddev', null=True)), + ('merge_passes_median', models.FloatField(blank=True, db_column='Merge_passes_median', null=True)), + ('innodb_io_r_ops_min', models.FloatField(blank=True, db_column='InnoDB_IO_r_ops_min', null=True)), + ('innodb_io_r_ops_max', models.FloatField(blank=True, db_column='InnoDB_IO_r_ops_max', null=True)), + ('innodb_io_r_ops_pct_95', models.FloatField(blank=True, db_column='InnoDB_IO_r_ops_pct_95', null=True)), + ('innodb_io_r_ops_stddev', models.FloatField(blank=True, db_column='InnoDB_IO_r_ops_stddev', null=True)), + ('innodb_io_r_ops_median', models.FloatField(blank=True, db_column='InnoDB_IO_r_ops_median', null=True)), + ('innodb_io_r_bytes_min', models.FloatField(blank=True, db_column='InnoDB_IO_r_bytes_min', null=True)), + ('innodb_io_r_bytes_max', models.FloatField(blank=True, db_column='InnoDB_IO_r_bytes_max', null=True)), + ('innodb_io_r_bytes_pct_95', models.FloatField(blank=True, db_column='InnoDB_IO_r_bytes_pct_95', null=True)), + ('innodb_io_r_bytes_stddev', models.FloatField(blank=True, db_column='InnoDB_IO_r_bytes_stddev', null=True)), + ('innodb_io_r_bytes_median', models.FloatField(blank=True, db_column='InnoDB_IO_r_bytes_median', null=True)), + ('innodb_io_r_wait_min', models.FloatField(blank=True, db_column='InnoDB_IO_r_wait_min', null=True)), + ('innodb_io_r_wait_max', models.FloatField(blank=True, db_column='InnoDB_IO_r_wait_max', null=True)), + ('innodb_io_r_wait_pct_95', models.FloatField(blank=True, db_column='InnoDB_IO_r_wait_pct_95', null=True)), + ('innodb_io_r_wait_stddev', models.FloatField(blank=True, db_column='InnoDB_IO_r_wait_stddev', null=True)), + ('innodb_io_r_wait_median', models.FloatField(blank=True, db_column='InnoDB_IO_r_wait_median', null=True)), + ('innodb_rec_lock_wait_min', models.FloatField(blank=True, db_column='InnoDB_rec_lock_wait_min', null=True)), + ('innodb_rec_lock_wait_max', models.FloatField(blank=True, db_column='InnoDB_rec_lock_wait_max', null=True)), + ('innodb_rec_lock_wait_pct_95', models.FloatField(blank=True, db_column='InnoDB_rec_lock_wait_pct_95', null=True)), + ('innodb_rec_lock_wait_stddev', models.FloatField(blank=True, db_column='InnoDB_rec_lock_wait_stddev', null=True)), + ('innodb_rec_lock_wait_median', models.FloatField(blank=True, db_column='InnoDB_rec_lock_wait_median', null=True)), + ('innodb_queue_wait_min', models.FloatField(blank=True, db_column='InnoDB_queue_wait_min', null=True)), + ('innodb_queue_wait_max', models.FloatField(blank=True, db_column='InnoDB_queue_wait_max', null=True)), + ('innodb_queue_wait_pct_95', models.FloatField(blank=True, db_column='InnoDB_queue_wait_pct_95', null=True)), + ('innodb_queue_wait_stddev', models.FloatField(blank=True, db_column='InnoDB_queue_wait_stddev', null=True)), + ('innodb_queue_wait_median', models.FloatField(blank=True, db_column='InnoDB_queue_wait_median', null=True)), + ('innodb_pages_distinct_min', models.FloatField(blank=True, db_column='InnoDB_pages_distinct_min', null=True)), + ('innodb_pages_distinct_max', models.FloatField(blank=True, db_column='InnoDB_pages_distinct_max', null=True)), + ('innodb_pages_distinct_pct_95', models.FloatField(blank=True, db_column='InnoDB_pages_distinct_pct_95', null=True)), + ('innodb_pages_distinct_stddev', models.FloatField(blank=True, db_column='InnoDB_pages_distinct_stddev', null=True)), + ('innodb_pages_distinct_median', models.FloatField(blank=True, db_column='InnoDB_pages_distinct_median', null=True)), + ('qc_hit_cnt', models.FloatField(blank=True, db_column='QC_Hit_cnt', null=True)), + ('qc_hit_sum', models.FloatField(blank=True, db_column='QC_Hit_sum', null=True)), + ('full_scan_cnt', models.FloatField(blank=True, db_column='Full_scan_cnt', null=True)), + ('full_scan_sum', models.FloatField(blank=True, db_column='Full_scan_sum', null=True)), + ('full_join_cnt', models.FloatField(blank=True, db_column='Full_join_cnt', null=True)), + ('full_join_sum', models.FloatField(blank=True, db_column='Full_join_sum', null=True)), + ('tmp_table_cnt', models.FloatField(blank=True, db_column='Tmp_table_cnt', null=True)), + ('tmp_table_sum', models.FloatField(blank=True, db_column='Tmp_table_sum', null=True)), + ('tmp_table_on_disk_cnt', models.FloatField(blank=True, db_column='Tmp_table_on_disk_cnt', null=True)), + ('tmp_table_on_disk_sum', models.FloatField(blank=True, db_column='Tmp_table_on_disk_sum', null=True)), + ('filesort_cnt', models.FloatField(blank=True, db_column='Filesort_cnt', null=True)), + ('filesort_sum', models.FloatField(blank=True, db_column='Filesort_sum', null=True)), + ('filesort_on_disk_cnt', models.FloatField(blank=True, db_column='Filesort_on_disk_cnt', null=True)), + ('filesort_on_disk_sum', models.FloatField(blank=True, db_column='Filesort_on_disk_sum', null=True)), + ], + options={ + 'verbose_name': '慢日志明细', + 'verbose_name_plural': '慢日志明细', + 'db_table': 'mysql_slow_query_review_history', + 'managed': False, + }, + ), + migrations.CreateModel( + name='ArchiveConfig', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=50, verbose_name='归档配置说明')), + ('audit_auth_groups', models.CharField(blank=True, max_length=255, verbose_name='审批权限组列表')), + ('src_db_name', models.CharField(max_length=64, verbose_name='源数据库')), + ('src_table_name', models.CharField(max_length=64, verbose_name='源表')), + ('dest_db_name', models.CharField(blank=True, max_length=64, null=True, verbose_name='目标数据库')), + ('dest_table_name', models.CharField(blank=True, max_length=64, null=True, verbose_name='目标表')), + ('condition', models.CharField(max_length=1000, verbose_name='归档条件,where条件')), + ('mode', models.CharField(choices=[('file', '文件'), ('dest', '其他实例'), ('purge', '直接删除')], max_length=10, verbose_name='归档模式')), + ('no_delete', models.BooleanField(verbose_name='是否保留源数据')), + ('sleep', models.IntegerField(default=1, verbose_name='归档limit行后的休眠秒数')), + ('status', models.IntegerField(blank=True, choices=[(0, '待审核'), (1, '审核通过'), (2, '审核不通过'), (3, '审核取消')], default=1, verbose_name='审核状态')), + ('state', models.BooleanField(default=True, verbose_name='是否启用归档')), + ('user_name', models.CharField(blank=True, default='', max_length=30, verbose_name='申请人')), + ('user_display', models.CharField(blank=True, default='', max_length=50, verbose_name='申请人中文名')), + ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), + ('last_archive_time', models.DateTimeField(blank=True, null=True, verbose_name='最近归档时间')), + ('sys_time', models.DateTimeField(auto_now=True, verbose_name='系统时间修改')), + ], + options={ + 'verbose_name': '归档配置表', + 'verbose_name_plural': '归档配置表', + 'db_table': 'archive_config', + 'managed': True, + }, + ), migrations.CreateModel( name='AuditEntry', fields=[ @@ -34,4 +189,558 @@ class Migration(migrations.Migration): 'managed': True, }, ), + migrations.CreateModel( + name='CloudAccessKey', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('type', models.CharField(choices=[('aliyun', 'aliyun')], default='', max_length=20)), + ('key_id', models.CharField(max_length=200)), + ('key_secret', models.CharField(max_length=200)), + ('remark', models.CharField(blank=True, default='', max_length=50)), + ], + options={ + 'verbose_name': '云服务认证信息配置', + 'verbose_name_plural': '云服务认证信息配置', + 'db_table': 'cloud_access_key', + 'managed': True, + }, + ), + migrations.CreateModel( + name='Config', + fields=[ + ('item', models.CharField(max_length=200, primary_key=True, serialize=False, verbose_name='配置项')), + ('value', mirage.fields.EncryptedCharField(max_length=500, verbose_name='配置项值')), + ('description', models.CharField(blank=True, default='', max_length=200, verbose_name='描述')), + ], + options={ + 'verbose_name': '系统配置', + 'verbose_name_plural': '系统配置', + 'db_table': 'sql_config', + 'managed': True, + }, + ), + migrations.CreateModel( + name='DataMaskingRules', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('rule_type', models.IntegerField(choices=[(1, '手机号'), (2, '证件号码'), (3, '银行卡'), (4, '邮箱'), (5, '金额'), (6, '其他')], unique=True, verbose_name='规则类型')), + ('rule_regex', models.CharField(max_length=255, verbose_name='规则脱敏所用的正则表达式,表达式必须分组,隐藏的组会使用****代替')), + ('hide_group', models.IntegerField(verbose_name='需要隐藏的组')), + ('rule_desc', models.CharField(blank=True, default='', max_length=100, verbose_name='规则描述')), + ('sys_time', models.DateTimeField(auto_now=True)), + ], + options={ + 'verbose_name': '脱敏规则配置', + 'verbose_name_plural': '脱敏规则配置', + 'db_table': 'data_masking_rules', + 'managed': True, + }, + ), + migrations.CreateModel( + name='Instance', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('instance_name', models.CharField(max_length=50, unique=True, verbose_name='实例名称')), + ('type', models.CharField(choices=[('master', '主库'), ('slave', '从库')], max_length=6, verbose_name='实例类型')), + ('db_type', models.CharField(choices=[('mysql', 'MySQL'), ('mssql', 'MsSQL'), ('redis', 'Redis'), ('pgsql', 'PgSQL'), ('oracle', 'Oracle'), ('mongo', 'Mongo'), ('phoenix', 'Phoenix'), ('inception', 'Inception'), ('goinception', 'goInception')], max_length=20, verbose_name='数据库类型')), + ('host', models.CharField(max_length=200, verbose_name='实例连接')), + ('port', models.IntegerField(default=0, verbose_name='端口')), + ('user', mirage.fields.EncryptedCharField(blank=True, default='', max_length=200, verbose_name='用户名')), + ('password', mirage.fields.EncryptedCharField(blank=True, default='', max_length=300, verbose_name='密码')), + ('db_name', models.CharField(blank=True, default='', max_length=64, verbose_name='数据库')), + ('charset', models.CharField(blank=True, default='', max_length=20, verbose_name='字符集')), + ('service_name', models.CharField(blank=True, max_length=50, null=True, verbose_name='Oracle service name')), + ('sid', models.CharField(blank=True, max_length=50, null=True, verbose_name='Oracle sid')), + ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), + ('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')), + ], + options={ + 'verbose_name': '实例配置', + 'verbose_name_plural': '实例配置', + 'db_table': 'sql_instance', + 'managed': True, + }, + ), + migrations.CreateModel( + name='InstanceTag', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('tag_code', models.CharField(max_length=20, unique=True, verbose_name='标签代码')), + ('tag_name', models.CharField(max_length=20, unique=True, verbose_name='标签名称')), + ('active', models.BooleanField(default=True, verbose_name='激活状态')), + ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), + ], + options={ + 'verbose_name': '实例标签', + 'verbose_name_plural': '实例标签', + 'db_table': 'sql_instance_tag', + 'managed': True, + }, + ), + migrations.CreateModel( + name='Permission', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ], + options={ + 'permissions': (('menu_dashboard', '菜单 Dashboard'), ('menu_sqlcheck', '菜单 SQL审核'), ('menu_sqlworkflow', '菜单 SQL上线'), ('menu_sqlanalyze', '菜单 SQL分析'), ('menu_query', '菜单 SQL查询'), ('menu_sqlquery', '菜单 在线查询'), ('menu_queryapplylist', '菜单 权限管理'), ('menu_sqloptimize', '菜单 SQL优化'), ('menu_sqladvisor', '菜单 优化工具'), ('menu_slowquery', '菜单 慢查日志'), ('menu_instance', '菜单 实例管理'), ('menu_instance_list', '菜单 实例列表'), ('menu_dbdiagnostic', '菜单 会话管理'), ('menu_database', '菜单 数据库管理'), ('menu_instance_account', '菜单 实例账号管理'), ('menu_param', '菜单 参数配置'), ('menu_data_dictionary', '菜单 数据字典'), ('menu_tools', '菜单 工具插件'), ('menu_archive', '菜单 数据归档'), ('menu_binlog2sql', '菜单 Binlog2SQL'), ('menu_schemasync', '菜单 SchemaSync'), ('menu_system', '菜单 系统管理'), ('menu_document', '菜单 相关文档'), ('sql_submit', '提交SQL上线工单'), ('sql_review', '审核SQL上线工单'), ('sql_execute_for_resource_group', '执行SQL上线工单(资源组粒度)'), ('sql_execute', '执行SQL上线工单(仅自己提交的)'), ('sql_analyze', '执行SQL分析'), ('optimize_sqladvisor', '执行SQLAdvisor'), ('optimize_sqltuning', '执行SQLTuning'), ('optimize_soar', '执行SOAR'), ('query_applypriv', '申请查询权限'), ('query_mgtpriv', '管理查询权限'), ('query_review', '审核查询权限'), ('query_submit', '提交SQL查询'), ('query_all_instances', '可查询所有实例'), ('query_resource_group_instance', '可查询所在资源组内的所有实例'), ('process_view', '查看会话'), ('process_kill', '终止会话'), ('tablespace_view', '查看表空间'), ('trx_view', '查看事务信息'), ('trxandlocks_view', '查看锁信息'), ('instance_account_manage', '管理实例账号'), ('param_view', '查看实例参数列表'), ('param_edit', '修改实例参数'), ('data_dictionary_export', '导出数据字典'), ('archive_apply', '提交归档申请'), ('archive_review', '审核归档申请'), ('archive_mgt', '管理归档申请')), + 'managed': True, + }, + ), + migrations.CreateModel( + name='QueryLog', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('instance_name', models.CharField(max_length=50, verbose_name='实例名称')), + ('db_name', models.CharField(max_length=64, verbose_name='数据库名称')), + ('sqllog', models.TextField(verbose_name='执行的查询语句')), + ('effect_row', models.BigIntegerField(verbose_name='返回行数')), + ('cost_time', models.CharField(default='', max_length=10, verbose_name='执行耗时')), + ('username', models.CharField(max_length=30, verbose_name='操作人')), + ('user_display', models.CharField(default='', max_length=50, verbose_name='操作人中文名')), + ('priv_check', models.BooleanField(choices=[(False, '跳过'), (True, '正常')], default=False, verbose_name='查询权限是否正常校验')), + ('hit_rule', models.BooleanField(choices=[(False, '未命中/未知'), (True, '命中')], default=False, verbose_name='查询是否命中脱敏规则')), + ('masking', models.BooleanField(choices=[(False, '否'), (True, '是')], default=False, verbose_name='查询结果是否正常脱敏')), + ('favorite', models.BooleanField(choices=[(False, '否'), (True, '是')], default=False, verbose_name='是否收藏')), + ('alias', models.CharField(blank=True, default='', max_length=64, verbose_name='语句标识')), + ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='操作时间')), + ('sys_time', models.DateTimeField(auto_now=True)), + ], + options={ + 'verbose_name': '查询日志', + 'verbose_name_plural': '查询日志', + 'db_table': 'query_log', + 'managed': True, + }, + ), + migrations.CreateModel( + name='ResourceGroup', + fields=[ + ('group_id', models.AutoField(primary_key=True, serialize=False, verbose_name='组ID')), + ('group_name', models.CharField(max_length=100, unique=True, verbose_name='组名称')), + ('group_parent_id', models.BigIntegerField(default=0, verbose_name='父级id')), + ('group_sort', models.IntegerField(default=1, verbose_name='排序')), + ('group_level', models.IntegerField(default=1, verbose_name='层级')), + ('ding_webhook', models.CharField(blank=True, max_length=255, verbose_name='钉钉webhook地址')), + ('feishu_webhook', models.CharField(blank=True, max_length=255, verbose_name='飞书webhook地址')), + ('qywx_webhook', models.CharField(blank=True, max_length=255, verbose_name='企业微信webhook地址')), + ('is_deleted', models.IntegerField(choices=[(0, '否'), (1, '是')], default=0, verbose_name='是否删除')), + ('create_time', models.DateTimeField(auto_now_add=True)), + ('sys_time', models.DateTimeField(auto_now=True)), + ], + options={ + 'verbose_name': '资源组管理', + 'verbose_name_plural': '资源组管理', + 'db_table': 'resource_group', + 'managed': True, + }, + ), + migrations.CreateModel( + name='SqlWorkflow', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('workflow_name', models.CharField(max_length=50, verbose_name='工单内容')), + ('demand_url', models.CharField(max_length=500, verbose_name='需求链接')), + ('group_id', models.IntegerField(verbose_name='组ID')), + ('group_name', models.CharField(max_length=100, verbose_name='组名称')), + ('db_name', models.CharField(max_length=64, verbose_name='数据库')), + ('syntax_type', models.IntegerField(choices=[(0, '其他'), (1, 'DDL'), (2, 'DML')], default=0, verbose_name='工单类型 0、未知,1、DDL,2、DML')), + ('is_backup', models.BooleanField(choices=[(False, '否'), (True, '是')], default=True, verbose_name='是否备份')), + ('engineer', models.CharField(max_length=30, verbose_name='发起人')), + ('engineer_display', models.CharField(default='', max_length=50, verbose_name='发起人中文名')), + ('status', models.CharField(choices=[('workflow_finish', '已正常结束'), ('workflow_abort', '人工终止流程'), ('workflow_manreviewing', '等待审核人审核'), ('workflow_review_pass', '审核通过'), ('workflow_timingtask', '定时执行'), ('workflow_queuing', '排队中'), ('workflow_executing', '执行中'), ('workflow_autoreviewwrong', '自动审核不通过'), ('workflow_exception', '执行有异常')], max_length=50)), + ('audit_auth_groups', models.CharField(max_length=255, verbose_name='审批权限组列表')), + ('run_date_start', models.DateTimeField(blank=True, null=True, verbose_name='可执行起始时间')), + ('run_date_end', models.DateTimeField(blank=True, null=True, verbose_name='可执行结束时间')), + ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), + ('finish_time', models.DateTimeField(blank=True, null=True, verbose_name='结束时间')), + ('is_manual', models.IntegerField(choices=[(0, '否'), (1, '是')], default=0, verbose_name='是否原生执行')), + ('instance', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='sql.instance')), + ], + options={ + 'verbose_name': 'SQL工单', + 'verbose_name_plural': 'SQL工单', + 'db_table': 'sql_workflow', + 'managed': True, + }, + ), + migrations.CreateModel( + name='Tunnel', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('tunnel_name', models.CharField(max_length=50, unique=True, verbose_name='隧道名称')), + ('host', models.CharField(max_length=200, verbose_name='隧道连接')), + ('port', models.IntegerField(default=0, verbose_name='端口')), + ('user', mirage.fields.EncryptedCharField(blank=True, default='', max_length=200, null=True, verbose_name='用户名')), + ('password', mirage.fields.EncryptedCharField(blank=True, default='', max_length=300, null=True, verbose_name='密码')), + ('pkey_path', mirage.fields.EncryptedCharField(blank=True, default='', max_length=300, null=True, verbose_name='密钥地址')), + ('pkey_password', mirage.fields.EncryptedCharField(blank=True, default='', max_length=300, null=True, verbose_name='密钥密码')), + ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), + ('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')), + ], + options={ + 'verbose_name': '隧道配置', + 'verbose_name_plural': '隧道配置', + 'db_table': 'ssh_tunnel', + 'managed': True, + }, + ), + migrations.CreateModel( + name='WorkflowAuditDetail', + fields=[ + ('audit_detail_id', models.AutoField(primary_key=True, serialize=False)), + ('audit_id', models.IntegerField(verbose_name='审核主表id')), + ('audit_user', models.CharField(max_length=30, verbose_name='审核人')), + ('audit_time', models.DateTimeField(verbose_name='审核时间')), + ('audit_status', models.IntegerField(choices=[(0, '待审核'), (1, '审核通过'), (2, '审核不通过'), (3, '审核取消')], verbose_name='审核状态')), + ('remark', models.CharField(default='', max_length=1000, verbose_name='审核备注')), + ('sys_time', models.DateTimeField(auto_now=True, verbose_name='系统时间')), + ], + options={ + 'verbose_name': '工作流审批明细', + 'verbose_name_plural': '工作流审批明细', + 'db_table': 'workflow_audit_detail', + 'managed': True, + }, + ), + migrations.CreateModel( + name='WorkflowLog', + fields=[ + ('id', models.AutoField(primary_key=True, serialize=False)), + ('audit_id', models.IntegerField(db_index=True, verbose_name='工单审批id')), + ('operation_type', models.SmallIntegerField(choices=[(0, '提交/待审核'), (1, '审核通过'), (2, '审核不通过'), (3, '审核取消'), (4, '定时执行'), (5, '执行工单'), (6, '执行结束')], verbose_name='操作类型')), + ('operation_type_desc', models.CharField(max_length=10, verbose_name='操作类型描述')), + ('operation_info', models.CharField(max_length=1000, verbose_name='操作信息')), + ('operator', models.CharField(max_length=30, verbose_name='操作人')), + ('operator_display', models.CharField(default='', max_length=50, verbose_name='操作人中文名')), + ('operation_time', models.DateTimeField(auto_now_add=True)), + ], + options={ + 'verbose_name': '工作流日志', + 'verbose_name_plural': '工作流日志', + 'db_table': 'workflow_log', + 'managed': True, + }, + ), + migrations.CreateModel( + name='WorkflowAuditSetting', + fields=[ + ('audit_setting_id', models.AutoField(primary_key=True, serialize=False)), + ('group_id', models.IntegerField(verbose_name='组ID')), + ('group_name', models.CharField(max_length=100, verbose_name='组名称')), + ('workflow_type', models.IntegerField(choices=[(1, 'sql_query'), (2, 'sql_review')], verbose_name='审批类型')), + ('audit_auth_groups', models.CharField(max_length=255, verbose_name='审批权限组列表')), + ('create_time', models.DateTimeField(auto_now_add=True)), + ('sys_time', models.DateTimeField(auto_now=True)), + ], + options={ + 'verbose_name': '审批流程配置', + 'verbose_name_plural': '审批流程配置', + 'db_table': 'workflow_audit_setting', + 'managed': True, + 'unique_together': {('group_id', 'workflow_type')}, + }, + ), + migrations.CreateModel( + name='WorkflowAudit', + fields=[ + ('audit_id', models.AutoField(primary_key=True, serialize=False)), + ('group_id', models.IntegerField(verbose_name='组ID')), + ('group_name', models.CharField(max_length=100, verbose_name='组名称')), + ('workflow_id', models.BigIntegerField(verbose_name='关联业务id')), + ('workflow_type', models.IntegerField(choices=[(1, 'sql_query'), (2, 'sql_review')], verbose_name='申请类型')), + ('workflow_title', models.CharField(max_length=50, verbose_name='申请标题')), + ('workflow_remark', models.CharField(blank=True, default='', max_length=140, verbose_name='申请备注')), + ('audit_auth_groups', models.CharField(max_length=255, verbose_name='审批权限组列表')), + ('current_audit', models.CharField(max_length=20, verbose_name='当前审批权限组')), + ('next_audit', models.CharField(max_length=20, verbose_name='下级审批权限组')), + ('current_status', models.IntegerField(choices=[(0, '待审核'), (1, '审核通过'), (2, '审核不通过'), (3, '审核取消')], verbose_name='审核状态')), + ('create_user', models.CharField(max_length=30, verbose_name='申请人')), + ('create_user_display', models.CharField(default='', max_length=50, verbose_name='申请人中文名')), + ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='申请时间')), + ('sys_time', models.DateTimeField(auto_now=True, verbose_name='系统时间')), + ], + options={ + 'verbose_name': '工作流审批列表', + 'verbose_name_plural': '工作流审批列表', + 'db_table': 'workflow_audit', + 'managed': True, + 'unique_together': {('workflow_id', 'workflow_type')}, + }, + ), + migrations.CreateModel( + name='SqlWorkflowContent', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('sql_content', models.TextField(verbose_name='具体sql内容')), + ('review_content', models.TextField(verbose_name='自动审核内容的JSON格式')), + ('execute_result', models.TextField(blank=True, verbose_name='执行结果的JSON格式')), + ('workflow', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='sql.sqlworkflow')), + ], + options={ + 'verbose_name': 'SQL工单内容', + 'verbose_name_plural': 'SQL工单内容', + 'db_table': 'sql_workflow_content', + 'managed': True, + }, + ), + migrations.CreateModel( + name='QueryPrivilegesApply', + fields=[ + ('apply_id', models.AutoField(primary_key=True, serialize=False)), + ('group_id', models.IntegerField(verbose_name='组ID')), + ('group_name', models.CharField(max_length=100, verbose_name='组名称')), + ('title', models.CharField(max_length=50, verbose_name='申请标题')), + ('user_name', models.CharField(max_length=30, verbose_name='申请人')), + ('user_display', models.CharField(default='', max_length=50, verbose_name='申请人中文名')), + ('db_list', models.TextField(default='', verbose_name='数据库')), + ('table_list', models.TextField(default='', verbose_name='表')), + ('valid_date', models.DateField(verbose_name='有效时间')), + ('limit_num', models.IntegerField(default=100, verbose_name='行数限制')), + ('priv_type', models.IntegerField(choices=[(1, 'DATABASE'), (2, 'TABLE')], default=0, verbose_name='权限类型')), + ('status', models.IntegerField(choices=[(0, '待审核'), (1, '审核通过'), (2, '审核不通过'), (3, '审核取消')], verbose_name='审核状态')), + ('audit_auth_groups', models.CharField(max_length=255, verbose_name='审批权限组列表')), + ('create_time', models.DateTimeField(auto_now_add=True)), + ('sys_time', models.DateTimeField(auto_now=True)), + ('instance', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='sql.instance')), + ], + options={ + 'verbose_name': '查询权限申请记录表', + 'verbose_name_plural': '查询权限申请记录表', + 'db_table': 'query_privileges_apply', + 'managed': True, + }, + ), + migrations.CreateModel( + name='ParamTemplate', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('db_type', models.CharField(choices=[('mysql', 'MySQL'), ('mssql', 'MsSQL'), ('redis', 'Redis'), ('pgsql', 'PgSQL'), ('oracle', 'Oracle'), ('mongo', 'Mongo'), ('phoenix', 'Phoenix'), ('inception', 'Inception'), ('goinception', 'goInception')], max_length=20, verbose_name='数据库类型')), + ('variable_name', models.CharField(max_length=64, verbose_name='参数名')), + ('default_value', models.CharField(max_length=1024, verbose_name='默认参数值')), + ('editable', models.BooleanField(default=False, verbose_name='是否支持修改')), + ('valid_values', models.CharField(blank=True, max_length=1024, verbose_name='有效参数值,范围参数[1-65535],值参数[ON|OFF]')), + ('description', models.CharField(blank=True, max_length=1024, verbose_name='参数描述')), + ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), + ('sys_time', models.DateTimeField(auto_now=True, verbose_name='系统时间修改')), + ], + options={ + 'verbose_name': '实例参数模板配置', + 'verbose_name_plural': '实例参数模板配置', + 'db_table': 'param_template', + 'managed': True, + 'unique_together': {('db_type', 'variable_name')}, + }, + ), + migrations.CreateModel( + name='ParamHistory', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('variable_name', models.CharField(max_length=64, verbose_name='参数名')), + ('old_var', models.CharField(max_length=1024, verbose_name='修改前参数值')), + ('new_var', models.CharField(max_length=1024, verbose_name='修改后参数值')), + ('set_sql', models.CharField(max_length=1024, verbose_name='在线变更配置执行的SQL语句')), + ('user_name', models.CharField(max_length=30, verbose_name='修改人')), + ('user_display', models.CharField(max_length=50, verbose_name='修改人中文名')), + ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='参数被修改时间点')), + ('instance', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='sql.instance')), + ], + options={ + 'verbose_name': '实例参数修改历史', + 'verbose_name_plural': '实例参数修改历史', + 'db_table': 'param_history', + 'ordering': ['-create_time'], + 'managed': True, + }, + ), + migrations.AddField( + model_name='instance', + name='instance_tag', + field=models.ManyToManyField(blank=True, to='sql.InstanceTag', verbose_name='实例标签'), + ), + migrations.AddField( + model_name='instance', + name='resource_group', + field=models.ManyToManyField(blank=True, to='sql.ResourceGroup', verbose_name='资源组'), + ), + migrations.AddField( + model_name='instance', + name='tunnel', + field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.CASCADE, to='sql.tunnel', verbose_name='连接隧道'), + ), + migrations.CreateModel( + name='DataMaskingColumns', + fields=[ + ('column_id', models.AutoField(primary_key=True, serialize=False, verbose_name='字段id')), + ('rule_type', models.IntegerField(choices=[(1, '手机号'), (2, '证件号码'), (3, '银行卡'), (4, '邮箱'), (5, '金额'), (6, '其他')], verbose_name='规则类型')), + ('active', models.BooleanField(choices=[(False, '未激活'), (True, '激活')], verbose_name='激活状态')), + ('table_schema', models.CharField(max_length=64, verbose_name='字段所在库名')), + ('table_name', models.CharField(max_length=64, verbose_name='字段所在表名')), + ('column_name', models.CharField(max_length=64, verbose_name='字段名')), + ('column_comment', models.CharField(blank=True, default='', max_length=1024, verbose_name='字段描述')), + ('create_time', models.DateTimeField(auto_now_add=True)), + ('sys_time', models.DateTimeField(auto_now=True)), + ('instance', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='sql.instance')), + ], + options={ + 'verbose_name': '脱敏字段配置', + 'verbose_name_plural': '脱敏字段配置', + 'db_table': 'data_masking_columns', + 'managed': True, + }, + ), + migrations.CreateModel( + name='ArchiveLog', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('cmd', models.CharField(max_length=2000, verbose_name='归档命令')), + ('condition', models.CharField(max_length=1000, verbose_name='归档条件,where条件')), + ('mode', models.CharField(choices=[('file', '文件'), ('dest', '其他实例'), ('purge', '直接删除')], max_length=10, verbose_name='归档模式')), + ('no_delete', models.BooleanField(verbose_name='是否保留源数据')), + ('sleep', models.IntegerField(default=0, verbose_name='归档limit行记录后的休眠秒数')), + ('select_cnt', models.IntegerField(verbose_name='查询数量')), + ('insert_cnt', models.IntegerField(verbose_name='插入数量')), + ('delete_cnt', models.IntegerField(verbose_name='删除数量')), + ('statistics', models.TextField(verbose_name='归档统计日志')), + ('success', models.BooleanField(verbose_name='是否归档成功')), + ('error_info', models.TextField(verbose_name='错误信息')), + ('start_time', models.DateTimeField(verbose_name='开始时间')), + ('end_time', models.DateTimeField(verbose_name='结束时间')), + ('sys_time', models.DateTimeField(auto_now=True, verbose_name='系统时间修改')), + ('archive', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='sql.archiveconfig')), + ], + options={ + 'verbose_name': '归档日志表', + 'verbose_name_plural': '归档日志表', + 'db_table': 'archive_log', + 'managed': True, + }, + ), + migrations.AddField( + model_name='archiveconfig', + name='dest_instance', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='dest_instance', to='sql.instance'), + ), + migrations.AddField( + model_name='archiveconfig', + name='resource_group', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='sql.resourcegroup'), + ), + migrations.AddField( + model_name='archiveconfig', + name='src_instance', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='src_instance', to='sql.instance'), + ), + migrations.CreateModel( + name='AliyunRdsConfig', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('rds_dbinstanceid', models.CharField(max_length=100, verbose_name='对应阿里云RDS实例ID')), + ('is_enable', models.BooleanField(default=False, verbose_name='是否启用')), + ('ak', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='sql.cloudaccesskey', verbose_name='RDS实例对应的AK配置')), + ('instance', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='sql.instance')), + ], + options={ + 'verbose_name': '阿里云rds配置', + 'verbose_name_plural': '阿里云rds配置', + 'db_table': 'aliyun_rds_config', + 'managed': True, + }, + ), + migrations.CreateModel( + name='Users', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('password', models.CharField(max_length=128, verbose_name='password')), + ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), + ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), + ('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')), + ('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')), + ('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')), + ('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')), + ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')), + ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')), + ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')), + ('display', models.CharField(default='', max_length=50, verbose_name='显示的中文名')), + ('ding_user_id', models.CharField(blank=True, max_length=64, verbose_name='钉钉UserID')), + ('wx_user_id', models.CharField(blank=True, max_length=64, verbose_name='企业微信UserID')), + ('feishu_open_id', models.CharField(blank=True, max_length=64, verbose_name='飞书OpenID')), + ('failed_login_count', models.IntegerField(default=0, verbose_name='失败计数')), + ('last_login_failed_at', models.DateTimeField(blank=True, null=True, verbose_name='上次失败登录时间')), + ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')), + ('resource_group', models.ManyToManyField(blank=True, to='sql.ResourceGroup', verbose_name='资源组')), + ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')), + ], + options={ + 'verbose_name': '用户管理', + 'verbose_name_plural': '用户管理', + 'db_table': 'sql_users', + 'managed': True, + }, + managers=[ + ('objects', django.contrib.auth.models.UserManager()), + ], + ), + migrations.CreateModel( + name='QueryPrivileges', + fields=[ + ('privilege_id', models.AutoField(primary_key=True, serialize=False)), + ('user_name', models.CharField(max_length=30, verbose_name='用户名')), + ('user_display', models.CharField(default='', max_length=50, verbose_name='申请人中文名')), + ('db_name', models.CharField(default='', max_length=64, verbose_name='数据库')), + ('table_name', models.CharField(default='', max_length=64, verbose_name='表')), + ('valid_date', models.DateField(verbose_name='有效时间')), + ('limit_num', models.IntegerField(default=100, verbose_name='行数限制')), + ('priv_type', models.IntegerField(choices=[(1, 'DATABASE'), (2, 'TABLE')], default=0, verbose_name='权限类型')), + ('is_deleted', models.IntegerField(default=0, verbose_name='是否删除')), + ('create_time', models.DateTimeField(auto_now_add=True)), + ('sys_time', models.DateTimeField(auto_now=True)), + ('instance', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='sql.instance')), + ], + options={ + 'verbose_name': '查询权限记录', + 'verbose_name_plural': '查询权限记录', + 'db_table': 'query_privileges', + 'managed': True, + 'index_together': {('user_name', 'instance', 'db_name', 'valid_date')}, + }, + ), + migrations.CreateModel( + name='InstanceDatabase', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('db_name', models.CharField(max_length=128, verbose_name='数据库名')), + ('owner', models.CharField(blank=True, default='', max_length=50, verbose_name='负责人')), + ('owner_display', models.CharField(blank=True, default='', max_length=50, verbose_name='负责人中文名')), + ('remark', models.CharField(blank=True, default='', max_length=255, verbose_name='备注')), + ('sys_time', models.DateTimeField(auto_now=True, verbose_name='系统修改时间')), + ('instance', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='sql.instance')), + ], + options={ + 'verbose_name': '实例数据库', + 'verbose_name_plural': '实例数据库列表', + 'db_table': 'instance_database', + 'managed': True, + 'unique_together': {('instance', 'db_name')}, + }, + ), + migrations.CreateModel( + name='InstanceAccount', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('user', mirage.fields.EncryptedCharField(max_length=128, verbose_name='账号')), + ('host', models.CharField(max_length=64, verbose_name='主机')), + ('password', mirage.fields.EncryptedCharField(blank=True, default='', max_length=128, verbose_name='密码')), + ('remark', models.CharField(max_length=255, verbose_name='备注')), + ('sys_time', models.DateTimeField(auto_now=True, verbose_name='系统修改时间')), + ('instance', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='sql.instance')), + ], + options={ + 'verbose_name': '实例账号列表', + 'verbose_name_plural': '实例账号列表', + 'db_table': 'instance_account', + 'managed': True, + 'unique_together': {('instance', 'user', 'host')}, + }, + ), ] From b44d4aeeffe850d6c6cb459b666d95876a339b9d Mon Sep 17 00:00:00 2001 From: ningyu Date: Thu, 2 Dec 2021 09:45:32 +0800 Subject: [PATCH 4/5] =?UTF-8?q?=E5=AE=A1=E8=AE=A1=E7=99=BB=E5=BD=95?= =?UTF-8?q?=E6=97=A5=E5=BF=97SQL=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/init_sql/audit_log.sql | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 src/init_sql/audit_log.sql diff --git a/src/init_sql/audit_log.sql b/src/init_sql/audit_log.sql new file mode 100644 index 0000000000..25779d6125 --- /dev/null +++ b/src/init_sql/audit_log.sql @@ -0,0 +1,11 @@ +-- 增加登录审计日志 +CREATE TABLE `audit_log` ( + `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'ID', + `user_id` int(11) DEFAULT NULL COMMENT '用户id', + `user_name` varchar(255) DEFAULT NULL COMMENT '用户名称', + `ip` varchar(255) DEFAULT NULL COMMENT '登录ip', + `action` varchar(255) DEFAULT NULL COMMENT '动作', + `action_time` datetime(6) NOT NULL COMMENT '操作时间', + PRIMARY KEY (`id`), + KEY `idx_username` (`user_name`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='登录审计日志表'; \ No newline at end of file From fe46d81e6d3c1b790de5a7b32f8562f3497990cc Mon Sep 17 00:00:00 2001 From: ningyu Date: Mon, 13 Dec 2021 11:52:37 +0800 Subject: [PATCH 5/5] =?UTF-8?q?1.=20=E5=88=A0=E9=99=A4migrations.=20=202.?= =?UTF-8?q?=20=E4=BF=AE=E6=94=B9audit=5Flog.sql=E6=96=87=E4=BB=B6=E5=90=8D?= =?UTF-8?q?=E4=B8=BAv1.8.3.sql.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sql/migrations/0001_initial.py | 746 --------------------- sql/migrations/__init__.py | 0 src/init_sql/{audit_log.sql => v1.8.3.sql} | 0 3 files changed, 746 deletions(-) delete mode 100644 sql/migrations/0001_initial.py delete mode 100644 sql/migrations/__init__.py rename src/init_sql/{audit_log.sql => v1.8.3.sql} (100%) diff --git a/sql/migrations/0001_initial.py b/sql/migrations/0001_initial.py deleted file mode 100644 index 5a301af7c1..0000000000 --- a/sql/migrations/0001_initial.py +++ /dev/null @@ -1,746 +0,0 @@ -# Generated by Django 3.1.13 on 2021-11-29 11:35 - -import django.contrib.auth.models -import django.contrib.auth.validators -from django.db import migrations, models -import django.db.models.deletion -import django.utils.timezone -import mirage.fields - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - ('auth', '0012_alter_user_first_name_max_length'), - ] - - operations = [ - migrations.CreateModel( - name='SlowQuery', - fields=[ - ('checksum', models.CharField(max_length=32, primary_key=True, serialize=False)), - ('fingerprint', models.TextField()), - ('sample', models.TextField()), - ('first_seen', models.DateTimeField(blank=True, null=True)), - ('last_seen', models.DateTimeField(blank=True, db_index=True, null=True)), - ('reviewed_by', models.CharField(blank=True, max_length=20, null=True)), - ('reviewed_on', models.DateTimeField(blank=True, null=True)), - ('comments', models.TextField(blank=True, null=True)), - ], - options={ - 'verbose_name': '慢日志统计', - 'verbose_name_plural': '慢日志统计', - 'db_table': 'mysql_slow_query_review', - 'managed': False, - }, - ), - migrations.CreateModel( - name='SlowQueryHistory', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('hostname_max', models.CharField(max_length=64)), - ('client_max', models.CharField(max_length=64, null=True)), - ('user_max', models.CharField(max_length=64)), - ('db_max', models.CharField(default=None, max_length=64, null=True)), - ('bytes_max', models.CharField(max_length=64, null=True)), - ('sample', models.TextField()), - ('ts_min', models.DateTimeField(db_index=True)), - ('ts_max', models.DateTimeField()), - ('ts_cnt', models.FloatField(blank=True, null=True)), - ('query_time_sum', models.FloatField(blank=True, db_column='Query_time_sum', null=True)), - ('query_time_min', models.FloatField(blank=True, db_column='Query_time_min', null=True)), - ('query_time_max', models.FloatField(blank=True, db_column='Query_time_max', null=True)), - ('query_time_pct_95', models.FloatField(blank=True, db_column='Query_time_pct_95', null=True)), - ('query_time_stddev', models.FloatField(blank=True, db_column='Query_time_stddev', null=True)), - ('query_time_median', models.FloatField(blank=True, db_column='Query_time_median', null=True)), - ('lock_time_sum', models.FloatField(blank=True, db_column='Lock_time_sum', null=True)), - ('lock_time_min', models.FloatField(blank=True, db_column='Lock_time_min', null=True)), - ('lock_time_max', models.FloatField(blank=True, db_column='Lock_time_max', null=True)), - ('lock_time_pct_95', models.FloatField(blank=True, db_column='Lock_time_pct_95', null=True)), - ('lock_time_stddev', models.FloatField(blank=True, db_column='Lock_time_stddev', null=True)), - ('lock_time_median', models.FloatField(blank=True, db_column='Lock_time_median', null=True)), - ('rows_sent_sum', models.FloatField(blank=True, db_column='Rows_sent_sum', null=True)), - ('rows_sent_min', models.FloatField(blank=True, db_column='Rows_sent_min', null=True)), - ('rows_sent_max', models.FloatField(blank=True, db_column='Rows_sent_max', null=True)), - ('rows_sent_pct_95', models.FloatField(blank=True, db_column='Rows_sent_pct_95', null=True)), - ('rows_sent_stddev', models.FloatField(blank=True, db_column='Rows_sent_stddev', null=True)), - ('rows_sent_median', models.FloatField(blank=True, db_column='Rows_sent_median', null=True)), - ('rows_examined_sum', models.FloatField(blank=True, db_column='Rows_examined_sum', null=True)), - ('rows_examined_min', models.FloatField(blank=True, db_column='Rows_examined_min', null=True)), - ('rows_examined_max', models.FloatField(blank=True, db_column='Rows_examined_max', null=True)), - ('rows_examined_pct_95', models.FloatField(blank=True, db_column='Rows_examined_pct_95', null=True)), - ('rows_examined_stddev', models.FloatField(blank=True, db_column='Rows_examined_stddev', null=True)), - ('rows_examined_median', models.FloatField(blank=True, db_column='Rows_examined_median', null=True)), - ('rows_affected_sum', models.FloatField(blank=True, db_column='Rows_affected_sum', null=True)), - ('rows_affected_min', models.FloatField(blank=True, db_column='Rows_affected_min', null=True)), - ('rows_affected_max', models.FloatField(blank=True, db_column='Rows_affected_max', null=True)), - ('rows_affected_pct_95', models.FloatField(blank=True, db_column='Rows_affected_pct_95', null=True)), - ('rows_affected_stddev', models.FloatField(blank=True, db_column='Rows_affected_stddev', null=True)), - ('rows_affected_median', models.FloatField(blank=True, db_column='Rows_affected_median', null=True)), - ('rows_read_sum', models.FloatField(blank=True, db_column='Rows_read_sum', null=True)), - ('rows_read_min', models.FloatField(blank=True, db_column='Rows_read_min', null=True)), - ('rows_read_max', models.FloatField(blank=True, db_column='Rows_read_max', null=True)), - ('rows_read_pct_95', models.FloatField(blank=True, db_column='Rows_read_pct_95', null=True)), - ('rows_read_stddev', models.FloatField(blank=True, db_column='Rows_read_stddev', null=True)), - ('rows_read_median', models.FloatField(blank=True, db_column='Rows_read_median', null=True)), - ('merge_passes_sum', models.FloatField(blank=True, db_column='Merge_passes_sum', null=True)), - ('merge_passes_min', models.FloatField(blank=True, db_column='Merge_passes_min', null=True)), - ('merge_passes_max', models.FloatField(blank=True, db_column='Merge_passes_max', null=True)), - ('merge_passes_pct_95', models.FloatField(blank=True, db_column='Merge_passes_pct_95', null=True)), - ('merge_passes_stddev', models.FloatField(blank=True, db_column='Merge_passes_stddev', null=True)), - ('merge_passes_median', models.FloatField(blank=True, db_column='Merge_passes_median', null=True)), - ('innodb_io_r_ops_min', models.FloatField(blank=True, db_column='InnoDB_IO_r_ops_min', null=True)), - ('innodb_io_r_ops_max', models.FloatField(blank=True, db_column='InnoDB_IO_r_ops_max', null=True)), - ('innodb_io_r_ops_pct_95', models.FloatField(blank=True, db_column='InnoDB_IO_r_ops_pct_95', null=True)), - ('innodb_io_r_ops_stddev', models.FloatField(blank=True, db_column='InnoDB_IO_r_ops_stddev', null=True)), - ('innodb_io_r_ops_median', models.FloatField(blank=True, db_column='InnoDB_IO_r_ops_median', null=True)), - ('innodb_io_r_bytes_min', models.FloatField(blank=True, db_column='InnoDB_IO_r_bytes_min', null=True)), - ('innodb_io_r_bytes_max', models.FloatField(blank=True, db_column='InnoDB_IO_r_bytes_max', null=True)), - ('innodb_io_r_bytes_pct_95', models.FloatField(blank=True, db_column='InnoDB_IO_r_bytes_pct_95', null=True)), - ('innodb_io_r_bytes_stddev', models.FloatField(blank=True, db_column='InnoDB_IO_r_bytes_stddev', null=True)), - ('innodb_io_r_bytes_median', models.FloatField(blank=True, db_column='InnoDB_IO_r_bytes_median', null=True)), - ('innodb_io_r_wait_min', models.FloatField(blank=True, db_column='InnoDB_IO_r_wait_min', null=True)), - ('innodb_io_r_wait_max', models.FloatField(blank=True, db_column='InnoDB_IO_r_wait_max', null=True)), - ('innodb_io_r_wait_pct_95', models.FloatField(blank=True, db_column='InnoDB_IO_r_wait_pct_95', null=True)), - ('innodb_io_r_wait_stddev', models.FloatField(blank=True, db_column='InnoDB_IO_r_wait_stddev', null=True)), - ('innodb_io_r_wait_median', models.FloatField(blank=True, db_column='InnoDB_IO_r_wait_median', null=True)), - ('innodb_rec_lock_wait_min', models.FloatField(blank=True, db_column='InnoDB_rec_lock_wait_min', null=True)), - ('innodb_rec_lock_wait_max', models.FloatField(blank=True, db_column='InnoDB_rec_lock_wait_max', null=True)), - ('innodb_rec_lock_wait_pct_95', models.FloatField(blank=True, db_column='InnoDB_rec_lock_wait_pct_95', null=True)), - ('innodb_rec_lock_wait_stddev', models.FloatField(blank=True, db_column='InnoDB_rec_lock_wait_stddev', null=True)), - ('innodb_rec_lock_wait_median', models.FloatField(blank=True, db_column='InnoDB_rec_lock_wait_median', null=True)), - ('innodb_queue_wait_min', models.FloatField(blank=True, db_column='InnoDB_queue_wait_min', null=True)), - ('innodb_queue_wait_max', models.FloatField(blank=True, db_column='InnoDB_queue_wait_max', null=True)), - ('innodb_queue_wait_pct_95', models.FloatField(blank=True, db_column='InnoDB_queue_wait_pct_95', null=True)), - ('innodb_queue_wait_stddev', models.FloatField(blank=True, db_column='InnoDB_queue_wait_stddev', null=True)), - ('innodb_queue_wait_median', models.FloatField(blank=True, db_column='InnoDB_queue_wait_median', null=True)), - ('innodb_pages_distinct_min', models.FloatField(blank=True, db_column='InnoDB_pages_distinct_min', null=True)), - ('innodb_pages_distinct_max', models.FloatField(blank=True, db_column='InnoDB_pages_distinct_max', null=True)), - ('innodb_pages_distinct_pct_95', models.FloatField(blank=True, db_column='InnoDB_pages_distinct_pct_95', null=True)), - ('innodb_pages_distinct_stddev', models.FloatField(blank=True, db_column='InnoDB_pages_distinct_stddev', null=True)), - ('innodb_pages_distinct_median', models.FloatField(blank=True, db_column='InnoDB_pages_distinct_median', null=True)), - ('qc_hit_cnt', models.FloatField(blank=True, db_column='QC_Hit_cnt', null=True)), - ('qc_hit_sum', models.FloatField(blank=True, db_column='QC_Hit_sum', null=True)), - ('full_scan_cnt', models.FloatField(blank=True, db_column='Full_scan_cnt', null=True)), - ('full_scan_sum', models.FloatField(blank=True, db_column='Full_scan_sum', null=True)), - ('full_join_cnt', models.FloatField(blank=True, db_column='Full_join_cnt', null=True)), - ('full_join_sum', models.FloatField(blank=True, db_column='Full_join_sum', null=True)), - ('tmp_table_cnt', models.FloatField(blank=True, db_column='Tmp_table_cnt', null=True)), - ('tmp_table_sum', models.FloatField(blank=True, db_column='Tmp_table_sum', null=True)), - ('tmp_table_on_disk_cnt', models.FloatField(blank=True, db_column='Tmp_table_on_disk_cnt', null=True)), - ('tmp_table_on_disk_sum', models.FloatField(blank=True, db_column='Tmp_table_on_disk_sum', null=True)), - ('filesort_cnt', models.FloatField(blank=True, db_column='Filesort_cnt', null=True)), - ('filesort_sum', models.FloatField(blank=True, db_column='Filesort_sum', null=True)), - ('filesort_on_disk_cnt', models.FloatField(blank=True, db_column='Filesort_on_disk_cnt', null=True)), - ('filesort_on_disk_sum', models.FloatField(blank=True, db_column='Filesort_on_disk_sum', null=True)), - ], - options={ - 'verbose_name': '慢日志明细', - 'verbose_name_plural': '慢日志明细', - 'db_table': 'mysql_slow_query_review_history', - 'managed': False, - }, - ), - migrations.CreateModel( - name='ArchiveConfig', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('title', models.CharField(max_length=50, verbose_name='归档配置说明')), - ('audit_auth_groups', models.CharField(blank=True, max_length=255, verbose_name='审批权限组列表')), - ('src_db_name', models.CharField(max_length=64, verbose_name='源数据库')), - ('src_table_name', models.CharField(max_length=64, verbose_name='源表')), - ('dest_db_name', models.CharField(blank=True, max_length=64, null=True, verbose_name='目标数据库')), - ('dest_table_name', models.CharField(blank=True, max_length=64, null=True, verbose_name='目标表')), - ('condition', models.CharField(max_length=1000, verbose_name='归档条件,where条件')), - ('mode', models.CharField(choices=[('file', '文件'), ('dest', '其他实例'), ('purge', '直接删除')], max_length=10, verbose_name='归档模式')), - ('no_delete', models.BooleanField(verbose_name='是否保留源数据')), - ('sleep', models.IntegerField(default=1, verbose_name='归档limit行后的休眠秒数')), - ('status', models.IntegerField(blank=True, choices=[(0, '待审核'), (1, '审核通过'), (2, '审核不通过'), (3, '审核取消')], default=1, verbose_name='审核状态')), - ('state', models.BooleanField(default=True, verbose_name='是否启用归档')), - ('user_name', models.CharField(blank=True, default='', max_length=30, verbose_name='申请人')), - ('user_display', models.CharField(blank=True, default='', max_length=50, verbose_name='申请人中文名')), - ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), - ('last_archive_time', models.DateTimeField(blank=True, null=True, verbose_name='最近归档时间')), - ('sys_time', models.DateTimeField(auto_now=True, verbose_name='系统时间修改')), - ], - options={ - 'verbose_name': '归档配置表', - 'verbose_name_plural': '归档配置表', - 'db_table': 'archive_config', - 'managed': True, - }, - ), - migrations.CreateModel( - name='AuditEntry', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('user_id', models.IntegerField(verbose_name='用户ID')), - ('user_name', models.CharField(max_length=255, null=True, verbose_name='用户名称')), - ('action', models.CharField(max_length=255, verbose_name='动作')), - ('ip', models.GenericIPAddressField(null=True, verbose_name='IP')), - ('action_time', models.DateTimeField(auto_now_add=True, verbose_name='操作时间')), - ], - options={ - 'verbose_name': '审计日志', - 'verbose_name_plural': '审计日志', - 'db_table': 'audit_log', - 'managed': True, - }, - ), - migrations.CreateModel( - name='CloudAccessKey', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('type', models.CharField(choices=[('aliyun', 'aliyun')], default='', max_length=20)), - ('key_id', models.CharField(max_length=200)), - ('key_secret', models.CharField(max_length=200)), - ('remark', models.CharField(blank=True, default='', max_length=50)), - ], - options={ - 'verbose_name': '云服务认证信息配置', - 'verbose_name_plural': '云服务认证信息配置', - 'db_table': 'cloud_access_key', - 'managed': True, - }, - ), - migrations.CreateModel( - name='Config', - fields=[ - ('item', models.CharField(max_length=200, primary_key=True, serialize=False, verbose_name='配置项')), - ('value', mirage.fields.EncryptedCharField(max_length=500, verbose_name='配置项值')), - ('description', models.CharField(blank=True, default='', max_length=200, verbose_name='描述')), - ], - options={ - 'verbose_name': '系统配置', - 'verbose_name_plural': '系统配置', - 'db_table': 'sql_config', - 'managed': True, - }, - ), - migrations.CreateModel( - name='DataMaskingRules', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('rule_type', models.IntegerField(choices=[(1, '手机号'), (2, '证件号码'), (3, '银行卡'), (4, '邮箱'), (5, '金额'), (6, '其他')], unique=True, verbose_name='规则类型')), - ('rule_regex', models.CharField(max_length=255, verbose_name='规则脱敏所用的正则表达式,表达式必须分组,隐藏的组会使用****代替')), - ('hide_group', models.IntegerField(verbose_name='需要隐藏的组')), - ('rule_desc', models.CharField(blank=True, default='', max_length=100, verbose_name='规则描述')), - ('sys_time', models.DateTimeField(auto_now=True)), - ], - options={ - 'verbose_name': '脱敏规则配置', - 'verbose_name_plural': '脱敏规则配置', - 'db_table': 'data_masking_rules', - 'managed': True, - }, - ), - migrations.CreateModel( - name='Instance', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('instance_name', models.CharField(max_length=50, unique=True, verbose_name='实例名称')), - ('type', models.CharField(choices=[('master', '主库'), ('slave', '从库')], max_length=6, verbose_name='实例类型')), - ('db_type', models.CharField(choices=[('mysql', 'MySQL'), ('mssql', 'MsSQL'), ('redis', 'Redis'), ('pgsql', 'PgSQL'), ('oracle', 'Oracle'), ('mongo', 'Mongo'), ('phoenix', 'Phoenix'), ('inception', 'Inception'), ('goinception', 'goInception')], max_length=20, verbose_name='数据库类型')), - ('host', models.CharField(max_length=200, verbose_name='实例连接')), - ('port', models.IntegerField(default=0, verbose_name='端口')), - ('user', mirage.fields.EncryptedCharField(blank=True, default='', max_length=200, verbose_name='用户名')), - ('password', mirage.fields.EncryptedCharField(blank=True, default='', max_length=300, verbose_name='密码')), - ('db_name', models.CharField(blank=True, default='', max_length=64, verbose_name='数据库')), - ('charset', models.CharField(blank=True, default='', max_length=20, verbose_name='字符集')), - ('service_name', models.CharField(blank=True, max_length=50, null=True, verbose_name='Oracle service name')), - ('sid', models.CharField(blank=True, max_length=50, null=True, verbose_name='Oracle sid')), - ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), - ('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')), - ], - options={ - 'verbose_name': '实例配置', - 'verbose_name_plural': '实例配置', - 'db_table': 'sql_instance', - 'managed': True, - }, - ), - migrations.CreateModel( - name='InstanceTag', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('tag_code', models.CharField(max_length=20, unique=True, verbose_name='标签代码')), - ('tag_name', models.CharField(max_length=20, unique=True, verbose_name='标签名称')), - ('active', models.BooleanField(default=True, verbose_name='激活状态')), - ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), - ], - options={ - 'verbose_name': '实例标签', - 'verbose_name_plural': '实例标签', - 'db_table': 'sql_instance_tag', - 'managed': True, - }, - ), - migrations.CreateModel( - name='Permission', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ], - options={ - 'permissions': (('menu_dashboard', '菜单 Dashboard'), ('menu_sqlcheck', '菜单 SQL审核'), ('menu_sqlworkflow', '菜单 SQL上线'), ('menu_sqlanalyze', '菜单 SQL分析'), ('menu_query', '菜单 SQL查询'), ('menu_sqlquery', '菜单 在线查询'), ('menu_queryapplylist', '菜单 权限管理'), ('menu_sqloptimize', '菜单 SQL优化'), ('menu_sqladvisor', '菜单 优化工具'), ('menu_slowquery', '菜单 慢查日志'), ('menu_instance', '菜单 实例管理'), ('menu_instance_list', '菜单 实例列表'), ('menu_dbdiagnostic', '菜单 会话管理'), ('menu_database', '菜单 数据库管理'), ('menu_instance_account', '菜单 实例账号管理'), ('menu_param', '菜单 参数配置'), ('menu_data_dictionary', '菜单 数据字典'), ('menu_tools', '菜单 工具插件'), ('menu_archive', '菜单 数据归档'), ('menu_binlog2sql', '菜单 Binlog2SQL'), ('menu_schemasync', '菜单 SchemaSync'), ('menu_system', '菜单 系统管理'), ('menu_document', '菜单 相关文档'), ('sql_submit', '提交SQL上线工单'), ('sql_review', '审核SQL上线工单'), ('sql_execute_for_resource_group', '执行SQL上线工单(资源组粒度)'), ('sql_execute', '执行SQL上线工单(仅自己提交的)'), ('sql_analyze', '执行SQL分析'), ('optimize_sqladvisor', '执行SQLAdvisor'), ('optimize_sqltuning', '执行SQLTuning'), ('optimize_soar', '执行SOAR'), ('query_applypriv', '申请查询权限'), ('query_mgtpriv', '管理查询权限'), ('query_review', '审核查询权限'), ('query_submit', '提交SQL查询'), ('query_all_instances', '可查询所有实例'), ('query_resource_group_instance', '可查询所在资源组内的所有实例'), ('process_view', '查看会话'), ('process_kill', '终止会话'), ('tablespace_view', '查看表空间'), ('trx_view', '查看事务信息'), ('trxandlocks_view', '查看锁信息'), ('instance_account_manage', '管理实例账号'), ('param_view', '查看实例参数列表'), ('param_edit', '修改实例参数'), ('data_dictionary_export', '导出数据字典'), ('archive_apply', '提交归档申请'), ('archive_review', '审核归档申请'), ('archive_mgt', '管理归档申请')), - 'managed': True, - }, - ), - migrations.CreateModel( - name='QueryLog', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('instance_name', models.CharField(max_length=50, verbose_name='实例名称')), - ('db_name', models.CharField(max_length=64, verbose_name='数据库名称')), - ('sqllog', models.TextField(verbose_name='执行的查询语句')), - ('effect_row', models.BigIntegerField(verbose_name='返回行数')), - ('cost_time', models.CharField(default='', max_length=10, verbose_name='执行耗时')), - ('username', models.CharField(max_length=30, verbose_name='操作人')), - ('user_display', models.CharField(default='', max_length=50, verbose_name='操作人中文名')), - ('priv_check', models.BooleanField(choices=[(False, '跳过'), (True, '正常')], default=False, verbose_name='查询权限是否正常校验')), - ('hit_rule', models.BooleanField(choices=[(False, '未命中/未知'), (True, '命中')], default=False, verbose_name='查询是否命中脱敏规则')), - ('masking', models.BooleanField(choices=[(False, '否'), (True, '是')], default=False, verbose_name='查询结果是否正常脱敏')), - ('favorite', models.BooleanField(choices=[(False, '否'), (True, '是')], default=False, verbose_name='是否收藏')), - ('alias', models.CharField(blank=True, default='', max_length=64, verbose_name='语句标识')), - ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='操作时间')), - ('sys_time', models.DateTimeField(auto_now=True)), - ], - options={ - 'verbose_name': '查询日志', - 'verbose_name_plural': '查询日志', - 'db_table': 'query_log', - 'managed': True, - }, - ), - migrations.CreateModel( - name='ResourceGroup', - fields=[ - ('group_id', models.AutoField(primary_key=True, serialize=False, verbose_name='组ID')), - ('group_name', models.CharField(max_length=100, unique=True, verbose_name='组名称')), - ('group_parent_id', models.BigIntegerField(default=0, verbose_name='父级id')), - ('group_sort', models.IntegerField(default=1, verbose_name='排序')), - ('group_level', models.IntegerField(default=1, verbose_name='层级')), - ('ding_webhook', models.CharField(blank=True, max_length=255, verbose_name='钉钉webhook地址')), - ('feishu_webhook', models.CharField(blank=True, max_length=255, verbose_name='飞书webhook地址')), - ('qywx_webhook', models.CharField(blank=True, max_length=255, verbose_name='企业微信webhook地址')), - ('is_deleted', models.IntegerField(choices=[(0, '否'), (1, '是')], default=0, verbose_name='是否删除')), - ('create_time', models.DateTimeField(auto_now_add=True)), - ('sys_time', models.DateTimeField(auto_now=True)), - ], - options={ - 'verbose_name': '资源组管理', - 'verbose_name_plural': '资源组管理', - 'db_table': 'resource_group', - 'managed': True, - }, - ), - migrations.CreateModel( - name='SqlWorkflow', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('workflow_name', models.CharField(max_length=50, verbose_name='工单内容')), - ('demand_url', models.CharField(max_length=500, verbose_name='需求链接')), - ('group_id', models.IntegerField(verbose_name='组ID')), - ('group_name', models.CharField(max_length=100, verbose_name='组名称')), - ('db_name', models.CharField(max_length=64, verbose_name='数据库')), - ('syntax_type', models.IntegerField(choices=[(0, '其他'), (1, 'DDL'), (2, 'DML')], default=0, verbose_name='工单类型 0、未知,1、DDL,2、DML')), - ('is_backup', models.BooleanField(choices=[(False, '否'), (True, '是')], default=True, verbose_name='是否备份')), - ('engineer', models.CharField(max_length=30, verbose_name='发起人')), - ('engineer_display', models.CharField(default='', max_length=50, verbose_name='发起人中文名')), - ('status', models.CharField(choices=[('workflow_finish', '已正常结束'), ('workflow_abort', '人工终止流程'), ('workflow_manreviewing', '等待审核人审核'), ('workflow_review_pass', '审核通过'), ('workflow_timingtask', '定时执行'), ('workflow_queuing', '排队中'), ('workflow_executing', '执行中'), ('workflow_autoreviewwrong', '自动审核不通过'), ('workflow_exception', '执行有异常')], max_length=50)), - ('audit_auth_groups', models.CharField(max_length=255, verbose_name='审批权限组列表')), - ('run_date_start', models.DateTimeField(blank=True, null=True, verbose_name='可执行起始时间')), - ('run_date_end', models.DateTimeField(blank=True, null=True, verbose_name='可执行结束时间')), - ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), - ('finish_time', models.DateTimeField(blank=True, null=True, verbose_name='结束时间')), - ('is_manual', models.IntegerField(choices=[(0, '否'), (1, '是')], default=0, verbose_name='是否原生执行')), - ('instance', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='sql.instance')), - ], - options={ - 'verbose_name': 'SQL工单', - 'verbose_name_plural': 'SQL工单', - 'db_table': 'sql_workflow', - 'managed': True, - }, - ), - migrations.CreateModel( - name='Tunnel', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('tunnel_name', models.CharField(max_length=50, unique=True, verbose_name='隧道名称')), - ('host', models.CharField(max_length=200, verbose_name='隧道连接')), - ('port', models.IntegerField(default=0, verbose_name='端口')), - ('user', mirage.fields.EncryptedCharField(blank=True, default='', max_length=200, null=True, verbose_name='用户名')), - ('password', mirage.fields.EncryptedCharField(blank=True, default='', max_length=300, null=True, verbose_name='密码')), - ('pkey_path', mirage.fields.EncryptedCharField(blank=True, default='', max_length=300, null=True, verbose_name='密钥地址')), - ('pkey_password', mirage.fields.EncryptedCharField(blank=True, default='', max_length=300, null=True, verbose_name='密钥密码')), - ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), - ('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')), - ], - options={ - 'verbose_name': '隧道配置', - 'verbose_name_plural': '隧道配置', - 'db_table': 'ssh_tunnel', - 'managed': True, - }, - ), - migrations.CreateModel( - name='WorkflowAuditDetail', - fields=[ - ('audit_detail_id', models.AutoField(primary_key=True, serialize=False)), - ('audit_id', models.IntegerField(verbose_name='审核主表id')), - ('audit_user', models.CharField(max_length=30, verbose_name='审核人')), - ('audit_time', models.DateTimeField(verbose_name='审核时间')), - ('audit_status', models.IntegerField(choices=[(0, '待审核'), (1, '审核通过'), (2, '审核不通过'), (3, '审核取消')], verbose_name='审核状态')), - ('remark', models.CharField(default='', max_length=1000, verbose_name='审核备注')), - ('sys_time', models.DateTimeField(auto_now=True, verbose_name='系统时间')), - ], - options={ - 'verbose_name': '工作流审批明细', - 'verbose_name_plural': '工作流审批明细', - 'db_table': 'workflow_audit_detail', - 'managed': True, - }, - ), - migrations.CreateModel( - name='WorkflowLog', - fields=[ - ('id', models.AutoField(primary_key=True, serialize=False)), - ('audit_id', models.IntegerField(db_index=True, verbose_name='工单审批id')), - ('operation_type', models.SmallIntegerField(choices=[(0, '提交/待审核'), (1, '审核通过'), (2, '审核不通过'), (3, '审核取消'), (4, '定时执行'), (5, '执行工单'), (6, '执行结束')], verbose_name='操作类型')), - ('operation_type_desc', models.CharField(max_length=10, verbose_name='操作类型描述')), - ('operation_info', models.CharField(max_length=1000, verbose_name='操作信息')), - ('operator', models.CharField(max_length=30, verbose_name='操作人')), - ('operator_display', models.CharField(default='', max_length=50, verbose_name='操作人中文名')), - ('operation_time', models.DateTimeField(auto_now_add=True)), - ], - options={ - 'verbose_name': '工作流日志', - 'verbose_name_plural': '工作流日志', - 'db_table': 'workflow_log', - 'managed': True, - }, - ), - migrations.CreateModel( - name='WorkflowAuditSetting', - fields=[ - ('audit_setting_id', models.AutoField(primary_key=True, serialize=False)), - ('group_id', models.IntegerField(verbose_name='组ID')), - ('group_name', models.CharField(max_length=100, verbose_name='组名称')), - ('workflow_type', models.IntegerField(choices=[(1, 'sql_query'), (2, 'sql_review')], verbose_name='审批类型')), - ('audit_auth_groups', models.CharField(max_length=255, verbose_name='审批权限组列表')), - ('create_time', models.DateTimeField(auto_now_add=True)), - ('sys_time', models.DateTimeField(auto_now=True)), - ], - options={ - 'verbose_name': '审批流程配置', - 'verbose_name_plural': '审批流程配置', - 'db_table': 'workflow_audit_setting', - 'managed': True, - 'unique_together': {('group_id', 'workflow_type')}, - }, - ), - migrations.CreateModel( - name='WorkflowAudit', - fields=[ - ('audit_id', models.AutoField(primary_key=True, serialize=False)), - ('group_id', models.IntegerField(verbose_name='组ID')), - ('group_name', models.CharField(max_length=100, verbose_name='组名称')), - ('workflow_id', models.BigIntegerField(verbose_name='关联业务id')), - ('workflow_type', models.IntegerField(choices=[(1, 'sql_query'), (2, 'sql_review')], verbose_name='申请类型')), - ('workflow_title', models.CharField(max_length=50, verbose_name='申请标题')), - ('workflow_remark', models.CharField(blank=True, default='', max_length=140, verbose_name='申请备注')), - ('audit_auth_groups', models.CharField(max_length=255, verbose_name='审批权限组列表')), - ('current_audit', models.CharField(max_length=20, verbose_name='当前审批权限组')), - ('next_audit', models.CharField(max_length=20, verbose_name='下级审批权限组')), - ('current_status', models.IntegerField(choices=[(0, '待审核'), (1, '审核通过'), (2, '审核不通过'), (3, '审核取消')], verbose_name='审核状态')), - ('create_user', models.CharField(max_length=30, verbose_name='申请人')), - ('create_user_display', models.CharField(default='', max_length=50, verbose_name='申请人中文名')), - ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='申请时间')), - ('sys_time', models.DateTimeField(auto_now=True, verbose_name='系统时间')), - ], - options={ - 'verbose_name': '工作流审批列表', - 'verbose_name_plural': '工作流审批列表', - 'db_table': 'workflow_audit', - 'managed': True, - 'unique_together': {('workflow_id', 'workflow_type')}, - }, - ), - migrations.CreateModel( - name='SqlWorkflowContent', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('sql_content', models.TextField(verbose_name='具体sql内容')), - ('review_content', models.TextField(verbose_name='自动审核内容的JSON格式')), - ('execute_result', models.TextField(blank=True, verbose_name='执行结果的JSON格式')), - ('workflow', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='sql.sqlworkflow')), - ], - options={ - 'verbose_name': 'SQL工单内容', - 'verbose_name_plural': 'SQL工单内容', - 'db_table': 'sql_workflow_content', - 'managed': True, - }, - ), - migrations.CreateModel( - name='QueryPrivilegesApply', - fields=[ - ('apply_id', models.AutoField(primary_key=True, serialize=False)), - ('group_id', models.IntegerField(verbose_name='组ID')), - ('group_name', models.CharField(max_length=100, verbose_name='组名称')), - ('title', models.CharField(max_length=50, verbose_name='申请标题')), - ('user_name', models.CharField(max_length=30, verbose_name='申请人')), - ('user_display', models.CharField(default='', max_length=50, verbose_name='申请人中文名')), - ('db_list', models.TextField(default='', verbose_name='数据库')), - ('table_list', models.TextField(default='', verbose_name='表')), - ('valid_date', models.DateField(verbose_name='有效时间')), - ('limit_num', models.IntegerField(default=100, verbose_name='行数限制')), - ('priv_type', models.IntegerField(choices=[(1, 'DATABASE'), (2, 'TABLE')], default=0, verbose_name='权限类型')), - ('status', models.IntegerField(choices=[(0, '待审核'), (1, '审核通过'), (2, '审核不通过'), (3, '审核取消')], verbose_name='审核状态')), - ('audit_auth_groups', models.CharField(max_length=255, verbose_name='审批权限组列表')), - ('create_time', models.DateTimeField(auto_now_add=True)), - ('sys_time', models.DateTimeField(auto_now=True)), - ('instance', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='sql.instance')), - ], - options={ - 'verbose_name': '查询权限申请记录表', - 'verbose_name_plural': '查询权限申请记录表', - 'db_table': 'query_privileges_apply', - 'managed': True, - }, - ), - migrations.CreateModel( - name='ParamTemplate', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('db_type', models.CharField(choices=[('mysql', 'MySQL'), ('mssql', 'MsSQL'), ('redis', 'Redis'), ('pgsql', 'PgSQL'), ('oracle', 'Oracle'), ('mongo', 'Mongo'), ('phoenix', 'Phoenix'), ('inception', 'Inception'), ('goinception', 'goInception')], max_length=20, verbose_name='数据库类型')), - ('variable_name', models.CharField(max_length=64, verbose_name='参数名')), - ('default_value', models.CharField(max_length=1024, verbose_name='默认参数值')), - ('editable', models.BooleanField(default=False, verbose_name='是否支持修改')), - ('valid_values', models.CharField(blank=True, max_length=1024, verbose_name='有效参数值,范围参数[1-65535],值参数[ON|OFF]')), - ('description', models.CharField(blank=True, max_length=1024, verbose_name='参数描述')), - ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), - ('sys_time', models.DateTimeField(auto_now=True, verbose_name='系统时间修改')), - ], - options={ - 'verbose_name': '实例参数模板配置', - 'verbose_name_plural': '实例参数模板配置', - 'db_table': 'param_template', - 'managed': True, - 'unique_together': {('db_type', 'variable_name')}, - }, - ), - migrations.CreateModel( - name='ParamHistory', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('variable_name', models.CharField(max_length=64, verbose_name='参数名')), - ('old_var', models.CharField(max_length=1024, verbose_name='修改前参数值')), - ('new_var', models.CharField(max_length=1024, verbose_name='修改后参数值')), - ('set_sql', models.CharField(max_length=1024, verbose_name='在线变更配置执行的SQL语句')), - ('user_name', models.CharField(max_length=30, verbose_name='修改人')), - ('user_display', models.CharField(max_length=50, verbose_name='修改人中文名')), - ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='参数被修改时间点')), - ('instance', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='sql.instance')), - ], - options={ - 'verbose_name': '实例参数修改历史', - 'verbose_name_plural': '实例参数修改历史', - 'db_table': 'param_history', - 'ordering': ['-create_time'], - 'managed': True, - }, - ), - migrations.AddField( - model_name='instance', - name='instance_tag', - field=models.ManyToManyField(blank=True, to='sql.InstanceTag', verbose_name='实例标签'), - ), - migrations.AddField( - model_name='instance', - name='resource_group', - field=models.ManyToManyField(blank=True, to='sql.ResourceGroup', verbose_name='资源组'), - ), - migrations.AddField( - model_name='instance', - name='tunnel', - field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.CASCADE, to='sql.tunnel', verbose_name='连接隧道'), - ), - migrations.CreateModel( - name='DataMaskingColumns', - fields=[ - ('column_id', models.AutoField(primary_key=True, serialize=False, verbose_name='字段id')), - ('rule_type', models.IntegerField(choices=[(1, '手机号'), (2, '证件号码'), (3, '银行卡'), (4, '邮箱'), (5, '金额'), (6, '其他')], verbose_name='规则类型')), - ('active', models.BooleanField(choices=[(False, '未激活'), (True, '激活')], verbose_name='激活状态')), - ('table_schema', models.CharField(max_length=64, verbose_name='字段所在库名')), - ('table_name', models.CharField(max_length=64, verbose_name='字段所在表名')), - ('column_name', models.CharField(max_length=64, verbose_name='字段名')), - ('column_comment', models.CharField(blank=True, default='', max_length=1024, verbose_name='字段描述')), - ('create_time', models.DateTimeField(auto_now_add=True)), - ('sys_time', models.DateTimeField(auto_now=True)), - ('instance', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='sql.instance')), - ], - options={ - 'verbose_name': '脱敏字段配置', - 'verbose_name_plural': '脱敏字段配置', - 'db_table': 'data_masking_columns', - 'managed': True, - }, - ), - migrations.CreateModel( - name='ArchiveLog', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('cmd', models.CharField(max_length=2000, verbose_name='归档命令')), - ('condition', models.CharField(max_length=1000, verbose_name='归档条件,where条件')), - ('mode', models.CharField(choices=[('file', '文件'), ('dest', '其他实例'), ('purge', '直接删除')], max_length=10, verbose_name='归档模式')), - ('no_delete', models.BooleanField(verbose_name='是否保留源数据')), - ('sleep', models.IntegerField(default=0, verbose_name='归档limit行记录后的休眠秒数')), - ('select_cnt', models.IntegerField(verbose_name='查询数量')), - ('insert_cnt', models.IntegerField(verbose_name='插入数量')), - ('delete_cnt', models.IntegerField(verbose_name='删除数量')), - ('statistics', models.TextField(verbose_name='归档统计日志')), - ('success', models.BooleanField(verbose_name='是否归档成功')), - ('error_info', models.TextField(verbose_name='错误信息')), - ('start_time', models.DateTimeField(verbose_name='开始时间')), - ('end_time', models.DateTimeField(verbose_name='结束时间')), - ('sys_time', models.DateTimeField(auto_now=True, verbose_name='系统时间修改')), - ('archive', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='sql.archiveconfig')), - ], - options={ - 'verbose_name': '归档日志表', - 'verbose_name_plural': '归档日志表', - 'db_table': 'archive_log', - 'managed': True, - }, - ), - migrations.AddField( - model_name='archiveconfig', - name='dest_instance', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='dest_instance', to='sql.instance'), - ), - migrations.AddField( - model_name='archiveconfig', - name='resource_group', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='sql.resourcegroup'), - ), - migrations.AddField( - model_name='archiveconfig', - name='src_instance', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='src_instance', to='sql.instance'), - ), - migrations.CreateModel( - name='AliyunRdsConfig', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('rds_dbinstanceid', models.CharField(max_length=100, verbose_name='对应阿里云RDS实例ID')), - ('is_enable', models.BooleanField(default=False, verbose_name='是否启用')), - ('ak', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='sql.cloudaccesskey', verbose_name='RDS实例对应的AK配置')), - ('instance', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='sql.instance')), - ], - options={ - 'verbose_name': '阿里云rds配置', - 'verbose_name_plural': '阿里云rds配置', - 'db_table': 'aliyun_rds_config', - 'managed': True, - }, - ), - migrations.CreateModel( - name='Users', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('password', models.CharField(max_length=128, verbose_name='password')), - ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), - ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), - ('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')), - ('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')), - ('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')), - ('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')), - ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')), - ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')), - ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')), - ('display', models.CharField(default='', max_length=50, verbose_name='显示的中文名')), - ('ding_user_id', models.CharField(blank=True, max_length=64, verbose_name='钉钉UserID')), - ('wx_user_id', models.CharField(blank=True, max_length=64, verbose_name='企业微信UserID')), - ('feishu_open_id', models.CharField(blank=True, max_length=64, verbose_name='飞书OpenID')), - ('failed_login_count', models.IntegerField(default=0, verbose_name='失败计数')), - ('last_login_failed_at', models.DateTimeField(blank=True, null=True, verbose_name='上次失败登录时间')), - ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')), - ('resource_group', models.ManyToManyField(blank=True, to='sql.ResourceGroup', verbose_name='资源组')), - ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')), - ], - options={ - 'verbose_name': '用户管理', - 'verbose_name_plural': '用户管理', - 'db_table': 'sql_users', - 'managed': True, - }, - managers=[ - ('objects', django.contrib.auth.models.UserManager()), - ], - ), - migrations.CreateModel( - name='QueryPrivileges', - fields=[ - ('privilege_id', models.AutoField(primary_key=True, serialize=False)), - ('user_name', models.CharField(max_length=30, verbose_name='用户名')), - ('user_display', models.CharField(default='', max_length=50, verbose_name='申请人中文名')), - ('db_name', models.CharField(default='', max_length=64, verbose_name='数据库')), - ('table_name', models.CharField(default='', max_length=64, verbose_name='表')), - ('valid_date', models.DateField(verbose_name='有效时间')), - ('limit_num', models.IntegerField(default=100, verbose_name='行数限制')), - ('priv_type', models.IntegerField(choices=[(1, 'DATABASE'), (2, 'TABLE')], default=0, verbose_name='权限类型')), - ('is_deleted', models.IntegerField(default=0, verbose_name='是否删除')), - ('create_time', models.DateTimeField(auto_now_add=True)), - ('sys_time', models.DateTimeField(auto_now=True)), - ('instance', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='sql.instance')), - ], - options={ - 'verbose_name': '查询权限记录', - 'verbose_name_plural': '查询权限记录', - 'db_table': 'query_privileges', - 'managed': True, - 'index_together': {('user_name', 'instance', 'db_name', 'valid_date')}, - }, - ), - migrations.CreateModel( - name='InstanceDatabase', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('db_name', models.CharField(max_length=128, verbose_name='数据库名')), - ('owner', models.CharField(blank=True, default='', max_length=50, verbose_name='负责人')), - ('owner_display', models.CharField(blank=True, default='', max_length=50, verbose_name='负责人中文名')), - ('remark', models.CharField(blank=True, default='', max_length=255, verbose_name='备注')), - ('sys_time', models.DateTimeField(auto_now=True, verbose_name='系统修改时间')), - ('instance', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='sql.instance')), - ], - options={ - 'verbose_name': '实例数据库', - 'verbose_name_plural': '实例数据库列表', - 'db_table': 'instance_database', - 'managed': True, - 'unique_together': {('instance', 'db_name')}, - }, - ), - migrations.CreateModel( - name='InstanceAccount', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('user', mirage.fields.EncryptedCharField(max_length=128, verbose_name='账号')), - ('host', models.CharField(max_length=64, verbose_name='主机')), - ('password', mirage.fields.EncryptedCharField(blank=True, default='', max_length=128, verbose_name='密码')), - ('remark', models.CharField(max_length=255, verbose_name='备注')), - ('sys_time', models.DateTimeField(auto_now=True, verbose_name='系统修改时间')), - ('instance', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='sql.instance')), - ], - options={ - 'verbose_name': '实例账号列表', - 'verbose_name_plural': '实例账号列表', - 'db_table': 'instance_account', - 'managed': True, - 'unique_together': {('instance', 'user', 'host')}, - }, - ), - ] diff --git a/sql/migrations/__init__.py b/sql/migrations/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/init_sql/audit_log.sql b/src/init_sql/v1.8.3.sql similarity index 100% rename from src/init_sql/audit_log.sql rename to src/init_sql/v1.8.3.sql