parent
							
								
									5e380fa09e
								
							
						
					
					
						commit
						153f800b91
					
				| @ -1,387 +0,0 @@ | ||||
| #!/usr/bin/env python | ||||
| # -*- coding:utf-8 -*- | ||||
| # project: 12月  | ||||
| # author: NinEveN | ||||
| # date: 2019/12/19 | ||||
| 
 | ||||
| import io | ||||
| import os | ||||
| import plistlib | ||||
| import re | ||||
| import zipfile | ||||
| 
 | ||||
| import qrcode | ||||
| import requests | ||||
| from PIL import Image | ||||
| from androguard.core.bytecodes import apk | ||||
| 
 | ||||
| 
 | ||||
| def bytes2human(n): | ||||
|     symbols = ('K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y') | ||||
|     prefix = {} | ||||
|     for i, s in enumerate(symbols): | ||||
|         prefix[s] = 1 << (i + 1) * 10 | ||||
|     for s in reversed(symbols): | ||||
|         if n >= prefix[s]: | ||||
|             value = float(n) / prefix[s] | ||||
|             return '%.1f%s' % (value, s) | ||||
|     return '%sB' % n | ||||
| 
 | ||||
| 
 | ||||
| class AppInfo(object): | ||||
|     def __init__(self, app_path): | ||||
|         self.app_path = app_path | ||||
|         self.result = {} | ||||
| 
 | ||||
|     def make_app_png(self, png_path, filename): | ||||
|         zf = zipfile.ZipFile(self.app_path) | ||||
|         name_list = zf.namelist() | ||||
|         if self.app_path.endswith("apk"): | ||||
|             # pattern = re.compile(r'res/drawable[^/]*/app_icon.png') | ||||
|             iconfile = apk.APK(self.app_path).get_app_icon() | ||||
|             size = 999 | ||||
| 
 | ||||
|         elif self.app_path.endswith("ipa"): | ||||
|             size = 0 | ||||
|             pattern = re.compile(r'Payload/[^/]*.app/AppIcon[^/]*[^(ipad)].png') | ||||
|             iconfile = "" | ||||
|             for path in name_list: | ||||
|                 m = pattern.match(path) | ||||
|                 if m is not None: | ||||
|                     filepath = m.group() | ||||
|                     fsize = len(zf.read(filepath)) | ||||
|                     if size == 0: | ||||
|                         iconfile = filepath | ||||
|                     if size < fsize: | ||||
|                         size = fsize | ||||
|                         iconfile = filepath | ||||
| 
 | ||||
|         else: | ||||
|             raise Exception("File type error") | ||||
| 
 | ||||
|         if size == 0: | ||||
|             raise Exception("File size error") | ||||
| 
 | ||||
|         if not os.path.isdir(png_path): | ||||
|             os.makedirs(png_path) | ||||
| 
 | ||||
|         with open(os.path.join(png_path, "%s.png" % filename), 'wb') as f: | ||||
|             f.write(zf.read(iconfile)) | ||||
| 
 | ||||
|         return "%s.png" % filename | ||||
| 
 | ||||
|     def get_app_data(self): | ||||
|         if self.app_path.endswith("apk"): | ||||
|             return self.__get_android_data() | ||||
|         elif self.app_path.endswith("ipa"): | ||||
|             return self.__get_ios_data() | ||||
|         else: | ||||
|             raise Exception("File type error") | ||||
| 
 | ||||
|     def __get_ios_info_path(self, ipaobj): | ||||
|         infopath_re = re.compile(r'Payload/[^/]*.app/Info.plist') | ||||
|         for i in ipaobj.namelist(): | ||||
|             m = infopath_re.match(i) | ||||
|             if m is not None: | ||||
|                 return m.group() | ||||
| 
 | ||||
|     def __get_android_data(self): | ||||
|         try: | ||||
|             apkobj = apk.APK(self.app_path) | ||||
|         except Exception as err: | ||||
|             raise err | ||||
|         else: | ||||
|             if apkobj.is_valid_APK(): | ||||
|                 versioncode = apkobj.get_androidversion_code() | ||||
|                 bundle_id = apkobj.get_package() | ||||
|                 labelname = apkobj.get_app_name() | ||||
|                 version = apkobj.get_androidversion_name() | ||||
|                 sdk_version = apkobj.get_min_sdk_version() | ||||
|                 self.result = { | ||||
|                     "labelname": labelname, | ||||
|                     "bundle_id": bundle_id, | ||||
|                     "versioncode": versioncode, | ||||
|                     "version": version, | ||||
|                     "miniOSversion": sdk_version, | ||||
|                     "type": "android", | ||||
|                 } | ||||
|         return self.result | ||||
| 
 | ||||
|     def __get_ios_data(self): | ||||
|         if zipfile.is_zipfile(self.app_path): | ||||
|             ipaobj = zipfile.ZipFile(self.app_path) | ||||
|             info_path = self.__get_ios_info_path(ipaobj) | ||||
|             if info_path: | ||||
|                 plist_data = ipaobj.read(info_path) | ||||
|                 plist_root = plistlib.loads(plist_data) | ||||
|                 labelname = plist_root['CFBundleDisplayName'] | ||||
|                 versioncode = plist_root['CFBundleVersion'] | ||||
|                 bundle_id = plist_root['CFBundleIdentifier'] | ||||
|                 version = plist_root['CFBundleShortVersionString'] | ||||
|                 miniOSversion = plist_root["MinimumOSVersion"] | ||||
|                 self.result = { | ||||
|                     "labelname": labelname, | ||||
|                     "bundle_id": bundle_id, | ||||
|                     "versioncode": versioncode, | ||||
|                     "version": version, | ||||
|                     "miniOSversion": miniOSversion, | ||||
|                     "type": "ios", | ||||
|                 } | ||||
| 
 | ||||
|             release_type_info = self.__get_ios_release_type(ipaobj) | ||||
|             if info_path: | ||||
|                 plist_data = ipaobj.read(release_type_info) | ||||
|                 if b"ProvisionedDevices" in plist_data: | ||||
|                     self.result["release_type"] = "Adhoc" | ||||
|                 else: | ||||
|                     self.result["release_type"] = "Inhouse" | ||||
|         return self.result | ||||
| 
 | ||||
|     def __get_ios_release_type(self, ipaobj): | ||||
|         infopath_re = re.compile(r'Payload/[^/]*.app/embedded.mobileprovision') | ||||
|         for i in ipaobj.namelist(): | ||||
|             m = infopath_re.match(i) | ||||
|             if m is not None: | ||||
|                 return m.group() | ||||
| 
 | ||||
| 
 | ||||
| class FirApi(object): | ||||
|     def __init__(self, api_token): | ||||
|         ''' | ||||
|         :param api_token: 长度为 32, 用户在 fir 的 api_token | ||||
|         ''' | ||||
|         self.api_token = api_token | ||||
|         self.app_url = "http://api.fir.im/apps" | ||||
| 
 | ||||
|     def __request(self, action, *args, **kwargs): | ||||
| 
 | ||||
|         fun_re = getattr(requests, action, None) | ||||
|         res = fun_re(*args, **kwargs) | ||||
|         return res | ||||
| 
 | ||||
|     def get_version_by_id(self, id): | ||||
|         ''' | ||||
| 
 | ||||
|         :param id: 应用ID,可在"应用管理"->"基本信息"查看 | ||||
|         :return: | ||||
|         ''' | ||||
|         url = "%s/latest/%s?api_token=%s" % (self.app_url, id, self.api_token) | ||||
|         res = self.__request("get", url) | ||||
|         if res.status_code == 200: | ||||
|             return res.json() | ||||
|         else: | ||||
|             raise Exception(res.content) | ||||
| 
 | ||||
|     def get_version_by_bundle_id(self, bundle_id, type): | ||||
|         ''' | ||||
| 
 | ||||
|         :param bundle_id: Bundle ID(iOS) / Package name(Android) | ||||
|         :param type: 应用类型 ( ios / android ) | ||||
|         :return: | ||||
|         ''' | ||||
|         url = "%s/latest/%s?api_token=%s&type=%s" % (self.app_url, | ||||
|                                                      bundle_id, self.api_token, | ||||
|                                                      type)  # 使用 bundle_id 请求必填 ( ios / android ) | ||||
|         res = self.__request("get", url) | ||||
|         if res.status_code == 200: | ||||
|             return res.json() | ||||
|         else: | ||||
|             raise Exception(res.content) | ||||
| 
 | ||||
|     def __get_upload_token(self, bundle_id, type): | ||||
|         ''' | ||||
| 
 | ||||
|         :param bundle_id: App 的 bundleId(发布新应用时必填) | ||||
|         :param type: ios 或者 android(发布新应用时必填) | ||||
|         :return: | ||||
|         ''' | ||||
|         data = { | ||||
|             "type": type, | ||||
|             "bundle_id": bundle_id, | ||||
|             "api_token": self.api_token, | ||||
|         } | ||||
|         res = self.__request("post", self.app_url, data) | ||||
|         if res.status_code == 201: | ||||
|             return res.json() | ||||
|         else: | ||||
|             raise Exception(res.content) | ||||
| 
 | ||||
|     def upload_app(self, app_path): | ||||
|         appobj = AppInfo(app_path) | ||||
|         appinfo = appobj.get_app_data() | ||||
|         icon_path = appobj.make_app_png() | ||||
|         bundle_id = appinfo.get("bundle_id") | ||||
|         upcretsdata = self.__get_upload_token(bundle_id, appinfo.get("type")) | ||||
| 
 | ||||
|         icon_key = upcretsdata["cert"]["icon"]["key"] | ||||
|         icon_token = upcretsdata["cert"]["icon"]["token"] | ||||
|         icon_upload_url = upcretsdata["cert"]["icon"]["upload_url"] | ||||
|         short_url = upcretsdata["short"] | ||||
| 
 | ||||
|         icon_data = { | ||||
|             "key": icon_key, | ||||
|             "token": icon_token, | ||||
|         } | ||||
|         files = {'file': (os.path.basename(icon_path), open(icon_path, 'rb'), {'Expires': '0'})} | ||||
|         res = self.__request("post", icon_upload_url, data=icon_data, files=files) | ||||
|         if res.status_code == 200: | ||||
|             print(res.json()) | ||||
|         else: | ||||
|             raise Exception(res.content) | ||||
| 
 | ||||
|         binary_key = upcretsdata["cert"]["binary"]["key"] | ||||
|         binary_token = upcretsdata["cert"]["binary"]["token"] | ||||
|         binary_upload_url = upcretsdata["cert"]["binary"]["upload_url"] | ||||
| 
 | ||||
|         binary_data = { | ||||
|             "key": binary_key, | ||||
|             "token": binary_token, | ||||
|             "x:name": appinfo["labelname"], | ||||
|             "x:version": appinfo["version"], | ||||
|             "x:build": appinfo["versioncode"], | ||||
|             "x:changelog": "" | ||||
|         } | ||||
| 
 | ||||
|         if appinfo["type"] == "ios": binary_data["x:release_type"] = appinfo.get("release_type", | ||||
|                                                                                  "Adhoc")  # Adhoc: 内测版   Inhouse:企业版 | ||||
| 
 | ||||
|         files = {'file': (os.path.basename(app_path), open(app_path, 'rb'), {'Expires': '0'})} | ||||
|         res = self.__request("post", binary_upload_url, data=binary_data, files=files) | ||||
|         if res.status_code == 200: | ||||
|             print(res.json()) | ||||
|             QrCode.make_logo_qr(os.path.join("https://fir.im/", short_url), icon_path, | ||||
|                                 "fir_%s.png" % (appinfo["labelname"])) | ||||
| 
 | ||||
|         else: | ||||
|             raise Exception(res.content) | ||||
| 
 | ||||
|     def get_app_list(self): | ||||
|         url = "%s?api_token=%s" % (self.app_url, self.api_token) | ||||
|         res = self.__request("get", url) | ||||
|         if res.status_code == 200: | ||||
|             return res.json() | ||||
|         else: | ||||
|             raise Exception(res.content) | ||||
| 
 | ||||
|     def get_app_infos_by_id(self, id): | ||||
|         ''' | ||||
| 
 | ||||
|         :param id: 应用id,可在"应用管理"->"基本信息"查看 | ||||
|         :return: | ||||
|         ''' | ||||
|         url = "%s/%s?api_token=%s" % (self.app_url, id, self.api_token) | ||||
|         res = self.__request("get", url) | ||||
|         if res.status_code == 200: | ||||
|             return res.json() | ||||
|         else: | ||||
|             raise Exception(res.content) | ||||
| 
 | ||||
