优化代码,增加苹果签名错误操作记录

dependabot/npm_and_yarn/fir_admin/async-2.6.4
nineven 3 years ago
parent 3ff322849a
commit d1a41a179b
  1. 7
      fir_client/src/components/FirDownload.vue
  2. 7
      fir_client/src/components/ShortDownload.vue
  3. 156
      fir_client/src/components/user/FirSuperSignBase.vue
  4. 7
      fir_client/src/components/user/FirUserAdvert.vue
  5. 8
      fir_client/src/components/user/FirUserDomain.vue
  6. 9
      fir_client/src/components/user/FirUserNotify.vue
  7. 8
      fir_client/src/components/user/FirUserOrders.vue
  8. 8
      fir_client/src/components/user/FirUserStorage.vue
  9. 15
      fir_client/src/restful/index.js
  10. 8
      fir_client/src/utils/base/utils.js
  11. 2
      fir_ser/api/utils/ctasks.py
  12. 4
      fir_ser/common/cache/storage.py
  13. 45
      fir_ser/common/constants.py
  14. 4
      fir_ser/common/libs/apple/appleapiv3.py
  15. 3
      fir_ser/common/libs/mp/wechat.py
  16. 28
      fir_ser/config.py
  17. 5
      fir_ser/fir_ser/settings.py
  18. 57
      fir_ser/xsign/migrations/0002_auto_20220405_1452.py
  19. 58
      fir_ser/xsign/models.py
  20. 14
      fir_ser/xsign/tasks.py
  21. 4
      fir_ser/xsign/urls.py
  22. 11
      fir_ser/xsign/utils/ctasks.py
  23. 23
      fir_ser/xsign/utils/iossignapi.py
  24. 14
      fir_ser/xsign/utils/iproxy.py
  25. 16
      fir_ser/xsign/utils/modelutils.py
  26. 18
      fir_ser/xsign/utils/serializer.py
  27. 7
      fir_ser/xsign/utils/signals.py
  28. 115
      fir_ser/xsign/utils/supersignutils.py
  29. 11
      fir_ser/xsign/views/appinfo.py
  30. 95
      fir_ser/xsign/views/supersign.py

@ -353,7 +353,7 @@
import QRCode from 'qrcodejs2' import QRCode from 'qrcodejs2'
import {appReport, geetest, getAuthTokenFun, getdownloadurl, getShortAppinfo, gettask} from '@/restful/download' import {appReport, geetest, getAuthTokenFun, getdownloadurl, getShortAppinfo, gettask} from '@/restful/download'
import {checkEmail, checkphone, getRandomStr} from "@/utils/base/utils"; import {checkEmail, checkphone, format_time, getRandomStr} from "@/utils/base/utils";
export default { export default {
name: "FirDownload", name: "FirDownload",
@ -923,10 +923,7 @@ export default {
return ftype return ftype
}, },
formatTime: function (stime) { formatTime: function (stime) {
if (stime) { return format_time(stime)
stime = stime.split(".")[0].split("T");
return stime[0] + " " + stime[1]
}
} }
} }
}; };

@ -340,7 +340,7 @@
import QRCode from 'qrcodejs2' import QRCode from 'qrcodejs2'
import {appReport, geetest, getAuthTokenFun, getdownloadurl, getShortAppinfo, gettask} from '@/restful/download' import {appReport, geetest, getAuthTokenFun, getdownloadurl, getShortAppinfo, gettask} from '@/restful/download'
import {checkEmail, checkphone, getRandomStr} from "@/utils/base/utils"; import {checkEmail, checkphone, format_time, getRandomStr} from "@/utils/base/utils";
export default { export default {
name: "ShortDownload", name: "ShortDownload",
@ -881,10 +881,7 @@ export default {
return ftype return ftype
}, },
formatTime: function (stime) { formatTime: function (stime) {
if (stime) { return format_time(stime)
stime = stime.split(".")[0].split("T");
return stime[0] + " " + stime[1]
}
} }
} }
}; };

