超级签名并发优化,签名余额还未限制,下个版本限制

qrnn
youngS 3 years ago
parent 169347ca16
commit a2c5d1bd3f
  1. 2
      fir_client/vue.config.js
  2. 1
      fir_ser/api/tasks.py
  3. 105
      fir_ser/api/utils/app/iossignapi.py
  4. 505
      fir_ser/api/utils/app/supersignutils.py
  5. 5
      fir_ser/api/utils/baseutils.py
  6. 28
      fir_ser/api/utils/storage/caches.py
  7. 6
      fir_ser/api/views/apps.py
  8. 37
      fir_ser/api/views/receiveudids.py
  9. 9
      fir_ser/api/views/uploads.py
  10. 13
      fir_ser/fir_ser/settings.py
  11. 92
      fir_ser/tests/postudid.py

@ -82,7 +82,7 @@ if (page) {
}
}
const version='1.2.8';
const version='1.3.1';
const pro_base_env = {
baseUrl: 'https://flyapps.cn',

@ -26,7 +26,6 @@ def run_sign_task(format_udid_info, short, client_ip):
ios_obj = IosUtils(format_udid_info, app_obj.user_id, app_obj)
if ios_obj.developer_obj is None:
return '签名余额不足'
with cache.lock("%s_%s_%s" % ('run_sign_task', app_obj.app_id, ios_obj.developer_obj.issuer_id), timeout=60 * 10):
ios_obj.get_developer_auth()
status, msg = ios_obj.sign_ipa(client_ip)
if not status:

@ -295,54 +295,6 @@ class AppDeveloperApiV2(object):
result['return_info'] = "%s" % e
return False, result
def get_profile(self, app_obj, udid_info, provision_name, auth, developer_app_id,
device_id_list, profile_id, device_err_callback, app_id_err_callback):
result = {}
bundle_id = app_obj.bundle_id
app_id = app_obj.app_id
s_type = app_obj.supersign_type
try:
apple_obj = AppStoreConnectApi(self.issuer_id, self.private_key_id, self.p8key)
if developer_app_id:
pass
else:
if s_type == 0:
bundle_obj = apple_obj.register_bundle_id(app_id, f"{bundle_id}.{self.issuer_id}.{app_id}")
else:
bundle_obj = apple_obj.register_bundle_id_enable_capability(app_id,
f"{bundle_id}.{self.issuer_id}.{app_id}",
s_type)
developer_app_id = bundle_obj.id
result['aid'] = developer_app_id
if udid_info:
device_udid = udid_info.get('udid')
device_name = udid_info.get('product')
device_obj = apple_obj.register_device(device_name, device_udid)
if device_obj:
result['did'] = device_obj.id
device_id_list.append(device_obj.id)
profile_obj = apple_obj.create_profile(profile_id, developer_app_id, auth.get('cert_id'),
provision_name.split("/")[-1],
device_id_list)
if profile_obj:
result['profile_id'] = profile_obj.id
n = base64.b64decode(profile_obj.profileContent)
if not os.path.isdir(os.path.dirname(provision_name)):
os.makedirs(os.path.dirname(provision_name))
with open(provision_name, 'wb') as f:
f.write(n)
return True, result
except Exception as e:
logger.error(f"app_id {app_obj.app_id} ios developer make profile Failed Exception:{e}")
result['return_info'] = "%s" % e
if "There are no current ios devices" in str(e):
device_err_callback()
if "There is no App ID with ID" in str(e):
app_id_err_callback()
return False, result
def del_profile(self, profile_id, profile_name):
result = {}
try:
@ -354,7 +306,7 @@ class AppDeveloperApiV2(object):
result['return_info'] = "%s" % e
return False, result
def set_device_status(self, status, device_id, device_name, device_udid):
def set_device_status(self, status, device_id, device_name, device_udid, device_err_callback=None):
result = {}
try:
apple_obj = AppStoreConnectApi(self.issuer_id, self.private_key_id, self.p8key)
@ -368,6 +320,8 @@ class AppDeveloperApiV2(object):
except Exception as e:
logger.error("ios developer set devices status Failed Exception:%s" % e)
result['return_info'] = "%s" % e
if device_err_callback and ("There are no current ios devices" in str(e) or "Device obj is None" in str(e)):
device_err_callback()
return False, result
def get_device(self):
@ -394,21 +348,66 @@ class AppDeveloperApiV2(object):
result['return_info'] = "%s" % e
return False, result
# 该方法未使用
def create_app(self, bundle_id, app_id, s_type):
def create_app(self, bundle_id, app_id, s_type, app_id_err_callback=None):
if app_id_err_callback is None:
app_id_err_callback = []
result = {}
try:
result = {}
apple_obj = AppStoreConnectApi(self.issuer_id, self.private_key_id, self.p8key)
if s_type == 0:
bundle_obj = apple_obj.register_bundle_id(app_id, f"{bundle_id}.{self.issuer_id}.{app_id}")
else:
bundle_obj = apple_obj.register_bundle_id_enable_capability(app_id,
f"{bundle_id}.{self.issuer_id}.{app_id}",
s_type)
developer_app_id = bundle_obj.id
result['aid'] = developer_app_id
result['aid'] = bundle_obj.id
return True, result
except Exception as e:
logger.error("ios developer create app Failed Exception:%s" % e)
result['return_info'] = "%s" % 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()
return False, result
def register_device(self, device_udid, device_name, device_err_callback=None):
result = {}
try:
apple_obj = AppStoreConnectApi(self.issuer_id, self.private_key_id, self.p8key)
return True, apple_obj.register_device(device_name, device_udid)
except Exception as e:
logger.error("ios developer register device Failed Exception:%s" % e)
result['return_info'] = "%s" % e
if device_err_callback and "There are no current ios devices" in str(e):
device_err_callback()
return False, result
def make_and_download_profile(self, app_obj, provision_name, auth, developer_app_id, device_id_list, profile_id,
app_id_err_callback=None):
if app_id_err_callback is None:
app_id_err_callback = []
result = {}
try:
apple_obj = AppStoreConnectApi(self.issuer_id, self.private_key_id, self.p8key)
profile_obj = apple_obj.create_profile(profile_id, developer_app_id, auth.get('cert_id'),
provision_name.split("/")[-1],
device_id_list)
if profile_obj:
result['profile_id'] = profile_obj.id
n = base64.b64decode(profile_obj.profileContent)
if not os.path.isdir(os.path.dirname(provision_name)):
os.makedirs(os.path.dirname(provision_name))
with open(provision_name, 'wb') as f:
f.write(n)
return True, result
except Exception as e:
logger.error(f"app_id {app_obj.app_id} ios developer make profile Failed Exception:{e}")
result['return_info'] = "%s" % 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()
return False, result
def modify_capability(self, app_obj, developer_app_id):

@ -9,6 +9,7 @@ import os
import time
import uuid
import zipfile
from functools import wraps
import xmltodict
from django.core.cache import cache
@ -22,7 +23,7 @@ from api.utils.modelutils import get_ios_developer_public_num, check_ipa_is_late
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, \
consume_user_download_times_by_app_obj
consume_user_download_times_by_app_obj, add_udid_cache_queue, get_and_clean_udid_cache_queue
from api.utils.storage.storage import Storage
from api.utils.utils import delete_app_to_dev_and_file, send_ios_developer_active_status, delete_local_files, \
download_files_form_oss, get_developer_udided
@ -53,13 +54,17 @@ def resign_by_app_id(app_obj, need_download_profile=True, force=True):
developer_app_id = developer_app_id_obj.aid
d_time = time.time()
if need_download_profile:
with cache.lock("%s_%s_%s" % ('download_profile', app_obj.app_id, developer_obj.issuer_id), timeout=60):
with cache.lock("%s_%s_%s" % ('make_and_download_profile', developer_obj.issuer_id, app_obj.app_id),
timeout=60):
IosUtils.modify_capability(developer_obj, app_obj, developer_app_id)
download_flag, result = IosUtils.exec_download_profile(app_obj, developer_obj, None, 2)
status, download_profile_result = IosUtils.make_and_download_profile(app_obj,
developer_obj,
developer_app_id_obj)
else:
download_flag = True
status = True
with cache.lock("%s_%s_%s" % ('run_sign', app_obj.app_id, developer_obj.issuer_id), timeout=60 * 10):
status, result = IosUtils.run_sign(user_obj, app_obj, developer_obj, download_flag, '', d_time, {})
if status:
status, result = IosUtils.run_sign(user_obj, app_obj, developer_obj, d_time, None, [])
info_list.append({'developer_id': developer_obj.issuer_id, 'result': (status, result)})
return info_list
@ -194,12 +199,50 @@ def get_apple_udid_key(auth):
def err_callback(func, *args, **kwargs):
@wraps(func)
def wrapper():
return func(*args, **kwargs)
return wrapper
def disable_developer_and_send_email(app_obj, developer_obj):
logger.error(f"app {app_obj} developer {developer_obj} sign failed. so disabled")
developer_obj.is_actived = False
developer_obj.save(update_fields=['is_actived'])
send_ios_developer_active_status(developer_obj.user_id,
MSGTEMPLATE.get('ERROR_DEVELOPER') % (
developer_obj.user_id.first_name, app_obj.name,
developer_obj.issuer_id))
def call_function_try_attempts(try_attempts=3, sleep_time=2, failed_callback=None):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
res = False, {}
start_time = time.time()
for i in range(try_attempts):
res = func(*args, **kwargs)
status, result = res
if status:
return res
else:
logger.warning(f'exec {func} failed. {try_attempts} times in total. now {sleep_time} later try '
f'again...{i}')
time.sleep(sleep_time)
if not res[0]:
logger.error(f'exec {func} failed after the maximum number of attempts. Failed:{res[1]}')
if failed_callback:
failed_callback()
logger.info(f"exec {func} finished. time:{time.time() - start_time}")
return res
return wrapper
return decorator
def get_developer_user_by_app_udid(user_obj, udid):
use_device_obj = APPSuperSignUsedInfo.objects.filter(udid__udid__udid=udid,
user_id=user_obj, developerid__is_actived=True,
@ -296,73 +339,6 @@ class IosUtils(object):
provision_name = os.path.join(cert_dir_path, self.app_obj.app_id)
return provision_name + '.mobileprovision'
@staticmethod
def exec_download_profile(app_obj, developer_obj, udid_info, sign_try_attempts=3):
result = {}
developer_app_id = None
auth = get_auth_form_developer(developer_obj)
while sign_try_attempts > 0:
logger.info(
f"exec_download_profile app_id:{app_obj} developer:{developer_obj} sign_try_attempts:{sign_try_attempts}")
device_id_list = DeveloperDevicesID.objects.filter(app_id=app_obj,
developerid=developer_obj).values_list('did')
device_id_lists = [did[0] for did in device_id_list]
developer_app_id = None
profile_id = None
add_did_flag = False
sync_device_obj = None
developer_app_id_obj = DeveloperAppID.objects.filter(developerid=developer_obj,
app_id=app_obj).first()
if developer_app_id_obj:
developer_app_id = developer_app_id_obj.aid
profile_id = developer_app_id_obj.profile_id
if udid_info:
sync_device_obj = UDIDsyncDeveloper.objects.filter(udid=udid_info.get('udid'),
developerid=developer_obj, status=True).first()
if sync_device_obj:
DeveloperDevicesID.objects.update_or_create(did=sync_device_obj.serial, udid=sync_device_obj,
developerid=developer_obj,
app_id=app_obj)
device_id_lists.append(sync_device_obj.serial)
udid_info = None
add_did_flag = True
logger.info(f"app {app_obj} device {sync_device_obj.serial} already in developer {developer_obj}")
status, result = get_api_obj(auth).get_profile(app_obj, udid_info,
get_profile_full_path(developer_obj, app_obj),
auth, developer_app_id, device_id_lists, profile_id,
err_callback(IosUtils.get_device_from_developer,
developer_obj),
err_callback(IosUtils.clean_super_sign_things_by_app_obj,
app_obj, developer_obj))
if add_did_flag and sync_device_obj:
result['did'] = sync_device_obj.serial
result['did_exists'] = True
if not status:
sign_try_attempts -= 1
logger.warning(f"app {app_obj} developer {developer_obj} sign failed {result} .try again ")
time.sleep(3)
else:
sign_try_attempts = -1
if sign_try_attempts != -1:
logger.error(f"app {app_obj} developer {developer_obj} sign failed {result}")
developer_obj.is_actived = False
developer_obj.save(update_fields=['is_actived'])
send_ios_developer_active_status(developer_obj.user_id,
MSGTEMPLATE.get('ERROR_DEVELOPER') % (
developer_obj.user_id.first_name, app_obj.name,
developer_obj.issuer_id))
return False, result
if not developer_app_id and result.get("aid", None):
DeveloperAppID.objects.create(aid=result["aid"], developerid=developer_obj, app_id=app_obj)
if result.get("profile_id", None):
DeveloperAppID.objects.filter(developerid=developer_obj, app_id=app_obj).update(
profile_id=result.get("profile_id", None))
return True, result
@staticmethod
def zip_cert(user_obj, developer_obj):
auth = get_auth_form_developer(developer_obj)
@ -407,8 +383,8 @@ class IosUtils(object):
return False, result
@staticmethod
def update_sign_file_name(user_obj, app_obj, developer_obj, release_obj, random_file_name):
apptodev_obj = APPToDeveloper.objects.filter(developerid=developer_obj, app_id=app_obj).first()
def update_sign_file_name(user_obj, app_obj, developer_obj_id, release_obj, random_file_name):
apptodev_obj = APPToDeveloper.objects.filter(developerid_id=developer_obj_id, app_id=app_obj).first()
storage_obj = Storage(user_obj)
if apptodev_obj:
delete_local_files(apptodev_obj.binary_file + ".ipa")
@ -421,76 +397,47 @@ class IosUtils(object):
logger.warning(f"update sign file , now clean ole {old_release_file}.ipa file")
delete_local_files(old_release_file + ".ipa")
else:
APPToDeveloper.objects.create(developerid=developer_obj, app_id=app_obj,
APPToDeveloper.objects.create(developerid_id=developer_obj_id, app_id=app_obj,
binary_file=random_file_name, release_file=release_obj.release_id)
logger.info(f"update sign file end, now upload {storage_obj.storage} {random_file_name}.ipa file")
return storage_obj.upload_file(os.path.join(MEDIA_ROOT, random_file_name + ".ipa"))
def update_developer_used_data(self, client_ip):
udid_obj = AppUDID.objects.filter(app_id=self.app_obj, udid__udid=self.udid,
udid__developerid=self.developer_obj).first()
@staticmethod
def update_developer_used_data(user_obj, app_obj, developer_obj, udid, client_ip):
udid_obj = AppUDID.objects.filter(app_id=app_obj, udid__udid=udid,
udid__developerid=developer_obj).first()
udid_obj.is_download = True
udid_obj.save(update_fields=['is_download'])
APPSuperSignUsedInfo.objects.update_or_create(app_id=self.app_obj,
user_id=self.user_obj,
developerid=self.developer_obj,
APPSuperSignUsedInfo.objects.update_or_create(app_id=app_obj,
user_id=user_obj,
developerid=developer_obj,
udid=udid_obj)
udid_sync_info = UDIDsyncDeveloper.objects.filter(developerid=self.developer_obj,
udid=self.udid).first()
IosDeveloperPublicPoolBill.objects.update_or_create(user_id=self.user_obj,
app_info=BillAppInfoSerializer(self.app_obj).data,
udid_sync_info = UDIDsyncDeveloper.objects.filter(developerid=developer_obj,
udid=udid).first()
IosDeveloperPublicPoolBill.objects.update_or_create(user_id=user_obj,
app_info=BillAppInfoSerializer(app_obj).data,
developer_info=BillDeveloperInfoSerializer(
self.developer_obj).data,
developer_obj).data,
action=0, number=1, udid_sync_info=udid_sync_info,
remote_addr=client_ip, product=udid_sync_info.product,
udid=self.udid, version=udid_sync_info.version,
app_id=self.app_obj
udid=udid, version=udid_sync_info.version,
app_id=app_obj
)
@staticmethod
def update_sign_data(user_obj, app_obj, developer_obj, random_file_name, release_obj, udid):
def update_sign_data(user_obj, app_obj, developer_obj_id, random_file_name, release_obj, udid_list):
# 更新新签名的ipa包
if IosUtils.update_sign_file_name(user_obj, app_obj, developer_obj, release_obj,
if IosUtils.update_sign_file_name(user_obj, app_obj, developer_obj_id, release_obj,
random_file_name):
if udid:
udid_obj = UDIDsyncDeveloper.objects.filter(developerid=developer_obj, udid=udid).first()
if udid_list:
for udid in udid_list:
udid_obj = UDIDsyncDeveloper.objects.filter(developerid_id=developer_obj_id, udid=udid).first()
AppUDID.objects.filter(app_id=app_obj, udid=udid_obj).update(**{"is_signed": True})
del_cache_response_by_short(app_obj.app_id, udid=udid)
return True
@staticmethod
def run_sign(user_obj, app_obj, developer_obj, download_flag, udid, d_time, result):
d_result = {'code': 0, 'msg': 'success'}
start_time = time.time()
if download_flag:
logger.info(f"app_id {app_obj} download profile success. time:{start_time - d_time}")
random_file_name = make_from_user_uuid(developer_obj.user_id)
release_obj = AppReleaseInfo.objects.filter(app_id=app_obj, is_master=True).first()
status, e_result = IosUtils.exec_sign(developer_obj.user_id, app_obj, developer_obj, random_file_name,
release_obj)
if status:
s_time1 = time.time()
logger.info(f"app_id {app_obj} exec sign ipa success. time:{s_time1 - start_time}")
if not IosUtils.update_sign_data(user_obj, app_obj, developer_obj, random_file_name, release_obj,
udid):
d_result['code'] = 1004
d_result['msg'] = '数据更新失败,请稍后重试'
return status, d_result
else:
d_result['code'] = 1004
d_result['msg'] = '签名失败,请检查包是否正常'
return status, d_result
else:
msg = f"app_id {app_obj} download profile failed. {result} time:{time.time() - start_time}"
d_result['code'] = 1002
d_result['msg'] = msg
logger.error(d_result)
return False, d_result
msg = f"app_id {app_obj} developer {developer_obj} sign end... time:{time.time() - start_time}"
logger.info(msg)
d_result['msg'] = msg
return True, d_result
del_cache_response_by_short(app_obj.app_id)
return True
def check_sign_permission(self):
d_result = {'code': 0, 'msg': 'success'}
@ -514,16 +461,7 @@ class IosUtils(object):
return False, d_result
return True, d_result
def sign_ipa(self, client_ip, sign_try_attempts=3):
"""
:param client_ip:
:param sign_try_attempts:
:return: status, result
:des: 仅用于苹果设备发送设备udid签名使用
"""
status, msg = self.check_sign_permission()
if not status:
return status, msg
def check_sign_is_exists(self, client_ip):
d_result = {'code': 0, 'msg': 'success'}
app_udid_obj = AppUDID.objects.filter(app_id=self.app_obj, udid__udid=self.udid,
udid__developerid=self.developer_obj).first()
@ -543,9 +481,148 @@ class IosUtils(object):
with cache.lock("%s_%s_%s" % ('run_sign', self.app_obj.app_id, self.developer_obj.issuer_id),
timeout=60 * 10):
logger.info("start run_sign ...")
return IosUtils.run_sign(self.user_obj, self.app_obj, self.developer_obj, 1,
self.udid, time.time(),
None)
return IosUtils.run_sign(self.user_obj, self.app_obj, self.developer_obj,
time.time(), client_ip, [self.udid])
@staticmethod
@call_function_try_attempts()
def check_or_register_devices(app_obj, developer_obj, udid_info):
"""
:param app_obj:
:param developer_obj:
:param udid_info:
:return:
"""
device_udid = udid_info.get('udid')
device_name = udid_info.get('product')
if not device_udid:
logger.error("device udid is not exists. so return and exit")
return True, 'continue'
sync_device_obj = UDIDsyncDeveloper.objects.filter(udid=device_udid,
developerid=developer_obj).first()
auth = get_auth_form_developer(developer_obj)
# 库里面存在,并且设备是可用状态,因此无需api注册
if sync_device_obj:
logger.info(f"app {app_obj} device {sync_device_obj.serial} already in developer {developer_obj}")
if not sync_device_obj.status:
# 库里面存在,并且设备是禁用状态,需要调用api启用
status, result = get_api_obj(auth).set_device_status("enable", sync_device_obj.serial,
sync_device_obj.product, sync_device_obj.udid,
err_callback(IosUtils.get_device_from_developer,
developer_obj))
if not status: # 已经包含异常操作,暂定
return status, result
sync_device_obj.status = True
sync_device_obj.save(update_fields=['status'])
else:
# 库里面不存在,注册设备,新设备注册默认就是启用状态
status, device_obj = get_api_obj(auth).register_device(device_udid, device_name,
err_callback(IosUtils.get_device_from_developer,
developer_obj))
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)
# 更新设备状态
# 1. UDIDsyncDeveloper 库,通过udid 更新或创建设备信息
# 2. DeveloperDevicesID 添加新应用-设备绑定信息数据
# 3. AppUDID 添加数据,该数据主要是为了记录使用,is_signed 判断是否已经签名成功
del udid_info['udid']
udid_info['is_signed'] = False
udid_info['is_download'] = False
AppUDID.objects.update_or_create(app_id=app_obj, udid=sync_device_obj,
defaults=udid_info)
DeveloperDevicesID.objects.update_or_create(did=sync_device_obj.serial, udid=sync_device_obj,
developerid=developer_obj,
app_id=app_obj)
return True, (sync_device_obj.serial, sync_device_obj.udid)
@staticmethod
@call_function_try_attempts()
def check_or_add_new_bundles(app_obj, developer_obj, failed_callback=None):
if failed_callback is None:
failed_callback = []
failed_callback.append(err_callback(IosUtils.clean_super_sign_things_by_app_obj,
app_obj, developer_obj))
developer_app_id_obj = DeveloperAppID.objects.filter(developerid=developer_obj,
app_id=app_obj).first()
if not developer_app_id_obj:
auth = get_auth_form_developer(developer_obj)
bundle_id = app_obj.bundle_id
app_id = app_obj.app_id
s_type = app_obj.supersign_type
status, result = get_api_obj(auth).create_app(bundle_id, app_id, s_type, failed_callback)
if status and result.get('aid'):
developer_app_id_obj = DeveloperAppID.objects.create(aid=result.get('aid'), developerid=developer_obj,
app_id=app_obj)
else:
return status, result
return True, developer_app_id_obj
@staticmethod
@call_function_try_attempts()
def make_and_download_profile(app_obj, developer_obj, developer_app_id_obj=None, new_device_id_list=None,
failed_callback=None):
if new_device_id_list is None:
new_device_id_list = []
if failed_callback is None:
failed_callback = []
failed_callback.append(err_callback(IosUtils.clean_super_sign_things_by_app_obj,
app_obj, developer_obj))
device_id_list = DeveloperDevicesID.objects.filter(app_id=app_obj,
developerid=developer_obj).values_list('did')
device_id_lists = [did[0] for did in device_id_list]
if new_device_id_list:
device_id_lists.extend(new_device_id_list)
device_id_lists = list(set(device_id_lists))
if not developer_app_id_obj:
developer_app_id_obj = DeveloperAppID.objects.filter(developerid=developer_obj, app_id=app_obj).first()
if not developer_app_id_obj:
logger.error("bundle id not found. so return and exit")
return True, 'continue'
developer_app_id = developer_app_id_obj.aid
profile_id = developer_app_id_obj.profile_id
auth = get_auth_form_developer(developer_obj)
status, result = get_api_obj(auth).make_and_download_profile(app_obj,
get_profile_full_path(developer_obj, app_obj),
auth, developer_app_id,
device_id_lists, profile_id, failed_callback)
if not status:
return False, result
if result.get("profile_id", None):
DeveloperAppID.objects.filter(developerid=developer_obj, app_id=app_obj).update(
profile_id=result.get("profile_id", None))
return True, True
def sign_failed_fun(self, d_result, msg):
d_result['code'] = 1002
d_result['msg'] = msg
logger.error(d_result)
disable_developer_and_send_email(self.app_obj, self.developer_obj)
self.get_developer_auth()
def sign_ipa(self, client_ip):
status, msg = self.check_sign_permission()
if not status:
return status, msg
d_result = {'code': 0, 'msg': 'success'}
result = self.check_sign_is_exists(client_ip)
if result:
return result
logger.info("udid %s not exists app_id %s ,need sign" % (self.udid, self.app_obj))
@ -556,79 +633,101 @@ class IosUtils(object):
return False, d_result
call_flag = True
download_flag = False
count = 1
result = {}
count = 0
start_time = time.time()
while call_flag:
logger.info(
count += 1
if count > 3:
break
logger.warning(
"call_loop download_profile appid:%s developer:%s count:%s" % (self.app_obj, self.developer_obj, count))
if self.developer_obj:
with cache.lock("%s_%s_%s" % ('download_profile', self.app_obj.app_id, self.developer_obj.issuer_id),
timeout=180):
with cache.lock("%s_%s_%s" % ('check_or_register_devices', self.developer_obj.issuer_id, self.udid),
timeout=60):
status, did_udid_result = IosUtils.check_or_register_devices(self.app_obj, self.developer_obj,
self.udid_info)
if not status:
msg = f"app_id {self.app_obj} register devices failed. {did_udid_result}"
self.sign_failed_fun(d_result, msg)
continue
if status and 'continue' in [str(did_udid_result)]:
return True, True
with cache.lock("%s_%s_%s" % (
'check_or_add_new_bundles', self.developer_obj.issuer_id, self.app_obj.app_id),
timeout=60):
status, bundle_result = IosUtils.check_or_add_new_bundles(self.app_obj, self.developer_obj)
if not status:
msg = f"app_id {self.app_obj} create bundles failed. {bundle_result}"
self.sign_failed_fun(d_result, msg)
continue
prefix_key = f"{self.developer_obj.issuer_id}-{self.app_obj.app_id}"
if 'continue' not in [str(did_udid_result)]:
add_udid_cache_queue(prefix_key, did_udid_result)
sync_device_obj = UDIDsyncDeveloper.objects.filter(udid=self.udid,
developerid=self.developer_obj,
status=True).first()
if sync_device_obj:
download_flag, result = IosUtils.exec_download_profile(self.app_obj, self.developer_obj,
self.udid_info, sign_try_attempts)
else:
time.sleep(5)
with cache.lock("%s_%s_%s" % (
'download_profile_and_add_device', self.developer_obj.issuer_id, self.udid),
timeout=120):
download_flag, result = IosUtils.exec_download_profile(self.app_obj, self.developer_obj,
self.udid_info, sign_try_attempts)
if download_flag:
call_flag = False
else:
self.get_developer_auth()
else:
call_flag = False
count += 1
if download_flag:
app_udid_obj = UDIDsyncDeveloper.objects.filter(developerid=self.developer_obj,
udid=self.udid)
udid_info = self.udid_info
del udid_info['udid']
AppUDID.objects.update_or_create(app_id=self.app_obj, udid=app_udid_obj.first(),
defaults=udid_info)
if not result.get("did_exists", None):
app_udid_obj = app_udid_obj.filter(status=True).first()
if not app_udid_obj:
appudid_obj = AppUDID.objects.filter(app_id=self.app_obj, udid__udid=self.udid,
udid__developerid=self.developer_obj)
device = appudid_obj.values("serial",
'product',
'udid__udid',
'version').first()
if result.get("did", None):
device['serial'] = result["did"]
device['status'] = True
app_udid_obj, _ = UDIDsyncDeveloper.objects.update_or_create(developerid=self.developer_obj,
udid=device['udid__udid'],
defaults=device)
DeveloperDevicesID.objects.create(did=result["did"], udid=app_udid_obj,
developerid=self.developer_obj,
app_id=self.app_obj)
'make_and_download_profile', self.developer_obj.issuer_id, self.app_obj.app_id),
timeout=60 * 10):
did_udid_lists = get_and_clean_udid_cache_queue(prefix_key)
if not did_udid_lists:
return True, True
logger.warning(
f'app {self.app_obj} receive {len(did_udid_lists)} did_udid_result: {did_udid_lists}')
status, download_profile_result = IosUtils.make_and_download_profile(self.app_obj,
self.developer_obj,
new_device_id_list=[did_udid[0]
for
did_udid in
did_udid_lists])
if not status:
msg = f"app_id {self.app_obj} download profile failed. {download_profile_result}"
self.sign_failed_fun(d_result, msg)
continue
else:
if result.get("did", None):
DeveloperDevicesID.objects.create(did=result["did"], udid=app_udid_obj,
developerid=self.developer_obj,
app_id=self.app_obj)
self.update_developer_used_data(client_ip)
if 'continue' in [str(did_udid_result), str(download_profile_result)]:
return True, True
return IosUtils.run_sign(self.user_obj, self.app_obj, self.developer_obj,
start_time, client_ip,
[did_udid[1] for did_udid in did_udid_lists])
if not self.developer_obj:
d_result['code'] = 1005
d_result['msg'] = '未找到可用苹果开发者'
logger.error(d_result)
return False, d_result
with cache.lock("%s_%s_%s" % ('run_sign', self.app_obj.app_id, self.developer_obj.issuer_id), timeout=60 * 10):
logger.info("start run_sign ...")
return IosUtils.run_sign(self.user_obj, self.app_obj, self.developer_obj, download_flag,
self.udid, start_time,
result)
return True, True
@staticmethod
def run_sign(user_obj, app_obj, developer_obj, d_time, client_ip, udid_list=None):
if udid_list is None:
udid_list = []
d_result = {'code': 0, 'msg': 'success'}
for udid in udid_list:
IosUtils.update_developer_used_data(user_obj, app_obj, developer_obj, udid, client_ip)
start_time = time.time()
logger.info(f"app_id {app_obj} download profile success. time:{start_time - d_time}")
random_file_name = make_from_user_uuid(developer_obj.user_id.uid)
release_obj = AppReleaseInfo.objects.filter(app_id=app_obj, is_master=True).first()
status, e_result = IosUtils.exec_sign(developer_obj.user_id, app_obj, developer_obj, random_file_name,
release_obj)
if status:
s_time1 = time.time()
logger.info(f"app_id {app_obj} exec sign ipa success. time:{s_time1 - start_time}")
if not IosUtils.update_sign_data(user_obj, app_obj, developer_obj.pk, random_file_name, release_obj,
udid_list):
d_result['code'] = 1004
d_result['msg'] = '数据更新失败,请稍后重试'
return status, d_result
else:
d_result['code'] = 1004
d_result['msg'] = '签名失败,请检查包是否正常'
return status, d_result
msg = f"app_id {app_obj} developer {developer_obj} sign end... time:{time.time() - start_time}"
logger.info(msg)
d_result['msg'] = msg
return True, d_result
@staticmethod
def disable_udid(udid_obj, app_id):
@ -699,17 +798,11 @@ class IosUtils(object):
def clean_app_by_developer_obj(app_obj, developer_obj):
auth = get_auth_form_developer(developer_obj)
developer_app_id_obj = DeveloperAppID.objects.filter(developerid=developer_obj, app_id=app_obj).first()
identifier_id = None
profile_id = None
if developer_app_id_obj:
identifier_id = developer_app_id_obj.aid
profile_id = developer_app_id_obj.profile_id
app_api_obj = get_api_obj(auth)
app_api_obj.del_profile(profile_id, app_obj.app_id)
app_api_obj.del_profile(developer_app_id_obj.profile_id, app_obj.app_id)
app_api_obj2 = get_api_obj(auth)
app_api_obj2.del_app(identifier_id, app_obj.bundle_id, app_obj.app_id)
app_api_obj2.del_app(developer_app_id_obj.aid, app_obj.bundle_id, app_obj.app_id)
developer_app_id_obj.delete()
@staticmethod

@ -56,10 +56,9 @@ class AESCipher(object):
return data
def make_from_user_uuid(userinfo):
user_id = userinfo.uid
def make_from_user_uuid(uid):
random_str = uuid.uuid1().__str__().split("-")[0:-1]
user_ran_str = uuid.uuid5(uuid.NAMESPACE_DNS, user_id).__str__().split("-")
user_ran_str = uuid.uuid5(uuid.NAMESPACE_DNS, uid).__str__().split("-")
user_ran_str.extend(random_str)
new_str = "".join(user_ran_str)
return new_str

@ -528,6 +528,10 @@ class CleanSignDataState(CacheBaseState):
...
class CleanAppSignDataState(CacheBaseState):
...
def add_download_times_free_base(user_obj, amount, payment_name, description, order_type=1):
order_number = get_order_num()
order_obj = Order.objects.create(payment_type=2, order_number=order_number, payment_number=order_number,
@ -553,3 +557,27 @@ def admin_change_user_download_times(user_obj, amount=200):
def auth_user_download_times_gift(user_obj, amount=200):
description = '实名认证赠送下载次数 %s ,现共可用次数 %s'
return add_download_times_free_base(user_obj, amount, '系统', description, 2)
def add_udid_cache_queue(prefix_key, values):
prefix_key = f"{CACHE_KEY_TEMPLATE.get('ipa_sign_udid_queue_key')}_{prefix_key}"
with cache.lock("%s_%s" % ('add_udid_cache_queue', prefix_key), timeout=10):
data = cache.get(prefix_key)
if data and isinstance(data, list):
data.append(values)
else:
data = [values]
cache.set(prefix_key, list(set(data)), 60 * 60)
return data
def get_and_clean_udid_cache_queue(prefix_key):
prefix_key = f"{CACHE_KEY_TEMPLATE.get('ipa_sign_udid_queue_key')}_{prefix_key}"
with cache.lock("%s_%s" % ('add_udid_cache_queue', prefix_key), timeout=10):
data = cache.get(prefix_key)
if data and isinstance(data, list):
...
else:
data = []
cache.delete(prefix_key)
return data

@ -19,7 +19,8 @@ from api.utils.auth import ExpiringTokenAuthentication
from api.utils.modelutils import get_user_domain_name, get_app_domain_name, check_super_sign_permission
from api.utils.response import BaseResponse
from api.utils.serializer import AppsSerializer, AppReleaseSerializer, AppsListSerializer
from api.utils.storage.caches import del_cache_response_by_short, get_app_today_download_times, del_cache_by_delete_app
from api.utils.storage.caches import del_cache_response_by_short, get_app_today_download_times, del_cache_by_delete_app, \
CleanAppSignDataState
from api.utils.storage.storage import Storage
from api.utils.utils import delete_local_files, delete_app_screenshots_files
@ -138,8 +139,11 @@ class AppInfoView(APIView):
clean = data.get("clean", None)
if clean:
logger.info(f"app_id:{app_id} clean:{clean} ,close super_sign should clean_app_by_user_obj")
CleanAppSignDataState.set_state(request.user.uid, timeout=60 * 10)
app_obj = Apps.objects.filter(user_id=request.user, app_id=app_id).first()
IosUtils.clean_app_by_user_obj(app_obj)
CleanAppSignDataState.del_state(request.user.uid)
return Response(res.dict)
has_combo = data.get("has_combo", None)

@ -22,9 +22,42 @@ from api.utils.throttle import ReceiveUdidThrottle1, ReceiveUdidThrottle2
from fir_ser.celery import app
logger = logging.getLogger(__name__)
from rest_framework import exceptions
class IosUDIDView(View):
class UdidView(View):
throttle_classes = []
def check_throttles(self, request):
"""
Check if request should be throttled.
Raises an appropriate exception if the request is throttled.
"""
throttle_durations = []
for throttle in [throttle() for throttle in self.throttle_classes]:
if not throttle.allow_request(request, self):
throttle_durations.append(throttle.wait())
if throttle_durations:
durations = [
duration for duration in throttle_durations
if duration is not None
]
duration = max(durations, default=None)
raise exceptions.Throttled(duration)
def dispatch(self, request, *args, **kwargs):
try:
self.check_throttles(request)
except Exception as e:
r_url = f"{get_redirect_server_domain(request)}/{kwargs.get('short')}?udid={str(e)}&msg=访问受限制,请稍后再试"
logger.error(f"ip:{get_real_ip_address(request)} short:{kwargs.get('short')} {str(e)}")
return HttpResponsePermanentRedirect(r_url)
return super().dispatch(request, *args, **kwargs)
class IosUDIDView(UdidView):
throttle_classes = [ReceiveUdidThrottle1, ReceiveUdidThrottle2]
def post(self, request, short):
@ -52,7 +85,7 @@ class IosUDIDView(View):
task_id = c_task.id
logger.info(f"sign app {app_obj} task_id:{task_id}")
try:
result = c_task.get(propagate=False, timeout=2)
result = c_task.get(propagate=False, timeout=0.5)
except TimeoutError:
result = ''
if c_task.successful():

@ -44,8 +44,9 @@ class AppAnalyseView(APIView):
if app_type.lower() == 'iOS'.lower():
ap = 'ipa'
app_uuid = make_app_uuid(request.user, bundle_id + ap)
release_id = make_from_user_uuid(request.user)
png_id = make_from_user_uuid(request.user)
uid = request.user.uid
release_id = make_from_user_uuid(uid)
png_id = make_from_user_uuid(uid)
app_obj = Apps.objects.filter(app_id=app_uuid).first()
binary_url = ''
enable_sign = False
@ -189,7 +190,7 @@ class UploadView(APIView):
else:
if f_type and f_type == 'certification':
storage = Storage(request.user, None, True)
upload_key = make_from_user_uuid(request.user) + '.' + app_type + settings.FILE_UPLOAD_TMP_KEY
upload_key = make_from_user_uuid(request.user.uid) + '.' + app_type + settings.FILE_UPLOAD_TMP_KEY
upload_token = storage.get_upload_token(upload_key)
storage_type = storage.get_storage_type()
res.data = {
@ -269,7 +270,7 @@ class UploadView(APIView):
res.msg = "错误的类型"
return Response(res.dict)
random_file_name = make_from_user_uuid(request.user)
random_file_name = make_from_user_uuid(request.user.uid)
local_file = os.path.join(settings.MEDIA_ROOT, cert_info.get("upload_key", random_file_name))
# 读取传入的文件
logger.info(f"user:{request.user} save file:{local_file}")

@ -215,6 +215,7 @@ CACHE_KEY_TEMPLATE = {
'super_sign_failed_send_msg_times_key': 'super_sign_failed_send_msg_times',
'wx_access_token_key': 'wx_basic_access_token',
'wx_ticket_info_key': 'wx_ticket_info',
'ipa_sign_udid_queue_key': 'ipa_sign_udid_queue',
}
DATA_DOWNLOAD_KEY = "d_token"
@ -359,16 +360,14 @@ CELERY_BROKER_URL = 'redis://:%s@%s/2' % (
#: Only add pickle to this list if your broker is secured
CELERYD_CONCURRENCY = 10 # worker并发数
CELERY_WORKER_CONCURRENCY = 30 # worker并发数
CELERYD_FORCE_EXECV = True # 非常重要,有些情况下可以防止死
CELERY_TASK_RESULT_EXPIRES = 3600 # 任务结果过期时间
CELERY_RESULT_EXPIRES = 3600 # 任务结果过期时间
CELERY_DISABLE_RATE_LIMITS = True # 任务发出后,经过一段时间还未收到acknowledge , 就将任务重新交给其他worker执行
CELERYD_PREFETCH_MULTIPLIER = 3 # celery worker 每次去redis取任务的数量
CELERY_WORKER_DISABLE_RATE_LIMITS = True # 任务发出后,经过一段时间还未收到acknowledge , 就将任务重新交给其他worker执行
CELERY_WORKER_PREFETCH_MULTIPLIER = 60 # celery worker 每次去redis取任务的数量
CELERYD_MAX_TASKS_PER_CHILD = 200 # 每个worker执行了多少任务就会死掉,我建议数量可以大一些,比如200
# CELERYD_WORKER_PREFETCH_MULTIPLIER =1
CELERY_WORKER_MAX_TASKS_PER_CHILD = 1000 # 每个worker执行了多少任务就会死掉,我建议数量可以大一些,比如200
CELERY_ENABLE_UTC = False
DJANGO_CELERY_BEAT_TZ_AWARE = False

@ -1,16 +1,16 @@
import random
import time
import requests
udid_lists = [
"75f14e4aee3dad37568a1a374f6c8b661b32d05e", "00008030-000C185C1143802E", "cadb0511abdb545d7eb7e828fd641a93e3164d02",
"efa8e5eb2a58fe65e303d1d9fad99d8b8670b6bb", "2525def0a63af76b8a09ea0dce0574d720925800",
"fdefbf544b7729db27c5294cd652e0dbb462cd22", "00008030-00122CD21A82402E", "e9f4cdc73fe24b1e20a4c33ae842d0d9ce6f8e34",
"75f14e4aee3dad37568a1a374f6c8b661b32d05e", "00008030-000C185C1143802E",
"efa8e5eb2a58fe65e303d1d9fad99d8b8670b6bb", "e9f4cdc73fe24b1e20a4c33ae842d0d9ce6f8e34",
"cd84e936355f492ee68b7182f12ce3a7d0205557", "00008101-000B05E4368B001E", "f81a3a4170cf8e6c71d525464e175049134a6dbc",
"6bae15671c8fe7096a40752071d87c6a837dd132", "7dd77d8cf13789e3f547f28ceb3e0c67b8694e37",
"53e663905fcd81887abffc0d92e5c116ea2bfe5c", "00008101-001325EE1188001E", "f55df38afe5c1242b8bc478d0182bbd0d7d08929",
"00008020-000951C40C12002E", "760862a7367dea986542dae17ee31cd139e454ad", "079d0e46eab5369184830d37cbed345f664e835a",
"00008030-000E6CC122D2402E", "00008101-0012758822A2001E", "00008020-00192CDE3A41002E",
"00008030-000E6CC122D2402E", "00008020-00192CDE3A41002E",
"e2bcc2bdf73bf815bfde88becdeee05abdebf4d1", "000f0a85cf2d0b65b7a1fa21f143516500e040e0",
"6dd86c12ab5506a1f7e45b0f58b6b1448f5cdf54", "49d8ca930a5610ba00190565ef756e24af97e63c",
"ad4016d3749277b58f6294a9496ad97fa7c88110", "6a245252fb9cca583fc84413a092e73f7afc3182",
@ -24,7 +24,7 @@ udid_lists = [
"00008101-001661290169001E", "00008020-001B78E93461002E", "a84919cf236558b0f83496302237fd7f62a212a7",
"00008101-0008093E3611001E", "00008110-00162C9A3C82801E", "638525bfd0ef1f029ed4c216f1430688093e5ac5",
"00008030-000A51AE0EFA402E", "00008110-0016082C1E32801E", "00008030-001828411163802E",
"20737b4d60c2e0ff34e32edde224e64d042f72c1", "41d1b77e3a831f82a0ca306beaceeb955280b57a"
]
udid_lists2 = [
@ -33,20 +33,19 @@ udid_lists2 = [
"00008020-00192CDE3A41002E",
"00008101-001614893A42001E",
"00008101-001449580100001E",
"00008101-0012758822A2001E",
"019e2418ebf7bebbe82b42e4255b06f60f43fdda",
"d3c7d88e66bb84b5b1b03fc70a12b568118464da",
"f55df38afe5c1242b8bc478d0182bbd0d7d08929"
]
short_list = [
"wgvn",
"ynei",
"mecg",
"plzd",
"ibhp",
"zpus",
"ucwk"
"tiok",
"semi",
"lify",
"nyvr",
"uwdy",
"ceuj",
"bqpi"
]
@ -62,8 +61,71 @@ from concurrent.futures import ThreadPoolExecutor
# postudid(udid="f55df38afe5c1242b8bc478d0182bbd0d7d08929")
# exit()
# import oss2
# tokens = {
# "access_key_id": "STS.NUrQfSoT1hxZt953jpkzpAxrD",
# "access_key_secret": "3DYz3ZVLCYaCF7mU7BT5DPdRDmCqpW1sFUwr2T6obt3X",
# "bucket": "fly001-storage",
# "endpoint": "oss-cn-beijing.aliyuncs.com",
# "expiration": 1636451222,
# "request_id": "E792C7D3-051E-5D77-8439-5F596F525827",
# "security_token": "CAISpAN1q6Ft5B2yfSjIr5bHGtzngosQ37qxdh+E12olZ/Vcrr3Zpjz2IHxKfHJtBukfs/synWtY7P4elpUoRsBMGhbINccstZoKoAqoM9Gb6pPp4OwP2c2vSTSeUEGq1ZTYPL2nEIqBI670ZluUnyQoh6P8enO+WkT9hDVoQCYgPLotRQCdQWdsq740T1IQAaZ4WFLVMfGySDeI5kPbEEtvvCdllGp78t7R68aA6x3Y/hyYr+gOvNbJP4SDZcthN4sdI9Cux75ffK3bzAtN7wRL7K5skJFc/TDOsquAYT9r7g6BLvDf//B2MQZ9fdJaIaNfq+XmnvAKi47sno/smRFWJrMXAWaNSoSxmJOdXfi1Mcwweru8YHmS24/VOsH571gqPCMSPVMRIYt+dnIgAk18QGGGJ/T4qVmRaFj7FfTcjPhviJd+n1zjppySIVfJX7CB815BYcNlNxxyZ0JHhTOwLf9eSWEWLQM7XYTyZJ5ocRVTpZnvuQDvTSB6xhlVxaaiOauN5/9GMNigA8sdgdBFOI4lvW87Ck/rTKinhUoE+J+r/3PICMMagAGS1Fzpp9+iSHjA8FQ0GKWeLqGIewHIijtDrjVAlXMikLPUiFJfZB9b0bO/9fUuFj8cmVEwF3uGsMfuaAorJ8oQzRkeVz87B67W391gwlx92P1KRtppvUxv0ic7lJI6nlp0Spd4hhpe3m80KYKG1LpVl7ZJBg2f+4CJ71yJsVmlQg=="
# }
#
# auth = oss2.StsAuth(tokens.get('access_key_id'), tokens.get('access_key_secret'), tokens.get('security_token'))
# bucket_get = oss2.Bucket(auth, tokens.get('endpoint'), tokens.get('bucket'))
# bucket_get.copy_object(tokens.get('bucket'), '3e7a72d0d43f59aa832974e8c98b84e3a0b305fc2d7a11ec96de.jpg', 'xxxx.jpg')
# res = bucket_get
# print(res)
# exit(1)
udid_test_s = [
"f81a3a4170cf8e6c71d525464e175049134a6dbc",
"6bae15671c8fe7096a40752071d87c6a837dd132",
"7dd77d8cf13789e3f547f28ceb3e0c67b8694e37",
"53e663905fcd81887abffc0d92e5c116ea2bfe5c",
"00008101-001325EE1188001E",
"f55df38afe5c1242b8bc478d0182bbd0d7d08929",
"00008020-000951C40C12002E",
"760862a7367dea986542dae17ee31cd139e454ad",
"079d0e46eab5369184830d37cbed345f664e835a",
"00008030-000E6CC122D2402E"
]
# postudid(udid="760862a7367dea986542dae17ee31cd139e454ad", short='semi')
# exit()
def call_function_try_attempts(try_attempts=3):
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(try_attempts):
print('111111111111111111111111111111111111111111111111', _)
res = func(*args, **kwargs)
status, result = res
if status:
return res
time.sleep(1)
return res
return wrapper
return decorator
# @call_function_try_attempts()
# def aaaa(x,y):
# if random.randrange(1,10) == sum([x,y]):
# return True,True
# return False,False
#
# print(aaaa(4,2))
if __name__ == '__main__':
for udid in udid_lists:
with ThreadPoolExecutor(max_workers=10) as executor:
for i in range(9):
for udid in udid_test_s:
with ThreadPoolExecutor(max_workers=20) as executor:
future = executor.submit(postudid, random.choice(short_list), udid)

Loading…
Cancel
Save