|     def modify_app_info(self, id, name=None, desc=None, short=None, genre_id=None, | ||||
|                         is_opened=None, is_show_plaza=None, passwd=None, | ||||
|                         store_link_visible=None): | ||||
|         ''' | ||||
| 
 | ||||
|         :param id: 应用id,可在"应用管理"->"基本信息"查看 | ||||
|         :param name: app 名称 | ||||
|         :param desc: app 详细描述 | ||||
|         :param short: 	app 短链接 | ||||
|         :param genre_id: 类别 id | ||||
|         :param is_opened: 是否公开 | ||||
|         :param is_show_plaza: 是否展示在广场页 | ||||
|         :param passwd: 密码访问(不能和is_opened is_show_plaza联合使用) | ||||
|         :param store_link_visible: 应用市场链接是否显示 | ||||
|         :return: | ||||
|         ''' | ||||
|         url = "%s/%s" % (self.app_url, id) | ||||
|         data = { | ||||
|             "api_token": self.api_token, | ||||
|         } | ||||
|         if name: data["name"] = name | ||||
|         if desc: data["desc"] = desc | ||||
|         if short: data["short"] = short | ||||
|         if genre_id: data["genre_id"] = genre_id | ||||
|         if is_opened: data["is_opened"] = is_opened | ||||
|         if is_show_plaza and not passwd: data["is_show_plaza"] = is_show_plaza | ||||
|         if passwd and not is_show_plaza: data["passwd"] = passwd | ||||
|         if is_show_plaza and passwd: | ||||
|             raise Exception("密码访问不能和is_opened is_show_plaza联合使用") | ||||
|         if store_link_visible: data["name"] = store_link_visible | ||||
| 
 | ||||
|         res = self.__request("put", url, data) | ||||
|         if res.status_code == 200: | ||||
|             return res.json() | ||||
|         else: | ||||
|             raise Exception(res.content) | ||||
| 
 | ||||
| 
 | ||||
| class QrCode(object): | ||||
| 
 | ||||
|     @staticmethod | ||||
|     def make_logo_qr(str, logo_path, save_path=None): | ||||
|         qr = qrcode.QRCode( | ||||
|             version=4, | ||||
|             error_correction=qrcode.constants.ERROR_CORRECT_Q, | ||||
|             box_size=8, | ||||
|             border=2 | ||||
|         ) | ||||
|         # 添加转换内容 | ||||
|         qr.add_data(str) | ||||
|         qr.make(fit=True) | ||||
|         # 生成二维码 | ||||
|         img = qr.make_image() | ||||
|         # | ||||
|         img = img.convert("RGBA") | ||||
| 
 | ||||
|         # 添加logo | ||||
|         if logo_path and os.path.exists(logo_path): | ||||
|             icon = Image.open(logo_path) | ||||
|             # 获取二维码图片的大小 | ||||
|             img_w, img_h = img.size | ||||
| 
 | ||||
|             factor = 4 | ||||
|             size_w = int(img_w / factor) | ||||
|             size_h = int(img_h / factor) | ||||
| 
 | ||||
|             # logo图片的大小不能超过二维码图片的1/4 | ||||
|             icon_w, icon_h = icon.size | ||||
|             if icon_w > size_w: | ||||
|                 icon_w = size_w | ||||
|             if icon_h > size_h: | ||||
|                 icon_h = size_h | ||||
|             icon = icon.resize((icon_w, icon_h), Image.ANTIALIAS) | ||||
|             # 详见:http://pillow.readthedocs.org/handbook/tutorial.html | ||||
| 
 | ||||
|             # 计算logo在二维码图中的位置 | ||||
|             w = int((img_w - icon_w) / 2) | ||||
|             h = int((img_h - icon_h) / 2) | ||||
|             icon = icon.convert("RGBA") | ||||
|             img.paste(icon, (w, h), icon) | ||||
|             # 详见:http://pillow.readthedocs.org/reference/Image.html#PIL.Image.Image.paste | ||||
| 
 | ||||
|         # 保存处理后图片 | ||||
|         if save_path: | ||||
|             if not os.path.isdir(os.path.dirname(save_path)): | ||||
|                 os.makedirs(os.path.dirname(save_path)) | ||||
|             img.save(save_path) | ||||
|         else: | ||||
|             imgByteArr = io.BytesIO() | ||||
|             img.save(imgByteArr, format="PNG") | ||||
|             return imgByteArr.getvalue() | ||||
| 
 | ||||
| 
 | ||||
| if __name__ == '__main__': | ||||
|     apk_path = "/root/hehemajiang.apk" | ||||
|     API_TOKEN = "3d2a41159a7d8c396ddf0e88799de14a" | ||||
| 
 | ||||
|     fir = FirApi(API_TOKEN) | ||||
| 
 | ||||
|     # 上传 | ||||
|     fir.upload_app(apk_path) | ||||
| 
 | ||||
|     # 获取版本信息 | ||||
|     # print(fir.get_version_by_id("5dfb54d3b2eb4612589c3040")) | ||||
| 
 | ||||
|     # 获取app详细信息 | ||||
|     # print(fir.get_app_infos_by_id("5dfb54d3b2eb4612589c3040")) | ||||
|     # 获取列表 | ||||
|     # print(fir.get_app_list()) | ||||
| @ -1,91 +0,0 @@ | ||||
| #!/usr/bin/env python | ||||
| # -*- coding:utf-8 -*- | ||||
| # project: 12月  | ||||
| # author: NinEveN | ||||
| # date: 2019/12/19 | ||||
| 
 | ||||
| ''' | ||||
| Python生成二维码 v1.0 | ||||
| 主要将文本生成二维码图片 | ||||
| 
 | ||||
| 测试一:将文本生成白底黑字的二维码图片 | ||||
| 测试二:将文本生成带logo的二维码图片 | ||||
| 
 | ||||
| ''' | ||||
| 
 | ||||
| import os | ||||
| 
 | ||||
| import qrcode | ||||
| from PIL import Image | ||||
| 
 | ||||
| 
 | ||||
| # 生成二维码图片 | ||||
| def make_qr(str, save): | ||||
|     qr = qrcode.QRCode( | ||||
|         version=4,  # 生成二维码尺寸的大小 1-40  1:21*21(21+(n-1)*4) | ||||
|         error_correction=qrcode.constants.ERROR_CORRECT_M,  # L:7% M:15% Q:25% H:30% | ||||
|         box_size=10,  # 每个格子的像素大小 | ||||
|         border=2,  # 边框的格子宽度大小 | ||||
|     ) | ||||
|     qr.add_data(str) | ||||
|     qr.make(fit=True) | ||||
| 
 | ||||
|     img = qr.make_image() | ||||
|     img.save(save) | ||||
| 
 | ||||
| 
 | ||||
| # 生成带logo的二维码图片 | ||||
| def make_logo_qr(str, logo, save): | ||||
|     # 参数配置 | ||||
|     qr = qrcode.QRCode( | ||||
|         version=4, | ||||
|         error_correction=qrcode.constants.ERROR_CORRECT_Q, | ||||
|         box_size=8, | ||||
|         border=2 | ||||
|     ) | ||||
|     # 添加转换内容 | ||||
|     qr.add_data(str) | ||||
|     # | ||||
|     qr.make(fit=True) | ||||
|     # 生成二维码 | ||||
|     img = qr.make_image() | ||||
|     # | ||||
|     img = img.convert("RGBA") | ||||
| 
 | ||||
|     # 添加logo | ||||
|     if logo and os.path.exists(logo): | ||||
|         icon = Image.open(logo) | ||||
|         # 获取二维码图片的大小 | ||||
|         img_w, img_h = img.size | ||||
| 
 | ||||
|         factor = 4 | ||||
|         size_w = int(img_w / factor) | ||||
|         size_h = int(img_h / factor) | ||||
| 
 | ||||
|         # logo图片的大小不能超过二维码图片的1/4 | ||||
|         icon_w, icon_h = icon.size | ||||
|         if icon_w > size_w: | ||||
|             icon_w = size_w | ||||
|         if icon_h > size_h: | ||||
|             icon_h = size_h | ||||
|         icon = icon.resize((icon_w, icon_h), Image.ANTIALIAS) | ||||
|         # 详见:http://pillow.readthedocs.org/handbook/tutorial.html | ||||
| 
 | ||||
|         # 计算logo在二维码图中的位置 | ||||
|         w = int((img_w - icon_w) / 2) | ||||
|         h = int((img_h - icon_h) / 2) | ||||
|         icon = icon.convert("RGBA") | ||||
|         img.paste(icon, (w, h), icon) | ||||
|         # 详见:http://pillow.readthedocs.org/reference/Image.html#PIL.Image.Image.paste | ||||
| 
 | ||||
|     # 保存处理后图片 | ||||
|     img.save(save) | ||||
| 
 | ||||
| 
 | ||||
| if __name__ == '__main__': | ||||
|     save_path = 'base__runmethod_02.png'  # 生成后的保存文件 | ||||
|     logo = '/tmp/pycharm_project_986/fir/tmp/icon/cmjpwyltznbioseh'  # logo图片 | ||||
| 
 | ||||
|     str = "https://fir.im/luy4" | ||||
| 
 | ||||
|     make_logo_qr(str, logo, save_path) | ||||
| @ -1,140 +0,0 @@ | ||||
| #!/usr/bin/env python | ||||
| # -*- coding:utf-8 -*- | ||||
| # project: 3月 | ||||
| # author: liuyu | ||||
| # date: 2020/3/6 | ||||
| import json | ||||
| import logging | ||||
| import os | ||||
| import signal | ||||
| import socket | ||||
| import time | ||||
| from subprocess import Popen, PIPE | ||||
| 
 | ||||
| import paramiko | ||||
| 
 | ||||
| logger = logging.getLogger(__name__) | ||||
| 
 | ||||
| 
 | ||||
| def default_result(): | ||||
|     return {'exit_code': '99', 'return_info': 'Failed to run, function_name is not existed'} | ||||
| 
 | ||||
| 
 | ||||
| def portisonline(host, port): | ||||
|     result = {} | ||||
|     sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | ||||
|     sk.settimeout(3) | ||||
|     result["host"] = host | ||||
|     result["port"] = port | ||||
|     result["functionname"] = "CHECK_HOST_ONLINE:" | ||||
|     try: | ||||
|         sk.connect((host, port)) | ||||
|         result["message"] = "Connection success" | ||||
|         result["code"] = 0 | ||||
|     except Exception: | ||||
|         result["message"] = "SSH Port Connection refused" | ||||
|         result["code"] = 1 | ||||
|     sk.close() | ||||
|     return result | ||||
| 
 | ||||
| 
 | ||||
| class SSHConnection(object): | ||||
|     def __init__(self, host, port, username, password): | ||||
|         self._host = host | ||||
|         self._port = port | ||||
|         self._username = username | ||||
|         self._password = password | ||||
|         self._transport = None | ||||
|         self._sftp = None | ||||
|         self._client = None | ||||
|         self._connect() | ||||
| 
 | ||||
|     def _connect(self): | ||||
|         transport = paramiko.Transport((self._host, self._port)) | ||||
|         transport.connect(username=self._username, password=self._password) | ||||
|         self._transport = transport | ||||
| 
 | ||||
|     # 下载 | ||||
|     def download(self, remotepath, localpath): | ||||
|         if self._sftp is None: | ||||
|             self._sftp = paramiko.SFTPClient.from_transport(self._transport) | ||||
|         self._sftp.get(remotepath, localpath) | ||||
|         self.close() | ||||
| 
 | ||||
|     # 上传 | ||||
|     def put(self, localpath, remotepath): | ||||
|         if self._sftp is None: | ||||
|             self._sftp = paramiko.SFTPClient.from_transport(self._transport) | ||||
|         self.exec_command('mkdir -p %s' % (os.path.dirname(remotepath))) | ||||
|         self._sftp.put(localpath, remotepath) | ||||
|         self.close() | ||||
| 
 | ||||
|     # 执行命令 | ||||
|     def exec_command(self, command): | ||||
|         if self._client is None: | ||||
|             self._client = paramiko.SSHClient() | ||||
|             self._client._transport = self._transport | ||||
|         stdin, stdout, stderr = self._client.exec_command(command) | ||||
|         data = stdout.read() | ||||
|         if len(data) > 0: | ||||
|             return {'status': 0, 'infos': data} | ||||
|         err = stderr.read() | ||||
|         if len(err) > 0: | ||||
|             return {'status': 1, 'infos': err} | ||||
|         return {'status': 0, 'infos': data} | ||||
| 
 | ||||
|     def close(self): | ||||
|         if self._transport: | ||||
|             self._transport.close() | ||||
|         if self._client: | ||||
|             self._client.close() | ||||
| 
 | ||||
| 
 | ||||
