diff --git a/fir_client/src/components/user/FirSuperSignBase.vue b/fir_client/src/components/user/FirSuperSignBase.vue index dc542d2..7b86689 100644 --- a/fir_client/src/components/user/FirSuperSignBase.vue +++ b/fir_client/src/components/user/FirSuperSignBase.vue @@ -65,26 +65,26 @@
- 导出证书 - 导入p12证书 - 清理签名数据 - 同步设备信息 + 同步设备信息 - 手动创建发布证书 - 删除发布证书 diff --git a/fir_client/src/components/user/FirUserProfileInfo.vue b/fir_client/src/components/user/FirUserProfileInfo.vue index 53dd77f..0963686 100644 --- a/fir_client/src/components/user/FirUserProfileInfo.vue +++ b/fir_client/src/components/user/FirUserProfileInfo.vue @@ -307,17 +307,19 @@ export default { } }, methods: { delete_wx_u(wx_user_info) { - this.$confirm(`此操作将导致微信用户 “${wx_user_info.nickname}” 无法通过扫码登录, 是否继续删除?`, '提示', { + this.$prompt(`此操作将导致微信用户 “${wx_user_info.nickname}” 无法通过扫码登录, 是否继续删除?`, '警告', { confirmButtonText: '确定', cancelButtonText: '取消', - type: 'warning' - }).then(() => { + inputPlaceholder: '该用户的登录密码', + inputValidator: this.validFun + }).then(({value}) => { this.wxUtilsFun({ - methods: 'DELETE', + methods: 'POST', data: { "size": this.pagination.pagesize, "page": this.pagination.currentPage, "openid": wx_user_info.openid, + "confirm_pwd": value } }) }).catch(() => { @@ -332,6 +334,8 @@ export default { if (data.code === 1000) { this.wx_user_list = data.data; this.show_wx_visible = true + } else if (data.code === 1001) { + this.$message.error(data.msg) } else { this.$message.error("获取授权列表失败") } diff --git a/fir_client/src/components/user/FirUserStorage.vue b/fir_client/src/components/user/FirUserStorage.vue index 9c99bfd..de41aeb 100644 --- a/fir_client/src/components/user/FirUserStorage.vue +++ b/fir_client/src/components/user/FirUserStorage.vue @@ -407,10 +407,7 @@ export default { }, validFun(val) { - if (val && val.length > 6) { - return true - } - return false + return val && val.length > 6; }, clean_storage_data(act) { diff --git a/fir_ser/api/utils/app/iossignapi.py b/fir_ser/api/utils/app/iossignapi.py index 218cb51..a9aec2b 100644 --- a/fir_ser/api/utils/app/iossignapi.py +++ b/fir_ser/api/utils/app/iossignapi.py @@ -196,9 +196,17 @@ def make_pem(cer_content, pem_path): def check_error_call_back(error): - logger.error(error) + msg = '' if 'Cannot connect to proxy' in error or 'Read timed out' in error or 'Max retries exceeded with' in error: logger.error('access apple api failed . change proxy ip again') + msg = "代理网络错误,请稍后重试或联系管理员处理" + if 'it may be encrypted with an unsupported algorithm' in error: + msg = "数据校验失败,请检查p8key内容是否正常" + if 'Authentication credentials are missing or invalid' in error: + msg = '认证失败,请检查开发者信息填写是否正确' + + logger.error(f"{msg} {error}") + return msg if msg else error class AppDeveloperApiV2(object): @@ -221,8 +229,7 @@ class AppDeveloperApiV2(object): return True, result except Exception as e: logger.error(f"ios developer active Failed Exception:{e}") - check_error_call_back(str(e)) - result['return_info'] = "%s" % e + result['return_info'] = check_error_call_back(str(e)) return False, result def file_format_path_name(self, user_obj): @@ -250,8 +257,7 @@ class AppDeveloperApiV2(object): return True, certificates except Exception as e: logger.error(f"ios developer create cert Failed Exception:{e}") - check_error_call_back(str(e)) - result['return_info'] = "%s" % e + result['return_info'] = check_error_call_back(str(e)) return False, result def get_cert_obj_by_cid(self): @@ -266,8 +272,7 @@ class AppDeveloperApiV2(object): return False, result except Exception as e: logger.error(f"ios developer get cert {self.cert_id} Failed Exception:{e}") - check_error_call_back(str(e)) - result['return_info'] = "%s" % e + result['return_info'] = check_error_call_back(str(e)) return False, result def revoke_cert(self): @@ -286,8 +291,7 @@ class AppDeveloperApiV2(object): return True, result except Exception as e: logger.error(f"ios developer cert {self.cert_id} revoke Failed Exception:{e}") - check_error_call_back(str(e)) - result['return_info'] = "%s" % e + result['return_info'] = check_error_call_back(str(e)) return False, result def auto_set_certid_by_p12(self, app_dev_pem): @@ -307,8 +311,7 @@ class AppDeveloperApiV2(object): return False, result except Exception as e: logger.error(f"ios developer cert {app_dev_pem} auto get Failed Exception:{e}") - check_error_call_back(str(e)) - result['return_info'] = "%s" % e + result['return_info'] = check_error_call_back(str(e)) return False, result def del_profile(self, profile_id, profile_name): @@ -319,8 +322,7 @@ class AppDeveloperApiV2(object): return True except Exception as e: logger.error(f"ios developer delete profile Failed Exception:{e}") - check_error_call_back(str(e)) - result['return_info'] = "%s" % e + result['return_info'] = check_error_call_back(str(e)) return False, result def set_device_status(self, status, device_id, device_name, device_udid, failed_call_prefix, @@ -337,8 +339,7 @@ class AppDeveloperApiV2(object): return True, result except Exception as e: logger.error("ios developer set devices status Failed Exception:%s" % e) - check_error_call_back(str(e)) - result['return_info'] = "%s" % e + result['return_info'] = check_error_call_back(str(e)) if device_err_callback and ("There are no current ios devices" in str(e) or "Device obj is None" in str(e)): with CleanErrorBundleIdSignDataState(failed_call_prefix) as state: if state: @@ -358,8 +359,7 @@ class AppDeveloperApiV2(object): return True, devices_obj_list except Exception as e: logger.error("ios developer get device Failed Exception:%s" % e) - check_error_call_back(str(e)) - result['return_info'] = "%s" % e + result['return_info'] = check_error_call_back(str(e)) return False, result def del_app(self, identifier_id, bundle_id, app_id): @@ -371,8 +371,7 @@ class AppDeveloperApiV2(object): except Exception as e: logger.error("ios developer delete app Failed Exception:%s" % e) - check_error_call_back(str(e)) - result['return_info'] = "%s" % e + result['return_info'] = check_error_call_back(str(e)) return False, result def create_app(self, bundle_id, app_id, s_type, app_id_err_callback=None): @@ -393,8 +392,7 @@ class AppDeveloperApiV2(object): except Exception as e: logger.error("ios developer create app Failed Exception:%s" % e) - check_error_call_back(str(e)) - result['return_info'] = "%s" % e + result['return_info'] = check_error_call_back(str(e)) if app_id_err_callback and "There is no App ID with ID" in str(e): for call_fun in app_id_err_callback: call_fun() @@ -407,8 +405,7 @@ class AppDeveloperApiV2(object): return True, apple_obj.register_device(device_name, device_udid) except Exception as e: logger.error("ios developer register device Failed Exception:%s" % e) - check_error_call_back(str(e)) - result['return_info'] = "%s" % e + result['return_info'] = check_error_call_back(str(e)) if device_err_callback and "There are no current ios devices" in str(e): with CleanErrorBundleIdSignDataState(failed_call_prefix) as state: @@ -439,8 +436,7 @@ class AppDeveloperApiV2(object): return True, result except Exception as e: logger.error(f"app_id {app_obj.app_id} ios developer make profile Failed Exception:{e}") - check_error_call_back(str(e)) - result['return_info'] = "%s" % e + result['return_info'] = check_error_call_back(str(e)) if app_id_err_callback and "There is no App ID with ID" in str(e): with CleanErrorBundleIdSignDataState(failed_call_prefix) as state: if state: @@ -474,6 +470,5 @@ class AppDeveloperApiV2(object): return True, result except Exception as e: logger.error("ios developer modify_capability Failed Exception:%s" % e) - check_error_call_back(str(e)) - result['return_info'] = "%s" % e + result['return_info'] = check_error_call_back(str(e)) return False, result diff --git a/fir_ser/api/utils/app/supersignutils.py b/fir_ser/api/utils/app/supersignutils.py index 8a16c9d..43328db 100644 --- a/fir_ser/api/utils/app/supersignutils.py +++ b/fir_ser/api/utils/app/supersignutils.py @@ -21,7 +21,7 @@ from api.utils.app.iossignapi import ResignApp, AppDeveloperApiV2 from api.utils.baseutils import file_format_path, delete_app_profile_file, get_profile_full_path, format_apple_date, \ get_format_time, make_app_uuid, make_from_user_uuid from api.utils.modelutils import get_ios_developer_public_num, check_ipa_is_latest_sign, \ - get_developer_can_used_from_public_sign + get_developer_can_used_from_public_sign, update_or_create_developer_udid_info from api.utils.response import BaseResponse from api.utils.serializer import BillAppInfoSerializer, BillDeveloperInfoSerializer from api.utils.storage.caches import del_cache_response_by_short, send_msg_over_limit, check_app_permission, \ @@ -278,7 +278,7 @@ def get_new_developer_by_app_obj(user_objs, app_obj, apple_to_app=False): developer_obj_lists = obj_base_filter.filter(appledevelopertoappuse__app_id=app_obj) else: developer_obj_lists = obj_base_filter.exclude(appledevelopertoappuse__developerid__isnull=False) - developer_obj_lists = developer_obj_lists.distinct().order_by("created_time") + developer_obj_lists = developer_obj_lists.all().distinct().order_by("created_time") for developer_obj in developer_obj_lists: # 通过开发者数限制进行过滤 used_number = get_developer_udided(developer_obj)[2] + get_developer_can_used_from_public_sign( @@ -288,60 +288,97 @@ def get_new_developer_by_app_obj(user_objs, app_obj, apple_to_app=False): apple_to_app_obj = AppleDeveloperToAppUse.objects.filter(app_id=app_obj, developerid=developer_obj).first() if apple_to_app_obj: - if used_number < apple_to_app_obj.usable_number: + # 通过配置的专属分配数量进行过滤 + app_used_number = DeveloperDevicesID.objects.filter(developerid=developer_obj, + app_id=app_obj).distinct().count() + if app_used_number < apple_to_app_obj.usable_number: can_used_developer_pk_list.append(developer_obj.pk) else: can_used_developer_pk_list.append(developer_obj.pk) return can_used_developer_pk_list -def get_developer_user_by_app_udid(user_objs, udid, app_obj): - developer_udid_obj_list = UDIDsyncDeveloper.objects.filter(developerid__user_id__in=user_objs, +def get_developer_by_pk_list(developer_pk_list, f_key): + developer_obj_dict = AppIOSDeveloperInfo.objects.filter(pk__in=developer_pk_list, + is_actived=True, + certid__isnull=False).values( + f_key, 'pk').annotate( + count=Count('pk')).order_by('created_time').order_by('count').first() + if developer_obj_dict: + developer_obj = AppIOSDeveloperInfo.objects.filter(pk=developer_obj_dict.get('pk')).first() + logger.info(f'get {f_key} suitable developer {developer_obj} and return') + return developer_obj + + +def get_developer_user_by_app_udid(user_objs, udid, app_obj, private_first=True): + """ + :param user_objs: [user_obj] + :param udid: udid + :param app_obj: app_obj + :param private_first: 专属应用优先使用专属开发者,而不是公共开发者 + :return: (developer_obj, is_exist_devices) + """ + developer_udid_queryset = UDIDsyncDeveloper.objects.filter(developerid__user_id__in=user_objs, developerid__is_actived=True, developerid__certid__isnull=False).values( - 'developerid').distinct() + 'developerid').all().distinct() # 根据udid和应用查找该用户开发者账户【主要可能是开发者未激活】 - developer_obj = AppIOSDeveloperInfo.objects.filter( - pk__in=developer_udid_obj_list.filter(udid=udid, developerid__apptodeveloper__app_id=app_obj)).last() - if developer_obj: - logger.info(f'udid:{udid} app_obj:{app_obj} return') - return developer_obj, True - - # 根据udid查找开发者账户 - developer_obj = AppIOSDeveloperInfo.objects.filter(pk__in=developer_udid_obj_list.filter(udid=udid)).last() - if developer_obj: - logger.info(f'udid:{udid} only and return') - return developer_obj, True + developer_pk_list = developer_udid_queryset.filter(udid=udid, developerid__apptodeveloper__app_id=app_obj) + app_search_flag = True + + # 联合udid和应用查询不到数据,那么,仅根据udid查找开发者账户 + if not developer_pk_list: + app_search_flag = False + developer_pk_list = developer_udid_queryset.filter(udid=udid) + + if developer_pk_list: + developer_obj = get_developer_by_pk_list(developer_pk_list, 'developerappid') + if developer_obj: + if app_search_flag: + logger.info(f'udid:{udid} and app_obj:{app_obj} exist and return. developer_obj: {developer_obj}') + else: + logger.info(f'udid:{udid} exist and return. app_obj:{app_obj} developer_obj: {developer_obj}') + return developer_obj, True + else: + logger.info(f"udid:{udid} is a new device. app_obj:{app_obj} will find a suitable developer and register it") # 新设备查找策略 # 根据app查找开发者账户 exist_app_developer_pk_list = [] - for developer_obj in AppIOSDeveloperInfo.objects.filter( - pk__in=developer_udid_obj_list.filter(developerid__apptodeveloper__app_id=app_obj)): - if get_developer_udided(developer_obj)[2] + get_developer_can_used_from_public_sign( - developer_obj.user_id) < developer_obj.usable_number: - logger.info(f'app_obj:{app_obj} only and return') - exist_app_developer_pk_list.append(developer_obj.pk) - # return developer_obj, False - + developer_pk_list = developer_udid_queryset.filter(developerid__apptodeveloper__app_id=app_obj) + if developer_pk_list: + for developer_obj in AppIOSDeveloperInfo.objects.filter(pk__in=developer_pk_list): + if get_developer_udided(developer_obj)[2] + get_developer_can_used_from_public_sign( + developer_obj.user_id) < developer_obj.usable_number: + exist_app_developer_pk_list.append(developer_obj.pk) + + # 查询状态正常的专属开发者信息,判断是否为专属应用 apple_to_app = AppleDeveloperToAppUse.objects.filter(app_id=app_obj, developerid__is_actived=True, developerid__certid__isnull=False).first() + + # 存在专属开发者账户,但是也同时存在已经安装的设备,优先选择已经安装设备的开发者账户?是or否 + # 是:使应用趋向于一个开发者,为后期更新提供方便 + # 否:优先使用专属开发者进行签名 + if not private_first and apple_to_app and exist_app_developer_pk_list: + return AppIOSDeveloperInfo.objects.filter(pk=exist_app_developer_pk_list[0]).first(), False + can_used_developer_pk_list = get_new_developer_by_app_obj(user_objs, app_obj, apple_to_app) + + # 存在指定开发者账户,但是获取可用的开发者账户为空,那么开始匹配全部开发者 if not can_used_developer_pk_list and apple_to_app: + logger.info(f"udid:{udid} app_obj:{app_obj} private developer is null. start find all developer") can_used_developer_pk_list = get_new_developer_by_app_obj(user_objs, app_obj) if can_used_developer_pk_list and exist_app_developer_pk_list: can_used_developer_pk_list = list(set(can_used_developer_pk_list) & set(exist_app_developer_pk_list)) + logger.info(f"udid:{udid} app_obj:{app_obj} find private and can use developer {can_used_developer_pk_list}") # 查询开发者策略 按照最小注册设备数进行查找,这样可以分散,进而使应用趋向于一个开发者,为后期更新提供方便 if can_used_developer_pk_list: - developer_obj_dict = AppIOSDeveloperInfo.objects.filter(pk__in=can_used_developer_pk_list).values( - 'udidsyncdeveloper', 'pk').annotate( - count=Count('pk')).order_by('created_time').order_by('count').first() - if developer_obj_dict: - developer_obj = AppIOSDeveloperInfo.objects.filter(pk=developer_obj_dict.get('pk')).first() - logger.info(f'get suitable developer {developer_obj} and return') + developer_obj = get_developer_by_pk_list(can_used_developer_pk_list, 'udidsyncdeveloper') + if developer_obj: + logger.info(f"udid:{udid} is a new device. app_obj:{app_obj} find {developer_obj} and return") return developer_obj, False return None, None @@ -517,12 +554,7 @@ class IosUtils(object): if app_udid_obj and app_udid_obj.is_download: sign_flag = False if app_udid_obj.is_signed: - # release_obj = AppReleaseInfo.objects.filter(app_id=self.app_obj, is_master=True).first() - if check_ipa_is_latest_sign(self.app_obj, self.developer_obj): - # - # for apptodev_obj in APPToDeveloper.objects.filter(app_id=self.app_obj).all(): - # if release_obj.release_id == apptodev_obj.release_file: msg = "udid %s exists app_id %s" % (self.udid, self.app_obj) d_result['msg'] = msg logger.info(d_result) @@ -581,15 +613,7 @@ class IosUtils(object): if not status: return status, device_obj - device = { - "serial": device_obj.id, - "product": device_obj.name, - "udid": device_obj.udid, - "version": device_obj.model, - "status": True if device_obj.status == 'ENABLED' else False - } - sync_device_obj, _ = UDIDsyncDeveloper.objects.update_or_create(developerid=developer_obj, - udid=device_obj.udid, defaults=device) + sync_device_obj, _ = update_or_create_developer_udid_info(device_obj, developer_obj) # 更新设备状态 # 1. UDIDsyncDeveloper 库,通过udid 更新或创建设备信息 @@ -1040,15 +1064,7 @@ class IosUtils(object): will_del_disabled_udid_list = list(set(udid_developer_list) - set(udid_enabled_result_list)) for device_obj in result: - device = { - "serial": device_obj.id, - "product": device_obj.name, - "udid": device_obj.udid, - "version": device_obj.model, - "status": True if device_obj.status == 'ENABLED' else False - } - obj, create = UDIDsyncDeveloper.objects.update_or_create(developerid=developer_obj, - udid=device_obj.udid, defaults=device) + obj, create = update_or_create_developer_udid_info(device_obj, developer_obj) if not create: DeveloperDevicesID.objects.filter(udid=obj, developerid=developer_obj).update( **{'did': device_obj.id}) diff --git a/fir_ser/api/utils/apple/appleapiv3.py b/fir_ser/api/utils/apple/appleapiv3.py index 2cf9fef..0fa59f0 100644 --- a/fir_ser/api/utils/apple/appleapiv3.py +++ b/fir_ser/api/utils/apple/appleapiv3.py @@ -705,7 +705,7 @@ def call_function_try_attempts(try_attempts=5, sleep_time=3): f'again...{i}') res = str(e) if 'Authentication credentials are missing or invalid' in str(e): - res = '认证失败,请检查开发者信息填写是否正确' + raise Exception(res) time.sleep(sleep_time) logger.info(f"exec {func} finished. time:{time.time() - start_time}") if not flag: diff --git a/fir_ser/api/utils/modelutils.py b/fir_ser/api/utils/modelutils.py index 400e3f5..24dfc3f 100644 --- a/fir_ser/api/utils/modelutils.py +++ b/fir_ser/api/utils/modelutils.py @@ -12,7 +12,7 @@ from urllib.parse import urljoin from django.db.models import Count, Sum, Q from api.models import AppReleaseInfo, UserDomainInfo, DomainCnameInfo, UserAdDisplayInfo, RemoteClientInfo, \ - AppIOSDeveloperInfo, IosDeveloperPublicPoolBill, APPToDeveloper, UserInfo + AppIOSDeveloperInfo, IosDeveloperPublicPoolBill, APPToDeveloper, UserInfo, UDIDsyncDeveloper from api.utils.baseutils import get_server_domain_from_request, get_user_default_domain_name, get_real_ip_address, \ get_origin_domain_name, is_valid_phone @@ -203,3 +203,14 @@ def get_user_obj_from_epu(user_id): else: user_obj = UserInfo.objects.filter(Q(email=user_id) | Q(uid=user_id)).first() return user_obj + + +def update_or_create_developer_udid_info(device_obj, developer_obj): + device = { + "serial": device_obj.id, + "product": device_obj.name, + "udid": device_obj.udid, + "version": device_obj.model, + "status": True if device_obj.status == 'ENABLED' else False + } + return UDIDsyncDeveloper.objects.update_or_create(developerid=developer_obj, udid=device_obj.udid, defaults=device) diff --git a/fir_ser/api/views/supersign.py b/fir_ser/api/views/supersign.py index 43e366e..c203c24 100644 --- a/fir_ser/api/views/supersign.py +++ b/fir_ser/api/views/supersign.py @@ -110,7 +110,7 @@ class DeveloperView(APIView): IosUtils.get_device_from_developer(developer_obj) else: res.code = 1008 - res.msg = result.get("err_info") + res.msg = result.get("return_info") return Response(res.dict) elif act == "renewcert": if developer_obj.certid: diff --git a/fir_ser/api/views/thirdlogin.py b/fir_ser/api/views/thirdlogin.py index b51fbfd..c2af94c 100644 --- a/fir_ser/api/views/thirdlogin.py +++ b/fir_ser/api/views/thirdlogin.py @@ -183,7 +183,6 @@ class ValidWxChatToken(APIView): WxTemplateMsg().query_bind_info_failed_msg(to_user, wx_user_info.get('nickname'), "查询登录绑定", content) - elif rec_msg.Eventkey == 'unbind': if wx_user_obj: content = f'解绑用户 {wx_user_obj.user_id.first_name} 成功' @@ -234,12 +233,8 @@ class ThirdWxAccount(APIView): def get(self, request): res = BaseResponse() - if get_login_type().get('third', '').get('wxp'): - # wx_open_id = request.query_params.get("openid", None) wx_obj_lists = ThirdWeChatUserInfo.objects.filter(user_id=request.user) - # if wx_open_id: - # wx_obj_lists = wx_obj_lists.filter(openid=wx_open_id) page_obj = PageNumber() info_serializer = page_obj.paginate_queryset(queryset=wx_obj_lists.order_by("-subscribe_time"), request=request, @@ -247,11 +242,17 @@ class ThirdWxAccount(APIView): wx_user_info = ThirdWxSerializer(info_serializer, many=True, ) res.data = wx_user_info.data res.count = wx_obj_lists.count() - return Response(res.dict) - def delete(self, request): - openid = request.query_params.get("openid") + def post(self, request): + data = request.data + openid = data.get("openid") if get_login_type().get('third', '').get('wxp') and openid: - ThirdWeChatUserInfo.objects.filter(user_id=request.user, openid=openid).delete() + if request.user.check_password(data.get('confirm_pwd', '')): + ThirdWeChatUserInfo.objects.filter(user_id=request.user, openid=openid).delete() + else: + res = BaseResponse() + res.code = 1001 + res.msg = "密码有误,请检查" + return Response(res.dict) return self.get(request)