# -*- coding: utf-8 -*- """ domain_info_api.py """ from __future__ import print_function, unicode_literals, absolute_import, division import json from datetime import datetime from operator import itemgetter from flask import request, g from peewee import fn from playhouse.shortcuts import model_to_dict from domain_admin.enums.operation_enum import OperationEnum from domain_admin.enums.role_enum import RoleEnum from domain_admin.model.domain_info_model import DomainInfoModel from domain_admin.model.domain_model import DomainModel from domain_admin.model.group_model import GroupModel from domain_admin.model.group_user_model import GroupUserModel from domain_admin.service import domain_info_service, async_task_service, file_service, group_service, \ operation_service, group_user_service, domain_service, common_service, domain_icp_service, tag_service, auth_service from domain_admin.utils import domain_util, time_util, icp_util from domain_admin.utils.flask_ext.app_exception import AppException, DataNotFoundAppException from domain_admin.utils.open_api import crtsh_api @auth_service.permission(role=RoleEnum.USER) @operation_service.operation_log_decorator( model=DomainInfoModel, operation_type_id=OperationEnum.CREATE, primary_key='domain_info_id' ) def add_domain_info(): """ 添加域名 :return: """ current_user_id = g.user_id domain = request.json['domain'] root_domain = domain_util.get_root_domain(domain) if not root_domain: raise AppException("不能解析根域名:{}".format(domain)) else: domain = root_domain domain_start_time = request.json.get('domain_start_time') domain_expire_time = request.json.get('domain_expire_time') is_auto_update = request.json.get('is_auto_update', True) is_auto_subdomain = request.json.get('is_auto_subdomain', False) comment = request.json.get('comment', '') tags = request.json.get('tags') icp_company = request.json.get('icp_company', '') icp_licence = request.json.get('icp_licence', '') group_id = request.json.get('group_id') or 0 user_id = request.json.get('user_id') row = domain_info_service.add_domain_info( domain=domain, domain_start_time=domain_start_time, domain_expire_time=domain_expire_time, comment=comment, tags=tags, group_id=group_id, user_id=user_id or current_user_id, icp_company=icp_company, icp_licence=icp_licence, is_auto_update=is_auto_update ) # 异步提交 if is_auto_subdomain: domain_service.auto_import_from_domain_async( root_domain=domain, group_id=group_id, user_id=current_user_id ) # async_task_service.submit_task( # fn=domain_service.auto_import_from_domain, # root_domain=domain, # group_id=group_id, # user_id=current_user_id # ) tag_service.add_tags(tags) return {'domain_info_id': row.id} @auth_service.permission(role=RoleEnum.USER) @operation_service.operation_log_decorator( model=DomainInfoModel, operation_type_id=OperationEnum.UPDATE, primary_key='domain_info_id' ) def update_domain_info_by_id(): """ 更新数据 :return: """ current_user_id = g.user_id domain_info_id = request.json['domain_info_id'] domain = domain_util.get_root_domain(request.json['domain']) domain_start_time = request.json.get('domain_start_time') domain_expire_time = request.json.get('domain_expire_time') is_auto_update = request.json.get('is_auto_update', True) is_auto_subdomain = request.json.get('is_auto_subdomain', False) comment = request.json.get('comment', '') group_id = request.json.get('group_id') or 0 tags = request.json.get('tags') icp_company = request.json.get('icp_company', '') icp_licence = request.json.get('icp_licence', '') user_id = request.json.get('user_id') # check data domain_info_row = DomainInfoModel.select().where( DomainInfoModel.id == domain_info_id, DomainInfoModel.user_id == current_user_id ).first() if not domain_info_row: raise DataNotFoundAppException() # is_auto_update = request.json.get('is_auto_update', True) # is_expire_monitor = request.json.get('is_expire_monitor', True) data = { 'domain': domain, 'comment': comment, 'group_id': group_id, 'tags_raw': json.dumps(tags, ensure_ascii=False), 'icp_company': icp_company, 'icp_licence': icp_licence, 'is_auto_update': is_auto_update } # 不自动更新,才可以提交开始时间和结束时间 if is_auto_update is False: data['domain_start_time'] = domain_start_time data['domain_expire_time'] = domain_expire_time if domain_expire_time: data['domain_expire_days'] = time_util.get_diff_days(datetime.now(), time_util.parse_time(domain_expire_time)) else: data['domain_expire_days'] = 0 if user_id: data['user_id'] = user_id DomainInfoModel.update(data).where( DomainInfoModel.id == domain_info_id ).execute() if domain_info_row.domain != domain and is_auto_update: # 需要自动更新 domain_info_service.update_domain_info_row(domain_info_row) if is_auto_subdomain: domain_service.auto_import_from_domain_async( root_domain=domain, group_id=group_id, user_id=current_user_id ) # # async_task_service.submit_task( # fn=domain_service.auto_import_from_domain, # root_domain=domain, # group_id=group_id, # user_id=current_user_id # ) tag_service.add_tags(tags) @auth_service.permission(role=RoleEnum.USER) @operation_service.operation_log_decorator( model=DomainInfoModel, operation_type_id=OperationEnum.UPDATE, primary_key='domain_info_id' ) def update_domain_info_field_by_id(): """ 更新单个数据 :return: """ current_user_id = g.user_id domain_info_id = request.json['domain_info_id'] field = request.json.get('field') value = request.json.get('value') if field not in ['is_auto_update', 'is_expire_monitor']: raise AppException("not allow field") data = { field: value, } # check data domain_info_row = DomainInfoModel.select().where( DomainInfoModel.id == domain_info_id, DomainInfoModel.user_id == current_user_id ).first() if not domain_info_row: raise DataNotFoundAppException() DomainInfoModel.update(data).where( DomainInfoModel.id == domain_info_row.id ).execute() @auth_service.permission(role=RoleEnum.USER) @operation_service.operation_log_decorator( model=DomainInfoModel, operation_type_id=OperationEnum.DELETE, primary_key='domain_info_id' ) def delete_domain_info_by_id(): """ 删除 :return: """ current_user_id = g.user_id domain_info_id = request.json['domain_info_id'] # check data domain_info_row = DomainInfoModel.select().where( DomainInfoModel.id == domain_info_id, DomainInfoModel.user_id == current_user_id ).first() if not domain_info_row: raise DataNotFoundAppException() DomainInfoModel.delete_by_id(domain_info_row.id) @auth_service.permission(role=RoleEnum.USER) @operation_service.operation_log_decorator( model=DomainInfoModel, operation_type_id=OperationEnum.BATCH_DELETE, primary_key='domain_info_ids' ) def delete_domain_info_by_ids(): """ 批量删除 """ current_user_id = g.user_id domain_info_ids = request.json['domain_info_ids'] DomainInfoModel.delete().where( DomainInfoModel.id.in_(domain_info_ids), DomainInfoModel.user_id == current_user_id ).execute() @auth_service.permission(role=RoleEnum.USER) def get_domain_info_by_id(): """ 获取 :return: """ current_user_id = g.user_id domain_info_id = request.json['domain_info_id'] # check data if g.current_user_row.role == RoleEnum.ADMIN: domain_info_row = DomainInfoModel.select().where( DomainInfoModel.id == domain_info_id ).first() else: domain_info_row = DomainInfoModel.select().where( DomainInfoModel.id == domain_info_id, DomainInfoModel.user_id == current_user_id ).first() if not domain_info_row: raise DataNotFoundAppException() domain_row = model_to_dict( model=domain_info_row, extra_attrs=[ 'real_domain_expire_days', 'update_time_label', 'tags', ], exclude=['tags_raw'], ) # 主机数量 ssl_count = DomainModel.select().where( DomainModel.root_domain == domain_row['domain'], DomainModel.user_id == current_user_id ).count() domain_row['ssl_count'] = ssl_count domain_row['group_name'] = group_service.get_group_name_by_id(domain_row['group_id']) # 编辑权限 group_user_permission_map = group_user_service.get_group_user_permission_map(current_user_id) if domain_row['user_id'] == current_user_id: has_edit_permission = True else: has_edit_permission = group_user_permission_map.get(domain_row['group_id'], False) domain_row['has_edit_permission'] = has_edit_permission common_service.load_user_name([domain_row]) return domain_row @auth_service.permission(role=RoleEnum.USER) def update_domain_info_row_by_id(): """ 更新当前行的域名信息 :return: """ current_user_id = g.user_id domain_info_id = request.json['domain_info_id'] # check data domain_info_row = DomainInfoModel.select().where( DomainInfoModel.id == domain_info_id, DomainInfoModel.user_id == current_user_id ).first() if not domain_info_row: raise DataNotFoundAppException() domain_info_service.update_domain_info_row(row=domain_info_row) @auth_service.permission(role=RoleEnum.USER) def update_all_domain_info_of_user(): """ 更新当前用户的域名信息 :return: """ current_user_id = g.user_id domain_info_service.update_all_domain_info_of_user(user_id=current_user_id) # async_task_service.submit_task(fn=domain_info_service.update_all_domain_info_of_user, user_id=current_user_id) @auth_service.permission(role=RoleEnum.USER) def update_all_domain_icp_of_user(): """ 更新当前用户的域名icp信息 :return: """ current_user_id = g.user_id domain_info_service.update_all_domain_icp_of_user(current_user_id) # async_task_service.submit_task(fn=domain_info_service.update_all_domain_icp_of_user, user_id=current_user_id) @auth_service.permission(role=RoleEnum.USER) def update_domain_row_icp(): """ 更新当前域名icp信息 :return: """ current_user_id = g.user_id domain_info_id = request.json['domain_info_id'] # check data domain_info_row = DomainInfoModel.select().where( DomainInfoModel.id == domain_info_id, DomainInfoModel.user_id == current_user_id ).first() if not domain_info_row: raise DataNotFoundAppException() domain_info_service.update_domain_row_icp(row=domain_info_row) @auth_service.permission(role=RoleEnum.USER) def import_domain_info_from_file(): """ 从文件导入域名 支持格式: txt、xlsx、csv :return: """ current_user_id = g.user_id update_file = request.files.get('file') filename = file_service.save_temp_file(update_file) # 导入数据 domain_info_service.add_domain_from_file(filename, current_user_id) # 异步查询 domain_info_service.handle_auto_import_domain_info(current_user_id) # async_task_service.submit_task(fn=domain_info_service.update_all_domain_info_of_user, user_id=current_user_id) @auth_service.permission(role=RoleEnum.USER) def export_domain_info_file(): """ 导出域名文件 csv格式 :return: """ current_user_id = g.user_id keyword = request.json.get('keyword') group_ids = request.json.get('group_ids') domain_expire_days = request.json.get('domain_expire_days') role = request.json.get('role') ext = request.json.get('ext', 'csv') order_prop = request.json.get('order_prop') or 'domain_expire_days' order_type = request.json.get('order_type') or 'ascending' params = { 'keyword': keyword, 'group_ids': group_ids, 'domain_expire_days': domain_expire_days, 'role': role, 'user_id': current_user_id } # 列表数据 query = domain_info_service.get_domain_info_query(**params) ordering = domain_info_service.get_ordering(order_prop=order_prop, order_type=order_type) rows = query.order_by(*ordering) lst = [model_to_dict( model=row, extra_attrs=[ 'domain_start_date', 'domain_expire_date', 'real_domain_expire_days', 'update_time_label', 'tags', 'tags_str', ] ) for row in rows] # 分组名 group_service.load_group_name(lst) filename = domain_info_service.export_domain_to_file(ext=ext, rows=lst) return { 'name': filename, 'url': file_service.resolve_temp_url(filename) } @auth_service.permission(role=RoleEnum.USER) def get_domain_info_list(): """ 获取域名列表 :return: """ current_user_id = g.user_id page = request.json.get('page', 1) size = request.json.get('size', 10) keyword = request.json.get('keyword') group_ids = request.json.get('group_ids') domain_expire_days = request.json.get('domain_expire_days') role = request.json.get('role') order_prop = request.json.get('order_prop') or 'domain_expire_days' order_type = request.json.get('order_type') or 'ascending' group_user_permission_map = {} if role == RoleEnum.ADMIN: pass else: # 所在分组 group_user_rows = GroupUserModel.select().where( GroupUserModel.user_id == current_user_id ) group_user_list = list(group_user_rows) # 组员权限 group_user_permission_map = {row.group_id: row.has_edit_permission for row in group_user_list} params = { 'keyword': keyword, 'group_ids': group_ids, 'domain_expire_days': domain_expire_days, 'role': role, 'user_id': current_user_id } # 列表数据 query = domain_info_service.get_domain_info_query(**params) total = query.count() lst = [] if total > 0: ordering = domain_info_service.get_ordering(order_prop=order_prop, order_type=order_type) rows = query.order_by(*ordering).paginate(page, size) lst = [model_to_dict( model=row, extra_attrs=[ 'real_domain_expire_days', 'update_time_label', 'tags', ] ) for row in rows] domain_list = [row['domain'] for row in lst] # 域名证书 root_domain_groups = DomainModel.select( DomainModel.root_domain, fn.COUNT(DomainModel.id).alias('count') ).where( DomainModel.root_domain.in_(domain_list), DomainModel.user_id == current_user_id ).group_by(DomainModel.root_domain) root_domain_groups_map = { row.root_domain: row.count for row in root_domain_groups } for row in lst: row['ssl_count'] = root_domain_groups_map.get(row['domain'], 0) if role == RoleEnum.ADMIN: has_edit_permission = True elif row['user_id'] == current_user_id: has_edit_permission = True else: has_edit_permission = group_user_permission_map.get(row['group_id'], False) row['has_edit_permission'] = has_edit_permission # 分组名 group_service.load_group_name(lst) # 用户名 common_service.load_user_name(lst) return { 'list': lst, 'total': total, } @auth_service.permission(role=RoleEnum.USER) def get_domain_info_group_filter(): """ 获取域名分组筛选器 :return: """ current_user_id = g.user_id # 分组列表数据 query = GroupModel.select().where( GroupModel.user_id == current_user_id ) total = query.count() lst = [] if total > 0: # 证书分组统计 cert_groups = DomainInfoModel.select( DomainInfoModel.group_id, fn.COUNT(DomainInfoModel.id).alias('count') ).group_by(DomainInfoModel.group_id) groups_map = { str(row.group_id): row.count for row in cert_groups } lst = [] for row in query: row_dict = model_to_dict(row) row_dict['domain_count'] = groups_map.get(str(row.id), 0) lst.append(row_dict) if groups_map.get('0'): lst.append({ 'domain_count': groups_map.get('0'), 'id': 0, 'name': '未分组', }) lst.sort(key=itemgetter('domain_count'), reverse=True) return { 'list': lst, 'total': len(lst), } @auth_service.permission(role=RoleEnum.USER) def get_icp(): """ 查询icp信息 """ domain = request.json['domain'] # 解析域名 resolve_domain = domain_util.parse_domain(domain) item = domain_icp_service.get_domain_icp(resolve_domain) if not item: raise AppException('没有查到icp信息') res = item.to_dict() res['resolve_domain'] = resolve_domain return res @auth_service.permission(role=RoleEnum.USER) def get_sub_domain_cert(): """ 获取子域证书列表 :return: """ keyword = request.json.get('keyword', 1) lst = crtsh_api.search(keyword) return { 'list': lst, 'total': len(lst) } @auth_service.permission(role=RoleEnum.USER) def auto_import_subdomain_by_ids(): """ 批量导入子域证书 @since v1.6.30 :return: """ current_user_id = g.user_id domain_ids = request.json['domain_ids'] rows = DomainInfoModel.select().where( DomainInfoModel.id.in_(domain_ids), DomainInfoModel.user_id == current_user_id ) # 异步提交 domain_service.auto_import_from_domain_batch_async(rows=rows, user_id=current_user_id)