| def shell_command(cmdstrs, timeout): | ||||
|     result = default_result() | ||||
|     result['return_info'] = '' | ||||
|     shell_start_time = time.time() | ||||
|     child = Popen(cmdstrs, shell=True, stdout=PIPE, stderr=PIPE) | ||||
|     if timeout: | ||||
|         while child.poll() is None: | ||||
|             time.sleep(1) | ||||
|             now = time.time() | ||||
|             if int(now - shell_start_time) > timeout: | ||||
|                 os.kill(child.pid, signal.SIGKILL) | ||||
|                 os.waitpid(-1, os.WNOHANG) | ||||
|                 result['exit_code'] = 126 | ||||
|                 return result | ||||
| 
 | ||||
|     out, err = child.communicate() | ||||
|     if err: | ||||
|         result['err_info'] = err.decode("utf-8") | ||||
|     shell_end_time = time.time() | ||||
|     result['shell_run_time'] = shell_end_time - shell_start_time | ||||
|     out = out.strip(b'\n') | ||||
|     result['return_info'] = out | ||||
|     result['exit_code'] = child.returncode | ||||
|     logger.info(f'shell: {cmdstrs} - return_info:{out} - exit_code:{child.returncode}') | ||||
|     return result | ||||
| 
 | ||||
| 
 | ||||
| def use_user_pass(hostip, port, user, passwd, cmdstrs): | ||||
|     result = default_result() | ||||
|     checkinfo = portisonline(hostip, port) | ||||
|     if checkinfo["code"] != 0: | ||||
|         result['shell_run_time'] = 0 | ||||
|         result['return_info'] = json.dumps(checkinfo) | ||||
|         result['exit_code'] = checkinfo["code"] | ||||
|     else: | ||||
|         conn = SSHConnection(hostip, port, user, passwd) | ||||
|         result['return_info'] = '' | ||||
|         shell_start_time = time.time() | ||||
|         out = conn.exec_command(cmdstrs) | ||||
|         shell_end_time = time.time() | ||||
|         result['shell_run_time'] = shell_end_time - shell_start_time | ||||
|         outs = str(out['infos'], 'utf-8') | ||||
|         outs = outs.strip('\n') | ||||
|         result['return_info'] = outs | ||||
|         result['exit_code'] = out['status'] | ||||
|         logger.info(f'host: {hostip} user:{user} - shell: {cmdstrs} - return_info:{out} - exit_code:{out["status"]}') | ||||
|         conn.close() | ||||
|     return result | ||||
| @ -0,0 +1,47 @@ | ||||
| #!/usr/bin/env python | ||||
| # -*- coding:utf-8 -*- | ||||
| # project: 4月 | ||||
| # author: liuyu | ||||
| # date: 2020/4/7 | ||||
| 
 | ||||
| import logging | ||||
| import time | ||||
| 
 | ||||
| from django.core.cache import cache | ||||
| 
 | ||||
| from api.models import Apps, UserInfo | ||||
| from common.utils.storage import Storage | ||||
| from fir_ser.settings import CACHE_KEY_TEMPLATE | ||||
| 
 | ||||
| logger = logging.getLogger(__name__) | ||||
| 
 | ||||
| 
 | ||||
| def sync_download_times(): | ||||
|     down_tem_key = CACHE_KEY_TEMPLATE.get("download_times_key") | ||||
|     key = "_".join([down_tem_key, '*']) | ||||
|     for app_download in cache.iter_keys(key): | ||||
|         count_hits = cache.get(app_download) | ||||
|         if count_hits: | ||||
|             app_id = app_download.split(down_tem_key)[1].strip('_') | ||||
|             Apps.objects.filter(app_id=app_id).update(count_hits=count_hits) | ||||
|             logger.info(f"sync_download_times app_id:{app_id} count_hits:{count_hits}") | ||||
| 
 | ||||
| 
 | ||||
| def auto_clean_upload_tmp_file(): | ||||
|     upload_tem_key = CACHE_KEY_TEMPLATE.get("upload_file_tmp_name_key") | ||||
|     key = "_".join([upload_tem_key, '*']) | ||||
|     for upload_tem_file_key in cache.iter_keys(key): | ||||
|         data = cache.get(upload_tem_file_key) | ||||
|         if data: | ||||
|             u_time = data.get("u_time", None) | ||||
|             if u_time and time.time() - u_time > 60 * 20: | ||||
|                 user_obj = UserInfo.objects.filter(pk=data.get("id")).first() | ||||
|                 if user_obj: | ||||
|                     storage = Storage(user_obj) | ||||
|                     filename = data.get("filename", None) | ||||
|                     if filename: | ||||
|                         storage.delete_file(filename) | ||||
|                         logger.info(f"auto_clean_upload_tmp_file delete_file :{filename}") | ||||
| 
 | ||||
|                 cache.delete(upload_tem_file_key) | ||||
|                 logger.info(f"auto_clean_upload_tmp_file upload_tem_file_key :{upload_tem_file_key}") | ||||
| @ -0,0 +1,38 @@ | ||||
| #!/usr/bin/env python | ||||
| # -*- coding:utf-8 -*- | ||||
| # project: 1月 | ||||
| # author: NinEveN | ||||
| # date: 2022/1/6 | ||||
| 
 | ||||
| from django.http import FileResponse | ||||
| from rest_framework.response import Response | ||||
| 
 | ||||
| from common.base.baseutils import make_random_uuid | ||||
| 
 | ||||
| 
 | ||||
| def file_response(stream, filename, content_type): | ||||
|     return FileResponse(stream, as_attachment=True, | ||||
|                         filename=filename, | ||||
|                         content_type=content_type) | ||||
| 
 | ||||
| 
 | ||||
| def mobileprovision_file_response(file_path): | ||||
|     return file_response(open(file_path, 'rb'), make_random_uuid() + '.mobileprovision', | ||||
|                          "application/x-apple-aspen-config") | ||||
| 
 | ||||
| 
 | ||||
| class ApiResponse(Response): | ||||
|     def __init__(self, code=1000, msg='success', data=None, status=None, headers=None, content_type=None, **kwargs): | ||||
|         dic = { | ||||
|             'code': code, | ||||
|             'msg': msg | ||||
|         } | ||||
|         if data is not None: | ||||
|             dic['data'] = data | ||||
|         dic.update(kwargs) | ||||
|         self._data = data | ||||
|         # 对象来调用对象的绑定方法,会自动传值 | ||||
|         super().__init__(data=dic, status=status, headers=headers, content_type=content_type) | ||||
| 
 | ||||
|         # 类来调用对象的绑定方法,这个方法就是一个普通函数,有几个参数就要传几个参数 | ||||
|         # Response.__init__(data=dic,status=status,headers=headers,content_type=content_type) | ||||
| @ -0,0 +1,46 @@ | ||||
| #!/usr/bin/env python | ||||
| # -*- coding:utf-8 -*- | ||||
| # author: NinEveN | ||||
| # date: 2022/2/15 | ||||
| import logging | ||||
| 
 | ||||
| from django.dispatch import Signal, receiver | ||||
| 
 | ||||
| from api.models import Apps | ||||
| from xsign.models import AppUDID, APPToDeveloper | ||||
| from xsign.tasks import run_resign_task | ||||
| from xsign.utils.supersignutils import IosUtils | ||||
| 
 | ||||
| logger = logging.getLogger(__name__) | ||||
| 
 | ||||
| """ | ||||
| 更新应用的时候,是否同时更新超级签名数据 | ||||
| """ | ||||
| run_resign_task_signal = Signal(providing_args=["app_pk"]) | ||||
| 
 | ||||
| 
 | ||||
| @receiver(run_resign_task_signal) | ||||
| def run_resign_task_callback(sender, **kwargs): | ||||
|     app_obj = Apps.objects.filter(pk=kwargs.get('app_pk')).first() | ||||
|     if app_obj: | ||||
|         AppUDID.objects.filter(app_id=app_obj, sign_status__gte=3).update(sign_status=3) | ||||
|         if app_obj.change_auto_sign: | ||||
|             c_task = run_resign_task(app_obj.pk, False, False) | ||||
|             logger.info(f"app {app_obj} run_resign_task end msg:{c_task}") | ||||
| 
 | ||||
| 
 | ||||
| """ | ||||
| 删除应用时的信号,用户清理签名相关数据 | ||||
| """ | ||||
| 
 | ||||
| delete_app_signal = Signal(providing_args=["app_pk"]) | ||||
| 
 | ||||
| 
 | ||||
| @receiver(delete_app_signal) | ||||
| def delete_app_callback(sender, **kwargs): | ||||
|     app_obj = Apps.objects.filter(pk=kwargs.get('app_pk')).first() | ||||
|     if app_obj: | ||||
|         count = APPToDeveloper.objects.filter(app_id=app_obj).count() | ||||
|         if app_obj.issupersign or count > 0: | ||||
|             logger.info(f"app_id:{app_obj.app_id} is supersign ,delete this app need clean IOS developer") | ||||
|             IosUtils.clean_app_by_user_obj(app_obj) | ||||
| @ -0,0 +1,15 @@ | ||||
| from django.contrib import admin | ||||
| 
 | ||||
| # Register your models here. | ||||
| from .models import * | ||||
| 
 | ||||
| admin.site.register(AppIOSDeveloperInfo) | ||||
| admin.site.register(AppleDeveloperToAppUse) | ||||
| admin.site.register(APPSuperSignUsedInfo) | ||||
| admin.site.register(APPToDeveloper) | ||||
| admin.site.register(AppUDID) | ||||
| admin.site.register(DeveloperAppID) | ||||
| admin.site.register(DeveloperDevicesID) | ||||
| admin.site.register(IosDeveloperBill) | ||||
| admin.site.register(IosDeveloperPublicPoolBill) | ||||
| admin.site.register(UDIDsyncDeveloper) | ||||
| @ -0,0 +1,5 @@ | ||||
| from django.apps import AppConfig | ||||
| 
 | ||||
| 
 | ||||
| class ApiConfig(AppConfig): | ||||
|     name = 'xsign' | ||||
| @ -0,0 +1,241 @@ | ||||
| from django.db import models | ||||
| 
 | ||||
| from api.models import Apps, UserInfo | ||||
| from common.base.daobase import AESCharField | ||||
| 
 | ||||
| 
 | ||||
| class AppIOSDeveloperInfo(models.Model): | ||||
|     """ | ||||
|     苹果开发者信息 | ||||
|     """ | ||||
|     user_id = models.ForeignKey(to=UserInfo, verbose_name="用户ID", on_delete=models.CASCADE) | ||||
|     issuer_id = models.CharField(max_length=64, null=False, verbose_name="标识创建认证令牌的发放者") | ||||
|     private_key_id = models.CharField(max_length=64, null=False, verbose_name="密钥 ID") | ||||
|     p8key = AESCharField(max_length=512, null=False, verbose_name="p8key") | ||||
|     certid = models.CharField(max_length=64, blank=True, verbose_name="超级签名自动创建证书ID", null=True) | ||||
|     usable_number = models.IntegerField(verbose_name="可使用设备数", default=100) | ||||
|     app_limit_number = models.IntegerField(verbose_name="可分配应用数,最大160", default=100) | ||||
|     created_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间") | ||||
|     updated_time = models.DateTimeField(auto_now=True, verbose_name="更新时间") | ||||
|     cert_expire_time = models.DateTimeField(blank=True, null=True, verbose_name="证书过期时间") | ||||
|     description = models.TextField('备注', blank=True, null=True, default='') | ||||
|     auth_type_choices = ((0, 'p8key认证'),) | ||||
|     auth_type = models.SmallIntegerField(choices=auth_type_choices, default=0, verbose_name="认证类型") | ||||
| 
 | ||||
|     # 协议待同意和维护中:代表只读,不可创建和注册新设备号 | ||||
|     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) | ||||
|     auto_check = models.BooleanField(verbose_name="是否自动检测开发者状态", default=False) | ||||
| 
 | ||||
|     class Meta: | ||||
|         verbose_name = '苹果开发者账户' | ||||
|         verbose_name_plural = "苹果开发者账户" | ||||
|         unique_together = (('user_id', 'issuer_id'),) | ||||
| 
 | ||||
|     def save(self, *args, **kwargs): | ||||
|         if self.usable_number > 100: | ||||
|             self.usable_number = 100 | ||||
|         elif self.usable_number < 0: | ||||
|             self.usable_number = 0 | ||||
| 
 | ||||
|         if self.app_limit_number > 160: | ||||
|             self.app_limit_number = 160 | ||||
|         elif self.app_limit_number < 0: | ||||
|             self.app_limit_number = 0 | ||||
| 
 | ||||
|         return super(AppIOSDeveloperInfo, self).save(*args, **kwargs) | ||||
| 
 | ||||
