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