下载域名统一管理

publicsignpoll
youngS 3 years ago
parent 3325814c03
commit b10a40d177
  1. 7
      fir_client/src/components/FirHeader.vue
  2. 3
      fir_client/src/components/apps/FirAppInfossecurity.vue
  3. 38
      fir_client/src/components/base/BindDomain.vue
  4. 248
      fir_client/src/components/user/FirUserDomain.vue
  5. 2
      fir_client/src/components/user/FirUserOrders.vue
  6. 15
      fir_client/src/restful/index.js
  7. 6
      fir_client/src/router/index.js
  8. 9
      fir_client/src/store/index.js
  9. 2
      fir_client/vue.config.js
  10. 18
      fir_ser/api/migrations/0010_alter_userdomaininfo_domain_type.py
  11. 8
      fir_ser/api/models.py
  12. 3
      fir_ser/api/urls.py
  13. 2
      fir_ser/api/utils/baseutils.py
  14. 18
      fir_ser/api/utils/serializer.py
  15. 40
      fir_ser/api/views/domain.py

@ -50,9 +50,6 @@
<el-dropdown-item command="userinfo">个人资料</el-dropdown-item>
<el-dropdown-item command="apitoken">API token</el-dropdown-item>
<el-dropdown-item command="setdomian">设置域名</el-dropdown-item>
<el-dropdown-item command="setqrdomian"
v-if="$store.state.userinfo&&$store.state.userinfo.role >1">下载码域名
</el-dropdown-item>
<el-dropdown-item command="storage" v-if="$store.state.userinfo.storage_active">存储管理
</el-dropdown-item>
<el-dropdown-item command="supersign" v-if="$store.state.userinfo.supersign_active">超级签名
@ -132,9 +129,7 @@
}, {methods: 'GET', token: this.token});
} else if (command === 'setdomian') {
this.$store.dispatch("dodomainaction", 1);
} else if (command === 'setqrdomian') {
this.$store.dispatch("dodomainaction", 2);
this.$router.push({"name": 'FirUserDomain'})
} else if (command === 'myorder') {
this.$router.push({"name": 'FirUserOrders'})
} else if (command === 'contact') {

@ -6,7 +6,8 @@
:close-on-press-escape="false"
:visible.sync="bind_domain_sure"
width="666px">
<bind-domain v-if="bind_domain_sure" transitionName="bind-app-domain" :app_id="this.currentapp.app_id"/>
<bind-domain v-if="bind_domain_sure" transitionName="bind-app-domain" :app_id="this.currentapp.app_id"
:domain_type="2"/>
</el-dialog>
<el-form label-width="80px">
<el-form-item label-width="200px" label="访问密码">

@ -38,7 +38,8 @@
>
<template slot-scope="scope">
<el-tooltip content="点击复制到剪贴板">
<el-link v-if="scope.row.host" :underline="false" v-clipboard:copy="scope.row.host"
<el-link v-if="scope.row.host" :underline="false"
v-clipboard:copy="scope.row.host"
v-clipboard:success="copy_success">{{ scope.row.host }}
</el-link>
</el-tooltip>
@ -51,7 +52,8 @@
width="300">
<template slot-scope="scope">
<el-tooltip content="点击复制到剪贴板">
<el-link v-if="scope.row.dns" :underline="false" v-clipboard:copy="scope.row.dns"
<el-link v-if="scope.row.dns" :underline="false"
v-clipboard:copy="scope.row.dns"
v-clipboard:success="copy_success">{{ scope.row.dns }}
</el-link>
</el-tooltip>
@ -72,7 +74,7 @@
</el-link>
</div>
<p style="margin: 10px 0">您的账户正在绑定域名<strong>{{ domain_name }}</strong></p>
<p style="margin: 10px 0">{{b_t_msg}}正在绑定域名<strong>{{ domain_name }}</strong></p>
<el-row>
<el-col :span="16"><p>系统未检出到您的CNAME记录请检查您的配置</p></el-col>
<el-col :span="6">
@ -92,7 +94,7 @@
</div>
<el-row>
<el-col :span="16"><p>您的账户已绑定域名<strong>{{ domain_name }}</strong></p></el-col>
<el-col :span="16"><p>{{b_t_msg}}已绑定域名<strong>{{ domain_name }}</strong></p></el-col>
<el-col :span="6">
<el-button type="danger" size="small" plain style="margin-top: 8px"
@click="remove_domain">
@ -164,6 +166,10 @@
type: Number,
default: 1
},
domain_state: {
type: Boolean,
default: false
},
},
data() {
return {
@ -172,10 +178,14 @@
bind_domain_sure: true,
domain_name: '',
domain_tData: [{'type': 'CNAME', 'host': 'xxx', 'dns': 'demo.xxx.cn'}],
force_bind:false,
force_bind: false,
b_t_msg: '您的账户',
}
},
mounted() {
if (this.domain_type === 2) {
this.b_t_msg = '您的应用'
}
this.bind_click();
},
beforeDestroy() {
@ -195,6 +205,9 @@
this.$store.dispatch("dodomainshow", false);
}
}
if (this.domain_state) {
this.$store.dispatch("dosetdomainstate", true);
}
} else {
if (data.code === 1004) {
this.active = 1;
@ -218,12 +231,16 @@
this.$store.dispatch("dodomainshow", true);
}
}
if (this.domain_state) {
this.$store.dispatch("dosetdomainstate", true);
}
} else {
this.$message.error("解除绑定失败 " + data.msg)
}
}, {methods: 'DELETE', data: {app_id: this.app_id, domain_type: this.domain_type}});
},
bind_click() {
this.$store.dispatch("dosetdomainstate", false);
domainFun(data => {
if (data.code === 1000) {
if (data.data) {
@ -266,13 +283,18 @@
}
} else {
this.$message.error("绑定失败 " + data.msg);
if(data.code === 1011){
this.force_bind=true
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,force_bind:force_bind}
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()

@ -0,0 +1,248 @@
<template>
<el-main>
<el-dialog
:title="domain_title"
:close-on-click-modal="false"
:close-on-press-escape="false"
:visible.sync="bind_domain_sure"
width="666px">
<bind-domain v-if="bind_domain_sure" transitionName="bind-app-domain" :app_id="current_domain_info.app_id"
:domain_type="current_domain_info.domain_type" :domain_state="true"/>
</el-dialog>
<div>
<el-input
style="width: 30%;margin-right: 30px;margin-bottom: 5px"
v-model="search_key"
clearable
placeholder="输入域名或者应用名称搜索"/>
<el-button type="primary" icon="el-icon-search" @click="handleCurrentChange(1)">
搜索
</el-button>
<div style="float: right">
<el-button type="primary" plain @click="$store.dispatch('dodomainaction', 1)">
设置下载页域名
</el-button>
<el-button type="primary" plain v-if="$store.state.userinfo&&$store.state.userinfo.role >1"
@click="$store.dispatch('dodomainaction', 2)">
设置下载码域名
</el-button>
</div>
<el-table
:data="domain_info_list"
v-loading="loading"
border
stripe
style="width: 100%">
<el-table-column
fixed
prop="domain_name"
align="center"
label="绑定域名">
<template slot-scope="scope">
<el-tooltip content="点击复制到剪贴板">
<el-link v-if="scope.row.domain_name" :underline="false"
v-clipboard:copy="scope.row.domain_name"
v-clipboard:success="copy_success">{{ scope.row.domain_name }}
</el-link>
</el-tooltip>
</template>
</el-table-column>
<el-table-column
prop="is_enable"
label="状态"
align="center"
width="110">
<template slot-scope="scope">
<el-tooltip content="点击查看域名绑定信息" v-if="scope.row.is_enable === true" placement="left-start">
<el-button type="success" size="small" @click="show_bind_domain_info(scope.row)">已绑定
</el-button>
</el-tooltip>
<el-tooltip content="激活下载域名绑定信息" v-else placement="left-start">
<el-button type="warning" size="small" @click="show_bind_domain_info(scope.row)">继续绑定
</el-button>
</el-tooltip>
</template>
</el-table-column>
<el-table-column
prop="domain_type"
align="center"
label="域名类型"
width="260">
<template slot-scope="scope">
<el-tooltip content="点击查看应用信息" v-if="scope.row.domain_type===2 && scope.row.app_info"
placement="right-start">
<el-button type="info" plain size="small" @click="appInfos(scope.row.app_info)">{{
format_domain_type(scope.row) }}
</el-button>
</el-tooltip>
<el-button v-else-if="scope.row.domain_type===1" type="success" plain size="small">{{
format_domain_type(scope.row) }}
</el-button>
<el-button v-else type="primary" plain size="small">{{ format_domain_type(scope.row) }}
</el-button>
</template>
</el-table-column>
<el-table-column
:formatter="format_create_time"
prop="created_time"
width="170"
align="center"
label="域名绑定时间"
>
</el-table-column>
</el-table>
</div>
<div style="margin-top: 20px;margin-bottom: 20px">
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page.sync="pagination.currentPage"
:page-sizes="[10, 20, 50, 100]"
:page-size="pagination.pagesize"
layout="total,sizes, prev, pager, next"
:total="pagination.total">
</el-pagination>
</div>
</el-main>
</template>
<script>
import {domaininfo} from "@/restful";
import BindDomain from "@/components/base/BindDomain";
import {getUserInfoFun} from '@/utils'
export default {
name: "FirUserDomain",
components: {BindDomain},
data() {
return {
domain_title: '绑定应用专属下载页域名',
bind_domain_sure: false,
domain_info_list: [],
search_key: "",
pagination: {"currentPage": 1, "total": 0, "pagesize": 10},
domain_type_choices: [],
current_domain_info: {'domain_type': 1, 'app_id': null},
loading: false,
}
},
methods: {
show_bind_domain_info(domain_info) {
this.bind_domain_sure = true;
let app_id = null;
if (domain_info.app_info && domain_info.app_info.app_id) {
app_id = domain_info.app_info.app_id;
}
this.current_domain_info.app_id = app_id;
this.current_domain_info.domain_type = domain_info.domain_type;
this.domain_title = this.format_domain_type(domain_info) + ' 绑定详情';
},
appInfos(app_info) {
if (app_info && app_info.app_id) {
this.$router.push({name: 'FirAppInfossecurity', params: {id: app_info.app_id}})
}
},
copy_success() {
this.$message.success('复制剪切板成功');
},
handleSizeChange(val) {
this.pagination.pagesize = val;
this.get_data_from_tabname({
"size": this.pagination.pagesize,
"page": 1
})
},
handleCurrentChange(val) {
this.pagination.currentPage = val;
this.get_data_from_tabname({
"size": this.pagination.pagesize,
"page": this.pagination.currentPage
})
},
get_data_from_tabname(data = {}) {
data.search_key = this.search_key.replace(/^\s+|\s+$/g, "");
this.UserDomainFun(data)
},
UserDomainFun(params) {
this.loading = true;
domaininfo(data => {
if (data.code === 1000) {
this.domain_info_list = data.data;
this.pagination.total = data.count;
this.domain_type_choices = data.domain_type_choices;
} else {
this.$message.error("域名绑定信息获取失败")
}
this.loading = false;
}, {methods: 'GET', data: params})
},
format_choices(key, obj) {
for (let i = 0; i < obj.length; i++) {
if (key === obj[i].id) {
return obj[i].name
}
}
return "未知"
},
format_domain_type(row) {
if (row.domain_type === 2) {
return "应用" + row.app_info.name + "域名"
}
return this.format_choices(row.domain_type, this.domain_type_choices)
},
format_create_time(row) {
return this.format_time(row.created_time)
},
format_time(stime) {
if (stime) {
stime = stime.split(".")[0].split("T");
return stime[0] + " " + stime[1]
} else
return '';
},
}, mounted() {
getUserInfoFun(this);
this.get_data_from_tabname();
}, watch: {
'$store.state.domian_show_state': function () {
if (this.$store.state.domian_show_state) {
this.bind_domain_sure = false;
this.get_data_from_tabname();
}
},
}
}
</script>
<style scoped>
.el-main {
margin: 20px auto 100px;
width: 1166px;
position: relative;
padding-bottom: 1px;
color: #9b9b9b;
-webkit-font-smoothing: antialiased;
border-radius: 1%;
}
</style>

@ -115,7 +115,7 @@
<div>
<el-input
style="width: 30%;margin-right: 30px;margin-bottom: 10px"
style="width: 30%;margin-right: 30px;margin-bottom: 5px"
v-model="order_id_seach"
clearable
placeholder="输入订单ID"/>

@ -662,3 +662,18 @@ export function wxutils(callBack, params, load = true) {
true
);
}
/**访问域名绑定 */
export function domaininfo(callBack, params, load = true) {
getData(
params.methods,
USERSEVER + '/domain_info',
params.data,
data => {
callBack(data);
},
load,
true,
true
);
}

@ -152,6 +152,12 @@ const router = new VueRouter({
meta: {label: '订单详情'},
component: () => import("@/components/user/FirUserOrders"),
},
{
path: 'domain',
name: 'FirUserDomain',
meta: {label: '绑定域名详情'},
component: () => import("@/components/user/FirUserDomain"),
},
{
path: 'supersign-help',
name: 'FirSuperSignHelp',

@ -17,6 +17,7 @@ const store = new Vuex.Store({
userInfoIndex: 0,
show_domain_msg: false,
domain_action: false,
domian_show_state: false,
},
mutations: {
setuserinfo(state, data) {
@ -36,9 +37,15 @@ const store = new Vuex.Store({
},
setdomainaction(state, val) {
state.domain_action = val
},
setdomainstate(state, val) {
state.domian_show_state = val
}
},
actions: {
dosetdomainstate(context, data) {
context.commit('setdomainstate', data);
},
dodomainaction(context, data) {
context.commit('setdomainaction', data);
},
@ -56,7 +63,7 @@ const store = new Vuex.Store({
},
doucurrentapp(context, val) {
context.commit('setcurrentapp', val)
}
},
}
});

@ -82,7 +82,7 @@ if (page) {
}
}
const version='1.2.7';
const version='1.2.8';
const pro_base_env = {
baseUrl: 'https://flyapps.cn',

@ -0,0 +1,18 @@
# Generated by Django 3.2.3 on 2021-09-23 12:47
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('api', '0009_userdomaininfo_domain_type'),
]
operations = [
migrations.AlterField(
model_name='userdomaininfo',
name='domain_type',
field=models.SmallIntegerField(choices=[(0, '下载码域名'), (1, '下载页域名'), (2, '应用专用域名')], default=1,
help_text='0 表示下载码域名,扫描下载码域名,会自动跳转到预览域名', verbose_name='域名类型'),
),
]

@ -482,7 +482,7 @@ class UserDomainInfo(models.Model):
domain_name = models.CharField(verbose_name="下载页面域名", db_index=True, max_length=64, null=False, blank=False)
is_enable = models.BooleanField(default=False, verbose_name="绑定成功")
created_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
domain_type_choices = ((0, '下载码域名'), (1, '预览下载域名'),)
domain_type_choices = ((0, '下载码域名'), (1, '下载域名'), (2, '应用专用域名'))
domain_type = models.SmallIntegerField(choices=domain_type_choices, default=1, verbose_name="域名类型",
help_text="0 表示下载码域名,扫描下载码域名,会自动跳转到预览域名")
@ -490,5 +490,11 @@ class UserDomainInfo(models.Model):
verbose_name = '用户分发域名绑定'
verbose_name_plural = "用户分发域名绑定"
def save(self, *args, **kwargs):
if self.domain_type == 2:
if not self.app_id:
raise KeyError('app_id must exists when domain_type is 2')
super(UserDomainInfo, self).save(*args, **kwargs)
def __str__(self):
return "%s-%s-%s" % (self.user_id, self.cname_id, self.domain_name)

@ -25,7 +25,7 @@ from api.views.storage import StorageView
from api.views.receiveudids import IosUDIDView, TaskView
from api.views.order import PriceView, OrderView, PaySuccess
from api.views.supersign import DeveloperView, SuperSignUsedView, AppUDIDUsedView, SuperSignCertView
from api.views.domain import DomainCnameView
from api.views.domain import DomainCnameView, DomainInfoView
from api.views.thirdlogin import ValidWxChatToken, ThirdWxAccount
# router=DefaultRouter()
@ -59,6 +59,7 @@ urlpatterns = [
re_path("^certification$", CertificationView.as_view()),
re_path(r"^pay_success/(?P<name>\w+)$", PaySuccess.as_view()),
re_path("^cname_domain$", DomainCnameView.as_view()),
re_path("^domain_info$", DomainInfoView.as_view()),
re_path("^mp.weixin$", ValidWxChatToken.as_view()),
re_path("^third.wx.login$", WeChatLoginView.as_view()),
re_path("^third.wx.sync$", WeChatLoginCheckView.as_view()),

@ -177,7 +177,7 @@ def get_user_domain_name(obj, domain_type=1):
def get_app_domain_name(obj):
domain_obj = UserDomainInfo.objects.filter(app_id=obj, is_enable=True, domain_type=1).first()
domain_obj = UserDomainInfo.objects.filter(app_id=obj, is_enable=True, domain_type=2).first()
if domain_obj:
return domain_obj.domain_name
return ''

@ -586,3 +586,21 @@ class ThirdWxSerializer(serializers.ModelSerializer):
class Meta:
model = models.ThirdWeChatUserInfo
exclude = ["id"]
class DomainNameSerializer(serializers.ModelSerializer):
class Meta:
model = models.UserDomainInfo
exclude = ["id", "user_id"]
app_info = serializers.SerializerMethodField()
def get_app_info(self, obj):
app_obj = obj.app_id
if app_obj:
app_info = {
'app_id': app_obj.app_id,
'name': app_obj.name,
}
return app_info
return {}

@ -3,7 +3,7 @@
# project: 3月
# author: NinEveN
# date: 2021/3/29
from rest_framework.pagination import PageNumberPagination
from rest_framework.views import APIView
from api.utils.baseutils import is_valid_domain, get_cname_from_domain, get_user_domain_name, \
@ -14,7 +14,9 @@ from rest_framework.response import Response
from api.models import UserDomainInfo, Apps
import logging
from api.utils.serializer import DomainNameSerializer
from api.utils.storage.caches import del_cache_response_by_short, reset_app_wx_easy_type, reset_short_response_cache
from api.utils.utils import get_choices_dict
logger = logging.getLogger(__name__)
@ -54,6 +56,7 @@ def add_new_domain_info(res, request, domain_name, domain_type):
app_obj = Apps.objects.filter(app_id=app_id).first()
if app_obj:
data_dict['app_id'] = app_obj
data_dict['domain_type'] = 2
UserDomainInfo.objects.create(**data_dict)
@ -164,3 +167,38 @@ class DomainCnameView(APIView):
reset_app_wx_easy_type(request.user, app_obj)
user_domain_obj.delete()
return Response(res.dict)
class PageNumber(PageNumberPagination):
page_size = 10 # 每页显示多少条
page_size_query_param = 'size' # URL中每页显示条数的参数
page_query_param = 'page' # URL中页码的参数
max_page_size = None # 最大页码数限制
class DomainInfoView(APIView):
authentication_classes = [ExpiringTokenAuthentication, ]
def get(self, request):
res = BaseResponse()
search_key = request.query_params.get("search_key", None)
obj_lists = UserDomainInfo.objects.filter(user_id=request.user)
if search_key:
obj_lists1 = obj_lists.filter(domain_name=search_key)
if not obj_lists1:
obj_lists = obj_lists.filter(app_id__name__contains=search_key)
else:
obj_lists = obj_lists1
page_obj = PageNumber()
domain_info_serializer = page_obj.paginate_queryset(
queryset=obj_lists.order_by("-created_time").order_by('domain_type'),
request=request,
view=self)
domain_info = DomainNameSerializer(domain_info_serializer, many=True, )
res.data = domain_info.data
res.count = obj_lists.count()
res.domain_type_choices = get_choices_dict(UserDomainInfo.domain_type_choices)
return Response(res.dict)

Loading…
Cancel
Save