|     def __str__(self): | ||||
|         return "%s-%s" % (self.user_id, self.issuer_id) | ||||
| 
 | ||||
| 
 | ||||
| class UDIDsyncDeveloper(models.Model): | ||||
|     """ | ||||
|     开发者设备ID记录 | ||||
|     """ | ||||
|     developerid = models.ForeignKey(to=AppIOSDeveloperInfo, on_delete=models.CASCADE, verbose_name="所使用苹果开发者账户") | ||||
|     udid = models.CharField(max_length=64, verbose_name="udid唯一标识", db_index=True) | ||||
|     product = models.CharField(max_length=64, verbose_name="产品", blank=True, null=True, ) | ||||
|     serial = models.CharField(max_length=64, verbose_name="序列号", blank=True, null=True, ) | ||||
|     version = models.CharField(max_length=64, verbose_name="型号", blank=True, null=True, ) | ||||
|     created_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间"), | ||||
|     status = models.BooleanField(verbose_name="设备在开发者平台状态", default=False) | ||||
| 
 | ||||
|     class Meta: | ||||
|         verbose_name = 'iOS开发平台同步设备信息' | ||||
|         verbose_name_plural = "iOS开发平台同步设备信息" | ||||
|         unique_together = ('udid', 'developerid',) | ||||
| 
 | ||||
|     def __str__(self): | ||||
|         return "%s-%s-%s" % (self.product, self.udid, self.developerid) | ||||
| 
 | ||||
| 
 | ||||
| class AppUDID(models.Model): | ||||
|     """ | ||||
|     用户发送的设备记录信息 | ||||
|     """ | ||||
|     app_id = models.ForeignKey(to=Apps, on_delete=models.CASCADE, verbose_name="属于哪个APP") | ||||
|     udid = models.ForeignKey(to=UDIDsyncDeveloper, verbose_name="udid唯一标识", on_delete=models.CASCADE) | ||||
|     product = models.CharField(max_length=64, verbose_name="产品", blank=True, null=True, ) | ||||
|     serial = models.CharField(max_length=64, verbose_name="序列号", blank=True, null=True, ) | ||||
|     version = models.CharField(max_length=64, verbose_name="型号", blank=True, null=True, ) | ||||
|     imei = models.CharField(max_length=64, verbose_name="型号", blank=True, null=True, ) | ||||
|     iccid = models.CharField(max_length=64, verbose_name="型号", blank=True, null=True, ) | ||||
|     created_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间") | ||||
|     updated_time = models.DateTimeField(auto_now=True, verbose_name="更新时间") | ||||
|     sign_status_choices = ((0, '新设备入库准备'), (1, '设备ID已经注册'), (2, 'bundelid已经注册'), (3, '描述文件已经下载'), (4, '已经完成签名打包')) | ||||
|     sign_status = models.SmallIntegerField(choices=sign_status_choices, default=0, verbose_name="签名状态") | ||||
| 
 | ||||
|     class Meta: | ||||
|         verbose_name = '设备详情' | ||||
|         verbose_name_plural = "设备详情" | ||||
|         unique_together = ('app_id', 'udid',) | ||||
| 
 | ||||
|     def __str__(self): | ||||
|         return "%s-%s" % (self.app_id.name, self.udid) | ||||
| 
 | ||||
| 
 | ||||
| class APPSuperSignUsedInfo(models.Model): | ||||
|     """ | ||||
|     设备消耗统计 | ||||
|     """ | ||||
|     user_id = models.ForeignKey(to=UserInfo, verbose_name="用户ID", on_delete=models.CASCADE) | ||||
|     app_id = models.ForeignKey(to=Apps, on_delete=models.CASCADE, verbose_name="属于哪个APP") | ||||
|     udid = models.ForeignKey(to=AppUDID, on_delete=models.CASCADE, verbose_name="所消耗的udid") | ||||
|     developerid = models.ForeignKey(to=AppIOSDeveloperInfo, on_delete=models.CASCADE, verbose_name="所使用苹果开发者账户") | ||||
|     created_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间") | ||||
|     updated_time = models.DateTimeField(auto_now=True, verbose_name="更新时间") | ||||
| 
 | ||||
|     class Meta: | ||||
|         verbose_name = '设备使用统计' | ||||
|         verbose_name_plural = "设备使用统计" | ||||
| 
 | ||||
|     def __str__(self): | ||||
|         return "%s-%s-%s" % (self.user_id, self.app_id, self.udid) | ||||
| 
 | ||||
| 
 | ||||
| class APPToDeveloper(models.Model): | ||||
|     """ | ||||
|     签名完成之后,通过该表查询 签名包地址 | ||||
|     """ | ||||
|     app_id = models.ForeignKey(to=Apps, on_delete=models.CASCADE, verbose_name="属于哪个APP") | ||||
|     developerid = models.ForeignKey(to=AppIOSDeveloperInfo, on_delete=models.CASCADE, verbose_name="所使用苹果开发者账户") | ||||
|     binary_file = models.CharField(max_length=128, blank=True, verbose_name="签名包名称", null=True, unique=True) | ||||
|     release_file = models.CharField(max_length=128, blank=True, verbose_name="源包名称", null=True) | ||||
|     updated_time = models.DateTimeField(auto_now=True, verbose_name="更新时间") | ||||
| 
 | ||||
|     class Meta: | ||||
|         verbose_name = '应用开发者绑定' | ||||
|         verbose_name_plural = "应用开发者绑定" | ||||
| 
 | ||||
|     def __str__(self): | ||||
|         return "%s-%s-%s" % (self.developerid, self.app_id, self.binary_file) | ||||
| 
 | ||||
| 
 | ||||
| class DeveloperAppID(models.Model): | ||||
|     """ | ||||
|     苹果应用消耗某个开发者某个描述文件对应记录表,一个苹果应用对应一个苹果开发者和该开发者下面对应的描述文件 | ||||
|     """ | ||||
|     aid = models.CharField(max_length=64, null=False)  # ,apple APP 唯一标识 | ||||
|     profile_id = models.CharField(max_length=64, null=True, blank=True)  # ,profile_id 唯一标识 | ||||
|     developerid = models.ForeignKey(to=AppIOSDeveloperInfo, on_delete=models.CASCADE, verbose_name="所使用苹果开发者账户") | ||||
|     app_id = models.ForeignKey(to=Apps, on_delete=models.CASCADE, verbose_name="属于哪个APP") | ||||
| 
 | ||||
|     class Meta: | ||||
|         verbose_name = '超级签APP id' | ||||
|         verbose_name_plural = "超级签APP id" | ||||
|         unique_together = ('aid', 'developerid', 'app_id') | ||||
| 
 | ||||
|     def __str__(self): | ||||
|         return "%s-%s-%s" % (self.aid, self.app_id, self.developerid) | ||||
| 
 | ||||
| 
 | ||||
| class DeveloperDevicesID(models.Model): | ||||
|     """ | ||||
|     苹果应用消耗某个开发者某个设备对应记录表,有一个苹果应用可能有该开发者下面多个设备记录 | ||||
|     """ | ||||
|     did = models.CharField(max_length=64, null=False)  # ,apple 设备唯一标识 | ||||
|     udid = models.ForeignKey(to=UDIDsyncDeveloper, on_delete=models.CASCADE, verbose_name="所消耗的udid") | ||||
|     developerid = models.ForeignKey(to=AppIOSDeveloperInfo, on_delete=models.CASCADE, verbose_name="所使用苹果开发者账户") | ||||
|     app_id = models.ForeignKey(to=Apps, on_delete=models.CASCADE, verbose_name="属于哪个APP") | ||||
| 
 | ||||
|     class Meta: | ||||
|         verbose_name = '超级签Devices id' | ||||
|         verbose_name_plural = "超级签Devices id" | ||||
|         unique_together = ('did', 'developerid', 'app_id') | ||||
| 
 | ||||
|     def __str__(self): | ||||
|         return "%s-%s-%s-%s" % (self.id, self.app_id, self.developerid, self.udid) | ||||
| 
 | ||||
| 
 | ||||
| class IosDeveloperPublicPoolBill(models.Model): | ||||
|     """ | ||||
|     苹果应用设备消费记录表 | ||||
|     """ | ||||
|     user_id = models.ForeignKey(to=UserInfo, verbose_name="用户ID", on_delete=models.CASCADE) | ||||
|     number = models.IntegerField(verbose_name="消耗次数", default=1) | ||||
|     app_info = models.JSONField(max_length=256, verbose_name="属于哪个APP", null=True, blank=True) | ||||
|     udid = models.CharField(max_length=64, verbose_name="设备udid", null=True, blank=True) | ||||
|     product = models.CharField(max_length=64, verbose_name="设备udid", null=True, blank=True) | ||||
|     version = models.CharField(max_length=64, verbose_name="设备udid", null=True, blank=True) | ||||
|     developer_info = models.JSONField(max_length=256, verbose_name="开发者信息", null=True, blank=True) | ||||
|     udid_sync_info = models.ForeignKey(to=UDIDsyncDeveloper, on_delete=models.SET_NULL, verbose_name="关联同步设备信息", | ||||
|                                        null=True, blank=True) | ||||
|     app_id = models.ForeignKey(to=Apps, on_delete=models.SET_NULL, verbose_name="属于哪个APP", null=True, blank=True) | ||||
|     description = models.CharField(verbose_name="操作描述", max_length=128, default='', blank=True) | ||||
|     remote_addr = models.GenericIPAddressField(verbose_name="远程IP地址") | ||||
|     created_time = models.DateTimeField(auto_now_add=True, verbose_name="添加时间") | ||||
| 
 | ||||
|     class Meta: | ||||
|         verbose_name = '设备消耗账单' | ||||
|         verbose_name_plural = "设备消耗账单" | ||||
| 
 | ||||
|     def __str__(self): | ||||
|         return "%s-%s" % (self.user_id, self.description) | ||||
| 
 | ||||
| 
 | ||||
| class IosDeveloperBill(models.Model): | ||||
|     """ | ||||
|     用户共享设备数,可将该用户设备数共享给 其他用户使用 | ||||
|     """ | ||||
|     user_id = models.ForeignKey(to=UserInfo, verbose_name="用户ID", on_delete=models.CASCADE, | ||||
|                                 related_name='org_user_id') | ||||
|     to_user_id = models.ForeignKey(to=UserInfo, verbose_name="用户ID", on_delete=models.CASCADE, | ||||
|                                    related_name='to_user_id', null=True, blank=True) | ||||
| 
 | ||||
|     status_choices = ((0, '失效'), (1, '已撤回'), (2, '成功')) | ||||
|     status = models.SmallIntegerField(choices=status_choices, default=0, verbose_name="状态", | ||||
|                                       help_text="0 失效 1 撤回 2 转账") | ||||
|     number = models.IntegerField(verbose_name="设备数量", default=1) | ||||
|     description = models.CharField(verbose_name="操作描述", max_length=128, default='', blank=True) | ||||
|     remote_addr = models.GenericIPAddressField(verbose_name="远程IP地址") | ||||
|     created_time = models.DateTimeField(auto_now_add=True, verbose_name="添加时间") | ||||
|     updated_time = models.DateTimeField(auto_now=True, verbose_name="更新时间") | ||||
| 
 | ||||
|     class Meta: | ||||
|         verbose_name = '设备划转账单' | ||||
|         verbose_name_plural = "设备划转账单" | ||||
| 
 | ||||
|     def __str__(self): | ||||
|         return "%s-%s—%s" % (self.user_id, self.to_user_id, self.description) | ||||
| 
 | ||||
| 
 | ||||
| class AppleDeveloperToAppUse(models.Model): | ||||
|     """ | ||||
|     苹果应用专属开发者配置表 | ||||
|     """ | ||||
|     app_id = models.ForeignKey(to=Apps, on_delete=models.CASCADE) | ||||
|     developerid = models.ForeignKey(to=AppIOSDeveloperInfo, on_delete=models.CASCADE) | ||||
|     usable_number = models.IntegerField(verbose_name="可使用设备数", default=100) | ||||
|     description = models.CharField(verbose_name="备注", max_length=256, default='', blank=True) | ||||
|     created_time = models.DateTimeField(auto_now_add=True, verbose_name="添加时间") | ||||
| 
 | ||||
|     class Meta: | ||||
|         verbose_name = '开发者专属于应用' | ||||
|         verbose_name_plural = "开发者专属于应用" | ||||
|         unique_together = ('app_id', 'developerid') | ||||
| 
 | ||||
|     def __str__(self): | ||||
|         return "%s-%s" % (self.app_id.name, self.developerid.issuer_id) | ||||
| @ -0,0 +1,80 @@ | ||||
| #!/usr/bin/env python | ||||
| # -*- coding:utf-8 -*- | ||||
| # project: 5月  | ||||
| # author: NinEveN | ||||
| # date: 2021/5/27 | ||||
| 
 | ||||
