代码结构优化

xsign
nineven 3 years ago
parent 5e380fa09e
commit 153f800b91
  1. 2
      fir_ser/admin/urls.py
  2. 50
      fir_ser/admin/utils/serializer.py
  3. 18
      fir_ser/admin/utils/utils.py
  4. 5
      fir_ser/admin/views/app.py
  5. 3
      fir_ser/admin/views/domain.py
  6. 2
      fir_ser/admin/views/login.py
  7. 3
      fir_ser/admin/views/order.py
  8. 3
      fir_ser/admin/views/storage.py
  9. 11
      fir_ser/admin/views/user.py
  10. 6
      fir_ser/api/admin.py
  11. 9
      fir_ser/api/base_views.py
  12. 2
      fir_ser/api/management/commands/expire_caches.py
  13. 238
      fir_ser/api/models.py
  14. 73
      fir_ser/api/tasks.py
  15. 14
      fir_ser/api/urls.py
  16. 387
      fir_ser/api/utils/app/firApi.py
  17. 91
      fir_ser/api/utils/app/raqc.py
  18. 140
      fir_ser/api/utils/app/shellcmds.py
  19. 51
      fir_ser/api/utils/apputils.py
  20. 47
      fir_ser/api/utils/ctasks.py
  21. 98
      fir_ser/api/utils/modelutils.py
  22. 325
      fir_ser/api/utils/serializer.py
  23. 63
      fir_ser/api/utils/utils.py
  24. 103
      fir_ser/api/views/apps.py
  25. 81
      fir_ser/api/views/download.py
  26. 2
      fir_ser/api/views/storage.py
  27. 16
      fir_ser/api/views/uploads.py
  28. 4
      fir_ser/cli/urls.py
  29. 38
      fir_ser/common/core/response.py
  30. 46
      fir_ser/common/core/singals.py
  31. 2
      fir_ser/common/libs/apple/appleapiv3.py
  32. 2
      fir_ser/common/libs/storage/localApi.py
  33. 6
      fir_ser/common/utils/caches.py
  34. 0
      fir_ser/common/utils/token.py
  35. 3
      fir_ser/fir_ser/settings.py
  36. 3
      fir_ser/fir_ser/urls.py
  37. 0
      fir_ser/xsign/__init__.py
  38. 15
      fir_ser/xsign/admin.py
  39. 5
      fir_ser/xsign/apps.py
  40. 0
      fir_ser/xsign/migrations/__init__.py
  41. 241
      fir_ser/xsign/models.py
  42. 80
      fir_ser/xsign/tasks.py
  43. 34
      fir_ser/xsign/urls.py
  44. 0
      fir_ser/xsign/utils/__init__.py
  45. 40
      fir_ser/xsign/utils/ctasks.py
  46. 39
      fir_ser/xsign/utils/iossignapi.py
  47. 0
      fir_ser/xsign/utils/iproxy.py
  48. 165
      fir_ser/xsign/utils/modelutils.py
  49. 363
      fir_ser/xsign/utils/serializer.py
  50. 20
      fir_ser/xsign/utils/supersignutils.py
  51. 73
      fir_ser/xsign/utils/utils.py
  52. 0
      fir_ser/xsign/views/__init__.py
  53. 0
      fir_ser/xsign/views/admin/__init__.py
  54. 11
      fir_ser/xsign/views/admin/supersign.py
  55. 151
      fir_ser/xsign/views/appinfo.py
  56. 106
      fir_ser/xsign/views/download.py
  57. 4
      fir_ser/xsign/views/receiveudids.py
  58. 22
      fir_ser/xsign/views/supersign.py

@ -22,8 +22,8 @@ from admin.views.login import LoginView, LoginUserView
from admin.views.order import OrderInfoView from admin.views.order import OrderInfoView
from admin.views.report import AdminReportView from admin.views.report import AdminReportView
from admin.views.storage import StorageInfoView, StorageChangeView from admin.views.storage import StorageInfoView, StorageChangeView
from admin.views.supersign import DeveloperInfoView, DevicesInfoView, SuperSignBillView, SuperSignBillUserInfoView
from admin.views.user import UserInfoView, UserCertificationInfoView, ThirdWxAccountView from admin.views.user import UserInfoView, UserCertificationInfoView, ThirdWxAccountView
from xsign.views.admin.supersign import DeveloperInfoView, DevicesInfoView, SuperSignBillView, SuperSignBillUserInfoView
router = SimpleRouter(False) router = SimpleRouter(False)
router.register('app/info', AppInfoView) router.register('app/info', AppInfoView)

@ -5,8 +5,7 @@ from rest_framework import serializers
from api import models from api import models
from api.utils.serializer import AppReleaseSerializer, UserInfoSerializer, AppsSerializer, StorageSerializer, \ from api.utils.serializer import AppReleaseSerializer, UserInfoSerializer, AppsSerializer, StorageSerializer, \
DeveloperSerializer, SuperSignUsedSerializer, ThirdWxSerializer, DomainNameSerializer, BillInfoSerializer, \ ThirdWxSerializer, DomainNameSerializer, AppReportSerializer
AppReportSerializer
from common.base.baseutils import get_choices_dict, get_choices_name_from_key from common.base.baseutils import get_choices_dict, get_choices_name_from_key
from common.utils.storage import Storage from common.utils.storage import Storage
@ -163,40 +162,6 @@ class AdminStorageSerializer(StorageSerializer):
used_id = serializers.IntegerField(source="user_id.pk") used_id = serializers.IntegerField(source="user_id.pk")
class AdminDeveloperSerializer(DeveloperSerializer):
class Meta:
model = models.AppIOSDeveloperInfo
# depth = 1
exclude = ["p8key", ]
auth_type_choices = serializers.SerializerMethodField()
def get_auth_type_choices(self, obj):
return get_choices_dict(obj.auth_type_choices)
status_choices = serializers.SerializerMethodField()
def get_status_choices(self, obj):
return get_choices_dict(obj.status_choices)
class AdminSuperSignUsedSerializer(SuperSignUsedSerializer):
class Meta:
model = models.APPSuperSignUsedInfo
fields = ["created_time", "device_udid", "device_name", "developer_id", "bundle_id", "bundle_name", "app_id",
"id", "user_id", "short", "developer_pk"]
app_id = serializers.IntegerField(source="app_id.pk")
user_id = serializers.IntegerField(source="user_id.pk")
short = serializers.CharField(source="app_id.short")
developer_status = serializers.CharField(source="developerid.get_status_display")
developer_id = serializers.CharField(source="developerid.issuer_id")
developer_description = serializers.CharField(source="developerid.description")
developer_pk = serializers.IntegerField(source="developerid.pk")
class AdminOrdersSerializer(serializers.ModelSerializer): class AdminOrdersSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = models.Order model = models.Order
@ -265,19 +230,6 @@ class AdminDomainNameSerializer(DomainNameSerializer):
return get_choices_dict(obj.domain_type_choices) return get_choices_dict(obj.domain_type_choices)
class AdminBillInfoSerializer(BillInfoSerializer):
class Meta:
model = models.IosDeveloperPublicPoolBill
fields = '__all__'
read_only_fields = ["id", "user_id", "to_user_id", "action", "number", "app_info", "udid",
"udid_sync_info", "app_id", "remote_addr"]
action_choices = serializers.SerializerMethodField()
def get_action_choices(self, obj):
return get_choices_dict(obj.action_choices)
class AdminAppReportSerializer(AppReportSerializer): class AdminAppReportSerializer(AppReportSerializer):
class Meta: class Meta:
model = models.AppReportInfo model = models.AppReportInfo

@ -5,7 +5,6 @@
# date: 2022/1/6 # date: 2022/1/6
from rest_framework.pagination import PageNumberPagination from rest_framework.pagination import PageNumberPagination
from rest_framework.response import Response
from rest_framework.viewsets import ModelViewSet from rest_framework.viewsets import ModelViewSet
@ -33,20 +32,3 @@ class BaseModelSet(ModelViewSet):
def update(self, request, *args, **kwargs): def update(self, request, *args, **kwargs):
data = super().update(request, *args, **kwargs).data data = super().update(request, *args, **kwargs).data
return ApiResponse(data=data) return ApiResponse(data=data)
class ApiResponse(Response):
def __init__(self, code=1000, msg='success', data=None, status=None, headers=None, content_type=None, **kwargs):
dic = {
'code': code,
'msg': msg
}
if data is not None:
dic['data'] = data
dic.update(kwargs)
self._data = data
# 对象来调用对象的绑定方法,会自动传值
super().__init__(data=dic, status=status, headers=headers, content_type=content_type)
# 类来调用对象的绑定方法,这个方法就是一个普通函数,有几个参数就要传几个参数
# Response.__init__(data=dic,status=status,headers=headers,content_type=content_type)

@ -11,12 +11,13 @@ from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.filters import OrderingFilter from rest_framework.filters import OrderingFilter
from admin.utils.serializer import AdminAppsSerializer, AdminAppReleaseSerializer from admin.utils.serializer import AdminAppsSerializer, AdminAppReleaseSerializer
from admin.utils.utils import AppsPageNumber, ApiResponse, BaseModelSet from admin.utils.utils import AppsPageNumber, BaseModelSet
from api.base_views import app_delete from api.base_views import app_delete
from api.models import AppReleaseInfo, Apps from api.models import AppReleaseInfo, Apps
from api.utils.TokenManager import verify_token
from common.core.auth import AdminTokenAuthentication from common.core.auth import AdminTokenAuthentication
from common.core.response import ApiResponse
from common.utils.caches import del_cache_response_by_short, get_download_url_by_cache from common.utils.caches import del_cache_response_by_short, get_download_url_by_cache
from common.utils.token import verify_token
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

@ -11,9 +11,10 @@ from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.filters import OrderingFilter from rest_framework.filters import OrderingFilter
from admin.utils.serializer import AdminDomainNameSerializer from admin.utils.serializer import AdminDomainNameSerializer
from admin.utils.utils import BaseModelSet, AppsPageNumber, ApiResponse from admin.utils.utils import BaseModelSet, AppsPageNumber
from api.models import UserDomainInfo from api.models import UserDomainInfo
from common.core.auth import AdminTokenAuthentication from common.core.auth import AdminTokenAuthentication
from common.core.response import ApiResponse
from common.utils.caches import reset_app_wx_easy_type from common.utils.caches import reset_app_wx_easy_type
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

@ -9,10 +9,10 @@ import logging
from django.contrib import auth from django.contrib import auth
from rest_framework.views import APIView from rest_framework.views import APIView
from admin.utils.utils import ApiResponse
from api.utils.serializer import UserInfoSerializer from api.utils.serializer import UserInfoSerializer
from api.utils.utils import get_captcha, valid_captcha, set_user_token from api.utils.utils import get_captcha, valid_captcha, set_user_token
from common.core.auth import ExpiringTokenAuthentication from common.core.auth import ExpiringTokenAuthentication
from common.core.response import ApiResponse
from common.core.sysconfig import Config from common.core.sysconfig import Config
from common.core.throttle import VisitRegister1Throttle, VisitRegister2Throttle from common.core.throttle import VisitRegister1Throttle, VisitRegister2Throttle
from common.utils.caches import login_auth_failed from common.utils.caches import login_auth_failed

@ -11,9 +11,10 @@ from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.filters import OrderingFilter from rest_framework.filters import OrderingFilter
from admin.utils.serializer import AdminOrdersSerializer from admin.utils.serializer import AdminOrdersSerializer
from admin.utils.utils import BaseModelSet, AppsPageNumber, ApiResponse from admin.utils.utils import BaseModelSet, AppsPageNumber
from api.models import UserInfo, Order from api.models import UserInfo, Order
from common.core.auth import AdminTokenAuthentication from common.core.auth import AdminTokenAuthentication
from common.core.response import ApiResponse
from common.utils.caches import update_order_info, admin_change_user_download_times from common.utils.caches import update_order_info, admin_change_user_download_times
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

@ -13,11 +13,12 @@ from rest_framework.filters import OrderingFilter
from rest_framework.views import APIView from rest_framework.views import APIView
from admin.utils.serializer import AdminStorageSerializer from admin.utils.serializer import AdminStorageSerializer
from admin.utils.utils import AppsPageNumber, BaseModelSet, ApiResponse from admin.utils.utils import AppsPageNumber, BaseModelSet
from api.base_views import storage_change from api.base_views import storage_change
from api.models import UserInfo, AppStorage from api.models import UserInfo, AppStorage
from common.base.baseutils import format_storage_selection from common.base.baseutils import format_storage_selection
from common.core.auth import AdminTokenAuthentication from common.core.auth import AdminTokenAuthentication
from common.core.response import ApiResponse
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

@ -12,10 +12,11 @@ from rest_framework.filters import OrderingFilter
from rest_framework.views import APIView from rest_framework.views import APIView
from admin.utils.serializer import AdminUserInfoSerializer, AdminUserCertificationSerializer, AdminThirdWxSerializer from admin.utils.serializer import AdminUserInfoSerializer, AdminUserCertificationSerializer, AdminThirdWxSerializer
from admin.utils.utils import AppsPageNumber, BaseModelSet, ApiResponse from admin.utils.utils import AppsPageNumber, BaseModelSet
from api.models import UserInfo, UserCertificationInfo, ThirdWeChatUserInfo from api.models import UserInfo, UserCertificationInfo, ThirdWeChatUserInfo
from common.base.baseutils import get_dict_from_filter_fields from common.base.baseutils import get_dict_from_filter_fields
from common.core.auth import AdminTokenAuthentication from common.core.auth import AdminTokenAuthentication
from common.core.response import ApiResponse
from common.core.sysconfig import Config from common.core.sysconfig import Config
from common.utils.caches import auth_user_download_times_gift from common.utils.caches import auth_user_download_times_gift
@ -46,12 +47,12 @@ class UserInfoView(APIView):
def put(self, request): def put(self, request):
data = request.data data = request.data
id = data.get("id", None) pk = data.get("id", None)
if not id: if not pk:
return ApiResponse(code=1003, msg="参数错误") return ApiResponse(code=1003, msg="参数错误")
user_obj = UserInfo.objects.filter(id=id).first() user_obj = UserInfo.objects.filter(id=pk).first()
if user_obj: if user_obj:
data['pk'] = id data['pk'] = pk
users_serializer = AdminUserInfoSerializer(user_obj, data=data, partial=True) users_serializer = AdminUserInfoSerializer(user_obj, data=data, partial=True)
if users_serializer.is_valid(): if users_serializer.is_valid():
users_serializer.save() users_serializer.save()

@ -9,16 +9,10 @@ admin.site.register(AppReleaseInfo)
admin.site.register(Token) admin.site.register(Token)
admin.site.register(VerifyName) admin.site.register(VerifyName)
admin.site.register(AppStorage) admin.site.register(AppStorage)
admin.site.register(AppUDID)
admin.site.register(AppIOSDeveloperInfo)
admin.site.register(APPSuperSignUsedInfo)
admin.site.register(APPToDeveloper)
admin.site.register(UDIDsyncDeveloper)
admin.site.register(Price) admin.site.register(Price)
admin.site.register(Order) admin.site.register(Order)
admin.site.register(DomainCnameInfo) admin.site.register(DomainCnameInfo)
admin.site.register(UserDomainInfo) admin.site.register(UserDomainInfo)
admin.site.register(UserAdDisplayInfo) admin.site.register(UserAdDisplayInfo)
admin.site.register(IosDeveloperPublicPoolBill)
admin.site.register(AppReportInfo) admin.site.register(AppReportInfo)
admin.site.register(RemoteClientInfo) admin.site.register(RemoteClientInfo)

@ -6,12 +6,12 @@
import logging import logging
from api.models import AppReleaseInfo, APPToDeveloper, UserInfo, AppScreenShot, AppStorage from api.models import AppReleaseInfo, UserInfo, AppScreenShot, AppStorage
from api.utils.app.supersignutils import IosUtils
from api.utils.response import BaseResponse from api.utils.response import BaseResponse
from api.utils.utils import delete_local_files, delete_app_screenshots_files, change_storage_and_change_head_img, \ from api.utils.utils import delete_local_files, delete_app_screenshots_files, change_storage_and_change_head_img, \
migrating_storage_data, clean_storage_data, check_storage_is_new_storage migrating_storage_data, clean_storage_data, check_storage_is_new_storage
from common.cache.state import MigrateStorageState from common.cache.state import MigrateStorageState
from common.core.singals import delete_app_signal
from common.utils.caches import del_cache_response_by_short, del_cache_by_delete_app, \ from common.utils.caches import del_cache_response_by_short, del_cache_by_delete_app, \
del_cache_storage del_cache_storage
from common.utils.storage import Storage from common.utils.storage import Storage
@ -31,10 +31,7 @@ def app_delete(app_obj):
res.msg = "数据迁移中" res.msg = "数据迁移中"
return res return res
count = APPToDeveloper.objects.filter(app_id=app_obj).count() delete_app_signal.send(None, app_pk=app_obj.pk)
if app_obj.issupersign or count > 0:
logger.info(f"app_id:{app_obj.app_id} is supersign ,delete this app need clean IOS developer")
IosUtils.clean_app_by_user_obj(app_obj)
storage = Storage(user_obj) storage = Storage(user_obj)
has_combo = app_obj.has_combo has_combo = app_obj.has_combo

@ -1,6 +1,7 @@
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from api.tasks import start_api_sever_do_clean from api.tasks import start_api_sever_do_clean
from xsign.utils.iproxy import clean_ip_proxy_infos
class Command(BaseCommand): class Command(BaseCommand):
@ -8,3 +9,4 @@ class Command(BaseCommand):
def handle(self, *args, **options): def handle(self, *args, **options):
start_api_sever_do_clean() start_api_sever_do_clean()
clean_ip_proxy_infos()

