diff --git a/fir_client/src/components/FirBase.vue b/fir_client/src/components/FirBase.vue index dcc987b..e1a8952 100644 --- a/fir_client/src/components/FirBase.vue +++ b/fir_client/src/components/FirBase.vue @@ -20,7 +20,7 @@
应用分发请绑定您自己的域名,平台分发域名可能因不可违因素更换,将导致您的应用无法访问 - 立即绑定 + 立即绑定
diff --git a/fir_client/src/components/base/BindDomain.vue b/fir_client/src/components/base/BindDomain.vue index 60f1534..48e7dd1 100644 --- a/fir_client/src/components/base/BindDomain.vue +++ b/fir_client/src/components/base/BindDomain.vue @@ -36,12 +36,26 @@ align="center" label="主机记录" > + + 上一步 - 下一步 + 强制绑定 + 下一步 + @@ -156,6 +172,7 @@ bind_domain_sure: true, domain_name: '', domain_tData: [{'type': 'CNAME', 'host': 'xxx', 'dns': 'demo.xxx.cn'}], + force_bind:false, } }, mounted() { @@ -165,13 +182,18 @@ this.bind_domain_sure = false; }, methods: { + copy_success() { + this.$message.success('复制剪切板成功'); + }, check_cname() { domainFun(data => { if (data.code === 1000) { if (this.active++ > 2) this.active = 3; this.bind_status = true; if (!this.app_id) { - this.$store.dispatch("dodomainshow", false); + if (this.domain_type === 1) { + this.$store.dispatch("dodomainshow", false); + } } } else { if (data.code === 1004) { @@ -234,7 +256,7 @@ } this.domain_tData[0].dns = cname_domain; }, - next() { + next(force_bind) { if (this.active === 1) { domainFun(data => { if (data.code === 1000) { @@ -243,11 +265,14 @@ if (this.active++ > 2) this.active = 3; } } else { - this.$message.error("绑定失败 " + data.msg) + this.$message.error("绑定失败 " + data.msg); + if(data.code === 1011){ + this.force_bind=true + } } }, { methods: 'POST', - data: {domain_name: this.domain_name, app_id: this.app_id, domain_type: this.domain_type} + data: {domain_name: this.domain_name, app_id: this.app_id, domain_type: this.domain_type,force_bind:force_bind} }) } else if (this.active === 2) { this.check_cname() diff --git a/fir_ser/api/utils/app/supersignutils.py b/fir_ser/api/utils/app/supersignutils.py index be3a0ed..31b744e 100644 --- a/fir_ser/api/utils/app/supersignutils.py +++ b/fir_ser/api/utils/app/supersignutils.py @@ -4,7 +4,7 @@ # author: liuyu # date: 2020/3/6 -import uuid, xmltodict, os, re, logging, time +import uuid, xmltodict, os, logging, time import zipfile from api.utils.response import BaseResponse diff --git a/fir_ser/api/utils/baseutils.py b/fir_ser/api/utils/baseutils.py index 7dfbf16..3d931fd 100644 --- a/fir_ser/api/utils/baseutils.py +++ b/fir_ser/api/utils/baseutils.py @@ -229,3 +229,16 @@ def get_filename_form_file(filename): f_type = '.ipa' return f"{app_obj.name}-{app_release_obj.app_version}-{app_obj.short}{f_type}" return filename + + +def check_app_domain_name_access(app_obj, access_domain_name, extra_domain=None): + if app_obj and access_domain_name: + domain_list = [] + if extra_domain: + domain_list.append(extra_domain) + app_domain_name = get_app_domain_name(app_obj) + if app_domain_name: domain_list.append(app_domain_name) + user_domain_name = get_user_domain_name(app_obj.user_id) + if user_domain_name: domain_list.append(user_domain_name) + if access_domain_name in domain_list: + return True diff --git a/fir_ser/api/utils/storage/caches.py b/fir_ser/api/utils/storage/caches.py index a1af506..170dae4 100644 --- a/fir_ser/api/utils/storage/caches.py +++ b/fir_ser/api/utils/storage/caches.py @@ -13,7 +13,7 @@ from django.utils import timezone from fir_ser.settings import CACHE_KEY_TEMPLATE, SERVER_DOMAIN, SYNC_CACHE_TO_DATABASE, DEFAULT_MOBILEPROVISION, \ USER_FREE_DOWNLOAD_TIMES, AUTH_USER_FREE_DOWNLOAD_TIMES from api.utils.storage.storage import Storage, LocalStorage -from api.utils.baseutils import get_app_d_count_by_app_id, get_app_domain_name, check_app_password +from api.utils.baseutils import get_app_d_count_by_app_id, get_app_domain_name, check_app_password, get_user_domain_name import logging from django.db.models import F @@ -146,12 +146,16 @@ def del_cache_response_by_short(app_id, udid=''): del_cache_response_by_short_util(combo_dict.get("short"), combo_dict.get("app_id"), udid) -def del_cache_response_by_short_util(short, app_id, udid): - logger.info(f"del_cache_response_by_short short:{short} app_id:{app_id} udid:{udid}") +def del_short_cache(short): key = "_".join([CACHE_KEY_TEMPLATE.get("download_short_key"), short, '*']) for app_download_key in cache.iter_keys(key): cache.delete(app_download_key) + +def del_cache_response_by_short_util(short, app_id, udid): + logger.info(f"del_cache_response_by_short short:{short} app_id:{app_id} udid:{udid}") + del_short_cache(short) + cache.delete("_".join([CACHE_KEY_TEMPLATE.get("app_instance_key"), app_id])) key = 'ShortDownloadView'.lower() @@ -274,16 +278,27 @@ def send_msg_over_limit(act, email): return limit_cache_util(act, auth_code_key, SYNC_CACHE_TO_DATABASE.get("try_send_msg_over_limit_times")) -def set_default_app_wx_easy(user_obj, only_clean_cache=False): - app_obj_lists = Apps.objects.filter(user_id=user_obj) - for app_obj in app_obj_lists: - if only_clean_cache: - del_cache_response_by_short(app_obj.app_id) - else: - if not get_app_domain_name(app_obj): - app_obj.wxeasytype = True - app_obj.save(update_fields=['wxeasytype']) - del_cache_response_by_short(app_obj.app_id) +def reset_short_response_cache(user_obj, app_obj=None): + if app_obj is None: + app_obj_short_list = [x[0] for x in Apps.objects.filter(user_id=user_obj).values_list('short').all() if x] + else: + app_obj_short_list = [app_obj.short] + for short in app_obj_short_list: + del_short_cache(short) + + +def reset_app_wx_easy_type(user_obj, app_obj=None): + if get_user_domain_name(user_obj): + return reset_short_response_cache(user_obj, app_obj) + if app_obj is None: + app_obj_list = Apps.objects.filter(user_id=user_obj).all() + else: + app_obj_list = [app_obj] + for app_obj in app_obj_list: + if not get_app_domain_name(app_obj): + app_obj.wxeasytype = True + app_obj.save(update_fields=['wxeasytype']) + del_short_cache(app_obj.short) def enable_user_download_times_flag(user_id): diff --git a/fir_ser/api/utils/utils.py b/fir_ser/api/utils/utils.py index cd423ba..ef232ee 100644 --- a/fir_ser/api/utils/utils.py +++ b/fir_ser/api/utils/utils.py @@ -7,7 +7,7 @@ import os, datetime, random import binascii from fir_ser.settings import SERVER_DOMAIN, CAPTCHA_LENGTH, MEDIA_ROOT, CACHE_KEY_TEMPLATE from api.models import APPSuperSignUsedInfo, APPToDeveloper, \ - UDIDsyncDeveloper, UserInfo, AppReleaseInfo, AppScreenShot, Token, DeveloperDevicesID, Apps + UDIDsyncDeveloper, UserInfo, AppReleaseInfo, AppScreenShot, Token, DeveloperDevicesID from api.utils.storage.caches import get_app_d_count_by_app_id from api.utils.storage.localApi import LocalStorage from api.utils.storage.storage import Storage diff --git a/fir_ser/api/views/domain.py b/fir_ser/api/views/domain.py index 7141096..fc875bb 100644 --- a/fir_ser/api/views/domain.py +++ b/fir_ser/api/views/domain.py @@ -14,7 +14,7 @@ from rest_framework.response import Response from api.models import UserDomainInfo, Apps import logging -from api.utils.storage.caches import set_default_app_wx_easy, del_cache_response_by_short +from api.utils.storage.caches import del_cache_response_by_short, reset_app_wx_easy_type, reset_short_response_cache logger = logging.getLogger(__name__) @@ -31,6 +31,32 @@ def get_domain_filter(request): return filter_dict +def remove_domain_wx_easy(app_obj, user_obj): + if app_obj and not get_user_domain_name(user_obj): + app_obj.wxeasytype = True + app_obj.save(update_fields=['wxeasytype']) + del_cache_response_by_short(app_obj.app_id) + + +def add_new_domain_info(res, request, domain_name, domain_type): + min_domain_cname_info_obj = get_min_default_domain_cname_obj(False) + if min_domain_cname_info_obj: + res.data = {'cname_domain': min_domain_cname_info_obj.domain_record} + data_dict = { + 'user_id': request.user, + 'cname_id': min_domain_cname_info_obj, + 'domain_name': domain_name, + 'app_id': None, + 'domain_type': domain_type + } + app_id = request.data.get("app_id", None) + if app_id: + app_obj = Apps.objects.filter(app_id=app_id).first() + if app_obj: + data_dict['app_id'] = app_obj + UserDomainInfo.objects.create(**data_dict) + + class DomainCnameView(APIView): authentication_classes = [ExpiringTokenAuthentication, ] @@ -49,6 +75,7 @@ class DomainCnameView(APIView): res = BaseResponse() domain_name = request.data.get("domain_name", None) domain_type = request.data.get('domain_type', 1) + force_bind = request.data.get('force_bind', 0) if domain_type not in [x[0] for x in list(UserDomainInfo.domain_type_choices)]: res.code = 1001 res.msg = "绑定失败" @@ -56,33 +83,37 @@ class DomainCnameView(APIView): if domain_name: domain_name = domain_name.strip(" ") if domain_name and len(domain_name) > 3 and is_valid_domain(domain_name): - if UserDomainInfo.objects.filter(domain_name=domain_name, is_enable=True, - domain_type=domain_type).count() != 0: - res.code = 1001 - res.msg = "该域名已经被绑定,请更换其他域名" + filter_dict = get_domain_filter(request) + if not force_bind: + user_domain_obj = UserDomainInfo.objects.filter(domain_name=domain_name, is_enable=True).first() + if user_domain_obj: + res.code = 1011 + if user_domain_obj.user_id.uid == request.user.uid: + app_obj = user_domain_obj.app_id + if app_obj: + res.msg = f"该域名已经被应用 {app_obj.name} 绑定" + elif user_domain_obj.domain_type == 0: + res.msg = f"该域名已经被 应用下载码绑定" + else: + res.msg = f"该域名已经被 预览下载页绑定" + else: + res.msg = "该域名已经被其他用户绑定" + res.msg += ",请更换要绑定的域名,或者进行强制绑定域名" + else: + user_domain_obj = UserDomainInfo.objects.filter(**get_domain_filter(request), + domain_name=domain_name).first() + if user_domain_obj: + res.data = {'cname_domain': user_domain_obj.cname_id.domain_record} + else: + UserDomainInfo.objects.filter(**filter_dict, is_enable=False).delete() + add_new_domain_info(res, request, domain_name, domain_type) else: - user_domain_obj = UserDomainInfo.objects.filter(**get_domain_filter(request), + user_domain_obj = UserDomainInfo.objects.filter(**filter_dict, domain_name=domain_name).first() if user_domain_obj: res.data = {'cname_domain': user_domain_obj.cname_id.domain_record} else: - UserDomainInfo.objects.filter(**get_domain_filter(request), is_enable=False).delete() - min_domain_cname_info_obj = get_min_default_domain_cname_obj(False) - if min_domain_cname_info_obj: - res.data = {'cname_domain': min_domain_cname_info_obj.domain_record} - data_dict = { - 'user_id': request.user, - 'cname_id': min_domain_cname_info_obj, - 'domain_name': domain_name, - 'app_id': None, - 'domain_type': domain_type - } - app_id = request.data.get("app_id", None) - if app_id: - app_obj = Apps.objects.filter(app_id=app_id).first() - if app_obj: - data_dict['app_id'] = app_obj - UserDomainInfo.objects.create(**data_dict) + add_new_domain_info(res, request, domain_name, domain_type) else: res.code = 1002 res.msg = "该域名校验失败,请检查" @@ -94,19 +125,26 @@ class DomainCnameView(APIView): if user_domain_obj: cname = get_cname_from_domain(user_domain_obj.domain_name) if cname == user_domain_obj.cname_id.domain_record + '.': - user_domain_obj.is_enable = True - user_domain_obj.save(update_fields=["is_enable"]) - UserDomainInfo.objects.filter(domain_name=user_domain_obj.domain_name, is_enable=False, - domain_type=user_domain_obj.domain_type).delete() - app_id = request.data.get("app_id", None) - if app_id: - app_obj = Apps.objects.filter(app_id=app_id).first() - if app_obj: - app_obj.wxeasytype = False - app_obj.save(update_fields=['wxeasytype']) - del_cache_response_by_short(app_obj.app_id) + user_domain_obj_list = UserDomainInfo.objects.filter(domain_name=user_domain_obj.domain_name, + is_enable=True).all() + if len(user_domain_obj_list) < 2: + if len(user_domain_obj_list) == 1: + user_obj = user_domain_obj_list.first().user_id + app_obj = user_domain_obj_list.first().app_id + user_domain_obj_list.delete() + reset_app_wx_easy_type(user_obj, app_obj) + + user_domain_obj.is_enable = True + user_domain_obj.save(update_fields=["is_enable"]) + + app_id = request.data.get("app_id", None) + app_obj = None + if app_id: + app_obj = Apps.objects.filter(app_id=app_id).first() + reset_app_wx_easy_type(request.user, app_obj) else: - set_default_app_wx_easy(request.user, True) + res.code = 1002 + res.msg = "该域名查询校验失败,请检查" else: res.code = 1003 res.msg = "系统未检出到您的CNAME记录" @@ -117,14 +155,12 @@ class DomainCnameView(APIView): def delete(sele, request): res = BaseResponse() - if UserDomainInfo.objects.filter(**get_domain_filter(request)).delete()[0]: + user_domain_obj = UserDomainInfo.objects.filter(**get_domain_filter(request)).first() + if user_domain_obj: app_id = request.query_params.get("app_id", None) + app_obj = None if app_id: app_obj = Apps.objects.filter(app_id=app_id).first() - if app_obj and not get_user_domain_name(request.user): - app_obj.wxeasytype = True - app_obj.save(update_fields=['wxeasytype']) - del_cache_response_by_short(app_obj.app_id) - else: - set_default_app_wx_easy(request.user) + reset_app_wx_easy_type(request.user, app_obj) + user_domain_obj.delete() return Response(res.dict) diff --git a/fir_ser/api/views/download.py b/fir_ser/api/views/download.py index 3895e94..765ca3d 100644 --- a/fir_ser/api/views/download.py +++ b/fir_ser/api/views/download.py @@ -3,6 +3,8 @@ # project: 3月 # author: liuyu # date: 2020/3/6 +from urllib.parse import urlsplit + from rest_framework.views import APIView from api.utils.TokenManager import verify_token @@ -21,7 +23,8 @@ from api.utils.serializer import AppsShortSerializer from api.models import Apps, AppReleaseInfo, APPToDeveloper, APPSuperSignUsedInfo from django.http import FileResponse import logging -from api.utils.baseutils import get_profile_full_path, get_app_domain_name, get_filename_form_file +from api.utils.baseutils import get_profile_full_path, get_app_domain_name, get_filename_form_file, \ + check_app_domain_name_access from api.utils.throttle import VisitShortThrottle, InstallShortThrottle, InstallThrottle1, InstallThrottle2 logger = logging.getLogger(__name__) @@ -146,19 +149,24 @@ class ShortDownloadView(APIView): res = check_app_permission(app_obj, res) if res.code != 1000: return Response(res.dict) + origin_domain_name = request.META.get('HTTP_ORIGIN', 'xx').split('//')[-1] + domain_name = get_redirect_server_domain(request, app_obj.user_id, get_app_domain_name(app_obj)) + if not check_app_domain_name_access(app_obj, origin_domain_name, domain_name.split('//')[-1]): + res.code = 1004 + res.msg = "访问域名不合法" + else: + if udid: + if not app_obj.issupersign: + res.code = 1002 + res.msg = "参数有误" + return Response(res.dict) + del_cache_response_by_short(app_obj.app_id, udid=udid) - if udid: - if not app_obj.issupersign: - res.code = 1002 - res.msg = "参数有误" - return Response(res.dict) - del_cache_response_by_short(app_obj.app_id, udid=udid) - - app_serializer = AppsShortSerializer(app_obj, context={"key": "ShortDownloadView", "release_id": release_id, - "storage": Storage(app_obj.user_id)}) - res.data = app_serializer.data - res.udid = udid - res.domain_name = get_redirect_server_domain(request, app_obj.user_id, get_app_domain_name(app_obj)) + app_serializer = AppsShortSerializer(app_obj, context={"key": "ShortDownloadView", "release_id": release_id, + "storage": Storage(app_obj.user_id)}) + res.data = app_serializer.data + res.udid = udid + res.domain_name = domain_name return Response(res.dict) # key的设置 @@ -167,13 +175,18 @@ class ShortDownloadView(APIView): release_id = request.query_params.get("release_id", '') udid = request.query_params.get("udid", None) time = request.query_params.get("time", None) + origin_domain_name = request.META.get('HTTP_ORIGIN', 'xx').split('//')[-1] + if not origin_domain_name: + origin_domain_name = 'default.site' if udid and time: udid = time if not udid: udid = "" - logging.info(f"get or make cache_response short:{kwargs.get('short', '')} release_id:{release_id} udid:{udid}") + logging.info( + f"get or make cache_response short:{kwargs.get('short', '')} origin_domain_name:{origin_domain_name} release_id:{release_id} udid:{udid}") return "_".join( - [settings.CACHE_KEY_TEMPLATE.get("download_short_key"), kwargs.get("short", ''), release_id, udid]) + [settings.CACHE_KEY_TEMPLATE.get("download_short_key"), kwargs.get("short", ''), origin_domain_name, + release_id, udid]) class InstallView(APIView):