| import logging | ||||
| 
 | ||||
| from celery import shared_task | ||||
| from django.core.cache import cache | ||||
| 
 | ||||
| from common.cache.state import MigrateStorageState | ||||
| from fir_ser.celery import app | ||||
| from xsign.models import Apps, DeveloperAppID | ||||
| from xsign.utils.ctasks import auto_check_ios_developer_active | ||||
| from xsign.utils.iproxy import get_best_proxy_ips | ||||
| from xsign.utils.supersignutils import IosUtils, resign_by_app_id_and_developer | ||||
| 
 | ||||
| logger = logging.getLogger(__name__) | ||||
| 
 | ||||
| 
 | ||||
| @shared_task | ||||
| def run_sign_task(format_udid_info, short, client_ip): | ||||
|     app_obj = Apps.objects.filter(short=short).first() | ||||
| 
 | ||||
|     if MigrateStorageState(app_obj.user_id.uid).get_state(): | ||||
|         msg = "数据迁移中,无法处理该操作" | ||||
|         return msg | ||||
| 
 | ||||
|     ios_obj = IosUtils(format_udid_info, app_obj.user_id, app_obj) | ||||
|     status, msg = ios_obj.sign_ipa(client_ip) | ||||
|     if not status: | ||||
|         code = msg.get("code", -1) | ||||
|         if code == 0: | ||||
|             msg = "" | ||||
|         elif code == 1005: | ||||
|             msg = "签名余额不足" | ||||
|         elif code == 1002: | ||||
|             msg = "维护中" | ||||
|         elif code == 1003: | ||||
|             msg = "应用余额不足" | ||||
|         elif code in [1004, 1001, 1009]: | ||||
|             msg = msg.get('msg', '未知错误') | ||||
|         else: | ||||
|             msg = '系统内部错误,请稍后再试或联系管理员' | ||||
|     else: | ||||
|         msg = "" | ||||
|     return msg | ||||
| 
 | ||||
| 
 | ||||
| def run_resign_task(app_id, need_download_profile=True, force=True): | ||||
|     app_obj = Apps.objects.filter(pk=app_id).first() | ||||
|     if app_obj.issupersign and app_obj.user_id.supersign_active: | ||||
|         with cache.lock("%s_%s" % ('task_resign', app_obj.app_id), timeout=60 * 60): | ||||
|             task_list = [] | ||||
|             for developer_app_id_obj in DeveloperAppID.objects.filter(app_id=app_obj).all(): | ||||
|                 c_task = run_resign_task_do.apply_async((app_id, developer_app_id_obj.developerid.pk, | ||||
|                                                          developer_app_id_obj.aid, need_download_profile, force)) | ||||
|                 task_list.append(c_task) | ||||
|             for c_task in task_list: | ||||
|                 msg = c_task.get(propagate=False) | ||||
|                 logger.info(f"app {app_obj} run_resign_task msg:{msg}") | ||||
|                 if c_task.successful(): | ||||
|                     c_task.forget() | ||||
|     return True | ||||
| 
 | ||||
| 
 | ||||
| @shared_task | ||||
| def run_resign_task_do(app_id, developer_id, developer_app_id, need_download_profile=True, force=True): | ||||
|     return resign_by_app_id_and_developer(app_id, developer_id, developer_app_id, need_download_profile, force) | ||||
| 
 | ||||
| 
 | ||||
| @app.task | ||||
| def auto_check_ios_developer_active_job(): | ||||
|     auto_check_ios_developer_active() | ||||
| 
 | ||||
| 
 | ||||
| @app.task | ||||
| def get_best_proxy_ips_job(): | ||||
|     get_best_proxy_ips() | ||||
| @ -0,0 +1,34 @@ | ||||
| """fir_ser URL Configuration | ||||
| 
 | ||||
| The `urlpatterns` list routes URLs to views. For more information please see: | ||||
|     https://docs.djangoproject.com/en/3.0/topics/http/urls/ | ||||
| Examples: | ||||
| Function views | ||||
|     1. Add an import:  from my_app import views | ||||
|     2. Add a URL to urlpatterns:  path('', views.home, name='home') | ||||
| Class-based views | ||||
|     1. Add an import:  from other_app.views import Home | ||||
|     2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home') | ||||
| Including another URLconf | ||||
|     1. Import the include() function: from django.urls import include, path | ||||
|     2. Add a URL to urlpatterns:  path('blog/', include('blog.urls')) | ||||
| """ | ||||
| from django.urls import re_path | ||||
| 
 | ||||
| from xsign.views.receiveudids import IosUDIDView, TaskView | ||||
| from xsign.views.supersign import DeveloperView, SuperSignUsedView, AppUDIDUsedView, SuperSignCertView, \ | ||||
|     DeviceUsedBillView, DeveloperDeviceView, DeviceUsedRankInfoView, AppleDeveloperBindAppsView, DeviceTransferBillView | ||||
| 
 | ||||
| urlpatterns = [ | ||||
|     re_path(r"^udid/(?P<short>\w+)$", IosUDIDView.as_view()), | ||||
|     re_path(r"^task/(?P<short>\w+)$", TaskView.as_view()), | ||||
|     re_path(r"^developer$", DeveloperView.as_view()), | ||||
|     re_path(r"^devices$", SuperSignUsedView.as_view()), | ||||
|     re_path(r"^udid$", AppUDIDUsedView.as_view()), | ||||
|     re_path(r"^udevices$", DeveloperDeviceView.as_view()), | ||||
|     re_path(r"^cert$", SuperSignCertView.as_view()), | ||||
|     re_path(r"^bill$", DeviceUsedBillView.as_view()), | ||||
|     re_path(r"^devicebill$", DeviceTransferBillView.as_view()), | ||||
|     re_path(r"^rank$", DeviceUsedRankInfoView.as_view()), | ||||
|     re_path(r"^bind$", AppleDeveloperBindAppsView.as_view()), | ||||
| ] | ||||
| @ -0,0 +1,165 @@ | ||||
| #!/usr/bin/env python | ||||
| # -*- coding:utf-8 -*- | ||||
| # project: 4月 | ||||
| # author: NinEveN | ||||
| # date: 2021/4/16 | ||||
| 
 | ||||
| 
 | ||||
| import logging | ||||
| 
 | ||||
| from django.db.models import Count, Sum, Q | ||||
| 
 | ||||
| from api.models import AppReleaseInfo, UserInfo | ||||
| from common.base.baseutils import is_valid_phone | ||||
| from common.core.sysconfig import Config | ||||
| from xsign.models import APPSuperSignUsedInfo, UDIDsyncDeveloper, AppUDID, APPToDeveloper, AppIOSDeveloperInfo, \ | ||||
|     IosDeveloperPublicPoolBill, IosDeveloperBill, DeveloperDevicesID, AppleDeveloperToAppUse, DeveloperAppID | ||||
| 
 | ||||
| logger = logging.getLogger(__name__) | ||||
| 
 | ||||
| 
 | ||||
| def get_ios_developer_public_num(user_obj): | ||||
|     add_number = get_user_public_sign_num(user_obj) | ||||
|     if not add_number: | ||||
|         add_number = -99 | ||||
|     used_number = get_user_public_used_sign_num(user_obj) | ||||
|     return add_number - used_number | ||||
| 
 | ||||
| 
 | ||||
| def get_user_public_sign_num(user_obj): | ||||
|     add_number = IosDeveloperBill.objects.filter(to_user_id=user_obj, status=2).aggregate( | ||||
|         number=Sum('number')) | ||||
|     number = add_number.get("number", 0) | ||||
|     return number if number else 0 | ||||
| 
 | ||||
| 
 | ||||
| def get_user_public_used_sign_num(user_obj): | ||||
|     used_number = IosDeveloperPublicPoolBill.objects.filter(user_id=user_obj, udid_sync_info__isnull=False).exclude( | ||||
|         udid_sync_info__developerid__user_id=user_obj).values('number', | ||||
|                                                               'udid_sync_info_id').annotate( | ||||
|         counts=Count('udid_sync_info_id')).aggregate(number=Sum('number')) | ||||
|     number = used_number.get("number", 0) | ||||
|     return number if number else 0 | ||||
| 
 | ||||
| 
 | ||||
| # def get_developer_can_used_from_public_sign(user_obj): | ||||
| #     o_number_info = IosDeveloperBill.objects.filter(to_user_id__isnull=False, user_id=user_obj).values( | ||||
| #         'number').aggregate(number=Sum('number')) | ||||
| #     o_number = o_number_info.get("number", 0) | ||||
| #     if o_number is None: | ||||
| #         o_number = 0 | ||||
| #     u_number_info = IosDeveloperPublicPoolBill.objects.filter( | ||||
| #         user_id_id__in=IosDeveloperBill.objects.filter(user_id=user_obj).values('to_user_id_id')).values( | ||||
| #         'number', | ||||
| #         'udid_sync_info_id').annotate( | ||||
| #         counts=Count('udid_sync_info_id')).aggregate(number=Sum('number')) | ||||
| #     u_number = u_number_info.get("number", 0) | ||||
| #     if u_number is None: | ||||
| #         u_number = 0 | ||||
| #     return o_number - u_number | ||||
| 
 | ||||
| 
 | ||||
| def check_super_sign_permission(user_obj): | ||||
|     if not user_obj.supersign_active: | ||||
|         return False | ||||
|     developer_count = AppIOSDeveloperInfo.objects.filter(user_id=user_obj).count() | ||||
|     if developer_count == 0 and get_ios_developer_public_num(user_obj) < 0: | ||||
|         return False | ||||
|     return True | ||||
| 
 | ||||
| 
 | ||||
| def check_ipa_is_latest_sign(app_obj, developer_obj=None): | ||||
|     if AppUDID.objects.filter(app_id=app_obj, udid__developerid=developer_obj, sign_status__lt=4).first(): | ||||
|         return | ||||
|     release_obj = AppReleaseInfo.objects.filter(app_id=app_obj, is_master=True).first() | ||||
|     all_app_to_dev = APPToDeveloper.objects.filter(app_id=app_obj) | ||||
|     if developer_obj: | ||||
|         all_app_to_dev = all_app_to_dev.filter(developerid=developer_obj) | ||||
|     all_app_to_dev = all_app_to_dev.all() | ||||
|     t_count = 0 | ||||
|     for apptodev_obj in all_app_to_dev: | ||||
|         if release_obj.release_id == apptodev_obj.release_file: | ||||
|             t_count += 1 | ||||
|     if t_count == all_app_to_dev.count(): | ||||
|         return True | ||||
| 
 | ||||
| 
 | ||||
| def get_developer_udided(developer_obj): | ||||
|     super_sing_used_obj = APPSuperSignUsedInfo.objects.filter(developerid=developer_obj).all() | ||||
|     udid_sync_developer_obj = UDIDsyncDeveloper.objects.filter(developerid=developer_obj).all() | ||||
|     developer_udid_lists = [] | ||||
|     super_sign_udid_lists = [] | ||||
|     if udid_sync_developer_obj: | ||||
|         developer_udid_lists = list(udid_sync_developer_obj.values_list("udid")) | ||||
|     if super_sing_used_obj: | ||||
|         super_sign_udid_lists = list(super_sing_used_obj.values_list("udid__udid__udid")) | ||||
|     return len(set(developer_udid_lists) - set(super_sign_udid_lists)), len(set(super_sign_udid_lists)), len( | ||||
|         set(developer_udid_lists)) | ||||
| 
 | ||||
| 
 | ||||
| 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) | ||||
| 
 | ||||
| 
 | ||||
| def check_uid_has_relevant(user_uid, to_user_uid): | ||||
|     if user_uid and to_user_uid: | ||||
|         return IosDeveloperBill.objects.filter(user_id__uid=user_uid, to_user_id__uid=to_user_uid, status=2).first() | ||||
| 
 | ||||
| 
 | ||||
| def usable_number(developer_obj): | ||||
|     d_count = UDIDsyncDeveloper.objects.filter(developerid=developer_obj).count() | ||||
|     u_count = developer_obj.usable_number | ||||
|     return d_count if d_count > u_count else u_count | ||||
| 
 | ||||
| 
 | ||||
| def get_developer_devices(developer_obj_lists): | ||||
|     other_used_sum = 0 | ||||
|     flyapp_used_sum = 0 | ||||
|     max_total = 0 | ||||
|     for dev_obj in developer_obj_lists.filter(status__in=Config.DEVELOPER_USE_STATUS): | ||||
|         other_used, flyapp_used, _ = get_developer_udided(dev_obj) | ||||
|         other_used_sum += other_used | ||||
|         flyapp_used_sum += flyapp_used | ||||
|         max_total += 100 | ||||
| 
 | ||||