@ -3,10 +3,10 @@ from django.contrib.contenttypes.models import ContentType
from django.db import models from django.db import models
from django.db.models import Count from django.db.models import Count
from api.utils.TokenManager import generate_alphanumeric_token_of_length, generate_numeric_token_of_length
from common.base.baseutils import make_random_uuid from common.base.baseutils import make_random_uuid
######################################## 用户表 ######################################## ######################################## 用户表 ########################################
from common.base.daobase import AESCharField from common.base.daobase import AESCharField
from common.utils.token import generate_alphanumeric_token_of_length, generate_numeric_token_of_length
# Create your models here. # Create your models here.
@ -69,7 +69,7 @@ class UserInfo(AbstractUser):
class ThirdWeChatUserInfo(models.Model): class ThirdWeChatUserInfo(models.Model):
user_id = models.ForeignKey(to="UserInfo", verbose_name="用户ID", on_delete=models.CASCADE) user_id = models.ForeignKey(to=UserInfo, verbose_name="用户ID", on_delete=models.CASCADE)
openid = models.CharField(max_length=64, unique=True, verbose_name="普通用户的标识,对当前公众号唯一") openid = models.CharField(max_length=64, unique=True, verbose_name="普通用户的标识,对当前公众号唯一")
nickname = models.CharField(max_length=64, verbose_name="昵称", blank=True) nickname = models.CharField(max_length=64, verbose_name="昵称", blank=True)
sex = models.SmallIntegerField(default=0, verbose_name="性别", help_text="值为1时是男性,值为2时是女性,值为0时是未知") sex = models.SmallIntegerField(default=0, verbose_name="性别", help_text="值为1时是男性,值为2时是女性,值为0时是未知")
@ -122,7 +122,7 @@ class VerifyName(models.Model):
class Apps(models.Model): class Apps(models.Model):
app_id = models.CharField(max_length=64, unique=True, db_index=True) # ,唯一标识 app_id = models.CharField(max_length=64, unique=True, db_index=True) # ,唯一标识
user_id = models.ForeignKey(to="UserInfo", verbose_name="用户ID", on_delete=models.CASCADE) user_id = models.ForeignKey(to=UserInfo, verbose_name="用户ID", on_delete=models.CASCADE)
type_choices = ((0, 'android'), (1, 'ios')) type_choices = ((0, 'android'), (1, 'ios'))
type = models.SmallIntegerField(choices=type_choices, default=0, verbose_name="类型") type = models.SmallIntegerField(choices=type_choices, default=0, verbose_name="类型")
status_choices = ((0, '封禁'), (1, '正常'), (2, '违规')) status_choices = ((0, '封禁'), (1, '正常'), (2, '违规'))
@ -147,7 +147,6 @@ class Apps(models.Model):
supersign_limit_number = models.IntegerField(verbose_name="签名使用限额", default=0) supersign_limit_number = models.IntegerField(verbose_name="签名使用限额", default=0)
wxredirect = models.BooleanField(verbose_name="微信内第三方链接自动跳转", default=True) wxredirect = models.BooleanField(verbose_name="微信内第三方链接自动跳转", default=True)
wxeasytype = models.BooleanField(verbose_name="微信内简易模式,避免微信封停", default=True) wxeasytype = models.BooleanField(verbose_name="微信内简易模式,避免微信封停", default=True)
# domain_name = models.CharField(verbose_name="专属访问域名", blank=True, null=True, max_length=64)
description = models.TextField('描述', blank=True, null=True, default=None, ) description = models.TextField('描述', blank=True, null=True, default=None, )
updated_time = models.DateTimeField(auto_now=True, verbose_name="更新时间") updated_time = models.DateTimeField(auto_now=True, verbose_name="更新时间")
@ -161,7 +160,7 @@ class Apps(models.Model):
class AppScreenShot(models.Model): class AppScreenShot(models.Model):
app_id = models.ForeignKey(to="Apps", on_delete=models.CASCADE, verbose_name="属于哪个APP") app_id = models.ForeignKey(to=Apps, on_delete=models.CASCADE, verbose_name="属于哪个APP")
screenshot_url = models.CharField(max_length=128, blank=True, verbose_name="应用截图URL") screenshot_url = models.CharField(max_length=128, blank=True, verbose_name="应用截图URL")
created_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间") created_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
@ -177,7 +176,7 @@ class AppScreenShot(models.Model):
class AppReleaseInfo(models.Model): class AppReleaseInfo(models.Model):
is_master = models.BooleanField(verbose_name="是否master版本", default=True) is_master = models.BooleanField(verbose_name="是否master版本", default=True)
release_id = models.CharField(max_length=64, unique=True, verbose_name="release 版本id", db_index=True) release_id = models.CharField(max_length=64, unique=True, verbose_name="release 版本id", db_index=True)
app_id = models.ForeignKey(to="Apps", on_delete=models.CASCADE, verbose_name="属于哪个APP") app_id = models.ForeignKey(to=Apps, on_delete=models.CASCADE, verbose_name="属于哪个APP")
build_version = models.CharField(max_length=64, verbose_name="build版本", blank=True) build_version = models.CharField(max_length=64, verbose_name="build版本", blank=True)
app_version = models.CharField(max_length=64, verbose_name="app版本", blank=True) app_version = models.CharField(max_length=64, verbose_name="app版本", blank=True)
release_choices = ((0, 'android'), (1, 'adhoc'), (2, 'Inhouse'), (3, 'unknown')) release_choices = ((0, 'android'), (1, 'adhoc'), (2, 'Inhouse'), (3, 'unknown'))
@ -200,7 +199,7 @@ class AppReleaseInfo(models.Model):
class AppStorage(models.Model): class AppStorage(models.Model):
user_id = models.ForeignKey(to="UserInfo", verbose_name="用户ID", on_delete=models.CASCADE) user_id = models.ForeignKey(to=UserInfo, verbose_name="用户ID", on_delete=models.CASCADE)
name = models.CharField(max_length=64, blank=True, null=True, verbose_name="存储名字") name = models.CharField(max_length=64, blank=True, null=True, verbose_name="存储名字")
storage_choices = ((0, '本地存储'), (1, '七牛云存储'), (2, '阿里云存储'), (3, '默认存储')) storage_choices = ((0, '本地存储'), (1, '七牛云存储'), (2, '阿里云存储'), (3, '默认存储'))
storage_type = models.SmallIntegerField(choices=storage_choices, default=3, verbose_name="存储类型") storage_type = models.SmallIntegerField(choices=storage_choices, default=3, verbose_name="存储类型")
@ -243,153 +242,6 @@ class AppStorage(models.Model):
return "%s %s" % (self.user_id.get_username(), self.name) return "%s %s" % (self.user_id.get_username(), self.name)
class AppUDID(models.Model):
app_id = models.ForeignKey(to="Apps", on_delete=models.CASCADE, verbose_name="属于哪个APP")
udid = models.ForeignKey(to="UDIDsyncDeveloper", verbose_name="udid唯一标识", on_delete=models.CASCADE)
product = models.CharField(max_length=64, verbose_name="产品", blank=True, null=True, )
serial = models.CharField(max_length=64, verbose_name="序列号", blank=True, null=True, )
version = models.CharField(max_length=64, verbose_name="型号", blank=True, null=True, )
imei = models.CharField(max_length=64, verbose_name="型号", blank=True, null=True, )
iccid = models.CharField(max_length=64, verbose_name="型号", blank=True, null=True, )
created_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
updated_time = models.DateTimeField(auto_now=True, verbose_name="更新时间")
# is_signed = models.BooleanField(verbose_name="是否完成签名打包", default=False)
# is_download = models.BooleanField(verbose_name="描述文件是否已经下载", default=False)
sign_status_choices = ((0, '新设备入库准备'), (1, '设备ID已经注册'), (2, 'bundelid已经注册'), (3, '描述文件已经下载'), (4, '已经完成签名打包'))
sign_status = models.SmallIntegerField(choices=sign_status_choices, default=0, verbose_name="签名状态")
class Meta:
verbose_name = '设备详情'
verbose_name_plural = "设备详情"
unique_together = ('app_id', 'udid',)
def __str__(self):
return "%s-%s" % (self.app_id.name, self.udid)
class AppIOSDeveloperInfo(models.Model):
user_id = models.ForeignKey(to="UserInfo", verbose_name="用户ID", on_delete=models.CASCADE)
issuer_id = models.CharField(max_length=64, null=False, verbose_name="标识创建认证令牌的发放者")
private_key_id = models.CharField(max_length=64, null=False, verbose_name="密钥 ID")
p8key = AESCharField(max_length=512, null=False, verbose_name="p8key")
certid = models.CharField(max_length=64, blank=True, verbose_name="超级签名自动创建证书ID", null=True)
usable_number = models.IntegerField(verbose_name="可使用设备数", default=100)
app_limit_number = models.IntegerField(verbose_name="可分配应用数,最大160", default=100)
created_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
updated_time = models.DateTimeField(auto_now=True, verbose_name="更新时间")
cert_expire_time = models.DateTimeField(blank=True, null=True, verbose_name="证书过期时间")
description = models.TextField('备注', blank=True, null=True, default='')
auth_type_choices = ((0, 'p8key认证'),)
auth_type = models.SmallIntegerField(choices=auth_type_choices, default=0, verbose_name="认证类型")
# 协议待同意和维护中:代表只读,不可创建和注册新设备号
status_choices = ((-1, '疑似被封'), (0, '未激活'), (1, '已激活'), (2, '协议待同意'), (3, '维护中'), (4, '证书过期'), (5, '状态异常'))
status = models.SmallIntegerField(choices=status_choices, verbose_name="账户状态", default=0)
clean_status = models.BooleanField(verbose_name="清理是否同时禁用设备ID", default=False)
auto_check = models.BooleanField(verbose_name="是否自动检测开发者状态", default=False)
class Meta:
verbose_name = '苹果开发者账户'
verbose_name_plural = "苹果开发者账户"
unique_together = (('user_id', 'issuer_id'),)
def save(self, *args, **kwargs):
if self.usable_number > 100:
self.usable_number = 100
elif self.usable_number < 0:
self.usable_number = 0
if self.app_limit_number > 160:
self.app_limit_number = 160
elif self.app_limit_number < 0:
self.app_limit_number = 0
return super(AppIOSDeveloperInfo, self).save(*args, **kwargs)
def __str__(self):
return "%s-%s" % (self.user_id, self.issuer_id)
class APPSuperSignUsedInfo(models.Model):
user_id = models.ForeignKey(to="UserInfo", verbose_name="用户ID", on_delete=models.CASCADE)
app_id = models.ForeignKey(to="Apps", on_delete=models.CASCADE, verbose_name="属于哪个APP")
udid = models.ForeignKey(to="AppUDID", on_delete=models.CASCADE, verbose_name="所消耗的udid")
developerid = models.ForeignKey(to="AppIOSDeveloperInfo", on_delete=models.CASCADE, verbose_name="所使用苹果开发者账户")
created_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
updated_time = models.DateTimeField(auto_now=True, verbose_name="更新时间")
class Meta:
verbose_name = '设备使用统计'
verbose_name_plural = "设备使用统计"
def __str__(self):
return "%s-%s-%s" % (self.user_id, self.app_id, self.udid)
class APPToDeveloper(models.Model):
app_id = models.ForeignKey(to="Apps", on_delete=models.CASCADE, verbose_name="属于哪个APP")
developerid = models.ForeignKey(to="AppIOSDeveloperInfo", on_delete=models.CASCADE, verbose_name="所使用苹果开发者账户")
binary_file = models.CharField(max_length=128, blank=True, verbose_name="签名包名称", null=True, unique=True)
release_file = models.CharField(max_length=128, blank=True, verbose_name="源包名称", null=True)
updated_time = models.DateTimeField(auto_now=True, verbose_name="更新时间")
class Meta:
verbose_name = '应用开发者绑定'
verbose_name_plural = "应用开发者绑定"
def __str__(self):
return "%s-%s-%s" % (self.developerid, self.app_id, self.binary_file)
class UDIDsyncDeveloper(models.Model):
developerid = models.ForeignKey(to="AppIOSDeveloperInfo", on_delete=models.CASCADE, verbose_name="所使用苹果开发者账户")
udid = models.CharField(max_length=64, verbose_name="udid唯一标识", db_index=True)
product = models.CharField(max_length=64, verbose_name="产品", blank=True, null=True, )
serial = models.CharField(max_length=64, verbose_name="序列号", blank=True, null=True, )
version = models.CharField(max_length=64, verbose_name="型号", blank=True, null=True, )
created_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间"),
status = models.BooleanField(verbose_name="设备在开发者平台状态", default=False)
class Meta:
verbose_name = 'iOS开发平台同步设备信息'
verbose_name_plural = "iOS开发平台同步设备信息"
unique_together = ('udid', 'developerid',)
def __str__(self):
return "%s-%s-%s" % (self.product, self.udid, self.developerid)
class DeveloperAppID(models.Model):
aid = models.CharField(max_length=64, null=False) # ,apple APP 唯一标识
profile_id = models.CharField(max_length=64, null=True, blank=True) # ,profile_id 唯一标识
developerid = models.ForeignKey(to="AppIOSDeveloperInfo", on_delete=models.CASCADE, verbose_name="所使用苹果开发者账户")
app_id = models.ForeignKey(to="Apps", on_delete=models.CASCADE, verbose_name="属于哪个APP")
class Meta:
verbose_name = '超级签APP id'
verbose_name_plural = "超级签APP id"
unique_together = ('aid', 'developerid', 'app_id')
def __str__(self):
return "%s-%s-%s" % (self.aid, self.app_id, self.developerid)
class DeveloperDevicesID(models.Model):
did = models.CharField(max_length=64, null=False) # ,apple 设备唯一标识
udid = models.ForeignKey(to="UDIDsyncDeveloper", on_delete=models.CASCADE, verbose_name="所消耗的udid")
developerid = models.ForeignKey(to="AppIOSDeveloperInfo", on_delete=models.CASCADE, verbose_name="所使用苹果开发者账户")
app_id = models.ForeignKey(to="Apps", on_delete=models.CASCADE, verbose_name="属于哪个APP")
class Meta:
verbose_name = '超级签Devices id'
verbose_name_plural = "超级签Devices id"
unique_together = ('did', 'developerid', 'app_id')
def __str__(self):
return "%s-%s-%s-%s" % (self.id, self.app_id, self.developerid, self.udid)
######################################## 订单表 ######################################## ######################################## 订单表 ########################################
class Order(models.Model): class Order(models.Model):
@ -399,7 +251,7 @@ class Order(models.Model):
payment_number = models.CharField(max_length=128, verbose_name="支付第3方订单号", null=True, blank=True) payment_number = models.CharField(max_length=128, verbose_name="支付第3方订单号", null=True, blank=True)
payment_name = models.CharField(max_length=128, verbose_name="支付商家名称", null=True, blank=True) payment_name = models.CharField(max_length=128, verbose_name="支付商家名称", null=True, blank=True)
order_number = models.CharField(max_length=128, verbose_name="订单号", unique=True) # 考虑到订单合并支付的问题 order_number = models.CharField(max_length=128, verbose_name="订单号", unique=True) # 考虑到订单合并支付的问题
user_id = models.ForeignKey("UserInfo", on_delete=models.CASCADE) user_id = models.ForeignKey(UserInfo, on_delete=models.CASCADE)
actual_amount = models.BigIntegerField(verbose_name="实付金额,单位分") actual_amount = models.BigIntegerField(verbose_name="实付金额,单位分")
actual_download_times = models.BigIntegerField(verbose_name="实际购买的数量", default=0) actual_download_times = models.BigIntegerField(verbose_name="实际购买的数量", default=0)
actual_download_gift_times = models.BigIntegerField(verbose_name="实际赠送的数量", default=0) actual_download_gift_times = models.BigIntegerField(verbose_name="实际赠送的数量", default=0)
@ -441,7 +293,7 @@ class Price(models.Model):
class UserCertificationInfo(models.Model): class UserCertificationInfo(models.Model):
user_id = models.OneToOneField(to="UserInfo", verbose_name="用户ID", on_delete=models.CASCADE, user_id = models.OneToOneField(to=UserInfo, verbose_name="用户ID", on_delete=models.CASCADE,
related_name='certification') related_name='certification')
name = models.CharField(max_length=128, null=False, verbose_name="真实姓名") name = models.CharField(max_length=128, null=False, verbose_name="真实姓名")
card = models.CharField(max_length=128, null=False, verbose_name="身份证号码", unique=True) card = models.CharField(max_length=128, null=False, verbose_name="身份证号码", unique=True)
@ -463,7 +315,7 @@ class UserCertificationInfo(models.Model):
class CertificationInfo(models.Model): class CertificationInfo(models.Model):
user_id = models.ForeignKey(to="UserInfo", verbose_name="用户ID", on_delete=models.CASCADE) user_id = models.ForeignKey(to=UserInfo, verbose_name="用户ID", on_delete=models.CASCADE)
certification_url = models.CharField(max_length=128, blank=True, verbose_name="认证URL") certification_url = models.CharField(max_length=128, blank=True, verbose_name="认证URL")
type_choices = ((0, '未知'), (1, '国徽面照片'), (2, '人像面照片'), (3, '手持身份证照片')) type_choices = ((0, '未知'), (1, '国徽面照片'), (2, '人像面照片'), (3, '手持身份证照片'))
type = models.SmallIntegerField(choices=type_choices, default=0, verbose_name="图像类型") type = models.SmallIntegerField(choices=type_choices, default=0, verbose_name="图像类型")
@ -500,9 +352,9 @@ class DomainCnameInfo(models.Model):
class UserDomainInfo(models.Model): class UserDomainInfo(models.Model):
user_id = models.ForeignKey(to="UserInfo", verbose_name="用户ID", on_delete=models.CASCADE) user_id = models.ForeignKey(to=UserInfo, verbose_name="用户ID", on_delete=models.CASCADE)
app_id = models.ForeignKey(to="Apps", on_delete=models.CASCADE, verbose_name="APP专属域名", null=True, blank=True) app_id = models.ForeignKey(to=Apps, on_delete=models.CASCADE, verbose_name="APP专属域名", null=True, blank=True)
cname_id = models.ForeignKey(to="DomainCnameInfo", verbose_name="cname解析ID", on_delete=models.CASCADE) cname_id = models.ForeignKey(to=DomainCnameInfo, verbose_name="cname解析ID", on_delete=models.CASCADE)
domain_name = models.CharField(verbose_name="下载页面域名", db_index=True, max_length=64, null=False, blank=False) domain_name = models.CharField(verbose_name="下载页面域名", db_index=True, max_length=64, null=False, blank=False)
is_https = models.BooleanField(default=False, verbose_name="是否支持HTTPS") is_https = models.BooleanField(default=False, verbose_name="是否支持HTTPS")
weight = models.IntegerField(verbose_name="下载页域名展示权重", default=10) weight = models.IntegerField(verbose_name="下载页域名展示权重", default=10)
@ -527,7 +379,7 @@ class UserDomainInfo(models.Model):
class UserAdDisplayInfo(models.Model): class UserAdDisplayInfo(models.Model):
user_id = models.ForeignKey(to="UserInfo", verbose_name="用户ID", on_delete=models.CASCADE) user_id = models.ForeignKey(to=UserInfo, verbose_name="用户ID", on_delete=models.CASCADE)
ad_name = models.CharField(verbose_name="广告名称", max_length=256, null=False, blank=False) ad_name = models.CharField(verbose_name="广告名称", max_length=256, null=False, blank=False)
ad_uri = models.CharField(verbose_name="广告跳转地址", max_length=256, null=False, blank=False) ad_uri = models.CharField(verbose_name="广告跳转地址", max_length=256, null=False, blank=False)
ad_pic = models.CharField(verbose_name="广告图片地址", max_length=256, null=False, blank=False, help_text="像素最高80px", ) ad_pic = models.CharField(verbose_name="广告图片地址", max_length=256, null=False, blank=False, help_text="像素最高80px", )
@ -546,52 +398,6 @@ class UserAdDisplayInfo(models.Model):
return "%s-%s-%s" % (self.user_id, self.description, self.is_enable) return "%s-%s-%s" % (self.user_id, self.description, self.is_enable)
class IosDeveloperPublicPoolBill(models.Model):
user_id = models.ForeignKey(to="UserInfo", verbose_name="用户ID", on_delete=models.CASCADE)
number = models.IntegerField(verbose_name="消耗次数", default=1)
app_info = models.JSONField(max_length=256, verbose_name="属于哪个APP", null=True, blank=True)
udid = models.CharField(max_length=64, verbose_name="设备udid", null=True, blank=True)
product = models.CharField(max_length=64, verbose_name="设备udid", null=True, blank=True)
version = models.CharField(max_length=64, verbose_name="设备udid", null=True, blank=True)
developer_info = models.JSONField(max_length=256, verbose_name="开发者信息", null=True, blank=True)
udid_sync_info = models.ForeignKey(to="UDIDsyncDeveloper", on_delete=models.SET_NULL, verbose_name="关联同步设备信息",
null=True, blank=True)
app_id = models.ForeignKey(to="Apps", on_delete=models.SET_NULL, verbose_name="属于哪个APP", null=True, blank=True)
description = models.CharField(verbose_name="操作描述", max_length=128, default='', blank=True)
remote_addr = models.GenericIPAddressField(verbose_name="远程IP地址")
created_time = models.DateTimeField(auto_now_add=True, verbose_name="添加时间")
class Meta:
verbose_name = '设备消耗账单'
verbose_name_plural = "设备消耗账单"
def __str__(self):
return "%s-%s" % (self.user_id, self.description)
class IosDeveloperBill(models.Model):
user_id = models.ForeignKey(to="UserInfo", verbose_name="用户ID", on_delete=models.CASCADE,
related_name='org_user_id')
to_user_id = models.ForeignKey(to="UserInfo", verbose_name="用户ID", on_delete=models.CASCADE,
related_name='to_user_id', null=True, blank=True)
status_choices = ((0, '失效'), (1, '已撤回'), (2, '成功'))
status = models.SmallIntegerField(choices=status_choices, default=0, verbose_name="状态",
help_text="0 失效 1 撤回 2 转账")
number = models.IntegerField(verbose_name="设备数量", default=1)
description = models.CharField(verbose_name="操作描述", max_length=128, default='', blank=True)
remote_addr = models.GenericIPAddressField(verbose_name="远程IP地址")
created_time = models.DateTimeField(auto_now_add=True, verbose_name="添加时间")
updated_time = models.DateTimeField(auto_now=True, verbose_name="更新时间")
class Meta:
verbose_name = '设备划转账单'
verbose_name_plural = "设备划转账单"
def __str__(self):
return "%s-%s%s" % (self.user_id, self.to_user_id, self.description)
class RemoteClientInfo(models.Model): class RemoteClientInfo(models.Model):
remote_addr = models.GenericIPAddressField(verbose_name="远程IP地址") remote_addr = models.GenericIPAddressField(verbose_name="远程IP地址")
user_agent = models.CharField(verbose_name="客户端agent", max_length=512) user_agent = models.CharField(verbose_name="客户端agent", max_length=512)
@ -610,7 +416,7 @@ class RemoteClientInfo(models.Model):
class AppReportInfo(models.Model): class AppReportInfo(models.Model):
app_id = models.ForeignKey(to="Apps", on_delete=models.SET_NULL, verbose_name="应用信息", app_id = models.ForeignKey(to=Apps, on_delete=models.SET_NULL, verbose_name="应用信息",
null=True, blank=True) null=True, blank=True)
app_name = models.CharField(max_length=32, blank=True, null=True, verbose_name="应用名称") app_name = models.CharField(max_length=32, blank=True, null=True, verbose_name="应用名称")
bundle_id = models.CharField(max_length=64, blank=True, verbose_name="bundle id") bundle_id = models.CharField(max_length=64, blank=True, verbose_name="bundle id")
@ -633,22 +439,6 @@ class AppReportInfo(models.Model):
return "%s-%s" % (self.app_name, self.report_reason) return "%s-%s" % (self.app_name, self.report_reason)
class AppleDeveloperToAppUse(models.Model):
app_id = models.ForeignKey(to='Apps', on_delete=models.CASCADE)
developerid = models.ForeignKey(to='AppIOSDeveloperInfo', on_delete=models.CASCADE)
usable_number = models.IntegerField(verbose_name="可使用设备数", default=100)
description = models.CharField(verbose_name="备注", max_length=256, default='', blank=True)
created_time = models.DateTimeField(auto_now_add=True, verbose_name="添加时间")
class Meta:
verbose_name = '开发者专属于应用'
verbose_name_plural = "开发者专属于应用"
unique_together = ('app_id', 'developerid')
def __str__(self):
return "%s-%s" % (self.app_id.name, self.developerid.issuer_id)
class SystemConfig(models.Model): class SystemConfig(models.Model):
key = models.CharField(max_length=128, unique=True, verbose_name="配置名称") key = models.CharField(max_length=128, unique=True, verbose_name="配置名称")
value = models.TextField(max_length=10240, verbose_name="配置值") value = models.TextField(max_length=10240, verbose_name="配置值")

