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