|     use_number_obj_list = developer_obj_lists.filter(status__in=Config.DEVELOPER_USE_STATUS) | ||||
|     all_use_number = 0 | ||||
|     all_usable_number = 0 | ||||
|     for use_number_obj in use_number_obj_list: | ||||
|         all_usable_number += usable_number(use_number_obj) | ||||
|         all_use_number += DeveloperDevicesID.objects.filter(developerid=use_number_obj).values( | ||||
|             'udid').distinct().count() | ||||
|     use_num = { | ||||
|         "all_usable_number": all_usable_number, | ||||
|         "all_use_number": all_use_number, | ||||
|         "other_used_sum": other_used_sum, | ||||
|         "flyapp_used_sum": flyapp_used_sum, | ||||
|         "max_total": max_total | ||||
|     } | ||||
|     return use_num | ||||
| 
 | ||||
| 
 | ||||
| def get_user_obj_from_epu(user_id): | ||||
|     if is_valid_phone(user_id): | ||||
|         user_obj = UserInfo.objects.filter(mobile=user_id).first() | ||||
|     else: | ||||
|         user_obj = UserInfo.objects.filter(Q(email=user_id) | Q(uid=user_id)).first() | ||||
|     return user_obj | ||||
| 
 | ||||
| 
 | ||||
| def get_app_sign_info(app_obj): | ||||
|     return { | ||||
|         'count': APPToDeveloper.objects.filter(app_id=app_obj).count(), | ||||
|         'private_developer_number': AppleDeveloperToAppUse.objects.filter(app_id=app_obj).count(), | ||||
|         'supersign_used_number': APPSuperSignUsedInfo.objects.filter(app_id=app_obj).all().count(), | ||||
|         'developer_used_count': DeveloperAppID.objects.filter(app_id=app_obj).all().count(), | ||||
|         'private_developer_used_number': DeveloperDevicesID.objects.filter(app_id=app_obj, | ||||
|                                                                            developerid__appledevelopertoappuse__app_id=app_obj).distinct().count() | ||||
|     } | ||||
| @ -0,0 +1,363 @@ | ||||
| import logging | ||||
| 
 | ||||
| from django.db.models import Sum | ||||
| from rest_framework import serializers | ||||
| 
 | ||||
| from common.base.baseutils import AppleDeveloperUid, get_choices_dict | ||||
| from common.core.sysconfig import Config | ||||
| from xsign import models | ||||
| from xsign.utils.modelutils import get_developer_udided | ||||
| 
 | ||||
| logger = logging.getLogger(__name__) | ||||
| 
 | ||||
| 
 | ||||
| class DeveloperSerializer(serializers.ModelSerializer): | ||||
|     class Meta: | ||||
|         model = models.AppIOSDeveloperInfo | ||||
|         # depth = 1 | ||||
|         exclude = ["id", "user_id", "p8key"] | ||||
| 
 | ||||
|     developer_used_number = serializers.SerializerMethodField() | ||||
|     developer_used_other_number = serializers.SerializerMethodField() | ||||
|     use_number = serializers.SerializerMethodField() | ||||
|     app_used_count = serializers.SerializerMethodField() | ||||
|     is_disabled = serializers.SerializerMethodField() | ||||
|     app_private_number = serializers.SerializerMethodField() | ||||
|     app_private_used_number = serializers.SerializerMethodField() | ||||
|     app_usable_number = serializers.SerializerMethodField() | ||||
|     app_used_number = serializers.SerializerMethodField() | ||||
|     private_usable_number = serializers.SerializerMethodField() | ||||
|     status_display = serializers.CharField(source="get_status_display") | ||||
| 
 | ||||
|     def get_app_usable_number(self, obj): | ||||
|         app_id = self.context.get('app_id') | ||||
|         if app_id: | ||||
|             apple_to_app_obj = models.AppleDeveloperToAppUse.objects.filter(developerid=obj, | ||||
|                                                                             app_id__app_id=app_id).first() | ||||
|             if apple_to_app_obj: | ||||
|                 return apple_to_app_obj.usable_number | ||||
|         return 0 | ||||
| 
 | ||||
|     def get_app_used_number(self, obj): | ||||
|         app_id = self.context.get('app_id', '') | ||||
|         if app_id: | ||||
|             return models.DeveloperDevicesID.objects.filter(developerid=obj, | ||||
|                                                             app_id__app_id=app_id).distinct().count() | ||||
|         return 0 | ||||
| 
 | ||||
|     def get_developer_used_number(self, obj): | ||||
|         return models.UDIDsyncDeveloper.objects.filter(developerid=obj).count() | ||||
| 
 | ||||
|     def get_developer_used_other_number(self, obj): | ||||
|         return get_developer_udided(obj)[0] | ||||
| 
 | ||||
|     def get_is_disabled(self, obj): | ||||
|         app_id = self.context.get('app_id', '') | ||||
|         if app_id: | ||||
|             developer_app_obj = models.DeveloperAppID.objects.filter(developerid=obj, | ||||
|                                                                      developerid__status__in=Config.DEVELOPER_USE_STATUS, | ||||
|                                                                      developerid__certid__isnull=False) | ||||
|             if developer_app_obj.filter(app_id__app_id=app_id).distinct().count(): | ||||
|                 return False | ||||
|             else: | ||||
|                 if developer_app_obj and developer_app_obj.distinct().count() < obj.app_limit_number: | ||||
|                     return False | ||||
|             return True | ||||
|         return True | ||||
| 
 | ||||
|     def get_app_private_number(self, obj): | ||||
|         return models.AppleDeveloperToAppUse.objects.filter(developerid=obj).count() | ||||
| 
 | ||||
|     def get_private_usable_number(self, obj): | ||||
|         used_number = models.AppleDeveloperToAppUse.objects.filter(developerid=obj).values('usable_number').aggregate( | ||||
|             Sum('usable_number')).get('usable_number__sum') | ||||
|         if not used_number: | ||||
|             used_number = 0 | ||||
|         return used_number | ||||
| 
 | ||||
|     def get_app_private_used_number(self, obj): | ||||
|         return models.DeveloperDevicesID.objects.filter(developerid=obj, | ||||
|                                                         app_id__appledevelopertoappuse__developerid=obj).values( | ||||
|             'udid').distinct().count() | ||||
| 
 | ||||
|     def get_use_number(self, obj): | ||||
|         return models.DeveloperDevicesID.objects.filter(developerid=obj).values('udid').distinct().count() | ||||
| 
 | ||||
|     def get_app_used_count(self, obj): | ||||
|         return models.DeveloperAppID.objects.filter(developerid=obj).count() | ||||
| 
 | ||||
| 
 | ||||
| class SuperSignUsedSerializer(serializers.ModelSerializer): | ||||
|     class Meta: | ||||
|         model = models.APPSuperSignUsedInfo | ||||
|         fields = ["created_time", "device_udid", "device_name", "developer_id", "bundle_id", "bundle_name", "other_uid", | ||||
|                   "developer_description", "developer_status"] | ||||
| 
 | ||||
|     device_udid = serializers.CharField(source="udid.udid.udid") | ||||
|     device_name = serializers.CharField(source="udid.product") | ||||
|     bundle_id = serializers.CharField(source="app_id.bundle_id") | ||||
|     bundle_name = serializers.CharField(source="app_id.name") | ||||
|     other_uid = serializers.SerializerMethodField() | ||||
|     developer_id = serializers.SerializerMethodField() | ||||
|     developer_description = serializers.SerializerMethodField() | ||||
|     developer_status = serializers.SerializerMethodField() | ||||
| 
 | ||||
|     def get_developer_status(self, obj): | ||||
|         return obj.developerid.get_status_display() | ||||
| 
 | ||||
|     def get_developer_id(self, obj): | ||||
|         issuer_id = obj.developerid.issuer_id | ||||
|         if self.context.get('mine'): | ||||
|             return issuer_id | ||||
|         else: | ||||
|             return f"{Config.DEVELOPER_UID_KEY}{AppleDeveloperUid().get_encrypt_uid(issuer_id)}" | ||||
| 
 | ||||
|     def get_developer_description(self, obj): | ||||
|         if self.context.get('mine'): | ||||
|             return obj.developerid.description | ||||
|         else: | ||||
|             return '公共账号池' | ||||
| 
 | ||||
|     def get_other_uid(self, obj): | ||||
|         user_obj = self.context.get('user_obj') | ||||
|         if obj.user_id != obj.developerid.user_id: | ||||
|             if user_obj.uid != obj.user_id.uid: | ||||
|                 return {'uid': obj.user_id.uid, 'name': obj.user_id.first_name} | ||||
| 
 | ||||
| 
 | ||||
| class DeveloperDeviceSerializer(serializers.ModelSerializer): | ||||
|     class Meta: | ||||
|         model = models.UDIDsyncDeveloper | ||||
|         exclude = ["id", "developerid"] | ||||
| 
 | ||||
|     developer_id = serializers.CharField(source="developerid.issuer_id") | ||||
|     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: | ||||
|         model = models.AppUDID | ||||
|         # depth = 1 | ||||
|         exclude = ["updated_time", "sign_status"] | ||||
| 
 | ||||
|     bundle_name = serializers.CharField(source="app_id.name") | ||||
|     bundle_id = serializers.CharField(source="app_id.bundle_id") | ||||
|     issuer_id = serializers.SerializerMethodField() | ||||
|     developer_description = serializers.SerializerMethodField() | ||||
|     developer_status = serializers.SerializerMethodField() | ||||
|     udid = serializers.CharField(source="udid.udid") | ||||
|     is_mine = serializers.SerializerMethodField() | ||||
|     other_uid = serializers.SerializerMethodField() | ||||
| 
 | ||||
|     def get_issuer_id(self, obj): | ||||
|         issuer_id = obj.udid.developerid.issuer_id | ||||
|         if self.context.get('mine'): | ||||
|             return issuer_id | ||||
|         else: | ||||
|             return f"{Config.DEVELOPER_UID_KEY}{AppleDeveloperUid().get_encrypt_uid(issuer_id)}" | ||||
| 
 | ||||
|     def get_developer_status(self, obj): | ||||
|         return obj.udid.developerid.get_status_display() | ||||
| 
 | ||||
|     def get_developer_description(self, obj): | ||||
|         if self.context.get('mine'): | ||||
|             return obj.udid.developerid.description | ||||
|         else: | ||||
|             return '公共账号池' | ||||
| 
 | ||||
|     def get_other_uid(self, obj): | ||||
|         user_obj = self.context.get('user_obj') | ||||
|         user_obj_new = obj.app_id.user_id | ||||
|         if user_obj.pk != user_obj_new.pk: | ||||
|             super_user_obj = models.APPSuperSignUsedInfo.objects.filter(udid=obj, app_id=obj.app_id).first() | ||||
|             if super_user_obj.developerid.user_id != user_obj_new: | ||||
|                 return {'uid': user_obj_new.uid, 'name': user_obj_new.first_name} | ||||
| 
 | ||||
|     def get_is_mine(self, obj): | ||||
|         return self.context.get('mine') | ||||
| 
 | ||||
| 
 | ||||
| class BillAppInfoSerializer(serializers.ModelSerializer): | ||||
|     class Meta: | ||||
|         model = models.Apps | ||||
|         fields = ["pk", "app_id", "user_id", "name", "type", "bundle_id", "updated_time"] | ||||
| 
 | ||||
| 
 | ||||
| class BillDeveloperInfoSerializer(serializers.ModelSerializer): | ||||
|     class Meta: | ||||
|         model = models.AppIOSDeveloperInfo | ||||
|         fields = ["pk", "issuer_id", "user_id", "certid"] | ||||
| 
 | ||||
| 
 | ||||
| class BillInfoSerializer(serializers.ModelSerializer): | ||||
|     class Meta: | ||||
|         model = models.IosDeveloperPublicPoolBill | ||||
|         exclude = ["user_id", "developer_info", "app_info", "udid_sync_info", "app_id"] | ||||
| 
 | ||||
|     app_name = serializers.SerializerMethodField() | ||||
|     description = serializers.SerializerMethodField() | ||||
|     is_used = serializers.SerializerMethodField() | ||||
|     app_status = serializers.SerializerMethodField() | ||||
|     remote_addr = serializers.SerializerMethodField() | ||||
| 
 | ||||
|     def get_remote_addr(self, obj): | ||||
|         return obj.remote_addr | ||||
| 
 | ||||
|     def get_app_status(self, obj): | ||||
|         if obj.app_id: | ||||
|             return True | ||||
|         return False | ||||
| 
 | ||||
