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

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. 158
      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. 149
      fir_ser/xsign/utils/supersignutils.py
  29. 11
      fir_ser/xsign/views/appinfo.py
  30. 97
      fir_ser/xsign/views/supersign.py

@ -353,7 +353,7 @@
import QRCode from 'qrcodejs2'
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 {
name: "FirDownload",
@ -923,10 +923,7 @@ export default {
return ftype
},
formatTime: function (stime) {
if (stime) {
stime = stime.split(".")[0].split("T");
return stime[0] + " " + stime[1]
}
return format_time(stime)
}
}
};

@ -340,7 +340,7 @@
import QRCode from 'qrcodejs2'
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 {
name: "ShortDownload",
@ -881,10 +881,7 @@ export default {
return ftype
},
formatTime: function (stime) {
if (stime) {
stime = stime.split(".")[0].split("T");
return stime[0] + " " + stime[1]
}
return format_time(stime)
}
}
};

@ -778,6 +778,10 @@
clearable
placeholder="输入开发者用户ID"
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>
@ -837,16 +841,19 @@
label="设备状态"
width="110">
<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-tag type="info">禁用</el-tag>
</el-link>
</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-tag>启用</el-tag>
</el-link>
</el-tooltip>
<el-tooltip v-else :content="`当设备状态为 ${scope.row.device_status} 时,意味着该开发者不可用`">
<el-tag type="danger">{{ scope.row.device_status }}</el-tag>
</el-tooltip>
</template>
</el-table-column>
<el-table-column
@ -1354,6 +1361,92 @@
</el-table>
</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">
<el-pagination
:current-page.sync="pagination.currentPage"
@ -1380,10 +1473,12 @@ import {
iosdeveloper,
iosdevices,
iosdevicesudid,
iosudevices
iosudevices,
signoperatemessage
} from "@/restful";
import {format_choices, getUserInfoFun, removeAaary} from "@/utils";
import AppleDeveloperBindApp from "@/components/base/AppleDeveloperBindApp";
import {format_time} from "@/utils/base/utils";
export default {
name: "FirSuperSignBase",
@ -1399,6 +1494,7 @@ export default {
bill_percent: 0,
fileList: [],
developer_udevices_lists: [],
operate_message_lists: [],
app_developer_lists: [],
app_devices_lists: [],
app_bill_lists: [],
@ -1411,6 +1507,9 @@ export default {
udidsearch: "",
Bundleidsearch: "",
appidseach: "",
devicestatus: "",
operatestatus: "",
operate_status_choices: [],
uidsearch: "",
appnamesearch: "",
timerangesearch: [],
@ -1490,6 +1589,7 @@ export default {
bind_appletoapp_sure: false,
appletoapp_title: '',
status_choices: [],
device_status_choices: [],
read_only_mode: 'off',
developer_status_choice: [],
multipleSelection: [],
@ -1989,7 +2089,11 @@ export default {
data.appnamesearch = this.appnamesearch.replace(/^\s+|\s+$/g, "");
this.iosdevicerankFun({"methods": "GET", "data": data})
} else if (tabname === "iosudevices") {
data.devicestatus = this.devicestatus;
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
deviceformatter(row, column) {
let stime = row.created_time;
if (stime) {
stime = stime.split(".")[0].split("T");
return stime[0] + " " + stime[1]
} else
return '';
return format_time(row.created_time)
},
// eslint-disable-next-line no-unused-vars
formatter(row, column) {
let stime = row.cert_expire_time;
if (stime) {
stime = stime.split(".")[0].split("T");
return stime[0] + " " + stime[1]
} else
return '';
return format_time(row.cert_expire_time)
},
// eslint-disable-next-line no-unused-vars
operateformatter(row, column) {
return format_time(row.operate_time)
},
iosdevicesFun(methods, data) {
this.loading = true;
@ -2063,7 +2161,21 @@ export default {
}
}
if (params.methods === 'PUT') {
this.$message.success("操作成功");
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("操作成功");
}
}
if (params.data.act === 'setstatus') {
this.setdeveloperstatusVisible = false
@ -2196,6 +2308,17 @@ export default {
"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) {
if (action !== 'GET') {
this.loadingfun = this.$loading({
@ -2212,6 +2335,7 @@ export default {
if (action !== "PUT") {
this.developer_udevices_lists = data.data;
this.pagination.total = data.count;
this.device_status_choices = data.status_choices;
} else {
this.refreshactiveFun()
}
@ -2231,7 +2355,7 @@ export default {
getUserInfoFun(this);
if (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) {
if (activeName_list[index] === activeName) {
this.activeName = activeName;

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

@ -248,13 +248,7 @@ export default {
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() {
getUserInfoFun(this);
this.get_data_from_tabname();

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

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

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

@ -63,3 +63,11 @@ export function geetestbase(func, self, uid, params, callback, errback, readybac
"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}")
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)
return RemoteClientInfo.objects.filter(created_time__lt=clean_time).delete()

@ -94,8 +94,8 @@ class IpProxyListCache(RedisCacheBase):
class IpProxyActiveCache(RedisCacheBase):
def __init__(self):
self.cache_key = CACHE_KEY_TEMPLATE.get("ip_proxy_store_active_key")
def __init__(self, issuer_id):
self.cache_key = f"{CACHE_KEY_TEMPLATE.get('ip_proxy_store_active_key')}_{issuer_id}"
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(
e) or 'Max retries exceeded with' in str(e):
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(
f'exec {func} failed. Failed:{e} {try_attempts} times in total. now {sleep_time} later try '
f'again...{i}')
@ -751,7 +751,7 @@ class AppStoreConnectApi(DevicesAPI, BundleIDsAPI, BundleIDsCapabilityAPI, Profi
self.private_key_id = private_key_id
self.p8_private_key = p8_private_key
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.__make_jwt_headers()
DevicesAPI.__init__(self, self.BASE_URI, self.headers)

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

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

@ -502,6 +502,11 @@ CELERY_BEAT_SCHEDULE = {
'schedule': crontab(hour=1, minute=1),
'args': ()
},
'auto_clean_sign_log_job': {
'task': 'xsign.tasks.auto_clean_sign_log_job',
'schedule': crontab(hour=2, minute=2),
'args': ()
},
}
# 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 common.base.daobase import AESCharField
from common.constants import DeviceStatus, AppleDeveloperStatus, SignStatus
class AppIOSDeveloperInfo(models.Model):
@ -23,8 +24,16 @@ class AppIOSDeveloperInfo(models.Model):
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)
status_choices = ((AppleDeveloperStatus.BAN, '疑似被封'), (AppleDeveloperStatus.INACTIVATED, '未激活'),
(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)
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, )
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)
created_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间") # django.utils.timezone.now()
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:
verbose_name = 'iOS开发平台同步设备信息'
@ -69,7 +80,7 @@ class UDIDsyncDeveloper(models.Model):
unique_together = ('udid', 'developerid',)
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):
@ -85,8 +96,14 @@ class AppUDID(models.Model):
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="签名状态")
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:
verbose_name = '设备详情'
@ -239,3 +256,30 @@ class AppleDeveloperToAppUse(models.Model):
def __str__(self):
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 fir_ser.celery import app
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.modelutils import add_sign_message
from xsign.utils.supersignutils import IosUtils, resign_by_app_id_and_developer
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)
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:
code = msg.get("code", -1)
if code == 0:
@ -84,3 +91,8 @@ def auto_check_ios_developer_active_job():
@app.task
def get_best_proxy_ips_job():
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.receiveudids import IosUDIDView, TaskView, ShowUdidView
from xsign.views.supersign import DeveloperView, SuperSignUsedView, AppUDIDUsedView, SuperSignCertView, \
DeviceUsedBillView, DeveloperDeviceView, DeviceUsedRankInfoView, AppleDeveloperBindAppsView, DeviceTransferBillView
DeviceUsedBillView, DeveloperDeviceView, DeviceUsedRankInfoView, AppleDeveloperBindAppsView, DeviceTransferBillView, \
SignOperateMessageView
urlpatterns = [
re_path(r"^developer$", DeveloperView.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"^udevices$", DeveloperDeviceView.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.notify.notify import check_developer_status_notify
from fir_ser.settings import SUPER_SIGN_ROOT, SYNC_CACHE_TO_DATABASE
from xsign.models import UserInfo, AppIOSDeveloperInfo, APPSuperSignUsedInfo
from xsign.utils.modelutils import get_developer_devices
from xsign.models import UserInfo, AppIOSDeveloperInfo, APPSuperSignUsedInfo, AppleSignMessage
from xsign.utils.modelutils import get_developer_devices, add_sign_message
from xsign.utils.supersignutils import IosUtils
logger = logging.getLogger(__name__)
@ -46,6 +46,8 @@ def auto_check_ios_developer_active():
if user_obj.supersign_active:
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}"
if not status:
add_sign_message(user_obj, developer_obj, None, '开发者状态自动检测', result.get('return_info'), False)
err_issuer_id.append(developer_obj)
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)
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.cache.state import CleanErrorBundleIdSignDataState
from common.constants import AppleDeveloperStatus
from common.core.sysconfig import Config
from common.libs.apple.appleapiv3 import AppStoreConnectApi, Certificates, Devices, BundleIds, Profiles
from fir_ser.settings import SUPER_SIGN_ROOT
from xsign.models import AppIOSDeveloperInfo
from xsign.utils.modelutils import add_sign_message
logger = logging.getLogger(__name__)
@ -225,30 +227,31 @@ def check_error_call_back(error, developer_pk):
msg = "代理网络错误,请稍后重试或联系管理员处理"
if 'it may be encrypted with an unsupported algorithm' in error:
msg = "数据校验失败,请检查p8key内容是否正常"
status = 5
status = AppleDeveloperStatus.ABNORMAL_STATUS
if 'Authentication credentials are missing or invalid' in error:
msg = '认证失败,请检查开发者信息填写是否正确'
status = 5
status = AppleDeveloperStatus.ABNORMAL_STATUS
if 'FORBIDDEN.REQUIRED_AGREEMENTS_MISSING_OR_EXPIRED' in error:
msg = '请登录 https://developer.apple.com/account/ 并同意最新协议'
status = 2
status = AppleDeveloperStatus.AGREEMENT_NOT_AGREED
if status is not None:
developer_obj = AppIOSDeveloperInfo.objects.filter(pk=developer_pk).first()
if developer_obj:
if developer_obj.status == -1 and status == 5:
status = -1
if developer_obj.status == AppleDeveloperStatus.BAN and status == AppleDeveloperStatus.ABNORMAL_STATUS:
status = AppleDeveloperStatus.BAN
AppIOSDeveloperInfo.objects.filter(pk=developer_pk).update(status=status)
logger.error(f"{msg} {error}")
return msg if msg else error
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.private_key_id = private_key_id
self.p8key = p8key
self.cert_id = cert_id
self.developer_pk = developer_pk
self.app_obj = app_obj
def __getattribute__(self, name):
attr = object.__getattribute__(self, name)
@ -307,7 +310,13 @@ class AppDeveloperApiV2(object):
with CleanErrorBundleIdSignDataState(failed_call_prefix) as state:
if state:
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()
else:
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:
pools.submit(task, proxy_ip)
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]
IpProxyListCache().set_storage_cache(best_sorted_ips, 6 * 60 * 60)
return best_sorted_ips
def get_proxy_ip_from_cache(change_ip=False):
active_proxy_cache = IpProxyActiveCache()
def get_proxy_ip_from_cache(issuer_id, change_ip=False):
active_proxy_cache = IpProxyActiveCache(issuer_id)
active_ip_proxy = active_proxy_cache.get_storage_cache()
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
list_proxy_cache = IpProxyListCache()
@ -84,4 +88,4 @@ def get_proxy_ip_from_cache(change_ip=False):
def clean_ip_proxy_infos():
logger.info("clean ip proxy infos")
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 common.base.baseutils import is_valid_phone
from common.constants import SignStatus
from common.core.sysconfig import Config
from xsign.models import APPSuperSignUsedInfo, UDIDsyncDeveloper, AppUDID, APPToDeveloper, AppIOSDeveloperInfo, \
IosDeveloperPublicPoolBill, IosDeveloperBill, DeveloperDevicesID, AppleDeveloperToAppUse, DeveloperAppID
IosDeveloperPublicPoolBill, IosDeveloperBill, DeveloperDevicesID, AppleDeveloperToAppUse, DeveloperAppID, \
AppleSignMessage
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):
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
release_obj = AppReleaseInfo.objects.filter(app_id=app_obj, is_master=True).first()
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,
"udid": device_obj.udid,
"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)
@ -197,3 +200,10 @@ def get_filename_form_file(filename):
filename = f"{app_obj.name}-sign-{app_obj.short}{f_type}"
check = True
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_description = serializers.CharField(source="developerid.description")
developer_status = serializers.CharField(source="developerid.get_status_display")
device_status = serializers.CharField(source="get_status_display")
app_used_count = serializers.SerializerMethodField()
def get_app_used_count(self, obj):
@ -361,3 +361,19 @@ class AppSignSerializer(serializers.ModelSerializer):
def get_developer_used_count(self, obj):
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.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, \
xsign_migrate_data_signal, xsign_clean_data_signal, xsign_app_release_obj_signal
from common.core.sysconfig import Config
@ -28,7 +29,8 @@ logger = logging.getLogger(__name__)
def run_resign_task_callback(sender, **kwargs):
app_obj = kwargs.get('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:
c_task = run_resign_task(app_obj.pk, False, False)
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')
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:
super_sign_obj = APPSuperSignUsedInfo.objects.filter(udid__udid__udid=udid,
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
from common.base.magic import run_function_by_locker, call_function_try_attempts, magic_wrapper
from common.cache.state import CleanErrorBundleIdSignDataState
from common.constants import DeviceStatus, AppleDeveloperStatus, SignStatus
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.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
from xsign.utils.iossignapi import ResignApp, AppDeveloperApiV2
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.utils import delete_app_to_dev_and_file
@ -198,10 +199,10 @@ def get_auth_form_developer(developer_obj):
return auth
def get_api_obj(developer_obj):
def get_api_obj(developer_obj, app_obj=None):
auth = get_auth_form_developer(developer_obj)
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:
app_api_obj = None
return app_api_obj
@ -214,13 +215,6 @@ def get_apple_udid_key(auth):
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):
can_used_developer_pk_list = []
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):
d_result = {'code': 0, 'msg': 'success'}
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.sign_status == 4:
if app_udid_obj and app_udid_obj.sign_status >= SignStatus.PROFILE_DOWNLOAD_COMPLETE:
if app_udid_obj.sign_status == SignStatus.SIGNATURE_PACKAGE_COMPLETE:
if check_ipa_is_latest_sign(app_obj, developer_obj):
d_result['msg'] = f'udid {udid} exists app_id {app_obj}'
logger.warning(d_result)
@ -411,13 +405,12 @@ class IosUtils(object):
self.auth = get_auth_form_developer(self.developer_obj)
else:
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):
send_msg_over_limit("set", self.user_obj.email)
sign_unavailable_developer_notify(self.user_obj, self.app_obj)
if send_msg_over_limit("get", self.user_obj.email):
send_msg_over_limit("set", self.user_obj.email)
sign_unavailable_developer_notify(self.user_obj, self.app_obj)
else:
logger.error(f"user {self.user_obj} send msg failed. over limit")
else:
logger.error(f"user {self.user_obj} send msg failed. over limit")
# def download_profile(self, developer_app_id, device_id_list):
# return get_api_obj(self.auth).get_profile(self.app_obj, self.udid_info,
@ -433,7 +426,7 @@ class IosUtils(object):
@staticmethod
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):
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:
for udid in udid_list:
udid_obj = UDIDsyncDeveloper.objects.filter(developerid_id=developer_obj_id, udid=udid).first()
AppUDID.objects.filter(app_id=app_obj, udid=udid_obj, 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)
return True
@ -533,6 +529,7 @@ class IosUtils(object):
d_result['msg'] = "app_id %s used over limit.now %s limit: %s" % (
self.app_obj, used_num, self.app_obj.supersign_limit_number)
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,
self.app_obj.supersign_limit_number)
return False, d_result
@ -600,20 +597,20 @@ class IosUtils(object):
logger.info(f"app {app_obj} device {sync_device_obj.serial} already in developer {developer_obj}")
if not sync_device_obj.status:
# 库里面存在,并且设备是禁用状态,需要调用api启用
status, result = get_api_obj(developer_obj).set_device_status("enable", sync_device_obj.serial,
sync_device_obj.product,
sync_device_obj.udid,
failed_call_prefix,
set_failed_callback)
status, result = get_api_obj(developer_obj, app_obj).set_device_status("enable", sync_device_obj.serial,
sync_device_obj.product,
sync_device_obj.udid,
failed_call_prefix,
set_failed_callback)
if not status: # 已经包含异常操作,暂定
return status, result
sync_device_obj.status = True
sync_device_obj.save(update_fields=['status'])
else:
# 库里面不存在,注册设备,新设备注册默认就是启用状态
status, device_obj = get_api_obj(developer_obj).register_device(device_udid, device_name,
failed_call_prefix,
register_failed_callback)
status, device_obj = get_api_obj(developer_obj, app_obj).register_device(device_udid, device_name,
failed_call_prefix,
register_failed_callback)
if not status:
return status, device_obj
@ -628,7 +625,7 @@ class IosUtils(object):
# 2. DeveloperDevicesID 添加新应用-设备绑定信息数据
# 3. AppUDID 添加数据,该数据主要是为了记录使用,is_signed 判断是否已经签名成功
del udid_info['udid']
udid_info['sign_status'] = 1
udid_info['sign_status'] = SignStatus.DEVICE_REGISTRATION_COMPLETE
# udid_info['is_download'] = False
udid_obj, _ = AppUDID.objects.update_or_create(app_id=app_obj, udid=sync_device_obj,
defaults=udid_info)
@ -672,14 +669,18 @@ class IosUtils(object):
bundle_id = app_obj.bundle_id
app_id = app_obj.app_id
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,
failed_callback)
status, result = get_api_obj(developer_obj, app_obj).create_app(bundle_id, app_id, s_type,
add_new_bundles_prefix,
failed_callback)
if status and result.get('aid'):
developer_app_id_obj = DeveloperAppID.objects.create(aid=result.get('aid'), developerid=developer_obj,
app_id=app_obj)
else:
return status, result
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
@staticmethod
@ -690,13 +691,16 @@ class IosUtils(object):
status, device_obj_list = get_api_obj(developer_obj).get_device()
if status:
for device_obj in device_obj_list:
if device_obj.status not in ['ENABLED', 'DISABLED']:
developer_obj.status = 5
if device_obj.status not in [DeviceStatus.ENABLED, DeviceStatus.DISABLED]:
developer_obj.status = AppleDeveloperStatus.DEVICE_ABNORMAL
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:
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, ''
@staticmethod
@ -738,13 +742,15 @@ class IosUtils(object):
developer_app_id = developer_app_id_obj.aid
profile_id = developer_app_id_obj.profile_id
auth = get_auth_form_developer(developer_obj)
status, result = get_api_obj(developer_obj).make_and_download_profile(app_obj,
get_profile_full_path(developer_obj,
app_obj),
auth, developer_app_id,
device_id_lists, profile_id,
failed_call_prefix, failed_callback,
)
status, result = get_api_obj(developer_obj, app_obj).make_and_download_profile(app_obj,
get_profile_full_path(
developer_obj,
app_obj),
auth, developer_app_id,
device_id_lists, profile_id,
failed_call_prefix,
failed_callback,
)
if not status:
return False, result
@ -757,7 +763,13 @@ class IosUtils(object):
d_result['code'] = 1002
d_result['msg'] = msg
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)
def sign_ipa(self, client_ip):
@ -887,7 +899,9 @@ class IosUtils(object):
udid_list = list(set(udid_list))
d_result = {'code': 0, 'msg': 'success'}
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()
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)
@ -940,9 +954,14 @@ class IosUtils(object):
def do_disable_device(developer_obj, udid_lists, udid_obj, disabled):
if udid_lists.count((udid_obj.udid.udid,)) == 1 and disabled:
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,
udid_obj.udid.udid)
UDIDsyncDeveloper.objects.filter(udid=udid_obj.udid.udid, developerid=developer_obj).update(status=False)
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)
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
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)
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
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)
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
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):
developer_app_id_obj = DeveloperAppID.objects.filter(developerid=developer_obj, app_id=app_obj).first()
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_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)
developer_app_id_obj.delete()
@ -1100,7 +1127,7 @@ class IosUtils(object):
developer_obj.cert_expire_time = format_apple_date(cert_obj.expirationDate)
cert_is_exists = False
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:
# 多次判断数据库证书id和苹果开发id不一致,可认为被用户删掉,需要执行清理开发者操作
if loop_count > 3:
@ -1110,10 +1137,12 @@ class IosUtils(object):
developer_obj.certid = None
developer_obj.cert_expire_time = None
# 捕获失败结果,三次重试之后,执行callback 方法,避免一次执行失败直接清理数据
developer_obj.status = AppleDeveloperStatus.CERTIFICATE_MISSING
developer_obj.save(update_fields=['certid', 'cert_expire_time', 'status'])
return False, {'return_info': '证书检测有误'}
else:
developer_obj.status = 4
developer_obj.save(update_fields=['certid', 'cert_expire_time', 'status'])
developer_obj.status = AppleDeveloperStatus.CERTIFICATE_MISSING
developer_obj.save(update_fields=['certid', 'cert_expire_time', 'status'])
else:
loop_count += 1
@ -1122,6 +1151,8 @@ class IosUtils(object):
return IosUtils.active_developer(developer_obj, auto_clean, loop_count)
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': '证书异常,苹果开发者证书为空,疑似证书在苹果开发平台被删除'}
developer_obj.save(update_fields=['certid', 'cert_expire_time', 'status'])
return status, result
@ -1140,7 +1171,7 @@ class IosUtils(object):
if status:
cert_id = result.id
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))
resign_app_obj = IosUtils.get_resign_obj(user_obj, developer_obj)
resign_app_obj.make_p12_from_cert(cert_id)
@ -1153,7 +1184,7 @@ class IosUtils(object):
if not status:
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(
status=4,
status=AppleDeveloperStatus.CERTIFICATE_MISSING,
certid=None, cert_expire_time=None)
return status, result
@ -1176,7 +1207,7 @@ class IosUtils(object):
status, result = app_api_obj.auto_set_certid_by_p12(app_dev_pem)
if status:
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))
return status, result
@ -1208,7 +1239,7 @@ class IosUtils(object):
return status1, {'return_info': msg}
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 develope info: {udid_result_list}")

