diff --git a/fir_ser/admin/urls.py b/fir_ser/admin/urls.py index 0594ba8..96b23af 100644 --- a/fir_ser/admin/urls.py +++ b/fir_ser/admin/urls.py @@ -22,8 +22,8 @@ from admin.views.login import LoginView, LoginUserView from admin.views.order import OrderInfoView from admin.views.report import AdminReportView 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 xsign.views.admin.supersign import DeveloperInfoView, DevicesInfoView, SuperSignBillView, SuperSignBillUserInfoView router = SimpleRouter(False) router.register('app/info', AppInfoView) diff --git a/fir_ser/admin/utils/serializer.py b/fir_ser/admin/utils/serializer.py index 4d6324e..049c112 100644 --- a/fir_ser/admin/utils/serializer.py +++ b/fir_ser/admin/utils/serializer.py @@ -5,8 +5,7 @@ from rest_framework import serializers from api import models from api.utils.serializer import AppReleaseSerializer, UserInfoSerializer, AppsSerializer, StorageSerializer, \ - DeveloperSerializer, SuperSignUsedSerializer, ThirdWxSerializer, DomainNameSerializer, BillInfoSerializer, \ - AppReportSerializer + ThirdWxSerializer, DomainNameSerializer, AppReportSerializer from common.base.baseutils import get_choices_dict, get_choices_name_from_key from common.utils.storage import Storage @@ -163,40 +162,6 @@ class AdminStorageSerializer(StorageSerializer): 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 Meta: model = models.Order @@ -265,19 +230,6 @@ class AdminDomainNameSerializer(DomainNameSerializer): 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 Meta: model = models.AppReportInfo diff --git a/fir_ser/admin/utils/utils.py b/fir_ser/admin/utils/utils.py index 436bdf2..6cd6f80 100644 --- a/fir_ser/admin/utils/utils.py +++ b/fir_ser/admin/utils/utils.py @@ -5,7 +5,6 @@ # date: 2022/1/6 from rest_framework.pagination import PageNumberPagination -from rest_framework.response import Response from rest_framework.viewsets import ModelViewSet @@ -33,20 +32,3 @@ class BaseModelSet(ModelViewSet): def update(self, request, *args, **kwargs): data = super().update(request, *args, **kwargs).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) diff --git a/fir_ser/admin/views/app.py b/fir_ser/admin/views/app.py index 238924d..15ec151 100644 --- a/fir_ser/admin/views/app.py +++ b/fir_ser/admin/views/app.py @@ -11,12 +11,13 @@ from django_filters.rest_framework import DjangoFilterBackend from rest_framework.filters import OrderingFilter 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.models import AppReleaseInfo, Apps -from api.utils.TokenManager import verify_token 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.token import verify_token logger = logging.getLogger(__name__) diff --git a/fir_ser/admin/views/domain.py b/fir_ser/admin/views/domain.py index 657f2e0..355e854 100644 --- a/fir_ser/admin/views/domain.py +++ b/fir_ser/admin/views/domain.py @@ -11,9 +11,10 @@ from django_filters.rest_framework import DjangoFilterBackend from rest_framework.filters import OrderingFilter 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 common.core.auth import AdminTokenAuthentication +from common.core.response import ApiResponse from common.utils.caches import reset_app_wx_easy_type logger = logging.getLogger(__name__) diff --git a/fir_ser/admin/views/login.py b/fir_ser/admin/views/login.py index 897fdc2..32e9cd8 100644 --- a/fir_ser/admin/views/login.py +++ b/fir_ser/admin/views/login.py @@ -9,10 +9,10 @@ import logging from django.contrib import auth from rest_framework.views import APIView -from admin.utils.utils import ApiResponse from api.utils.serializer import UserInfoSerializer from api.utils.utils import get_captcha, valid_captcha, set_user_token from common.core.auth import ExpiringTokenAuthentication +from common.core.response import ApiResponse from common.core.sysconfig import Config from common.core.throttle import VisitRegister1Throttle, VisitRegister2Throttle from common.utils.caches import login_auth_failed diff --git a/fir_ser/admin/views/order.py b/fir_ser/admin/views/order.py index 25dafa3..e865ec4 100644 --- a/fir_ser/admin/views/order.py +++ b/fir_ser/admin/views/order.py @@ -11,9 +11,10 @@ from django_filters.rest_framework import DjangoFilterBackend from rest_framework.filters import OrderingFilter 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 common.core.auth import AdminTokenAuthentication +from common.core.response import ApiResponse from common.utils.caches import update_order_info, admin_change_user_download_times logger = logging.getLogger(__name__) diff --git a/fir_ser/admin/views/storage.py b/fir_ser/admin/views/storage.py index 20d0f57..68b6dfd 100644 --- a/fir_ser/admin/views/storage.py +++ b/fir_ser/admin/views/storage.py @@ -13,11 +13,12 @@ from rest_framework.filters import OrderingFilter from rest_framework.views import APIView 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.models import UserInfo, AppStorage from common.base.baseutils import format_storage_selection from common.core.auth import AdminTokenAuthentication +from common.core.response import ApiResponse logger = logging.getLogger(__name__) diff --git a/fir_ser/admin/views/user.py b/fir_ser/admin/views/user.py index 6299e46..a11090b 100644 --- a/fir_ser/admin/views/user.py +++ b/fir_ser/admin/views/user.py @@ -12,10 +12,11 @@ from rest_framework.filters import OrderingFilter from rest_framework.views import APIView 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 common.base.baseutils import get_dict_from_filter_fields from common.core.auth import AdminTokenAuthentication +from common.core.response import ApiResponse from common.core.sysconfig import Config from common.utils.caches import auth_user_download_times_gift @@ -46,12 +47,12 @@ class UserInfoView(APIView): def put(self, request): data = request.data - id = data.get("id", None) - if not id: + pk = data.get("id", None) + if not pk: return ApiResponse(code=1003, msg="参数错误") - user_obj = UserInfo.objects.filter(id=id).first() + user_obj = UserInfo.objects.filter(id=pk).first() if user_obj: - data['pk'] = id + data['pk'] = pk users_serializer = AdminUserInfoSerializer(user_obj, data=data, partial=True) if users_serializer.is_valid(): users_serializer.save() diff --git a/fir_ser/api/admin.py b/fir_ser/api/admin.py index ea8c4cd..a18e325 100644 --- a/fir_ser/api/admin.py +++ b/fir_ser/api/admin.py @@ -9,16 +9,10 @@ admin.site.register(AppReleaseInfo) admin.site.register(Token) admin.site.register(VerifyName) 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(Order) admin.site.register(DomainCnameInfo) admin.site.register(UserDomainInfo) admin.site.register(UserAdDisplayInfo) -admin.site.register(IosDeveloperPublicPoolBill) admin.site.register(AppReportInfo) admin.site.register(RemoteClientInfo) diff --git a/fir_ser/api/base_views.py b/fir_ser/api/base_views.py index 3440aef..b1925ec 100644 --- a/fir_ser/api/base_views.py +++ b/fir_ser/api/base_views.py @@ -6,12 +6,12 @@ import logging -from api.models import AppReleaseInfo, APPToDeveloper, UserInfo, AppScreenShot, AppStorage -from api.utils.app.supersignutils import IosUtils +from api.models import AppReleaseInfo, UserInfo, AppScreenShot, AppStorage from api.utils.response import BaseResponse 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 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, \ del_cache_storage from common.utils.storage import Storage @@ -31,10 +31,7 @@ def app_delete(app_obj): res.msg = "数据迁移中" return res - 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) + delete_app_signal.send(None, app_pk=app_obj.pk) storage = Storage(user_obj) has_combo = app_obj.has_combo diff --git a/fir_ser/api/management/commands/expire_caches.py b/fir_ser/api/management/commands/expire_caches.py index c69d090..84404e9 100644 --- a/fir_ser/api/management/commands/expire_caches.py +++ b/fir_ser/api/management/commands/expire_caches.py @@ -1,6 +1,7 @@ from django.core.management.base import BaseCommand from api.tasks import start_api_sever_do_clean +from xsign.utils.iproxy import clean_ip_proxy_infos class Command(BaseCommand): @@ -8,3 +9,4 @@ class Command(BaseCommand): def handle(self, *args, **options): start_api_sever_do_clean() + clean_ip_proxy_infos() diff --git a/fir_ser/api/models.py b/fir_ser/api/models.py index 2aa605c..1230b7d 100644 --- a/fir_ser/api/models.py +++ b/fir_ser/api/models.py @@ -3,10 +3,10 @@ from django.contrib.contenttypes.models import ContentType from django.db import models 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.daobase import AESCharField +from common.utils.token import generate_alphanumeric_token_of_length, generate_numeric_token_of_length # Create your models here. @@ -69,7 +69,7 @@ class UserInfo(AbstractUser): 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="普通用户的标识,对当前公众号唯一") nickname = models.CharField(max_length=64, verbose_name="昵称", blank=True) sex = models.SmallIntegerField(default=0, verbose_name="性别", help_text="值为1时是男性,值为2时是女性,值为0时是未知") @@ -122,7 +122,7 @@ class VerifyName(models.Model): class Apps(models.Model): 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 = models.SmallIntegerField(choices=type_choices, default=0, verbose_name="类型") status_choices = ((0, '封禁'), (1, '正常'), (2, '违规')) @@ -147,7 +147,6 @@ class Apps(models.Model): supersign_limit_number = models.IntegerField(verbose_name="签名使用限额", default=0) wxredirect = 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, ) updated_time = models.DateTimeField(auto_now=True, verbose_name="更新时间") @@ -161,7 +160,7 @@ class Apps(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") created_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间") @@ -177,7 +176,7 @@ class AppScreenShot(models.Model): class AppReleaseInfo(models.Model): 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) - 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) app_version = models.CharField(max_length=64, verbose_name="app版本", blank=True) release_choices = ((0, 'android'), (1, 'adhoc'), (2, 'Inhouse'), (3, 'unknown')) @@ -200,7 +199,7 @@ class AppReleaseInfo(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="存储名字") storage_choices = ((0, '本地存储'), (1, '七牛云存储'), (2, '阿里云存储'), (3, '默认存储')) 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) -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): @@ -399,7 +251,7 @@ class Order(models.Model): 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) 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_download_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): - 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') name = models.CharField(max_length=128, null=False, verbose_name="真实姓名") card = models.CharField(max_length=128, null=False, verbose_name="身份证号码", unique=True) @@ -463,7 +315,7 @@ class UserCertificationInfo(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") type_choices = ((0, '未知'), (1, '国徽面照片'), (2, '人像面照片'), (3, '手持身份证照片')) type = models.SmallIntegerField(choices=type_choices, default=0, verbose_name="图像类型") @@ -500,9 +352,9 @@ class DomainCnameInfo(models.Model): class UserDomainInfo(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专属域名", null=True, blank=True) - cname_id = models.ForeignKey(to="DomainCnameInfo", verbose_name="cname解析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) + 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) is_https = models.BooleanField(default=False, verbose_name="是否支持HTTPS") weight = models.IntegerField(verbose_name="下载页域名展示权重", default=10) @@ -527,7 +379,7 @@ class UserDomainInfo(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_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", ) @@ -546,52 +398,6 @@ class UserAdDisplayInfo(models.Model): 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): remote_addr = models.GenericIPAddressField(verbose_name="远程IP地址") user_agent = models.CharField(verbose_name="客户端agent", max_length=512) @@ -610,7 +416,7 @@ class RemoteClientInfo(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) 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") @@ -633,22 +439,6 @@ class AppReportInfo(models.Model): 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): key = models.CharField(max_length=128, unique=True, verbose_name="配置名称") value = models.TextField(max_length=10240, verbose_name="配置值") diff --git a/fir_ser/api/tasks.py b/fir_ser/api/tasks.py index 9520f39..419b215 100644 --- a/fir_ser/api/tasks.py +++ b/fir_ser/api/tasks.py @@ -6,83 +6,24 @@ import logging -from celery import shared_task -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.utils.ctasks import sync_download_times, auto_clean_upload_tmp_file 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.libs.geetest.geetest_utils import check_bypass_status from common.libs.mp.wechat import sync_wx_access_token from common.utils.storage import get_local_storage from fir_ser.celery import app +from xsign.utils.ctasks import auto_delete_ios_mobile_tmp_file 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 start_api_sever_do_clean(): # 启动服务的时候,同时执行下面操作,主要是修改配置存储的时候,需要执行清理,否则会出问题,如果不修改,则无需执行 logger.info("clean local storage cache") get_local_storage(clean_cache=True) check_bypass_status() - clean_ip_proxy_infos() invalid_config_cache() @@ -112,17 +53,7 @@ def auto_delete_tmp_file_job(): auto_delete_ios_mobile_tmp_file() -@app.task -def auto_check_ios_developer_active_job(): - auto_check_ios_developer_active() - - @app.task def sync_wx_access_token_job(): if get_login_type().get('third', '').get('wxp'): sync_wx_access_token() - - -@app.task -def get_best_proxy_ips_job(): - get_best_proxy_ips() diff --git a/fir_ser/api/urls.py b/fir_ser/api/urls.py index ac2eaef..65d1918 100644 --- a/fir_ser/api/urls.py +++ b/fir_ser/api/urls.py @@ -23,11 +23,8 @@ from api.views.login import LoginView, UserInfoView, RegistView, AuthorizationVi UserApiTokenView, CertificationView, ChangeInfoView, WeChatLoginView, WeChatLoginCheckView from api.views.logout import LogoutView from api.views.order import PriceView, OrderView, PaySuccess -from api.views.receiveudids import IosUDIDView, TaskView from api.views.report import ReportView 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.uploads import AppAnalyseView, UploadView @@ -51,21 +48,10 @@ urlpatterns = [ re_path("^userinfo", UserInfoView.as_view()), re_path("^token", UserApiTokenView.as_view()), re_path(r"^short/(?P\w+)$", ShortDownloadView.as_view()), - re_path(r"^udid/(?P\w+)$", IosUDIDView.as_view()), - re_path(r"^task/(?P\w+)$", TaskView.as_view()), re_path("^analyse$", AppAnalyseView.as_view()), re_path("^advert$", UserAdInfoView.as_view()), re_path("^report$", ReportView.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("^orders$", OrderView.as_view()), re_path("^certification$", CertificationView.as_view()), diff --git a/fir_ser/api/utils/app/firApi.py b/fir_ser/api/utils/app/firApi.py deleted file mode 100644 index d291240..0000000 --- a/fir_ser/api/utils/app/firApi.py +++ /dev/null @@ -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()) diff --git a/fir_ser/api/utils/app/raqc.py b/fir_ser/api/utils/app/raqc.py deleted file mode 100644 index fb20af7..0000000 --- a/fir_ser/api/utils/app/raqc.py +++ /dev/null @@ -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) diff --git a/fir_ser/api/utils/app/shellcmds.py b/fir_ser/api/utils/app/shellcmds.py deleted file mode 100644 index b242728..0000000 --- a/fir_ser/api/utils/app/shellcmds.py +++ /dev/null @@ -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 diff --git a/fir_ser/api/utils/app/apputils.py b/fir_ser/api/utils/apputils.py similarity index 80% rename from fir_ser/api/utils/app/apputils.py rename to fir_ser/api/utils/apputils.py index 5f87786..a52c632 100644 --- a/fir_ser/api/utils/app/apputils.py +++ b/fir_ser/api/utils/apputils.py @@ -16,57 +16,6 @@ from common.utils.storage import Storage logger = logging.getLogger(__name__) -def make_resigned(bin_url, img_url, bundle_id, app_version, name): - ios_plist_tem = """ - - items - - - assets - - - kind - software-package - url - - - - kind - display-image - needs-shine - 0 - url - - - - kind - full-size-image - needs-shine - - url - - - - metadata - - bundle-identifier - {bundle_id} - bundle-version - - kind - software - title - - - - - -""".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): symbols = ('K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y') prefix = {} diff --git a/fir_ser/api/utils/ctasks.py b/fir_ser/api/utils/ctasks.py new file mode 100644 index 0000000..d9cda65 --- /dev/null +++ b/fir_ser/api/utils/ctasks.py @@ -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}") diff --git a/fir_ser/api/utils/modelutils.py b/fir_ser/api/utils/modelutils.py index cb8f29c..dc302ab 100644 --- a/fir_ser/api/utils/modelutils.py +++ b/fir_ser/api/utils/modelutils.py @@ -9,14 +9,12 @@ import logging import random 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 api.models import AppReleaseInfo, UserDomainInfo, DomainCnameInfo, UserAdDisplayInfo, RemoteClientInfo, \ - AppIOSDeveloperInfo, IosDeveloperPublicPoolBill, APPToDeveloper, UserInfo, UDIDsyncDeveloper, IosDeveloperBill, \ - AppUDID +from api.models import AppReleaseInfo, UserDomainInfo, DomainCnameInfo, UserAdDisplayInfo, RemoteClientInfo 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__) @@ -175,96 +173,6 @@ def check_app_domain_name_access(app_obj, access_domain_name, user_obj, extra_do 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): page_size = 10 # 每页显示多少条 page_size_query_param = 'size' # URL中每页显示条数的参数 diff --git a/fir_ser/api/utils/serializer.py b/fir_ser/api/utils/serializer.py index 0832b4f..c2b53c4 100644 --- a/fir_ser/api/utils/serializer.py +++ b/fir_ser/api/utils/serializer.py @@ -1,19 +1,16 @@ import logging import os -from django.db.models import Sum from rest_framework import serializers from api import models -from api.utils.TokenManager import make_token -from api.utils.app.apputils import bytes2human +from api.utils.apputils import bytes2human 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, AppleDeveloperUid +from common.base.baseutils import get_choices_dict 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.storage import Storage +from common.utils.token import make_token logger = logging.getLogger(__name__) @@ -114,16 +111,16 @@ class AppsSerializer(serializers.ModelSerializer): def get_preview_url(self, obj): return get_app_download_uri(None, obj.user_id, obj) - 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() + # 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() domain_name = serializers.SerializerMethodField() @@ -135,15 +132,15 @@ class AppsSerializer(serializers.ModelSerializer): def get_sign_type_choice(self, obj): return get_choices_dict(obj.supersign_type_choices) - 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() + # 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() screenshots = serializers.SerializerMethodField() @@ -378,179 +375,6 @@ class StorageSerializer(serializers.ModelSerializer): 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 Meta: model = models.Price @@ -627,112 +451,7 @@ class AppAdInfoSerializer(UserAdInfoSerializer): 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 Meta: model = models.AppReportInfo 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 diff --git a/fir_ser/api/utils/utils.py b/fir_ser/api/utils/utils.py index e68cef3..f5fb85f 100644 --- a/fir_ser/api/utils/utils.py +++ b/fir_ser/api/utils/utils.py @@ -12,10 +12,7 @@ from captcha.helpers import captcha_image_url from captcha.models import CaptchaStore from django.core.cache import cache -from api.models import APPSuperSignUsedInfo, APPToDeveloper, \ - 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.models import UserInfo, AppReleaseInfo, AppScreenShot, Token, UserAdDisplayInfo from api.utils.modelutils import get_app_d_count_by_app_id from common.base.baseutils import get_real_ip_address 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.sendmsg import SendMessage 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 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(): captcha_key = CaptchaStore.generate_key() captcha_image = captcha_image_url(captcha_key) diff --git a/fir_ser/api/views/apps.py b/fir_ser/api/views/apps.py index 56e6ce0..be5e504 100644 --- a/fir_ser/api/views/apps.py +++ b/fir_ser/api/views/apps.py @@ -12,16 +12,14 @@ from rest_framework.response import Response from rest_framework.views import APIView from api.base_views import app_delete -from api.models import Apps, AppReleaseInfo, APPToDeveloper, UserInfo, AppScreenShot, AppUDID -from api.tasks import run_resign_task -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.models import Apps, AppReleaseInfo, UserInfo, AppScreenShot +from api.utils.modelutils import get_user_domain_name, get_app_domain_name from api.utils.response import BaseResponse from api.utils.serializer import AppsSerializer, AppReleaseSerializer, AppsListSerializer, AppsQrListSerializer 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.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.storage import Storage @@ -117,8 +115,6 @@ class AppInfoView(APIView): if app_obj: app_serializer = AppsSerializer(app_obj, context={"storage": Storage(request.user)}) res.data = app_serializer.data - count = APPToDeveloper.objects.filter(app_id=app_obj).count() - res.data["count"] = count else: logger.error(f"app_id:{app_id} is not found in user:{request.user}") res.msg = "未找到该应用" @@ -142,18 +138,6 @@ class AppInfoView(APIView): 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: actions = has_combo.get("action", None) @@ -183,95 +167,23 @@ class AppInfoView(APIView): res.msg = "该应用已经关联" 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__)) app_obj.description = data.get("description", app_obj.description) app_obj.short = data.get("short", app_obj.short) app_obj.name = data.get("name", app_obj.name) 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) - 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): app_obj.wxeasytype = data.get("wxeasytype", app_obj.wxeasytype) else: app_obj.wxeasytype = 1 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) 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__}") 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}") @@ -337,10 +249,7 @@ class AppReleaseInfoView(APIView): app_release_obj.delete() 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}") - count = APPToDeveloper.objects.filter(app_id=app_obj).count() - 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) + delete_app_signal.send(None, app_pk=app_obj.pk) 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) diff --git a/fir_ser/api/views/download.py b/fir_ser/api/views/download.py index fb2c5b2..0b49ae3 100644 --- a/fir_ser/api/views/download.py +++ b/fir_ser/api/views/download.py @@ -11,43 +11,24 @@ from django.http import FileResponse from rest_framework.response import Response from rest_framework.views import APIView -from api.models import Apps, AppReleaseInfo, APPToDeveloper, APPSuperSignUsedInfo -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.models import Apps, AppReleaseInfo from api.utils.modelutils import get_filename_form_file, check_app_domain_name_access, \ ad_random_weight, get_app_download_uri from api.utils.response import BaseResponse from api.utils.serializer import AppsShortSerializer, AppAdInfoSerializer -from common.base.baseutils import get_profile_full_path, make_random_uuid, get_origin_domain_name, \ - format_get_uri, get_server_domain_from_request +from common.base.baseutils import get_origin_domain_name, format_get_uri 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.throttle import VisitShortThrottle, InstallShortThrottle, InstallThrottle1, InstallThrottle2 from common.utils.caches import check_app_permission, get_app_download_url from common.utils.storage import Storage, get_local_storage +from common.utils.token import verify_token from fir_ser import settings 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): """ 文件下载接口,适用于本地存储和所有plist文件下载 @@ -69,59 +50,7 @@ class DownloadView(APIView): 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") - 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': # 企业签名安装信任跳转 + if f_type == 'dmobileprovision': # 企业签名安装信任跳转 release_obj = AppReleaseInfo.objects.filter(release_id=filename.split('.')[0]).first() if release_obj: file_path = Config.DEFAULT_MOBILEPROVISION.get("enterprise").get('path') diff --git a/fir_ser/api/views/storage.py b/fir_ser/api/views/storage.py index 0083c34..a143576 100644 --- a/fir_ser/api/views/storage.py +++ b/fir_ser/api/views/storage.py @@ -11,7 +11,7 @@ from rest_framework.views import APIView from api.base_views import storage_change, app_delete 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.serializer import StorageSerializer from api.utils.utils import upload_oss_default_head_img diff --git a/fir_ser/api/views/uploads.py b/fir_ser/api/views/uploads.py index f670465..c50f55c 100644 --- a/fir_ser/api/views/uploads.py +++ b/fir_ser/api/views/uploads.py @@ -10,19 +10,20 @@ import os from rest_framework.response import Response from rest_framework.views import APIView -from api.models import Apps, AppReleaseInfo, UserInfo, AppScreenShot, CertificationInfo, UserAdDisplayInfo, AppUDID -from api.tasks import run_resign_task -from api.utils.TokenManager import verify_token -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.models import Apps, AppReleaseInfo, UserInfo, AppScreenShot, CertificationInfo, UserAdDisplayInfo +from api.utils.apputils import get_random_short, save_app_infos +from api.utils.modelutils import get_app_download_uri from api.utils.response import BaseResponse from common.base.baseutils import make_app_uuid, make_from_user_uuid from common.cache.state import MigrateStorageState from common.core.auth import ExpiringTokenAuthentication +from common.core.singals import run_resign_task_signal from common.core.sysconfig import Config from common.utils.caches import upload_file_tmp_name, del_cache_response_by_short from common.utils.storage import Storage +from common.utils.token import verify_token from fir_ser import settings +from xsign.utils.modelutils import check_super_sign_permission 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() 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}") + run_resign_task_signal.send(sender=None, app_pk=app_obj.pk) else: storage.delete_file(app_tmp_filename) storage.delete_file(png_tmp_filename) diff --git a/fir_ser/cli/urls.py b/fir_ser/cli/urls.py index f09db92..64b3eb3 100644 --- a/fir_ser/cli/urls.py +++ b/fir_ser/cli/urls.py @@ -21,8 +21,8 @@ from cli.views.uploads import CliAppAnalyseView, CliUploadView urlpatterns = [ re_path("^apps$", CliAppsView.as_view()), - re_path("^apps/(?P\w+)", CliAppInfoView.as_view()), - re_path("^appinfos/(?P\w+)/(?P\w+)", CliAppReleaseInfoView.as_view()), + re_path(r"^apps/(?P\w+)", CliAppInfoView.as_view()), + re_path(r"^appinfos/(?P\w+)/(?P\w+)", CliAppReleaseInfoView.as_view()), re_path("^upload$", CliUploadView.as_view()), re_path("^userinfo", CliUserInfoView.as_view()), re_path("^analyse$", CliAppAnalyseView.as_view()), diff --git a/fir_ser/common/core/response.py b/fir_ser/common/core/response.py new file mode 100644 index 0000000..ddfedd3 --- /dev/null +++ b/fir_ser/common/core/response.py @@ -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) diff --git a/fir_ser/common/core/singals.py b/fir_ser/common/core/singals.py new file mode 100644 index 0000000..9848c00 --- /dev/null +++ b/fir_ser/common/core/singals.py @@ -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) diff --git a/fir_ser/common/libs/apple/appleapiv3.py b/fir_ser/common/libs/apple/appleapiv3.py index 5f0709f..a6b6675 100644 --- a/fir_ser/common/libs/apple/appleapiv3.py +++ b/fir_ser/common/libs/apple/appleapiv3.py @@ -15,8 +15,8 @@ from functools import wraps import jwt import requests -from api.utils.crontab.iproxy import get_proxy_ip_from_cache from common.core.sysconfig import Config +from xsign.utils.iproxy import get_proxy_ip_from_cache logger = logging.getLogger(__name__) diff --git a/fir_ser/common/libs/storage/localApi.py b/fir_ser/common/libs/storage/localApi.py index 20f7199..315b1fd 100644 --- a/fir_ser/common/libs/storage/localApi.py +++ b/fir_ser/common/libs/storage/localApi.py @@ -9,8 +9,8 @@ import logging import os -from api.utils.TokenManager import make_token from common.libs.storage.aliyunApi import AliYunCdn +from common.utils.token import make_token from fir_ser import settings logger = logging.getLogger(__name__) diff --git a/fir_ser/common/utils/caches.py b/fir_ser/common/utils/caches.py index 7acec8a..8af5612 100644 --- a/fir_ser/common/utils/caches.py +++ b/fir_ser/common/utils/caches.py @@ -12,8 +12,7 @@ from django.core.cache import cache from django.db.models import F from django.utils import timezone -from api.models import Apps, UserInfo, AppReleaseInfo, AppUDID, APPToDeveloper, APPSuperSignUsedInfo, \ - UserCertificationInfo, Order +from api.models import Apps, UserInfo, AppReleaseInfo, UserCertificationInfo, Order 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 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) set_app_download_by_cache(app_id) amount = app_obj.get("d_count") - # # 超级签需要多消耗2倍下载次数 - # if app_obj.get("issupersign"): - # amount *= 2 auth_status = False status = app_obj.get('user_id__certification__status', None) if status and status == 1: diff --git a/fir_ser/api/utils/TokenManager.py b/fir_ser/common/utils/token.py similarity index 100% rename from fir_ser/api/utils/TokenManager.py rename to fir_ser/common/utils/token.py diff --git a/fir_ser/fir_ser/settings.py b/fir_ser/fir_ser/settings.py index 3335ae1..aa03d8f 100644 --- a/fir_ser/fir_ser/settings.py +++ b/fir_ser/fir_ser/settings.py @@ -46,6 +46,7 @@ INSTALLED_APPS = [ 'django_celery_results', 'corsheaders', 'django_filters', + 'xsign.apps.ApiConfig', ] MIDDLEWARE = [ @@ -368,7 +369,7 @@ LOGGING = { 'encoding': 'utf-8', }, 'sql': { - 'level': 'DEBUG', + 'level': 'INFO', 'class': 'logging.handlers.RotatingFileHandler', # 保存到文件,自动切 'filename': os.path.join(BASE_LOG_DIR, "flyapp_sql.log"), # 日志文件 'maxBytes': 1024 * 1024 * 50, # 日志大小 50M diff --git a/fir_ser/fir_ser/urls.py b/fir_ser/fir_ser/urls.py index f12104d..faf74ea 100644 --- a/fir_ser/fir_ser/urls.py +++ b/fir_ser/fir_ser/urls.py @@ -19,12 +19,13 @@ from django.views.static import serve from admin.views.celery_flower import CeleryFlowerView from api.views.download import DownloadView, InstallView -from api.views.receiveudids import IosUDIDView, ShowUdidView from fir_ser import settings +from xsign.views.receiveudids import IosUDIDView, ShowUdidView urlpatterns = [ re_path('fly.admin/', admin.site.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/v3/fir/server/", include('admin.urls')), re_path('^captcha/', include('captcha.urls')), diff --git a/fir_ser/api/utils/app/__init__.py b/fir_ser/xsign/__init__.py similarity index 100% rename from fir_ser/api/utils/app/__init__.py rename to fir_ser/xsign/__init__.py diff --git a/fir_ser/xsign/admin.py b/fir_ser/xsign/admin.py new file mode 100644 index 0000000..ce666d0 --- /dev/null +++ b/fir_ser/xsign/admin.py @@ -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) diff --git a/fir_ser/xsign/apps.py b/fir_ser/xsign/apps.py new file mode 100644 index 0000000..923e3c8 --- /dev/null +++ b/fir_ser/xsign/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class ApiConfig(AppConfig): + name = 'xsign' diff --git a/fir_ser/xsign/migrations/__init__.py b/fir_ser/xsign/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/fir_ser/xsign/models.py b/fir_ser/xsign/models.py new file mode 100644 index 0000000..fd53b0b --- /dev/null +++ b/fir_ser/xsign/models.py @@ -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) diff --git a/fir_ser/xsign/tasks.py b/fir_ser/xsign/tasks.py new file mode 100644 index 0000000..cc23972 --- /dev/null +++ b/fir_ser/xsign/tasks.py @@ -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() diff --git a/fir_ser/xsign/urls.py b/fir_ser/xsign/urls.py new file mode 100644 index 0000000..680b267 --- /dev/null +++ b/fir_ser/xsign/urls.py @@ -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\w+)$", IosUDIDView.as_view()), + re_path(r"^task/(?P\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()), +] diff --git a/fir_ser/xsign/utils/__init__.py b/fir_ser/xsign/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/fir_ser/api/utils/crontab/ctasks.py b/fir_ser/xsign/utils/ctasks.py similarity index 55% rename from fir_ser/api/utils/crontab/ctasks.py rename to fir_ser/xsign/utils/ctasks.py index 1a26bd2..072043b 100644 --- a/fir_ser/api/utils/crontab/ctasks.py +++ b/fir_ser/xsign/utils/ctasks.py @@ -8,49 +8,15 @@ import logging import os 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 common.core.sysconfig import Config -from common.utils.storage import Storage -from fir_ser.settings import CACHE_KEY_TEMPLATE, SYNC_CACHE_TO_DATABASE, SUPER_SIGN_ROOT +from fir_ser.settings import SUPER_SIGN_ROOT, SYNC_CACHE_TO_DATABASE +from xsign.models import UserInfo, AppIOSDeveloperInfo +from xsign.utils.supersignutils import IosUtils 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(): 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): diff --git a/fir_ser/api/utils/app/iossignapi.py b/fir_ser/xsign/utils/iossignapi.py similarity index 94% rename from fir_ser/api/utils/app/iossignapi.py rename to fir_ser/xsign/utils/iossignapi.py index 885aa2d..8258135 100644 --- a/fir_ser/api/utils/app/iossignapi.py +++ b/fir_ser/xsign/utils/iossignapi.py @@ -8,41 +8,60 @@ import datetime import logging import os import re +import signal import time +from subprocess import Popen, PIPE 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) -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.cache.state import CleanErrorBundleIdSignDataState from common.core.sysconfig import Config from common.libs.apple.appleapiv3 import AppStoreConnectApi from fir_ser.settings import SUPER_SIGN_ROOT +from xsign.models import AppIOSDeveloperInfo logger = logging.getLogger(__name__) -def exec_shell(cmd, remote=False, timeout=None): - if remote: - host_ip = "10.66.6.66" - port = 65534 - user = "root" - passwd = "root" - result = use_user_pass(host_ip, port, user, passwd, cmd) - return result - else: - logger.info(f"exec_shell cmd:{cmd}") - result = shell_command(cmd, timeout) - logger.info(f"exec_shell cmd:{cmd} result:{result}") - if result.get("exit_code") != 0: - err_info = result.get("err_info", None) - if err_info: - logger.error(f"exec_shell cmd:{cmd} failed: {err_info}") - result["err_info"] = "Unknown Error" - return False, result - return True, result +def shell_command(command, timeout): + result = {'exit_code': 99, 'return_info': ''} + shell_start_time = time.time() + child = Popen(command, 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: {command} - return_info:{out} - exit_code:{child.returncode}') + return result + + +def exec_shell(cmd, timeout=None): + logger.info(f"exec_shell cmd:{cmd}") + result = shell_command(cmd, timeout) + logger.info(f"exec_shell cmd:{cmd} result:{result}") + if result.get("exit_code") != 0: + err_info = result.get("err_info", None) + if err_info: + logger.error(f"exec_shell cmd:{cmd} failed: {err_info}") + result["err_info"] = "Unknown Error" + return False, result + return True, result class ResignApp(object): diff --git a/fir_ser/api/utils/crontab/iproxy.py b/fir_ser/xsign/utils/iproxy.py similarity index 100% rename from fir_ser/api/utils/crontab/iproxy.py rename to fir_ser/xsign/utils/iproxy.py diff --git a/fir_ser/xsign/utils/modelutils.py b/fir_ser/xsign/utils/modelutils.py new file mode 100644 index 0000000..ce04ceb --- /dev/null +++ b/fir_ser/xsign/utils/modelutils.py @@ -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() + } diff --git a/fir_ser/xsign/utils/serializer.py b/fir_ser/xsign/utils/serializer.py new file mode 100644 index 0000000..41c5c62 --- /dev/null +++ b/fir_ser/xsign/utils/serializer.py @@ -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() diff --git a/fir_ser/api/utils/app/supersignutils.py b/fir_ser/xsign/utils/supersignutils.py similarity index 98% rename from fir_ser/api/utils/app/supersignutils.py rename to fir_ser/xsign/utils/supersignutils.py index 5c8565d..305003f 100644 --- a/fir_ser/api/utils/app/supersignutils.py +++ b/fir_ser/xsign/utils/supersignutils.py @@ -15,16 +15,10 @@ import xmltodict from django.core.cache import cache from django.db.models import Count, F -from api.models import APPSuperSignUsedInfo, AppUDID, AppIOSDeveloperInfo, AppReleaseInfo, Apps, APPToDeveloper, \ - 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.models import UserInfo, AppReleaseInfo, Apps from api.utils.response import BaseResponse -from api.utils.serializer import BillAppInfoSerializer, BillDeveloperInfoSerializer -from api.utils.utils import delete_app_to_dev_and_file, send_ios_developer_active_status, delete_local_files, \ - download_files_form_oss, get_developer_udided +from api.utils.utils import send_ios_developer_active_status, delete_local_files, \ + download_files_form_oss 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 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 from common.utils.storage import Storage 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__) diff --git a/fir_ser/xsign/utils/utils.py b/fir_ser/xsign/utils/utils.py new file mode 100644 index 0000000..e4d14d2 --- /dev/null +++ b/fir_ser/xsign/utils/utils.py @@ -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 = """ + + items + + + assets + + + kind + software-package + url + + + + kind + display-image + needs-shine + 0 + url + + + + kind + full-size-image + needs-shine + + url + + + + metadata + + bundle-identifier + {bundle_id} + bundle-version + + kind + software + title + + + + + +""".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 diff --git a/fir_ser/xsign/views/__init__.py b/fir_ser/xsign/views/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/fir_ser/xsign/views/admin/__init__.py b/fir_ser/xsign/views/admin/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/fir_ser/admin/views/supersign.py b/fir_ser/xsign/views/admin/supersign.py similarity index 91% rename from fir_ser/admin/views/supersign.py rename to fir_ser/xsign/views/admin/supersign.py index 18035f4..45dd6b3 100644 --- a/fir_ser/admin/views/supersign.py +++ b/fir_ser/xsign/views/admin/supersign.py @@ -11,13 +11,14 @@ from django_filters.rest_framework import DjangoFilterBackend from rest_framework.filters import OrderingFilter from rest_framework.views import APIView -from admin.utils.serializer import AdminDeveloperSerializer, AdminSuperSignUsedSerializer, AdminBillInfoSerializer -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 admin.utils.utils import AppsPageNumber, BaseModelSet from common.base.baseutils import get_real_ip_address 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__) diff --git a/fir_ser/xsign/views/appinfo.py b/fir_ser/xsign/views/appinfo.py new file mode 100644 index 0000000..4319043 --- /dev/null +++ b/fir_ser/xsign/views/appinfo.py @@ -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) diff --git a/fir_ser/xsign/views/download.py b/fir_ser/xsign/views/download.py new file mode 100644 index 0000000..9e8aa0b --- /dev/null +++ b/fir_ser/xsign/views/download.py @@ -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校验失败") diff --git a/fir_ser/api/views/receiveudids.py b/fir_ser/xsign/views/receiveudids.py similarity index 97% rename from fir_ser/api/views/receiveudids.py rename to fir_ser/xsign/views/receiveudids.py index 4a00345..0177912 100644 --- a/fir_ser/api/views/receiveudids.py +++ b/fir_ser/xsign/views/receiveudids.py @@ -12,8 +12,6 @@ from rest_framework.response import Response from rest_framework.views import APIView 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, \ get_app_download_uri 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.utils.caches import check_app_permission 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__) diff --git a/fir_ser/api/views/supersign.py b/fir_ser/xsign/views/supersign.py similarity index 98% rename from fir_ser/api/views/supersign.py rename to fir_ser/xsign/views/supersign.py index 00eb042..8cbaa72 100644 --- a/fir_ser/api/views/supersign.py +++ b/fir_ser/xsign/views/supersign.py @@ -12,22 +12,24 @@ from django.http.response import FileResponse from rest_framework.response import Response from rest_framework.views import APIView -from api.models import AppIOSDeveloperInfo, APPSuperSignUsedInfo, AppUDID, IosDeveloperPublicPoolBill, \ - UDIDsyncDeveloper, AppleDeveloperToAppUse, Apps, DeveloperAppID, APPToDeveloper, DeveloperDevicesID, \ - 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.models import Apps, UserInfo +from api.utils.modelutils import PageNumber 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.cache.state import CleanSignDataState, MigrateStorageState from common.core.auth import ExpiringTokenAuthentication, SuperSignPermission from common.core.sysconfig import Config 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__)