|     def get_is_used(self, obj): | ||||
|         if obj.udid_sync_info: | ||||
|             return True | ||||
|         return False | ||||
| 
 | ||||
|     def get_app_name(self, obj): | ||||
|         if obj.app_info: | ||||
|             return obj.app_info.get('name') | ||||
| 
 | ||||
|     def get_description(self, obj): | ||||
|         if obj.udid: | ||||
|             return f"{self.get_app_name(obj)}-消耗- {obj.number} 设备数" | ||||
| 
 | ||||
| 
 | ||||
| class BillTransferSerializer(serializers.ModelSerializer): | ||||
|     class Meta: | ||||
|         model = models.IosDeveloperBill | ||||
|         exclude = ["id", "remote_addr", "user_id", "to_user_id"] | ||||
| 
 | ||||
|     target_user = serializers.SerializerMethodField() | ||||
|     cancel = serializers.SerializerMethodField() | ||||
|     number = serializers.SerializerMethodField() | ||||
|     status_display = serializers.CharField(source='get_status_display') | ||||
| 
 | ||||
|     def get_target_user(self, obj): | ||||
|         user_obj = obj.user_id | ||||
|         if self.get_cancel(obj): | ||||
|             user_obj = obj.to_user_id | ||||
|         return {'uid': user_obj.uid, 'name': user_obj.first_name} | ||||
| 
 | ||||
|     def get_cancel(self, obj): | ||||
|         return self.context.get('user_obj').pk == obj.user_id.pk | ||||
| 
 | ||||
|     def get_number(self, obj): | ||||
|         if self.get_cancel(obj): | ||||
|             return -obj.number | ||||
|         else: | ||||
|             return obj.number | ||||
| 
 | ||||
| 
 | ||||
| class AppleDeveloperToAppUseSerializer(serializers.ModelSerializer): | ||||
|     class Meta: | ||||
|         model = models.AppleDeveloperToAppUse | ||||
|         # exclude = ["user_id", "to_user_id", "developer_info", "app_info", "udid_sync_info", "app_id"] | ||||
|         fields = ["issuer_id", "app_id", "created_time", "description", "usable_number"] | ||||
| 
 | ||||
|     issuer_id = serializers.CharField(source="developerid.issuer_id") | ||||
|     app_id = serializers.CharField(source="app_id.app_id") | ||||
| 
 | ||||
| 
 | ||||
| class AppleDeveloperToAppUseAppsSerializer(serializers.ModelSerializer): | ||||
|     class Meta: | ||||
|         model = models.Apps | ||||
|         fields = ["app_id", "name", "short", "bundle_id", "description", "app_usable_number", "app_used_number"] | ||||
| 
 | ||||
|     app_usable_number = serializers.SerializerMethodField() | ||||
|     app_used_number = serializers.SerializerMethodField() | ||||
| 
 | ||||
|     def get_app_usable_number(self, obj): | ||||
|         issuer_id = self.context.get('issuer_id') | ||||
|         if issuer_id: | ||||
|             apple_to_app_obj = models.AppleDeveloperToAppUse.objects.filter(developerid__issuer_id=issuer_id, | ||||
|                                                                             app_id=obj).first() | ||||
|             if apple_to_app_obj: | ||||
|                 return apple_to_app_obj.usable_number | ||||
|         return 0 | ||||
| 
 | ||||
|     def get_app_used_number(self, obj): | ||||
|         issuer_id = self.context.get('issuer_id') | ||||
|         if issuer_id: | ||||
|             return models.DeveloperDevicesID.objects.filter(app_id=obj, developerid__issuer_id=issuer_id).count() | ||||
|         return 0 | ||||
| 
 | ||||
| 
 | ||||
| class AdminDeveloperSerializer(DeveloperSerializer): | ||||
|     class Meta: | ||||
|         model = models.AppIOSDeveloperInfo | ||||
|         # depth = 1 | ||||
|         exclude = ["p8key", ] | ||||
| 
 | ||||
|     auth_type_choices = serializers.SerializerMethodField() | ||||
| 
 | ||||
|     def get_auth_type_choices(self, obj): | ||||
|         return get_choices_dict(obj.auth_type_choices) | ||||
| 
 | ||||
|     status_choices = serializers.SerializerMethodField() | ||||
| 
 | ||||
|     def get_status_choices(self, obj): | ||||
|         return get_choices_dict(obj.status_choices) | ||||
| 
 | ||||
| 
 | ||||
| class AdminSuperSignUsedSerializer(SuperSignUsedSerializer): | ||||
|     class Meta: | ||||
|         model = models.APPSuperSignUsedInfo | ||||
|         fields = ["created_time", "device_udid", "device_name", "developer_id", "bundle_id", "bundle_name", "app_id", | ||||
|                   "id", "user_id", "short", "developer_pk"] | ||||
| 
 | ||||
|     app_id = serializers.IntegerField(source="app_id.pk") | ||||
| 
 | ||||
|     user_id = serializers.IntegerField(source="user_id.pk") | ||||
| 
 | ||||
|     short = serializers.CharField(source="app_id.short") | ||||
|     developer_status = serializers.CharField(source="developerid.get_status_display") | ||||
|     developer_id = serializers.CharField(source="developerid.issuer_id") | ||||
|     developer_description = serializers.CharField(source="developerid.description") | ||||
|     developer_pk = serializers.IntegerField(source="developerid.pk") | ||||
| 
 | ||||
| 
 | ||||
| class AdminBillInfoSerializer(BillInfoSerializer): | ||||
|     class Meta: | ||||
|         model = models.IosDeveloperPublicPoolBill | ||||
|         fields = '__all__' | ||||
|         read_only_fields = ["id", "user_id", "to_user_id", "action", "number", "app_info", "udid", | ||||
|                             "udid_sync_info", "app_id", "remote_addr"] | ||||
| 
 | ||||
|     action_choices = serializers.SerializerMethodField() | ||||
| 
 | ||||
|     def get_action_choices(self, obj): | ||||
|         return get_choices_dict(obj.action_choices) | ||||
| 
 | ||||
| 
 | ||||
| class AppSignSerializer(serializers.ModelSerializer): | ||||
|     class Meta: | ||||
|         model = models.Apps | ||||
|         exclude = ["user_id", "id"] | ||||
| 
 | ||||
|     private_developer_number = serializers.SerializerMethodField() | ||||
| 
 | ||||
|     def get_private_developer_number(self, obj): | ||||
|         return models.AppleDeveloperToAppUse.objects.filter(app_id=obj).count() | ||||
| 
 | ||||
|     private_developer_used_number = serializers.SerializerMethodField() | ||||
| 
 | ||||
|     def get_private_developer_used_number(self, obj): | ||||
|         return models.DeveloperDevicesID.objects.filter(app_id=obj, | ||||
|                                                         developerid__appledevelopertoappuse__app_id=obj).distinct().count() | ||||
| 
 | ||||
|     supersign_used_number = serializers.SerializerMethodField() | ||||
| 
 | ||||
|     def get_supersign_used_number(self, obj): | ||||
|         return models.APPSuperSignUsedInfo.objects.filter(app_id=obj).all().count() | ||||
| 
 | ||||
|     developer_used_count = serializers.SerializerMethodField() | ||||
| 
 | ||||
|     def get_developer_used_count(self, obj): | ||||
|         return models.DeveloperAppID.objects.filter(app_id=obj).all().count() | ||||
| @ -0,0 +1,73 @@ | ||||
| #!/usr/bin/env python | ||||
| # -*- coding:utf-8 -*- | ||||
| # project: 5月 | ||||
| # author: liuyu | ||||
| # date: 2020/5/7 | ||||
| import logging | ||||
| 
 | ||||
| from api.utils.utils import delete_local_files | ||||
| from common.utils.storage import Storage | ||||
| from xsign.models import APPToDeveloper | ||||
| 
 | ||||
| logger = logging.getLogger(__name__) | ||||
| 
 | ||||
| 
 | ||||
| def delete_app_to_dev_and_file(developer_obj, app_id): | ||||
|     app_to_developer_obj = APPToDeveloper.objects.filter(developerid=developer_obj, app_id_id=app_id) | ||||
|     if app_to_developer_obj: | ||||
|         binary_file = app_to_developer_obj.first().binary_file + ".ipa" | ||||
|         delete_local_files(binary_file) | ||||
|         storage = Storage(developer_obj.user_id) | ||||
|         storage.delete_file(binary_file) | ||||
|         app_to_developer_obj.delete() | ||||
| 
 | ||||
| 
 | ||||
| def make_resigned(bin_url, img_url, bundle_id, app_version, name): | ||||
|     ios_plist_tem = """<?xml version="1.0" encoding="UTF-8"?> | ||||
| <plist version="1.0"><dict> | ||||
|   <key>items</key> | ||||
|   <array> | ||||
|     <dict> | ||||
|       <key>assets</key> | ||||
|       <array> | ||||
|         <dict> | ||||
|           <key>kind</key> | ||||
|           <string>software-package</string> | ||||
|           <key>url</key> | ||||
|           <string><![CDATA[{bin_url}]]></string> | ||||
|         </dict> | ||||
|         <dict> | ||||
|           <key>kind</key> | ||||
|           <string>display-image</string> | ||||
|           <key>needs-shine</key> | ||||
|           <integer>0</integer> | ||||
|           <key>url</key> | ||||
|           <string><![CDATA[{img_url}]]></string> | ||||
|         </dict> | ||||
|         <dict> | ||||
|           <key>kind</key> | ||||
|           <string>full-size-image</string> | ||||
|           <key>needs-shine</key> | ||||
|           <true/> | ||||
|           <key>url</key> | ||||
|           <string><![CDATA[{img_url}]]></string> | ||||
|         </dict> | ||||
|       </array> | ||||
|       <key>metadata</key> | ||||
|       <dict> | ||||
|         <key>bundle-identifier</key> | ||||
|         <string>{bundle_id}</string> | ||||
|         <key>bundle-version</key> | ||||
|         <string><![CDATA[{app_version}]]></string> | ||||
|         <key>kind</key> | ||||
|         <string>software</string> | ||||
|         <key>title</key> | ||||
|         <string><![CDATA[{name}]]></string> | ||||
|       </dict> | ||||
|     </dict> | ||||
|   </array> | ||||
| </dict> | ||||
| </plist>""".format(bin_url=bin_url, img_url=img_url, bundle_id=bundle_id, app_version=app_version, name=name) | ||||
|     logger.info( | ||||
|         f"make_resigned bin_url {bin_url} ,img_url {img_url}, bundle_id {bundle_id}, app_version {app_version}, name {name}") | ||||
|     return ios_plist_tem | ||||
| @ -0,0 +1,151 @@ | ||||
| #!/usr/bin/env python | ||||
| # -*- coding:utf-8 -*- | ||||
| # project: 3月 | ||||
| # author: liuyu | ||||
| # date: 2020/3/6 | ||||
| import logging | ||||
| 
 | ||||
| from rest_framework.response import Response | ||||
| from rest_framework.views import APIView | ||||
| 
 | ||||
| from api.models import Apps | ||||
| from api.utils.response import BaseResponse | ||||
| from common.cache.state import MigrateStorageState, CleanAppSignDataState | ||||
| from common.core.auth import ExpiringTokenAuthentication | ||||
| from common.core.sysconfig import Config | ||||
| from common.utils.caches import del_cache_response_by_short | ||||
| from xsign.models import AppUDID | ||||
| from xsign.tasks import run_resign_task | ||||
| from xsign.utils.modelutils import get_app_sign_info, check_super_sign_permission | ||||
| from xsign.utils.supersignutils import IosUtils | ||||
| 
 | ||||
| logger = logging.getLogger(__name__) | ||||
| 
 | ||||
| 
 | ||||
| class AppSignInfoView(APIView): | ||||
|     authentication_classes = [ExpiringTokenAuthentication, ] | ||||
| 
 | ||||
|     def get(self, request, app_id): | ||||
|         res = BaseResponse() | ||||
|         if app_id: | ||||
|             app_obj = Apps.objects.filter(user_id=request.user, app_id=app_id).first() | ||||
|             if app_obj: | ||||
|                 res.data = get_app_sign_info(app_obj) | ||||
|             else: | ||||
|                 logger.error(f"app_id:{app_id} is not found in user:{request.user}") | ||||
|                 res.msg = "未找到该应用" | ||||
|                 res.code = 1003 | ||||
| 
 | ||||
|         return Response(res.dict) | ||||
| 
 | ||||
|     def put(self, request, app_id): | ||||
|         res = BaseResponse() | ||||
|         if app_id: | ||||
|             data = request.data | ||||
|             if MigrateStorageState(request.user.uid).get_state(): | ||||
|                 res.code = 1008 | ||||
|                 res.msg = "数据迁移中,无法处理该操作" | ||||
|                 return Response(res.dict) | ||||
| 
 | ||||