@ -9,6 +9,7 @@ from rest_framework.views import APIView
from api.models import Apps
from common.cache.state import MigrateStorageState, CleanAppSignDataState
from common.constants import SignStatus
from common.core.auth import ExpiringTokenAuthentication
from common.core.response import ApiResponse
from common.core.sysconfig import Config
@ -107,15 +108,17 @@ class AppSignInfoView(APIView):
if app_obj.issupersign:
c_task = None
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:
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)
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:
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():
flag = True
c_task = run_resign_task(app_obj.pk, flag)
@ -123,7 +126,7 @@ class AppSignInfoView(APIView):
if do_sign_flag == 3:
if app_obj.change_auto_sign:
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():
flag = True
c_task = run_resign_task(app_obj.pk, flag, False)

@ -6,6 +6,7 @@
import datetime
import json
import logging
from concurrent.futures import ThreadPoolExecutor
from django.db.models import Count, Q, Sum
from django.http.response import FileResponse
@ -17,19 +18,20 @@ from api.utils.modelutils import PageNumber
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.cache.state import CleanSignDataState, MigrateStorageState
from common.constants import SignStatus, AppleDeveloperStatus
from common.core.auth import ExpiringTokenAuthentication, SuperSignPermission
from common.core.sysconfig import Config
from common.utils.download import get_app_download_url
from xsign.models import AppIOSDeveloperInfo, APPSuperSignUsedInfo, AppUDID, IosDeveloperPublicPoolBill, \
UDIDsyncDeveloper, AppleDeveloperToAppUse, DeveloperAppID, APPToDeveloper, DeveloperDevicesID, \
IosDeveloperBill
IosDeveloperBill, AppleSignMessage
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, \
get_developer_devices
from xsign.utils.serializer import DeveloperSerializer, SuperSignUsedSerializer, DeviceUDIDSerializer, \
BillInfoSerializer, \
DeveloperDeviceSerializer, AppleDeveloperToAppUseSerializer, AppleDeveloperToAppUseAppsSerializer, \
BillTransferSerializer
BillTransferSerializer, AppleSignMessageSerializer
from xsign.utils.supersignutils import IosUtils
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()
else:
act = data.get("act", '').strip()
pools = ThreadPoolExecutor(10)
if act == "syncalldevice":
res = BaseResponse()
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,
status__in=Config.DEVELOPER_USE_STATUS).all():
status, result = IosUtils.get_device_from_developer(developer_s_obj)
if not status:
result_list.append(result.get("err_info"))
pools.submit(run_task, developer_s_obj)
pools.shutdown()
if len(result_list):
logger.warning(result_list)
res.data = result_list
return Response(res.dict)
elif act == "checkauth":
issuer_ids = data.get("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,
issuer_id__in=issuer_ids).all():
status, result = IosUtils.active_developer(developer_s_obj, False)
if status:
IosUtils.get_device_from_developer(developer_s_obj)
pools.submit(run_task, developer_s_obj)
pools.shutdown()
if len(result_list):
logger.warning(result_list)
res.data = result_list
elif act == "setstatus":
issuer_ids = data.get("issuer_ids", [])
status = data.get("status", None)
@ -169,7 +195,7 @@ class DeveloperView(APIView):
if status:
if act == 'renewcert':
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)
# if status:
@ -200,7 +226,7 @@ class DeveloperView(APIView):
res.msg = "数据清理中,请耐心等待"
return Response(res.dict)
elif act == 'disable':
developer_obj.status = 0
developer_obj.status = AppleDeveloperStatus.INACTIVATED
developer_obj.save(update_fields=['status'])
return Response(res.dict)
else:
@ -233,16 +259,16 @@ class DeveloperView(APIView):
p8key = data.get("p8key", developer_obj.p8key)
if private_key_id != "" and private_key_id != developer_obj.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")
if p8key != "" and p8key != developer_obj.p8key:
developer_obj.p8key = p8key
developer_obj.status = 0
developer_obj.status = AppleDeveloperStatus.INACTIVATED
update_fields.append("p8key")
read_only_mode = data.get("read_only_mode", '')
if developer_obj.status == 1 and read_only_mode == 'on':
developer_obj.status = 3
if developer_obj.status == AppleDeveloperStatus.ACTIVATED and read_only_mode == 'on':
developer_obj.status = AppleDeveloperStatus.MAINTENANCE
update_fields.append("status")
try:
@ -362,7 +388,8 @@ class SuperSignUsedView(APIView):
appudid_obj = AppUDID.objects.filter(app_id=app_obj, udid__udid=device_udid,
udid__developerid__issuer_id=developer_id).last()
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
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))
@ -454,7 +481,10 @@ class DeveloperDeviceView(APIView):
udid = request.query_params.get("udid", 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, )
if device_status:
super_sign_used_objs = super_sign_used_objs.filter(status=device_status)
if issuer_id:
super_sign_used_objs = super_sign_used_objs.filter(developerid__issuer_id=issuer_id)
if udid:
@ -465,6 +495,8 @@ class DeveloperDeviceView(APIView):
view=self)
app_serializer = DeveloperDeviceSerializer(app_page_serializer, many=True)
res.data = app_serializer.data
res.status_choices = get_choices_dict(UDIDsyncDeveloper.status_choices)
res.count = super_sign_used_objs.count()
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,
udid__developerid=developer_obj).all()
for app_udid_obj in app_udid_obj_list:
for app_id_info in DeveloperDevicesID.objects.filter(udid=app_udid_obj.udid,
developerid=developer_obj).values(
'app_id').all().distinct():
q_infos = DeveloperDevicesID.objects.filter(udid=app_udid_obj.udid,
developerid=developer_obj).values(
'app_id').all().distinct()
for app_id_info in q_infos:
logger.error(f"user {request.user} delete devices {app_udid_obj}")
IosUtils.disable_udid(app_udid_obj, app_id_info.get('app_id'), True)
AppUDID.objects.filter(pk=app_udid_obj.pk).delete()
@ -878,3 +911,31 @@ class AppleDeveloperBindAppsView(APIView):
except Exception as e:
logger.error(f'update app developer used failed infos:{infos} Exception:{e}')
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