@ -6,83 +6,24 @@
import logging import logging
from celery import shared_task from api.utils.ctasks import sync_download_times, auto_clean_upload_tmp_file
from django.core.cache import cache
from api.models import Apps, DeveloperAppID
from api.utils.app.supersignutils import IosUtils, resign_by_app_id_and_developer
from api.utils.crontab.ctasks import sync_download_times, auto_clean_upload_tmp_file, auto_delete_ios_mobile_tmp_file, \
auto_check_ios_developer_active
from api.utils.crontab.iproxy import get_best_proxy_ips, clean_ip_proxy_infos
from api.views.login import get_login_type from api.views.login import get_login_type
from common.cache.state import MigrateStorageState
from common.core.sysconfig import Config, invalid_config_cache from common.core.sysconfig import Config, invalid_config_cache
from common.libs.geetest.geetest_utils import check_bypass_status from common.libs.geetest.geetest_utils import check_bypass_status
from common.libs.mp.wechat import sync_wx_access_token from common.libs.mp.wechat import sync_wx_access_token
from common.utils.storage import get_local_storage from common.utils.storage import get_local_storage
from fir_ser.celery import app from fir_ser.celery import app
from xsign.utils.ctasks import auto_delete_ios_mobile_tmp_file
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@shared_task
def run_sign_task(format_udid_info, short, client_ip):
app_obj = Apps.objects.filter(short=short).first()
if MigrateStorageState(app_obj.user_id.uid).get_state():
msg = "数据迁移中,无法处理该操作"
return msg
ios_obj = IosUtils(format_udid_info, app_obj.user_id, app_obj)
status, msg = ios_obj.sign_ipa(client_ip)
if not status:
code = msg.get("code", -1)
if code == 0:
msg = ""
elif code == 1005:
msg = "签名余额不足"
elif code == 1002:
msg = "维护中"
elif code == 1003:
msg = "应用余额不足"
elif code in [1004, 1001, 1009]:
msg = msg.get('msg', '未知错误')
else:
msg = '系统内部错误,请稍后再试或联系管理员'
else:
msg = ""
return msg
def run_resign_task(app_id, need_download_profile=True, force=True):
app_obj = Apps.objects.filter(pk=app_id).first()
if app_obj.issupersign and app_obj.user_id.supersign_active:
with cache.lock("%s_%s" % ('task_resign', app_obj.app_id), timeout=60 * 60):
task_list = []
for developer_app_id_obj in DeveloperAppID.objects.filter(app_id=app_obj).all():
c_task = run_resign_task_do.apply_async((app_id, developer_app_id_obj.developerid.pk,
developer_app_id_obj.aid, need_download_profile, force))
task_list.append(c_task)
for c_task in task_list:
msg = c_task.get(propagate=False)
logger.info(f"app {app_obj} run_resign_task msg:{msg}")
if c_task.successful():
c_task.forget()
return True
@shared_task
def run_resign_task_do(app_id, developer_id, developer_app_id, need_download_profile=True, force=True):
return resign_by_app_id_and_developer(app_id, developer_id, developer_app_id, need_download_profile, force)
@app.task @app.task
def start_api_sever_do_clean(): def start_api_sever_do_clean():
# 启动服务的时候,同时执行下面操作,主要是修改配置存储的时候,需要执行清理,否则会出问题,如果不修改,则无需执行 # 启动服务的时候,同时执行下面操作,主要是修改配置存储的时候,需要执行清理,否则会出问题,如果不修改,则无需执行
logger.info("clean local storage cache") logger.info("clean local storage cache")
get_local_storage(clean_cache=True) get_local_storage(clean_cache=True)
check_bypass_status() check_bypass_status()
clean_ip_proxy_infos()
invalid_config_cache() invalid_config_cache()
@ -112,17 +53,7 @@ def auto_delete_tmp_file_job():
auto_delete_ios_mobile_tmp_file() auto_delete_ios_mobile_tmp_file()
@app.task
def auto_check_ios_developer_active_job():
auto_check_ios_developer_active()
@app.task @app.task
def sync_wx_access_token_job(): def sync_wx_access_token_job():
if get_login_type().get('third', '').get('wxp'): if get_login_type().get('third', '').get('wxp'):
sync_wx_access_token() sync_wx_access_token()
@app.task
def get_best_proxy_ips_job():
get_best_proxy_ips()