@ -778,6 +778,10 @@
clearable clearable
placeholder="输入开发者用户ID" placeholder="输入开发者用户ID"
style="width: 30%;margin-right: 30px;margin-bottom: 10px"/> style="width: 30%;margin-right: 30px;margin-bottom: 10px"/>
<el-select v-if="status_choices" v-model="devicestatus" clearable placeholder="设备状态"
style="width: 120px;margin-right: 30px" @change="handleCurrentChange(1)">
<el-option v-for="item in device_status_choices" :key="item.id" :label="item.name" :value="item.id"/>
</el-select>
<el-button icon="el-icon-search" type="primary" @click="handleCurrentChange(1)"> <el-button icon="el-icon-search" type="primary" @click="handleCurrentChange(1)">
搜索 搜索
</el-button> </el-button>
@ -837,16 +841,19 @@
label="设备状态" label="设备状态"
width="110"> width="110">
<template slot-scope="scope"> <template slot-scope="scope">
<el-tooltip v-if="!scope.row.status" content="点击启用"> <el-tooltip v-if="scope.row.status === 'DISABLED'" content="点击启用">
<el-link :underline="false" type="info" @click="changeDevice(scope.row,1)"> <el-link :underline="false" type="info" @click="changeDevice(scope.row,1)">
<el-tag type="info">禁用</el-tag> <el-tag type="info">禁用</el-tag>
</el-link> </el-link>
</el-tooltip> </el-tooltip>
<el-tooltip v-else content="点击禁用,禁用会自动清理已经安装的数据"> <el-tooltip v-else-if="scope.row.status === 'ENABLED'" content="点击禁用,禁用会自动清理已经安装的数据">
<el-link :underline="false" @click="changeDevice(scope.row,0)"> <el-link :underline="false" @click="changeDevice(scope.row,0)">
<el-tag>启用</el-tag> <el-tag>启用</el-tag>
</el-link> </el-link>
</el-tooltip> </el-tooltip>
<el-tooltip v-else :content="`当设备状态为 ${scope.row.device_status} 时,意味着该开发者不可用`">
<el-tag type="danger">{{ scope.row.device_status }}</el-tag>
</el-tooltip>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column <el-table-column
@ -1354,6 +1361,92 @@
</el-table> </el-table>
</el-tab-pane> </el-tab-pane>
<el-tab-pane label="操作日志" name="operatemsg">
<el-input
v-model="appidseach"
clearable
placeholder="输入开发者用户ID"
style="width: 30%;margin-right: 30px;margin-bottom: 10px"/>
<el-select v-if="status_choices" v-model="operatestatus" clearable placeholder="操作状态"
style="width: 120px;margin-right: 30px" @change="handleCurrentChange(1)">
<el-option v-for="item in operate_status_choices" :key="item.id" :label="item.name" :value="item.id"/>
</el-select>
<el-button icon="el-icon-search" type="primary" @click="handleCurrentChange(1)">
搜索
</el-button>
<el-table
v-loading="loading"
:data="operate_message_lists"
border
stripe
style="width: 100%">
<el-table-column
align="center"
label="操作标题"
prop="title"
width="120">
</el-table-column>
<el-table-column
:formatter="operateformatter"
align="center"
label="操作时间"
prop="operate_time"
width="100">
</el-table-column>
<el-table-column
align="center"
label="签名应用"
prop="app_info"
width="100">
<template slot-scope="scope">
<el-popover v-if="scope.row.app_info.bundle_id" placement="top" trigger="hover">
<p>应用名称: {{ scope.row.app_info.bundle_name }}</p>
<p>应用ID: {{ scope.row.app_info.bundle_id }}</p>
<div slot="reference" class="name-wrapper">
<span>{{ scope.row.app_info.bundle_name }}</span>
</div>
</el-popover>
<span v-else>/</span>
</template>
</el-table-column>
<el-table-column
align="center"
label="操作状态"
width="80">
<template slot-scope="scope">
<el-tag v-if="scope.row.operate_status === 1">成功</el-tag>
<el-tag v-else type="danger">失败</el-tag>
</template>
</el-table-column>
<el-table-column
align="center"
label="操作日志"
prop="message">
</el-table-column>
<el-table-column
align="center"
label="开发者ID"
prop="developer_id"
width="166">
<template slot-scope="scope">
<el-popover placement="top" trigger="hover">
<p>开发者ID: {{ scope.row.developer_id }}</p>
<p>开发者备注: {{ scope.row.developer_description }}</p>
<p>开发者状态: {{ scope.row.developer_status }}</p>
<div slot="reference" class="name-wrapper">
<span>{{ scope.row.developer_id }}</span>
</div>
</el-popover>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
<div v-if="activeName!== 'adddeveloper'" style="margin-top: 20px"> <div v-if="activeName!== 'adddeveloper'" style="margin-top: 20px">
<el-pagination <el-pagination
:current-page.sync="pagination.currentPage" :current-page.sync="pagination.currentPage"
@ -1380,10 +1473,12 @@ import {
iosdeveloper, iosdeveloper,
iosdevices, iosdevices,
iosdevicesudid, iosdevicesudid,
iosudevices iosudevices,
signoperatemessage
} from "@/restful"; } from "@/restful";
import {format_choices, getUserInfoFun, removeAaary} from "@/utils"; import {format_choices, getUserInfoFun, removeAaary} from "@/utils";
import AppleDeveloperBindApp from "@/components/base/AppleDeveloperBindApp"; import AppleDeveloperBindApp from "@/components/base/AppleDeveloperBindApp";
import {format_time} from "@/utils/base/utils";
export default { export default {
name: "FirSuperSignBase", name: "FirSuperSignBase",
@ -1399,6 +1494,7 @@ export default {
bill_percent: 0, bill_percent: 0,
fileList: [], fileList: [],
developer_udevices_lists: [], developer_udevices_lists: [],
operate_message_lists: [],
app_developer_lists: [], app_developer_lists: [],
app_devices_lists: [], app_devices_lists: [],
app_bill_lists: [], app_bill_lists: [],
@ -1411,6 +1507,9 @@ export default {
udidsearch: "", udidsearch: "",
Bundleidsearch: "", Bundleidsearch: "",
appidseach: "", appidseach: "",
devicestatus: "",
operatestatus: "",
operate_status_choices: [],
uidsearch: "", uidsearch: "",
appnamesearch: "", appnamesearch: "",
timerangesearch: [], timerangesearch: [],
@ -1490,6 +1589,7 @@ export default {
bind_appletoapp_sure: false, bind_appletoapp_sure: false,
appletoapp_title: '', appletoapp_title: '',
status_choices: [], status_choices: [],
device_status_choices: [],
read_only_mode: 'off', read_only_mode: 'off',
developer_status_choice: [], developer_status_choice: [],
multipleSelection: [], multipleSelection: [],
@ -1989,7 +2089,11 @@ export default {
data.appnamesearch = this.appnamesearch.replace(/^\s+|\s+$/g, ""); data.appnamesearch = this.appnamesearch.replace(/^\s+|\s+$/g, "");
this.iosdevicerankFun({"methods": "GET", "data": data}) this.iosdevicerankFun({"methods": "GET", "data": data})
} else if (tabname === "iosudevices") { } else if (tabname === "iosudevices") {
data.devicestatus = this.devicestatus;
this.iosudevicesFun("GET", data) this.iosudevicesFun("GET", data)
} else if (tabname === 'operatemsg') {
data.operate_status = this.operatestatus;
this.operateMessageFun({"methods": "GET", "data": data})
} }
}, },
@ -2000,21 +2104,15 @@ export default {
}, },
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
deviceformatter(row, column) { deviceformatter(row, column) {
let stime = row.created_time; return format_time(row.created_time)
if (stime) {
stime = stime.split(".")[0].split("T");
return stime[0] + " " + stime[1]
} else
return '';
}, },
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
formatter(row, column) { formatter(row, column) {
let stime = row.cert_expire_time; return format_time(row.cert_expire_time)
if (stime) { },
stime = stime.split(".")[0].split("T"); // eslint-disable-next-line no-unused-vars
return stime[0] + " " + stime[1] operateformatter(row, column) {
} else return format_time(row.operate_time)
return '';
}, },
iosdevicesFun(methods, data) { iosdevicesFun(methods, data) {
this.loading = true; this.loading = true;
@ -2063,8 +2161,22 @@ export default {
} }
} }
if (params.methods === 'PUT') { if (params.methods === 'PUT') {
if (data.data && data.data.length > 0) {
let html_msg = ''
for (let e_msg of data.data) {
html_msg += `${e_msg.msg} <br/>`
}
this.$message({
dangerouslyUseHTMLString: true,
message: html_msg,
duration: 60000,
type: 'warning',
showClose: true
});
} else {
this.$message.success("操作成功"); this.$message.success("操作成功");
} }
}
if (params.data.act === 'setstatus') { if (params.data.act === 'setstatus') {
this.setdeveloperstatusVisible = false this.setdeveloperstatusVisible = false
this.change_developer_status = '' this.change_developer_status = ''
@ -2196,6 +2308,17 @@ export default {
"methods": action, "data": data "methods": action, "data": data
}) })
}, },
operateMessageFun(params) {
signoperatemessage(data => {
if (data.code === 1000) {
this.operate_message_lists = data.data;
this.pagination.total = data.count;
this.operate_status_choices = data.status_choices
} else {
this.$message.error("操作失败了 " + data.msg)
}
}, params)
},
iosudevicesFun(action, data) { iosudevicesFun(action, data) {
if (action !== 'GET') { if (action !== 'GET') {
this.loadingfun = this.$loading({ this.loadingfun = this.$loading({
@ -2212,6 +2335,7 @@ export default {
if (action !== "PUT") { if (action !== "PUT") {
this.developer_udevices_lists = data.data; this.developer_udevices_lists = data.data;
this.pagination.total = data.count; this.pagination.total = data.count;
this.device_status_choices = data.status_choices;
} else { } else {
this.refreshactiveFun() this.refreshactiveFun()
} }
@ -2231,7 +2355,7 @@ export default {
getUserInfoFun(this); getUserInfoFun(this);
if (this.$route.params.act) { if (this.$route.params.act) {
let activeName = this.$route.params.act; let activeName = this.$route.params.act;
let activeName_list = ["iosdeveloper", "adddeveloper", "iosudevices", "useddevices", "devicesudid", "devicesbill", "transferbill", "devicesrank"]; let activeName_list = ["iosdeveloper", "adddeveloper", "iosudevices", "useddevices", "devicesudid", "devicesbill", "transferbill", "devicesrank", "operatemsg"];
for (let index in activeName_list) { for (let index in activeName_list) {
if (activeName_list[index] === activeName) { if (activeName_list[index] === activeName) {
this.activeName = activeName; this.activeName = activeName;

@ -204,6 +204,7 @@
import {advertinfo} from "@/restful"; import {advertinfo} from "@/restful";
import {VueCropper} from 'vue-cropper' import {VueCropper} from 'vue-cropper'
import {AvatarUploadUtils, dataURLtoFile, getUserInfoFun} from '@/utils' import {AvatarUploadUtils, dataURLtoFile, getUserInfoFun} from '@/utils'
import {format_time} from "@/utils/base/utils";
export default { export default {
name: "FirUserAdvert", name: "FirUserAdvert",
@ -389,11 +390,7 @@ export default {
}, },
format_time(stime) { format_time(stime) {
if (stime) { return format_time(stime)
stime = stime.split(".")[0].split("T");
return stime[0] + " " + stime[1]
} else
return '';
}, },
}, mounted() { }, mounted() {
getUserInfoFun(this); getUserInfoFun(this);

@ -248,13 +248,7 @@ export default {
return this.format_time(row.created_time) return this.format_time(row.created_time)
}, },
format_time(stime) {
if (stime) {
stime = stime.split(".")[0].split("T");
return stime[0] + " " + stime[1]
} else
return '';
},
}, mounted() { }, mounted() {
getUserInfoFun(this); getUserInfoFun(this);
this.get_data_from_tabname(); this.get_data_from_tabname();

@ -343,7 +343,7 @@
<script> <script>
import {notifyConfigInfo, NotifyInfoFun, notifyReceiverInfo, wxBindFun, wxLoginFun} from "@/restful"; import {notifyConfigInfo, NotifyInfoFun, notifyReceiverInfo, wxBindFun, wxLoginFun} from "@/restful";
import {geetest, getUserInfoFun} from "@/utils"; import {geetest, getUserInfoFun} from "@/utils";
import {checkEmail, checkphone, getRandomStr} from "@/utils/base/utils"; import {checkEmail, checkphone, format_time, getRandomStr} from "@/utils/base/utils";
export default { export default {
name: "FirUserNotify", name: "FirUserNotify",
@ -672,12 +672,7 @@ export default {
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
formatter(row, column) { formatter(row, column) {
let stime = row.create_time; return format_time(row.create_time)
if (stime) {
stime = stime.split(".")[0].split("T");
return stime[0] + " " + stime[1]
} else
return '';
}, },
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
get_data_from_tabname(tabname, data = {}) { get_data_from_tabname(tabname, data = {}) {

@ -250,7 +250,7 @@
import {my_order, order_sync} from "@/restful"; import {my_order, order_sync} from "@/restful";
import {format_choices, getUserInfoFun} from '@/utils' import {format_choices, getUserInfoFun} from '@/utils'
import VueQr from 'vue-qr'; import VueQr from 'vue-qr';
import {getRandomStr} from "@/utils/base/utils"; import {format_time, getRandomStr} from "@/utils/base/utils";
export default { export default {
name: "FirUserOrders", name: "FirUserOrders",
@ -424,11 +424,7 @@ export default {
}, },
format_time(stime) { format_time(stime) {
if (stime) { return format_time(stime)
stime = stime.split(".")[0].split("T");
return stime[0] + " " + stime[1]
} else
return '';
}, },
MyOrderFun(params) { MyOrderFun(params) {
this.loading = true; this.loading = true;

@ -321,6 +321,7 @@
<script> <script>
import {cleanStorageData, getStorageinfo} from "@/restful"; import {cleanStorageData, getStorageinfo} from "@/restful";
import {deepCopy, getUserInfoFun} from "@/utils"; import {deepCopy, getUserInfoFun} from "@/utils";
import {format_time} from "@/utils/base/utils";
export default { export default {
name: "FirUserStorage", name: "FirUserStorage",
@ -534,12 +535,7 @@ export default {
}, },
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
formatter(row, column) { formatter(row, column) {
let stime = row.updated_time; return format_time(row.updated_time)
if (stime) {
stime = stime.split(".")[0].split("T");
return stime[0] + " " + stime[1]
} else
return '';
}, },
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
get_data_from_tabname(tabname, data = {}) { get_data_from_tabname(tabname, data = {}) {

@ -933,6 +933,21 @@ export function developercert(callBack, params, load = true) {
); );
} }
/**签名证书 */
export function signoperatemessage(callBack, params, load = true) {
getData(
params.methods,
SIGNSEVER + '/message',
params.data,
data => {
callBack(data);
},
load,
true,
true
);
}
/**获取签名任务状态 */ /**获取签名任务状态 */
export function gettask(callBack, params, load = true) { export function gettask(callBack, params, load = true) {
getData( getData(

@ -63,3 +63,11 @@ export function geetestbase(func, self, uid, params, callback, errback, readybac
"data": {user_id: uid} "data": {user_id: uid}
}); });
} }
export function format_time(stime) {
if (stime) {
stime = stime.split(".")[0].split("T");
return stime[0] + " " + stime[1]
} else
return '';
}

@ -48,7 +48,7 @@ def auto_clean_upload_tmp_file():
logger.info(f"auto_clean_upload_tmp_file upload_tem_file_key :{upload_tem_file_key}") logger.info(f"auto_clean_upload_tmp_file upload_tem_file_key :{upload_tem_file_key}")
def auto_clean_remote_client_log(clean_day=30): def auto_clean_remote_client_log(clean_day=30 * 6):
clean_time = datetime.datetime.now() - datetime.timedelta(days=clean_day) clean_time = datetime.datetime.now() - datetime.timedelta(days=clean_day)
return RemoteClientInfo.objects.filter(created_time__lt=clean_time).delete() return RemoteClientInfo.objects.filter(created_time__lt=clean_time).delete()

@ -94,8 +94,8 @@ class IpProxyListCache(RedisCacheBase):
class IpProxyActiveCache(RedisCacheBase): class IpProxyActiveCache(RedisCacheBase):
def __init__(self): def __init__(self, issuer_id):
self.cache_key = CACHE_KEY_TEMPLATE.get("ip_proxy_store_active_key") self.cache_key = f"{CACHE_KEY_TEMPLATE.get('ip_proxy_store_active_key')}_{issuer_id}"
super().__init__(self.cache_key) super().__init__(self.cache_key)

@ -0,0 +1,45 @@
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# project: fir_ser
# filename: constants
# author: liuyu
# data: 2022/4/5
class DeviceStatus(object):
"""
设备状态: 当开发者设备里面存在 PROCESSING INELIGIBLE 状态时再次注册的设备状态全部都为 PROCESSING
"""
DISABLED = 'DISABLED'
ENABLED = 'ENABLED'
PROCESSING = 'PROCESSING'
INELIGIBLE = 'INELIGIBLE'
class AppleDeveloperStatus(object):
"""
status_choices = ((-1, '疑似被封'), (0, '未激活'), (1, '已激活'), (2, '协议待同意'),
(3, '维护中'), (4, '证书过期'), (5, '证书丢失'), (6, '设备状态异常'), (99, '状态异常'))
"""
BAN = -1
INACTIVATED = 0
ACTIVATED = 1
AGREEMENT_NOT_AGREED = 2
MAINTENANCE = 3
CERTIFICATE_EXPIRED = 4
CERTIFICATE_MISSING = 5
DEVICE_ABNORMAL = 6
ABNORMAL_STATUS = 99
class SignStatus(object):
"""
sign_status_choices = ((0, '新设备入库准备'), (1, '设备ID已经注册'), (2, 'bundelid已经注册'),
(3, '描述文件已经下载'), (4, '已经完成签名打包'))
"""
SIGNATURE_PREPARE = 0
DEVICE_REGISTRATION_COMPLETE = 1
APP_REGISTRATION_COMPLETE = 2
PROFILE_DOWNLOAD_COMPLETE = 3
SIGNATURE_PACKAGE_COMPLETE = 4

@ -714,7 +714,7 @@ def call_function_try_attempts(try_attempts=3, sleep_time=3):
if 'Cannot connect to proxy' in str(e) or 'Read timed out' in str( if 'Cannot connect to proxy' in str(e) or 'Read timed out' in str(
e) or 'Max retries exceeded with' in str(e): e) or 'Max retries exceeded with' in str(e):
logger.error('access apple api failed . change proxy ip again') logger.error('access apple api failed . change proxy ip again')
get_proxy_ip_from_cache(True) get_proxy_ip_from_cache(args[0].issuer_id, True)
logger.warning( logger.warning(
f'exec {func} failed. Failed:{e} {try_attempts} times in total. now {sleep_time} later try ' f'exec {func} failed. Failed:{e} {try_attempts} times in total. now {sleep_time} later try '
f'again...{i}') f'again...{i}')
@ -751,7 +751,7 @@ class AppStoreConnectApi(DevicesAPI, BundleIDsAPI, BundleIDsCapabilityAPI, Profi
self.private_key_id = private_key_id self.private_key_id = private_key_id
self.p8_private_key = p8_private_key self.p8_private_key = p8_private_key
self.exp_seconds = exp_seconds self.exp_seconds = exp_seconds
self.proxies = get_proxy_ip_from_cache() self.proxies = get_proxy_ip_from_cache(issuer_id)
self.timeout = Config.APPLE_DEVELOPER_API_TIMEOUT if Config.APPLE_DEVELOPER_API_TIMEOUT else 120 self.timeout = Config.APPLE_DEVELOPER_API_TIMEOUT if Config.APPLE_DEVELOPER_API_TIMEOUT else 120
self.__make_jwt_headers() self.__make_jwt_headers()
DevicesAPI.__init__(self, self.BASE_URI, self.headers) DevicesAPI.__init__(self, self.BASE_URI, self.headers)

@ -5,9 +5,8 @@
# date: 2021/9/6 # date: 2021/9/6
import json import json
import logging import logging
import urllib
from urllib.parse import quote
from hashlib import sha1 from hashlib import sha1
from urllib.parse import quote
import requests import requests
from django.urls import reverse from django.urls import reverse

@ -5,6 +5,8 @@
# date: 2021/7/19 # date: 2021/7/19
import os import os
from common.constants import AppleDeveloperStatus
API_DOMAIN = "https://app.hehelucky.cn" API_DOMAIN = "https://app.hehelucky.cn"
WEB_DOMAIN = "https://app.hehelucky.cn" WEB_DOMAIN = "https://app.hehelucky.cn"
MOBILEPROVISION = "https://static.flyapps.top/embedded1.mobileprovision" MOBILEPROVISION = "https://static.flyapps.top/embedded1.mobileprovision"
@ -393,9 +395,25 @@ class DOWNLOADTIMESCONF(object):
class APPLEDEVELOPERCONF(object): class APPLEDEVELOPERCONF(object):
# (-1, '疑似被封'), (0, '未激活'), (1, '已激活'), (2, '协议待同意'), (3, '维护中'), (4, '证书过期'), (5, '状态异常') # (-1, '疑似被封'), (0, '未激活'), (1, '已激活'), (2, '协议待同意'), (3, '维护中'), (4, '证书过期'), (5, '状态异常')
DEVELOPER_SIGN_STATUS = [1] # 开发者可用于签名的查询 # 开发者可用于签名的查询
DEVELOPER_USE_STATUS = [1, 2, 3, 4, 5] # 开发者可用状态,详情查看 model.AppIOSDeveloperInfo DEVELOPER_SIGN_STATUS = [AppleDeveloperStatus.ACTIVATED]
DEVELOPER_AUTO_CHECK_STATUS = [1, 2, 4, 5] # 定时认证自动检测
DEVELOPER_WRITE_STATUS = [1, 3, 4] # 开发者api写操作查询[该状态用于苹果api接口] # 开发者可用状态,详情查看 model.AppIOSDeveloperInfo
DEVELOPER_DISABLED_STATUS = [2, 4, 5] # 开发者不可 修改为状态,用户前端控制 DEVELOPER_USE_STATUS = [AppleDeveloperStatus.ACTIVATED, AppleDeveloperStatus.AGREEMENT_NOT_AGREED,
AppleDeveloperStatus.MAINTENANCE, AppleDeveloperStatus.CERTIFICATE_EXPIRED,
AppleDeveloperStatus.CERTIFICATE_MISSING, AppleDeveloperStatus.DEVICE_ABNORMAL,
AppleDeveloperStatus.ABNORMAL_STATUS]
# 定时认证自动检测
DEVELOPER_AUTO_CHECK_STATUS = [AppleDeveloperStatus.ACTIVATED, AppleDeveloperStatus.AGREEMENT_NOT_AGREED,
AppleDeveloperStatus.CERTIFICATE_EXPIRED, AppleDeveloperStatus.CERTIFICATE_MISSING,
AppleDeveloperStatus.DEVICE_ABNORMAL, AppleDeveloperStatus.ABNORMAL_STATUS]
# 开发者api写操作查询[该状态用于苹果api接口]
DEVELOPER_WRITE_STATUS = [AppleDeveloperStatus.ACTIVATED, AppleDeveloperStatus.MAINTENANCE,
AppleDeveloperStatus.CERTIFICATE_EXPIRED, AppleDeveloperStatus.CERTIFICATE_MISSING,
AppleDeveloperStatus.DEVICE_ABNORMAL]
# 开发者不可 修改为状态,用户前端控制
DEVELOPER_DISABLED_STATUS = [AppleDeveloperStatus.AGREEMENT_NOT_AGREED, AppleDeveloperStatus.CERTIFICATE_EXPIRED,
AppleDeveloperStatus.CERTIFICATE_MISSING, AppleDeveloperStatus.DEVICE_ABNORMAL]
DEVELOPER_UID_KEY = "T:" # 开发者共享给其他第三方用户, 中间必须包含 : 前端需要根据 : 进行分割 DEVELOPER_UID_KEY = "T:" # 开发者共享给其他第三方用户, 中间必须包含 : 前端需要根据 : 进行分割

@ -502,6 +502,11 @@ CELERY_BEAT_SCHEDULE = {
'schedule': crontab(hour=1, minute=1), 'schedule': crontab(hour=1, minute=1),
'args': () 'args': ()
}, },
'auto_clean_sign_log_job': {
'task': 'xsign.tasks.auto_clean_sign_log_job',
'schedule': crontab(hour=2, minute=2),
'args': ()
},
} }
# listen port # listen port

@ -0,0 +1,57 @@
# Generated by Django 3.2.3 on 2022-04-05 14:52
import django.utils.timezone
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('api', '0004_auto_20220330_0009'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('xsign', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='udidsyncdeveloper',
name='created_time',
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now, verbose_name='创建时间'),
preserve_default=False,
),
migrations.AlterField(
model_name='udidsyncdeveloper',
name='status',
field=models.CharField(
choices=[('DISABLED', '禁用'), ('ENABLED', '启用'), ('PROCESSING', '处理中'), ('INELIGIBLE', '不合格')],
default='DISABLED', max_length=16, verbose_name='设备状态'),
),
migrations.AlterField(
model_name='appiosdeveloperinfo',
name='status',
field=models.SmallIntegerField(choices=[(-1, '疑似被封'), (0, '未激活'), (1, '已激活'), (2, '协议待同意'),
(3, '维护中'), (4, '证书过期'), (5, '证书丢失'), (6, '设备异常'),
(99, '状态异常')], default=0, verbose_name='账户状态'),
),
migrations.CreateModel(
name='AppleSignMessage',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(blank=True, default='', max_length=256, verbose_name='操作功能')),
('operate_status',
models.SmallIntegerField(choices=[(0, '失败'), (1, '成功')], default=0, verbose_name='状态')),
('message', models.TextField(blank=True, default='', verbose_name='操作详细日志')),
('operate_time', models.DateTimeField(auto_now_add=True, verbose_name='操作时间')),
('app_id',
models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='api.apps')),
('developerid',
models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='xsign.appiosdeveloperinfo')),
('user_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL,
verbose_name='操作用户')),
],
options={
'verbose_name': '应用签名操作记录',
'verbose_name_plural': '应用签名操作记录',
},
),
]

@ -2,6 +2,7 @@ from django.db import models
from api.models import Apps, UserInfo from api.models import Apps, UserInfo
from common.base.daobase import AESCharField from common.base.daobase import AESCharField
from common.constants import DeviceStatus, AppleDeveloperStatus, SignStatus
class AppIOSDeveloperInfo(models.Model): class AppIOSDeveloperInfo(models.Model):
@ -23,8 +24,16 @@ class AppIOSDeveloperInfo(models.Model):
auth_type = models.SmallIntegerField(choices=auth_type_choices, default=0, verbose_name="认证类型") auth_type = models.SmallIntegerField(choices=auth_type_choices, default=0, verbose_name="认证类型")
# 协议待同意和维护中:代表只读,不可创建和注册新设备号 # 协议待同意和维护中:代表只读,不可创建和注册新设备号
status_choices = ((-1, '疑似被封'), (0, '未激活'), (1, '已激活'), (2, '协议待同意'), (3, '维护中'), (4, '证书过期'), (5, '状态异常')) status_choices = ((AppleDeveloperStatus.BAN, '疑似被封'), (AppleDeveloperStatus.INACTIVATED, '未激活'),
status = models.SmallIntegerField(choices=status_choices, verbose_name="账户状态", default=0) (AppleDeveloperStatus.ACTIVATED, '已激活'),
(AppleDeveloperStatus.AGREEMENT_NOT_AGREED, '协议待同意'),
(AppleDeveloperStatus.MAINTENANCE, '维护中'),
(AppleDeveloperStatus.CERTIFICATE_EXPIRED, '证书过期'),
(AppleDeveloperStatus.CERTIFICATE_MISSING, '证书丢失'),
(AppleDeveloperStatus.DEVICE_ABNORMAL, '设备异常'),
(AppleDeveloperStatus.ABNORMAL_STATUS, '状态异常'))
status = models.SmallIntegerField(choices=status_choices, verbose_name="账户状态",
default=AppleDeveloperStatus.INACTIVATED)
clean_status = models.BooleanField(verbose_name="清理是否同时禁用设备ID", default=False) clean_status = models.BooleanField(verbose_name="清理是否同时禁用设备ID", default=False)
auto_check = models.BooleanField(verbose_name="是否自动检测开发者状态", default=False) auto_check = models.BooleanField(verbose_name="是否自动检测开发者状态", default=False)
@ -60,8 +69,10 @@ class UDIDsyncDeveloper(models.Model):
product = models.CharField(max_length=64, verbose_name="产品", blank=True, null=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, ) serial = models.CharField(max_length=64, verbose_name="序列号", blank=True, null=True, )
version = 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="创建时间"), created_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间") # django.utils.timezone.now()
status = models.BooleanField(verbose_name="设备在开发者平台状态", default=False) status_choices = ((DeviceStatus.DISABLED, '禁用'), (DeviceStatus.ENABLED, '启用'),
(DeviceStatus.PROCESSING, '处理中'), (DeviceStatus.INELIGIBLE, '不合格'))
status = models.CharField(choices=status_choices, verbose_name="设备状态", default=DeviceStatus.DISABLED, max_length=16)
class Meta: class Meta:
verbose_name = 'iOS开发平台同步设备信息' verbose_name = 'iOS开发平台同步设备信息'
@ -69,7 +80,7 @@ class UDIDsyncDeveloper(models.Model):
unique_together = ('udid', 'developerid',) unique_together = ('udid', 'developerid',)
def __str__(self): def __str__(self):
return "%s-%s-%s" % (self.product, self.udid, self.developerid) return "%s-%s-%s-%s" % (self.product, self.udid, self.developerid, self.status)
class AppUDID(models.Model): class AppUDID(models.Model):
@ -85,8 +96,14 @@ class AppUDID(models.Model):
iccid = 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="创建时间") created_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
updated_time = models.DateTimeField(auto_now=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="签名状态") sign_status_choices = ((SignStatus.SIGNATURE_PREPARE, '新设备入库准备'),
(SignStatus.DEVICE_REGISTRATION_COMPLETE, '设备ID已经注册'),
(SignStatus.APP_REGISTRATION_COMPLETE, 'bundelid已经注册'),
(SignStatus.PROFILE_DOWNLOAD_COMPLETE, '描述文件已经下载'),
(SignStatus.SIGNATURE_PACKAGE_COMPLETE, '已经完成签名打包'))
sign_status = models.SmallIntegerField(choices=sign_status_choices, default=SignStatus.SIGNATURE_PREPARE,
verbose_name="签名状态")
class Meta: class Meta:
verbose_name = '设备详情' verbose_name = '设备详情'
@ -239,3 +256,30 @@ class AppleDeveloperToAppUse(models.Model):
def __str__(self): def __str__(self):
return "%s-%s" % (self.app_id.name, self.developerid.issuer_id) return "%s-%s" % (self.app_id.name, self.developerid.issuer_id)
class OperateMessageBase(models.Model):
"""
操作详细记录一般为失败的记录
"""
user_id = models.ForeignKey(to=UserInfo, verbose_name="操作用户", on_delete=models.CASCADE)
title = models.CharField(verbose_name="操作功能", max_length=256, default='', blank=True)
status_choices = ((0, '失败'), (1, '成功'))
operate_status = models.SmallIntegerField(choices=status_choices, default=0, verbose_name="状态")
message = models.TextField(verbose_name="操作详细日志", default='', blank=True)
operate_time = models.DateTimeField(auto_now_add=True, verbose_name="操作时间")
class Meta:
abstract = True
class AppleSignMessage(OperateMessageBase):
app_id = models.ForeignKey(to=Apps, on_delete=models.CASCADE, null=True, blank=True)
developerid = models.ForeignKey(to=AppIOSDeveloperInfo, on_delete=models.CASCADE)
class Meta:
verbose_name = '应用签名操作记录'
verbose_name_plural = "应用签名操作记录"
def __str__(self):
return "%s-%s-%s" % (self.app_id.name, self.developerid.issuer_id, self.title)

@ -12,8 +12,9 @@ from django.core.cache import cache
from common.cache.state import MigrateStorageState from common.cache.state import MigrateStorageState
from fir_ser.celery import app from fir_ser.celery import app
from xsign.models import Apps, DeveloperAppID from xsign.models import Apps, DeveloperAppID
from xsign.utils.ctasks import auto_check_ios_developer_active from xsign.utils.ctasks import auto_check_ios_developer_active, auto_clean_sign_log
from xsign.utils.iproxy import get_best_proxy_ips from xsign.utils.iproxy import get_best_proxy_ips
from xsign.utils.modelutils import add_sign_message
from xsign.utils.supersignutils import IosUtils, resign_by_app_id_and_developer from xsign.utils.supersignutils import IosUtils, resign_by_app_id_and_developer
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -29,6 +30,12 @@ def run_sign_task(format_udid_info, short, client_ip):
ios_obj = IosUtils(format_udid_info, app_obj.user_id, app_obj) ios_obj = IosUtils(format_udid_info, app_obj.user_id, app_obj)
status, msg = ios_obj.sign_ipa(client_ip) status, msg = ios_obj.sign_ipa(client_ip)
if ios_obj.developer_obj:
if not status:
add_sign_message(app_obj.user_id, ios_obj.developer_obj, app_obj, '签名失败了', msg, False)
else:
add_sign_message(app_obj.user_id, ios_obj.developer_obj, app_obj, '签名成功', msg, True)
if not status: if not status:
code = msg.get("code", -1) code = msg.get("code", -1)
if code == 0: if code == 0:
@ -84,3 +91,8 @@ def auto_check_ios_developer_active_job():
@app.task @app.task
def get_best_proxy_ips_job(): def get_best_proxy_ips_job():
get_best_proxy_ips() get_best_proxy_ips()
@app.task
def auto_clean_sign_log_job():
auto_clean_sign_log()

@ -19,11 +19,13 @@ from xsign.views.appinfo import AppCanSignView, AppSignInfoView
from xsign.views.download import XsignDownloadView from xsign.views.download import XsignDownloadView
from xsign.views.receiveudids import IosUDIDView, TaskView, ShowUdidView from xsign.views.receiveudids import IosUDIDView, TaskView, ShowUdidView
from xsign.views.supersign import DeveloperView, SuperSignUsedView, AppUDIDUsedView, SuperSignCertView, \ from xsign.views.supersign import DeveloperView, SuperSignUsedView, AppUDIDUsedView, SuperSignCertView, \
DeviceUsedBillView, DeveloperDeviceView, DeviceUsedRankInfoView, AppleDeveloperBindAppsView, DeviceTransferBillView DeviceUsedBillView, DeveloperDeviceView, DeviceUsedRankInfoView, AppleDeveloperBindAppsView, DeviceTransferBillView, \
SignOperateMessageView
urlpatterns = [ urlpatterns = [
re_path(r"^developer$", DeveloperView.as_view()), re_path(r"^developer$", DeveloperView.as_view()),
re_path(r"^devices$", SuperSignUsedView.as_view()), re_path(r"^devices$", SuperSignUsedView.as_view()),
re_path(r"^message$", SignOperateMessageView.as_view()),
re_path(r"^udid$", AppUDIDUsedView.as_view()), re_path(r"^udid$", AppUDIDUsedView.as_view()),
re_path(r"^udevices$", DeveloperDeviceView.as_view()), re_path(r"^udevices$", DeveloperDeviceView.as_view()),
re_path(r"^cert$", SuperSignCertView.as_view()), re_path(r"^cert$", SuperSignCertView.as_view()),

@ -15,8 +15,8 @@ from django.template import loader
from common.core.sysconfig import Config from common.core.sysconfig import Config
from common.notify.notify import check_developer_status_notify from common.notify.notify import check_developer_status_notify
from fir_ser.settings import SUPER_SIGN_ROOT, SYNC_CACHE_TO_DATABASE from fir_ser.settings import SUPER_SIGN_ROOT, SYNC_CACHE_TO_DATABASE
from xsign.models import UserInfo, AppIOSDeveloperInfo, APPSuperSignUsedInfo from xsign.models import UserInfo, AppIOSDeveloperInfo, APPSuperSignUsedInfo, AppleSignMessage
from xsign.utils.modelutils import get_developer_devices from xsign.utils.modelutils import get_developer_devices, add_sign_message
from xsign.utils.supersignutils import IosUtils from xsign.utils.supersignutils import IosUtils
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -46,6 +46,8 @@ def auto_check_ios_developer_active():
if user_obj.supersign_active: if user_obj.supersign_active:
status, result = IosUtils.active_developer(developer_obj, False) status, result = IosUtils.active_developer(developer_obj, False)
msg = f"auto_check_ios_developer_active user:{user_obj} ios.developer:{developer_obj} status:{status} result:{result}" msg = f"auto_check_ios_developer_active user:{user_obj} ios.developer:{developer_obj} status:{status} result:{result}"
if not status:
add_sign_message(user_obj, developer_obj, None, '开发者状态自动检测', result.get('return_info'), False)
err_issuer_id.append(developer_obj) err_issuer_id.append(developer_obj)
error_issuer_id[user_obj.uid] = list(set(err_issuer_id)) error_issuer_id[user_obj.uid] = list(set(err_issuer_id))
@ -81,3 +83,8 @@ def auto_check_ios_developer_active():
}) })
# send_ios_developer_active_status(userinfo, content) # send_ios_developer_active_status(userinfo, content)
check_developer_status_notify(userinfo, developer_obj_list, content) check_developer_status_notify(userinfo, developer_obj_list, content)
def auto_clean_sign_log(clean_day=30 * 6):
clean_time = datetime.datetime.now() - datetime.timedelta(days=clean_day)
return AppleSignMessage.objects.filter(operate_time__lt=clean_time).delete()

@ -17,10 +17,12 @@ from OpenSSL.crypto import (load_pkcs12, dump_certificate_request, dump_privatek
from common.base.baseutils import get_format_time, format_apple_date, make_app_uuid from common.base.baseutils import get_format_time, format_apple_date, make_app_uuid
from common.cache.state import CleanErrorBundleIdSignDataState from common.cache.state import CleanErrorBundleIdSignDataState
from common.constants import AppleDeveloperStatus
from common.core.sysconfig import Config from common.core.sysconfig import Config
from common.libs.apple.appleapiv3 import AppStoreConnectApi, Certificates, Devices, BundleIds, Profiles from common.libs.apple.appleapiv3 import AppStoreConnectApi, Certificates, Devices, BundleIds, Profiles
from fir_ser.settings import SUPER_SIGN_ROOT from fir_ser.settings import SUPER_SIGN_ROOT
from xsign.models import AppIOSDeveloperInfo from xsign.models import AppIOSDeveloperInfo
from xsign.utils.modelutils import add_sign_message
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -225,30 +227,31 @@ def check_error_call_back(error, developer_pk):
msg = "代理网络错误,请稍后重试或联系管理员处理" msg = "代理网络错误,请稍后重试或联系管理员处理"
if 'it may be encrypted with an unsupported algorithm' in error: if 'it may be encrypted with an unsupported algorithm' in error:
msg = "数据校验失败,请检查p8key内容是否正常" msg = "数据校验失败,请检查p8key内容是否正常"
status = 5 status = AppleDeveloperStatus.ABNORMAL_STATUS
if 'Authentication credentials are missing or invalid' in error: if 'Authentication credentials are missing or invalid' in error:
msg = '认证失败,请检查开发者信息填写是否正确' msg = '认证失败,请检查开发者信息填写是否正确'
status = 5 status = AppleDeveloperStatus.ABNORMAL_STATUS
if 'FORBIDDEN.REQUIRED_AGREEMENTS_MISSING_OR_EXPIRED' in error: if 'FORBIDDEN.REQUIRED_AGREEMENTS_MISSING_OR_EXPIRED' in error:
msg = '请登录 https://developer.apple.com/account/ 并同意最新协议' msg = '请登录 https://developer.apple.com/account/ 并同意最新协议'
status = 2 status = AppleDeveloperStatus.AGREEMENT_NOT_AGREED
if status is not None: if status is not None:
developer_obj = AppIOSDeveloperInfo.objects.filter(pk=developer_pk).first() developer_obj = AppIOSDeveloperInfo.objects.filter(pk=developer_pk).first()
if developer_obj: if developer_obj:
if developer_obj.status == -1 and status == 5: if developer_obj.status == AppleDeveloperStatus.BAN and status == AppleDeveloperStatus.ABNORMAL_STATUS:
status = -1 status = AppleDeveloperStatus.BAN
AppIOSDeveloperInfo.objects.filter(pk=developer_pk).update(status=status) AppIOSDeveloperInfo.objects.filter(pk=developer_pk).update(status=status)
logger.error(f"{msg} {error}") logger.error(f"{msg} {error}")
return msg if msg else error return msg if msg else error
class AppDeveloperApiV2(object): class AppDeveloperApiV2(object):
def __init__(self, issuer_id, private_key_id, p8key, cert_id, developer_pk): def __init__(self, issuer_id, private_key_id, p8key, cert_id, developer_pk, app_obj):
self.issuer_id = issuer_id self.issuer_id = issuer_id
self.private_key_id = private_key_id self.private_key_id = private_key_id
self.p8key = p8key self.p8key = p8key
self.cert_id = cert_id self.cert_id = cert_id
self.developer_pk = developer_pk self.developer_pk = developer_pk
self.app_obj = app_obj
def __getattribute__(self, name): def __getattribute__(self, name):
attr = object.__getattribute__(self, name) attr = object.__getattribute__(self, name)
@ -307,7 +310,13 @@ class AppDeveloperApiV2(object):
with CleanErrorBundleIdSignDataState(failed_call_prefix) as state: with CleanErrorBundleIdSignDataState(failed_call_prefix) as state:
if state: if state:
for func in callback.get('func_list', []): for func in callback.get('func_list', []):
logger.info(f'issuer_id:{self.issuer_id} run callback func {func}') msg = f'issuer_id:{self.issuer_id} run callback func {func.__name__}.err_msg:{err_msg}'
logger.warning(msg)
developer_obj = AppIOSDeveloperInfo.objects.filter(pk=self.developer_pk).first()
if developer_obj:
add_sign_message(developer_obj.user_id, developer_obj, self.app_obj,
'操作失败,执行失败回调方法',
msg, False)
func() func()
else: else:
logger.warning( logger.warning(

@ -41,17 +41,21 @@ def get_best_proxy_ips(url='https://api.appstoreconnect.apple.com/agreement'):
for proxy_ip in active_proxy_ips: for proxy_ip in active_proxy_ips:
pools.submit(task, proxy_ip) pools.submit(task, proxy_ip)
pools.shutdown() pools.shutdown()
best_sorted_ips = sorted(access_ip_info, key=lambda x: x.get('time'))[:8]
best_count = len(active_proxy_ips)
if len(active_proxy_ips) > 8:
best_count = int(best_count * 0.8)
best_sorted_ips = sorted(access_ip_info, key=lambda x: x.get('time'))[:best_count]
best_sorted_ips = [ip_proxy['ip'] for ip_proxy in best_sorted_ips] best_sorted_ips = [ip_proxy['ip'] for ip_proxy in best_sorted_ips]
IpProxyListCache().set_storage_cache(best_sorted_ips, 6 * 60 * 60) IpProxyListCache().set_storage_cache(best_sorted_ips, 6 * 60 * 60)
return best_sorted_ips return best_sorted_ips
def get_proxy_ip_from_cache(change_ip=False): def get_proxy_ip_from_cache(issuer_id, change_ip=False):
active_proxy_cache = IpProxyActiveCache() active_proxy_cache = IpProxyActiveCache(issuer_id)
active_ip_proxy = active_proxy_cache.get_storage_cache() active_ip_proxy = active_proxy_cache.get_storage_cache()
if not change_ip and active_ip_proxy: if not change_ip and active_ip_proxy:
logger.info(f"get ip proxy cache {active_ip_proxy}") logger.info(f"issuer_id:{issuer_id} get ip proxy cache {active_ip_proxy}")
return active_ip_proxy return active_ip_proxy
list_proxy_cache = IpProxyListCache() list_proxy_cache = IpProxyListCache()
@ -84,4 +88,4 @@ def get_proxy_ip_from_cache(change_ip=False):
def clean_ip_proxy_infos(): def clean_ip_proxy_infos():
logger.info("clean ip proxy infos") logger.info("clean ip proxy infos")
IpProxyListCache().del_storage_cache() IpProxyListCache().del_storage_cache()
IpProxyActiveCache().del_storage_cache() IpProxyActiveCache('*').del_many()

@ -11,9 +11,11 @@ from django.db.models import Count, Sum, Q
from api.models import AppReleaseInfo, UserInfo from api.models import AppReleaseInfo, UserInfo
from common.base.baseutils import is_valid_phone from common.base.baseutils import is_valid_phone
from common.constants import SignStatus
from common.core.sysconfig import Config from common.core.sysconfig import Config
from xsign.models import APPSuperSignUsedInfo, UDIDsyncDeveloper, AppUDID, APPToDeveloper, AppIOSDeveloperInfo, \ from xsign.models import APPSuperSignUsedInfo, UDIDsyncDeveloper, AppUDID, APPToDeveloper, AppIOSDeveloperInfo, \
IosDeveloperPublicPoolBill, IosDeveloperBill, DeveloperDevicesID, AppleDeveloperToAppUse, DeveloperAppID IosDeveloperPublicPoolBill, IosDeveloperBill, DeveloperDevicesID, AppleDeveloperToAppUse, DeveloperAppID, \
AppleSignMessage
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -69,7 +71,8 @@ def check_super_sign_permission(user_obj):
def check_ipa_is_latest_sign(app_obj, developer_obj=None): 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(): if AppUDID.objects.filter(app_id=app_obj, udid__developerid=developer_obj,
sign_status__lt=SignStatus.SIGNATURE_PACKAGE_COMPLETE).first():
return return
release_obj = AppReleaseInfo.objects.filter(app_id=app_obj, is_master=True).first() release_obj = AppReleaseInfo.objects.filter(app_id=app_obj, is_master=True).first()
all_app_to_dev = APPToDeveloper.objects.filter(app_id=app_obj) all_app_to_dev = APPToDeveloper.objects.filter(app_id=app_obj)
@ -103,7 +106,7 @@ def update_or_create_developer_udid_info(device_obj, developer_obj):
"product": device_obj.name, "product": device_obj.name,
"udid": device_obj.udid, "udid": device_obj.udid,
"version": device_obj.model, "version": device_obj.model,
"status": True if device_obj.status == 'ENABLED' else False "status": device_obj.status
} }
return UDIDsyncDeveloper.objects.update_or_create(developerid=developer_obj, udid=device_obj.udid, defaults=device) return UDIDsyncDeveloper.objects.update_or_create(developerid=developer_obj, udid=device_obj.udid, defaults=device)
@ -197,3 +200,10 @@ def get_filename_form_file(filename):
filename = f"{app_obj.name}-sign-{app_obj.short}{f_type}" filename = f"{app_obj.name}-sign-{app_obj.short}{f_type}"
check = True check = True
return check, filename return check, filename
def add_sign_message(user_obj, developer_obj, app_obj, title, message, is_success):
if app_obj:
title = f'应用【{app_obj.name}{title}'
AppleSignMessage.objects.create(user_id=user_obj, developerid=developer_obj, app_id=app_obj,
title=title, message=message, operate_status=is_success)

@ -133,7 +133,7 @@ class DeveloperDeviceSerializer(serializers.ModelSerializer):
developer_id = serializers.CharField(source="developerid.issuer_id") developer_id = serializers.CharField(source="developerid.issuer_id")
developer_description = serializers.CharField(source="developerid.description") developer_description = serializers.CharField(source="developerid.description")
developer_status = serializers.CharField(source="developerid.get_status_display") developer_status = serializers.CharField(source="developerid.get_status_display")
device_status = serializers.CharField(source="get_status_display")
app_used_count = serializers.SerializerMethodField() app_used_count = serializers.SerializerMethodField()
def get_app_used_count(self, obj): def get_app_used_count(self, obj):
@ -361,3 +361,19 @@ class AppSignSerializer(serializers.ModelSerializer):
def get_developer_used_count(self, obj): def get_developer_used_count(self, obj):
return models.DeveloperAppID.objects.filter(app_id=obj).all().count() return models.DeveloperAppID.objects.filter(app_id=obj).all().count()
class AppleSignMessageSerializer(serializers.ModelSerializer):
class Meta:
model = models.AppleSignMessage
exclude = ["user_id", "id", "developerid", "app_id"]
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_info = serializers.SerializerMethodField()
def get_app_info(self, obj):
if obj.app_id:
return {'bundle_name': obj.app_id.name, 'bundle_id': obj.app_id.bundle_id}
return {}

@ -8,6 +8,7 @@ from django.dispatch import receiver
from api.models import AppReleaseInfo from api.models import AppReleaseInfo
from api.utils.utils import migrating_storage_file_data, get_filename_from_apptype from api.utils.utils import migrating_storage_file_data, get_filename_from_apptype
from common.constants import SignStatus
from common.core.signals import run_resign_task_signal, delete_app_signal, xsign_app_download_url_signal, \ from common.core.signals import run_resign_task_signal, delete_app_signal, xsign_app_download_url_signal, \
xsign_migrate_data_signal, xsign_clean_data_signal, xsign_app_release_obj_signal xsign_migrate_data_signal, xsign_clean_data_signal, xsign_app_release_obj_signal
from common.core.sysconfig import Config from common.core.sysconfig import Config
@ -28,7 +29,8 @@ logger = logging.getLogger(__name__)
def run_resign_task_callback(sender, **kwargs): def run_resign_task_callback(sender, **kwargs):
app_obj = kwargs.get('app_obj') app_obj = kwargs.get('app_obj')
if app_obj: if app_obj:
AppUDID.objects.filter(app_id=app_obj, sign_status__gte=3).update(sign_status=3) sign_status = SignStatus.PROFILE_DOWNLOAD_COMPLETE
AppUDID.objects.filter(app_id=app_obj, sign_status__gte=sign_status).update(sign_status=sign_status)
if app_obj.change_auto_sign: if app_obj.change_auto_sign:
c_task = run_resign_task(app_obj.pk, False, False) c_task = run_resign_task(app_obj.pk, False, False)
logger.info(f"app {app_obj} run_resign_task end msg:{c_task}") logger.info(f"app {app_obj} run_resign_task end msg:{c_task}")
@ -63,7 +65,8 @@ def xsign_app_download_url_callback(sender, **kwargs):
app_pk = kwargs.get('app_pk') app_pk = kwargs.get('app_pk')
local_storage = LocalStorage(**Config.IOS_PMFILE_DOWNLOAD_DOMAIN) local_storage = LocalStorage(**Config.IOS_PMFILE_DOWNLOAD_DOMAIN)
appudid_obj = AppUDID.objects.filter(app_id_id=app_pk, udid__udid=udid, sign_status=4).last() appudid_obj = AppUDID.objects.filter(app_id_id=app_pk, udid__udid=udid,
sign_status=SignStatus.SIGNATURE_PACKAGE_COMPLETE).last()
if appudid_obj: if appudid_obj:
super_sign_obj = APPSuperSignUsedInfo.objects.filter(udid__udid__udid=udid, super_sign_obj = APPSuperSignUsedInfo.objects.filter(udid__udid__udid=udid,
app_id_id=app_pk, app_id_id=app_pk,

@ -21,6 +21,7 @@ from common.base.baseutils import file_format_path, delete_app_profile_file, get
get_format_time, make_app_uuid, make_from_user_uuid get_format_time, make_app_uuid, make_from_user_uuid
from common.base.magic import run_function_by_locker, call_function_try_attempts, magic_wrapper from common.base.magic import run_function_by_locker, call_function_try_attempts, magic_wrapper
from common.cache.state import CleanErrorBundleIdSignDataState from common.cache.state import CleanErrorBundleIdSignDataState
from common.constants import DeviceStatus, AppleDeveloperStatus, SignStatus
from common.core.sysconfig import Config from common.core.sysconfig import Config
from common.notify.notify import sign_failed_notify, sign_unavailable_developer_notify, sign_app_over_limit_notify from common.notify.notify import sign_failed_notify, sign_unavailable_developer_notify, sign_app_over_limit_notify
from common.utils.caches import del_cache_response_by_short, send_msg_over_limit, check_app_permission, \ from common.utils.caches import del_cache_response_by_short, send_msg_over_limit, check_app_permission, \
@ -32,7 +33,7 @@ from xsign.models import APPSuperSignUsedInfo, AppUDID, AppIOSDeveloperInfo, APP
IosDeveloperBill IosDeveloperBill
from xsign.utils.iossignapi import ResignApp, AppDeveloperApiV2 from xsign.utils.iossignapi import ResignApp, AppDeveloperApiV2
from xsign.utils.modelutils import get_ios_developer_public_num, check_ipa_is_latest_sign, \ from xsign.utils.modelutils import get_ios_developer_public_num, check_ipa_is_latest_sign, \
update_or_create_developer_udid_info, check_uid_has_relevant, get_developer_udided update_or_create_developer_udid_info, check_uid_has_relevant, get_developer_udided, add_sign_message
from xsign.utils.serializer import BillAppInfoSerializer, BillDeveloperInfoSerializer from xsign.utils.serializer import BillAppInfoSerializer, BillDeveloperInfoSerializer
from xsign.utils.utils import delete_app_to_dev_and_file from xsign.utils.utils import delete_app_to_dev_and_file
@ -198,10 +199,10 @@ def get_auth_form_developer(developer_obj):
return auth return auth
def get_api_obj(developer_obj): def get_api_obj(developer_obj, app_obj=None):
auth = get_auth_form_developer(developer_obj) auth = get_auth_form_developer(developer_obj)
if auth.get("issuer_id"): if auth.get("issuer_id"):
app_api_obj = AppDeveloperApiV2(**auth, developer_pk=developer_obj.pk) app_api_obj = AppDeveloperApiV2(**auth, developer_pk=developer_obj.pk, app_obj=app_obj)
else: else:
app_api_obj = None app_api_obj = None
return app_api_obj return app_api_obj
@ -214,13 +215,6 @@ def get_apple_udid_key(auth):
return m_key return m_key
def disable_developer_and_send_email(app_obj, developer_obj):
logger.error(f"app {app_obj} developer {developer_obj} sign failed. so disabled")
developer_obj.status = 5
developer_obj.save(update_fields=['status'])
sign_failed_notify(developer_obj.user_id, developer_obj, app_obj)
def get_new_developer_by_app_obj(app_obj, obj_base_filter, apple_to_app=False): def get_new_developer_by_app_obj(app_obj, obj_base_filter, apple_to_app=False):
can_used_developer_pk_list = [] can_used_developer_pk_list = []
if apple_to_app: if apple_to_app:
@ -375,8 +369,8 @@ def get_developer_obj_by_others(user_obj, udid, app_obj, read_only):
def check_sign_is_exists(user_obj, app_obj, udid, developer_obj, sign=True): def check_sign_is_exists(user_obj, app_obj, udid, developer_obj, sign=True):
d_result = {'code': 0, 'msg': 'success'} d_result = {'code': 0, 'msg': 'success'}
app_udid_obj = AppUDID.objects.filter(app_id=app_obj, udid__udid=udid, udid__developerid=developer_obj).first() app_udid_obj = AppUDID.objects.filter(app_id=app_obj, udid__udid=udid, udid__developerid=developer_obj).first()
if app_udid_obj and app_udid_obj.sign_status >= 3: if app_udid_obj and app_udid_obj.sign_status >= SignStatus.PROFILE_DOWNLOAD_COMPLETE:
if app_udid_obj.sign_status == 4: if app_udid_obj.sign_status == SignStatus.SIGNATURE_PACKAGE_COMPLETE:
if check_ipa_is_latest_sign(app_obj, developer_obj): if check_ipa_is_latest_sign(app_obj, developer_obj):
d_result['msg'] = f'udid {udid} exists app_id {app_obj}' d_result['msg'] = f'udid {udid} exists app_id {app_obj}'
logger.warning(d_result) logger.warning(d_result)
@ -411,7 +405,6 @@ class IosUtils(object):
self.auth = get_auth_form_developer(self.developer_obj) self.auth = get_auth_form_developer(self.developer_obj)
else: else:
logger.error(f"user {self.user_obj} has no active apple developer") logger.error(f"user {self.user_obj} has no active apple developer")
if self.user_obj.email:
if send_msg_over_limit("get", self.user_obj.email): if send_msg_over_limit("get", self.user_obj.email):
send_msg_over_limit("set", self.user_obj.email) send_msg_over_limit("set", self.user_obj.email)
sign_unavailable_developer_notify(self.user_obj, self.app_obj) sign_unavailable_developer_notify(self.user_obj, self.app_obj)
@ -433,7 +426,7 @@ class IosUtils(object):
@staticmethod @staticmethod
def modify_capability(developer_obj, app_obj, developer_app_id): def modify_capability(developer_obj, app_obj, developer_app_id):
return get_api_obj(developer_obj).modify_capability(app_obj, developer_app_id) return get_api_obj(developer_obj, app_obj).modify_capability(app_obj, developer_app_id)
def get_profile_full_path(self): def get_profile_full_path(self):
cert_dir_name = make_app_uuid(self.user_obj, get_apple_udid_key(self.auth)) cert_dir_name = make_app_uuid(self.user_obj, get_apple_udid_key(self.auth))
@ -521,7 +514,10 @@ class IosUtils(object):
if udid_list: if udid_list:
for udid in udid_list: for udid in udid_list:
udid_obj = UDIDsyncDeveloper.objects.filter(developerid_id=developer_obj_id, udid=udid).first() udid_obj = UDIDsyncDeveloper.objects.filter(developerid_id=developer_obj_id, udid=udid).first()
AppUDID.objects.filter(app_id=app_obj, udid=udid_obj, sign_status=3).update(sign_status=4) AppUDID.objects.filter(app_id=app_obj,
udid=udid_obj,
sign_status=SignStatus.PROFILE_DOWNLOAD_COMPLETE).update(
sign_status=SignStatus.SIGNATURE_PACKAGE_COMPLETE)
del_cache_response_by_short(app_obj.app_id) del_cache_response_by_short(app_obj.app_id)
return True return True
@ -533,6 +529,7 @@ class IosUtils(object):
d_result['msg'] = "app_id %s used over limit.now %s limit: %s" % ( d_result['msg'] = "app_id %s used over limit.now %s limit: %s" % (
self.app_obj, used_num, self.app_obj.supersign_limit_number) self.app_obj, used_num, self.app_obj.supersign_limit_number)
logger.error(d_result) logger.error(d_result)
add_sign_message(self.user_obj, self.developer_obj, self.app_obj, '签名余额不足', d_result['msg'], False)
sign_app_over_limit_notify(self.app_obj.user_id, self.app_obj, used_num, sign_app_over_limit_notify(self.app_obj.user_id, self.app_obj, used_num,
self.app_obj.supersign_limit_number) self.app_obj.supersign_limit_number)
return False, d_result return False, d_result
@ -600,7 +597,7 @@ class IosUtils(object):
logger.info(f"app {app_obj} device {sync_device_obj.serial} already in developer {developer_obj}") logger.info(f"app {app_obj} device {sync_device_obj.serial} already in developer {developer_obj}")
if not sync_device_obj.status: if not sync_device_obj.status:
# 库里面存在,并且设备是禁用状态,需要调用api启用 # 库里面存在,并且设备是禁用状态,需要调用api启用
status, result = get_api_obj(developer_obj).set_device_status("enable", sync_device_obj.serial, status, result = get_api_obj(developer_obj, app_obj).set_device_status("enable", sync_device_obj.serial,
sync_device_obj.product, sync_device_obj.product,
sync_device_obj.udid, sync_device_obj.udid,
failed_call_prefix, failed_call_prefix,
@ -611,7 +608,7 @@ class IosUtils(object):
sync_device_obj.save(update_fields=['status']) sync_device_obj.save(update_fields=['status'])
else: else:
# 库里面不存在,注册设备,新设备注册默认就是启用状态 # 库里面不存在,注册设备,新设备注册默认就是启用状态
status, device_obj = get_api_obj(developer_obj).register_device(device_udid, device_name, status, device_obj = get_api_obj(developer_obj, app_obj).register_device(device_udid, device_name,
failed_call_prefix, failed_call_prefix,
register_failed_callback) register_failed_callback)
if not status: if not status:
@ -628,7 +625,7 @@ class IosUtils(object):
# 2. DeveloperDevicesID 添加新应用-设备绑定信息数据 # 2. DeveloperDevicesID 添加新应用-设备绑定信息数据
# 3. AppUDID 添加数据,该数据主要是为了记录使用,is_signed 判断是否已经签名成功 # 3. AppUDID 添加数据,该数据主要是为了记录使用,is_signed 判断是否已经签名成功
del udid_info['udid'] del udid_info['udid']
udid_info['sign_status'] = 1 udid_info['sign_status'] = SignStatus.DEVICE_REGISTRATION_COMPLETE
# udid_info['is_download'] = False # udid_info['is_download'] = False
udid_obj, _ = AppUDID.objects.update_or_create(app_id=app_obj, udid=sync_device_obj, udid_obj, _ = AppUDID.objects.update_or_create(app_id=app_obj, udid=sync_device_obj,
defaults=udid_info) defaults=udid_info)
@ -672,14 +669,18 @@ class IosUtils(object):
bundle_id = app_obj.bundle_id bundle_id = app_obj.bundle_id
app_id = app_obj.app_id app_id = app_obj.app_id
s_type = app_obj.supersign_type s_type = app_obj.supersign_type
status, result = get_api_obj(developer_obj).create_app(bundle_id, app_id, s_type, add_new_bundles_prefix, status, result = get_api_obj(developer_obj, app_obj).create_app(bundle_id, app_id, s_type,
add_new_bundles_prefix,
failed_callback) failed_callback)
if status and result.get('aid'): if status and result.get('aid'):
developer_app_id_obj = DeveloperAppID.objects.create(aid=result.get('aid'), developerid=developer_obj, developer_app_id_obj = DeveloperAppID.objects.create(aid=result.get('aid'), developerid=developer_obj,
app_id=app_obj) app_id=app_obj)
else: else:
return status, result return status, result
AppUDID.objects.filter(app_id=app_obj, udid__developerid_id=developer_obj, sign_status=1).update(sign_status=2) AppUDID.objects.filter(app_id=app_obj,
udid__developerid_id=developer_obj,
sign_status=SignStatus.DEVICE_REGISTRATION_COMPLETE).update(
sign_status=SignStatus.APP_REGISTRATION_COMPLETE)
return True, developer_app_id_obj return True, developer_app_id_obj
@staticmethod @staticmethod
@ -690,13 +691,16 @@ class IosUtils(object):
status, device_obj_list = get_api_obj(developer_obj).get_device() status, device_obj_list = get_api_obj(developer_obj).get_device()
if status: if status:
for device_obj in device_obj_list: for device_obj in device_obj_list:
if device_obj.status not in ['ENABLED', 'DISABLED']: if device_obj.status not in [DeviceStatus.ENABLED, DeviceStatus.DISABLED]:
developer_obj.status = 5 developer_obj.status = AppleDeveloperStatus.DEVICE_ABNORMAL
developer_obj.save(update_fields=['status']) developer_obj.save(update_fields=['status'])
return False, f'issuer_id:{developer_obj.issuer_id} device status unexpected. device_obj:{device_obj}' err_msg = f'issuer_id:{developer_obj.issuer_id} device status unexpected. device_obj:{device_obj}'
add_sign_message(developer_obj.user_id, developer_obj, None, '开发者设备状态异常',
err_msg, False)
return False, err_msg
else: else:
if org_device_obj and org_device_obj.id == device_obj.id: if org_device_obj and org_device_obj.id == device_obj.id:
org_device_obj.status = True if device_obj.status == 'ENABLED' else False org_device_obj.status = device_obj.status
return True, '' return True, ''
@staticmethod @staticmethod
@ -738,12 +742,14 @@ class IosUtils(object):
developer_app_id = developer_app_id_obj.aid developer_app_id = developer_app_id_obj.aid
profile_id = developer_app_id_obj.profile_id profile_id = developer_app_id_obj.profile_id
auth = get_auth_form_developer(developer_obj) auth = get_auth_form_developer(developer_obj)
status, result = get_api_obj(developer_obj).make_and_download_profile(app_obj, status, result = get_api_obj(developer_obj, app_obj).make_and_download_profile(app_obj,
get_profile_full_path(developer_obj, get_profile_full_path(
developer_obj,
app_obj), app_obj),
auth, developer_app_id, auth, developer_app_id,
device_id_lists, profile_id, device_id_lists, profile_id,
failed_call_prefix, failed_callback, failed_call_prefix,
failed_callback,
) )
if not status: if not status:
@ -757,7 +763,13 @@ class IosUtils(object):
d_result['code'] = 1002 d_result['code'] = 1002
d_result['msg'] = msg d_result['msg'] = msg
logger.error(d_result) logger.error(d_result)
disable_developer_and_send_email(self.app_obj, self.developer_obj) add_sign_message(self.user_obj, self.developer_obj, self.app_obj, '签名失败', msg, False)
logger.error(f"app {self.app_obj} developer {self.developer_obj} sign failed. so disabled")
if self.developer_obj.status == AppleDeveloperStatus.ACTIVATED:
self.developer_obj.status = AppleDeveloperStatus.ABNORMAL_STATUS
self.developer_obj.save(update_fields=['status'])
sign_failed_notify(self.developer_obj.user_id, self.developer_obj, self.app_obj)
self.get_developer_auth(False) self.get_developer_auth(False)
def sign_ipa(self, client_ip): def sign_ipa(self, client_ip):
@ -887,7 +899,9 @@ class IosUtils(object):
udid_list = list(set(udid_list)) udid_list = list(set(udid_list))
d_result = {'code': 0, 'msg': 'success'} d_result = {'code': 0, 'msg': 'success'}
AppUDID.objects.filter(app_id=app_obj, udid__udid__in=udid_list, AppUDID.objects.filter(app_id=app_obj, udid__udid__in=udid_list,
udid__developerid=developer_obj, sign_status=2).update(sign_status=3) udid__developerid=developer_obj,
sign_status=SignStatus.APP_REGISTRATION_COMPLETE).update(
sign_status=SignStatus.PROFILE_DOWNLOAD_COMPLETE)
start_time = time.time() start_time = time.time()
logger.info(f"app_id {app_obj} download profile success. time:{start_time - d_time}") logger.info(f"app_id {app_obj} download profile success. time:{start_time - d_time}")
random_file_name = make_from_user_uuid(developer_obj.user_id.uid) random_file_name = make_from_user_uuid(developer_obj.user_id.uid)
@ -940,9 +954,14 @@ class IosUtils(object):
def do_disable_device(developer_obj, udid_lists, udid_obj, disabled): def do_disable_device(developer_obj, udid_lists, udid_obj, disabled):
if udid_lists.count((udid_obj.udid.udid,)) == 1 and disabled: if udid_lists.count((udid_obj.udid.udid,)) == 1 and disabled:
app_api_obj = get_api_obj(developer_obj) app_api_obj = get_api_obj(developer_obj)
app_api_obj.set_device_status("disable", udid_obj.udid.serial, udid_obj.udid.product, udid_obj.udid.udid, status, result = app_api_obj.set_device_status("disable", udid_obj.udid.serial, udid_obj.udid.product,
udid_obj.udid.udid) udid_obj.udid.udid, udid_obj.udid.udid)
UDIDsyncDeveloper.objects.filter(udid=udid_obj.udid.udid, developerid=developer_obj).update(status=False) if status:
UDIDsyncDeveloper.objects.filter(udid=udid_obj.udid.udid, developerid=developer_obj).update(
status=result.status)
else:
logger.error(
f'issuer_id: {developer_obj.issuer_id} {developer_obj} {udid_obj.udid.udid} set disabled failed')
@staticmethod @staticmethod
def do_enable_device_by_sync(developer_obj, udid_sync_obj): def do_enable_device_by_sync(developer_obj, udid_sync_obj):
@ -951,7 +970,11 @@ class IosUtils(object):
udid_sync_obj.udid, udid_sync_obj.udid,
udid_sync_obj.udid) udid_sync_obj.udid)
if status: if status:
UDIDsyncDeveloper.objects.filter(pk=udid_sync_obj.pk, developerid=developer_obj).update(status=True) UDIDsyncDeveloper.objects.filter(pk=udid_sync_obj.pk, developerid=developer_obj).update(
status=result.status)
else:
logger.error(
f'issuer_id: {developer_obj.issuer_id} {developer_obj} {udid_sync_obj.udid} set enabled failed')
@staticmethod @staticmethod
def do_disable_device_by_sync(developer_obj, udid_sync_obj): def do_disable_device_by_sync(developer_obj, udid_sync_obj):
@ -960,7 +983,11 @@ class IosUtils(object):
udid_sync_obj.udid, udid_sync_obj.udid,
udid_sync_obj.udid) udid_sync_obj.udid)
if status: if status:
UDIDsyncDeveloper.objects.filter(pk=udid_sync_obj.pk, developerid=developer_obj).update(status=False) UDIDsyncDeveloper.objects.filter(pk=udid_sync_obj.pk, developerid=developer_obj).update(
status=result.status)
else:
logger.error(
f'issuer_id: {developer_obj.issuer_id} {developer_obj} {udid_sync_obj.udid} set disabled failed')
@staticmethod @staticmethod
def clean_udid_by_app_obj(app_obj, developer_obj): def clean_udid_by_app_obj(app_obj, developer_obj):
@ -1008,9 +1035,9 @@ class IosUtils(object):
def clean_app_by_developer_obj(app_obj, developer_obj): def clean_app_by_developer_obj(app_obj, developer_obj):
developer_app_id_obj = DeveloperAppID.objects.filter(developerid=developer_obj, app_id=app_obj).first() developer_app_id_obj = DeveloperAppID.objects.filter(developerid=developer_obj, app_id=app_obj).first()
if developer_app_id_obj: if developer_app_id_obj:
app_api_obj = get_api_obj(developer_obj) app_api_obj = get_api_obj(developer_obj, app_obj)
app_api_obj.del_profile(developer_app_id_obj.profile_id, app_obj.app_id) app_api_obj.del_profile(developer_app_id_obj.profile_id, app_obj.app_id)
app_api_obj2 = get_api_obj(developer_obj) app_api_obj2 = get_api_obj(developer_obj, app_obj)
app_api_obj2.del_app(developer_app_id_obj.aid, app_obj.bundle_id, app_obj.app_id) app_api_obj2.del_app(developer_app_id_obj.aid, app_obj.bundle_id, app_obj.app_id)
developer_app_id_obj.delete() developer_app_id_obj.delete()
@ -1100,7 +1127,7 @@ class IosUtils(object):
developer_obj.cert_expire_time = format_apple_date(cert_obj.expirationDate) developer_obj.cert_expire_time = format_apple_date(cert_obj.expirationDate)
cert_is_exists = False cert_is_exists = False
break break
developer_obj.status = 1 developer_obj.status = AppleDeveloperStatus.ACTIVATED
if developer_obj.certid and len(developer_obj.certid) > 3 and cert_is_exists and len(result) > 0: if developer_obj.certid and len(developer_obj.certid) > 3 and cert_is_exists and len(result) > 0:
# 多次判断数据库证书id和苹果开发id不一致,可认为被用户删掉,需要执行清理开发者操作 # 多次判断数据库证书id和苹果开发id不一致,可认为被用户删掉,需要执行清理开发者操作
if loop_count > 3: if loop_count > 3:
@ -1110,9 +1137,11 @@ class IosUtils(object):
developer_obj.certid = None developer_obj.certid = None
developer_obj.cert_expire_time = None developer_obj.cert_expire_time = None
# 捕获失败结果,三次重试之后,执行callback 方法,避免一次执行失败直接清理数据 # 捕获失败结果,三次重试之后,执行callback 方法,避免一次执行失败直接清理数据
developer_obj.status = AppleDeveloperStatus.CERTIFICATE_MISSING
developer_obj.save(update_fields=['certid', 'cert_expire_time', 'status'])
return False, {'return_info': '证书检测有误'} return False, {'return_info': '证书检测有误'}
else: else:
developer_obj.status = 4 developer_obj.status = AppleDeveloperStatus.CERTIFICATE_MISSING
developer_obj.save(update_fields=['certid', 'cert_expire_time', 'status']) developer_obj.save(update_fields=['certid', 'cert_expire_time', 'status'])
else: else:
@ -1122,6 +1151,8 @@ class IosUtils(object):
return IosUtils.active_developer(developer_obj, auto_clean, loop_count) return IosUtils.active_developer(developer_obj, auto_clean, loop_count)
if developer_obj.certid and len(developer_obj.certid) > 3 and len(result) == 0: if developer_obj.certid and len(developer_obj.certid) > 3 and len(result) == 0:
developer_obj.status = AppleDeveloperStatus.CERTIFICATE_MISSING
developer_obj.save(update_fields=['certid', 'cert_expire_time', 'status'])
return False, {'return_info': '证书异常,苹果开发者证书为空,疑似证书在苹果开发平台被删除'} return False, {'return_info': '证书异常,苹果开发者证书为空,疑似证书在苹果开发平台被删除'}
developer_obj.save(update_fields=['certid', 'cert_expire_time', 'status']) developer_obj.save(update_fields=['certid', 'cert_expire_time', 'status'])
return status, result return status, result
@ -1140,7 +1171,7 @@ class IosUtils(object):
if status: if status:
cert_id = result.id cert_id = result.id
AppIOSDeveloperInfo.objects.filter(user_id=user_obj, issuer_id=developer_obj.issuer_id).update( AppIOSDeveloperInfo.objects.filter(user_id=user_obj, issuer_id=developer_obj.issuer_id).update(
status=1, status=AppleDeveloperStatus.ACTIVATED,
certid=cert_id, cert_expire_time=format_apple_date(result.expirationDate)) certid=cert_id, cert_expire_time=format_apple_date(result.expirationDate))
resign_app_obj = IosUtils.get_resign_obj(user_obj, developer_obj) resign_app_obj = IosUtils.get_resign_obj(user_obj, developer_obj)
resign_app_obj.make_p12_from_cert(cert_id) resign_app_obj.make_p12_from_cert(cert_id)
@ -1153,7 +1184,7 @@ class IosUtils(object):
if not status: if not status:
logger.warning('%s revoke cert failed,but i need clean cert_id %s' % (developer_obj.issuer_id, result)) logger.warning('%s revoke cert failed,but i need clean cert_id %s' % (developer_obj.issuer_id, result))
AppIOSDeveloperInfo.objects.filter(user_id=user_obj, issuer_id=developer_obj.issuer_id).update( AppIOSDeveloperInfo.objects.filter(user_id=user_obj, issuer_id=developer_obj.issuer_id).update(
status=4, status=AppleDeveloperStatus.CERTIFICATE_MISSING,
certid=None, cert_expire_time=None) certid=None, cert_expire_time=None)
return status, result return status, result
@ -1176,7 +1207,7 @@ class IosUtils(object):
status, result = app_api_obj.auto_set_certid_by_p12(app_dev_pem) status, result = app_api_obj.auto_set_certid_by_p12(app_dev_pem)
if status: if status:
AppIOSDeveloperInfo.objects.filter(user_id=user_obj, issuer_id=auth.get("issuer_id")).update( AppIOSDeveloperInfo.objects.filter(user_id=user_obj, issuer_id=auth.get("issuer_id")).update(
status=1, status=AppleDeveloperStatus.ACTIVATED,
certid=result.id, cert_expire_time=format_apple_date(result.expirationDate)) certid=result.id, cert_expire_time=format_apple_date(result.expirationDate))
return status, result return status, result
@ -1208,7 +1239,7 @@ class IosUtils(object):
return status1, {'return_info': msg} return status1, {'return_info': msg}
udid_result_list = [device.udid for device in result] udid_result_list = [device.udid for device in result]
udid_enabled_result_list = [device.udid for device in result if device.status == 'ENABLED'] udid_enabled_result_list = [device.udid for device in result if device.status == DeviceStatus.ENABLED]
logger.warning(f"issuer_id:{developer_obj.issuer_id} udid database info: {udid_developer_list}") logger.warning(f"issuer_id:{developer_obj.issuer_id} udid database info: {udid_developer_list}")
logger.warning(f"issuer_id:{developer_obj.issuer_id} udid develope info: {udid_result_list}") logger.warning(f"issuer_id:{developer_obj.issuer_id} udid develope info: {udid_result_list}")

@ -9,6 +9,7 @@ from rest_framework.views import APIView
from api.models import Apps from api.models import Apps
from common.cache.state import MigrateStorageState, CleanAppSignDataState from common.cache.state import MigrateStorageState, CleanAppSignDataState
from common.constants import SignStatus
from common.core.auth import ExpiringTokenAuthentication from common.core.auth import ExpiringTokenAuthentication
from common.core.response import ApiResponse from common.core.response import ApiResponse
from common.core.sysconfig import Config from common.core.sysconfig import Config
@ -107,15 +108,17 @@ class AppSignInfoView(APIView):
if app_obj.issupersign: if app_obj.issupersign:
c_task = None c_task = None
if do_sign_flag == 1: if do_sign_flag == 1:
AppUDID.objects.filter(app_id=app_obj).update(sign_status=2) AppUDID.objects.filter(app_id=app_obj).update(sign_status=SignStatus.APP_REGISTRATION_COMPLETE)
if app_obj.change_auto_sign: if app_obj.change_auto_sign:
c_task = run_resign_task(app_obj.pk, True) c_task = run_resign_task(app_obj.pk, True)
if do_sign_flag == 2: if do_sign_flag == 2:
AppUDID.objects.filter(app_id=app_obj, sign_status__gte=3).update(sign_status=3) sign_status = SignStatus.PROFILE_DOWNLOAD_COMPLETE
AppUDID.objects.filter(app_id=app_obj, sign_status__gte=sign_status).update(
sign_status=sign_status)
if app_obj.change_auto_sign: if app_obj.change_auto_sign:
flag = False flag = False
if AppUDID.objects.filter(app_id=app_obj, sign_status=2, if AppUDID.objects.filter(app_id=app_obj, sign_status=SignStatus.APP_REGISTRATION_COMPLETE,
udid__developerid__status__in=Config.DEVELOPER_WRITE_STATUS).first(): udid__developerid__status__in=Config.DEVELOPER_WRITE_STATUS).first():
flag = True flag = True
c_task = run_resign_task(app_obj.pk, flag) c_task = run_resign_task(app_obj.pk, flag)
@ -123,7 +126,7 @@ class AppSignInfoView(APIView):
if do_sign_flag == 3: if do_sign_flag == 3:
if app_obj.change_auto_sign: if app_obj.change_auto_sign:
flag = False flag = False
if AppUDID.objects.filter(app_id=app_obj, sign_status=2, if AppUDID.objects.filter(app_id=app_obj, sign_status=SignStatus.APP_REGISTRATION_COMPLETE,
udid__developerid__status__in=Config.DEVELOPER_WRITE_STATUS).first(): udid__developerid__status__in=Config.DEVELOPER_WRITE_STATUS).first():
flag = True flag = True
c_task = run_resign_task(app_obj.pk, flag, False) c_task = run_resign_task(app_obj.pk, flag, False)

@ -6,6 +6,7 @@
import datetime import datetime
import json import json
import logging import logging
from concurrent.futures import ThreadPoolExecutor
from django.db.models import Count, Q, Sum from django.db.models import Count, Q, Sum
from django.http.response import FileResponse from django.http.response import FileResponse
@ -17,19 +18,20 @@ from api.utils.modelutils import PageNumber
from api.utils.response import BaseResponse from api.utils.response import BaseResponse
from common.base.baseutils import get_choices_dict, get_choices_name_from_key, AppleDeveloperUid, get_real_ip_address from common.base.baseutils import get_choices_dict, get_choices_name_from_key, AppleDeveloperUid, get_real_ip_address
from common.cache.state import CleanSignDataState, MigrateStorageState from common.cache.state import CleanSignDataState, MigrateStorageState
from common.constants import SignStatus, AppleDeveloperStatus
from common.core.auth import ExpiringTokenAuthentication, SuperSignPermission from common.core.auth import ExpiringTokenAuthentication, SuperSignPermission
from common.core.sysconfig import Config from common.core.sysconfig import Config
from common.utils.download import get_app_download_url from common.utils.download import get_app_download_url
from xsign.models import AppIOSDeveloperInfo, APPSuperSignUsedInfo, AppUDID, IosDeveloperPublicPoolBill, \ from xsign.models import AppIOSDeveloperInfo, APPSuperSignUsedInfo, AppUDID, IosDeveloperPublicPoolBill, \
UDIDsyncDeveloper, AppleDeveloperToAppUse, DeveloperAppID, APPToDeveloper, DeveloperDevicesID, \ UDIDsyncDeveloper, AppleDeveloperToAppUse, DeveloperAppID, APPToDeveloper, DeveloperDevicesID, \
IosDeveloperBill IosDeveloperBill, AppleSignMessage
from xsign.tasks import run_resign_task_do, run_resign_task from xsign.tasks import run_resign_task_do, run_resign_task
from xsign.utils.modelutils import get_user_public_used_sign_num, get_user_public_sign_num, check_uid_has_relevant, \ from xsign.utils.modelutils import get_user_public_used_sign_num, get_user_public_sign_num, check_uid_has_relevant, \
get_developer_devices get_developer_devices
from xsign.utils.serializer import DeveloperSerializer, SuperSignUsedSerializer, DeviceUDIDSerializer, \ from xsign.utils.serializer import DeveloperSerializer, SuperSignUsedSerializer, DeviceUDIDSerializer, \
BillInfoSerializer, \ BillInfoSerializer, \
DeveloperDeviceSerializer, AppleDeveloperToAppUseSerializer, AppleDeveloperToAppUseAppsSerializer, \ DeveloperDeviceSerializer, AppleDeveloperToAppUseSerializer, AppleDeveloperToAppUseAppsSerializer, \
BillTransferSerializer BillTransferSerializer, AppleSignMessageSerializer
from xsign.utils.supersignutils import IosUtils from xsign.utils.supersignutils import IosUtils
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -92,25 +94,49 @@ class DeveloperView(APIView):
developer_obj = AppIOSDeveloperInfo.objects.filter(user_id=request.user, issuer_id=issuer_id).first() developer_obj = AppIOSDeveloperInfo.objects.filter(user_id=request.user, issuer_id=issuer_id).first()
else: else:
act = data.get("act", '').strip() act = data.get("act", '').strip()
pools = ThreadPoolExecutor(10)
if act == "syncalldevice": if act == "syncalldevice":
res = BaseResponse() res = BaseResponse()
result_list = [] result_list = []
def run_task(developer_obj):
status, result = IosUtils.get_device_from_developer(developer_obj)
if not status:
result_list.append({'issuer_id': developer_obj.issuer_id, 'msg': result.get("return_info")})
for developer_s_obj in AppIOSDeveloperInfo.objects.filter(user_id=request.user, for developer_s_obj in AppIOSDeveloperInfo.objects.filter(user_id=request.user,
status__in=Config.DEVELOPER_USE_STATUS).all(): status__in=Config.DEVELOPER_USE_STATUS).all():
status, result = IosUtils.get_device_from_developer(developer_s_obj) pools.submit(run_task, developer_s_obj)
if not status: pools.shutdown()
result_list.append(result.get("err_info"))
if len(result_list): if len(result_list):
logger.warning(result_list) logger.warning(result_list)
res.data = result_list
return Response(res.dict) return Response(res.dict)
elif act == "checkauth": elif act == "checkauth":
issuer_ids = data.get("issuer_ids", []) issuer_ids = data.get("issuer_ids", [])
if issuer_ids: if issuer_ids:
result_list = []
def run_task(developer_obj):
status, result = IosUtils.active_developer(developer_obj, False)
if status:
status, result = IosUtils.get_device_from_developer(developer_obj, True)
if not status:
result_list.append(
{'issuer_id': developer_obj.issuer_id, 'msg': result.get("return_info")})
else:
result_list.append({'issuer_id': developer_obj.issuer_id, 'msg': result.get("return_info")})
for developer_s_obj in AppIOSDeveloperInfo.objects.filter(user_id=request.user, for developer_s_obj in AppIOSDeveloperInfo.objects.filter(user_id=request.user,
issuer_id__in=issuer_ids).all(): issuer_id__in=issuer_ids).all():
status, result = IosUtils.active_developer(developer_s_obj, False) pools.submit(run_task, developer_s_obj)
if status: pools.shutdown()
IosUtils.get_device_from_developer(developer_s_obj) if len(result_list):
logger.warning(result_list)
res.data = result_list
elif act == "setstatus": elif act == "setstatus":
issuer_ids = data.get("issuer_ids", []) issuer_ids = data.get("issuer_ids", [])
status = data.get("status", None) status = data.get("status", None)
@ -169,7 +195,7 @@ class DeveloperView(APIView):
if status: if status:
if act == 'renewcert': if act == 'renewcert':
AppUDID.objects.filter(udid__developerid=developer_obj).update( AppUDID.objects.filter(udid__developerid=developer_obj).update(
sign_status=2) sign_status=SignStatus.APP_REGISTRATION_COMPLETE)
status, result = IosUtils.create_developer_cert(developer_obj, request.user) status, result = IosUtils.create_developer_cert(developer_obj, request.user)
# if status: # if status:
@ -200,7 +226,7 @@ class DeveloperView(APIView):
res.msg = "数据清理中,请耐心等待" res.msg = "数据清理中,请耐心等待"
return Response(res.dict) return Response(res.dict)
elif act == 'disable': elif act == 'disable':
developer_obj.status = 0 developer_obj.status = AppleDeveloperStatus.INACTIVATED
developer_obj.save(update_fields=['status']) developer_obj.save(update_fields=['status'])
return Response(res.dict) return Response(res.dict)
else: else:
@ -233,16 +259,16 @@ class DeveloperView(APIView):
p8key = data.get("p8key", developer_obj.p8key) p8key = data.get("p8key", developer_obj.p8key)
if private_key_id != "" and private_key_id != developer_obj.private_key_id: if private_key_id != "" and private_key_id != developer_obj.private_key_id:
developer_obj.private_key_id = private_key_id developer_obj.private_key_id = private_key_id
developer_obj.status = 0 developer_obj.status = AppleDeveloperStatus.INACTIVATED
update_fields.append("private_key_id") update_fields.append("private_key_id")
if p8key != "" and p8key != developer_obj.p8key: if p8key != "" and p8key != developer_obj.p8key:
developer_obj.p8key = p8key developer_obj.p8key = p8key
developer_obj.status = 0 developer_obj.status = AppleDeveloperStatus.INACTIVATED
update_fields.append("p8key") update_fields.append("p8key")
read_only_mode = data.get("read_only_mode", '') read_only_mode = data.get("read_only_mode", '')
if developer_obj.status == 1 and read_only_mode == 'on': if developer_obj.status == AppleDeveloperStatus.ACTIVATED and read_only_mode == 'on':
developer_obj.status = 3 developer_obj.status = AppleDeveloperStatus.MAINTENANCE
update_fields.append("status") update_fields.append("status")
try: try:
@ -362,7 +388,8 @@ class SuperSignUsedView(APIView):
appudid_obj = AppUDID.objects.filter(app_id=app_obj, udid__udid=device_udid, appudid_obj = AppUDID.objects.filter(app_id=app_obj, udid__udid=device_udid,
udid__developerid__issuer_id=developer_id).last() udid__developerid__issuer_id=developer_id).last()
need_download_profile = True need_download_profile = True
if appudid_obj.sign_status in [3, 4]: if appudid_obj.sign_status in [SignStatus.PROFILE_DOWNLOAD_COMPLETE,
SignStatus.SIGNATURE_PACKAGE_COMPLETE]:
need_download_profile = False need_download_profile = False
c_task = run_resign_task_do.apply_async((app_obj.pk, app_to_dev_obj.developerid.pk, c_task = run_resign_task_do.apply_async((app_obj.pk, app_to_dev_obj.developerid.pk,
developer_app_id_obj.aid, need_download_profile, False)) developer_app_id_obj.aid, need_download_profile, False))
@ -454,7 +481,10 @@ class DeveloperDeviceView(APIView):
udid = request.query_params.get("udid", None) udid = request.query_params.get("udid", None)
issuer_id = request.query_params.get("issuer_id", None) issuer_id = request.query_params.get("issuer_id", None)
device_status = request.query_params.get("devicestatus", None)
super_sign_used_objs = UDIDsyncDeveloper.objects.filter(developerid__user_id=request.user, ) super_sign_used_objs = UDIDsyncDeveloper.objects.filter(developerid__user_id=request.user, )
if device_status:
super_sign_used_objs = super_sign_used_objs.filter(status=device_status)
if issuer_id: if issuer_id:
super_sign_used_objs = super_sign_used_objs.filter(developerid__issuer_id=issuer_id) super_sign_used_objs = super_sign_used_objs.filter(developerid__issuer_id=issuer_id)
if udid: if udid:
@ -465,6 +495,8 @@ class DeveloperDeviceView(APIView):
view=self) view=self)
app_serializer = DeveloperDeviceSerializer(app_page_serializer, many=True) app_serializer = DeveloperDeviceSerializer(app_page_serializer, many=True)
res.data = app_serializer.data res.data = app_serializer.data
res.status_choices = get_choices_dict(UDIDsyncDeveloper.status_choices)
res.count = super_sign_used_objs.count() res.count = super_sign_used_objs.count()
return Response(res.dict) return Response(res.dict)
@ -484,9 +516,10 @@ class DeveloperDeviceView(APIView):
app_udid_obj_list = AppUDID.objects.filter(udid__udid=udid, app_id__user_id=request.user, app_udid_obj_list = AppUDID.objects.filter(udid__udid=udid, app_id__user_id=request.user,
udid__developerid=developer_obj).all() udid__developerid=developer_obj).all()
for app_udid_obj in app_udid_obj_list: for app_udid_obj in app_udid_obj_list:
for app_id_info in DeveloperDevicesID.objects.filter(udid=app_udid_obj.udid, q_infos = DeveloperDevicesID.objects.filter(udid=app_udid_obj.udid,
developerid=developer_obj).values( developerid=developer_obj).values(
'app_id').all().distinct(): 'app_id').all().distinct()
for app_id_info in q_infos:
logger.error(f"user {request.user} delete devices {app_udid_obj}") logger.error(f"user {request.user} delete devices {app_udid_obj}")
IosUtils.disable_udid(app_udid_obj, app_id_info.get('app_id'), True) IosUtils.disable_udid(app_udid_obj, app_id_info.get('app_id'), True)
AppUDID.objects.filter(pk=app_udid_obj.pk).delete() AppUDID.objects.filter(pk=app_udid_obj.pk).delete()
@ -878,3 +911,31 @@ class AppleDeveloperBindAppsView(APIView):
except Exception as e: except Exception as e:
logger.error(f'update app developer used failed infos:{infos} Exception:{e}') logger.error(f'update app developer used failed infos:{infos} Exception:{e}')
return Response(res.dict) return Response(res.dict)
class SignOperateMessageView(APIView):
authentication_classes = [ExpiringTokenAuthentication, ]
permission_classes = [SuperSignPermission, ]
def get(self, request):
res = BaseResponse()
issuer_id = request.query_params.get("issuer_id", None)
operate_status = request.query_params.get("operate_status", None)
sign_message_queryset = AppleSignMessage.objects.filter(user_id=request.user)
if issuer_id:
sign_message_queryset = sign_message_queryset.filter(developerid__issuer_id=issuer_id)
if operate_status:
sign_message_queryset = sign_message_queryset.filter(operate_status=operate_status)
page_obj = PageNumber()
page_serializer = page_obj.paginate_queryset(queryset=sign_message_queryset.order_by("-operate_time"),
request=request,
view=self)
message_serializer = AppleSignMessageSerializer(page_serializer, many=True, )
res.data = message_serializer.data
res.count = sign_message_queryset.count()
res.status_choices = get_choices_dict(AppleSignMessage.status_choices)
return Response(res.dict)

Loading…
Cancel
Save