diff --git a/fir_admin/src/views/appinfos/list.vue b/fir_admin/src/views/appinfos/list.vue index 2fca46b..5bbfe84 100644 --- a/fir_admin/src/views/appinfos/list.vue +++ b/fir_admin/src/views/appinfos/list.vue @@ -4,7 +4,7 @@ - + @@ -221,7 +221,7 @@ export default { type: 'warning' }).then(() => { this.listLoading = true - deleteApp(app_id).then(response => { + deleteApp(app_id).then(() => { this.$message.success('删除成功') this.fetchData() this.listLoading = false diff --git a/fir_client/src/components/FirHeader.vue b/fir_client/src/components/FirHeader.vue index 9bba2f4..45afa21 100644 --- a/fir_client/src/components/FirHeader.vue +++ b/fir_client/src/components/FirHeader.vue @@ -51,6 +51,7 @@ 个人资料 API token 设置域名 + 下载配置 消息中心 宣传广告 @@ -135,6 +136,8 @@ export default { } else if (command === 'setdomian') { this.$router.push({"name": 'FirUserDomain'}) + } else if (command === 'download') { + this.$router.push({"name": 'FirUserDownload'}) } else if (command === 'setadvert') { this.$router.push({"name": 'FirUserAdvert'}) } else if (command === 'myorder') { diff --git a/fir_client/src/components/base/BindDomain.vue b/fir_client/src/components/base/BindDomain.vue index 51b3c66..0dfd13a 100644 --- a/fir_client/src/components/base/BindDomain.vue +++ b/fir_client/src/components/base/BindDomain.vue @@ -21,6 +21,7 @@ 请联系域名管理员,前往 {{ domain_name }} 域名 DNS 管理后台添加如下 CNAME 记录。 + + + Private + + - 解除绑定 + 解除绑定并删除记录 @@ -98,13 +104,14 @@ - 解除绑定 + 解除绑定并删除记录 + + + + Private + + + + {{ scope.row.dns }} + + + - 已经修改配置,再次检查绑定 + 已经修改配置,再次检查绑定 @@ -139,8 +159,8 @@ 上一步 - 强制绑定 - 下一步 + 强制绑定 + 下一步 @@ -173,17 +193,18 @@ export default { c_domain_name: { type: String, default: '' - }, + } }, data() { return { active: 1, bind_status: false, bind_domain_sure: true, - domain_tData: [{'type': 'CNAME', 'host': 'xxx', 'dns': 'demo.xxx.cn'}], + domain_tData: [{'type': 'CNAME', 'host': 'xxx', 'dns': 'demo.xxx.cn', 'is_private': false}], domain_name: '', force_bind: false, b_t_msg: '您的账户', + is_loading: false } }, mounted() { @@ -203,10 +224,13 @@ export default { this.$message.success('复制剪切板成功'); }, check_cname() { + this.is_loading = true domainFun(data => { + this.is_loading = false if (data.code === 1000) { if (this.active++ > 2) this.active = 3; this.bind_status = true; + this.$message.success("绑定成功") if (!this.app_id) { if (this.domain_type === 1) { this.$store.dispatch("dodomainshow", false); @@ -258,7 +282,7 @@ export default { this.domain_name = data.data.domain_name; } if (data.data.domain_record) { - this.format_domain_tData(data.data.domain_record); + this.format_domain_tData(data.data); if (this.active++ > 2) this.active = 3; } if (data.data.is_enable) { @@ -281,14 +305,15 @@ export default { domain_name_list.splice(domain_name_list.length - 2, 2); this.domain_tData[0].host = domain_name_list.join(".") } - this.domain_tData[0].dns = cname_domain; + this.domain_tData[0].dns = cname_domain.domain_record; + this.domain_tData[0].is_private = cname_domain.is_private; }, next(force_bind) { if (this.active === 1) { domainFun(data => { if (data.code === 1000) { - if (data.data && data.data.cname_domain) { - this.format_domain_tData(data.data.cname_domain); + if (data.data && data.data.domain_record) { + this.format_domain_tData(data.data); if (this.active++ > 2) this.active = 3; } } else { diff --git a/fir_client/src/components/user/FirUserDomain.vue b/fir_client/src/components/user/FirUserDomain.vue index 795afe3..741d6c7 100644 --- a/fir_client/src/components/user/FirUserDomain.vue +++ b/fir_client/src/components/user/FirUserDomain.vue @@ -11,47 +11,6 @@ :domain_state="true" :domain_type="current_domain_info.domain_type" transitionName="bind-app-domain"/> - - - - - - 配置KEY {{ info.key }} - - - - - - 描述信息 - {{ info.title }} - - 下载页部署源码及操作文档 - 点击下载 - - - - - - - - - - 下载页配置 - - 添加下载页域名 @@ -94,6 +48,9 @@ label="绑定域名" prop="domain_name"> + + Private + + + + + + + 点击关闭 HTTPS 访问 + + + 点击开启支持 HTTPS 访问 + + + 已开启 + + 未开启 + + + - { + if (data.code === 1000) { + this.$message.success("https支持修改成功") + } else { + this.$message.error("修改失败 " + data.msg) + } + }, {methods: 'PUT', data: domain_info}) + }, configFun() { personalConfigInfo(data => { if (data.code === 1000) { diff --git a/fir_client/src/components/user/FirUserDownload.vue b/fir_client/src/components/user/FirUserDownload.vue new file mode 100644 index 0000000..7c8f65d --- /dev/null +++ b/fir_client/src/components/user/FirUserDownload.vue @@ -0,0 +1,470 @@ + + + + + + + + + 配置KEY {{ info.key }} + + + + + + 描述信息 + {{ info.title }} + + + + + 下载页源码及操作文档 + + + + {{short_download_uri.title}} + 点击下载 + + + + + + + + + + + + + + + + + + + + 1.如果是cdn等含有CNAME,直接添加cdn的 CNAME 值 + 2.如果是服务器,需要将域名解析到服务器,然后添加该解析的域名 + + + + + + + + + + + + + + + + + 下一步 + + + + + + + + 请联系域名管理员,前往 {{ cnameInfo.domain_record }} 域名 DNS 管理后台添加如下 {{ cnameInfo.r_type }} 记录。 + + + + + + + {{ scope.row.host_r }} + + + + + + + + {{ scope.row.cname_r }} + + + + + + + + + 上一步 + 下一步 + + + + + + + + + + + + 搜索 + + + + + + + 下载页配置 + + + + 添加下载页服务器 + + + + + + + + + {{ scope.row.domain_record }} + + + + + + + + + + + + + 成功 + + + + 继续绑定 + + + + + + + + + + + + + + + + 删除 + + + + + + + + + + + + + + + + + + + + diff --git a/fir_client/src/restful/index.js b/fir_client/src/restful/index.js index 57e3ae9..743d73e 100644 --- a/fir_client/src/restful/index.js +++ b/fir_client/src/restful/index.js @@ -605,6 +605,17 @@ export function domainFun(callBack, params) { ); } +/**用户添加下载页服务器*/ +export function dCnameInfoFun(callBack, params) { + getData( + params.methods, + USERSEVER + '/cname_info', + params.data, + data => { + callBack(data); + } + ); +} /**微信用户绑定 */ export function wxutils(callBack, params) { diff --git a/fir_client/src/router/index.js b/fir_client/src/router/index.js index dbf1515..99bbc19 100644 --- a/fir_client/src/router/index.js +++ b/fir_client/src/router/index.js @@ -168,6 +168,11 @@ const router = new VueRouter({ name: 'FirUserDomain', meta: {label: '绑定域名详情'}, component: () => import("@/components/user/FirUserDomain"), + }, { + path: 'download', + name: 'FirUserDownload', + meta: {label: '下载页设置'}, + component: () => import("@/components/user/FirUserDownload"), }, { path: 'advert', diff --git a/fir_ser/api/migrations/0007_auto_20220725_1612.py b/fir_ser/api/migrations/0007_auto_20220725_1612.py new file mode 100644 index 0000000..0b4f2da --- /dev/null +++ b/fir_ser/api/migrations/0007_auto_20220725_1612.py @@ -0,0 +1,32 @@ +# Generated by Django 3.2.3 on 2022-07-25 16:12 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0006_auto_20220513_0843'), + ] + + operations = [ + migrations.AlterField( + model_name='domaincnameinfo', + name='is_enable', + field=models.BooleanField(default=False, verbose_name='是否启用该解析'), + ), + migrations.AlterField( + model_name='domaincnameinfo', + name='domain_record', + field=models.CharField(max_length=128, verbose_name='记录值'), + ), + migrations.AddField( + model_name='domaincnameinfo', + name='user_ipk', + field=models.IntegerField(default=0, verbose_name='用户ipk'), + ), + migrations.AlterUniqueTogether( + name='domaincnameinfo', + unique_together={('user_ipk', 'domain_record')}, + ), + ] diff --git a/fir_ser/api/models.py b/fir_ser/api/models.py index d15fc3e..51ee372 100644 --- a/fir_ser/api/models.py +++ b/fir_ser/api/models.py @@ -63,7 +63,7 @@ class UserInfo(AbstractUser): self.api_token = self.uid + generate_alphanumeric_token_of_length(64) if not self.default_domain_name_id: default_domain_obj = min( - DomainCnameInfo.objects.annotate(Count('userinfo')).filter(is_enable=True, is_system=True), + DomainCnameInfo.objects.annotate(Count('userinfo')).filter(is_enable=True, is_system=True, user_ipk=0), key=lambda x: x.userinfo__count) if default_domain_obj: self.default_domain_name_id = default_domain_obj.pk @@ -378,9 +378,10 @@ class CertificationInfo(models.Model): class DomainCnameInfo(models.Model): - domain_record = models.CharField(max_length=128, unique=True, verbose_name="记录值") + user_ipk = models.IntegerField(verbose_name="用户ipk", default=0) + domain_record = models.CharField(max_length=128, verbose_name="记录值") ip_address = models.CharField(max_length=128, verbose_name="域名解析地址", null=False) - is_enable = models.BooleanField(default=True, verbose_name="是否启用该解析") + is_enable = models.BooleanField(default=False, verbose_name="是否启用该解析") is_system = models.BooleanField(default=False, verbose_name="是否是系统自带解析") is_https = models.BooleanField(default=False, verbose_name="是否支持HTTPS") description = models.TextField(verbose_name='备注', blank=True, null=True, default='') @@ -389,9 +390,10 @@ class DomainCnameInfo(models.Model): class Meta: verbose_name = '系统分发域名配置' verbose_name_plural = "系统分发域名配置" + unique_together = (('user_ipk', 'domain_record'),) def save(self, *args, **kwargs): - if not self.domain_record or (self.domain_record and len(self.domain_record) < 26): # 最多3个启用的价格表 + if self.user_ipk == 0 and (not self.domain_record or (self.domain_record and len(self.domain_record) < 26)): self.domain_record = '%s.%s' % (generate_numeric_token_of_length(24, 'abcdef'), self.domain_record) super(DomainCnameInfo, self).save(*args, **kwargs) diff --git a/fir_ser/api/urls.py b/fir_ser/api/urls.py index 848ce90..cd5c785 100644 --- a/fir_ser/api/urls.py +++ b/fir_ser/api/urls.py @@ -17,7 +17,7 @@ from django.urls import re_path from api.views.advert import UserAdInfoView from api.views.apps import AppsView, AppInfoView, AppReleaseInfoView, AppsQrcodeShowView, AppDownloadTokenView -from api.views.domain import DomainCnameView, DomainInfoView +from api.views.domain import DomainCnameView, DomainInfoView, DomainCnameInfoView from api.views.download import ShortDownloadView, InstallView, DownloadView from api.views.getip import GetRemoteIp from api.views.login import LoginView, UserInfoView, RegistView, AuthorizationView, ChangeAuthorizationView, \ @@ -73,6 +73,7 @@ urlpatterns = [ re_path(r"^pay_success/(?P\w+)$", PaySuccess.as_view()), re_path("^cname_domain$", DomainCnameView.as_view()), re_path("^domain_info$", DomainInfoView.as_view()), + re_path("^cname_info$", DomainCnameInfoView.as_view()), re_path("^mp.weixin$", ValidWxChatToken.as_view()), re_path("^mp.applet$", WeChatAppletView.as_view()), re_path("^mp.web.login$", WeChatWebLoginView.as_view(), name="mp.web.login"), diff --git a/fir_ser/api/utils/modelutils.py b/fir_ser/api/utils/modelutils.py index ba13a1c..85c625e 100644 --- a/fir_ser/api/utils/modelutils.py +++ b/fir_ser/api/utils/modelutils.py @@ -90,14 +90,15 @@ def get_app_download_uri(request, user_obj, app_obj=None, preview=True): return get_server_domain_from_request(request, server_domain) -def get_min_default_domain_cname_obj(is_system=True): +def get_min_default_domain_cname_obj(is_system=True, user_ipk=0): if is_system: c_n = 'userinfo' else: c_n = 'userdomaininfo' - domain_queryset = DomainCnameInfo.objects.annotate(Count(c_n)).filter(is_enable=True, is_system=is_system) + domain_queryset = DomainCnameInfo.objects.annotate(Count(c_n)).filter(is_enable=True, is_system=is_system, + user_ipk=user_ipk) if not domain_queryset: - return DomainCnameInfo.objects.filter(is_enable=True, is_system=True).first() + return DomainCnameInfo.objects.filter(is_enable=True, is_system=True, user_ipk=user_ipk).first() return min(domain_queryset, key=lambda x: getattr(x, f'{c_n}__count')) diff --git a/fir_ser/api/utils/serializer.py b/fir_ser/api/utils/serializer.py index 04ae1df..56844ea 100644 --- a/fir_ser/api/utils/serializer.py +++ b/fir_ser/api/utils/serializer.py @@ -508,6 +508,17 @@ class DomainNameSerializer(serializers.ModelSerializer): return app_info return {} + is_private = serializers.SerializerMethodField() + + def get_is_private(self, obj): + return bool(obj.cname_id.user_ipk) + + +class DomainCnameInfoSerializer(serializers.ModelSerializer): + class Meta: + model = models.DomainCnameInfo + exclude = ["id", "user_ipk", "is_https", "is_system"] + class UserAdInfoSerializer(serializers.ModelSerializer): class Meta: diff --git a/fir_ser/api/views/domain.py b/fir_ser/api/views/domain.py index cd4c2b1..73ae558 100644 --- a/fir_ser/api/views/domain.py +++ b/fir_ser/api/views/domain.py @@ -5,16 +5,20 @@ # date: 2021/3/29 import logging +from dns import rdatatype from rest_framework.response import Response from rest_framework.views import APIView -from api.models import UserDomainInfo, Apps +from api.models import UserDomainInfo, Apps, DomainCnameInfo from api.utils.modelutils import get_user_domain_name, get_min_default_domain_cname_obj, PageNumber from api.utils.response import BaseResponse -from api.utils.serializer import DomainNameSerializer -from common.base.baseutils import is_valid_domain, get_cname_from_domain, get_choices_dict +from api.utils.serializer import DomainNameSerializer, DomainCnameInfoSerializer +from common.base.baseutils import is_valid_domain, get_cname_from_domain, get_choices_dict, make_app_uuid, \ + format_cname_host from common.core.auth import ExpiringTokenAuthentication -from common.utils.caches import del_cache_response_by_short, reset_app_wx_easy_type +from common.core.sysconfig import UserConfig +from common.utils.caches import del_cache_response_by_short, reset_app_wx_easy_type, reset_short_response_cache +from fir_ser.settings import DOMAIN_CNAME_KEY logger = logging.getLogger(__name__) @@ -54,9 +58,16 @@ def remove_domain_wx_easy(app_obj, user_obj): def add_new_domain_info(res, request, domain_name, domain_type): - min_domain_cname_info_obj = get_min_default_domain_cname_obj(False) + user_ipk = 0 + if UserConfig(request.user).PRIVATE_DOWNLOAD_PAGE: + user_ipk = request.user.pk + + min_domain_cname_info_obj = get_min_default_domain_cname_obj(False, user_ipk) if min_domain_cname_info_obj: - res.data = {'cname_domain': min_domain_cname_info_obj.domain_record} + res.data = { + 'domain_record': min_domain_cname_info_obj.domain_record, + 'is_private': bool(min_domain_cname_info_obj.user_ipk) + } data_dict = { 'user_id': request.user, 'cname_id': min_domain_cname_info_obj, @@ -78,7 +89,7 @@ class DomainCnameView(APIView): def get(self, request): res = BaseResponse() - res.data = {'domain_name': '', 'domain_record': '', 'is_enable': False} + res.data = {'domain_name': '', 'domain_record': '', 'is_enable': False, 'is_private': False} user_domain_obj = UserDomainInfo.objects.filter(**get_domain_filter(request)).last() domain_type = request.query_params.get("domain_type", -1) @@ -91,6 +102,7 @@ class DomainCnameView(APIView): res.data['is_enable'] = user_domain_obj.is_enable if user_domain_obj.cname_id: res.data['domain_record'] = user_domain_obj.cname_id.domain_record + res.data['is_private'] = bool(user_domain_obj.cname_id.user_ipk) return Response(res.dict) def post(self, request): @@ -126,7 +138,10 @@ class DomainCnameView(APIView): kwargs['domain_name'] = domain_name user_domain_obj = UserDomainInfo.objects.filter(**kwargs).first() if user_domain_obj: - res.data = {'cname_domain': user_domain_obj.cname_id.domain_record} + res.data = { + 'domain_record': user_domain_obj.cname_id.domain_record, + 'is_private': bool(user_domain_obj.cname_id.user_ipk) + } else: kwargs.pop('domain_name') UserDomainInfo.objects.filter(**kwargs, is_enable=False).delete() @@ -136,7 +151,10 @@ class DomainCnameView(APIView): kwargs['domain_name'] = domain_name user_domain_obj = UserDomainInfo.objects.filter(**kwargs).first() if user_domain_obj: - res.data = {'cname_domain': user_domain_obj.cname_id.domain_record} + res.data = { + 'domain_record': user_domain_obj.cname_id.domain_record, + 'is_private': bool(user_domain_obj.cname_id.user_ipk) + } else: add_new_domain_info(res, request, domain_name, domain_type) else: @@ -234,13 +252,97 @@ class DomainInfoView(APIView): res = BaseResponse() domain_name = request.data.get('domain_name', '') weight = request.data.get('weight', 10) + is_https = request.data.get('is_https', 10) domain_type = request.data.get('domain_type', None) if domain_type is not None and weight and domain_name: domain_name_obj = UserDomainInfo.objects.filter(user_id=request.user, domain_name=domain_name, domain_type=domain_type).all() if domain_name_obj and len(domain_name_obj) == 1: - domain_name_obj.update(weight=weight) + domain_name_obj.update(weight=weight, is_https=is_https) + reset_short_response_cache(request.user, None) else: res.code = 1002 res.msg = '参数有误' return Response(res.dict) + + +class DomainCnameInfoView(APIView): + authentication_classes = [ExpiringTokenAuthentication, ] + + def get(self, request): + res = BaseResponse() + search_key = request.query_params.get("search_key", None) + obj_lists = DomainCnameInfo.objects.filter(user_ipk=request.user.pk) + if search_key: + obj_lists = obj_lists.filter(domain_record=search_key) + + page_obj = PageNumber() + domain_info_serializer = page_obj.paginate_queryset( + queryset=obj_lists.order_by("-created_time"), request=request, view=self) + domain_info = DomainCnameInfoSerializer(domain_info_serializer, many=True, ) + res.data = domain_info.data + res.count = obj_lists.count() + + return Response(res.dict) + + def put(self, request): + res = BaseResponse() + domain_record = request.data.get('domain_record') + act = request.data.get('act') + if domain_record and act: + c_obj = DomainCnameInfo.objects.filter(user_ipk=request.user.pk, domain_record=domain_record).first() + if not c_obj: + res.code = 1003 + res.msg = '参数有误' + return Response(res.dict) + if act == 'check': + resolve_cname = f"{request.user.uid}{make_app_uuid(request.user, domain_record)}" + auth_domain_record = f'{DOMAIN_CNAME_KEY}.{domain_record}' + if get_cname_from_domain(auth_domain_record, resolve_cname, rd_type=rdatatype.TXT): + c_obj.is_enable = True + c_obj.save(update_fields=['is_enable']) + return Response(res.dict) + else: + res.code = 1004 + res.msg = 'TXT解析错误,请检查或者稍后再试' + else: + res.code = 1002 + res.msg = '参数有误' + return Response(res.dict) + + def post(self, request): + res = BaseResponse() + domain_info = request.data + domain_record = domain_info.get('domain_record') + ip_address = domain_info.get('ip_address') + description = domain_info.get('description') + if domain_record and ip_address: + c_obj = DomainCnameInfo.objects.filter(user_ipk=request.user.pk, domain_record=domain_record) + if not c_obj: + DomainCnameInfo.objects.create(user_ipk=request.user.pk, domain_record=domain_record, + ip_address=ip_address, description=description) + + res.data = { + 'a_type': 'DNS', + 'r_type': 'TXT', + 'host_r': f'{DOMAIN_CNAME_KEY}.{format_cname_host(domain_record)}', + 'cname_r': f"{request.user.uid}{make_app_uuid(request.user, domain_record)}", + 'domain_record': domain_record + } + else: + res.code = 1001 + res.msg = "参数有误" + return Response(res.dict) + + def delete(self, request): + res = BaseResponse() + domain_record = request.query_params.get('domain_record') + if domain_record: + c_query_set = DomainCnameInfo.objects.filter(user_ipk=request.user.pk, domain_record=domain_record) + if UserDomainInfo.objects.filter(user_id=request.user, cname_id=c_query_set.first(), + is_enable=True).count(): + reset_short_response_cache(request.user, None) + c_query_set.delete() + if DomainCnameInfo.objects.filter(user_ipk=request.user.pk, is_enable=True).count() == 0: + UserConfig(request.user).del_value('PRIVATE_DOWNLOAD_PAGE') + return Response(res.dict) diff --git a/fir_ser/api/views/personalconfig.py b/fir_ser/api/views/personalconfig.py index ce53caa..d569d81 100644 --- a/fir_ser/api/views/personalconfig.py +++ b/fir_ser/api/views/personalconfig.py @@ -10,7 +10,7 @@ import logging from rest_framework.response import Response from rest_framework.views import APIView -from api.models import UserPersonalConfig +from api.models import UserPersonalConfig, DomainCnameInfo from api.utils.response import BaseResponse from api.utils.serializer import PersonalConfigSerializer from common.core.auth import ExpiringTokenAuthentication, SuperSignPermission @@ -25,14 +25,21 @@ def get_personal_config(request): if config_type == 'super_sign': personal_config = Config.DEVELOPER_STATUS_CONFIG elif config_type == 'preview_route': - personal_config = ['PREVIEW_ROUTE_HASH'] + personal_config = ['PRIVATE_DOWNLOAD_PAGE', 'PREVIEW_ROUTE_HASH'] elif config_type == 'short_download_uri': - personal_config = ['DOWNLOAD_DEPLOYMENT_URL'] + personal_config = ['DOWNLOAD_DEPLOYMENT_HISTORY_URL', 'DOWNLOAD_DEPLOYMENT_HASH_URL'] else: personal_config = [] return personal_config +def check_config_auth(request, config_key, config_value): + if config_key == 'PRIVATE_DOWNLOAD_PAGE': + if not DomainCnameInfo.objects.filter(user_ipk=request.user.pk, is_enable=True).count(): + return False + return True + + class PersonalConfigView(APIView): authentication_classes = [ExpiringTokenAuthentication, ] permission_classes = [SuperSignPermission, ] @@ -60,9 +67,11 @@ class PersonalConfigView(APIView): res = BaseResponse() config_key = request.data.get("config_key", None) config_value = request.data.get("config_value", None) + if config_key is not None and config_value is not None and config_key in get_personal_config(request): - UserPersonalConfig.objects.filter(user_id=request.user, key=config_key).update(value=config_value) - UserConfig(request.user).invalid_config_cache(config_key) + if check_config_auth(request, config_key, config_value): + UserPersonalConfig.objects.filter(user_id=request.user, key=config_key).update(value=config_value) + UserConfig(request.user).invalid_config_cache(config_key) return Response(res.dict) def delete(self, request): diff --git a/fir_ser/common/base/baseutils.py b/fir_ser/common/base/baseutils.py index 39d4560..506fcaa 100644 --- a/fir_ser/common/base/baseutils.py +++ b/fir_ser/common/base/baseutils.py @@ -19,7 +19,7 @@ from Crypto.Cipher import AES from django.core.exceptions import ValidationError from django.core.validators import validate_email from django.utils import timezone -from dns.resolver import Resolver +from dns import resolver, rdatatype from fir_ser.settings import SUPER_SIGN_ROOT @@ -218,20 +218,22 @@ def format_storage_selection(storage_info_list, storage_choice_list): return storage_choice_list -def get_cname_from_domain(domain, resolve_cname): +def get_cname_from_domain(domain, resolve_cname, rd_type=rdatatype.CNAME): dns_list = [ ["119.29.29.29", "114.114.114.114"], ["223.5.5.5", "223.6.6.6"], ["8.8.8.8", "8.8.4.4"], ] - dns_resolver = Resolver() + dns_resolver = resolver.Resolver() domain = domain.lower().strip() count = 3 while count: try: dns_resolver.nameservers = dns_list[len(dns_list) - count] - if dns_resolver.resolve(domain, 'CNAME')[0].to_text() == resolve_cname: - return True + for ans in dns_resolver.resolve(domain, rd_type): + logger.info(f"dns {dns_resolver.nameservers} resolve {domain} answer: {ans.to_text()}") + if ans.to_text().strip("'").strip('"') == resolve_cname.strip("'").strip('"'): + return True except Exception as e: logger.error(f"dns {dns_resolver.nameservers} resolve {domain} failed Exception:{e}") count -= 1 @@ -373,3 +375,13 @@ def make_resigned(bin_url, img_url, bundle_id, app_version, 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 format_cname_host(c_name: str): + cname_list = c_name.split('.') + cname_len = len(cname_list) + if cname_len == 2: + return '' + elif cname_len > 2: + return '.'.join(cname_list[0:-2]) + return '' diff --git a/fir_ser/common/core/sysconfig.py b/fir_ser/common/core/sysconfig.py index 379f463..c11c971 100644 --- a/fir_ser/common/core/sysconfig.py +++ b/fir_ser/common/core/sysconfig.py @@ -158,8 +158,20 @@ class DomainConfCache(ConfigCacheBase): return self.get_value('WEB_DOMAIN', DOMAINCONF.WEB_DOMAIN) @property - def DOWNLOAD_DEPLOYMENT_URL(self): - return self.get_value('DOWNLOAD_DEPLOYMENT_URL', DOMAINCONF.DOWNLOAD_DEPLOYMENT_URL) + def DOWNLOAD_DEPLOYMENT_HASH_URL(self): + return self.get_value('DOWNLOAD_DEPLOYMENT_HASH_URL', DOMAINCONF.DOWNLOAD_DEPLOYMENT_HASH_URL) + + @property + def DOWNLOAD_DEPLOYMENT_HISTORY_URL(self): + return self.get_value('DOWNLOAD_DEPLOYMENT_HISTORY_URL', DOMAINCONF.DOWNLOAD_DEPLOYMENT_HISTORY_URL) + + @property + def DOWNLOAD_DEPLOYMENT_HASH_URL_DES(self): + return self.get_value('DOWNLOAD_DEPLOYMENT_HASH_URL_DES', DOMAINCONF.DOWNLOAD_DEPLOYMENT_HASH_URL_DES) + + @property + def DOWNLOAD_DEPLOYMENT_HISTORY_URL_DES(self): + return self.get_value('DOWNLOAD_DEPLOYMENT_HISTORY_URL_DES', DOMAINCONF.DOWNLOAD_DEPLOYMENT_HISTORY_URL_DES) class BaseConfCache(ConfigCacheBase): @@ -457,6 +469,14 @@ class UserPersonalConfKeyCache(ConfigCacheBase): def PREVIEW_ROUTE_HASH_DES(self): return super().get_value('PREVIEW_ROUTE_HASH_DES', USERPERSONALCONFIGKEY.PREVIEW_ROUTE_HASH_DES) + @property + def PRIVATE_DOWNLOAD_PAGE(self): + return super().get_value('PRIVATE_DOWNLOAD_PAGE', USERPERSONALCONFIGKEY.PRIVATE_DOWNLOAD_PAGE) + + @property + def PRIVATE_DOWNLOAD_PAGE_DES(self): + return super().get_value('PRIVATE_DOWNLOAD_PAGE_DES', USERPERSONALCONFIGKEY.PRIVATE_DOWNLOAD_PAGE_DES) + class ConfigDescriptionCache(ConfigCacheBase): def __init__(self, *args, **kwargs): diff --git a/fir_ser/config.py b/fir_ser/config.py index 680bbe2..a27f186 100644 --- a/fir_ser/config.py +++ b/fir_ser/config.py @@ -14,7 +14,10 @@ class DOMAINCONF(object): API_DOMAIN = "https://app.hehelucky.cn" WEB_DOMAIN = "https://app.hehelucky.cn" MOBILEPROVISION = "https://static.flyapps.top/embedded2.mobileprovision" - DOWNLOAD_DEPLOYMENT_URL = "https://static.flyapps.top/download.v63.23.tar.gz" + DOWNLOAD_DEPLOYMENT_HASH_URL = "https://static.flyapps.top/download.v63.23.tar.gz" + DOWNLOAD_DEPLOYMENT_HISTORY_URL = "https://static.flyapps.top/download.v63.23.tar.gz" + DOWNLOAD_DEPLOYMENT_HASH_URL_DES = "hash模式" + DOWNLOAD_DEPLOYMENT_HISTORY_URL_DES = "history模式" class BASECONF(object): @@ -471,8 +474,10 @@ class USERPERSONALCONFIGKEY(object): 'DEVELOPER_ABNORMAL_DEVICE_WRITE'] PREVIEW_ROUTE_HASH = False # 预览路由模式是否是hash, 如果使用hash模式,url中就会存在“#“符号,这个符号后面的是路径。 - PREVIEW_ROUTE_HASH_DES = '【下载页路由hash模式】:使用hash模式,url中就会存在“#“符号,这个符号后面的是路径。非hash模式,需要nginx进行额外配置' + PREVIEW_ROUTE_HASH_DES = '【下载页路由hash模式】:使用hash模式,url中就会存在“#“符号,这个符号后面的是路径。非hash模式(history模式),需要nginx进行额外配置' + PRIVATE_DOWNLOAD_PAGE = False # 私有下载页配置,默认false + PRIVATE_DOWNLOAD_PAGE_DES = '【私有下载页模式】:默认是关闭状态,若开启,则需要先添加下载域名服务器' class OSSSTORAGECONF(object): STORAGE_FREE_CAPACITY = 2048 * 1024 * 1024 # 单位byte 2G diff --git a/fir_ser/fir_ser/settings.py b/fir_ser/fir_ser/settings.py index 1dc93f8..a07d68b 100644 --- a/fir_ser/fir_ser/settings.py +++ b/fir_ser/fir_ser/settings.py @@ -290,6 +290,7 @@ CACHE_KEY_TEMPLATE = { DATA_DOWNLOAD_KEY = "d_token" FILE_UPLOAD_TMP_KEY = ".tmp" +DOMAIN_CNAME_KEY = "_fly_dnsauth" SYNC_CACHE_TO_DATABASE = { 'download_times': 10, # 下载次数同步时间