@ -23,11 +23,8 @@ from api.views.login import LoginView, UserInfoView, RegistView, AuthorizationVi
UserApiTokenView, CertificationView, ChangeInfoView, WeChatLoginView, WeChatLoginCheckView UserApiTokenView, CertificationView, ChangeInfoView, WeChatLoginView, WeChatLoginCheckView
from api.views.logout import LogoutView from api.views.logout import LogoutView
from api.views.order import PriceView, OrderView, PaySuccess from api.views.order import PriceView, OrderView, PaySuccess
from api.views.receiveudids import IosUDIDView, TaskView
from api.views.report import ReportView from api.views.report import ReportView
from api.views.storage import StorageView, CleanStorageView from api.views.storage import StorageView, CleanStorageView
from api.views.supersign import DeveloperView, SuperSignUsedView, AppUDIDUsedView, SuperSignCertView, \
DeviceUsedBillView, DeveloperDeviceView, DeviceUsedRankInfoView, AppleDeveloperBindAppsView, DeviceTransferBillView
from api.views.thirdlogin import ValidWxChatToken, ThirdWxAccount from api.views.thirdlogin import ValidWxChatToken, ThirdWxAccount
from api.views.uploads import AppAnalyseView, UploadView from api.views.uploads import AppAnalyseView, UploadView
@ -51,21 +48,10 @@ urlpatterns = [
re_path("^userinfo", UserInfoView.as_view()), re_path("^userinfo", UserInfoView.as_view()),
re_path("^token", UserApiTokenView.as_view()), re_path("^token", UserApiTokenView.as_view()),
re_path(r"^short/(?P<short>\w+)$", ShortDownloadView.as_view()), re_path(r"^short/(?P<short>\w+)$", ShortDownloadView.as_view()),
re_path(r"^udid/(?P<short>\w+)$", IosUDIDView.as_view()),
re_path(r"^task/(?P<short>\w+)$", TaskView.as_view()),
re_path("^analyse$", AppAnalyseView.as_view()), re_path("^analyse$", AppAnalyseView.as_view()),
re_path("^advert$", UserAdInfoView.as_view()), re_path("^advert$", UserAdInfoView.as_view()),
re_path("^report$", ReportView.as_view()), re_path("^report$", ReportView.as_view()),
re_path("^qrcode$", AppsQrcodeShowView.as_view()), re_path("^qrcode$", AppsQrcodeShowView.as_view()),
re_path("^supersign/developer$", DeveloperView.as_view()),
re_path("^supersign/devices$", SuperSignUsedView.as_view()),
re_path("^supersign/udid$", AppUDIDUsedView.as_view()),
re_path("^supersign/udevices$", DeveloperDeviceView.as_view()),
re_path("^supersign/cert$", SuperSignCertView.as_view()),
re_path("^supersign/bill$", DeviceUsedBillView.as_view()),
re_path("^supersign/devicebill$", DeviceTransferBillView.as_view()),
re_path("^supersign/rank$", DeviceUsedRankInfoView.as_view()),
re_path("^supersign/bind$", AppleDeveloperBindAppsView.as_view()),
re_path("^package_prices$", PriceView.as_view()), re_path("^package_prices$", PriceView.as_view()),
re_path("^orders$", OrderView.as_view()), re_path("^orders$", OrderView.as_view()),
re_path("^certification$", CertificationView.as_view()), re_path("^certification$", CertificationView.as_view()),

@ -1,387 +0,0 @@
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# project: 12月
# author: NinEveN
# date: 2019/12/19
import io
import os
import plistlib
import re
import zipfile
import qrcode
import requests
from PIL import Image
from androguard.core.bytecodes import apk
def bytes2human(n):
symbols = ('K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y')
prefix = {}
for i, s in enumerate(symbols):
prefix[s] = 1 << (i + 1) * 10
for s in reversed(symbols):
if n >= prefix[s]:
value = float(n) / prefix[s]
return '%.1f%s' % (value, s)
return '%sB' % n
class AppInfo(object):
def __init__(self, app_path):
self.app_path = app_path
self.result = {}
def make_app_png(self, png_path, filename):
zf = zipfile.ZipFile(self.app_path)
name_list = zf.namelist()
if self.app_path.endswith("apk"):
# pattern = re.compile(r'res/drawable[^/]*/app_icon.png')
iconfile = apk.APK(self.app_path).get_app_icon()
size = 999
elif self.app_path.endswith("ipa"):
size = 0
pattern = re.compile(r'Payload/[^/]*.app/AppIcon[^/]*[^(ipad)].png')
iconfile = ""
for path in name_list:
m = pattern.match(path)
if m is not None:
filepath = m.group()
fsize = len(zf.read(filepath))
if size == 0:
iconfile = filepath
if size < fsize:
size = fsize
iconfile = filepath
else:
raise Exception("File type error")
if size == 0:
raise Exception("File size error")
if not os.path.isdir(png_path):
os.makedirs(png_path)
with open(os.path.join(png_path, "%s.png" % filename), 'wb') as f:
f.write(zf.read(iconfile))
return "%s.png" % filename
def get_app_data(self):
if self.app_path.endswith("apk"):
return self.__get_android_data()
elif self.app_path.endswith("ipa"):
return self.__get_ios_data()
else:
raise Exception("File type error")
def __get_ios_info_path(self, ipaobj):
infopath_re = re.compile(r'Payload/[^/]*.app/Info.plist')
for i in ipaobj.namelist():
m = infopath_re.match(i)
if m is not None:
return m.group()
def __get_android_data(self):
try:
apkobj = apk.APK(self.app_path)
except Exception as err:
raise err
else:
if apkobj.is_valid_APK():
versioncode = apkobj.get_androidversion_code()
bundle_id = apkobj.get_package()
labelname = apkobj.get_app_name()
version = apkobj.get_androidversion_name()
sdk_version = apkobj.get_min_sdk_version()
self.result = {
"labelname": labelname,
"bundle_id": bundle_id,
"versioncode": versioncode,
"version": version,
"miniOSversion": sdk_version,
"type": "android",
}
return self.result
def __get_ios_data(self):
if zipfile.is_zipfile(self.app_path):
ipaobj = zipfile.ZipFile(self.app_path)
info_path = self.__get_ios_info_path(ipaobj)
if info_path:
plist_data = ipaobj.read(info_path)
plist_root = plistlib.loads(plist_data)
labelname = plist_root['CFBundleDisplayName']
versioncode = plist_root['CFBundleVersion']
bundle_id = plist_root['CFBundleIdentifier']
version = plist_root['CFBundleShortVersionString']
miniOSversion = plist_root["MinimumOSVersion"]
self.result = {
"labelname": labelname,
"bundle_id": bundle_id,
"versioncode": versioncode,
"version": version,
"miniOSversion": miniOSversion,
"type": "ios",
}
release_type_info = self.__get_ios_release_type(ipaobj)
if info_path:
plist_data = ipaobj.read(release_type_info)
if b"ProvisionedDevices" in plist_data:
self.result["release_type"] = "Adhoc"
else:
self.result["release_type"] = "Inhouse"
return self.result
def __get_ios_release_type(self, ipaobj):
infopath_re = re.compile(r'Payload/[^/]*.app/embedded.mobileprovision')
for i in ipaobj.namelist():
m = infopath_re.match(i)
if m is not None:
return m.group()
class FirApi(object):
def __init__(self, api_token):
'''
:param api_token: 长度为 32, 用户在 fir api_token
'''
self.api_token = api_token
self.app_url = "http://api.fir.im/apps"
def __request(self, action, *args, **kwargs):
fun_re = getattr(requests, action, None)
res = fun_re(*args, **kwargs)
return res
def get_version_by_id(self, id):
'''
:param id: 应用ID可在"应用管理"->"基本信息"查看
:return:
'''
url = "%s/latest/%s?api_token=%s" % (self.app_url, id, self.api_token)
res = self.__request("get", url)
if res.status_code == 200:
return res.json()
else:
raise Exception(res.content)
def get_version_by_bundle_id(self, bundle_id, type):
'''
:param bundle_id: Bundle ID(iOS) / Package name(Android)
:param type: 应用类型 ( ios / android )
:return:
'''
url = "%s/latest/%s?api_token=%s&type=%s" % (self.app_url,
bundle_id, self.api_token,
type) # 使用 bundle_id 请求必填 ( ios / android )
res = self.__request("get", url)
if res.status_code == 200:
return res.json()
else:
raise Exception(res.content)
def __get_upload_token(self, bundle_id, type):
'''
:param bundle_id: App bundleId发布新应用时必填
:param type: ios 或者 android发布新应用时必填
:return:
'''
data = {
"type": type,
"bundle_id": bundle_id,
"api_token": self.api_token,
}
res = self.__request("post", self.app_url, data)
if res.status_code == 201:
return res.json()
else:
raise Exception(res.content)
def upload_app(self, app_path):
appobj = AppInfo(app_path)
appinfo = appobj.get_app_data()
icon_path = appobj.make_app_png()
bundle_id = appinfo.get("bundle_id")
upcretsdata = self.__get_upload_token(bundle_id, appinfo.get("type"))
icon_key = upcretsdata["cert"]["icon"]["key"]
icon_token = upcretsdata["cert"]["icon"]["token"]
icon_upload_url = upcretsdata["cert"]["icon"]["upload_url"]
short_url = upcretsdata["short"]
icon_data = {
"key": icon_key,
"token": icon_token,
}
files = {'file': (os.path.basename(icon_path), open(icon_path, 'rb'), {'Expires': '0'})}
res = self.__request("post", icon_upload_url, data=icon_data, files=files)
if res.status_code == 200:
print(res.json())
else:
raise Exception(res.content)
binary_key = upcretsdata["cert"]["binary"]["key"]
binary_token = upcretsdata["cert"]["binary"]["token"]
binary_upload_url = upcretsdata["cert"]["binary"]["upload_url"]
binary_data = {
"key": binary_key,
"token": binary_token,
"x:name": appinfo["labelname"],
"x:version": appinfo["version"],
"x:build": appinfo["versioncode"],
"x:changelog": ""
}
if appinfo["type"] == "ios": binary_data["x:release_type"] = appinfo.get("release_type",
"Adhoc") # Adhoc: 内测版 Inhouse:企业版
files = {'file': (os.path.basename(app_path), open(app_path, 'rb'), {'Expires': '0'})}
res = self.__request("post", binary_upload_url, data=binary_data, files=files)
if res.status_code == 200:
print(res.json())
QrCode.make_logo_qr(os.path.join("https://fir.im/", short_url), icon_path,
"fir_%s.png" % (appinfo["labelname"]))
else:
raise Exception(res.content)
def get_app_list(self):
url = "%s?api_token=%s" % (self.app_url, self.api_token)
res = self.__request("get", url)
if res.status_code == 200:
return res.json()
else:
raise Exception(res.content)
def get_app_infos_by_id(self, id):
'''
:param id: 应用id可在"应用管理"->"基本信息"查看
:return:
'''
url = "%s/%s?api_token=%s" % (self.app_url, id, self.api_token)
res = self.__request("get", url)
if res.status_code == 200:
return res.json()
else:
raise Exception(res.content)
def modify_app_info(self, id, name=None, desc=None, short=None, genre_id=None,
is_opened=None, is_show_plaza=None, passwd=None,
store_link_visible=None):
'''
:param id: 应用id可在"应用管理"->"基本信息"查看
:param name: app 名称
:param desc: app 详细描述
:param short: app 短链接
:param genre_id: 类别 id
:param is_opened: 是否公开
:param is_show_plaza: 是否展示在广场页
:param passwd: 密码访问(不能和is_opened is_show_plaza联合使用)
:param store_link_visible: 应用市场链接是否显示
:return:
'''
url = "%s/%s" % (self.app_url, id)
data = {
"api_token": self.api_token,
}
if name: data["name"] = name
if desc: data["desc"] = desc
if short: data["short"] = short
if genre_id: data["genre_id"] = genre_id
if is_opened: data["is_opened"] = is_opened
if is_show_plaza and not passwd: data["is_show_plaza"] = is_show_plaza
if passwd and not is_show_plaza: data["passwd"] = passwd
if is_show_plaza and passwd:
raise Exception("密码访问不能和is_opened is_show_plaza联合使用")
if store_link_visible: data["name"] = store_link_visible
res = self.__request("put", url, data)
if res.status_code == 200:
return res.json()
else:
raise Exception(res.content)
class QrCode(object):
@staticmethod
def make_logo_qr(str, logo_path, save_path=None):
qr = qrcode.QRCode(
version=4,
error_correction=qrcode.constants.ERROR_CORRECT_Q,
box_size=8,
border=2
)
# 添加转换内容
qr.add_data(str)
qr.make(fit=True)
# 生成二维码
img = qr.make_image()
#
img = img.convert("RGBA")
# 添加logo
if logo_path and os.path.exists(logo_path):
icon = Image.open(logo_path)
# 获取二维码图片的大小
img_w, img_h = img.size
factor = 4
size_w = int(img_w / factor)
size_h = int(img_h / factor)
# logo图片的大小不能超过二维码图片的1/4
icon_w, icon_h = icon.size
if icon_w > size_w:
icon_w = size_w
if icon_h > size_h:
icon_h = size_h
icon = icon.resize((icon_w, icon_h), Image.ANTIALIAS)
# 详见:http://pillow.readthedocs.org/handbook/tutorial.html
# 计算logo在二维码图中的位置
w = int((img_w - icon_w) / 2)
h = int((img_h - icon_h) / 2)
icon = icon.convert("RGBA")
img.paste(icon, (w, h), icon)
# 详见:http://pillow.readthedocs.org/reference/Image.html#PIL.Image.Image.paste
# 保存处理后图片
if save_path:
if not os.path.isdir(os.path.dirname(save_path)):
os.makedirs(os.path.dirname(save_path))
img.save(save_path)
else:
imgByteArr = io.BytesIO()
img.save(imgByteArr, format="PNG")
return imgByteArr.getvalue()
if __name__ == '__main__':
apk_path = "/root/hehemajiang.apk"
API_TOKEN = "3d2a41159a7d8c396ddf0e88799de14a"
fir = FirApi(API_TOKEN)
# 上传
fir.upload_app(apk_path)
# 获取版本信息
# print(fir.get_version_by_id("5dfb54d3b2eb4612589c3040"))
# 获取app详细信息
# print(fir.get_app_infos_by_id("5dfb54d3b2eb4612589c3040"))
# 获取列表
# print(fir.get_app_list())

@ -1,91 +0,0 @@
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# project: 12月
# author: NinEveN
# date: 2019/12/19
'''
Python生成二维码 v1.0
主要将文本生成二维码图片
测试一将文本生成白底黑字的二维码图片
测试二将文本生成带logo的二维码图片
'''
import os
import qrcode
from PIL import Image
# 生成二维码图片
def make_qr(str, save):
qr = qrcode.QRCode(
version=4, # 生成二维码尺寸的大小 1-40 1:21*21(21+(n-1)*4)
error_correction=qrcode.constants.ERROR_CORRECT_M, # L:7% M:15% Q:25% H:30%
box_size=10, # 每个格子的像素大小
border=2, # 边框的格子宽度大小
)
qr.add_data(str)
qr.make(fit=True)
img = qr.make_image()
img.save(save)
# 生成带logo的二维码图片
def make_logo_qr(str, logo, save):
# 参数配置
qr = qrcode.QRCode(
version=4,
error_correction=qrcode.constants.ERROR_CORRECT_Q,
box_size=8,
border=2
)
# 添加转换内容
qr.add_data(str)
#
qr.make(fit=True)
# 生成二维码
img = qr.make_image()
#
img = img.convert("RGBA")
# 添加logo
if logo and os.path.exists(logo):
icon = Image.open(logo)
# 获取二维码图片的大小
img_w, img_h = img.size
factor = 4
size_w = int(img_w / factor)
size_h = int(img_h / factor)
# logo图片的大小不能超过二维码图片的1/4
icon_w, icon_h = icon.size
if icon_w > size_w:
icon_w = size_w
if icon_h > size_h:
icon_h = size_h
icon = icon.resize((icon_w, icon_h), Image.ANTIALIAS)
# 详见:http://pillow.readthedocs.org/handbook/tutorial.html
# 计算logo在二维码图中的位置
w = int((img_w - icon_w) / 2)
h = int((img_h - icon_h) / 2)
icon = icon.convert("RGBA")
img.paste(icon, (w, h), icon)
# 详见:http://pillow.readthedocs.org/reference/Image.html#PIL.Image.Image.paste
# 保存处理后图片
img.save(save)
if __name__ == '__main__':
save_path = 'base__runmethod_02.png' # 生成后的保存文件
logo = '/tmp/pycharm_project_986/fir/tmp/icon/cmjpwyltznbioseh' # logo图片
str = "https://fir.im/luy4"
make_logo_qr(str, logo, save_path)

@ -1,140 +0,0 @@
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# project: 3月
# author: liuyu
# date: 2020/3/6
import json
import logging
import os
import signal
import socket
import time
from subprocess import Popen, PIPE
import paramiko
logger = logging.getLogger(__name__)
def default_result():
return {'exit_code': '99', 'return_info': 'Failed to run, function_name is not existed'}
def portisonline(host, port):
result = {}
sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sk.settimeout(3)
result["host"] = host
result["port"] = port
result["functionname"] = "CHECK_HOST_ONLINE:"
try:
sk.connect((host, port))
result["message"] = "Connection success"
result["code"] = 0
except Exception:
result["message"] = "SSH Port Connection refused"
result["code"] = 1
sk.close()
return result
class SSHConnection(object):
def __init__(self, host, port, username, password):
self._host = host
self._port = port
self._username = username
self._password = password
self._transport = None
self._sftp = None
self._client = None
self._connect()
def _connect(self):
transport = paramiko.Transport((self._host, self._port))
transport.connect(username=self._username, password=self._password)
self._transport = transport
# 下载
def download(self, remotepath, localpath):
if self._sftp is None:
self._sftp = paramiko.SFTPClient.from_transport(self._transport)
self._sftp.get(remotepath, localpath)
self.close()
# 上传
def put(self, localpath, remotepath):
if self._sftp is None:
self._sftp = paramiko.SFTPClient.from_transport(self._transport)
self.exec_command('mkdir -p %s' % (os.path.dirname(remotepath)))
self._sftp.put(localpath, remotepath)
self.close()
# 执行命令
def exec_command(self, command):
if self._client is None:
self._client = paramiko.SSHClient()
self._client._transport = self._transport
stdin, stdout, stderr = self._client.exec_command(command)
data = stdout.read()
if len(data) > 0:
return {'status': 0, 'infos': data}
err = stderr.read()
if len(err) > 0:
return {'status': 1, 'infos': err}
return {'status': 0, 'infos': data}
def close(self):
if self._transport:
self._transport.close()
if self._client:
self._client.close()
def shell_command(cmdstrs, timeout):
result = default_result()
result['return_info'] = ''
shell_start_time = time.time()
child = Popen(cmdstrs, shell=True, stdout=PIPE, stderr=PIPE)
if timeout:
while child.poll() is None:
time.sleep(1)
now = time.time()
if int(now - shell_start_time) > timeout:
os.kill(child.pid, signal.SIGKILL)
os.waitpid(-1, os.WNOHANG)
result['exit_code'] = 126
return result
out, err = child.communicate()
if err:
result['err_info'] = err.decode("utf-8")
shell_end_time = time.time()
result['shell_run_time'] = shell_end_time - shell_start_time
out = out.strip(b'\n')
result['return_info'] = out
result['exit_code'] = child.returncode
logger.info(f'shell: {cmdstrs} - return_info:{out} - exit_code:{child.returncode}')
return result
def use_user_pass(hostip, port, user, passwd, cmdstrs):
result = default_result()
checkinfo = portisonline(hostip, port)
if checkinfo["code"] != 0:
result['shell_run_time'] = 0
result['return_info'] = json.dumps(checkinfo)
result['exit_code'] = checkinfo["code"]
else:
conn = SSHConnection(hostip, port, user, passwd)
result['return_info'] = ''
shell_start_time = time.time()
out = conn.exec_command(cmdstrs)
shell_end_time = time.time()
result['shell_run_time'] = shell_end_time - shell_start_time
outs = str(out['infos'], 'utf-8')
outs = outs.strip('\n')
result['return_info'] = outs
result['exit_code'] = out['status']
logger.info(f'host: {hostip} user:{user} - shell: {cmdstrs} - return_info:{out} - exit_code:{out["status"]}')
conn.close()
return result

@ -16,57 +16,6 @@ from common.utils.storage import Storage
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def make_resigned(bin_url, img_url, bundle_id, app_version, name):
ios_plist_tem = """<?xml version="1.0" encoding="UTF-8"?>
<plist version="1.0"><dict>
<key>items</key>
<array>
<dict>
<key>assets</key>
<array>
<dict>
<key>kind</key>
<string>software-package</string>
<key>url</key>
<string><![CDATA[{bin_url}]]></string>
</dict>
<dict>
<key>kind</key>
<string>display-image</string>
<key>needs-shine</key>
<integer>0</integer>
<key>url</key>
<string><![CDATA[{img_url}]]></string>
</dict>
<dict>
<key>kind</key>
<string>full-size-image</string>
<key>needs-shine</key>
<true/>
<key>url</key>
<string><![CDATA[{img_url}]]></string>
</dict>
</array>
<key>metadata</key>
<dict>
<key>bundle-identifier</key>
<string>{bundle_id}</string>
<key>bundle-version</key>
<string><![CDATA[{app_version}]]></string>
<key>kind</key>
<string>software</string>
<key>title</key>
<string><![CDATA[{name}]]></string>
</dict>
</dict>
</array>
</dict>
</plist>""".format(bin_url=bin_url, img_url=img_url, bundle_id=bundle_id, app_version=app_version, name=name)
logger.info(
f"make_resigned bin_url {bin_url} ,img_url {img_url}, bundle_id {bundle_id}, app_version {app_version}, name {name}")
return ios_plist_tem
def bytes2human(n): def bytes2human(n):
symbols = ('K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y') symbols = ('K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y')
prefix = {} prefix = {}

@ -0,0 +1,47 @@
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# project: 4月
# author: liuyu
# date: 2020/4/7
import logging
import time
from django.core.cache import cache
from api.models import Apps, UserInfo
from common.utils.storage import Storage
from fir_ser.settings import CACHE_KEY_TEMPLATE
logger = logging.getLogger(__name__)
def sync_download_times():
down_tem_key = CACHE_KEY_TEMPLATE.get("download_times_key")
key = "_".join([down_tem_key, '*'])
for app_download in cache.iter_keys(key):
count_hits = cache.get(app_download)
if count_hits:
app_id = app_download.split(down_tem_key)[1].strip('_')
Apps.objects.filter(app_id=app_id).update(count_hits=count_hits)
logger.info(f"sync_download_times app_id:{app_id} count_hits:{count_hits}")
def auto_clean_upload_tmp_file():
upload_tem_key = CACHE_KEY_TEMPLATE.get("upload_file_tmp_name_key")
key = "_".join([upload_tem_key, '*'])
for upload_tem_file_key in cache.iter_keys(key):
data = cache.get(upload_tem_file_key)
if data:
u_time = data.get("u_time", None)
if u_time and time.time() - u_time > 60 * 20:
user_obj = UserInfo.objects.filter(pk=data.get("id")).first()
if user_obj:
storage = Storage(user_obj)
filename = data.get("filename", None)
if filename:
storage.delete_file(filename)
logger.info(f"auto_clean_upload_tmp_file delete_file :{filename}")
cache.delete(upload_tem_file_key)
logger.info(f"auto_clean_upload_tmp_file upload_tem_file_key :{upload_tem_file_key}")

@ -9,14 +9,12 @@ import logging
import random import random
from urllib.parse import urljoin from urllib.parse import urljoin
from django.db.models import Count, Sum, Q from django.db.models import Count
from rest_framework.pagination import PageNumberPagination from rest_framework.pagination import PageNumberPagination
from api.models import AppReleaseInfo, UserDomainInfo, DomainCnameInfo, UserAdDisplayInfo, RemoteClientInfo, \ from api.models import AppReleaseInfo, UserDomainInfo, DomainCnameInfo, UserAdDisplayInfo, RemoteClientInfo
AppIOSDeveloperInfo, IosDeveloperPublicPoolBill, APPToDeveloper, UserInfo, UDIDsyncDeveloper, IosDeveloperBill, \
AppUDID
from common.base.baseutils import get_server_domain_from_request, get_user_default_domain_name, get_real_ip_address, \ from common.base.baseutils import get_server_domain_from_request, get_user_default_domain_name, get_real_ip_address, \
get_origin_domain_name, is_valid_phone get_origin_domain_name
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -175,96 +173,6 @@ def check_app_domain_name_access(app_obj, access_domain_name, user_obj, extra_do
return True return True
def get_ios_developer_public_num(user_obj):
add_number = get_user_public_sign_num(user_obj)
if not add_number:
add_number = -99
used_number = get_user_public_used_sign_num(user_obj)
return add_number - used_number
def get_user_public_sign_num(user_obj):
add_number = IosDeveloperBill.objects.filter(to_user_id=user_obj, status=2).aggregate(
number=Sum('number'))
number = add_number.get("number", 0)
return number if number else 0
def get_user_public_used_sign_num(user_obj):
used_number = IosDeveloperPublicPoolBill.objects.filter(user_id=user_obj, udid_sync_info__isnull=False).exclude(
udid_sync_info__developerid__user_id=user_obj).values('number',
'udid_sync_info_id').annotate(
counts=Count('udid_sync_info_id')).aggregate(number=Sum('number'))
number = used_number.get("number", 0)
return number if number else 0
# def get_developer_can_used_from_public_sign(user_obj):
# o_number_info = IosDeveloperBill.objects.filter(to_user_id__isnull=False, user_id=user_obj).values(
# 'number').aggregate(number=Sum('number'))
# o_number = o_number_info.get("number", 0)
# if o_number is None:
# o_number = 0
# u_number_info = IosDeveloperPublicPoolBill.objects.filter(
# user_id_id__in=IosDeveloperBill.objects.filter(user_id=user_obj).values('to_user_id_id')).values(
# 'number',
# 'udid_sync_info_id').annotate(
# counts=Count('udid_sync_info_id')).aggregate(number=Sum('number'))
# u_number = u_number_info.get("number", 0)
# if u_number is None:
# u_number = 0
# return o_number - u_number
def check_super_sign_permission(user_obj):
if not user_obj.supersign_active:
return False
developer_count = AppIOSDeveloperInfo.objects.filter(user_id=user_obj).count()
if developer_count == 0 and get_ios_developer_public_num(user_obj) < 0:
return False
return True
def check_ipa_is_latest_sign(app_obj, developer_obj=None):
if AppUDID.objects.filter(app_id=app_obj, udid__developerid=developer_obj, sign_status__lt=4).first():
return
release_obj = AppReleaseInfo.objects.filter(app_id=app_obj, is_master=True).first()
all_app_to_dev = APPToDeveloper.objects.filter(app_id=app_obj)
if developer_obj:
all_app_to_dev = all_app_to_dev.filter(developerid=developer_obj)
all_app_to_dev = all_app_to_dev.all()
t_count = 0
for apptodev_obj in all_app_to_dev:
if release_obj.release_id == apptodev_obj.release_file:
t_count += 1
if t_count == all_app_to_dev.count():
return True
def get_user_obj_from_epu(user_id):
if is_valid_phone(user_id):
user_obj = UserInfo.objects.filter(mobile=user_id).first()
else:
user_obj = UserInfo.objects.filter(Q(email=user_id) | Q(uid=user_id)).first()
return user_obj
def update_or_create_developer_udid_info(device_obj, developer_obj):
device = {
"serial": device_obj.id,
"product": device_obj.name,
"udid": device_obj.udid,
"version": device_obj.model,
"status": True if device_obj.status == 'ENABLED' else False
}
return UDIDsyncDeveloper.objects.update_or_create(developerid=developer_obj, udid=device_obj.udid, defaults=device)
def check_uid_has_relevant(user_uid, to_user_uid):
if user_uid and to_user_uid:
return IosDeveloperBill.objects.filter(user_id__uid=user_uid, to_user_id__uid=to_user_uid, status=2).first()
class PageNumber(PageNumberPagination): class PageNumber(PageNumberPagination):
page_size = 10 # 每页显示多少条 page_size = 10 # 每页显示多少条
page_size_query_param = 'size' # URL中每页显示条数的参数 page_size_query_param = 'size' # URL中每页显示条数的参数

@ -1,19 +1,16 @@
import logging import logging
import os import os
from django.db.models import Sum
from rest_framework import serializers from rest_framework import serializers
from api import models from api import models
from api.utils.TokenManager import make_token from api.utils.apputils import bytes2human
from api.utils.app.apputils import bytes2human
from api.utils.modelutils import get_user_domain_name, get_app_domain_name, get_app_download_uri from api.utils.modelutils import get_user_domain_name, get_app_domain_name, get_app_download_uri
from api.utils.utils import get_developer_udided from common.base.baseutils import get_choices_dict
from common.base.baseutils import get_choices_dict, AppleDeveloperUid
from common.cache.storage import AdPicShowCache from common.cache.storage import AdPicShowCache
from common.core.sysconfig import Config
from common.utils.caches import get_user_free_download_times, get_user_cert_auth_status from common.utils.caches import get_user_free_download_times, get_user_cert_auth_status
from common.utils.storage import Storage from common.utils.storage import Storage
from common.utils.token import make_token
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -114,16 +111,16 @@ class AppsSerializer(serializers.ModelSerializer):
def get_preview_url(self, obj): def get_preview_url(self, obj):
return get_app_download_uri(None, obj.user_id, obj) return get_app_download_uri(None, obj.user_id, obj)
private_developer_number = serializers.SerializerMethodField() # private_developer_number = serializers.SerializerMethodField()
#
def get_private_developer_number(self, obj): # def get_private_developer_number(self, obj):
return models.AppleDeveloperToAppUse.objects.filter(app_id=obj).count() # return models.AppleDeveloperToAppUse.objects.filter(app_id=obj).count()
#
private_developer_used_number = serializers.SerializerMethodField() # private_developer_used_number = serializers.SerializerMethodField()
#
def get_private_developer_used_number(self, obj): # def get_private_developer_used_number(self, obj):
return models.DeveloperDevicesID.objects.filter(app_id=obj, # return models.DeveloperDevicesID.objects.filter(app_id=obj,
developerid__appledevelopertoappuse__app_id=obj).distinct().count() # developerid__appledevelopertoappuse__app_id=obj).distinct().count()
domain_name = serializers.SerializerMethodField() domain_name = serializers.SerializerMethodField()
@ -135,15 +132,15 @@ class AppsSerializer(serializers.ModelSerializer):
def get_sign_type_choice(self, obj): def get_sign_type_choice(self, obj):
return get_choices_dict(obj.supersign_type_choices) return get_choices_dict(obj.supersign_type_choices)
supersign_used_number = serializers.SerializerMethodField() # supersign_used_number = serializers.SerializerMethodField()
#
def get_supersign_used_number(self, obj): # def get_supersign_used_number(self, obj):
return models.APPSuperSignUsedInfo.objects.filter(app_id=obj).all().count() # return models.APPSuperSignUsedInfo.objects.filter(app_id=obj).all().count()
#
developer_used_count = serializers.SerializerMethodField() # developer_used_count = serializers.SerializerMethodField()
#
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()
screenshots = serializers.SerializerMethodField() screenshots = serializers.SerializerMethodField()
@ -378,179 +375,6 @@ class StorageSerializer(serializers.ModelSerializer):
return super().update(instance, validated_data) return super().update(instance, validated_data)
class DeveloperSerializer(serializers.ModelSerializer):
class Meta:
model = models.AppIOSDeveloperInfo
# depth = 1
exclude = ["id", "user_id", "p8key"]
developer_used_number = serializers.SerializerMethodField()
developer_used_other_number = serializers.SerializerMethodField()
use_number = serializers.SerializerMethodField()
app_used_count = serializers.SerializerMethodField()
is_disabled = serializers.SerializerMethodField()
app_private_number = serializers.SerializerMethodField()
app_private_used_number = serializers.SerializerMethodField()
app_usable_number = serializers.SerializerMethodField()
app_used_number = serializers.SerializerMethodField()
private_usable_number = serializers.SerializerMethodField()
status_display = serializers.CharField(source="get_status_display")
def get_app_usable_number(self, obj):
app_id = self.context.get('app_id')
if app_id:
apple_to_app_obj = models.AppleDeveloperToAppUse.objects.filter(developerid=obj,
app_id__app_id=app_id).first()
if apple_to_app_obj:
return apple_to_app_obj.usable_number
return 0
def get_app_used_number(self, obj):
app_id = self.context.get('app_id', '')
if app_id:
return models.DeveloperDevicesID.objects.filter(developerid=obj,
app_id__app_id=app_id).distinct().count()
return 0
def get_developer_used_number(self, obj):
return models.UDIDsyncDeveloper.objects.filter(developerid=obj).count()
def get_developer_used_other_number(self, obj):
return get_developer_udided(obj)[0]
def get_is_disabled(self, obj):
app_id = self.context.get('app_id', '')
if app_id:
developer_app_obj = models.DeveloperAppID.objects.filter(developerid=obj,
developerid__status__in=Config.DEVELOPER_USE_STATUS,
developerid__certid__isnull=False)
if developer_app_obj.filter(app_id__app_id=app_id).distinct().count():
return False
else:
if developer_app_obj and developer_app_obj.distinct().count() < obj.app_limit_number:
return False
return True
return True
def get_app_private_number(self, obj):
return models.AppleDeveloperToAppUse.objects.filter(developerid=obj).count()
def get_private_usable_number(self, obj):
used_number = models.AppleDeveloperToAppUse.objects.filter(developerid=obj).values('usable_number').aggregate(
Sum('usable_number')).get('usable_number__sum')
if not used_number:
used_number = 0
return used_number
def get_app_private_used_number(self, obj):
return models.DeveloperDevicesID.objects.filter(developerid=obj,
app_id__appledevelopertoappuse__developerid=obj).values(
'udid').distinct().count()
def get_use_number(self, obj):
return models.DeveloperDevicesID.objects.filter(developerid=obj).values('udid').distinct().count()
def get_app_used_count(self, obj):
return models.DeveloperAppID.objects.filter(developerid=obj).count()
class SuperSignUsedSerializer(serializers.ModelSerializer):
class Meta:
model = models.APPSuperSignUsedInfo
fields = ["created_time", "device_udid", "device_name", "developer_id", "bundle_id", "bundle_name", "other_uid",
"developer_description", "developer_status"]
device_udid = serializers.CharField(source="udid.udid.udid")
device_name = serializers.CharField(source="udid.product")
bundle_id = serializers.CharField(source="app_id.bundle_id")
bundle_name = serializers.CharField(source="app_id.name")
other_uid = serializers.SerializerMethodField()
developer_id = serializers.SerializerMethodField()
developer_description = serializers.SerializerMethodField()
developer_status = serializers.SerializerMethodField()
def get_developer_status(self, obj):
return obj.developerid.get_status_display()
def get_developer_id(self, obj):
issuer_id = obj.developerid.issuer_id
if self.context.get('mine'):
return issuer_id
else:
return f"{Config.DEVELOPER_UID_KEY}{AppleDeveloperUid().get_encrypt_uid(issuer_id)}"
def get_developer_description(self, obj):
if self.context.get('mine'):
return obj.developerid.description
else:
return '公共账号池'
def get_other_uid(self, obj):
user_obj = self.context.get('user_obj')
if obj.user_id != obj.developerid.user_id:
if user_obj.uid != obj.user_id.uid:
return {'uid': obj.user_id.uid, 'name': obj.user_id.first_name}
class DeveloperDeviceSerializer(serializers.ModelSerializer):
class Meta:
model = models.UDIDsyncDeveloper
exclude = ["id", "developerid"]
developer_id = serializers.CharField(source="developerid.issuer_id")
developer_description = serializers.CharField(source="developerid.description")
developer_status = serializers.CharField(source="developerid.get_status_display")
app_used_count = serializers.SerializerMethodField()
def get_app_used_count(self, obj):
return models.DeveloperDevicesID.objects.filter(udid=obj, developerid=obj.developerid).values(
'app_id').distinct().count()
class DeviceUDIDSerializer(serializers.ModelSerializer):
class Meta:
model = models.AppUDID
# depth = 1
exclude = ["updated_time", "sign_status"]
bundle_name = serializers.CharField(source="app_id.name")
bundle_id = serializers.CharField(source="app_id.bundle_id")
issuer_id = serializers.SerializerMethodField()
developer_description = serializers.SerializerMethodField()
developer_status = serializers.SerializerMethodField()
udid = serializers.CharField(source="udid.udid")
is_mine = serializers.SerializerMethodField()
other_uid = serializers.SerializerMethodField()
def get_issuer_id(self, obj):
issuer_id = obj.udid.developerid.issuer_id
if self.context.get('mine'):
return issuer_id
else:
return f"{Config.DEVELOPER_UID_KEY}{AppleDeveloperUid().get_encrypt_uid(issuer_id)}"
def get_developer_status(self, obj):
return obj.udid.developerid.get_status_display()
def get_developer_description(self, obj):
if self.context.get('mine'):
return obj.udid.developerid.description
else:
return '公共账号池'
def get_other_uid(self, obj):
user_obj = self.context.get('user_obj')
user_obj_new = obj.app_id.user_id
if user_obj.pk != user_obj_new.pk:
super_user_obj = models.APPSuperSignUsedInfo.objects.filter(udid=obj, app_id=obj.app_id).first()
if super_user_obj.developerid.user_id != user_obj_new:
return {'uid': user_obj_new.uid, 'name': user_obj_new.first_name}
def get_is_mine(self, obj):
return self.context.get('mine')
class PriceSerializer(serializers.ModelSerializer): class PriceSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = models.Price model = models.Price
@ -627,112 +451,7 @@ class AppAdInfoSerializer(UserAdInfoSerializer):
return get_download_url_from_context(self, obj, '', obj.ad_pic, True) return get_download_url_from_context(self, obj, '', obj.ad_pic, True)
class BillAppInfoSerializer(serializers.ModelSerializer):
class Meta:
model = models.Apps
fields = ["pk", "app_id", "user_id", "name", "type", "bundle_id", "updated_time"]
class BillDeveloperInfoSerializer(serializers.ModelSerializer):
class Meta:
model = models.AppIOSDeveloperInfo
fields = ["pk", "issuer_id", "user_id", "certid"]
class BillInfoSerializer(serializers.ModelSerializer):
class Meta:
model = models.IosDeveloperPublicPoolBill
exclude = ["user_id", "developer_info", "app_info", "udid_sync_info", "app_id"]
app_name = serializers.SerializerMethodField()
description = serializers.SerializerMethodField()
is_used = serializers.SerializerMethodField()
app_status = serializers.SerializerMethodField()
remote_addr = serializers.SerializerMethodField()
def get_remote_addr(self, obj):
return obj.remote_addr
def get_app_status(self, obj):
if obj.app_id:
return True
return False
def get_is_used(self, obj):
if obj.udid_sync_info:
return True
return False
def get_app_name(self, obj):
if obj.app_info:
return obj.app_info.get('name')
def get_description(self, obj):
if obj.udid:
return f"{self.get_app_name(obj)}-消耗- {obj.number} 设备数"
class BillTransferSerializer(serializers.ModelSerializer):
class Meta:
model = models.IosDeveloperBill
exclude = ["id", "remote_addr", "user_id", "to_user_id"]
target_user = serializers.SerializerMethodField()
cancel = serializers.SerializerMethodField()
number = serializers.SerializerMethodField()
status_display = serializers.CharField(source='get_status_display')
def get_target_user(self, obj):
user_obj = obj.user_id
if self.get_cancel(obj):
user_obj = obj.to_user_id
return {'uid': user_obj.uid, 'name': user_obj.first_name}
def get_cancel(self, obj):
return self.context.get('user_obj').pk == obj.user_id.pk
def get_number(self, obj):
if self.get_cancel(obj):
return -obj.number
else:
return obj.number
class AppReportSerializer(serializers.ModelSerializer): class AppReportSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = models.AppReportInfo model = models.AppReportInfo
exclude = ["id"] exclude = ["id"]
class AppleDeveloperToAppUseSerializer(serializers.ModelSerializer):
class Meta:
model = models.AppleDeveloperToAppUse
# exclude = ["user_id", "to_user_id", "developer_info", "app_info", "udid_sync_info", "app_id"]
fields = ["issuer_id", "app_id", "created_time", "description", "usable_number"]
issuer_id = serializers.CharField(source="developerid.issuer_id")
app_id = serializers.CharField(source="app_id.app_id")
class AppleDeveloperToAppUseAppsSerializer(serializers.ModelSerializer):
class Meta:
model = models.Apps
fields = ["app_id", "name", "short", "bundle_id", "description", "app_usable_number", "app_used_number"]
app_usable_number = serializers.SerializerMethodField()
app_used_number = serializers.SerializerMethodField()
def get_app_usable_number(self, obj):
issuer_id = self.context.get('issuer_id')
if issuer_id:
apple_to_app_obj = models.AppleDeveloperToAppUse.objects.filter(developerid__issuer_id=issuer_id,
app_id=obj).first()
if apple_to_app_obj:
return apple_to_app_obj.usable_number
return 0
def get_app_used_number(self, obj):
issuer_id = self.context.get('issuer_id')
if issuer_id:
return models.DeveloperDevicesID.objects.filter(app_id=obj, developerid__issuer_id=issuer_id).count()
return 0

@ -12,10 +12,7 @@ from captcha.helpers import captcha_image_url
from captcha.models import CaptchaStore from captcha.models import CaptchaStore
from django.core.cache import cache from django.core.cache import cache
from api.models import APPSuperSignUsedInfo, APPToDeveloper, \ from api.models import UserInfo, AppReleaseInfo, AppScreenShot, Token, UserAdDisplayInfo
UDIDsyncDeveloper, UserInfo, AppReleaseInfo, AppScreenShot, Token, DeveloperDevicesID, UserAdDisplayInfo
from api.utils.TokenManager import generate_numeric_token_of_length, generate_alphanumeric_token_of_length, make_token, \
verify_token
from api.utils.modelutils import get_app_d_count_by_app_id from api.utils.modelutils import get_app_d_count_by_app_id
from common.base.baseutils import get_real_ip_address from common.base.baseutils import get_real_ip_address
from common.cache.storage import UserTokenCache, TempCache from common.cache.storage import UserTokenCache, TempCache
@ -24,67 +21,13 @@ from common.libs.storage.localApi import LocalStorage
from common.utils.caches import consume_user_download_times from common.utils.caches import consume_user_download_times
from common.utils.sendmsg import SendMessage from common.utils.sendmsg import SendMessage
from common.utils.storage import Storage from common.utils.storage import Storage
from common.utils.token import generate_numeric_token_of_length, generate_alphanumeric_token_of_length, make_token, \
verify_token
from fir_ser.settings import CAPTCHA_LENGTH, MEDIA_ROOT from fir_ser.settings import CAPTCHA_LENGTH, MEDIA_ROOT
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def delete_app_to_dev_and_file(developer_obj, app_id):
app_to_developer_obj = APPToDeveloper.objects.filter(developerid=developer_obj, app_id_id=app_id)
if app_to_developer_obj:
binary_file = app_to_developer_obj.first().binary_file + ".ipa"
delete_local_files(binary_file)
storage = Storage(developer_obj.user_id)
storage.delete_file(binary_file)
app_to_developer_obj.delete()
def get_developer_udided(developer_obj):
super_sing_used_obj = APPSuperSignUsedInfo.objects.filter(developerid=developer_obj).all()
udid_sync_developer_obj = UDIDsyncDeveloper.objects.filter(developerid=developer_obj).all()
developer_udid_lists = []
super_sign_udid_lists = []
if udid_sync_developer_obj:
developer_udid_lists = list(udid_sync_developer_obj.values_list("udid"))
if super_sing_used_obj:
super_sign_udid_lists = list(super_sing_used_obj.values_list("udid__udid__udid"))
return len(set(developer_udid_lists) - set(super_sign_udid_lists)), len(set(super_sign_udid_lists)), len(
set(developer_udid_lists))
def usable_number(developer_obj):
d_count = UDIDsyncDeveloper.objects.filter(developerid=developer_obj).count()
u_count = developer_obj.usable_number
return d_count if d_count > u_count else u_count
def get_developer_devices(developer_obj_lists):
other_used_sum = 0
flyapp_used_sum = 0
max_total = 0
for dev_obj in developer_obj_lists.filter(status__in=Config.DEVELOPER_USE_STATUS):
other_used, flyapp_used, _ = get_developer_udided(dev_obj)
other_used_sum += other_used
flyapp_used_sum += flyapp_used
max_total += 100
use_number_obj_list = developer_obj_lists.filter(status__in=Config.DEVELOPER_USE_STATUS)
all_use_number = 0
all_usable_number = 0
for use_number_obj in use_number_obj_list:
all_usable_number += usable_number(use_number_obj)
all_use_number += DeveloperDevicesID.objects.filter(developerid=use_number_obj).values(
'udid').distinct().count()
use_num = {
"all_usable_number": all_usable_number,
"all_use_number": all_use_number,
"other_used_sum": other_used_sum,
"flyapp_used_sum": flyapp_used_sum,
"max_total": max_total
}
return use_num
def get_captcha(): def get_captcha():
captcha_key = CaptchaStore.generate_key() captcha_key = CaptchaStore.generate_key()
captcha_image = captcha_image_url(captcha_key) captcha_image = captcha_image_url(captcha_key)

@ -12,16 +12,14 @@ from rest_framework.response import Response
from rest_framework.views import APIView from rest_framework.views import APIView
from api.base_views import app_delete from api.base_views import app_delete
from api.models import Apps, AppReleaseInfo, APPToDeveloper, UserInfo, AppScreenShot, AppUDID from api.models import Apps, AppReleaseInfo, UserInfo, AppScreenShot
from api.tasks import run_resign_task from api.utils.modelutils import get_user_domain_name, get_app_domain_name
from api.utils.app.supersignutils import IosUtils
from api.utils.modelutils import get_user_domain_name, get_app_domain_name, check_super_sign_permission
from api.utils.response import BaseResponse from api.utils.response import BaseResponse
from api.utils.serializer import AppsSerializer, AppReleaseSerializer, AppsListSerializer, AppsQrListSerializer from api.utils.serializer import AppsSerializer, AppReleaseSerializer, AppsListSerializer, AppsQrListSerializer
from api.utils.utils import delete_local_files, delete_app_screenshots_files from api.utils.utils import delete_local_files, delete_app_screenshots_files
from common.cache.state import MigrateStorageState, CleanAppSignDataState from common.cache.state import MigrateStorageState
from common.core.auth import ExpiringTokenAuthentication from common.core.auth import ExpiringTokenAuthentication
from common.core.sysconfig import Config from common.core.singals import delete_app_signal
from common.utils.caches import del_cache_response_by_short, get_app_today_download_times, del_cache_by_delete_app from common.utils.caches import del_cache_response_by_short, get_app_today_download_times, del_cache_by_delete_app
from common.utils.storage import Storage from common.utils.storage import Storage
@ -117,8 +115,6 @@ class AppInfoView(APIView):
if app_obj: if app_obj:
app_serializer = AppsSerializer(app_obj, context={"storage": Storage(request.user)}) app_serializer = AppsSerializer(app_obj, context={"storage": Storage(request.user)})
res.data = app_serializer.data res.data = app_serializer.data
count = APPToDeveloper.objects.filter(app_id=app_obj).count()
res.data["count"] = count
else: else:
logger.error(f"app_id:{app_id} is not found in user:{request.user}") logger.error(f"app_id:{app_id} is not found in user:{request.user}")
res.msg = "未找到该应用" res.msg = "未找到该应用"
@ -142,18 +138,6 @@ class AppInfoView(APIView):
res.msg = "数据迁移中,无法处理该操作" res.msg = "数据迁移中,无法处理该操作"
return Response(res.dict) return Response(res.dict)
clean = data.get("clean", None)
if clean:
with CleanAppSignDataState(request.user.uid) as state:
if state:
logger.info(f"app_id:{app_id} clean:{clean} ,close super_sign should clean_app_by_user_obj")
app_obj = Apps.objects.filter(user_id=request.user, app_id=app_id).first()
IosUtils.clean_app_by_user_obj(app_obj)
else:
res.code = 1008
res.msg = "数据清理中,请耐心等待"
return Response(res.dict)
has_combo = data.get("has_combo", None) has_combo = data.get("has_combo", None)
if has_combo: if has_combo:
actions = has_combo.get("action", None) actions = has_combo.get("action", None)
@ -183,95 +167,23 @@ class AppInfoView(APIView):
res.msg = "该应用已经关联" res.msg = "该应用已经关联"
else: else:
try: try:
do_sign_flag = 0
app_obj = Apps.objects.filter(user_id=request.user, app_id=app_id).first() app_obj = Apps.objects.filter(user_id=request.user, app_id=app_id).first()
logger.info("app_id:%s update old data:%s" % (app_id, app_obj.__dict__)) logger.info("app_id:%s update old data:%s" % (app_id, app_obj.__dict__))
app_obj.description = data.get("description", app_obj.description) app_obj.description = data.get("description", app_obj.description)
app_obj.short = data.get("short", app_obj.short) app_obj.short = data.get("short", app_obj.short)
app_obj.name = data.get("name", app_obj.name) app_obj.name = data.get("name", app_obj.name)
app_obj.password = data.get("password", app_obj.password) app_obj.password = data.get("password", app_obj.password)
app_obj.supersign_limit_number = data.get("supersign_limit_number",
app_obj.supersign_limit_number)
app_obj.isshow = data.get("isshow", app_obj.isshow) app_obj.isshow = data.get("isshow", app_obj.isshow)
update_fields = ["description", "short", "name", "password", "supersign_limit_number", "isshow"] update_fields = ["description", "short", "name", "password", "isshow"]
if get_user_domain_name(request.user) or get_app_domain_name(app_obj): if get_user_domain_name(request.user) or get_app_domain_name(app_obj):
app_obj.wxeasytype = data.get("wxeasytype", app_obj.wxeasytype) app_obj.wxeasytype = data.get("wxeasytype", app_obj.wxeasytype)
else: else:
app_obj.wxeasytype = 1 app_obj.wxeasytype = 1
update_fields.append("wxeasytype") update_fields.append("wxeasytype")
if app_obj.issupersign:
if app_obj.supersign_type in [x[0] for x in list(app_obj.supersign_type_choices)]:
if app_obj.supersign_type != data.get("supersign_type", app_obj.supersign_type):
do_sign_flag = 1
app_obj.supersign_type = data.get("supersign_type", app_obj.supersign_type)
update_fields.append("supersign_type")
new_bundle_id = data.get("new_bundle_id", None)
new_bundle_name = data.get("new_bundle_name", None)
if new_bundle_id is not None:
if new_bundle_id and new_bundle_id != app_obj.bundle_id and len(new_bundle_id) > 3:
if new_bundle_id != app_obj.new_bundle_id:
do_sign_flag = 2
app_obj.new_bundle_id = new_bundle_id
if new_bundle_id == '' or (app_obj.new_bundle_id != new_bundle_id):
if app_obj.bundle_id != app_obj.new_bundle_id:
do_sign_flag = 2
app_obj.new_bundle_id = app_obj.bundle_id
update_fields.append('new_bundle_id')
if new_bundle_name is not None:
if new_bundle_name and new_bundle_name != app_obj.name and len(new_bundle_name) > 0:
if new_bundle_name != app_obj.new_bundle_name:
do_sign_flag = 2
app_obj.new_bundle_name = new_bundle_name
if new_bundle_name == '' or (app_obj.new_bundle_name != new_bundle_name):
if app_obj.name != app_obj.new_bundle_name:
do_sign_flag = 2
app_obj.new_bundle_name = app_obj.name
update_fields.append('new_bundle_name')
app_obj.wxredirect = data.get("wxredirect", app_obj.wxredirect) app_obj.wxredirect = data.get("wxredirect", app_obj.wxredirect)
update_fields.append("wxredirect") update_fields.append("wxredirect")
if app_obj.type == 1 and data.get('issupersign', -1) != -1:
if data.get('issupersign', -1) == 1 and not check_super_sign_permission(request.user):
logger.error(f"app_id:{app_id} can't open super_sign,owner has no ios developer")
res.code = 1008
res.msg = "超级签余额不足,无法开启"
return Response(res.dict)
do_sign_flag = 3
app_obj.issupersign = data.get("issupersign", app_obj.issupersign)
update_fields.append("issupersign")
if app_obj.issupersign and data.get('change_auto_sign', -1) != -1:
if data.get('change_auto_sign', -1) == 1:
do_sign_flag = 3
app_obj.change_auto_sign = data.get("change_auto_sign", app_obj.change_auto_sign)
update_fields.append("change_auto_sign")
logger.info(f"app_id:{app_id} update new data:{app_obj.__dict__}") logger.info(f"app_id:{app_id} update new data:{app_obj.__dict__}")
app_obj.save(update_fields=update_fields) app_obj.save(update_fields=update_fields)
if app_obj.issupersign:
c_task = None
if do_sign_flag == 1:
AppUDID.objects.filter(app_id=app_obj).update(sign_status=2)
if app_obj.change_auto_sign:
c_task = run_resign_task(app_obj.pk, True)
if do_sign_flag == 2:
AppUDID.objects.filter(app_id=app_obj, sign_status__gte=3).update(sign_status=3)
if app_obj.change_auto_sign:
flag = False
if AppUDID.objects.filter(app_id=app_obj, sign_status=2,
udid__developerid__status__in=Config.DEVELOPER_WRITE_STATUS).first():
flag = True
c_task = run_resign_task(app_obj.pk, flag)
if do_sign_flag == 3:
if app_obj.change_auto_sign:
flag = False
if AppUDID.objects.filter(app_id=app_obj, sign_status=2,
udid__developerid__status__in=Config.DEVELOPER_WRITE_STATUS).first():
flag = True
c_task = run_resign_task(app_obj.pk, flag, False)
if c_task:
logger.info(f"app {app_obj} run_resign_task msg:{c_task}")
del_cache_response_by_short(app_obj.app_id) del_cache_response_by_short(app_obj.app_id)
except Exception as e: except Exception as e:
logger.error(f"app_id:{app_id} update Exception:{e}") logger.error(f"app_id:{app_id} update Exception:{e}")
@ -337,10 +249,7 @@ class AppReleaseInfoView(APIView):
app_release_obj.delete() app_release_obj.delete()
elif app_release_obj.is_master and app_release_count < 2: elif app_release_obj.is_master and app_release_count < 2:
logger.info(f"delete app master release {app_release_obj} and clean app {app_obj}") logger.info(f"delete app master release {app_release_obj} and clean app {app_obj}")
count = APPToDeveloper.objects.filter(app_id=app_obj).count() delete_app_signal.send(None, app_pk=app_obj.pk)
if app_obj.issupersign or count > 0:
logger.info(f"app_id:{app_id} is super_sign ,delete this app need clean IOS developer")
IosUtils.clean_app_by_user_obj(app_obj)
storage.delete_file(app_release_obj.release_id, app_release_obj.release_type) storage.delete_file(app_release_obj.release_id, app_release_obj.release_type)
delete_local_files(app_release_obj.release_id, app_release_obj.release_type) delete_local_files(app_release_obj.release_id, app_release_obj.release_type)

@ -11,43 +11,24 @@ from django.http import FileResponse
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.views import APIView from rest_framework.views import APIView
from api.models import Apps, AppReleaseInfo, APPToDeveloper, APPSuperSignUsedInfo from api.models import Apps, AppReleaseInfo
from api.utils.TokenManager import verify_token
from api.utils.app.apputils import make_resigned
from api.utils.app.supersignutils import make_sign_udid_mobile_config
from api.utils.modelutils import get_filename_form_file, check_app_domain_name_access, \ from api.utils.modelutils import get_filename_form_file, check_app_domain_name_access, \
ad_random_weight, get_app_download_uri ad_random_weight, get_app_download_uri
from api.utils.response import BaseResponse from api.utils.response import BaseResponse
from api.utils.serializer import AppsShortSerializer, AppAdInfoSerializer from api.utils.serializer import AppsShortSerializer, AppAdInfoSerializer
from common.base.baseutils import get_profile_full_path, make_random_uuid, get_origin_domain_name, \ from common.base.baseutils import get_origin_domain_name, format_get_uri
format_get_uri, get_server_domain_from_request
from common.core.decorators import cache_response # 本来使用的是 drf-extensions==0.7.0 但是还未支持该版本Django from common.core.decorators import cache_response # 本来使用的是 drf-extensions==0.7.0 但是还未支持该版本Django
from common.core.response import mobileprovision_file_response
from common.core.sysconfig import Config from common.core.sysconfig import Config
from common.core.throttle import VisitShortThrottle, InstallShortThrottle, InstallThrottle1, InstallThrottle2 from common.core.throttle import VisitShortThrottle, InstallShortThrottle, InstallThrottle1, InstallThrottle2
from common.utils.caches import check_app_permission, get_app_download_url from common.utils.caches import check_app_permission, get_app_download_url
from common.utils.storage import Storage, get_local_storage from common.utils.storage import Storage, get_local_storage
from common.utils.token import verify_token
from fir_ser import settings from fir_ser import settings
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def file_response(stream, filename, content_type):
return FileResponse(stream, as_attachment=True,
filename=filename,
content_type=content_type)
def mobileprovision_file_response(file_path):
return file_response(open(file_path, 'rb'), make_random_uuid() + '.mobileprovision',
"application/x-apple-aspen-config")
def get_post_udid_url(request, short):
server_domain = get_server_domain_from_request(request, Config.POST_UDID_DOMAIN)
path_info_lists = [server_domain, "udid", short]
return "/".join(path_info_lists)
class DownloadView(APIView): class DownloadView(APIView):
""" """
文件下载接口,适用于本地存储和所有plist文件下载 文件下载接口,适用于本地存储和所有plist文件下载
@ -69,59 +50,7 @@ class DownloadView(APIView):
flag = verify_token(down_token, filename) flag = verify_token(down_token, filename)
if flag: if flag:
if f_type == 'plist': if f_type == 'dmobileprovision': # 企业签名安装信任跳转
release_id = filename.split('.')[0]
app_to_developer_obj = APPToDeveloper.objects.filter(binary_file=release_id).first()
if app_to_developer_obj:
release_obj = AppReleaseInfo.objects.filter(is_master=True,
app_id=app_to_developer_obj.app_id).first()
else:
release_obj = AppReleaseInfo.objects.filter(release_id=release_id).first()
if release_obj:
app_obj = release_obj.app_id
storage = Storage(app_obj.user_id)
bundle_id = app_obj.bundle_id
app_version = release_obj.app_version
name = app_obj.name
ios_plist_bytes = make_resigned(storage.get_download_url(filename.split('.')[0] + ".ipa"),
storage.get_download_url(release_obj.icon_url), bundle_id,
app_version, name)
return file_response(ios_plist_bytes, make_random_uuid(), "application/x-plist")
res.msg = "plist release_id error"
elif f_type == 'mobileconifg':
release_obj = AppReleaseInfo.objects.filter(release_id=filename.split('.')[0]).first()
if release_obj:
app_obj = release_obj.app_id
udid_url = get_post_udid_url(request, app_obj.short)
ios_udid_mobile_config = make_sign_udid_mobile_config(udid_url, f'{app_obj.app_id}_{app_obj.short}',
app_obj.bundle_id,
app_obj.name)
return file_response(ios_udid_mobile_config, make_random_uuid() + '.mobileconfig',
"application/x-apple-aspen-config")
res.msg = "mobile_config release_id error"
elif f_type == 'mobileprovision':
release_obj = AppReleaseInfo.objects.filter(release_id=filename.split('.')[0]).first()
if release_obj:
app_super_obj = APPSuperSignUsedInfo.objects.filter(app_id=release_obj.app_id).last()
if not app_super_obj:
app_super_obj = APPSuperSignUsedInfo.objects.last()
if not app_super_obj:
file_path = Config.DEFAULT_MOBILEPROVISION.get("supersign").get('path')
if file_path and os.path.isfile(file_path):
return mobileprovision_file_response(file_path)
else:
developer_obj = app_super_obj.developerid
file_path = get_profile_full_path(developer_obj, release_obj.app_id)
if os.path.isfile(file_path):
return mobileprovision_file_response(file_path)
else:
file_path = Config.DEFAULT_MOBILEPROVISION.get("supersign").get('path')
if file_path and os.path.isfile(file_path):
return mobileprovision_file_response(file_path)
res.msg = "mobile_provision release_id error"
elif f_type == 'dmobileprovision': # 企业签名安装信任跳转
release_obj = AppReleaseInfo.objects.filter(release_id=filename.split('.')[0]).first() release_obj = AppReleaseInfo.objects.filter(release_id=filename.split('.')[0]).first()
if release_obj: if release_obj:
file_path = Config.DEFAULT_MOBILEPROVISION.get("enterprise").get('path') file_path = Config.DEFAULT_MOBILEPROVISION.get("enterprise").get('path')

@ -11,7 +11,7 @@ from rest_framework.views import APIView
from api.base_views import storage_change, app_delete from api.base_views import storage_change, app_delete
from api.models import AppStorage, Apps from api.models import AppStorage, Apps
from api.utils.app.apputils import clean_history_apps from api.utils.apputils import clean_history_apps
from api.utils.response import BaseResponse from api.utils.response import BaseResponse
from api.utils.serializer import StorageSerializer from api.utils.serializer import StorageSerializer
from api.utils.utils import upload_oss_default_head_img from api.utils.utils import upload_oss_default_head_img

@ -10,19 +10,20 @@ import os
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.views import APIView from rest_framework.views import APIView
from api.models import Apps, AppReleaseInfo, UserInfo, AppScreenShot, CertificationInfo, UserAdDisplayInfo, AppUDID from api.models import Apps, AppReleaseInfo, UserInfo, AppScreenShot, CertificationInfo, UserAdDisplayInfo
from api.tasks import run_resign_task from api.utils.apputils import get_random_short, save_app_infos
from api.utils.TokenManager import verify_token from api.utils.modelutils import get_app_download_uri
from api.utils.app.apputils import get_random_short, save_app_infos
from api.utils.modelutils import check_super_sign_permission, get_app_download_uri
from api.utils.response import BaseResponse from api.utils.response import BaseResponse
from common.base.baseutils import make_app_uuid, make_from_user_uuid from common.base.baseutils import make_app_uuid, make_from_user_uuid
from common.cache.state import MigrateStorageState from common.cache.state import MigrateStorageState
from common.core.auth import ExpiringTokenAuthentication from common.core.auth import ExpiringTokenAuthentication
from common.core.singals import run_resign_task_signal
from common.core.sysconfig import Config from common.core.sysconfig import Config
from common.utils.caches import upload_file_tmp_name, del_cache_response_by_short from common.utils.caches import upload_file_tmp_name, del_cache_response_by_short
from common.utils.storage import Storage from common.utils.storage import Storage
from common.utils.token import verify_token
from fir_ser import settings from fir_ser import settings
from xsign.utils.modelutils import check_super_sign_permission
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -141,10 +142,7 @@ class AppAnalyseView(APIView):
app_obj = Apps.objects.filter(bundle_id=data.get("bundleid"), user_id=request.user, type=4).first() app_obj = Apps.objects.filter(bundle_id=data.get("bundleid"), user_id=request.user, type=4).first()
if app_obj: if app_obj:
AppUDID.objects.filter(app_id=app_obj, sign_status__gte=3).update(sign_status=3) run_resign_task_signal.send(sender=None, app_pk=app_obj.pk)
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}")
else: else:
storage.delete_file(app_tmp_filename) storage.delete_file(app_tmp_filename)
storage.delete_file(png_tmp_filename) storage.delete_file(png_tmp_filename)

@ -21,8 +21,8 @@ from cli.views.uploads import CliAppAnalyseView, CliUploadView
urlpatterns = [ urlpatterns = [
re_path("^apps$", CliAppsView.as_view()), re_path("^apps$", CliAppsView.as_view()),
re_path("^apps/(?P<app_id>\w+)", CliAppInfoView.as_view()), re_path(r"^apps/(?P<app_id>\w+)", CliAppInfoView.as_view()),
re_path("^appinfos/(?P<app_id>\w+)/(?P<act>\w+)", CliAppReleaseInfoView.as_view()), re_path(r"^appinfos/(?P<app_id>\w+)/(?P<act>\w+)", CliAppReleaseInfoView.as_view()),
re_path("^upload$", CliUploadView.as_view()), re_path("^upload$", CliUploadView.as_view()),
re_path("^userinfo", CliUserInfoView.as_view()), re_path("^userinfo", CliUserInfoView.as_view()),
re_path("^analyse$", CliAppAnalyseView.as_view()), re_path("^analyse$", CliAppAnalyseView.as_view()),

@ -0,0 +1,38 @@
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# project: 1月
# author: NinEveN
# date: 2022/1/6
from django.http import FileResponse
from rest_framework.response import Response
from common.base.baseutils import make_random_uuid
def file_response(stream, filename, content_type):
return FileResponse(stream, as_attachment=True,
filename=filename,
content_type=content_type)
def mobileprovision_file_response(file_path):
return file_response(open(file_path, 'rb'), make_random_uuid() + '.mobileprovision',
"application/x-apple-aspen-config")
class ApiResponse(Response):
def __init__(self, code=1000, msg='success', data=None, status=None, headers=None, content_type=None, **kwargs):
dic = {
'code': code,
'msg': msg
}
if data is not None:
dic['data'] = data
dic.update(kwargs)
self._data = data
# 对象来调用对象的绑定方法,会自动传值
super().__init__(data=dic, status=status, headers=headers, content_type=content_type)
# 类来调用对象的绑定方法,这个方法就是一个普通函数,有几个参数就要传几个参数
# Response.__init__(data=dic,status=status,headers=headers,content_type=content_type)

@ -0,0 +1,46 @@
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# author: NinEveN
# date: 2022/2/15
import logging
from django.dispatch import Signal, receiver
from api.models import Apps
from xsign.models import AppUDID, APPToDeveloper
from xsign.tasks import run_resign_task
from xsign.utils.supersignutils import IosUtils
logger = logging.getLogger(__name__)
"""
更新应用的时候是否同时更新超级签名数据
"""
run_resign_task_signal = Signal(providing_args=["app_pk"])
@receiver(run_resign_task_signal)
def run_resign_task_callback(sender, **kwargs):
app_obj = Apps.objects.filter(pk=kwargs.get('app_pk')).first()
if app_obj:
AppUDID.objects.filter(app_id=app_obj, sign_status__gte=3).update(sign_status=3)
if app_obj.change_auto_sign:
c_task = run_resign_task(app_obj.pk, False, False)
logger.info(f"app {app_obj} run_resign_task end msg:{c_task}")
"""
删除应用时的信号用户清理签名相关数据
"""
delete_app_signal = Signal(providing_args=["app_pk"])
@receiver(delete_app_signal)
def delete_app_callback(sender, **kwargs):
app_obj = Apps.objects.filter(pk=kwargs.get('app_pk')).first()
if app_obj:
count = APPToDeveloper.objects.filter(app_id=app_obj).count()
if app_obj.issupersign or count > 0:
logger.info(f"app_id:{app_obj.app_id} is supersign ,delete this app need clean IOS developer")
IosUtils.clean_app_by_user_obj(app_obj)

@ -15,8 +15,8 @@ from functools import wraps
import jwt import jwt
import requests import requests
from api.utils.crontab.iproxy import get_proxy_ip_from_cache
from common.core.sysconfig import Config from common.core.sysconfig import Config
from xsign.utils.iproxy import get_proxy_ip_from_cache
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

@ -9,8 +9,8 @@
import logging import logging
import os import os
from api.utils.TokenManager import make_token
from common.libs.storage.aliyunApi import AliYunCdn from common.libs.storage.aliyunApi import AliYunCdn
from common.utils.token import make_token
from fir_ser import settings from fir_ser import settings
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

@ -12,8 +12,7 @@ from django.core.cache import cache
from django.db.models import F from django.db.models import F
from django.utils import timezone from django.utils import timezone
from api.models import Apps, UserInfo, AppReleaseInfo, AppUDID, APPToDeveloper, APPSuperSignUsedInfo, \ from api.models import Apps, UserInfo, AppReleaseInfo, UserCertificationInfo, Order
UserCertificationInfo, Order
from api.utils.modelutils import get_app_d_count_by_app_id, get_app_domain_name, get_user_domain_name, \ from api.utils.modelutils import get_app_d_count_by_app_id, get_app_domain_name, get_user_domain_name, \
add_remote_info_from_request add_remote_info_from_request
from common.base.baseutils import check_app_password, get_order_num, get_real_ip_address from common.base.baseutils import check_app_password, get_order_num, get_real_ip_address
@ -560,9 +559,6 @@ def get_app_download_url(request, res, app_id, short, password, release_id, isdo
add_remote_info_from_request(request, msg) add_remote_info_from_request(request, msg)
set_app_download_by_cache(app_id) set_app_download_by_cache(app_id)
amount = app_obj.get("d_count") amount = app_obj.get("d_count")
# # 超级签需要多消耗2倍下载次数
# if app_obj.get("issupersign"):
# amount *= 2
auth_status = False auth_status = False
status = app_obj.get('user_id__certification__status', None) status = app_obj.get('user_id__certification__status', None)
if status and status == 1: if status and status == 1:

@ -46,6 +46,7 @@ INSTALLED_APPS = [
'django_celery_results', 'django_celery_results',
'corsheaders', 'corsheaders',
'django_filters', 'django_filters',
'xsign.apps.ApiConfig',
] ]
MIDDLEWARE = [ MIDDLEWARE = [
@ -368,7 +369,7 @@ LOGGING = {
'encoding': 'utf-8', 'encoding': 'utf-8',
}, },
'sql': { 'sql': {
'level': 'DEBUG', 'level': 'INFO',
'class': 'logging.handlers.RotatingFileHandler', # 保存到文件,自动切 'class': 'logging.handlers.RotatingFileHandler', # 保存到文件,自动切
'filename': os.path.join(BASE_LOG_DIR, "flyapp_sql.log"), # 日志文件 'filename': os.path.join(BASE_LOG_DIR, "flyapp_sql.log"), # 日志文件
'maxBytes': 1024 * 1024 * 50, # 日志大小 50M 'maxBytes': 1024 * 1024 * 50, # 日志大小 50M

@ -19,12 +19,13 @@ from django.views.static import serve
from admin.views.celery_flower import CeleryFlowerView from admin.views.celery_flower import CeleryFlowerView
from api.views.download import DownloadView, InstallView from api.views.download import DownloadView, InstallView
from api.views.receiveudids import IosUDIDView, ShowUdidView
from fir_ser import settings from fir_ser import settings
from xsign.views.receiveudids import IosUDIDView, ShowUdidView
urlpatterns = [ urlpatterns = [
re_path('fly.admin/', admin.site.urls), re_path('fly.admin/', admin.site.urls),
re_path("api/v1/fir/server/", include('api.urls')), re_path("api/v1/fir/server/", include('api.urls')),
re_path("api/v1/fir/xsign/", include('xsign.urls')),
re_path("api/v2/fir/server/", include('cli.urls')), re_path("api/v2/fir/server/", include('cli.urls')),
re_path("api/v3/fir/server/", include('admin.urls')), re_path("api/v3/fir/server/", include('admin.urls')),
re_path('^captcha/', include('captcha.urls')), re_path('^captcha/', include('captcha.urls')),

@ -0,0 +1,15 @@
from django.contrib import admin
# Register your models here.
from .models import *
admin.site.register(AppIOSDeveloperInfo)
admin.site.register(AppleDeveloperToAppUse)
admin.site.register(APPSuperSignUsedInfo)
admin.site.register(APPToDeveloper)
admin.site.register(AppUDID)
admin.site.register(DeveloperAppID)
admin.site.register(DeveloperDevicesID)
admin.site.register(IosDeveloperBill)
admin.site.register(IosDeveloperPublicPoolBill)
admin.site.register(UDIDsyncDeveloper)

@ -0,0 +1,5 @@
from django.apps import AppConfig
class ApiConfig(AppConfig):
name = 'xsign'

@ -0,0 +1,241 @@
from django.db import models
from api.models import Apps, UserInfo
from common.base.daobase import AESCharField
class AppIOSDeveloperInfo(models.Model):
"""
苹果开发者信息
"""
user_id = models.ForeignKey(to=UserInfo, verbose_name="用户ID", on_delete=models.CASCADE)
issuer_id = models.CharField(max_length=64, null=False, verbose_name="标识创建认证令牌的发放者")
private_key_id = models.CharField(max_length=64, null=False, verbose_name="密钥 ID")
p8key = AESCharField(max_length=512, null=False, verbose_name="p8key")
certid = models.CharField(max_length=64, blank=True, verbose_name="超级签名自动创建证书ID", null=True)
usable_number = models.IntegerField(verbose_name="可使用设备数", default=100)
app_limit_number = models.IntegerField(verbose_name="可分配应用数,最大160", default=100)
created_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
updated_time = models.DateTimeField(auto_now=True, verbose_name="更新时间")
cert_expire_time = models.DateTimeField(blank=True, null=True, verbose_name="证书过期时间")
description = models.TextField('备注', blank=True, null=True, default='')
auth_type_choices = ((0, 'p8key认证'),)
auth_type = models.SmallIntegerField(choices=auth_type_choices, default=0, verbose_name="认证类型")
# 协议待同意和维护中:代表只读,不可创建和注册新设备号
status_choices = ((-1, '疑似被封'), (0, '未激活'), (1, '已激活'), (2, '协议待同意'), (3, '维护中'), (4, '证书过期'), (5, '状态异常'))
status = models.SmallIntegerField(choices=status_choices, verbose_name="账户状态", default=0)
clean_status = models.BooleanField(verbose_name="清理是否同时禁用设备ID", default=False)
auto_check = models.BooleanField(verbose_name="是否自动检测开发者状态", default=False)
class Meta:
verbose_name = '苹果开发者账户'
verbose_name_plural = "苹果开发者账户"
unique_together = (('user_id', 'issuer_id'),)
def save(self, *args, **kwargs):
if self.usable_number > 100:
self.usable_number = 100
elif self.usable_number < 0:
self.usable_number = 0
if self.app_limit_number > 160:
self.app_limit_number = 160
elif self.app_limit_number < 0:
self.app_limit_number = 0
return super(AppIOSDeveloperInfo, self).save(*args, **kwargs)
def __str__(self):
return "%s-%s" % (self.user_id, self.issuer_id)
class UDIDsyncDeveloper(models.Model):
"""
开发者设备ID记录
"""
developerid = models.ForeignKey(to=AppIOSDeveloperInfo, on_delete=models.CASCADE, verbose_name="所使用苹果开发者账户")
udid = models.CharField(max_length=64, verbose_name="udid唯一标识", db_index=True)
product = models.CharField(max_length=64, verbose_name="产品", blank=True, null=True, )
serial = models.CharField(max_length=64, verbose_name="序列号", blank=True, null=True, )
version = models.CharField(max_length=64, verbose_name="型号", blank=True, null=True, )
created_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间"),
status = models.BooleanField(verbose_name="设备在开发者平台状态", default=False)
class Meta:
verbose_name = 'iOS开发平台同步设备信息'
verbose_name_plural = "iOS开发平台同步设备信息"
unique_together = ('udid', 'developerid',)
def __str__(self):
return "%s-%s-%s" % (self.product, self.udid, self.developerid)
class AppUDID(models.Model):
"""
用户发送的设备记录信息
"""
app_id = models.ForeignKey(to=Apps, on_delete=models.CASCADE, verbose_name="属于哪个APP")
udid = models.ForeignKey(to=UDIDsyncDeveloper, verbose_name="udid唯一标识", on_delete=models.CASCADE)
product = models.CharField(max_length=64, verbose_name="产品", blank=True, null=True, )
serial = models.CharField(max_length=64, verbose_name="序列号", blank=True, null=True, )
version = models.CharField(max_length=64, verbose_name="型号", blank=True, null=True, )
imei = models.CharField(max_length=64, verbose_name="型号", blank=True, null=True, )
iccid = models.CharField(max_length=64, verbose_name="型号", blank=True, null=True, )
created_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
updated_time = models.DateTimeField(auto_now=True, verbose_name="更新时间")
sign_status_choices = ((0, '新设备入库准备'), (1, '设备ID已经注册'), (2, 'bundelid已经注册'), (3, '描述文件已经下载'), (4, '已经完成签名打包'))
sign_status = models.SmallIntegerField(choices=sign_status_choices, default=0, verbose_name="签名状态")
class Meta:
verbose_name = '设备详情'
verbose_name_plural = "设备详情"
unique_together = ('app_id', 'udid',)
def __str__(self):
return "%s-%s" % (self.app_id.name, self.udid)
class APPSuperSignUsedInfo(models.Model):
"""
设备消耗统计
"""
user_id = models.ForeignKey(to=UserInfo, verbose_name="用户ID", on_delete=models.CASCADE)
app_id = models.ForeignKey(to=Apps, on_delete=models.CASCADE, verbose_name="属于哪个APP")
udid = models.ForeignKey(to=AppUDID, on_delete=models.CASCADE, verbose_name="所消耗的udid")
developerid = models.ForeignKey(to=AppIOSDeveloperInfo, on_delete=models.CASCADE, verbose_name="所使用苹果开发者账户")
created_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
updated_time = models.DateTimeField(auto_now=True, verbose_name="更新时间")
class Meta:
verbose_name = '设备使用统计'
verbose_name_plural = "设备使用统计"
def __str__(self):
return "%s-%s-%s" % (self.user_id, self.app_id, self.udid)
class APPToDeveloper(models.Model):
"""
签名完成之后通过该表查询 签名包地址
"""
app_id = models.ForeignKey(to=Apps, on_delete=models.CASCADE, verbose_name="属于哪个APP")
developerid = models.ForeignKey(to=AppIOSDeveloperInfo, on_delete=models.CASCADE, verbose_name="所使用苹果开发者账户")
binary_file = models.CharField(max_length=128, blank=True, verbose_name="签名包名称", null=True, unique=True)
release_file = models.CharField(max_length=128, blank=True, verbose_name="源包名称", null=True)
updated_time = models.DateTimeField(auto_now=True, verbose_name="更新时间")
class Meta:
verbose_name = '应用开发者绑定'
verbose_name_plural = "应用开发者绑定"
def __str__(self):
return "%s-%s-%s" % (self.developerid, self.app_id, self.binary_file)
class DeveloperAppID(models.Model):
"""
苹果应用消耗某个开发者某个描述文件对应记录表一个苹果应用对应一个苹果开发者和该开发者下面对应的描述文件
"""
aid = models.CharField(max_length=64, null=False) # ,apple APP 唯一标识
profile_id = models.CharField(max_length=64, null=True, blank=True) # ,profile_id 唯一标识
developerid = models.ForeignKey(to=AppIOSDeveloperInfo, on_delete=models.CASCADE, verbose_name="所使用苹果开发者账户")
app_id = models.ForeignKey(to=Apps, on_delete=models.CASCADE, verbose_name="属于哪个APP")
class Meta:
verbose_name = '超级签APP id'
verbose_name_plural = "超级签APP id"
unique_together = ('aid', 'developerid', 'app_id')
def __str__(self):
return "%s-%s-%s" % (self.aid, self.app_id, self.developerid)
class DeveloperDevicesID(models.Model):
"""
苹果应用消耗某个开发者某个设备对应记录表有一个苹果应用可能有该开发者下面多个设备记录
"""
did = models.CharField(max_length=64, null=False) # ,apple 设备唯一标识
udid = models.ForeignKey(to=UDIDsyncDeveloper, on_delete=models.CASCADE, verbose_name="所消耗的udid")
developerid = models.ForeignKey(to=AppIOSDeveloperInfo, on_delete=models.CASCADE, verbose_name="所使用苹果开发者账户")
app_id = models.ForeignKey(to=Apps, on_delete=models.CASCADE, verbose_name="属于哪个APP")
class Meta:
verbose_name = '超级签Devices id'
verbose_name_plural = "超级签Devices id"
unique_together = ('did', 'developerid', 'app_id')
def __str__(self):
return "%s-%s-%s-%s" % (self.id, self.app_id, self.developerid, self.udid)
class IosDeveloperPublicPoolBill(models.Model):
"""
苹果应用设备消费记录表
"""
user_id = models.ForeignKey(to=UserInfo, verbose_name="用户ID", on_delete=models.CASCADE)
number = models.IntegerField(verbose_name="消耗次数", default=1)
app_info = models.JSONField(max_length=256, verbose_name="属于哪个APP", null=True, blank=True)
udid = models.CharField(max_length=64, verbose_name="设备udid", null=True, blank=True)
product = models.CharField(max_length=64, verbose_name="设备udid", null=True, blank=True)
version = models.CharField(max_length=64, verbose_name="设备udid", null=True, blank=True)
developer_info = models.JSONField(max_length=256, verbose_name="开发者信息", null=True, blank=True)
udid_sync_info = models.ForeignKey(to=UDIDsyncDeveloper, on_delete=models.SET_NULL, verbose_name="关联同步设备信息",
null=True, blank=True)
app_id = models.ForeignKey(to=Apps, on_delete=models.SET_NULL, verbose_name="属于哪个APP", null=True, blank=True)
description = models.CharField(verbose_name="操作描述", max_length=128, default='', blank=True)
remote_addr = models.GenericIPAddressField(verbose_name="远程IP地址")
created_time = models.DateTimeField(auto_now_add=True, verbose_name="添加时间")
class Meta:
verbose_name = '设备消耗账单'
verbose_name_plural = "设备消耗账单"
def __str__(self):
return "%s-%s" % (self.user_id, self.description)
class IosDeveloperBill(models.Model):
"""
用户共享设备数可将该用户设备数共享给 其他用户使用
"""
user_id = models.ForeignKey(to=UserInfo, verbose_name="用户ID", on_delete=models.CASCADE,
related_name='org_user_id')
to_user_id = models.ForeignKey(to=UserInfo, verbose_name="用户ID", on_delete=models.CASCADE,
related_name='to_user_id', null=True, blank=True)
status_choices = ((0, '失效'), (1, '已撤回'), (2, '成功'))
status = models.SmallIntegerField(choices=status_choices, default=0, verbose_name="状态",
help_text="0 失效 1 撤回 2 转账")
number = models.IntegerField(verbose_name="设备数量", default=1)
description = models.CharField(verbose_name="操作描述", max_length=128, default='', blank=True)
remote_addr = models.GenericIPAddressField(verbose_name="远程IP地址")
created_time = models.DateTimeField(auto_now_add=True, verbose_name="添加时间")
updated_time = models.DateTimeField(auto_now=True, verbose_name="更新时间")
class Meta:
verbose_name = '设备划转账单'
verbose_name_plural = "设备划转账单"
def __str__(self):
return "%s-%s%s" % (self.user_id, self.to_user_id, self.description)
class AppleDeveloperToAppUse(models.Model):
"""
苹果应用专属开发者配置表
"""
app_id = models.ForeignKey(to=Apps, on_delete=models.CASCADE)
developerid = models.ForeignKey(to=AppIOSDeveloperInfo, on_delete=models.CASCADE)
usable_number = models.IntegerField(verbose_name="可使用设备数", default=100)
description = models.CharField(verbose_name="备注", max_length=256, default='', blank=True)
created_time = models.DateTimeField(auto_now_add=True, verbose_name="添加时间")
class Meta:
verbose_name = '开发者专属于应用'
verbose_name_plural = "开发者专属于应用"
unique_together = ('app_id', 'developerid')
def __str__(self):
return "%s-%s" % (self.app_id.name, self.developerid.issuer_id)

@ -0,0 +1,80 @@
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# project: 5月
# author: NinEveN
# date: 2021/5/27
import logging
from celery import shared_task
from django.core.cache import cache
from common.cache.state import MigrateStorageState
from fir_ser.celery import app
from xsign.models import Apps, DeveloperAppID
from xsign.utils.ctasks import auto_check_ios_developer_active
from xsign.utils.iproxy import get_best_proxy_ips
from xsign.utils.supersignutils import IosUtils, resign_by_app_id_and_developer
logger = logging.getLogger(__name__)
@shared_task
def run_sign_task(format_udid_info, short, client_ip):
app_obj = Apps.objects.filter(short=short).first()
if MigrateStorageState(app_obj.user_id.uid).get_state():
msg = "数据迁移中,无法处理该操作"
return msg
ios_obj = IosUtils(format_udid_info, app_obj.user_id, app_obj)
status, msg = ios_obj.sign_ipa(client_ip)
if not status:
code = msg.get("code", -1)
if code == 0:
msg = ""
elif code == 1005:
msg = "签名余额不足"
elif code == 1002:
msg = "维护中"
elif code == 1003:
msg = "应用余额不足"
elif code in [1004, 1001, 1009]:
msg = msg.get('msg', '未知错误')
else:
msg = '系统内部错误,请稍后再试或联系管理员'
else:
msg = ""
return msg
def run_resign_task(app_id, need_download_profile=True, force=True):
app_obj = Apps.objects.filter(pk=app_id).first()
if app_obj.issupersign and app_obj.user_id.supersign_active:
with cache.lock("%s_%s" % ('task_resign', app_obj.app_id), timeout=60 * 60):
task_list = []
for developer_app_id_obj in DeveloperAppID.objects.filter(app_id=app_obj).all():
c_task = run_resign_task_do.apply_async((app_id, developer_app_id_obj.developerid.pk,
developer_app_id_obj.aid, need_download_profile, force))
task_list.append(c_task)
for c_task in task_list:
msg = c_task.get(propagate=False)
logger.info(f"app {app_obj} run_resign_task msg:{msg}")
if c_task.successful():
c_task.forget()
return True
@shared_task
def run_resign_task_do(app_id, developer_id, developer_app_id, need_download_profile=True, force=True):
return resign_by_app_id_and_developer(app_id, developer_id, developer_app_id, need_download_profile, force)
@app.task
def auto_check_ios_developer_active_job():
auto_check_ios_developer_active()
@app.task
def get_best_proxy_ips_job():
get_best_proxy_ips()

@ -0,0 +1,34 @@
"""fir_ser URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/3.0/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.urls import re_path
from xsign.views.receiveudids import IosUDIDView, TaskView
from xsign.views.supersign import DeveloperView, SuperSignUsedView, AppUDIDUsedView, SuperSignCertView, \
DeviceUsedBillView, DeveloperDeviceView, DeviceUsedRankInfoView, AppleDeveloperBindAppsView, DeviceTransferBillView
urlpatterns = [
re_path(r"^udid/(?P<short>\w+)$", IosUDIDView.as_view()),
re_path(r"^task/(?P<short>\w+)$", TaskView.as_view()),
re_path(r"^developer$", DeveloperView.as_view()),
re_path(r"^devices$", SuperSignUsedView.as_view()),
re_path(r"^udid$", AppUDIDUsedView.as_view()),
re_path(r"^udevices$", DeveloperDeviceView.as_view()),
re_path(r"^cert$", SuperSignCertView.as_view()),
re_path(r"^bill$", DeviceUsedBillView.as_view()),
re_path(r"^devicebill$", DeviceTransferBillView.as_view()),
re_path(r"^rank$", DeviceUsedRankInfoView.as_view()),
re_path(r"^bind$", AppleDeveloperBindAppsView.as_view()),
]

@ -8,49 +8,15 @@ import logging
import os import os
import time import time
from django.core.cache import cache
from api.models import Apps, UserInfo, AppIOSDeveloperInfo
from api.utils.app.supersignutils import IosUtils
from api.utils.utils import send_ios_developer_active_status from api.utils.utils import send_ios_developer_active_status
from common.core.sysconfig import Config from common.core.sysconfig import Config
from common.utils.storage import Storage from fir_ser.settings import SUPER_SIGN_ROOT, SYNC_CACHE_TO_DATABASE
from fir_ser.settings import CACHE_KEY_TEMPLATE, SYNC_CACHE_TO_DATABASE, SUPER_SIGN_ROOT from xsign.models import UserInfo, AppIOSDeveloperInfo
from xsign.utils.supersignutils import IosUtils
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def sync_download_times():
down_tem_key = CACHE_KEY_TEMPLATE.get("download_times_key")
key = "_".join([down_tem_key, '*'])
for app_download in cache.iter_keys(key):
count_hits = cache.get(app_download)
if count_hits:
app_id = app_download.split(down_tem_key)[1].strip('_')
Apps.objects.filter(app_id=app_id).update(count_hits=count_hits)
logger.info(f"sync_download_times app_id:{app_id} count_hits:{count_hits}")
def auto_clean_upload_tmp_file():
upload_tem_key = CACHE_KEY_TEMPLATE.get("upload_file_tmp_name_key")
key = "_".join([upload_tem_key, '*'])
for upload_tem_file_key in cache.iter_keys(key):
data = cache.get(upload_tem_file_key)
if data:
u_time = data.get("u_time", None)
if u_time and time.time() - u_time > 60 * 20:
user_obj = UserInfo.objects.filter(pk=data.get("id")).first()
if user_obj:
storage = Storage(user_obj)
filename = data.get("filename", None)
if filename:
storage.delete_file(filename)
logger.info(f"auto_clean_upload_tmp_file delete_file :{filename}")
cache.delete(upload_tem_file_key)
logger.info(f"auto_clean_upload_tmp_file upload_tem_file_key :{upload_tem_file_key}")
def auto_delete_ios_mobile_tmp_file(): def auto_delete_ios_mobile_tmp_file():
mobile_config_tmp_dir = os.path.join(SUPER_SIGN_ROOT, 'tmp', 'mobile_config') mobile_config_tmp_dir = os.path.join(SUPER_SIGN_ROOT, 'tmp', 'mobile_config')
for root, dirs, files in os.walk(mobile_config_tmp_dir, topdown=False): for root, dirs, files in os.walk(mobile_config_tmp_dir, topdown=False):

@ -8,31 +8,50 @@ import datetime
import logging import logging
import os import os
import re import re
import signal
import time import time
from subprocess import Popen, PIPE
from OpenSSL.crypto import (load_pkcs12, dump_certificate_request, dump_privatekey, PKey, TYPE_RSA, X509Req, from OpenSSL.crypto import (load_pkcs12, dump_certificate_request, dump_privatekey, PKey, TYPE_RSA, X509Req,
dump_certificate, load_privatekey, load_certificate, PKCS12, FILETYPE_PEM, FILETYPE_ASN1) dump_certificate, load_privatekey, load_certificate, PKCS12, FILETYPE_PEM, FILETYPE_ASN1)
from api.models import AppIOSDeveloperInfo
from api.utils.app.shellcmds import shell_command, use_user_pass
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.core.sysconfig import Config from common.core.sysconfig import Config
from common.libs.apple.appleapiv3 import AppStoreConnectApi from common.libs.apple.appleapiv3 import AppStoreConnectApi
from fir_ser.settings import SUPER_SIGN_ROOT from fir_ser.settings import SUPER_SIGN_ROOT
from xsign.models import AppIOSDeveloperInfo
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def exec_shell(cmd, remote=False, timeout=None): def shell_command(command, timeout):
if remote: result = {'exit_code': 99, 'return_info': ''}
host_ip = "10.66.6.66" shell_start_time = time.time()
port = 65534 child = Popen(command, shell=True, stdout=PIPE, stderr=PIPE)
user = "root" if timeout:
passwd = "root" while child.poll() is None:
result = use_user_pass(host_ip, port, user, passwd, cmd) time.sleep(1)
now = time.time()
if int(now - shell_start_time) > timeout:
os.kill(child.pid, signal.SIGKILL)
os.waitpid(-1, os.WNOHANG)
result['exit_code'] = 126
return result return result
else:
out, err = child.communicate()
if err:
result['err_info'] = err.decode("utf-8")
shell_end_time = time.time()
result['shell_run_time'] = shell_end_time - shell_start_time
out = out.strip(b'\n')
result['return_info'] = out
result['exit_code'] = child.returncode
logger.info(f'shell: {command} - return_info:{out} - exit_code:{child.returncode}')
return result
def exec_shell(cmd, timeout=None):
logger.info(f"exec_shell cmd:{cmd}") logger.info(f"exec_shell cmd:{cmd}")
result = shell_command(cmd, timeout) result = shell_command(cmd, timeout)
logger.info(f"exec_shell cmd:{cmd} result:{result}") logger.info(f"exec_shell cmd:{cmd} result:{result}")

@ -0,0 +1,165 @@
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# project: 4月
# author: NinEveN
# date: 2021/4/16
import logging
from django.db.models import Count, Sum, Q
from api.models import AppReleaseInfo, UserInfo
from common.base.baseutils import is_valid_phone
from common.core.sysconfig import Config
from xsign.models import APPSuperSignUsedInfo, UDIDsyncDeveloper, AppUDID, APPToDeveloper, AppIOSDeveloperInfo, \
IosDeveloperPublicPoolBill, IosDeveloperBill, DeveloperDevicesID, AppleDeveloperToAppUse, DeveloperAppID
logger = logging.getLogger(__name__)
def get_ios_developer_public_num(user_obj):
add_number = get_user_public_sign_num(user_obj)
if not add_number:
add_number = -99
used_number = get_user_public_used_sign_num(user_obj)
return add_number - used_number
def get_user_public_sign_num(user_obj):
add_number = IosDeveloperBill.objects.filter(to_user_id=user_obj, status=2).aggregate(
number=Sum('number'))
number = add_number.get("number", 0)
return number if number else 0
def get_user_public_used_sign_num(user_obj):
used_number = IosDeveloperPublicPoolBill.objects.filter(user_id=user_obj, udid_sync_info__isnull=False).exclude(
udid_sync_info__developerid__user_id=user_obj).values('number',
'udid_sync_info_id').annotate(
counts=Count('udid_sync_info_id')).aggregate(number=Sum('number'))
number = used_number.get("number", 0)
return number if number else 0
# def get_developer_can_used_from_public_sign(user_obj):
# o_number_info = IosDeveloperBill.objects.filter(to_user_id__isnull=False, user_id=user_obj).values(
# 'number').aggregate(number=Sum('number'))
# o_number = o_number_info.get("number", 0)
# if o_number is None:
# o_number = 0
# u_number_info = IosDeveloperPublicPoolBill.objects.filter(
# user_id_id__in=IosDeveloperBill.objects.filter(user_id=user_obj).values('to_user_id_id')).values(
# 'number',
# 'udid_sync_info_id').annotate(
# counts=Count('udid_sync_info_id')).aggregate(number=Sum('number'))
# u_number = u_number_info.get("number", 0)
# if u_number is None:
# u_number = 0
# return o_number - u_number
def check_super_sign_permission(user_obj):
if not user_obj.supersign_active:
return False
developer_count = AppIOSDeveloperInfo.objects.filter(user_id=user_obj).count()
if developer_count == 0 and get_ios_developer_public_num(user_obj) < 0:
return False
return True
def check_ipa_is_latest_sign(app_obj, developer_obj=None):
if AppUDID.objects.filter(app_id=app_obj, udid__developerid=developer_obj, sign_status__lt=4).first():
return
release_obj = AppReleaseInfo.objects.filter(app_id=app_obj, is_master=True).first()
all_app_to_dev = APPToDeveloper.objects.filter(app_id=app_obj)
if developer_obj:
all_app_to_dev = all_app_to_dev.filter(developerid=developer_obj)
all_app_to_dev = all_app_to_dev.all()
t_count = 0
for apptodev_obj in all_app_to_dev:
if release_obj.release_id == apptodev_obj.release_file:
t_count += 1
if t_count == all_app_to_dev.count():
return True
def get_developer_udided(developer_obj):
super_sing_used_obj = APPSuperSignUsedInfo.objects.filter(developerid=developer_obj).all()
udid_sync_developer_obj = UDIDsyncDeveloper.objects.filter(developerid=developer_obj).all()
developer_udid_lists = []
super_sign_udid_lists = []
if udid_sync_developer_obj:
developer_udid_lists = list(udid_sync_developer_obj.values_list("udid"))
if super_sing_used_obj:
super_sign_udid_lists = list(super_sing_used_obj.values_list("udid__udid__udid"))
return len(set(developer_udid_lists) - set(super_sign_udid_lists)), len(set(super_sign_udid_lists)), len(
set(developer_udid_lists))
def update_or_create_developer_udid_info(device_obj, developer_obj):
device = {
"serial": device_obj.id,
"product": device_obj.name,
"udid": device_obj.udid,
"version": device_obj.model,
"status": True if device_obj.status == 'ENABLED' else False
}
return UDIDsyncDeveloper.objects.update_or_create(developerid=developer_obj, udid=device_obj.udid, defaults=device)
def check_uid_has_relevant(user_uid, to_user_uid):
if user_uid and to_user_uid:
return IosDeveloperBill.objects.filter(user_id__uid=user_uid, to_user_id__uid=to_user_uid, status=2).first()
def usable_number(developer_obj):
d_count = UDIDsyncDeveloper.objects.filter(developerid=developer_obj).count()
u_count = developer_obj.usable_number
return d_count if d_count > u_count else u_count
def get_developer_devices(developer_obj_lists):
other_used_sum = 0
flyapp_used_sum = 0
max_total = 0
for dev_obj in developer_obj_lists.filter(status__in=Config.DEVELOPER_USE_STATUS):
other_used, flyapp_used, _ = get_developer_udided(dev_obj)
other_used_sum += other_used
flyapp_used_sum += flyapp_used
max_total += 100
use_number_obj_list = developer_obj_lists.filter(status__in=Config.DEVELOPER_USE_STATUS)
all_use_number = 0
all_usable_number = 0
for use_number_obj in use_number_obj_list:
all_usable_number += usable_number(use_number_obj)
all_use_number += DeveloperDevicesID.objects.filter(developerid=use_number_obj).values(
'udid').distinct().count()
use_num = {
"all_usable_number": all_usable_number,
"all_use_number": all_use_number,
"other_used_sum": other_used_sum,
"flyapp_used_sum": flyapp_used_sum,
"max_total": max_total
}
return use_num
def get_user_obj_from_epu(user_id):
if is_valid_phone(user_id):
user_obj = UserInfo.objects.filter(mobile=user_id).first()
else:
user_obj = UserInfo.objects.filter(Q(email=user_id) | Q(uid=user_id)).first()
return user_obj
def get_app_sign_info(app_obj):
return {
'count': APPToDeveloper.objects.filter(app_id=app_obj).count(),
'private_developer_number': AppleDeveloperToAppUse.objects.filter(app_id=app_obj).count(),
'supersign_used_number': APPSuperSignUsedInfo.objects.filter(app_id=app_obj).all().count(),
'developer_used_count': DeveloperAppID.objects.filter(app_id=app_obj).all().count(),
'private_developer_used_number': DeveloperDevicesID.objects.filter(app_id=app_obj,
developerid__appledevelopertoappuse__app_id=app_obj).distinct().count()
}

@ -0,0 +1,363 @@
import logging
from django.db.models import Sum
from rest_framework import serializers
from common.base.baseutils import AppleDeveloperUid, get_choices_dict
from common.core.sysconfig import Config
from xsign import models
from xsign.utils.modelutils import get_developer_udided
logger = logging.getLogger(__name__)
class DeveloperSerializer(serializers.ModelSerializer):
class Meta:
model = models.AppIOSDeveloperInfo
# depth = 1
exclude = ["id", "user_id", "p8key"]
developer_used_number = serializers.SerializerMethodField()
developer_used_other_number = serializers.SerializerMethodField()
use_number = serializers.SerializerMethodField()
app_used_count = serializers.SerializerMethodField()
is_disabled = serializers.SerializerMethodField()
app_private_number = serializers.SerializerMethodField()
app_private_used_number = serializers.SerializerMethodField()
app_usable_number = serializers.SerializerMethodField()
app_used_number = serializers.SerializerMethodField()
private_usable_number = serializers.SerializerMethodField()
status_display = serializers.CharField(source="get_status_display")
def get_app_usable_number(self, obj):
app_id = self.context.get('app_id')
if app_id:
apple_to_app_obj = models.AppleDeveloperToAppUse.objects.filter(developerid=obj,
app_id__app_id=app_id).first()
if apple_to_app_obj:
return apple_to_app_obj.usable_number
return 0
def get_app_used_number(self, obj):
app_id = self.context.get('app_id', '')
if app_id:
return models.DeveloperDevicesID.objects.filter(developerid=obj,
app_id__app_id=app_id).distinct().count()
return 0
def get_developer_used_number(self, obj):
return models.UDIDsyncDeveloper.objects.filter(developerid=obj).count()
def get_developer_used_other_number(self, obj):
return get_developer_udided(obj)[0]
def get_is_disabled(self, obj):
app_id = self.context.get('app_id', '')
if app_id:
developer_app_obj = models.DeveloperAppID.objects.filter(developerid=obj,
developerid__status__in=Config.DEVELOPER_USE_STATUS,
developerid__certid__isnull=False)
if developer_app_obj.filter(app_id__app_id=app_id).distinct().count():
return False
else:
if developer_app_obj and developer_app_obj.distinct().count() < obj.app_limit_number:
return False
return True
return True
def get_app_private_number(self, obj):
return models.AppleDeveloperToAppUse.objects.filter(developerid=obj).count()
def get_private_usable_number(self, obj):
used_number = models.AppleDeveloperToAppUse.objects.filter(developerid=obj).values('usable_number').aggregate(
Sum('usable_number')).get('usable_number__sum')
if not used_number:
used_number = 0
return used_number
def get_app_private_used_number(self, obj):
return models.DeveloperDevicesID.objects.filter(developerid=obj,
app_id__appledevelopertoappuse__developerid=obj).values(
'udid').distinct().count()
def get_use_number(self, obj):
return models.DeveloperDevicesID.objects.filter(developerid=obj).values('udid').distinct().count()
def get_app_used_count(self, obj):
return models.DeveloperAppID.objects.filter(developerid=obj).count()
class SuperSignUsedSerializer(serializers.ModelSerializer):
class Meta:
model = models.APPSuperSignUsedInfo
fields = ["created_time", "device_udid", "device_name", "developer_id", "bundle_id", "bundle_name", "other_uid",
"developer_description", "developer_status"]
device_udid = serializers.CharField(source="udid.udid.udid")
device_name = serializers.CharField(source="udid.product")
bundle_id = serializers.CharField(source="app_id.bundle_id")
bundle_name = serializers.CharField(source="app_id.name")
other_uid = serializers.SerializerMethodField()
developer_id = serializers.SerializerMethodField()
developer_description = serializers.SerializerMethodField()
developer_status = serializers.SerializerMethodField()
def get_developer_status(self, obj):
return obj.developerid.get_status_display()
def get_developer_id(self, obj):
issuer_id = obj.developerid.issuer_id
if self.context.get('mine'):
return issuer_id
else:
return f"{Config.DEVELOPER_UID_KEY}{AppleDeveloperUid().get_encrypt_uid(issuer_id)}"
def get_developer_description(self, obj):
if self.context.get('mine'):
return obj.developerid.description
else:
return '公共账号池'
def get_other_uid(self, obj):
user_obj = self.context.get('user_obj')
if obj.user_id != obj.developerid.user_id:
if user_obj.uid != obj.user_id.uid:
return {'uid': obj.user_id.uid, 'name': obj.user_id.first_name}
class DeveloperDeviceSerializer(serializers.ModelSerializer):
class Meta:
model = models.UDIDsyncDeveloper
exclude = ["id", "developerid"]
developer_id = serializers.CharField(source="developerid.issuer_id")
developer_description = serializers.CharField(source="developerid.description")
developer_status = serializers.CharField(source="developerid.get_status_display")
app_used_count = serializers.SerializerMethodField()
def get_app_used_count(self, obj):
return models.DeveloperDevicesID.objects.filter(udid=obj, developerid=obj.developerid).values(
'app_id').distinct().count()
class DeviceUDIDSerializer(serializers.ModelSerializer):
class Meta:
model = models.AppUDID
# depth = 1
exclude = ["updated_time", "sign_status"]
bundle_name = serializers.CharField(source="app_id.name")
bundle_id = serializers.CharField(source="app_id.bundle_id")
issuer_id = serializers.SerializerMethodField()
developer_description = serializers.SerializerMethodField()
developer_status = serializers.SerializerMethodField()
udid = serializers.CharField(source="udid.udid")
is_mine = serializers.SerializerMethodField()
other_uid = serializers.SerializerMethodField()
def get_issuer_id(self, obj):
issuer_id = obj.udid.developerid.issuer_id
if self.context.get('mine'):
return issuer_id
else:
return f"{Config.DEVELOPER_UID_KEY}{AppleDeveloperUid().get_encrypt_uid(issuer_id)}"
def get_developer_status(self, obj):
return obj.udid.developerid.get_status_display()
def get_developer_description(self, obj):
if self.context.get('mine'):
return obj.udid.developerid.description
else:
return '公共账号池'
def get_other_uid(self, obj):
user_obj = self.context.get('user_obj')
user_obj_new = obj.app_id.user_id
if user_obj.pk != user_obj_new.pk:
super_user_obj = models.APPSuperSignUsedInfo.objects.filter(udid=obj, app_id=obj.app_id).first()
if super_user_obj.developerid.user_id != user_obj_new:
return {'uid': user_obj_new.uid, 'name': user_obj_new.first_name}
def get_is_mine(self, obj):
return self.context.get('mine')
class BillAppInfoSerializer(serializers.ModelSerializer):
class Meta:
model = models.Apps
fields = ["pk", "app_id", "user_id", "name", "type", "bundle_id", "updated_time"]
class BillDeveloperInfoSerializer(serializers.ModelSerializer):
class Meta:
model = models.AppIOSDeveloperInfo
fields = ["pk", "issuer_id", "user_id", "certid"]
class BillInfoSerializer(serializers.ModelSerializer):
class Meta:
model = models.IosDeveloperPublicPoolBill
exclude = ["user_id", "developer_info", "app_info", "udid_sync_info", "app_id"]
app_name = serializers.SerializerMethodField()
description = serializers.SerializerMethodField()
is_used = serializers.SerializerMethodField()
app_status = serializers.SerializerMethodField()
remote_addr = serializers.SerializerMethodField()
def get_remote_addr(self, obj):
return obj.remote_addr
def get_app_status(self, obj):
if obj.app_id:
return True
return False
def get_is_used(self, obj):
if obj.udid_sync_info:
return True
return False
def get_app_name(self, obj):
if obj.app_info:
return obj.app_info.get('name')
def get_description(self, obj):
if obj.udid:
return f"{self.get_app_name(obj)}-消耗- {obj.number} 设备数"
class BillTransferSerializer(serializers.ModelSerializer):
class Meta:
model = models.IosDeveloperBill
exclude = ["id", "remote_addr", "user_id", "to_user_id"]
target_user = serializers.SerializerMethodField()
cancel = serializers.SerializerMethodField()
number = serializers.SerializerMethodField()
status_display = serializers.CharField(source='get_status_display')
def get_target_user(self, obj):
user_obj = obj.user_id
if self.get_cancel(obj):
user_obj = obj.to_user_id
return {'uid': user_obj.uid, 'name': user_obj.first_name}
def get_cancel(self, obj):
return self.context.get('user_obj').pk == obj.user_id.pk
def get_number(self, obj):
if self.get_cancel(obj):
return -obj.number
else:
return obj.number
class AppleDeveloperToAppUseSerializer(serializers.ModelSerializer):
class Meta:
model = models.AppleDeveloperToAppUse
# exclude = ["user_id", "to_user_id", "developer_info", "app_info", "udid_sync_info", "app_id"]
fields = ["issuer_id", "app_id", "created_time", "description", "usable_number"]
issuer_id = serializers.CharField(source="developerid.issuer_id")
app_id = serializers.CharField(source="app_id.app_id")
class AppleDeveloperToAppUseAppsSerializer(serializers.ModelSerializer):
class Meta:
model = models.Apps
fields = ["app_id", "name", "short", "bundle_id", "description", "app_usable_number", "app_used_number"]
app_usable_number = serializers.SerializerMethodField()
app_used_number = serializers.SerializerMethodField()
def get_app_usable_number(self, obj):
issuer_id = self.context.get('issuer_id')
if issuer_id:
apple_to_app_obj = models.AppleDeveloperToAppUse.objects.filter(developerid__issuer_id=issuer_id,
app_id=obj).first()
if apple_to_app_obj:
return apple_to_app_obj.usable_number
return 0
def get_app_used_number(self, obj):
issuer_id = self.context.get('issuer_id')
if issuer_id:
return models.DeveloperDevicesID.objects.filter(app_id=obj, developerid__issuer_id=issuer_id).count()
return 0
class AdminDeveloperSerializer(DeveloperSerializer):
class Meta:
model = models.AppIOSDeveloperInfo
# depth = 1
exclude = ["p8key", ]
auth_type_choices = serializers.SerializerMethodField()
def get_auth_type_choices(self, obj):
return get_choices_dict(obj.auth_type_choices)
status_choices = serializers.SerializerMethodField()
def get_status_choices(self, obj):
return get_choices_dict(obj.status_choices)
class AdminSuperSignUsedSerializer(SuperSignUsedSerializer):
class Meta:
model = models.APPSuperSignUsedInfo
fields = ["created_time", "device_udid", "device_name", "developer_id", "bundle_id", "bundle_name", "app_id",
"id", "user_id", "short", "developer_pk"]
app_id = serializers.IntegerField(source="app_id.pk")
user_id = serializers.IntegerField(source="user_id.pk")
short = serializers.CharField(source="app_id.short")
developer_status = serializers.CharField(source="developerid.get_status_display")
developer_id = serializers.CharField(source="developerid.issuer_id")
developer_description = serializers.CharField(source="developerid.description")
developer_pk = serializers.IntegerField(source="developerid.pk")
class AdminBillInfoSerializer(BillInfoSerializer):
class Meta:
model = models.IosDeveloperPublicPoolBill
fields = '__all__'
read_only_fields = ["id", "user_id", "to_user_id", "action", "number", "app_info", "udid",
"udid_sync_info", "app_id", "remote_addr"]
action_choices = serializers.SerializerMethodField()
def get_action_choices(self, obj):
return get_choices_dict(obj.action_choices)
class AppSignSerializer(serializers.ModelSerializer):
class Meta:
model = models.Apps
exclude = ["user_id", "id"]
private_developer_number = serializers.SerializerMethodField()
def get_private_developer_number(self, obj):
return models.AppleDeveloperToAppUse.objects.filter(app_id=obj).count()
private_developer_used_number = serializers.SerializerMethodField()
def get_private_developer_used_number(self, obj):
return models.DeveloperDevicesID.objects.filter(app_id=obj,
developerid__appledevelopertoappuse__app_id=obj).distinct().count()
supersign_used_number = serializers.SerializerMethodField()
def get_supersign_used_number(self, obj):
return models.APPSuperSignUsedInfo.objects.filter(app_id=obj).all().count()
developer_used_count = serializers.SerializerMethodField()
def get_developer_used_count(self, obj):
return models.DeveloperAppID.objects.filter(app_id=obj).all().count()

@ -15,16 +15,10 @@ import xmltodict
from django.core.cache import cache from django.core.cache import cache
from django.db.models import Count, F from django.db.models import Count, F
from api.models import APPSuperSignUsedInfo, AppUDID, AppIOSDeveloperInfo, AppReleaseInfo, Apps, APPToDeveloper, \ from api.models import UserInfo, AppReleaseInfo, Apps
UDIDsyncDeveloper, DeveloperAppID, DeveloperDevicesID, IosDeveloperPublicPoolBill, UserInfo, AppleDeveloperToAppUse, \
IosDeveloperBill
from api.utils.app.iossignapi import ResignApp, AppDeveloperApiV2
from api.utils.modelutils import get_ios_developer_public_num, check_ipa_is_latest_sign, \
update_or_create_developer_udid_info, check_uid_has_relevant
from api.utils.response import BaseResponse from api.utils.response import BaseResponse
from api.utils.serializer import BillAppInfoSerializer, BillDeveloperInfoSerializer from api.utils.utils import send_ios_developer_active_status, delete_local_files, \
from api.utils.utils import delete_app_to_dev_and_file, send_ios_developer_active_status, delete_local_files, \ download_files_form_oss
download_files_form_oss, get_developer_udided
from common.base.baseutils import file_format_path, delete_app_profile_file, get_profile_full_path, format_apple_date, \ from common.base.baseutils import file_format_path, delete_app_profile_file, get_profile_full_path, format_apple_date, \
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 from common.base.magic import run_function_by_locker, call_function_try_attempts
@ -34,6 +28,14 @@ from common.utils.caches import del_cache_response_by_short, send_msg_over_limit
consume_user_download_times_by_app_obj, add_udid_cache_queue, get_and_clean_udid_cache_queue consume_user_download_times_by_app_obj, add_udid_cache_queue, get_and_clean_udid_cache_queue
from common.utils.storage import Storage from common.utils.storage import Storage
from fir_ser.settings import SUPER_SIGN_ROOT, MEDIA_ROOT from fir_ser.settings import SUPER_SIGN_ROOT, MEDIA_ROOT
from xsign.models import APPSuperSignUsedInfo, AppUDID, AppIOSDeveloperInfo, APPToDeveloper, \
UDIDsyncDeveloper, DeveloperAppID, DeveloperDevicesID, IosDeveloperPublicPoolBill, AppleDeveloperToAppUse, \
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
from xsign.utils.serializer import BillAppInfoSerializer, BillDeveloperInfoSerializer
from xsign.utils.utils import delete_app_to_dev_and_file
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

@ -0,0 +1,73 @@
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# project: 5月
# author: liuyu
# date: 2020/5/7
import logging
from api.utils.utils import delete_local_files
from common.utils.storage import Storage
from xsign.models import APPToDeveloper
logger = logging.getLogger(__name__)
def delete_app_to_dev_and_file(developer_obj, app_id):
app_to_developer_obj = APPToDeveloper.objects.filter(developerid=developer_obj, app_id_id=app_id)
if app_to_developer_obj:
binary_file = app_to_developer_obj.first().binary_file + ".ipa"
delete_local_files(binary_file)
storage = Storage(developer_obj.user_id)
storage.delete_file(binary_file)
app_to_developer_obj.delete()
def make_resigned(bin_url, img_url, bundle_id, app_version, name):
ios_plist_tem = """<?xml version="1.0" encoding="UTF-8"?>
<plist version="1.0"><dict>
<key>items</key>
<array>
<dict>
<key>assets</key>
<array>
<dict>
<key>kind</key>
<string>software-package</string>
<key>url</key>
<string><![CDATA[{bin_url}]]></string>
</dict>
<dict>
<key>kind</key>
<string>display-image</string>
<key>needs-shine</key>
<integer>0</integer>
<key>url</key>
<string><![CDATA[{img_url}]]></string>
</dict>
<dict>
<key>kind</key>
<string>full-size-image</string>
<key>needs-shine</key>
<true/>
<key>url</key>
<string><![CDATA[{img_url}]]></string>
</dict>
</array>
<key>metadata</key>
<dict>
<key>bundle-identifier</key>
<string>{bundle_id}</string>
<key>bundle-version</key>
<string><![CDATA[{app_version}]]></string>
<key>kind</key>
<string>software</string>
<key>title</key>
<string><![CDATA[{name}]]></string>
</dict>
</dict>
</array>
</dict>
</plist>""".format(bin_url=bin_url, img_url=img_url, bundle_id=bundle_id, app_version=app_version, name=name)
logger.info(
f"make_resigned bin_url {bin_url} ,img_url {img_url}, bundle_id {bundle_id}, app_version {app_version}, name {name}")
return ios_plist_tem

@ -11,13 +11,14 @@ from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.filters import OrderingFilter from rest_framework.filters import OrderingFilter
from rest_framework.views import APIView from rest_framework.views import APIView
from admin.utils.serializer import AdminDeveloperSerializer, AdminSuperSignUsedSerializer, AdminBillInfoSerializer from admin.utils.utils import AppsPageNumber, BaseModelSet
from admin.utils.utils import AppsPageNumber, BaseModelSet, ApiResponse
from api.models import APPSuperSignUsedInfo, AppIOSDeveloperInfo, IosDeveloperPublicPoolBill, IosDeveloperBill
from api.utils.modelutils import get_user_public_used_sign_num, get_user_public_sign_num, get_user_obj_from_epu
from api.utils.utils import get_developer_devices
from common.base.baseutils import get_real_ip_address from common.base.baseutils import get_real_ip_address
from common.core.auth import AdminTokenAuthentication from common.core.auth import AdminTokenAuthentication
from common.core.response import ApiResponse
from xsign.models import APPSuperSignUsedInfo, AppIOSDeveloperInfo, IosDeveloperPublicPoolBill, IosDeveloperBill
from xsign.utils.modelutils import get_user_public_used_sign_num, get_user_public_sign_num, get_developer_devices, \
get_user_obj_from_epu
from xsign.utils.serializer import AdminDeveloperSerializer, AdminSuperSignUsedSerializer, AdminBillInfoSerializer
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

@ -0,0 +1,151 @@
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# project: 3月
# author: liuyu
# date: 2020/3/6
import logging
from rest_framework.response import Response
from rest_framework.views import APIView
from api.models import Apps
from api.utils.response import BaseResponse
from common.cache.state import MigrateStorageState, CleanAppSignDataState
from common.core.auth import ExpiringTokenAuthentication
from common.core.sysconfig import Config
from common.utils.caches import del_cache_response_by_short
from xsign.models import AppUDID
from xsign.tasks import run_resign_task
from xsign.utils.modelutils import get_app_sign_info, check_super_sign_permission
from xsign.utils.supersignutils import IosUtils
logger = logging.getLogger(__name__)
class AppSignInfoView(APIView):
authentication_classes = [ExpiringTokenAuthentication, ]
def get(self, request, app_id):
res = BaseResponse()
if app_id:
app_obj = Apps.objects.filter(user_id=request.user, app_id=app_id).first()
if app_obj:
res.data = get_app_sign_info(app_obj)
else:
logger.error(f"app_id:{app_id} is not found in user:{request.user}")
res.msg = "未找到该应用"
res.code = 1003
return Response(res.dict)
def put(self, request, app_id):
res = BaseResponse()
if app_id:
data = request.data
if MigrateStorageState(request.user.uid).get_state():
res.code = 1008
res.msg = "数据迁移中,无法处理该操作"
return Response(res.dict)
clean = data.get("clean", None)
if clean:
with CleanAppSignDataState(request.user.uid) as state:
if state:
logger.info(f"app_id:{app_id} clean:{clean} ,close super_sign should clean_app_by_user_obj")
app_obj = Apps.objects.filter(user_id=request.user, app_id=app_id).first()
IosUtils.clean_app_by_user_obj(app_obj)
else:
res.code = 1008
res.msg = "数据清理中,请耐心等待"
return Response(res.dict)
has_combo = data.get("has_combo", None)
if has_combo:
...
else:
try:
do_sign_flag = 0
app_obj = Apps.objects.filter(user_id=request.user, app_id=app_id).first()
logger.info("app_id:%s update old data:%s" % (app_id, app_obj.__dict__))
update_fields = []
if app_obj.issupersign:
app_obj.supersign_limit_number = data.get("supersign_limit_number",
app_obj.supersign_limit_number)
update_fields.append("supersign_limit_number")
if app_obj.supersign_type in [x[0] for x in list(app_obj.supersign_type_choices)]:
if app_obj.supersign_type != data.get("supersign_type", app_obj.supersign_type):
do_sign_flag = 1
app_obj.supersign_type = data.get("supersign_type", app_obj.supersign_type)
update_fields.append("supersign_type")
new_bundle_id = data.get("new_bundle_id", None)
new_bundle_name = data.get("new_bundle_name", None)
if new_bundle_id is not None:
if new_bundle_id and new_bundle_id != app_obj.bundle_id and len(new_bundle_id) > 3:
if new_bundle_id != app_obj.new_bundle_id:
do_sign_flag = 2
app_obj.new_bundle_id = new_bundle_id
if new_bundle_id == '' or (app_obj.new_bundle_id != new_bundle_id):
if app_obj.bundle_id != app_obj.new_bundle_id:
do_sign_flag = 2
app_obj.new_bundle_id = app_obj.bundle_id
update_fields.append('new_bundle_id')
if new_bundle_name is not None:
if new_bundle_name and new_bundle_name != app_obj.name and len(new_bundle_name) > 0:
if new_bundle_name != app_obj.new_bundle_name:
do_sign_flag = 2
app_obj.new_bundle_name = new_bundle_name
if new_bundle_name == '' or (app_obj.new_bundle_name != new_bundle_name):
if app_obj.name != app_obj.new_bundle_name:
do_sign_flag = 2
app_obj.new_bundle_name = app_obj.name
update_fields.append('new_bundle_name')
if app_obj.type == 1 and data.get('issupersign', -1) != -1:
if data.get('issupersign', -1) == 1 and not check_super_sign_permission(request.user):
logger.error(f"app_id:{app_id} can't open super_sign,owner has no ios developer")
res.code = 1008
res.msg = "超级签余额不足,无法开启"
return Response(res.dict)
do_sign_flag = 3
app_obj.issupersign = data.get("issupersign", app_obj.issupersign)
update_fields.append("issupersign")
if app_obj.issupersign and data.get('change_auto_sign', -1) != -1:
if data.get('change_auto_sign', -1) == 1:
do_sign_flag = 3
app_obj.change_auto_sign = data.get("change_auto_sign", app_obj.change_auto_sign)
update_fields.append("change_auto_sign")
logger.info(f"app_id:{app_id} update new data:{app_obj.__dict__}")
app_obj.save(update_fields=update_fields)
if app_obj.issupersign:
c_task = None
if do_sign_flag == 1:
AppUDID.objects.filter(app_id=app_obj).update(sign_status=2)
if app_obj.change_auto_sign:
c_task = run_resign_task(app_obj.pk, True)
if do_sign_flag == 2:
AppUDID.objects.filter(app_id=app_obj, sign_status__gte=3).update(sign_status=3)
if app_obj.change_auto_sign:
flag = False
if AppUDID.objects.filter(app_id=app_obj, sign_status=2,
udid__developerid__status__in=Config.DEVELOPER_WRITE_STATUS).first():
flag = True
c_task = run_resign_task(app_obj.pk, flag)
if do_sign_flag == 3:
if app_obj.change_auto_sign:
flag = False
if AppUDID.objects.filter(app_id=app_obj, sign_status=2,
udid__developerid__status__in=Config.DEVELOPER_WRITE_STATUS).first():
flag = True
c_task = run_resign_task(app_obj.pk, flag, False)
if c_task:
logger.info(f"app {app_obj} run_resign_task msg:{c_task}")
del_cache_response_by_short(app_obj.app_id)
except Exception as e:
logger.error(f"app_id:{app_id} update Exception:{e}")
res.code = 1005
res.msg = "短连接已经存在"
return Response(res.dict)

@ -0,0 +1,106 @@
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# project: 3月
# author: liuyu
# date: 2020/3/6
import logging
import os
from rest_framework.views import APIView
from api.models import AppReleaseInfo
from common.base.baseutils import get_profile_full_path, make_random_uuid, get_server_domain_from_request
from common.core.response import ApiResponse, file_response, mobileprovision_file_response
from common.core.sysconfig import Config
from common.utils.storage import Storage, get_local_storage
from common.utils.token import verify_token
from fir_ser import settings
from xsign.models import APPToDeveloper, APPSuperSignUsedInfo
from xsign.utils.supersignutils import make_sign_udid_mobile_config
from xsign.utils.utils import make_resigned
logger = logging.getLogger(__name__)
def get_post_udid_url(request, short):
server_domain = get_server_domain_from_request(request, Config.POST_UDID_DOMAIN)
path_info_lists = [server_domain, "udid", short]
return "/".join(path_info_lists)
class DownloadView(APIView):
"""
文件下载接口,适用于本地存储和所有plist文件下载
"""
def get(self, request, filename):
down_token = request.query_params.get(settings.DATA_DOWNLOAD_KEY, None)
f_type = filename.split(".")[-1]
flag = True
storage_obj = get_local_storage()
if storage_obj.download_auth_type == 1:
if not down_token:
logger.error(f"file {filename} download failed lost down_token")
return ApiResponse(code=1004, msg="缺失token")
flag = verify_token(down_token, filename)
if flag:
if f_type == 'plist':
release_id = filename.split('.')[0]
app_to_developer_obj = APPToDeveloper.objects.filter(binary_file=release_id).first()
if app_to_developer_obj:
release_obj = AppReleaseInfo.objects.filter(is_master=True,
app_id=app_to_developer_obj.app_id).first()
else:
release_obj = AppReleaseInfo.objects.filter(release_id=release_id).first()
if release_obj:
app_obj = release_obj.app_id
storage = Storage(app_obj.user_id)
bundle_id = app_obj.bundle_id
app_version = release_obj.app_version
name = app_obj.name
ios_plist_bytes = make_resigned(storage.get_download_url(filename.split('.')[0] + ".ipa"),
storage.get_download_url(release_obj.icon_url), bundle_id,
app_version, name)
return file_response(ios_plist_bytes, make_random_uuid(), "application/x-plist")
return ApiResponse(code=1004, msg="plist release_id error")
elif f_type == 'mobileconifg':
release_obj = AppReleaseInfo.objects.filter(release_id=filename.split('.')[0]).first()
if release_obj:
app_obj = release_obj.app_id
udid_url = get_post_udid_url(request, app_obj.short)
ios_udid_mobile_config = make_sign_udid_mobile_config(udid_url, f'{app_obj.app_id}_{app_obj.short}',
app_obj.bundle_id,
app_obj.name)
return file_response(ios_udid_mobile_config, make_random_uuid() + '.mobileconfig',
"application/x-apple-aspen-config")
return ApiResponse(code=1004, msg="mobile_config release_id error")
elif f_type == 'mobileprovision':
release_obj = AppReleaseInfo.objects.filter(release_id=filename.split('.')[0]).first()
if release_obj:
app_super_obj = APPSuperSignUsedInfo.objects.filter(app_id=release_obj.app_id).last()
if not app_super_obj:
app_super_obj = APPSuperSignUsedInfo.objects.last()
if not app_super_obj:
file_path = Config.DEFAULT_MOBILEPROVISION.get("supersign").get('path')
if file_path and os.path.isfile(file_path):
return mobileprovision_file_response(file_path)
else:
developer_obj = app_super_obj.developerid
file_path = get_profile_full_path(developer_obj, release_obj.app_id)
if os.path.isfile(file_path):
return mobileprovision_file_response(file_path)
else:
file_path = Config.DEFAULT_MOBILEPROVISION.get("supersign").get('path')
if file_path and os.path.isfile(file_path):
return mobileprovision_file_response(file_path)
return ApiResponse(code=1004, msg="mobile_provision release_id error")
else:
logger.warning("super sing error file type")
logger.error(f"file {filename} download failed. down_token verify failed")
return ApiResponse(code=1004, msg="token校验失败")

@ -12,8 +12,6 @@ from rest_framework.response import Response
from rest_framework.views import APIView from rest_framework.views import APIView
from api.models import Apps from api.models import Apps
from api.tasks import run_sign_task
from api.utils.app.supersignutils import udid_bytes_to_dict, make_sign_udid_mobile_config
from api.utils.modelutils import get_redirect_server_domain, add_remote_info_from_request, \ from api.utils.modelutils import get_redirect_server_domain, add_remote_info_from_request, \
get_app_download_uri get_app_download_uri
from api.utils.response import BaseResponse from api.utils.response import BaseResponse
@ -22,6 +20,8 @@ from common.core.sysconfig import Config
from common.core.throttle import ReceiveUdidThrottle1, ReceiveUdidThrottle2 from common.core.throttle import ReceiveUdidThrottle1, ReceiveUdidThrottle2
from common.utils.caches import check_app_permission from common.utils.caches import check_app_permission
from fir_ser.celery import app from fir_ser.celery import app
from xsign.tasks import run_sign_task
from xsign.utils.supersignutils import udid_bytes_to_dict, make_sign_udid_mobile_config
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

@ -12,22 +12,24 @@ from django.http.response import FileResponse
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.views import APIView from rest_framework.views import APIView
from api.models import AppIOSDeveloperInfo, APPSuperSignUsedInfo, AppUDID, IosDeveloperPublicPoolBill, \ from api.models import Apps, UserInfo
UDIDsyncDeveloper, AppleDeveloperToAppUse, Apps, DeveloperAppID, APPToDeveloper, DeveloperDevicesID, \ from api.utils.modelutils import PageNumber
IosDeveloperBill, UserInfo
from api.utils.app.supersignutils import IosUtils
from api.utils.modelutils import get_user_public_used_sign_num, get_user_public_sign_num, PageNumber, \
check_uid_has_relevant
from api.utils.response import BaseResponse from api.utils.response import BaseResponse
from api.utils.serializer import DeveloperSerializer, SuperSignUsedSerializer, DeviceUDIDSerializer, BillInfoSerializer, \
DeveloperDeviceSerializer, AppleDeveloperToAppUseSerializer, AppleDeveloperToAppUseAppsSerializer, \
BillTransferSerializer
from api.utils.utils import get_developer_devices
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.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.caches import get_app_download_url from common.utils.caches import get_app_download_url
from xsign.models import AppIOSDeveloperInfo, APPSuperSignUsedInfo, AppUDID, IosDeveloperPublicPoolBill, \
UDIDsyncDeveloper, AppleDeveloperToAppUse, DeveloperAppID, APPToDeveloper, DeveloperDevicesID, \
IosDeveloperBill
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
from xsign.utils.supersignutils import IosUtils
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
Loading…
Cancel
Save