|             clean = data.get("clean", None) | ||||
|             if clean: | ||||
|                 with CleanAppSignDataState(request.user.uid) as state: | ||||
|                     if state: | ||||
|                         logger.info(f"app_id:{app_id} clean:{clean} ,close super_sign should clean_app_by_user_obj") | ||||
|                         app_obj = Apps.objects.filter(user_id=request.user, app_id=app_id).first() | ||||
|                         IosUtils.clean_app_by_user_obj(app_obj) | ||||
|                     else: | ||||
|                         res.code = 1008 | ||||
|                         res.msg = "数据清理中,请耐心等待" | ||||
|                 return Response(res.dict) | ||||
| 
 | ||||
|             has_combo = data.get("has_combo", None) | ||||
|             if has_combo: | ||||
|                 ... | ||||
|             else: | ||||
|                 try: | ||||
|                     do_sign_flag = 0 | ||||
|                     app_obj = Apps.objects.filter(user_id=request.user, app_id=app_id).first() | ||||
|                     logger.info("app_id:%s update old data:%s" % (app_id, app_obj.__dict__)) | ||||
|                     update_fields = [] | ||||
|                     if app_obj.issupersign: | ||||
|                         app_obj.supersign_limit_number = data.get("supersign_limit_number", | ||||
|                                                                   app_obj.supersign_limit_number) | ||||
|                         update_fields.append("supersign_limit_number") | ||||
|                         if app_obj.supersign_type in [x[0] for x in list(app_obj.supersign_type_choices)]: | ||||
|                             if app_obj.supersign_type != data.get("supersign_type", app_obj.supersign_type): | ||||
|                                 do_sign_flag = 1 | ||||
|                             app_obj.supersign_type = data.get("supersign_type", app_obj.supersign_type) | ||||
|                             update_fields.append("supersign_type") | ||||
|                         new_bundle_id = data.get("new_bundle_id", None) | ||||
|                         new_bundle_name = data.get("new_bundle_name", None) | ||||
|                         if new_bundle_id is not None: | ||||
|                             if new_bundle_id and new_bundle_id != app_obj.bundle_id and len(new_bundle_id) > 3: | ||||
|                                 if new_bundle_id != app_obj.new_bundle_id: | ||||
|                                     do_sign_flag = 2 | ||||
|                                 app_obj.new_bundle_id = new_bundle_id | ||||
|                             if new_bundle_id == '' or (app_obj.new_bundle_id != new_bundle_id): | ||||
|                                 if app_obj.bundle_id != app_obj.new_bundle_id: | ||||
|                                     do_sign_flag = 2 | ||||
|                                 app_obj.new_bundle_id = app_obj.bundle_id | ||||
|                             update_fields.append('new_bundle_id') | ||||
|                         if new_bundle_name is not None: | ||||
|                             if new_bundle_name and new_bundle_name != app_obj.name and len(new_bundle_name) > 0: | ||||
|                                 if new_bundle_name != app_obj.new_bundle_name: | ||||
|                                     do_sign_flag = 2 | ||||
|                                 app_obj.new_bundle_name = new_bundle_name | ||||
|                             if new_bundle_name == '' or (app_obj.new_bundle_name != new_bundle_name): | ||||
|                                 if app_obj.name != app_obj.new_bundle_name: | ||||
|                                     do_sign_flag = 2 | ||||
|                                 app_obj.new_bundle_name = app_obj.name | ||||
|                             update_fields.append('new_bundle_name') | ||||
|                     if app_obj.type == 1 and data.get('issupersign', -1) != -1: | ||||
|                         if data.get('issupersign', -1) == 1 and not check_super_sign_permission(request.user): | ||||
|                             logger.error(f"app_id:{app_id} can't open super_sign,owner has no ios developer") | ||||
|                             res.code = 1008 | ||||
|                             res.msg = "超级签余额不足,无法开启" | ||||
|                             return Response(res.dict) | ||||
|                         do_sign_flag = 3 | ||||
|                         app_obj.issupersign = data.get("issupersign", app_obj.issupersign) | ||||
|                         update_fields.append("issupersign") | ||||
|                     if app_obj.issupersign and data.get('change_auto_sign', -1) != -1: | ||||
|                         if data.get('change_auto_sign', -1) == 1: | ||||
|                             do_sign_flag = 3 | ||||
|                         app_obj.change_auto_sign = data.get("change_auto_sign", app_obj.change_auto_sign) | ||||
|                         update_fields.append("change_auto_sign") | ||||
| 
 | ||||
|                     logger.info(f"app_id:{app_id} update new data:{app_obj.__dict__}") | ||||
|                     app_obj.save(update_fields=update_fields) | ||||
|                     if app_obj.issupersign: | ||||
|                         c_task = None | ||||
|                         if do_sign_flag == 1: | ||||
|                             AppUDID.objects.filter(app_id=app_obj).update(sign_status=2) | ||||
|                             if app_obj.change_auto_sign: | ||||
|                                 c_task = run_resign_task(app_obj.pk, True) | ||||
| 
 | ||||
|                         if do_sign_flag == 2: | ||||
|                             AppUDID.objects.filter(app_id=app_obj, sign_status__gte=3).update(sign_status=3) | ||||
|                             if app_obj.change_auto_sign: | ||||
|                                 flag = False | ||||
|                                 if AppUDID.objects.filter(app_id=app_obj, sign_status=2, | ||||
|                                                           udid__developerid__status__in=Config.DEVELOPER_WRITE_STATUS).first(): | ||||
|                                     flag = True | ||||
|                                 c_task = run_resign_task(app_obj.pk, flag) | ||||
| 
 | ||||
|                         if do_sign_flag == 3: | ||||
|                             if app_obj.change_auto_sign: | ||||
|                                 flag = False | ||||
|                                 if AppUDID.objects.filter(app_id=app_obj, sign_status=2, | ||||
|                                                           udid__developerid__status__in=Config.DEVELOPER_WRITE_STATUS).first(): | ||||
|                                     flag = True | ||||
|                                 c_task = run_resign_task(app_obj.pk, flag, False) | ||||
| 
 | ||||
|                         if c_task: | ||||
|                             logger.info(f"app {app_obj} run_resign_task msg:{c_task}") | ||||
|                     del_cache_response_by_short(app_obj.app_id) | ||||
|                 except Exception as e: | ||||
|                     logger.error(f"app_id:{app_id} update Exception:{e}") | ||||
|                     res.code = 1005 | ||||
|                     res.msg = "短连接已经存在" | ||||
| 
 | ||||
|         return Response(res.dict) | ||||
| @ -0,0 +1,106 @@ | ||||
| #!/usr/bin/env python | ||||
| # -*- coding:utf-8 -*- | ||||
| # project: 3月  | ||||
| # author: liuyu | ||||
| # date: 2020/3/6 | ||||
| 
 | ||||
| import logging | ||||
| import os | ||||
| 
 | ||||
| from rest_framework.views import APIView | ||||
| 
 | ||||
| from api.models import AppReleaseInfo | ||||
| from common.base.baseutils import get_profile_full_path, make_random_uuid, get_server_domain_from_request | ||||
| from common.core.response import ApiResponse, file_response, mobileprovision_file_response | ||||
| from common.core.sysconfig import Config | ||||
| from common.utils.storage import Storage, get_local_storage | ||||
| from common.utils.token import verify_token | ||||
| from fir_ser import settings | ||||
| from xsign.models import APPToDeveloper, APPSuperSignUsedInfo | ||||
| from xsign.utils.supersignutils import make_sign_udid_mobile_config | ||||
| from xsign.utils.utils import make_resigned | ||||
| 
 | ||||
| logger = logging.getLogger(__name__) | ||||
| 
 | ||||
| 
 | ||||
| def get_post_udid_url(request, short): | ||||
|     server_domain = get_server_domain_from_request(request, Config.POST_UDID_DOMAIN) | ||||
|     path_info_lists = [server_domain, "udid", short] | ||||
|     return "/".join(path_info_lists) | ||||
| 
 | ||||
| 
 | ||||
| class DownloadView(APIView): | ||||
|     """ | ||||
|     文件下载接口,适用于本地存储和所有plist文件下载 | ||||
|     """ | ||||
| 
 | ||||
|     def get(self, request, filename): | ||||
|         down_token = request.query_params.get(settings.DATA_DOWNLOAD_KEY, None) | ||||
|         f_type = filename.split(".")[-1] | ||||
|         flag = True | ||||
|         storage_obj = get_local_storage() | ||||
|         if storage_obj.download_auth_type == 1: | ||||
|             if not down_token: | ||||
|                 logger.error(f"file {filename} download failed lost down_token") | ||||
|                 return ApiResponse(code=1004, msg="缺失token") | ||||
| 
 | ||||
|             flag = verify_token(down_token, filename) | ||||
| 
 | ||||
|         if flag: | ||||
|             if f_type == 'plist': | ||||
|                 release_id = filename.split('.')[0] | ||||
|                 app_to_developer_obj = APPToDeveloper.objects.filter(binary_file=release_id).first() | ||||
|                 if app_to_developer_obj: | ||||
|                     release_obj = AppReleaseInfo.objects.filter(is_master=True, | ||||
|                                                                 app_id=app_to_developer_obj.app_id).first() | ||||
|                 else: | ||||
|                     release_obj = AppReleaseInfo.objects.filter(release_id=release_id).first() | ||||
|                 if release_obj: | ||||
|                     app_obj = release_obj.app_id | ||||
|                     storage = Storage(app_obj.user_id) | ||||
|                     bundle_id = app_obj.bundle_id | ||||
|                     app_version = release_obj.app_version | ||||
|                     name = app_obj.name | ||||
|                     ios_plist_bytes = make_resigned(storage.get_download_url(filename.split('.')[0] + ".ipa"), | ||||
|                                                     storage.get_download_url(release_obj.icon_url), bundle_id, | ||||
|                                                     app_version, name) | ||||
|                     return file_response(ios_plist_bytes, make_random_uuid(), "application/x-plist") | ||||
|                 return ApiResponse(code=1004, msg="plist release_id error") | ||||
|             elif f_type == 'mobileconifg': | ||||
|                 release_obj = AppReleaseInfo.objects.filter(release_id=filename.split('.')[0]).first() | ||||
|                 if release_obj: | ||||
|                     app_obj = release_obj.app_id | ||||
|                     udid_url = get_post_udid_url(request, app_obj.short) | ||||
|                     ios_udid_mobile_config = make_sign_udid_mobile_config(udid_url, f'{app_obj.app_id}_{app_obj.short}', | ||||
|                                                                           app_obj.bundle_id, | ||||
|                                                                           app_obj.name) | ||||
|                     return file_response(ios_udid_mobile_config, make_random_uuid() + '.mobileconfig', | ||||
|                                          "application/x-apple-aspen-config") | ||||
|                 return ApiResponse(code=1004, msg="mobile_config release_id error") | ||||
|             elif f_type == 'mobileprovision': | ||||
|                 release_obj = AppReleaseInfo.objects.filter(release_id=filename.split('.')[0]).first() | ||||
|                 if release_obj: | ||||
|                     app_super_obj = APPSuperSignUsedInfo.objects.filter(app_id=release_obj.app_id).last() | ||||
|                     if not app_super_obj: | ||||
|                         app_super_obj = APPSuperSignUsedInfo.objects.last() | ||||
| 
 | ||||
|                     if not app_super_obj: | ||||
|                         file_path = Config.DEFAULT_MOBILEPROVISION.get("supersign").get('path') | ||||
|                         if file_path and os.path.isfile(file_path): | ||||
|                             return mobileprovision_file_response(file_path) | ||||
|                     else: | ||||
|                         developer_obj = app_super_obj.developerid | ||||
|                         file_path = get_profile_full_path(developer_obj, release_obj.app_id) | ||||
|                         if os.path.isfile(file_path): | ||||
|                             return mobileprovision_file_response(file_path) | ||||
|                         else: | ||||
|                             file_path = Config.DEFAULT_MOBILEPROVISION.get("supersign").get('path') | ||||
|                             if file_path and os.path.isfile(file_path): | ||||
|                                 return mobileprovision_file_response(file_path) | ||||
| 
 | ||||
|                 return ApiResponse(code=1004, msg="mobile_provision release_id error") | ||||
|             else: | ||||
|                 logger.warning("super sing error file type") | ||||
| 
 | ||||
|         logger.error(f"file {filename} download failed. down_token verify failed") | ||||
|         return ApiResponse(code=1004, msg="token校验失败") | ||||
					Loading…
					
					
				
		Reference in new issue