diff --git a/fir_client/src/components/user/FirSuperSignBase.vue b/fir_client/src/components/user/FirSuperSignBase.vue index 82f0237..2e23bb7 100644 --- a/fir_client/src/components/user/FirSuperSignBase.vue +++ b/fir_client/src/components/user/FirSuperSignBase.vue @@ -79,6 +79,16 @@ 该模式下,新设备,新应用无法进行注册安装,但是已经安装的不影响 通过账户激活进行关闭维护模式 + + + + 清理数据的时候,是否同时将开发者设备禁用,默认不禁用 + @@ -202,6 +212,44 @@ 关闭 + + + + + + + + + + + + + + + + + 关闭 + + @@ -586,6 +634,17 @@ align="center" label="设备UDID" prop="udid"> + + + 该设备UDID 被分配到了 {{ scope.row.app_used_count }} 个应用 + + 点击查看分配信息 + + + {{ scope.row.udid }} + + + - 禁用 - - 启用 - + + + 禁用 + + + + + 启用 + + - - + + + + + + - - + + + + + + + width="150"> - 删除 - - - ☺ - + + + 仅删除 + + + + + + 删除并禁用设备 + + + @@ -1111,7 +1185,9 @@ export default { developer_status_choice: [], multipleSelection: [], setdeveloperstatusVisible: false, - change_developer_status: '' + change_developer_status: '', + udeviceappinfoVisible: false, + app_used_info_list: [] } }, watch: { 'dialogaddDeveloperVisible': function () { @@ -1121,6 +1197,24 @@ export default { } }, methods: { + changeDevice(info, disabled) { + this.iosudevicesFun("PUT", {developer_id: info.developer_id, udid: info.udid, disabled: disabled}) + }, + show_app_used_info(info) { + this.loading = true; + iosdevices(data => { + if (data.code === 1000) { + this.app_used_info_list = data.data; + this.udeviceappinfoVisible = true + } else { + this.$message.error("数据获取失败" + data.msg) + this.app_used_info_list = []; + } + this.loading = false; + }, { + "methods": 'GET', "data": {udid: info.udid, issuer_id: info.developer_id, page: 1, size: 200} + }) + }, setdeveloperstatusFun() { if (this.multipleSelection && this.multipleSelection.length > 0) { this.setdeveloperstatusVisible = true @@ -1273,13 +1367,13 @@ export default { return '#F50346'; } }, - udidDeleteFun(scope) { + udidDeleteFun(scope, disabled) { this.$confirm('此操作会禁用该苹果开发者账户下面的该设备,可能会导致超级签包的闪退, 是否继续?', '警告', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => { - this.iosdevicesudidFun('DELETE', {id: scope.row.id, aid: scope.row.app_id}, scope); + this.iosdevicesudidFun('DELETE', {id: scope.row.id, aid: scope.row.app_id, disabled: disabled}, scope); }).catch(() => { this.$message({ type: 'info', @@ -1464,7 +1558,7 @@ export default { data.appnamesearch = this.appnamesearch.replace(/^\s+|\s+$/g, ""); this.iosdevicerankFun({"methods": "GET", "data": data}) } else if (tabname === "iosudevices") { - this.iosudevicesFun("GET", data, null) + this.iosudevicesFun("GET", data) } }, @@ -1559,7 +1653,7 @@ export default { } else { this.$message.error("操作失败") } - if (params.methods === 'PUT') { + if (params.methods === 'PUT' || params.methods === 'DELETE') { this.get_data_from_tabname(this.activeName, { "size": this.pagination.pagesize, "page": this.pagination.currentPage @@ -1651,7 +1745,7 @@ export default { "methods": action, "data": data }) }, - iosudevicesFun(action, data, scope) { + iosudevicesFun(action, data) { if (action !== 'GET') { this.loadingfun = this.$loading({ lock: true, @@ -1664,13 +1758,14 @@ export default { } iosudevices(data => { if (data.code === 1000) { - if (action !== "DELETE") { + if (action !== "PUT") { this.developer_udevices_lists = data.data; this.pagination.total = data.count; } else { - if (scope) { - this.app_udid_lists = removeAaary(this.app_udid_lists, scope.row) - } + this.get_data_from_tabname(this.activeName, { + "size": this.pagination.pagesize, + "page": this.pagination.currentPage + }) } } else { this.$message.error("操作失败了 " + data.msg); diff --git a/fir_ser/api/migrations/0029_auto_20220102_1522.py b/fir_ser/api/migrations/0029_auto_20220102_1522.py new file mode 100644 index 0000000..b839e7b --- /dev/null +++ b/fir_ser/api/migrations/0029_auto_20220102_1522.py @@ -0,0 +1,24 @@ +# Generated by Django 3.2.3 on 2022-01-02 15:22 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ('api', '0028_auto_20211228_1454'), + ] + + operations = [ + migrations.AddField( + model_name='appiosdeveloperinfo', + name='clean_status', + field=models.BooleanField(default=False, verbose_name='清理是否同时禁用设备ID'), + ), + migrations.AlterField( + model_name='appiosdeveloperinfo', + name='status', + field=models.SmallIntegerField( + choices=[(-1, '疑似被封'), (0, '未激活'), (1, '已激活'), (2, '协议待同意'), (3, '维护中'), (4, '证书过期'), (5, '状态异常')], + default=0, verbose_name='账户状态'), + ), + ] diff --git a/fir_ser/api/models.py b/fir_ser/api/models.py index 8c68484..1a5fd99 100644 --- a/fir_ser/api/models.py +++ b/fir_ser/api/models.py @@ -282,9 +282,11 @@ class AppIOSDeveloperInfo(models.Model): auth_type = models.SmallIntegerField(choices=auth_type_choices, default=0, verbose_name="认证类型") # 协议待同意和维护中:代表只读,不可创建和注册新设备号 - status_choices = ((0, '未激活'), (1, '已激活'), (2, '协议待同意'), (3, '维护中'), (4, '证书过期'), (5, '状态异常')) + status_choices = ((-1, '疑似被封'), (0, '未激活'), (1, '已激活'), (2, '协议待同意'), (3, '维护中'), (4, '证书过期'), (5, '状态异常')) status = models.SmallIntegerField(choices=status_choices, verbose_name="账户状态", default=0) + clean_status = models.BooleanField(verbose_name="清理是否同时禁用设备ID", default=False) + class Meta: verbose_name = '苹果开发者账户' verbose_name_plural = "苹果开发者账户" diff --git a/fir_ser/api/utils/app/iossignapi.py b/fir_ser/api/utils/app/iossignapi.py index 4d4b65e..2e4cbf4 100644 --- a/fir_ser/api/utils/app/iossignapi.py +++ b/fir_ser/api/utils/app/iossignapi.py @@ -8,6 +8,7 @@ import datetime import logging import os import re +import time from OpenSSL.crypto import (load_pkcs12, dump_certificate_request, dump_privatekey, PKey, TYPE_RSA, X509Req, dump_certificate, load_privatekey, load_certificate, PKCS12, FILETYPE_PEM, FILETYPE_ASN1) @@ -212,6 +213,10 @@ def check_error_call_back(error, developer_pk): msg = '请登录 https://developer.apple.com/account/ 并同意最新协议' status = 2 if status is not None: + developer_obj = AppIOSDeveloperInfo.objects.filter(pk=developer_pk).first() + if developer_obj: + if developer_obj.status == -1 and status == 5: + status = -1 AppIOSDeveloperInfo.objects.filter(pk=developer_pk).update(status=status) logger.error(f"{msg} {error}") return msg if msg else error @@ -225,6 +230,28 @@ class AppDeveloperApiV2(object): self.cert_id = cert_id self.developer_pk = developer_pk + def __getattribute__(self, name): + attr = object.__getattribute__(self, name) + if hasattr(attr, '__call__'): + def func(*args, **kwargs): + if attr.__name__ in ['active', 'get_device']: + return attr(*args, **kwargs) + else: + if AppIOSDeveloperInfo.objects.filter(pk=self.developer_pk, status__in=[1, 3, 4]).first(): + start_time = time.time() + logger.info(f'{self.issuer_id} calling {attr.__name__} time:{start_time}') + result = attr(*args, **kwargs) + logger.info(f'{self.issuer_id} done {attr.__name__} used time:{time.time() - start_time}') + return result + else: + result = False, {'return_info': '开发者状态异常'} + logger.warning(f'{self.issuer_id} can not calling {attr.__name__} {result}') + return result + + return func + else: + return attr + def active(self): result = {'data': []} try: diff --git a/fir_ser/api/utils/app/supersignutils.py b/fir_ser/api/utils/app/supersignutils.py index 44f238d..3994674 100644 --- a/fir_ser/api/utils/app/supersignutils.py +++ b/fir_ser/api/utils/app/supersignutils.py @@ -845,7 +845,7 @@ class IosUtils(object): return True, d_result @staticmethod - def disable_udid(udid_obj, app_id): + def disable_udid(udid_obj, app_id, disabled=False): usedeviceobj = APPSuperSignUsedInfo.objects.filter(udid=udid_obj, app_id_id=app_id) if usedeviceobj: @@ -853,7 +853,7 @@ class IosUtils(object): # 需要判断该设备在同一个账户下 的多个应用,若存在,则不操作 udid_lists = list( APPSuperSignUsedInfo.objects.values_list("udid__udid__udid").filter(developerid=developer_obj)) - IosUtils.do_disable_device(developer_obj, udid_lists, udid_obj) + IosUtils.do_disable_device(developer_obj, udid_lists, udid_obj, disabled) DeveloperDevicesID.objects.filter(udid__appudid=udid_obj, developerid=developer_obj, app_id_id=app_id).delete() udid_obj.delete() @@ -865,13 +865,27 @@ class IosUtils(object): delete_app_profile_file(developer_obj, app_obj) @staticmethod - def do_disable_device(developer_obj, udid_lists, udid_obj): - if udid_lists.count((udid_obj.udid.udid,)) == 1: + def do_disable_device(developer_obj, udid_lists, udid_obj, disabled): + if udid_lists.count((udid_obj.udid.udid,)) == 1 and disabled: app_api_obj = get_api_obj(developer_obj) app_api_obj.set_device_status("disable", udid_obj.udid.serial, udid_obj.udid.product, udid_obj.udid.udid, udid_obj.udid.udid) UDIDsyncDeveloper.objects.filter(udid=udid_obj.udid.udid, developerid=developer_obj).update(status=False) + @staticmethod + def do_enable_device_by_sync(developer_obj, udid_sync_obj): + app_api_obj = get_api_obj(developer_obj) + app_api_obj.set_device_status("enable", udid_sync_obj.serial, udid_sync_obj.product, udid_sync_obj.udid, + udid_sync_obj.udid) + UDIDsyncDeveloper.objects.filter(pk=udid_sync_obj.pk, developerid=developer_obj).update(status=True) + + @staticmethod + def do_disable_device_by_sync(developer_obj, udid_sync_obj): + app_api_obj = get_api_obj(developer_obj) + app_api_obj.set_device_status("disable", udid_sync_obj.serial, udid_sync_obj.product, udid_sync_obj.udid, + udid_sync_obj.udid) + UDIDsyncDeveloper.objects.filter(pk=udid_sync_obj.pk, developerid=developer_obj).update(status=False) + @staticmethod def clean_udid_by_app_obj(app_obj, developer_obj): @@ -882,7 +896,7 @@ class IosUtils(object): for SuperSignUsed_obj in APPSuperSignUsedInfo.objects.filter(app_id=app_obj, developerid=developer_obj): try: udid_obj = SuperSignUsed_obj.udid - IosUtils.do_disable_device(developer_obj, udid_lists, udid_obj) + IosUtils.do_disable_device(developer_obj, udid_lists, udid_obj, developer_obj.clean_status) SuperSignUsed_obj.delete() DeveloperDevicesID.objects.filter(udid__appudid=udid_obj, developerid=developer_obj, app_id=app_obj).delete() diff --git a/fir_ser/api/utils/serializer.py b/fir_ser/api/utils/serializer.py index 0ea36b4..270299c 100644 --- a/fir_ser/api/utils/serializer.py +++ b/fir_ser/api/utils/serializer.py @@ -640,6 +640,12 @@ class DeveloperDeviceSerializer(serializers.ModelSerializer): developer_description = serializers.CharField(source="developerid.description") developer_status = serializers.CharField(source="developerid.get_status_display") + app_used_count = serializers.SerializerMethodField() + + def get_app_used_count(self, obj): + return models.DeveloperDevicesID.objects.filter(udid=obj, developerid=obj.developerid).values( + 'app_id').distinct().count() + class DeviceUDIDSerializer(serializers.ModelSerializer): class Meta: diff --git a/fir_ser/api/views/supersign.py b/fir_ser/api/views/supersign.py index de9b0bd..15fdf30 100644 --- a/fir_ser/api/views/supersign.py +++ b/fir_ser/api/views/supersign.py @@ -14,7 +14,7 @@ from rest_framework.response import Response from rest_framework.views import APIView from api.models import AppIOSDeveloperInfo, APPSuperSignUsedInfo, AppUDID, IosDeveloperPublicPoolBill, \ - UDIDsyncDeveloper, AppleDeveloperToAppUse, Apps, DeveloperAppID, APPToDeveloper + UDIDsyncDeveloper, AppleDeveloperToAppUse, Apps, DeveloperAppID, APPToDeveloper, DeveloperDevicesID from api.utils.app.supersignutils import IosUtils from api.utils.auth import ExpiringTokenAuthentication, SuperSignPermission from api.utils.modelutils import get_user_public_used_sign_num, get_user_public_sign_num @@ -198,6 +198,10 @@ class DeveloperView(APIView): developer_obj.description = data.get("description", developer_obj.description) update_fields.append("description") + + developer_obj.clean_status = data.get("clean_status", developer_obj.clean_status) + update_fields.append("clean_status") + private_key_id = data.get("private_key_id", developer_obj.private_key_id) p8key = data.get("p8key", developer_obj.p8key) if private_key_id != "" and private_key_id != developer_obj.private_key_id: @@ -215,7 +219,7 @@ class DeveloperView(APIView): update_fields.append("status") try: - update_fields.append("status") + # update_fields.append("status") developer_obj.save(update_fields=update_fields) logger.info( f"user {request.user} ios developer {developer_obj} update now data {developer_obj.__dict__}") @@ -340,12 +344,17 @@ class AppUDIDUsedView(APIView): res = BaseResponse() pk = request.query_params.get("id", None) app_id = request.query_params.get("aid", None) - app_udid_obj = AppUDID.objects.filter(pk=pk) + disabled = request.query_params.get("disabled", None) + if disabled is not None and disabled == '1': + disabled = True + else: + disabled = False + app_udid_obj = AppUDID.objects.filter(pk=pk, app_id__user_id=request.user) if app_udid_obj: super_sign_used_obj = APPSuperSignUsedInfo.objects.filter(udid=app_udid_obj.first()).first() if super_sign_used_obj and super_sign_used_obj.developerid.user_id.pk == request.user.pk: logger.error(f"user {request.user} delete devices {app_udid_obj}") - IosUtils.disable_udid(app_udid_obj.first(), app_id) + IosUtils.disable_udid(app_udid_obj.first(), app_id, disabled) app_udid_obj.delete() else: res.code = 10002 @@ -376,20 +385,31 @@ class DeveloperDeviceView(APIView): res.count = super_sign_used_objs.count() return Response(res.dict) - def delete(self, request): + def put(self, request): res = BaseResponse() - pk = request.query_params.get("id", None) - app_id = request.query_params.get("aid", None) - app_udid_obj = AppUDID.objects.filter(pk=pk, app_id__user_id=request.user) - if app_udid_obj: - super_sign_used_obj = APPSuperSignUsedInfo.objects.filter(udid=app_udid_obj.first()).first() - if super_sign_used_obj and super_sign_used_obj.developerid.user_id.pk == request.user.pk: - logger.error(f"user {request.user} delete devices {app_udid_obj}") - IosUtils.disable_udid(app_udid_obj.first(), app_id) - app_udid_obj.delete() + developer_id = request.data.get("developer_id", None) + udid = request.data.get("udid", None) + disabled = request.data.get("disabled", None) + if developer_id and udid and disabled is not None and disabled in [0, 1]: + developer_obj = AppIOSDeveloperInfo.objects.filter(user_id=request.user, issuer_id=developer_id).first() + udid_sync_obj_queryset = UDIDsyncDeveloper.objects.filter(udid=udid, developerid=developer_obj).all() + + if disabled == 1: + for udid_sync_obj in udid_sync_obj_queryset: + IosUtils.do_enable_device_by_sync(developer_obj, udid_sync_obj) else: - res.code = 10002 - res.msg = '公共账号池不允许删除' + app_udid_obj_list = AppUDID.objects.filter(udid__udid=udid, app_id__user_id=request.user, + udid__developerid=developer_obj).all() + for app_udid_obj in app_udid_obj_list: + for app_id_info in DeveloperDevicesID.objects.filter(udid=app_udid_obj.udid, + developerid=developer_obj).values( + 'app_id').all().distinct(): + logger.error(f"user {request.user} delete devices {app_udid_obj}") + IosUtils.disable_udid(app_udid_obj, app_id_info.get('app_id'), True) + AppUDID.objects.filter(pk=app_udid_obj.pk).delete() + + for udid_sync_obj in udid_sync_obj_queryset: + IosUtils.do_disable_device_by_sync(developer_obj, udid_sync_obj) return Response(res.dict)
该设备UDID 被分配到了 {{ scope.row.app_used_count }} 个应用